Changed exit mechanism on gclc

This commit is contained in:
Emmanuel Bigeon 2016-06-09 17:04:53 -04:00
parent 9660d675b6
commit 48c51b3a16
29 changed files with 1059 additions and 442 deletions

View File

@ -87,7 +87,7 @@ of Emmanuel Bigeon. -->
<dependency> <dependency>
<groupId>fr.bigeon</groupId> <groupId>fr.bigeon</groupId>
<artifactId>gclc</artifactId> <artifactId>gclc</artifactId>
<version>1.1.2</version> <version>1.2.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>fr.bigeon</groupId> <groupId>fr.bigeon</groupId>

View File

@ -71,6 +71,10 @@ public class ThreadedServerConsoleManager implements ConsoleManager {
private BufferedReader input; private BufferedReader input;
/** the prompting status */ /** the prompting status */
private boolean doPrompt; private boolean doPrompt;
/**
*
*/
private boolean closed = false;
/** Create the console manager. /** Create the console manager.
* *
@ -159,6 +163,14 @@ public class ThreadedServerConsoleManager implements ConsoleManager {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
// Do nothing // Do nothing
this.closed = true;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
@Override
public boolean isClosed() {
return closed;
} }
} }

View File

@ -116,6 +116,13 @@ public class ConsoleRunnableTest {
public void close() throws IOException { public void close() throws IOException {
// do nothing // do nothing
} }
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
@Override
public boolean isClosed() {
return i == cmds.length;
}
} }
/** Test method for /** Test method for
@ -136,7 +143,7 @@ public class ConsoleRunnableTest {
Object lock = new Object(); Object lock = new Object();
ConsoleApplication app = new ConsoleTestApplication( ConsoleApplication app = new ConsoleTestApplication(
new ConsoleManagerTestImplementation( new ConsoleManagerTestImplementation(
new String[] {"test", ConsoleTestApplication.EXIT})); new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$
ConsoleRunnable runnable = new ConsoleRunnable(app, lock); ConsoleRunnable runnable = new ConsoleRunnable(app, lock);
Thread th = new Thread(runnable); Thread th = new Thread(runnable);
@ -151,7 +158,7 @@ public class ConsoleRunnableTest {
Object lock = new Object(); Object lock = new Object();
ConsoleApplication app = new ConsoleTestApplication( ConsoleApplication app = new ConsoleTestApplication(
new ConsoleManagerTestImplementation( new ConsoleManagerTestImplementation(
new String[] {"test", ConsoleTestApplication.EXIT})); new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$
ConsoleRunnable runnable = new ConsoleRunnable(app, lock); ConsoleRunnable runnable = new ConsoleRunnable(app, lock);
runnable.stop(); runnable.stop();
Thread th = new Thread(runnable); Thread th = new Thread(runnable);
@ -166,7 +173,7 @@ public class ConsoleRunnableTest {
Object lock = new Object(); Object lock = new Object();
ConsoleApplication app = new ConsoleTestApplication( ConsoleApplication app = new ConsoleTestApplication(
new ConsoleManagerTestImplementation( new ConsoleManagerTestImplementation(
new String[] {"test", ConsoleTestApplication.EXIT})); new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$
ConsoleRunnable runnable = new ConsoleRunnable(app, lock); ConsoleRunnable runnable = new ConsoleRunnable(app, lock);
runnable.run(); runnable.run();
} }

View File

@ -34,12 +34,15 @@
*/ */
package fr.bigeon.gclc.socket; package fr.bigeon.gclc.socket;
import java.io.IOException;
import fr.bigeon.gclc.ConsoleApplication; import fr.bigeon.gclc.ConsoleApplication;
import fr.bigeon.gclc.command.Command; 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.exception.InvalidCommandName;
import fr.bigeon.gclc.manager.ConsoleManager; import fr.bigeon.gclc.manager.ConsoleManager;
import fr.bigeon.gclc.prompt.CLIPrompterMessages;
/** A test-purpose application /** A test-purpose application
* *
@ -56,7 +59,7 @@ public class ConsoleTestApplication extends ConsoleApplication {
"See you"); "See you");
try { try {
add(new ExitCommand(EXIT, this)); add(new ExitCommand(EXIT, this));
addHelpCommand("help"); add(new HelpExecutor("help", manager, this.getRoot()));
add(new Command("test") { add(new Command("test") {
@Override @Override
@ -65,63 +68,16 @@ public class ConsoleTestApplication extends ConsoleApplication {
} }
@Override @Override
public void execute(String... args) { public void execute(String... args) throws CommandRunException {
manager.println("Test command ran fine"); try {
manager.println("Test command ran fine");
} catch (IOException e) {
throw new CommandRunException("manager closed", e);
}
} }
}); });
} catch (final InvalidCommandName e) { } catch (final InvalidCommandName e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
/** <p>
* 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);
}
}
} }

View File

@ -51,13 +51,18 @@
<dependency> <dependency>
<groupId>fr.bigeon</groupId> <groupId>fr.bigeon</groupId>
<artifactId>gclc</artifactId> <artifactId>gclc</artifactId>
<version>1.1.2</version> <version>1.2.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.swt.gtk.linux</groupId> <groupId>org.eclipse.swt</groupId>
<artifactId>x86_64</artifactId> <artifactId>org.eclipse.swt.gtk.linux.x86_64</artifactId>
<version>3.3.0-v3346</version> <version>4.3</version>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.swt</groupId>
<artifactId>org.eclipse.swt.win32.win32.x86_64</artifactId>
<version>4.3</version>
</dependency>
<dependency> <dependency>
<groupId>fr.bigeon</groupId> <groupId>fr.bigeon</groupId>
<artifactId>collections</artifactId> <artifactId>collections</artifactId>

View File

@ -100,9 +100,9 @@ public class SWTConsole extends Composite implements ConsoleManager {
if (e.keyCode == SWT.ARROW_UP && if (e.keyCode == SWT.ARROW_UP &&
currentIndex < commands.size() - 1) { currentIndex < commands.size() - 1) {
currentIndex++; currentIndex++;
consoleInput.setText( String cmd = commands.get(commands.size() - currentIndex - 1);
commands.get(commands.size() - currentIndex - 1)); consoleInput.setText(cmd);
consoleInput.setSelection(consoleInput.getText().length()); consoleInput.setSelection(cmd.length());
} }
// Lower arrow retrieves next commands // Lower arrow retrieves next commands
@ -111,9 +111,10 @@ public class SWTConsole extends Composite implements ConsoleManager {
currentIndex--; currentIndex--;
consoleInput.setText(new String()); consoleInput.setText(new String());
} else if (currentIndex > 0) { } else if (currentIndex > 0) {
consoleInput.setText(commands String cmd = commands
.get(commands.size() - (--currentIndex) - 1)); .get(commands.size() - (--currentIndex) - 1);
consoleInput.setSelection(consoleInput.getText().length()); consoleInput.setText(cmd);
consoleInput.setSelection(cmd.length());
} }
} }
} }
@ -245,8 +246,11 @@ public class SWTConsole extends Composite implements ConsoleManager {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#prompt() */ * @see fr.bigeon.gclc.ConsoleManager#prompt() */
@Override @Override
public String prompt() { public String prompt() throws IOException {
synchronized (promptLock) { synchronized (promptLock) {
if (isDisposed()) {
throw new IOException();
}
try { try {
Display.getDefault().syncExec(new Runnable() { Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
@ -268,8 +272,11 @@ public class SWTConsole extends Composite implements ConsoleManager {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */ * @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */
@Override @Override
public String prompt(final String message) { public String prompt(final String message) throws IOException {
synchronized (promptLock) { synchronized (promptLock) {
if (isDisposed()) {
throw new IOException();
}
try { try {
Display.getDefault().syncExec(new Runnable() { Display.getDefault().syncExec(new Runnable() {
@SuppressWarnings("synthetic-access") @SuppressWarnings("synthetic-access")
@ -284,6 +291,9 @@ public class SWTConsole extends Composite implements ConsoleManager {
}); });
prompting = true; prompting = true;
promptLock.wait(); promptLock.wait();
if (isDisposed()) {
throw new IOException();
}
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
command = null; command = null;
} finally { } finally {
@ -292,7 +302,8 @@ public class SWTConsole extends Composite implements ConsoleManager {
@Override @Override
public void run() { public void run() {
lblPromptlabel.setText(prompt); lblPromptlabel.setText(prompt);
lblPromptlabel.pack(); // relayout
SWTConsole.this.layout();
} }
}); });
} }
@ -317,7 +328,8 @@ public class SWTConsole extends Composite implements ConsoleManager {
@Override @Override
public void run() { public void run() {
lblPromptlabel.setText(prompt); lblPromptlabel.setText(prompt);
lblPromptlabel.pack(); // relayout
SWTConsole.this.layout();
} }
}); });
} }
@ -325,9 +337,22 @@ public class SWTConsole extends Composite implements ConsoleManager {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleManager#close() */ * @see fr.bigeon.gclc.manager.ConsoleManager#close() */
@Override @Override
public void close() throws IOException { public void close() {
synchronized (promptLock) {
promptLock.notify();
}
if (consoleInput.isDisposed()) {
return;
}
consoleInput.setEnabled(false); consoleInput.setEnabled(false);
consoleOutput.setEnabled(false); consoleOutput.setEnabled(false);
} }
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
@Override
public boolean isClosed() {
return isDisposed();
}
} }

View File

@ -79,4 +79,12 @@ public class SWTConsoleShell extends Shell {
public ConsoleManager getManager() { public ConsoleManager getManager() {
return console; return console;
} }
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Shell#dispose() */
@Override
public void dispose() {
super.dispose();
console.close();
}
} }

View File

@ -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);
}
}

View File

@ -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;
/**
* <p>
* 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();
}
}
}

View File

@ -36,7 +36,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>gclc</artifactId> <artifactId>gclc</artifactId>
<version>1.1.3-SNAPSHOT</version> <version>1.2.0-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<url>http://www.bigeon.fr/emmanuel</url> <url>http://www.bigeon.fr/emmanuel</url>
<properties> <properties>

View File

@ -36,12 +36,14 @@
* Created on: Sep 6, 2014 */ * Created on: Sep 6, 2014 */
package fr.bigeon.gclc; package fr.bigeon.gclc;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import fr.bigeon.gclc.command.ExitCommand;
import fr.bigeon.gclc.command.HelpExecutor; import fr.bigeon.gclc.command.HelpExecutor;
import fr.bigeon.gclc.command.ICommand; import fr.bigeon.gclc.command.ICommand;
import fr.bigeon.gclc.command.ICommandProvider; 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.i18n.Messages;
import fr.bigeon.gclc.manager.ConsoleManager; import fr.bigeon.gclc.manager.ConsoleManager;
import fr.bigeon.gclc.manager.SystemConsoleManager; import fr.bigeon.gclc.manager.SystemConsoleManager;
import fr.bigeon.gclc.prompt.CLIPrompterMessages;
/** <p> /** <p>
* A {@link ConsoleApplication} is an application that require the user to input * A {@link ConsoleApplication} is an application that require the user to input
@ -64,7 +65,7 @@ import fr.bigeon.gclc.prompt.CLIPrompterMessages;
* <pre> * <pre>
* {@link ConsoleApplication} app = new {@link ConsoleApplication#ConsoleApplication(String, String, String) ConsoleApplication("exit", "welcome", "see you latter")}; * {@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.{@link ConsoleApplication#add(ICommand) add}("my_command", new {@link ICommand MyCommand()});
* app.start(); * app.{@link ConsoleApplication#start start}();
* </pre> * </pre>
* <p> * <p>
* That will launch in the console application that will display "welcome", * 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 * @param welcome the header message to display on launch of this
* application * application
* @param goodbye the message to display on exit * @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, public ConsoleApplication(ConsoleManager manager, String exit,
String welcome, String goodbye) throws InvalidCommandName { String welcome, String goodbye) throws InvalidCommandName {
this(manager, welcome, goodbye); this(manager, welcome, goodbye);
@ -118,7 +122,12 @@ public class ConsoleApplication implements ICommandProvider {
* @param welcome the header message to display on launch of this * @param welcome the header message to display on launch of this
* application * application
* @param goodbye the message to display on exit * @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, public ConsoleApplication(String exit, String welcome,
String goodbye) throws InvalidCommandName { String goodbye) throws InvalidCommandName {
this(new SystemConsoleManager(), welcome, goodbye); this(new SystemConsoleManager(), welcome, goodbye);
@ -134,12 +143,19 @@ public class ConsoleApplication implements ICommandProvider {
* *
* @param cmd the handle for help * @param cmd the handle for help
* @return if the help command was added * @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 { public final boolean addHelpCommand(String cmd) throws InvalidCommandName {
return root.add(new HelpExecutor(cmd, manager, root)); return root.add(new HelpExecutor(cmd, manager, root));
} }
/** @param listener the listener to remove. */ /** Add a command request listener.
* <p>
* A listener can listen several times to the same application.
*
* @param listener the listener to add. */
public final void addListener(CommandRequestListener listener) { public final void addListener(CommandRequestListener listener) {
listeners.add(listener); listeners.add(listener);
} }
@ -149,12 +165,14 @@ public class ConsoleApplication implements ICommandProvider {
* fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String, * fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String,
* java.lang.String[]) */ * java.lang.String[]) */
@Override @Override
public final void executeSub(String command, String... args) { public final void executeSub(String command,
String... args) throws CommandRunException {
root.executeSub(command, args); root.executeSub(command, args);
} }
/** Exit this running application before next command prompt */ /** Exit this running application before next command prompt */
public final void exit() { public final void exit() {
LOGGER.fine("Request exiting application..."); //$NON-NLS-1$
running = false; running = false;
} }
@ -170,11 +188,12 @@ public class ConsoleApplication implements ICommandProvider {
return manager; return manager;
} }
/** @param cmd the command to interpret */ /** @param cmd the command to interpret
public final void interpretCommand(String cmd) { * @throws IOException if the manager was closed */
public final void interpretCommand(String cmd) throws IOException {
List<String> args; List<String> args;
try { try {
args = splitCommand(cmd); args = GCLCConstants.splitCommand(cmd);
} catch (CommandParsingException e1) { } catch (CommandParsingException e1) {
manager.println("Command line cannot be parsed"); //$NON-NLS-1$ manager.println("Command line cannot be parsed"); //$NON-NLS-1$
LOGGER.log(Level.INFO, "Invalid user command " + cmd, e1); //$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 listener the listener to remove (once) */
*
* @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<String> splitCommand(String cmd) throws CommandParsingException {
final List<String> 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 */
public final void removeListener(CommandRequestListener listener) { public final void removeListener(CommandRequestListener listener) {
for (int i = 0; i < listeners.size(); i++) { for (int i = 0; i < listeners.size(); i++) {
if (listeners.get(i) == listener) { if (listeners.get(i) == listener) {
@ -261,73 +224,41 @@ public class ConsoleApplication implements ICommandProvider {
/** Launches the prompting application */ /** Launches the prompting application */
public final void start() { public final void start() {
if (header != null) { try {
manager.println(header); running = true;
} if (header != null) {
running = true; manager.println(header);
do {
final String cmd = manager.prompt();
if (cmd.isEmpty()) {
continue;
} }
for (final CommandRequestListener listener : listeners) { do {
listener.commandRequest(cmd); 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); LOGGER.fine("Exiting application."); //$NON-NLS-1$
} while (running); } catch (IOException e) {
if (footer != null) { // The manager was closed
manager.println(footer); running = false;
LOGGER.log(Level.WARNING,
"The console manager was closed. Closing the application as no one can reach it.", //$NON-NLS-1$
e);
} }
} }
}
/** <p> /** @return if the application is running */
* A command to exit a {@link ConsoleApplication}. public boolean isRunning() {
* return running;
* @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;
} }
/** The actions to take before exiting */ /** @return the root */
public void beforeExit() { public SubedCommand getRoot() {
// Do nothing by default return root;
}
@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);
} }
} }

View File

@ -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
* <p>
* 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<String> splitCommand(String cmd) throws CommandParsingException {
final List<String> 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);
}
}

View File

@ -38,23 +38,25 @@
*/ */
package fr.bigeon.gclc.command; package fr.bigeon.gclc.command;
import java.io.IOException;
import fr.bigeon.gclc.manager.ConsoleManager; import fr.bigeon.gclc.manager.ConsoleManager;
/** <p> /** <p>
* A command to execute. It is mandatory that it has a name and that name cannot * 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.
* <p> * <p>
* A command can be executed, with parameters that will be provided as an array * A command can be executed, with parameters that will be provided as an array
* of strings. * of strings.
* <p> * <p>
* The help mechanism can be overwritten, but is by default doing the following: * The help mechanism is doing the following:
* <ul> * <ul>
* <li>Print the command name * <li>Print the command name
* <li>Print the {@link #brief()} message * <li>Print the {@link #brief()} message
* <li>Print a blank line * <li>Print a blank line
* <li>Print "Usage:" * <li>Print "Usage:"
* <li>Print the usage pattern * <li>Print the {@link #usagePattern() usage pattern}
* <li>Print the usage details * <li>Print the {@link #usageDetail() usage details}
* </ul> * </ul>
* <p> * <p>
* The default behavior for the brief message is to print the tip preceeded by a * 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, * @see fr.bigeon.gclc.command.ICommand#help(fr.bigeon.gclc.ConsoleManager,
* java.lang.String) */ * java.lang.String) */
@Override @Override
public final void help(ConsoleManager manager, String... args) { public final void help(ConsoleManager manager,
String... args) throws IOException {
manager.println(getCommandName()); manager.println(getCommandName());
manager.println(brief()); manager.println(brief());
manager.println(); manager.println();

View File

@ -39,7 +39,9 @@ package fr.bigeon.gclc.command;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import fr.bigeon.gclc.exception.CommandRunException;
import fr.bigeon.gclc.exception.InvalidCommandName; import fr.bigeon.gclc.exception.InvalidCommandName;
import fr.bigeon.gclc.i18n.Messages;
/** <p> /** <p>
* A command provider is a map of key word to command to execute * 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$ private static final String SPACE = " "; //$NON-NLS-1$
/** The commands map */ /** The commands map */
protected final Set<ICommand> commands; protected final Set<ICommand> commands;
/** The error command to be executed when the command isn't recognized */
protected final ICommand error;
/** @param error the error command */ /** Create a command provider */
public CommandProvider(ICommand error) { public CommandProvider() {
super(); super();
commands = new HashSet<>(); commands = new HashSet<>();
this.error = error;
} }
/* (non-Javadoc) /* (non-Javadoc)
@ -75,14 +74,16 @@ public class CommandProvider implements ICommandProvider {
} }
@Override @Override
public void executeSub(String cmd, String... args) { public void executeSub(String cmd,
String... args) throws CommandRunException {
for (final ICommand command : commands) { for (final ICommand command : commands) {
if (command.getCommandName().equals(cmd)) { if (command.getCommandName().equals(cmd)) {
command.execute(args); command.execute(args);
return; return;
} }
} }
error.execute(cmd); throw new CommandRunException(
Messages.getString("CommandProvider.unrecognized0", cmd)); //$NON-NLS-1$
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@ -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;
/** <p>
* 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);
}
}

View File

@ -38,6 +38,9 @@
*/ */
package fr.bigeon.gclc.command; 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.manager.ConsoleManager;
import fr.bigeon.gclc.prompt.CLIPrompterMessages; import fr.bigeon.gclc.prompt.CLIPrompterMessages;
@ -70,8 +73,12 @@ public class HelpExecutor extends Command {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */ * @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */
@Override @Override
public void execute(String... args) { public void execute(String... args) throws CommandRunException {
cmd.help(consoleManager, args); try {
cmd.help(consoleManager, args);
} catch (IOException e) {
throw new CommandRunException("Console manager closed", e); //$NON-NLS-1$
}
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@ -38,6 +38,9 @@
*/ */
package fr.bigeon.gclc.command; 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.manager.ConsoleManager;
/** The contract of commands /** The contract of commands
@ -47,8 +50,10 @@ import fr.bigeon.gclc.manager.ConsoleManager;
* @author Emmanuel Bigeon */ * @author Emmanuel Bigeon */
public interface ICommand { public interface ICommand {
/** @param args the arguments of the command (some expect an empty array) */ /** @param args the arguments of the command (some expect an empty array)
void execute(String... args); * @throws CommandRunException if the execution of the command failed for
* any reason */
void execute(String... args) throws CommandRunException;
/** @return the command's name */ /** @return the command's name */
String getCommandName(); String getCommandName();
@ -68,8 +73,9 @@ public interface ICommand {
* </pre> * </pre>
* *
* @param manager the manager to print the data * @param manager the manager to print the data
* @param args the arguments called with the help */ * @param args the arguments called with the help
void help(ConsoleManager manager, String... args); * @throws IOException if the manager was closed */
void help(ConsoleManager manager, String... args) throws IOException;
/** @return a tip on the command */ /** @return a tip on the command */
String tip(); String tip();

View File

@ -36,6 +36,7 @@
* Created on: Sep 6, 2014 */ * Created on: Sep 6, 2014 */
package fr.bigeon.gclc.command; package fr.bigeon.gclc.command;
import fr.bigeon.gclc.exception.CommandRunException;
import fr.bigeon.gclc.exception.InvalidCommandName; import fr.bigeon.gclc.exception.InvalidCommandName;
/** <p> /** <p>
@ -62,8 +63,10 @@ public interface ICommandProvider {
* the user for a choice. * the user for a choice.
* *
* @param command the name of the command the user wishes to execute * @param command the name of the command the user wishes to execute
* @param args the arguments for the command */ * @param args the arguments for the command
public void executeSub(String command, String... args); * @throws CommandRunException if the command failed to run */
public void executeSub(String command,
String... args) throws CommandRunException;
/** <p> /** <p>
* This method provide the command with the given name found. If no command * This method provide the command with the given name found. If no command

View File

@ -38,11 +38,13 @@
*/ */
package fr.bigeon.gclc.command; package fr.bigeon.gclc.command;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import fr.bigeon.gclc.exception.CommandRunException;
import fr.bigeon.gclc.manager.ConsoleManager; import fr.bigeon.gclc.manager.ConsoleManager;
/** <p> /** <p>
@ -115,13 +117,12 @@ public abstract class ParametrizedCommand extends Command {
* @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */ * @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */
@SuppressWarnings("boxing") @SuppressWarnings("boxing")
@Override @Override
public final void execute(String... args) { public final void execute(String... args) throws CommandRunException {
final CommandParameters parameters = new CommandParameters( final CommandParameters parameters = new CommandParameters(
boolParams.keySet(), stringParams.keySet(), strict); boolParams.keySet(), stringParams.keySet(), strict);
if (!parameters.parseArgs(args)) { if (!parameters.parseArgs(args)) {
// ERROR the parameters could not be correctly parsed // ERROR the parameters could not be correctly parsed
manager.println("Unable to read arguments"); //$NON-NLS-1$ throw new CommandRunException("Unable to read arguments"); //$NON-NLS-1$
return;
} }
final List<String> toProvide = new ArrayList<>(); final List<String> toProvide = new ArrayList<>();
for (final String string : params.keySet()) { 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. // 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<String> toProvide,
CommandParameters parameters) throws CommandRunException {
for (final String string : toProvide) { for (final String string : toProvide) {
String value = manager.prompt(string); String value;
while (value.isEmpty()) { try {
value = manager.prompt(string); 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); parameters.set(string, value);
} }
doExecute(parameters);
} }
} }

View File

@ -36,8 +36,10 @@
* Created on: Sep 6, 2014 */ * Created on: Sep 6, 2014 */
package fr.bigeon.gclc.command; package fr.bigeon.gclc.command;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import fr.bigeon.gclc.exception.CommandRunException;
import fr.bigeon.gclc.exception.InvalidCommandName; import fr.bigeon.gclc.exception.InvalidCommandName;
import fr.bigeon.gclc.manager.ConsoleManager; import fr.bigeon.gclc.manager.ConsoleManager;
@ -61,44 +63,40 @@ public class SubedCommand implements ICommandProvider, ICommand {
/** The name of the command */ /** The name of the command */
private final String name; private final String name;
/** @param name the name of the command /** @param name the name of the command */
* @param error the error to execute when called with wrong usage */ public SubedCommand(String name) {
public SubedCommand(String name, ICommand error) {
this.name = name; this.name = name;
provider = new CommandProvider(error); provider = new CommandProvider();
noArgCommand = null; noArgCommand = null;
tip = null; tip = null;
} }
/** @param name the name of the command /** @param name the name of the command
* @param noArgCommand the command to execute when no extra parameter are * @param noArgCommand the command to execute when no extra parameter are
* provided * provided */
* @param error the error to execute when called with wrong usage */ public SubedCommand(String name, ICommand noArgCommand) {
public SubedCommand(String name, ICommand error, ICommand noArgCommand) {
this.name = name; this.name = name;
provider = new CommandProvider(error); provider = new CommandProvider();
this.noArgCommand = noArgCommand; this.noArgCommand = noArgCommand;
tip = null; tip = null;
} }
/** @param name the name of the command /** @param name the name of the command
* @param noArgCommand the command to execute * @param noArgCommand the command to execute
* @param error the error to execute when called with wrong usage
* @param tip the help tip associated */ * @param tip the help tip associated */
public SubedCommand(String name, ICommand error, ICommand noArgCommand, public SubedCommand(String name, ICommand noArgCommand,
String tip) { String tip) {
this.name = name; this.name = name;
provider = new CommandProvider(error); provider = new CommandProvider();
this.noArgCommand = noArgCommand; this.noArgCommand = noArgCommand;
this.tip = tip; this.tip = tip;
} }
/** @param name the name of the command /** @param name the name of the command
* @param error the error to execute when called with wrong usage
* @param tip the help tip associated */ * @param tip the help tip associated */
public SubedCommand(String name, ICommand error, String tip) { public SubedCommand(String name, String tip) {
this.name = name; this.name = name;
provider = new CommandProvider(error); provider = new CommandProvider();
noArgCommand = null; noArgCommand = null;
this.tip = tip; this.tip = tip;
} }
@ -111,12 +109,12 @@ public class SubedCommand implements ICommandProvider, ICommand {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.acide.Command#execute(java.lang.String[]) */ * @see fr.bigeon.acide.Command#execute(java.lang.String[]) */
@Override @Override
public void execute(String... args) { public void execute(String... args) throws CommandRunException {
if (args.length == 0 || args[0].startsWith("-")) { //$NON-NLS-1$ if (args.length == 0 || args[0].startsWith("-")) { //$NON-NLS-1$
if (noArgCommand != null) { if (noArgCommand != null) {
noArgCommand.execute(args); noArgCommand.execute(args);
} else { } else {
provider.error.execute(args); throw new CommandRunException("Unrecognized command"); //$NON-NLS-1$
} }
} else { } else {
executeSub(args[0], Arrays.copyOfRange(args, 1, args.length)); 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, * fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String,
* java.lang.String[]) */ * java.lang.String[]) */
@Override @Override
public void executeSub(String command, String... args) { public void executeSub(String command,
String... args) throws CommandRunException {
provider.executeSub(command, args); provider.executeSub(command, args);
} }
@ -147,28 +146,27 @@ public class SubedCommand implements ICommandProvider, ICommand {
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#help() */ * @see fr.bigeon.gclc.command.Command#help() */
@Override @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$ if (args.length != 0 && !args[0].startsWith("-")) { //$NON-NLS-1$
// Specific // Specific
final ICommand c = get(args[0]); final ICommand c = get(args[0]);
if (c != null) { if (c != null) {
c.help(manager, Arrays.copyOfRange(args, 1, args.length)); c.help(manager, Arrays.copyOfRange(args, 1, args.length));
} else {
provider.error.execute(args);
} }
} else { } else {
// Generic // Generic
if (noArgCommand != null && noArgCommand.tip() != null) { if (noArgCommand != null && noArgCommand.tip() != null) {
manager.println(TAB + noArgCommand.tip()); manager.println(TAB + noArgCommand.tip());
} }
for (final ICommand cmd : provider.commands) { for (final ICommand cmd : provider.commands) {
if (cmd.tip() == null) { if (cmd.tip() == null) {
manager.println(TAB + cmd.getCommandName()); manager.println(TAB + cmd.getCommandName());
} else { } else {
manager.println(TAB + cmd.getCommandName() + ": " + //$NON-NLS-1$ manager.println(TAB + cmd.getCommandName() + ": " + //$NON-NLS-1$
cmd.tip()); cmd.tip());
}
} }
}
} }
} }

View File

@ -38,6 +38,9 @@
*/ */
package fr.bigeon.gclc.command; 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.manager.ConsoleManager;
import fr.bigeon.gclc.prompt.CLIPrompterMessages; import fr.bigeon.gclc.prompt.CLIPrompterMessages;
@ -62,12 +65,17 @@ public final class UnrecognizedCommand implements ICommand {
} }
@Override @Override
public void execute(String... args) { public void execute(String... args) throws CommandRunException {
if (args.length > 0) {
manager.println(CLIPrompterMessages.getString(UNRECOGNIZED_CMD, try {
(Object[]) args)); if (args.length > 0) {
} else { manager.println(CLIPrompterMessages.getString(UNRECOGNIZED_CMD,
manager.println(CLIPrompterMessages.getString(EXPECTED_CMD)); (Object[]) args));
} else {
manager.println(CLIPrompterMessages.getString(EXPECTED_CMD));
}
} catch (IOException e) {
throw new CommandRunException("Manager closed", e); //$NON-NLS-1$
} }
} }

View File

@ -42,7 +42,7 @@ package fr.bigeon.gclc.exception;
* An exception thrown when a command failed to run correctly. * An exception thrown when a command failed to run correctly.
* *
* @author Emmanuel BIGEON */ * @author Emmanuel BIGEON */
public class CommandRunException extends RuntimeException { public class CommandRunException extends Exception {
/** /**
* *

View File

@ -52,21 +52,32 @@ public interface ConsoleManager {
/** @return the prompt prefix */ /** @return the prompt prefix */
String getPrompt(); String getPrompt();
/** @param text the message to print (without line break at the end). */ /** @param text the message to print (without line break at the end).
void print(String text); * @throws IOException if the manager is closed or could not read the
* prompt */
void print(String text) throws IOException;
/** Prints an end of line */ /** Prints an end of line
void println(); *
* @throws IOException if the manager is closed or could not read the
* prompt */
void println() throws IOException;
/** @param message the message to print */ /** @param message the message to print
void println(String message); * @throws IOException if the manager is closed or could not read the
* prompt */
void println(String message) throws IOException;
/** @return the user inputed string */ /** @return the user inputed string
String prompt(); * @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 /** @param message the message to prompt the user
* @return the user inputed string */ * @return the user inputed string
String prompt(String message); * @throws IOException if the manager is closed or could not read the
* prompt */
String prompt(String message) throws IOException;
/** <p> /** <p>
* Set a prompting prefix. * Set a prompting prefix.
@ -74,6 +85,11 @@ public interface ConsoleManager {
* @param prompt the prompt */ * @param prompt the prompt */
void setPrompt(String 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; void close() throws IOException;
/** @return if the manager is closed. */
boolean isClosed();
} }

View File

@ -38,8 +38,10 @@
*/ */
package fr.bigeon.gclc.manager; package fr.bigeon.gclc.manager;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -64,13 +66,15 @@ public class SystemConsoleManager implements ConsoleManager { // NOSONAR
/** The print stream */ /** The print stream */
private final PrintStream out; private final PrintStream out;
/** The input stream */ /** 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 /** This default constructor relies on the system defined standart output
* and input stream. */ * and input stream. */
public SystemConsoleManager() { public SystemConsoleManager() {
out = System.out; // NOSONAR this(System.out, System.in);
in = System.in;
} }
/** @param out the output stream /** @param out the output stream
@ -78,7 +82,7 @@ public class SystemConsoleManager implements ConsoleManager { // NOSONAR
public SystemConsoleManager(PrintStream out, InputStream in) { public SystemConsoleManager(PrintStream out, InputStream in) {
super(); super();
this.out = out; this.out = out;
this.in = in; this.in = new BufferedReader(new InputStreamReader(in));
} }
/** @return the prompt */ /** @return the prompt */
@ -90,47 +94,60 @@ public class SystemConsoleManager implements ConsoleManager { // NOSONAR
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#print(java.lang.Object) */ * @see fr.bigeon.gclc.ConsoleManager#print(java.lang.Object) */
@Override @Override
public void print(String object) { public void print(String object) throws IOException {
if (closed) {
throw new IOException();
}
out.print(object); out.print(object);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#println() */ * @see fr.bigeon.gclc.ConsoleManager#println() */
@Override @Override
public void println() { public void println() throws IOException {
if (closed) {
throw new IOException();
}
out.println(); out.println();
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#println(java.lang.Object) */ * @see fr.bigeon.gclc.ConsoleManager#println(java.lang.Object) */
@Override @Override
public void println(String object) { public void println(String object) throws IOException {
if (closed) {
throw new IOException();
}
out.println(object); out.println(object);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#prompt() */ * @see fr.bigeon.gclc.ConsoleManager#prompt() */
@Override @Override
public String prompt() { public String prompt() throws IOException {
return prompt(new String() + prompt); return prompt(new String() + prompt);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */ * @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */
@Override @Override
public String prompt(String message) { public String prompt(String message) throws IOException {
if (closed) {
throw new IOException();
}
String result = new String(); String result = new String();
out.print(message + ' '); out.print(message + ' ');
char c; char c;
try { try {
c = (char) in.read(); result = in.readLine();
while (c != System.lineSeparator().charAt(0)) { // c = (char) in.read();
result += c; // while (c != System.lineSeparator().charAt(0)) {
c = (char) in.read(); // result += c;
} // c = (char) in.read();
while (in.available() != 0) { // }
in.read(); // while (in.available() != 0) {
} // in.read();
// }
} catch (final IOException e) { } catch (final IOException e) {
LOGGER.log(Level.SEVERE, "Unable to read prompt", e); //$NON-NLS-1$ 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() */ * @see fr.bigeon.gclc.manager.ConsoleManager#close() */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
in.close(); closed = true;
}
/* (non-Javadoc)
* @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */
@Override
public boolean isClosed() {
return closed;
} }
} }

View File

@ -36,6 +36,7 @@
* Created on: Jul 31, 2014 */ * Created on: Jul 31, 2014 */
package fr.bigeon.gclc.prompt; package fr.bigeon.gclc.prompt;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -80,9 +81,10 @@ public class CLIPrompter {
* @param choices the choices * @param choices the choices
* @param cancel the cancel option if it exists * @param cancel the cancel option if it exists
* @return the number of choices plus one (or the number of choices if there * @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 <U> int listChoices(ConsoleManager manager, List<U> choices, private static <U> int listChoices(ConsoleManager manager, List<U> choices,
String cancel) { String cancel) throws IOException {
int index = 0; int index = 0;
for (final U u : choices) { for (final U u : choices) {
manager.println(index++ + ") " + u); //$NON-NLS-1$ manager.println(index++ + ") " + u); //$NON-NLS-1$
@ -95,9 +97,10 @@ public class CLIPrompter {
/** @param manager the manager /** @param manager the manager
* @param message the prompting message * @param message the prompting message
* @return the choice */ * @return the choice
* @throws IOException if the manager was closed */
public static boolean promptBoolean(ConsoleManager manager, public static boolean promptBoolean(ConsoleManager manager,
String message) { String message) throws IOException {
String result = manager String result = manager
.prompt(message + CLIPrompterMessages.getString(BOOL_CHOICES)); .prompt(message + CLIPrompterMessages.getString(BOOL_CHOICES));
boolean first = true; boolean first = true;
@ -134,11 +137,12 @@ public class CLIPrompter {
* @param message the message * @param message the message
* @param cancel the cancel option, or null * @param cancel the cancel option, or null
* @param <U> the type of elements * @param <U> the type of elements
* @return the choice */ * @return the choice
* @throws IOException if the manager was closed */
@SuppressWarnings("boxing") @SuppressWarnings("boxing")
public static <U> U promptChoice(ConsoleManager manager, List<String> keys, public static <U> U promptChoice(ConsoleManager manager, List<String> keys,
List<U> choices, String message, List<U> choices, String message,
String cancel) { String cancel) throws IOException {
final Integer index = promptChoice(manager, keys, message, cancel); final Integer index = promptChoice(manager, keys, message, cancel);
if (index == null) { if (index == null) {
return null; return null;
@ -153,10 +157,11 @@ public class CLIPrompter {
* @param choicesMap the map of label to actual objects * @param choicesMap the map of label to actual objects
* @param message the prompting message * @param message the prompting message
* @param cancel the cancel option if it exists (null otherwise) * @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 <U, T> T promptChoice(ConsoleManager manager, List<U> choices, public static <U, T> T promptChoice(ConsoleManager manager, List<U> choices,
Map<U, T> choicesMap, String message, Map<U, T> choicesMap, String message,
String cancel) { String cancel) throws IOException {
return choicesMap.get(choices.get( return choicesMap.get(choices.get(
promptChoice(manager, choices, message, cancel).intValue())); promptChoice(manager, choices, message, cancel).intValue()));
} }
@ -166,11 +171,12 @@ public class CLIPrompter {
* @param choices the list of choices * @param choices the list of choices
* @param message the prompting message * @param message the prompting message
* @param cancel the cancel option, or null * @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") @SuppressWarnings("boxing")
public static <U> Integer promptChoice(ConsoleManager manager, public static <U> Integer promptChoice(ConsoleManager manager,
List<U> choices, String message, List<U> choices, String message,
String cancel) { String cancel) throws IOException {
manager.println(message); manager.println(message);
final int index = listChoices(manager, choices, cancel); final int index = listChoices(manager, choices, cancel);
String result = ""; //$NON-NLS-1$ String result = ""; //$NON-NLS-1$
@ -207,18 +213,21 @@ public class CLIPrompter {
* @param choicesMap the map of label to actual objects * @param choicesMap the map of label to actual objects
* @param message the prompting message * @param message the prompting message
* @param cancel the cancel option (or null) * @param cancel the cancel option (or null)
* @return the chosen object */ * @return the chosen object
* @throws IOException if the manager was closed */
public static <U, T> T promptChoice(ConsoleManager manager, public static <U, T> T promptChoice(ConsoleManager manager,
Map<U, T> choicesMap, String message, Map<U, T> choicesMap, String message,
String cancel) { String cancel) throws IOException {
return promptChoice(manager, new ArrayList<>(choicesMap.keySet()), return promptChoice(manager, new ArrayList<>(choicesMap.keySet()),
choicesMap, message, cancel); choicesMap, message, cancel);
} }
/** @param manager the manager /** @param manager the manager
* @param message the prompt message * @param message the prompt message
* @return the integer */ * @return the integer
public static int promptInteger(ConsoleManager manager, String message) { * @throws IOException if the manager was closed */
public static int promptInteger(ConsoleManager manager,
String message) throws IOException {
boolean still = true; boolean still = true;
int r = 0; int r = 0;
while (still) { while (still) {
@ -243,9 +252,10 @@ public class CLIPrompter {
* *
* @param manager the manager * @param manager the manager
* @param message the message * @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<String> promptList(ConsoleManager manager, public static List<String> promptList(ConsoleManager manager,
String message) { String message) throws IOException {
return promptList(manager, message, return promptList(manager, message,
CLIPrompterMessages.getString("promptlist.exit.defaultkey")); //$NON-NLS-1$ CLIPrompterMessages.getString("promptlist.exit.defaultkey")); //$NON-NLS-1$
} }
@ -255,9 +265,11 @@ public class CLIPrompter {
* @param manager the manager * @param manager the manager
* @param message the message * @param message the message
* @param ender the ending sequence for the list * @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<String> promptList(ConsoleManager manager, public static List<String> promptList(ConsoleManager manager,
String message, String ender) { String message,
String ender) throws IOException {
final List<String> strings = new ArrayList<>(); final List<String> strings = new ArrayList<>();
manager.println( manager.println(
message + CLIPrompterMessages.getString(LIST_DISP_KEY, ender)); message + CLIPrompterMessages.getString(LIST_DISP_KEY, ender));
@ -275,9 +287,10 @@ public class CLIPrompter {
* *
* @param manager the manager * @param manager the manager
* @param message the prompting message * @param message the prompting message
* @return the text */ * @return the text
* @throws IOException if the manager was closed */
public static String promptLongText(ConsoleManager manager, public static String promptLongText(ConsoleManager manager,
String message) { String message) throws IOException {
return promptLongText(manager, message, CLIPrompterMessages return promptLongText(manager, message, CLIPrompterMessages
.getString("promptlongtext.exit.defaultkey")); //$NON-NLS-1$ .getString("promptlongtext.exit.defaultkey")); //$NON-NLS-1$
} }
@ -287,9 +300,10 @@ public class CLIPrompter {
* @param manager the manager * @param manager the manager
* @param message the prompting message * @param message the prompting message
* @param ender the ender character * @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, public static String promptLongText(ConsoleManager manager, String message,
String ender) { String ender) throws IOException {
manager.println(message + CLIPrompterMessages manager.println(message + CLIPrompterMessages
.getString("promptlongtext.exit.dispkey", ender)); //$NON-NLS-1$ .getString("promptlongtext.exit.dispkey", ender)); //$NON-NLS-1$
String res = manager.prompt(PROMPT); String res = manager.prompt(PROMPT);
@ -308,11 +322,12 @@ public class CLIPrompter {
* @param choices the real choices * @param choices the real choices
* @param message the message * @param message the message
* @param <U> the type of elements * @param <U> the type of elements
* @return the choice */ * @return the choice
* @throws IOException if the manager was closed */
public static <U> List<U> promptMultiChoice(ConsoleManager manager, public static <U> List<U> promptMultiChoice(ConsoleManager manager,
List<String> keys, List<String> keys,
List<U> choices, List<U> choices,
String message) { String message) throws IOException {
final List<Integer> indices = promptMultiChoice(manager, keys, message); final List<Integer> indices = promptMultiChoice(manager, keys, message);
final List<U> userChoices = new ArrayList<>(); final List<U> userChoices = new ArrayList<>();
for (final Integer integer : indices) { for (final Integer integer : indices) {
@ -327,11 +342,12 @@ public class CLIPrompter {
* @param choices the list of labels (in order to be displayed) * @param choices the list of labels (in order to be displayed)
* @param choicesMap the map of label to actual objects * @param choicesMap the map of label to actual objects
* @param message the prompting message * @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 <U, T> List<T> promptMultiChoice(ConsoleManager manager, public static <U, T> List<T> promptMultiChoice(ConsoleManager manager,
List<U> choices, List<U> choices,
Map<U, T> choicesMap, Map<U, T> choicesMap,
String message) { String message) throws IOException {
final List<Integer> chs = promptMultiChoice(manager, choices, message); final List<Integer> chs = promptMultiChoice(manager, choices, message);
final List<T> userChoices = new ArrayList<>(); final List<T> userChoices = new ArrayList<>();
for (final Integer integer : chs) { for (final Integer integer : chs) {
@ -344,11 +360,12 @@ public class CLIPrompter {
* @param <U> the type of choices * @param <U> the type of choices
* @param choices the list of choices * @param choices the list of choices
* @param message the prompting message * @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") @SuppressWarnings("boxing")
public static <U> List<Integer> promptMultiChoice(ConsoleManager manager, public static <U> List<Integer> promptMultiChoice(ConsoleManager manager,
List<U> choices, List<U> choices,
String message) { String message) throws IOException {
manager.println(message); manager.println(message);
final int index = listChoices(manager, choices, null); final int index = listChoices(manager, choices, null);
String result = ""; //$NON-NLS-1$ String result = ""; //$NON-NLS-1$
@ -404,10 +421,11 @@ public class CLIPrompter {
* @param <T> The real choices objects * @param <T> The real choices objects
* @param choicesMap the map of label to actual objects * @param choicesMap the map of label to actual objects
* @param message the prompting message * @param message the prompting message
* @return the chosen objects */ * @return the chosen objects
* @throws IOException if the manager was closed */
public static <U, T> List<T> promptMultiChoice(ConsoleManager manager, public static <U, T> List<T> promptMultiChoice(ConsoleManager manager,
Map<U, T> choicesMap, Map<U, T> choicesMap,
String message) { String message) throws IOException {
return promptMultiChoice(manager, new ArrayList<>(choicesMap.keySet()), return promptMultiChoice(manager, new ArrayList<>(choicesMap.keySet()),
choicesMap, message); choicesMap, message);
} }
@ -415,9 +433,10 @@ public class CLIPrompter {
/** @param manager the manager /** @param manager the manager
* @param prompt the prompting message * @param prompt the prompting message
* @param reprompt the prompting message after empty input * @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, public static String promptNonEmpty(ConsoleManager manager, String prompt,
String reprompt) { String reprompt) throws IOException {
String res = manager.prompt(prompt); String res = manager.prompt(prompt);
while (res.isEmpty()) { while (res.isEmpty()) {
res = manager.prompt(reprompt); res = manager.prompt(reprompt);

View File

@ -1 +1,2 @@
CommandProvider.unrecognized=Unrecognized command '{0}'
ConsoleApplication.cmd.failed=The command '{0}' failed due to : ConsoleApplication.cmd.failed=The command '{0}' failed due to :

View File

@ -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$
}
}
}

View File

@ -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();
}
}
}

View File

@ -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<String> 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
}
}
}