diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConsoleRunnable.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConsoleRunnable.java new file mode 100644 index 0000000..2a6d2ea --- /dev/null +++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ConsoleRunnable.java @@ -0,0 +1,77 @@ +/* + * Copyright E. Bigeon (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * Socket implementation of GCLC. + * + * 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-socket:fr.bigeon.gclc.socket.ConsoleRunnable.java + * Created on: Jun 1, 2016 + */ +package fr.bigeon.gclc.socket; + +import fr.bigeon.gclc.ConsoleApplication; + +/** A runnable class that will actually have the application running. + * + * @author Emmanuel Bigeon */ +public class ConsoleRunnable implements Runnable { + + /** The actual application */ + private final ConsoleApplication app; + /** The synchronization object */ + private final Object promptingLock; + + /** @param app the application + * @param promptingLock the synchronization object */ + public ConsoleRunnable(ConsoleApplication app, Object promptingLock) { + super(); + this.app = app; + this.promptingLock = promptingLock; + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() */ + @Override + public void run() { + app.start(); + synchronized (promptingLock) { + // release all waiting elements before ending + promptingLock.notifyAll(); + } + } + + /** Stop the application (it will finish its current operation) */ + public void stop() { + app.exit(); + } + +} diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleApplicationShell.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleApplicationShell.java index c20fedc..3d3b250 100644 --- a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleApplicationShell.java +++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/SocketConsoleApplicationShell.java @@ -44,7 +44,10 @@ import java.io.PipedOutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketException; import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; import fr.bigeon.gclc.ConsoleApplication; import fr.bigeon.gclc.ConsoleManager; @@ -70,16 +73,20 @@ import fr.bigeon.smu.StringEncoder; * @author Emmanuel Bigeon */ public class SocketConsoleApplicationShell implements Runnable { + /** + * + */ + private static final String INTERRUPTION_WHILE_WORKING = "Interruption while application was working"; //$NON-NLS-1$ /** The end of line character */ protected static final String EOL = "\n"; //$NON-NLS-1$ /** The encoder */ - private static final StringEncoder ENCODER = new StringEncoder("%", Arrays.asList(EOL)); //$NON-NLS-1$ + private static final StringEncoder ENCODER = new StringEncoder("%", //$NON-NLS-1$ + Arrays.asList(EOL)); + /** The class logger */ + private static final Logger LOGGER = Logger + .getLogger(SocketConsoleApplicationShell.class.getName()); /** The listening port */ private final int port; - /** The output */ - private PrintWriter output; - /** The input reader */ - private BufferedReader input; /** The input */ private final PipedInputStream consoleInput = new PipedInputStream(); /** The application */ @@ -88,180 +95,174 @@ public class SocketConsoleApplicationShell implements Runnable { private final String close; /** The running status */ private boolean running; - /** If the prompt should be next activity */ - private boolean doPrompt = false; /** An object to lock on for prompt */ private final Object promptingLock = new Object(); /** The console manager implementation */ - private final ConsoleManager consoleManager = new ConsoleManager() { - - private String prompt = new String(); - private StringBuffer buffer = new StringBuffer(); - - @Override - public void setPrompt(String prompt) { - this.prompt = prompt; - } - - @SuppressWarnings("synthetic-access") - @Override - public String prompt(String message) { - buffer.append(message); - String userInput = new String(); - boolean prompting = true; - while (prompting) { - output.println(ENCODER.encode(buffer.toString())); - try { - synchronized (promptingLock) { - doPrompt = true; - promptingLock.notify(); - } - userInput = input.readLine(); - doPrompt = false; - prompting = false; - } catch (final IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - buffer = new StringBuffer(); - return userInput; - } - - @Override - public String prompt() { - return prompt(prompt); - } - - @Override - public void println(String message) { - buffer.append(message + EOL); - - } - - @Override - public void println() { - buffer.append(EOL); - } - - @Override - public void print(String text) { - buffer.append(text); - } - - @Override - public String getPrompt() { - return prompt; - } - }; + private final ThreadedServerConsoleManager consoleManager = new ThreadedServerConsoleManager( + ENCODER, promptingLock); /** The auto close flag. if this is true, every request closes the session * after its call */ private final boolean autoClose; + /** The server socket */ + private ServerSocket serverSocket; + /** The application shutdown string */ + private final String applicationShutdown; - /** The runnable class that will actually have the application running. - * - * @author Emmanuel Bigeon */ - private class ConsoleRunnable implements Runnable { - - /** - * - */ - public ConsoleRunnable() { - // TODO Auto-generated constructor stub - } - - /* (non-Javadoc) - * @see java.lang.Runnable#run() */ - @SuppressWarnings("synthetic-access") - @Override - public void run() { - running = true; - app.start(); - running = false; - synchronized (promptingLock) { - promptingLock.notifyAll(); - } - } - - } - - /** TODO Describe SocketConsoleApplicationShell.java Constructor + /** Create a socket application shell which will listen on the given port + * and close session upon the provided string reception by client * * @param port the port to listen to - * @param close the session closing command */ - public SocketConsoleApplicationShell(int port, String close) { + * @param close the session closing command + * @param applicationShutdown the appication shut down command */ + public SocketConsoleApplicationShell(int port, String close, + String applicationShutdown) { this.port = port; this.close = close; + this.applicationShutdown = applicationShutdown; this.autoClose = false; } - /** TODO Describe SocketConsoleApplicationShell.java Constructor + /** Create a socket application shell which will listen on the given port + * and auto close session after one instruction * * @param port the port to listen to * @param autoClose if the session must be closed once the request has been - * sent */ - public SocketConsoleApplicationShell(int port, boolean autoClose) { + * sent + * @param applicationShutdown the application shutdown command */ + public SocketConsoleApplicationShell(int port, boolean autoClose, + String applicationShutdown) { this.port = port; - this.close = null; this.autoClose = autoClose; + this.applicationShutdown = applicationShutdown; + this.close = autoClose ? null : "close"; //$NON-NLS-1$ } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { - try (ServerSocket serverSocket = new ServerSocket(port)) { - final ConsoleRunnable runnable = new ConsoleRunnable(); + try (ServerSocket actualServerSocket = new ServerSocket(port)) { + this.serverSocket = actualServerSocket; + final ConsoleRunnable runnable = new ConsoleRunnable(app, + promptingLock); final Thread appTh = new Thread(runnable); running = true; try (PipedOutputStream outStream = new PipedOutputStream(); - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outStream))) { + BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(outStream))) { consoleInput.connect(outStream); - try (BufferedReader inBuf = new BufferedReader(new InputStreamReader(consoleInput))) { - input = inBuf; - while (running) { - try (Socket clientSocket = serverSocket.accept(); - PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); - BufferedReader in = new BufferedReader( - new InputStreamReader(clientSocket.getInputStream()));) { - output = out; - // Initiate application - if (!appTh.isAlive()) { - appTh.start(); - } else { - output.println("Reconnected"); //$NON-NLS-1$ - } - synchronized (promptingLock) { - String ln; - if (!doPrompt) try { - promptingLock.wait(); - } catch (final InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - while (running && (ln = in.readLine()) != null) { - if (ln.equals(close)) { - break; - } - writer.write(ln + EOL); - writer.flush(); - try { - promptingLock.wait(); - } catch (final InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - if (autoClose) running = false; - } - } - } - } + try (InputStreamReader isr = new InputStreamReader( + consoleInput); + BufferedReader inBuf = new BufferedReader(isr);) { + consoleManager.setInput(inBuf); + runSokectServer(appTh, writer); + // Close the application + // Pass command to application + writer.write(applicationShutdown + EOL); + writer.flush(); } } } catch (final IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + LOGGER.log(Level.SEVERE, + "Communication error between client and server", e); //$NON-NLS-1$ + } + } + + /** @param appTh the application thread + * @param writer the writer to the application + * @throws IOException if the communication with the client failed */ + private void runSokectServer(Thread appTh, + BufferedWriter writer) throws IOException { + while (running) { + try (Socket clientSocket = serverSocket.accept(); + PrintWriter out = new PrintWriter( + clientSocket.getOutputStream(), true); + BufferedReader in = new BufferedReader(new InputStreamReader( + clientSocket.getInputStream()));) { + // this is not threaded to avoid several clients at the same + // time + consoleManager.setOutput(out); + // Initiate application + if (!appTh.isAlive()) { + appTh.start(); + } else { + out.println("Reconnected"); //$NON-NLS-1$ + } + communicate(writer, in); + } catch (SocketException e) { + LOGGER.log(Level.INFO, "Socket closed", e); //$NON-NLS-1$ + } + } + } + + /** active communication between server and client + * + * @param writer the writer to the application + * @param in the input from the client + * @throws IOException if the communication failed */ + private void communicate(BufferedWriter writer, + BufferedReader in) throws IOException { + synchronized (promptingLock) { + if (!consoleManager.isPrompting()) { + try { + // wait for application to finish its operation + promptingLock.wait(); + } catch (final InterruptedException e) { + LOGGER.log(Level.SEVERE, INTERRUPTION_WHILE_WORKING, e); + } + } + if (autoClose) { + communicateOnce(in, writer); + } else { + communicateLoop(in, writer); + } + } + } + + /** @param in the input from the client + * @param writer the output to the client + * @throws IOException if the communication failed */ + private void communicateOnce(BufferedReader in, + BufferedWriter writer) throws IOException { + String ln; + if ((ln = in.readLine()) != null) { + if (ln.equals(close)) { + return; + } + // Pass command to application + writer.write(ln + EOL); + writer.flush(); + try { + // Wait for application process to + // finish + promptingLock.wait(); + } catch (final InterruptedException e) { + LOGGER.log(Level.SEVERE, INTERRUPTION_WHILE_WORKING, e); + } + } + } + + /** @param in the input from the client + * @param writer the output to the client + * @throws IOException if the communication failed */ + private void communicateLoop(BufferedReader in, + BufferedWriter writer) throws IOException { + String ln; + while (running && (ln = in.readLine()) != null) { + if (ln.equals(close)) { + break; + } + // Pass command to application + writer.write(ln + EOL); + writer.flush(); + try { + // Wait for application process to + // finish + promptingLock.wait(); + } catch (final InterruptedException e) { + LOGGER.log(Level.SEVERE, INTERRUPTION_WHILE_WORKING, e); + } } } @@ -275,4 +276,21 @@ public class SocketConsoleApplicationShell implements Runnable { this.app = app; } + /** This method will request the server to stop. + *
+ * In most cases, this will terminate communication on every client. On some
+ * cases, the */
+ public void stop() {
+ running = false;
+ try {
+ serverSocket.close();
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, "Exception in closing socket server", e); //$NON-NLS-1$
+ }
+ synchronized (promptingLock) {
+ promptingLock.notifyAll();
+ }
+ app.exit();
+ }
+
}
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
new file mode 100644
index 0000000..3b4ef78
--- /dev/null
+++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ThreadedServerConsoleManager.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright E. Bigeon (2014)
+ *
+ * emmanuel@bigeon.fr
+ *
+ * This software is a computer program whose purpose is to
+ * Socket implementation of GCLC.
+ *
+ * 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-socket:fr.bigeon.gclc.socket.ThreadedServerConsoleManager.java
+ * Created on: Jun 1, 2016
+ */
+package fr.bigeon.gclc.socket;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import fr.bigeon.gclc.ConsoleManager;
+import fr.bigeon.smu.StringEncoder;
+
+/** The console manager for socket communication
+ *
+ * @author Emmanuel Bigeon */
+public class ThreadedServerConsoleManager implements ConsoleManager {
+
+ /** The eol character */
+ private static final String EOL = "\n"; //$NON-NLS-1$
+ /** The class logger */
+ private static final Logger LOGGER = Logger
+ .getLogger(ThreadedServerConsoleManager.class.getName());
+ /** The prompting sequence */
+ private String prompt = new String();
+ /** The buffer of data to send to the user */
+ private StringBuilder buffer = new StringBuilder();
+ /** The synchronized object */
+ private final Object promptingLock;
+ /** The output to write data comming from the application */
+ private PrintWriter output;
+ /** The encoder to encode data coming from the application */
+ private final StringEncoder encoder;
+ /** The input to wait data from the user */
+ private BufferedReader input;
+ /** the prompting status */
+ private boolean doPrompt;
+
+ /** Create the console manager.
+ *
+ * @param encoder the encoder for output
+ * @param promptingLock the synchronization object */
+ public ThreadedServerConsoleManager(StringEncoder encoder,
+ Object promptingLock) {
+ super();
+ this.encoder = encoder;
+ this.promptingLock = promptingLock;
+ }
+
+ /** @param input the input to set */
+ public void setInput(BufferedReader input) {
+ this.input = input;
+ }
+
+ /** @param output the output to set */
+ public void setOutput(PrintWriter output) {
+ this.output = output;
+ }
+
+ @Override
+ public void setPrompt(String prompt) {
+ this.prompt = prompt;
+ }
+
+ @Override
+ public String prompt(String message) {
+ buffer.append(message);
+ String userInput = new String();
+ boolean prompting = true;
+ while (prompting) {
+ // Send buffer content
+ output.println(encoder.encode(buffer.toString()));
+ try {
+ synchronized (promptingLock) {
+ doPrompt = true;
+ promptingLock.notify();
+ }
+ userInput = input.readLine();
+ doPrompt = false;
+ prompting = false;
+ } catch (final IOException e) {
+ LOGGER.log(Level.SEVERE, "input reading error", e); //$NON-NLS-1$
+ }
+ }
+ // Renew buffer
+ buffer = new StringBuilder();
+ return userInput;
+ }
+
+ /** @return the prompting status */
+ public synchronized boolean isPrompting() {
+ return doPrompt;
+ }
+
+ @Override
+ public String prompt() {
+ return prompt(prompt);
+ }
+
+ @Override
+ public void println(String message) {
+ buffer.append(message + EOL);
+
+ }
+
+ @Override
+ public void println() {
+ buffer.append(EOL);
+ }
+
+ @Override
+ public void print(String text) {
+ buffer.append(text);
+ }
+
+ @Override
+ public String getPrompt() {
+ return prompt;
+ }
+
+
+}
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
new file mode 100644
index 0000000..8508811
--- /dev/null
+++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright E. Bigeon (2014)
+ *
+ * emmanuel@bigeon.fr
+ *
+ * This software is a computer program whose purpose is to
+ * Socket implementation of GCLC.
+ *
+ * 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-socket:fr.bigeon.gclc.socket.ConsoleRunnableTest.java
+ * Created on: Jun 1, 2016
+ */
+package fr.bigeon.gclc.socket;
+
+import org.junit.Test;
+
+import fr.bigeon.gclc.ConsoleApplication;
+import fr.bigeon.gclc.ConsoleManager;
+import fr.bigeon.gclc.system.SystemConsoleManager;
+
+/** Test class for {@link ConsoleRunnable}
+ *
+ * @author Emmanuel Bigeon */
+@SuppressWarnings({"static-method", "unused"})
+public class ConsoleRunnableTest {
+
+ /**
+ * Test method for {@link fr.bigeon.gclc.socket.ConsoleRunnable#ConsoleRunnable(fr.bigeon.gclc.ConsoleApplication, java.lang.Object)}.
+ */
+ @Test
+ public void testConsoleRunnable() {
+ Object lock = new Object();
+ ConsoleApplication app = new ConsoleTestApplication(
+ new SystemConsoleManager());
+ ConsoleRunnable runnable = new ConsoleRunnable(app, lock);
+
+ }
+
+ /**
+ * Test method for {@link fr.bigeon.gclc.socket.ConsoleRunnable#run()}.
+ */
+ @Test
+ public void testRunFlow() {
+ Object lock = new Object();
+ ConsoleApplication app = new ConsoleTestApplication(
+ new ConsoleManager() {
+
+ @Override
+ public void setPrompt(String prompt) {
+ // do nothing
+ }
+
+ @Override
+ public String getPrompt() {
+ // Not used in test
+ return ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public String prompt() {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) { // NOSONAR
+ // do nothing
+ }
+ return "mock"; //$NON-NLS-1$
+ }
+
+ @Override
+ public String prompt(String message) {
+ return prompt();
+ }
+
+ @Override
+ public void println(String message) {
+ // do nothing
+ }
+
+ @Override
+ public void println() {
+ // do nothing
+ }
+
+ @Override
+ public void print(String text) {
+ // do nothing
+ }
+ });
+ ConsoleRunnable runnable = new ConsoleRunnable(app, lock);
+
+ Thread th = new Thread(runnable);
+ th.start();
+
+ runnable.stop();
+ }
+
+ /**
+ * Test method for {@link fr.bigeon.gclc.socket.ConsoleRunnable#stop()}.
+ */
+ @Test
+ public void testStop() {
+ Object lock = new Object();
+ ConsoleApplication app = new ConsoleTestApplication(
+ new ConsoleManager() {
+
+ @Override
+ public void setPrompt(String prompt) {
+ throw new RuntimeException("Not implemented yet"); //$NON-NLS-1$
+ }
+
+ @Override
+ public String getPrompt() {
+ throw new RuntimeException("Not implemented yet"); //$NON-NLS-1$
+ }
+
+ @Override
+ public String prompt() {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return "mock"; //$NON-NLS-1$
+ }
+
+ @Override
+ public String prompt(String message) {
+ throw new RuntimeException("Not implemented yet"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void println(String message) {
+ //
+ }
+
+ @Override
+ public void println() {
+ //
+ }
+
+ @Override
+ public void print(String text) {
+ //
+ }
+ });
+ ConsoleRunnable runnable = new ConsoleRunnable(app, lock);
+ runnable.stop();
+ Thread th = new Thread(runnable);
+ th.start();
+ runnable.stop();
+ runnable.stop();
+ }
+
+ /** Test method for {@link fr.bigeon.gclc.socket.ConsoleRunnable#stop()}. */
+ @Test
+ public void testRun() {
+ Object lock = new Object();
+ ConsoleApplication app = new ConsoleTestApplication(
+ new ConsoleManager() {
+
+ @Override
+ public void setPrompt(String prompt) {
+ throw new RuntimeException("Not implemented yet"); //$NON-NLS-1$
+ }
+
+ @Override
+ public String getPrompt() {
+ throw new RuntimeException("Not implemented yet"); //$NON-NLS-1$
+ }
+
+ @Override
+ public String prompt() {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return "exit"; //$NON-NLS-1$
+ }
+
+ @Override
+ public String prompt(String message) {
+ throw new RuntimeException("Not implemented yet"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void println(String message) {
+ //
+ }
+
+ @Override
+ public void println() {
+ //
+ }
+
+ @Override
+ public void print(String text) {
+ //
+ }
+ });
+ 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 a662c71..32ca902 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
@@ -39,16 +39,18 @@ import fr.bigeon.gclc.ConsoleManager;
import fr.bigeon.gclc.command.Command;
import fr.bigeon.gclc.exception.InvalidCommandName;
-/** TODO Describe ConsoleTestApplication.java
- * @author Emmanuel Bigeon
- *
- */
+/** A test-purpose application
+ *
+ * @author Emmanuel Bigeon */
public class ConsoleTestApplication extends ConsoleApplication {
+ /** Exit command */
+ public static final String EXIT = "exit"; //$NON-NLS-1$
+
/** @param manager the manager */
@SuppressWarnings("nls")
public ConsoleTestApplication(final ConsoleManager manager) {
- super(manager, "exit",
+ super(manager, EXIT,
"Welcome to the test application. Type help or test.",
"See you");
addHelpCommand("help");
@@ -66,9 +68,7 @@ public class ConsoleTestApplication extends ConsoleApplication {
}
});
} catch (final InvalidCommandName e) {
- // TODO Auto-generated catch block
e.printStackTrace();
}
}
-
}
diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/SocketConsoleApplicationTest.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/SocketConsoleApplicationTest.java
new file mode 100644
index 0000000..91d5907
--- /dev/null
+++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/SocketConsoleApplicationTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright E. Bigeon (2014)
+ *
+ * emmanuel@bigeon.fr
+ *
+ * This software is a computer program whose purpose is to
+ * Socket implementation of GCLC.
+ *
+ * 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-socket:fr.bigeon.gclc.socket.SocketConsoleApplicationTest.java
+ * Created on: Jun 1, 2016
+ */
+package fr.bigeon.gclc.socket;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.Socket;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import fr.bigeon.smu.StringEncoder;
+
+/** Test class for {@link SocketConsoleApplicationShell}
+ *
+ * @author Emmanuel Bigeon */
+@SuppressWarnings({"static-method", "unused", "javadoc", "nls"})
+public class SocketConsoleApplicationTest {
+
+ private static final StringEncoder ENCODER = new StringEncoder("%",
+ Arrays.asList("\n")); //$NON-NLS-1$
+
+ @Test
+ public void integrationTest() {
+ Thread server = TestServer.startServer(false);
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ final String hostName = "127.0.0.1";
+ final int portNumber = 3300;
+
+ try (Socket kkSocket = new Socket(hostName, portNumber);
+ PrintWriter out = new PrintWriter(kkSocket.getOutputStream(),
+ true);
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(kkSocket.getInputStream()));) {
+
+ String fromServer;
+ int i = 0;
+ String[] cmds = {"help", "test", "close"};
+ while ((fromServer = in.readLine()) != null) {
+ System.out.println("Server: \n" + ENCODER.decode(fromServer));
+ if (fromServer.equals("Bye.")) {
+ break;
+ }
+
+ final String fromUser = cmds[i];
+ if (fromUser != null) {
+ System.out.println("Client: " + fromUser);
+ out.println(fromUser);
+ }
+ i++;
+ }
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ TestServer.closeServer();
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e2) {
+ e2.printStackTrace();
+ }
+ server = TestServer.startServer(true);
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+
+ try (Socket kkSocket = new Socket(hostName, portNumber);
+ PrintWriter out = new PrintWriter(kkSocket.getOutputStream(),
+ true);
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(kkSocket.getInputStream()));) {
+
+ String fromServer;
+ int i = 0;
+ String[] cmds = {"help", "test", "close"};
+ while ((fromServer = in.readLine()) != null) {
+// System.out.println("Server: \n" + ENCODER.decode(fromServer));
+ if (fromServer.equals("Bye.")) {
+ break;
+ }
+
+ final String fromUser = cmds[i];
+ if (fromUser != null) {
+// System.out.println("Client: " + fromUser);
+ out.println(fromUser);
+ }
+ i++;
+ }
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ TestServer.closeServer();
+
+ }
+}
diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestServer.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestServer.java
index bff12b1..0df3e3f 100644
--- a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestServer.java
+++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/TestServer.java
@@ -34,26 +34,58 @@
*/
package fr.bigeon.gclc.socket;
-/** TODO Describe TestServer.java
- * @author Emmanuel Bigeon
- *
- */
+/** A test server
+ *
+ * @author Emmanuel Bigeon */
+@SuppressWarnings({"javadoc", "nls"})
public class TestServer {
+
+ private static SocketConsoleApplicationShell SHELL;
+ private static Thread server;
+
/** @param args no argument */
- @SuppressWarnings("nls")
- public static void main(String[] args) {
- final SocketConsoleApplicationShell shell = new SocketConsoleApplicationShell(
- 3300, "close");
- final ConsoleTestApplication app = new ConsoleTestApplication(
- shell.getConsoleManager());
- shell.setApplication(app);
- final Thread serverTh = new Thread(shell, "gclcServer");
- serverTh.start();
+ public static void main(String... args) {
try {
- serverTh.join();
+ startServer(false).join();
} catch (final InterruptedException e) {
- // TODO Auto-generated catch block
e.printStackTrace();
}
}
+
+ public static Thread getServer() {
+ if (server == null) {
+ server = new Thread(getShell(), "gclcServer");
+ server.start();
+ }
+ return server;
+ }
+
+ /** @return */
+ private static SocketConsoleApplicationShell getShell() {
+ if (SHELL == null) {
+ SHELL = new SocketConsoleApplicationShell(3300, "close",
+ ConsoleTestApplication.EXIT);
+ final ConsoleTestApplication app = new ConsoleTestApplication(
+ SHELL.getConsoleManager());
+ SHELL.setApplication(app);
+ }
+ return SHELL;
+ }
+
+ public static Thread startServer(boolean autoClose) {
+ if (SHELL == null) {
+ SHELL = new SocketConsoleApplicationShell(3300, autoClose,
+ ConsoleTestApplication.EXIT);
+ final ConsoleTestApplication app = new ConsoleTestApplication(
+ SHELL.getConsoleManager());
+ SHELL.setApplication(app);
+ server = null;
+ }
+ return getServer();
+ }
+
+ public static void closeServer() {
+ SHELL.stop();
+ SHELL = null;
+ }
}
diff --git a/gclc-swt/pom.xml b/gclc-swt/pom.xml
index c800141..a41023e 100644
--- a/gclc-swt/pom.xml
+++ b/gclc-swt/pom.xml
@@ -61,7 +61,7 @@
- *
+ *
* @author Emmanuel Bigeon */
-public class SWTConsole extends Composite implements ConsoleManager, CommandRequestListener {
+public class SWTConsole extends Composite implements ConsoleManager {
+ /** A key listener to validate commands and manage the history of commands
+ *
+ * @author Emmanuel Bigeon */
+ public static final class HistoryTextKeyListener extends KeyAdapter {
+
+ /** The size of commands history */
+ private static final int DEFAULT_HISTORY_SIZE = 10;
+ /** The history ribbon */
+ private final Ribbon
- *
+ *
* @author Emmanuel Bigeon */
public class SWTConsoleShell extends Shell {
/** The console component */
- private final SWTConsole console;
+ private SWTConsole console;
/** Create the shell.
- *
+ *
* @param display the display */
public SWTConsoleShell(Display display) {
super(display, SWT.SHELL_TRIM);
setLayout(new FillLayout(SWT.HORIZONTAL));
- console = new SWTConsole(this, SWT.NONE);
createContents();
}
- /**
- * Create contents of the shell.
- */
- protected void createContents() {
- setText("Console Application"); //$NON-NLS-1$
- setSize(450, 300);
- }
-
@Override
protected void checkSubclass() {
// Disable the check that prevents subclassing of SWT components
}
+ /** Create contents of the shell. */
+ protected void createContents() {
+ console = new SWTConsole(this, SWT.NONE);
+ setText("Console Application"); //$NON-NLS-1$
+ }
+
/** @return the console manager */
public ConsoleManager getManager() {
return console;
}
-
- /** @return the element awaiting commands */
- public CommandRequestListener getCommandListener() {
- return console;
- }
}
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
index 15e96e8..f706949 100644
--- a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/AppTest.java
+++ b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/AppTest.java
@@ -38,45 +38,55 @@ import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
-/**
- * Unit test for simple App.
- */
-public class AppTest
- extends TestCase
-{
- /**
- * Create the test case
+/** 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 );
+ * @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 );
+ /** @return the suite of tests being tested */
+ public static Test suite() {
+ return new TestSuite(AppTest.class);
}
- /**
- * Rigourous Test :-)
- */
+ /** Rigourous Test :-) */
@SuppressWarnings("static-method")
- public void testApp()
- {
+ 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));
+// swtConsole.setLayoutData(
+// new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
// final ConsoleApplication appl = new ConsoleApplication(swtConsole,
-// "exit",
-// "Hello", "See you");
+// "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() {
@@ -94,9 +104,11 @@ public class AppTest
// });
// applThread.start();
// while (!shell.isDisposed()) {
-// if (!display.readAndDispatch()) display.sleep();
+// if (!display.readAndDispatch()) {
+// display.sleep();
+// }
// }
// display.dispose();
- assertTrue( true );
+ assertTrue(true);
}
}
diff --git a/gclc/src/main/java/fr/bigeon/gclc/CommandRequestListener.java b/gclc/src/main/java/fr/bigeon/gclc/CommandRequestListener.java
index 3a0a5be..6917341 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/CommandRequestListener.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/CommandRequestListener.java
@@ -45,7 +45,7 @@ package fr.bigeon.gclc;
* @author Emmanuel Bigeon */
public interface CommandRequestListener {
/** Indicates that the given command was requested to the application
- *
+ *
* @param command the command */
void commandRequest(String command);
}
diff --git a/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java b/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java
index f42fce7..49c171b 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java
@@ -39,26 +39,31 @@ package fr.bigeon.gclc;
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.Command;
import fr.bigeon.gclc.command.HelpExecutor;
+import fr.bigeon.gclc.command.ICommand;
import fr.bigeon.gclc.command.ICommandProvider;
import fr.bigeon.gclc.command.SubedCommand;
import fr.bigeon.gclc.command.UnrecognizedCommand;
+import fr.bigeon.gclc.exception.CommandParsingException;
import fr.bigeon.gclc.exception.CommandRunException;
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;
-import fr.bigeon.gclc.system.SystemConsoleManager;
/**
* A {@link ConsoleApplication} is an application that require the user to input
* commands.
*
* A typical use case is the following:
- *
+ *
*
@@ -70,6 +75,9 @@ import fr.bigeon.gclc.system.SystemConsoleManager;
* @author Emmanuel BIGEON */
public class ConsoleApplication implements ICommandProvider {
+ /** The class logger */
+ private static final Logger LOGGER = Logger
+ .getLogger(ConsoleApplication.class.getName());
/** The welcome message */
private final String header;
/** The good bye message */
@@ -98,132 +106,42 @@ public class ConsoleApplication implements ICommandProvider {
* @param exit the keyword for the exit command of this application
* @param welcome the header message to display on launch of this
* application
- * @param goodbye the message to display on exit */
+ * @param goodbye the message to display on exit
+ * @throws InvalidCommandName if the exit command name is invalid */
public ConsoleApplication(ConsoleManager manager, String exit,
- String welcome, String goodbye) {
+ String welcome, String goodbye) throws InvalidCommandName {
this(manager, welcome, goodbye);
- try {
- root.add(new ExitCommand(exit, this));
- } catch (InvalidCommandName e) {
- throw new RuntimeException("Invalid exit command name", e); //$NON-NLS-1$
- }
+ root.add(new ExitCommand(exit, this));
}
/** @param exit the keyword for the exit command of this application
* @param welcome the header message to display on launch of this
* application
- * @param goodbye the message to display on exit */
- public ConsoleApplication(String exit, String welcome, String goodbye) {
+ * @param goodbye the message to display on exit
+ * @throws InvalidCommandName if the exit command name is invalid */
+ public ConsoleApplication(String exit, String welcome,
+ String goodbye) throws InvalidCommandName {
this(new SystemConsoleManager(), welcome, goodbye);
- try {
- root.add(new ExitCommand(exit, this));
- } catch (InvalidCommandName e) {
- throw new RuntimeException("Invalid exit command name", e); //$NON-NLS-1$
- }
+ root.add(new ExitCommand(exit, this));
}
@Override
- public final boolean add(Command cmd) throws InvalidCommandName {
+ public final boolean add(ICommand cmd) throws InvalidCommandName {
return root.add(cmd);
}
- /** Launches the prompting application */
- public final void start() {
- if (header != null) manager.println(header);
- running = true;
- do {
- String cmd = manager.prompt();
- if (cmd.isEmpty()) {
- continue;
- }
- for (CommandRequestListener listener : listeners) {
- listener.commandRequest(cmd);
- }
- interpretCommand(cmd);
- } while (running);
- if (footer != null) manager.println(footer);
- }
-
- /** @param cmd the command to interpret */
- public final void interpretCommand(String cmd) {
- List
* A command to exit a {@link ConsoleApplication}.
*
* @author Emmanuel BIGEON */
-class ExitCommand extends Command {
+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) {
- super(name);
+ this.name = name;
this.app = app;
}
- @Override
- public String tip() {
- return CLIPrompterMessages.getString(EXIT);
- }
-
- @Override
- public void help(ConsoleManager manager, String... args) {
- manager.println(CLIPrompterMessages
- .getString(EXIT_MAN, (Object[]) args));
+ /** The actions to take before exiting */
+ public void beforeExit() {
+ // Do nothing by default
}
@Override
@@ -292,8 +313,21 @@ class ExitCommand extends Command {
app.exit();
}
- /** The actions to take before exiting */
- public void beforeExit() {
- // Do nothing by default
+ /* (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);
}
}
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 6e24c45..2fba973 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/command/Command.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/command/Command.java
@@ -38,7 +38,7 @@
*/
package fr.bigeon.gclc.command;
-import fr.bigeon.gclc.ConsoleManager;
+import fr.bigeon.gclc.manager.ConsoleManager;
/**
* A command to execute. It is mandatory that it has a name and that name cannot
@@ -59,12 +59,12 @@ import fr.bigeon.gclc.ConsoleManager;
*
* The default behavior for the brief message is to print the tip preceeded by a
* couple of spaces.
- *
+ *
* @author Emmanuel BIGEON */
-public abstract class Command {
+public abstract class Command implements ICommand {
/**
- *
+ *
*/
private static final String EOL_LINUX = "\n"; //$NON-NLS-1$
/** The name of the command */
@@ -76,41 +76,34 @@ public abstract class Command {
this.name = name;
}
- /** @return the command's name */
+ /** @return a brief description of the command */
+ protected String brief() {
+ return " " + tip(); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.command.ICommand#getCommandName() */
+ @Override
public final String getCommandName() {
return name;
}
- /** @param args the arguments of the command (some expect an empty array) */
- public abstract void execute(String... args);
-
- /** This prints the help associated to this command.
- *
- * The default behavior is to print:
- *
- *
* This method return the detail of the help. It immediatly follows the
* {@link #usagePattern() usage pattern}.
- *
+ *
* @return the detailed help (should end with end of line or be empty) */
@SuppressWarnings("static-method")
protected String usageDetail() {
@@ -129,17 +122,9 @@ public abstract class Command {
/**
* This prints the usage pattern for the command. It follows the brief
* introduction on the command ({@link #brief()})
- *
+ *
* @return the usage pattern */
protected String usagePattern() {
return getCommandName();
}
-
- /** @return a brief description of the command */
- protected String brief() {
- return " " + tip(); //$NON-NLS-1$
- }
-
- /** @return a tip on the command */
- public abstract String tip();
}
diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/CommandParameters.java b/gclc/src/main/java/fr/bigeon/gclc/command/CommandParameters.java
index 968c440..aec0b64 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/command/CommandParameters.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/command/CommandParameters.java
@@ -42,6 +42,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -50,10 +51,14 @@ import java.util.Set;
*
* @author Emmanuel BIGEON */
public class CommandParameters {
+ /**
+ *
+ */
+ private static final int STRINGARG_NUMBER_OF_ELEMENTS = 2;
/** Boolean arguments */
- private final HashMap
+ * This method return 0 if the parsing was incorrect, or the number of
+ * parsed elements.
+ *
+ * @param arg the argument
+ * @param next the next element
+ * @return the number of element read */
+ private int parseArg(String arg, String next) {
+ if (!arg.startsWith("-")) { //$NON-NLS-1$
+ return 1;
+ }
+ String name = arg.substring(1);
+ if (boolArgs.containsKey(name)) {
+ boolArgs.put(name, Boolean.TRUE);
+ return 1;
+ }
+ if (stringArgs.containsKey(name)) {
+ return parseStringArg(name, next);
+ }
+ if (strict) {
+ return 0;
+ }
+ additional.add(name);
+ return 1;
+ }
+
+ /** Add a string arg value
+ *
+ * @param name the string arg name
+ * @param next the string arg value
+ * @return 2 or 0 if next is invalid */
+ private int parseStringArg(String name, String next) {
+ if (next == null) {
+ return 0;
+ }
+ stringArgs.put(name, next);
+ return STRINGARG_NUMBER_OF_ELEMENTS;
}
/** @param string the key
@@ -136,4 +164,12 @@ public class CommandParameters {
boolArgs.put(string, value);
}
}
+
+ /** @param string the key
+ * @param value the value */
+ public void set(String string, String value) {
+ if (stringArgs.containsKey(string)) {
+ stringArgs.put(string, value);
+ }
+ }
}
diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/CommandProvider.java b/gclc/src/main/java/fr/bigeon/gclc/command/CommandProvider.java
index d0cdec0..e9152a7 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/command/CommandProvider.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/command/CommandProvider.java
@@ -46,37 +46,29 @@ import fr.bigeon.gclc.exception.InvalidCommandName;
*
* @author Emmanuel BIGEON */
public class CommandProvider implements ICommandProvider {
+ /** The minus character */
+ private static final String MINUS = "-"; //$NON-NLS-1$
+ /** The space character */
+ private static final String SPACE = " "; //$NON-NLS-1$
/** The commands map */
- protected final Set
- * TODO
+/** A command to print help of an other command.
+ *
+ * This command will display the help of an other command
*
* @author Emmanuel BIGEON */
public class HelpExecutor extends Command {
/** The command to execute the help of */
- private final Command cmd;
+ private final ICommand cmd;
/** The console manager */
private final ConsoleManager consoleManager;
/** @param cmdName the command name
* @param consoleManager the manager for the console
* @param cmd the command to execute the help of */
- public HelpExecutor(String cmdName, ConsoleManager consoleManager, Command cmd) {
+ public HelpExecutor(String cmdName, ConsoleManager consoleManager,
+ ICommand cmd) {
super(cmdName);
this.cmd = cmd;
- if (consoleManager == null) throw new NullPointerException("Argument cannot be null: ConsoleManager"); //$NON-NLS-1$
+ if (consoleManager == null) {
+ throw new NullPointerException(
+ "Argument cannot be null: ConsoleManager"); //$NON-NLS-1$
+ }
this.consoleManager = consoleManager;
}
@@ -70,15 +75,17 @@ public class HelpExecutor extends Command {
}
/* (non-Javadoc)
- * @see fr.bigeon.gclc.command.Command#help() */
+ * @see fr.bigeon.gclc.command.Command#brief() */
@Override
- public void help(ConsoleManager manager, String... args) {
- manager.println(getCommandName());
- manager.println(" A command to get help for other commands"); //$NON-NLS-1$
- manager.println();
- manager.println("Usage"); //$NON-NLS-1$
- manager.println(" " + getCommandName() + "
+ * This interface describe the contract of commands
+ *
+ * @author Emmanuel Bigeon */
+public interface ICommand {
+
+ /** @param args the arguments of the command (some expect an empty array) */
+ void execute(String... args);
+
+ /** @return the command's name */
+ String getCommandName();
+
+ /** This prints the help associated to this command.
+ *
+ * The default behavior is to print:
+ *
+ *
- * This method provide the command with the given name found. If no command
- * with this name is found, an error command is usually returned. If there
- * are several commands with the same name, the behavior is unspecified.
- * Depending on the implementation, it may return an error command or the
- * first command with this name found.
- *
- * @param command the name of the command the user wishes to execute
- * @return the command to execute */
- public Command get(String command);
-
/**
* Adds a command to this provider, if no command was associated with the
* given key
- *
+ *
* @param value the command to execute
* @return if the command was added
* @throws InvalidCommandName if the command name is invalid */
- public boolean add(Command value) throws InvalidCommandName;
+ public boolean add(ICommand value) throws InvalidCommandName;
/**
* This method executes the command with the given name found. If no command
@@ -71,9 +60,20 @@ public interface ICommandProvider {
* are several commands with the same name, the behavior is unspecified.
* Depending on the implementation, it may run an error command or prompt
* the user for a choice.
- *
+ *
* @param command the name of the command the user wishes to execute
* @param args the arguments for the command */
public void executeSub(String command, String... args);
+ /**
+ * This method provide the command with the given name found. If no command
+ * with this name is found, an error command is usually returned. If there
+ * are several commands with the same name, the behavior is unspecified.
+ * Depending on the implementation, it may return an error command or the
+ * first command with this name found.
+ *
+ * @param command the name of the command the user wishes to execute
+ * @return the command to execute */
+ public ICommand get(String command);
+
}
\ No newline at end of file
diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java b/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java
index e72ed85..141f7fa 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java
@@ -43,7 +43,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import fr.bigeon.gclc.ConsoleManager;
+import fr.bigeon.gclc.manager.ConsoleManager;
/**
* A command relying on the {@link CommandParameters} to store parameters values
@@ -51,7 +51,8 @@ import fr.bigeon.gclc.ConsoleManager;
* @author Emmanuel BIGEON */
public abstract class ParametrizedCommand extends Command {
- /** If the command may use interactive prompting for required parameters that
+ /** If the command may use interactive prompting for required parameters
+ * that
* were not provided on execution */
private boolean interactive = true;
/** The manager */
@@ -62,7 +63,8 @@ public abstract class ParametrizedCommand extends Command {
private final Map
* Add a parameter to the defined parameters
- *
+ *
* @param param the parameter identification
* @param stringOrBool if the parameter is a parameter with an argument
* @param needed if the parameter is required */
@@ -106,27 +108,32 @@ public abstract class ParametrizedCommand extends Command {
}
}
+ /** @param parameters the command parameters */
+ protected abstract void doExecute(CommandParameters parameters);
+
/* (non-Javadoc)
* @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */
@SuppressWarnings("boxing")
@Override
public final void execute(String... args) {
- CommandParameters parameters = new CommandParameters(
+ 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;
}
- List
* A subed command is a command that can execute sub commands depending on the
* first argument.
*
* @author Emmanuel BIGEON */
-public class SubedCommand extends Command implements ICommandProvider {
+public class SubedCommand implements ICommandProvider, ICommand {
+ /** The tab character */
+ private static final String TAB = "\t"; //$NON-NLS-1$
/**
* The command to execute when this command is called with no sub arguments.
* This may be null, in which case the command should have arguments. */
- private final Command noArgCommand;
+ private final ICommand noArgCommand;
/** A tip on this command. */
private final String tip;
/** The provider */
private final CommandProvider provider;
+ /** 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, Command error) {
- super(name);
+ public SubedCommand(String name, ICommand error) {
+ this.name = name;
provider = new CommandProvider(error);
noArgCommand = null;
tip = null;
}
- /** @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, Command error, String tip) {
- super(name);
- provider = new CommandProvider(error);
- noArgCommand = null;
- this.tip = tip;
- }
-
- /** @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, Command error, Command noArgCommand,
- String tip) {
- super(name);
- provider = new CommandProvider(error);
- this.noArgCommand = noArgCommand;
- this.tip = tip;
- }
-
/** @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, Command error, Command noArgCommand) {
- super(name);
+ public SubedCommand(String name, ICommand error, ICommand noArgCommand) {
+ this.name = name;
provider = new CommandProvider(error);
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,
+ String tip) {
+ this.name = name;
+ provider = new CommandProvider(error);
+ 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) {
+ this.name = name;
+ provider = new CommandProvider(error);
+ noArgCommand = null;
+ this.tip = tip;
+ }
+
+ @Override
+ public boolean add(ICommand value) throws InvalidCommandName {
+ return provider.add(value);
+ }
+
/* (non-Javadoc)
* @see fr.bigeon.acide.Command#execute(java.lang.String[]) */
@Override
public void execute(String... args) {
if (args.length == 0 || args[0].startsWith("-")) { //$NON-NLS-1$
- if (noArgCommand != null)
+ if (noArgCommand != null) {
noArgCommand.execute(args);
- else provider.error.execute(args);
- } else {
- executeSub(args[0], Arrays.copyOfRange(args, 1, args.length));
- }
- }
-
- /* (non-Javadoc)
- * @see fr.bigeon.gclc.command.Command#help() */
- @Override
- public void help(ConsoleManager manager, String... args) {
- if (args.length != 0 && !args[0].startsWith("-")) { //$NON-NLS-1$
- // Specific
- Command c = get(args[0]);
- if (c != null)
- c.help(manager, Arrays.copyOfRange(args, 1, args.length));
- else {
+ } else {
provider.error.execute(args);
}
} else {
- // Generic
- if (noArgCommand != null)
- if (noArgCommand.tip() != null)
- manager.println("\t" + noArgCommand.tip()); //$NON-NLS-1$
- for (Command cmd : provider.commands) {
- if (cmd.tip() == null)
- manager.println("\t" + cmd.getCommandName()); //$NON-NLS-1$
- else manager.println("\t" + cmd.getCommandName() + ": " + //$NON-NLS-1$ //$NON-NLS-2$
- cmd.tip());
- }
+ executeSub(args[0], Arrays.copyOfRange(args, 1, args.length));
}
}
- /* (non-Javadoc)
- * @see fr.bigeon.gclc.command.Command#tip() */
- @Override
- public String tip() {
- return tip;
- }
-
- @Override
- public Command get(String commandName) {
- return provider.get(commandName);
- }
-
- @Override
- public boolean add(Command value) throws InvalidCommandName {
- return provider.add(value);
- }
-
/* (non-Javadoc)
* @see
* fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String,
@@ -164,6 +132,53 @@ public class SubedCommand extends Command implements ICommandProvider {
provider.executeSub(command, args);
}
+ @Override
+ public ICommand get(String commandName) {
+ return provider.get(commandName);
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.command.ICommand#getCommandName() */
+ @Override
+ public String getCommandName() {
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.command.Command#help() */
+ @Override
+ public void help(ConsoleManager manager, String... args) {
+ 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());
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.command.Command#tip() */
+ @Override
+ public String tip() {
+ return tip;
+ }
+
/* (non-Javadoc)
* @see java.lang.Object#toString() */
@Override
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 c9222f2..fc82832 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/command/UnrecognizedCommand.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/command/UnrecognizedCommand.java
@@ -38,14 +38,14 @@
*/
package fr.bigeon.gclc.command;
-import fr.bigeon.gclc.ConsoleManager;
+import fr.bigeon.gclc.manager.ConsoleManager;
import fr.bigeon.gclc.prompt.CLIPrompterMessages;
/**
* The error message for unrecognized commands
*
* @author Emmanuel BIGEON */
-public final class UnrecognizedCommand extends Command {
+public final class UnrecognizedCommand implements ICommand {
/** The unrecognized command key */
private static final String UNRECOGNIZED_CMD = "unrecognized.cmd"; //$NON-NLS-1$
/** The unrecognized command key */
@@ -55,7 +55,6 @@ public final class UnrecognizedCommand extends Command {
/** @param manager the console manager to use */
public UnrecognizedCommand(ConsoleManager manager) {
- super(new String());
if (manager == null) {
throw new NullPointerException("The argument cannot be null"); //$NON-NLS-1$
}
@@ -63,8 +62,20 @@ public final class UnrecognizedCommand extends Command {
}
@Override
- public String tip() {
- return null;
+ public void execute(String... args) {
+ if (args.length > 0) {
+ manager.println(CLIPrompterMessages.getString(UNRECOGNIZED_CMD,
+ (Object[]) args));
+ } else {
+ manager.println(CLIPrompterMessages.getString(EXPECTED_CMD));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.command.ICommand#getCommandName() */
+ @Override
+ public String getCommandName() {
+ return ""; //$NON-NLS-1$
}
@Override
@@ -75,10 +86,7 @@ public final class UnrecognizedCommand extends Command {
}
@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 String tip() {
+ return null;
}
}
\ No newline at end of file
diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/package-info.java b/gclc/src/main/java/fr/bigeon/gclc/command/package-info.java
index f1dfae2..d24d6b3 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/command/package-info.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/command/package-info.java
@@ -35,8 +35,17 @@
/** gclc:fr.bigeon.gclc.command.package-info.java
* Created on: Sep 6, 2014 */
-/**
- * TODO
+/** This package groups elements related to
+ * {@link fr.bigeon.gclc.command.ICommand}
+ *
+ * There are some implementations, such as the
+ * {@link fr.bigeon.gclc.command.ParametrizedCommand} for commands with a
+ * predefined set of flags and option taking a string as value, the
+ * {@link fr.bigeon.gclc.command.SubedCommand} for a command that is declined in
+ * a set of sub commands, the {@link fr.bigeon.gclc.command.HelpExecutor} for
+ * help display of other commands and the
+ * {@link fr.bigeon.gclc.command.UnrecognizedCommand} that should not be
+ * directly accessible to the user, but will be used in case of error in typing.
*
* @author Emmanuel BIGEON */
package fr.bigeon.gclc.command;
\ No newline at end of file
diff --git a/gclc/src/main/java/fr/bigeon/gclc/system/package-info.java b/gclc/src/main/java/fr/bigeon/gclc/exception/CommandParsingException.java
similarity index 68%
rename from gclc/src/main/java/fr/bigeon/gclc/system/package-info.java
rename to gclc/src/main/java/fr/bigeon/gclc/exception/CommandParsingException.java
index 445b10f..2091f6a 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/system/package-info.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/exception/CommandParsingException.java
@@ -33,14 +33,33 @@
* knowledge of the CeCILL license and that you accept its terms.
*/
/**
- * gclc:fr.bigeon.gclc.system.package-info.java
- * Created on: Dec 19, 2014
+ * gclc:fr.bigeon.gclc.exception.CommandParsingException.java
+ * Created on: Jun 1, 2016
*/
-/**
- *
- * The basic system based console manager elements
+package fr.bigeon.gclc.exception;
+
+/** An exception raised during command parsing
*
- * @author Emmanuel BIGEON
- *
- */
-package fr.bigeon.gclc.system;
\ No newline at end of file
+ * @author Emmanuel Bigeon */
+public class CommandParsingException extends Exception {
+
+ /** svuid */
+ private static final long serialVersionUID = 1L;
+
+ /** @param message an explaination
+ * @param cause the cause */
+ public CommandParsingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /** @param message an explaination */
+ public CommandParsingException(String message) {
+ super(message);
+ }
+
+ /** @param cause the cause */
+ public CommandParsingException(Throwable cause) {
+ super(cause);
+ }
+
+}
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 8a31ae5..35a14a5 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/exception/CommandRunException.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/exception/CommandRunException.java
@@ -45,21 +45,21 @@ package fr.bigeon.gclc.exception;
public class CommandRunException extends RuntimeException {
/**
- *
+ *
*/
private static final long serialVersionUID = 1L;
+ /** @param message a message */
+ public CommandRunException(String message) {
+ super(message);
+ }
+
/** @param message a message
* @param cause the cause */
public CommandRunException(String message, Throwable cause) {
super(message, cause);
}
- /** @param message a message */
- public CommandRunException(String message) {
- super(message);
- }
-
/* (non-Javadoc)
* @see java.lang.Throwable#getLocalizedMessage() */
@Override
diff --git a/gclc/src/main/java/fr/bigeon/gclc/exception/InvalidCommandName.java b/gclc/src/main/java/fr/bigeon/gclc/exception/InvalidCommandName.java
index 2d2cd54..8e7d46f 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/exception/InvalidCommandName.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/exception/InvalidCommandName.java
@@ -46,7 +46,7 @@ package fr.bigeon.gclc.exception;
public class InvalidCommandName extends Exception {
/**
- *
+ *
*/
private static final long serialVersionUID = 1L;
diff --git a/gclc/src/main/java/fr/bigeon/gclc/i18n/Messages.java b/gclc/src/main/java/fr/bigeon/gclc/i18n/Messages.java
new file mode 100644
index 0000000..097b828
--- /dev/null
+++ b/gclc/src/main/java/fr/bigeon/gclc/i18n/Messages.java
@@ -0,0 +1,81 @@
+/*
+ * 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.i18n.Messages.java
+ * Created on: Jun 1, 2016
+ */
+package fr.bigeon.gclc.i18n;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/** Internationalization class.
+ *
+ * @author Emmanuel Bigeon */
+public class Messages {
+ /** The resource bundle name */
+ private static final String BUNDLE_NAME = "fr.bigeon.gclc.l10n.messages"; //$NON-NLS-1$
+
+ /** The resource bundle */
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
+ .getBundle(BUNDLE_NAME);
+
+ /** The class logger */
+ private static final Logger LOGGER = Logger
+ .getLogger(Messages.class.getName());
+
+ /** Utility class */
+ private Messages() {
+ // Utility class
+ }
+
+ /** Get formatted internationalized messages
+ *
+ * @param key the message key
+ * @param args the formatting arguments
+ * @return the formatted internationalized message */
+ public static String getString(String key, Object... args) {
+ try {
+ return MessageFormat.format(RESOURCE_BUNDLE.getString(key), args);
+ } catch (MissingResourceException e) {
+ LOGGER.log(Level.WARNING,
+ "Unrecognized internationalization message key: " + key, e); //$NON-NLS-1$
+ return '!' + key + '!';
+ }
+ }
+}
diff --git a/gclc/src/main/java/fr/bigeon/gclc/ConsoleManager.java b/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java
similarity index 93%
rename from gclc/src/main/java/fr/bigeon/gclc/ConsoleManager.java
rename to gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java
index 24ce4a1..37cc72e 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/ConsoleManager.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java
@@ -36,7 +36,9 @@
* gclc:fr.bigeon.gclc.ConsoleManager.java
* Created on: Dec 19, 2014
*/
-package fr.bigeon.gclc;
+package fr.bigeon.gclc.manager;
+
+import java.io.IOException;
/**
* A console manager is in charge of the basic prompts and prints on a console.
@@ -47,28 +49,31 @@ package fr.bigeon.gclc;
* @author Emmanuel BIGEON */
public interface ConsoleManager {
- /**
- * Set a prompting prefix.
- *
- * @param prompt the prompt */
- void setPrompt(String prompt);
-
/** @return the prompt prefix */
String getPrompt();
+ /** @param text the message to print (without line break at the end). */
+ void print(String text);
+
+ /** Prints an end of line */
+ void println();
+
+ /** @param message the message to print */
+ void println(String message);
+
/** @return the user inputed string */
String prompt();
/** @param message the message to prompt the user
* @return the user inputed string */
String prompt(String message);
-
- /** @param message the message to print */
- void println(String message);
-
- /** Prints an end of line */
- void println();
- /** @param text the message to print (without line break at the end). */
- void print(String text);
+ /**
+ * Set a prompting prefix.
+ *
+ * @param prompt the prompt */
+ void setPrompt(String prompt);
+
+ /** @throws IOException if the close raised an exception */
+ void close() throws IOException;
}
diff --git a/gclc/src/main/java/fr/bigeon/gclc/system/SystemConsoleManager.java b/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java
similarity index 64%
rename from gclc/src/main/java/fr/bigeon/gclc/system/SystemConsoleManager.java
rename to gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java
index 8012ce3..286edb6 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/system/SystemConsoleManager.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java
@@ -36,33 +36,107 @@
* gclc:fr.bigeon.gclc.system.SystemConsoleManager.java
* Created on: Dec 19, 2014
*/
-package fr.bigeon.gclc.system;
+package fr.bigeon.gclc.manager;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
-import fr.bigeon.gclc.ConsoleManager;
-
-/**
+/** A console using the input stream and print stream.
*
- * TODO
+ * The default constructor will use the system standart input and output.
*
- * @author Emmanuel BIGEON
- *
- */
-public class SystemConsoleManager implements ConsoleManager {
+ * @author Emmanuel BIGEON */
+public class SystemConsoleManager implements ConsoleManager { // NOSONAR
/** The default prompt */
public static final String DEFAULT_PROMPT = ">"; //$NON-NLS-1$
+ /** The logger */
+ private static final Logger LOGGER = Logger
+ .getLogger(SystemConsoleManager.class.getName());
+
/** The command prompt. It can be changed. */
private String prompt = DEFAULT_PROMPT;
+ /** The print stream */
+ private final PrintStream out;
+ /** The input stream */
+ private final InputStream in;
+
+ /** This default constructor relies on the system defined standart output
+ * and input stream. */
+ public SystemConsoleManager() {
+ out = System.out; // NOSONAR
+ in = System.in;
+ }
+
+ /** @param out the output stream
+ * @param in the input stream */
+ public SystemConsoleManager(PrintStream out, InputStream in) {
+ super();
+ this.out = out;
+ this.in = in;
+ }
+
/** @return the prompt */
@Override
public String getPrompt() {
return prompt;
}
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.ConsoleManager#print(java.lang.Object) */
+ @Override
+ public void print(String object) {
+ out.print(object);
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.ConsoleManager#println() */
+ @Override
+ public void println() {
+ out.println();
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.ConsoleManager#println(java.lang.Object) */
+ @Override
+ public void println(String object) {
+ out.println(object);
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.ConsoleManager#prompt() */
+ @Override
+ public String prompt() {
+ return prompt(new String() + prompt);
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */
+ @Override
+ public String prompt(String message) {
+ String result = new String();
+ 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();
+ }
+ } catch (final IOException e) {
+ LOGGER.log(Level.SEVERE, "Unable to read prompt", e); //$NON-NLS-1$
+ }
+ return result;
+ }
+
/** @param prompt the prompt to set */
@Override
public void setPrompt(String prompt) {
@@ -70,56 +144,10 @@ public class SystemConsoleManager implements ConsoleManager {
}
/* (non-Javadoc)
- * @see fr.bigeon.gclc.ConsoleManager#prompt()
- */
+ * @see fr.bigeon.gclc.manager.ConsoleManager#close() */
@Override
- public String prompt() {
- return prompt(new String() + prompt);
- }
-
- /* (non-Javadoc)
- * @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String)
- */
- @Override
- public String prompt(String message) {
- String result = new String();
- System.out.print(message + ' ');
- char c;
- try {
- c = (char) System.in.read();
- while (c != System.lineSeparator().charAt(0)) {
- result += c;
- c = (char) System.in.read();
- }
- while (System.in.available() != 0)
- System.in.read();
- } catch (IOException e) {
- e.printStackTrace();
- }
- return result;
- }
-
- /* (non-Javadoc)
- * @see fr.bigeon.gclc.ConsoleManager#println(java.lang.Object)
- */
- @Override
- public void println(String object) {
- System.out.println(object);
- }
-
- /* (non-Javadoc)
- * @see fr.bigeon.gclc.ConsoleManager#print(java.lang.Object)
- */
- @Override
- public void print(String object) {
- System.out.print(object);
- }
-
- /* (non-Javadoc)
- * @see fr.bigeon.gclc.ConsoleManager#println() */
- @Override
- public void println() {
- System.out.println();
+ public void close() throws IOException {
+ in.close();
}
}
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 3f03cbc..adf5d43 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/prompt/CLIPrompter.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/prompt/CLIPrompter.java
@@ -39,8 +39,10 @@ package fr.bigeon.gclc.prompt;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
-import fr.bigeon.gclc.ConsoleManager;
+import fr.bigeon.gclc.manager.ConsoleManager;
/**
* The {@link CLIPrompter} class is a utility class that provides method to
@@ -49,6 +51,14 @@ import fr.bigeon.gclc.ConsoleManager;
* @author Emmanuel BIGEON */
public class CLIPrompter {
+ /** message key for format error in prompting a choice */
+ private static final String PROMPTCHOICE_FORMATERR = "promptchoice.formaterr"; //$NON-NLS-1$
+ /** message key for out of bound error in prompting a choice */
+ private static final String PROMPTCHOICE_OUTOFBOUNDS = "promptchoice.outofbounds"; //$NON-NLS-1$
+ /** message key for first form of no in prompting a choice */
+ private static final String PROMPTBOOL_CHOICES_NO1 = "promptbool.choices.no1"; //$NON-NLS-1$
+ /** message key for first form of yes in prompting a choice */
+ private static final String PROMPTBOOL_CHOICES_YES1 = "promptbool.choices.yes1"; //$NON-NLS-1$
@SuppressWarnings("javadoc")
private static final String BOOL_CHOICES = "promptbool.choices"; //$NON-NLS-1$
@SuppressWarnings("javadoc")
@@ -57,203 +67,67 @@ public class CLIPrompter {
private static final String PROMPT = "prompt.lineprompt"; //$NON-NLS-1$
@SuppressWarnings("javadoc")
private static final String LIST_CHOICE_SEP = "promptlist.multi.sepkey"; //$NON-NLS-1$
+ /** The class logger */
+ private static final Logger LOGGER = Logger
+ .getLogger(CLIPrompter.class.getName());
- /** @param manager the manager
- * @param prompt the prompting message
- * @param reprompt the prompting message after empty input
- * @return the non empty input */
- public static String promptNonEmpty(ConsoleManager manager, String prompt,
- String reprompt) {
- String res = manager.prompt(prompt);
- while (res.isEmpty())
- res = manager.prompt(reprompt);
- return res;
- }
-
- /** Prompt for a text with several lines.
- *
- * @param manager the manager
- * @param message the prompting message
- * @param ender the ender character
- * @return the text */
- public static String promptLongText(ConsoleManager manager, String message,
- String ender) {
- manager.println(message +
- CLIPrompterMessages.getString(
- "promptlongtext.exit.dispkey", ender)); //$NON-NLS-1$
- String res = manager.prompt(PROMPT);
- String line = res;
- while (!line.equals(ender)) {
- line = manager.prompt(PROMPT);
- if (!line.equals(ender)) res += System.lineSeparator() + line;
- }
- return res.equals(ender) ? "" : res; //$NON-NLS-1$
- }
-
- /** Prompt for a text with several lines.
- *
- * @param manager the manager
- * @param message the prompting message
- * @return the text */
- public static String promptLongText(ConsoleManager manager, String message) {
- return promptLongText(manager, message,
- CLIPrompterMessages.getString("promptlongtext.exit.defaultkey")); //$NON-NLS-1$
+ /** Utility class */
+ private CLIPrompter() {
+ // Utility class
}
/** @param manager the manager
- * @param message the prompt message
- * @return the integer */
- public static int promptInteger(ConsoleManager manager, String message) {
- boolean still = true;
- int r = 0;
- while (still) {
- String result = manager.prompt(message);
- try {
- if (result.isEmpty()) {
- still = true;
- continue;
- }
- r = Integer.parseInt(result);
- still = false;
- } catch (Exception e) {
- still = true;
- }
+ * @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) */
+ private static int listChoices(ConsoleManager manager, List choices,
+ String cancel) {
+ int index = 0;
+ for (final U u : choices) {
+ manager.println(index++ + ") " + u); //$NON-NLS-1$
}
- return r;
+ if (cancel != null) {
+ manager.println(index + ") " + cancel); //$NON-NLS-1$
+ }
+ return index;
}
/** @param manager the manager
* @param message the prompting message
* @return the choice */
- public static boolean promptBoolean(ConsoleManager manager, String message) {
- String result = manager.prompt(message +
- CLIPrompterMessages.getString(BOOL_CHOICES));
+ public static boolean promptBoolean(ConsoleManager manager,
+ String message) {
+ String result = manager
+ .prompt(message + CLIPrompterMessages.getString(BOOL_CHOICES));
boolean first = true;
- String choices = CLIPrompterMessages
- .getString("promptbool.choices.yes1") + //$NON-NLS-1$
- ", " + //$NON-NLS-1$
- CLIPrompterMessages
- .getString("promptbool.choices.no1"); //$NON-NLS-1$
- while (!(result.equalsIgnoreCase(CLIPrompterMessages
- .getString("promptbool.choices.yes1")) || //$NON-NLS-1$
- CLIPrompterMessages
- .getString("promptbool.choices.no1").equalsIgnoreCase( //$NON-NLS-1$
- result) ||
- CLIPrompterMessages
- .getString("promptbool.choices.no2").equalsIgnoreCase( //$NON-NLS-1$
- result) || CLIPrompterMessages.getString(
- "promptbool.choices.yes2").equalsIgnoreCase(result))) { //$NON-NLS-1$
+ final String choices = CLIPrompterMessages
+ .getString(PROMPTBOOL_CHOICES_YES1) + ", " + //$NON-NLS-1$
+ CLIPrompterMessages
+ .getString(PROMPTBOOL_CHOICES_NO1);
+ while (!(result.equalsIgnoreCase(
+ CLIPrompterMessages.getString(PROMPTBOOL_CHOICES_YES1)) ||
+ CLIPrompterMessages.getString(PROMPTBOOL_CHOICES_NO1)
+ .equalsIgnoreCase(result) ||
+ CLIPrompterMessages.getString("promptbool.choices.no2") //$NON-NLS-1$
+ .equalsIgnoreCase(result) ||
+ CLIPrompterMessages.getString("promptbool.choices.yes2") //$NON-NLS-1$
+ .equalsIgnoreCase(result))) {
if (!first) {
-
- manager.println(CLIPrompterMessages.getString(
- "promptbool.choices.invalid", choices)); //$NON-NLS-1$
- result = manager.prompt(message +
- CLIPrompterMessages.getString(BOOL_CHOICES));
+
+ manager.println(CLIPrompterMessages
+ .getString("promptbool.choices.invalid", choices)); //$NON-NLS-1$
+ result = manager.prompt(
+ message + CLIPrompterMessages.getString(BOOL_CHOICES));
}
first = false;
}
- return result.equalsIgnoreCase(CLIPrompterMessages
- .getString("promptbool.choices.yes1")) || //$NON-NLS-1$
+ return result.equalsIgnoreCase(
+ CLIPrompterMessages.getString(PROMPTBOOL_CHOICES_YES1)) ||
result.equalsIgnoreCase(CLIPrompterMessages
.getString("promptbool.choices.yes2")); //$NON-NLS-1$
}
- /** @param manager the manager
- * @param The choices labels type
- * @param
+/**
* Utility class for the messages of the CLIPrompter
*
- * @author Emmanuel BIGEON
- */
+ * @author Emmanuel BIGEON */
public class CLIPrompterMessages {
- /**
- * The resource name
- */
+ /** The resource name */
private static final String BUNDLE_NAME = "fr.bigeon.gclc.messages"; //$NON-NLS-1$
- /**
- * The resource
- */
+ /** The resource */
private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
.getBundle(BUNDLE_NAME);
- /**
- * Utility class
- */
- private CLIPrompterMessages() {}
+ /** The logger */
+ private static final Logger LOGGER = Logger
+ .getLogger(CLIPrompterMessages.class.getName());
- /**
- * Return the formatted message corresponding to the given key
- *
+ /** Utility class */
+ private CLIPrompterMessages() {
+ // Utility constructor
+ }
+
+ /** Return the formatted message corresponding to the given key
+ *
* @param key the message's key
* @param args the arguments
- * @return the formatted message
- */
+ * @return the formatted message */
public static String getString(String key, Object... args) {
try {
return MessageFormat.format(RESOURCE_BUNDLE.getString(key), args);
- } catch (MissingResourceException e) {
+ } catch (final MissingResourceException e) {
+ LOGGER.log(Level.WARNING, "Unrecognized key: " + key, e); //$NON-NLS-1$
return '!' + key + '!';
}
}
diff --git a/gclc/src/main/java/fr/bigeon/gclc/prompt/package-info.java b/gclc/src/main/java/fr/bigeon/gclc/prompt/package-info.java
index 754630f..85ed457 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/prompt/package-info.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/prompt/package-info.java
@@ -34,8 +34,14 @@
*/
/** gclc:fr.bigeon.gclc.prompt.package-info.java
* Created on: Sep 6, 2014 */
-/**
- * TODO
+/** Client prompting related objects.
+ *
+ * This package is used for the formatting of prompts for the user. The
+ * {@link fr.bigeon.gclc.prompt.CLIPrompter} class provides utility methods to
+ * retrieve certain basic type of data from the user or to give list choices.
+ *
+ * The {@link fr.bigeon.gclc.prompt.CLIPrompterMessages} class is used for
+ * internationalization of the prompting methods.
*
* @author Emmanuel BIGEON */
package fr.bigeon.gclc.prompt;
\ No newline at end of file
diff --git a/gclc/src/main/java/fr/bigeon/gclc/tools/PrintUtils.java b/gclc/src/main/java/fr/bigeon/gclc/tools/PrintUtils.java
index 50533a1..0a138fb 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/tools/PrintUtils.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/tools/PrintUtils.java
@@ -41,27 +41,35 @@ package fr.bigeon.gclc.tools;
import java.util.ArrayList;
import java.util.List;
-/**
- *
- * TODO
+/** A tool class for printing text in a console.
*
- * @author Emmanuel BIGEON
- *
- */
+ * @author Emmanuel BIGEON */
public class PrintUtils {
+
+ /** The continuation dot string */
+ private static final String CONT_DOT = "..."; //$NON-NLS-1$
+ /** The continuation dot string length */
+ private static final int CONT_DOT_LENGTH = CONT_DOT.length();
+
+ /** Utility class */
+ private PrintUtils() {
+ // Utility class
+ }
+
/** @param text the text to print
* @param nbCharacters the number of characters of the resulting text
* @param indicateTooLong if an indication shell be given that the text
* didn't fit
* @return the text to print (will be of exactly nbCharacters). */
public static String print(String text, int nbCharacters,
- boolean indicateTooLong) {
+ boolean indicateTooLong) {
String res = text;
if (res.length() > nbCharacters) {
// Cut
if (indicateTooLong) {
// With suspension dots
- res = res.substring(0, nbCharacters - 3) + "..."; //$NON-NLS-1$
+ res = res.substring(0, nbCharacters - CONT_DOT_LENGTH) +
+ CONT_DOT;
} else {
res = res.substring(0, nbCharacters);
}
@@ -77,9 +85,10 @@ public class PrintUtils {
* @param i the length of the wrap
* @return the list of resulting strings */
public static List
* {@link ConsoleApplication} app = new {@link ConsoleApplication#ConsoleApplication(String, String, String) ConsoleApplication("exit", "welcome", "see you latter")};
- * app.{@link ConsoleApplication#add(Command) add}("my_command", new {@link Command MyCommand()});
+ * app.{@link ConsoleApplication#add(ICommand) add}("my_command", new {@link ICommand MyCommand()});
* app.start();
*
*
- * [Command name]
- * [brief message]
- *
- * Usage:
- * [Usage pattern]
- *
- * [Usage details]
- *
- *
- * @param manager the manager to print the data
- * @param args the arguments called with the help */
- public void help(ConsoleManager manager, String... args) {
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.command.ICommand#help(fr.bigeon.gclc.ConsoleManager,
+ * java.lang.String) */
+ @Override
+ public final void help(ConsoleManager manager, String... args) {
manager.println(getCommandName());
manager.println(brief());
manager.println();
manager.println("Usage:"); //$NON-NLS-1$
manager.println(usagePattern());
manager.println();
- String details = usageDetail();
+ final String details = usageDetail();
if (details != null && !details.isEmpty()) {
manager.print(details);
- if (!(details.endsWith(EOL_LINUX) || details.endsWith(System.lineSeparator()))) {
+ if (!(details.endsWith(EOL_LINUX) ||
+ details.endsWith(System.lineSeparator()))) {
manager.println();
}
}
@@ -119,7 +112,7 @@ public abstract class Command {
/**
+ * [Command name]
+ * [brief message]
+ *
+ * Usage:
+ * [Usage pattern]
+ *
+ * [Usage details]
+ *
+ *
+ * @param manager the manager to print the data
+ * @param args the arguments called with the help */
+ void help(ConsoleManager manager, String... args);
+
+ /** @return a tip on the command */
+ String tip();
+
+}
\ No newline at end of file
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 b4f888c..f217aa9 100644
--- a/gclc/src/main/java/fr/bigeon/gclc/command/ICommandProvider.java
+++ b/gclc/src/main/java/fr/bigeon/gclc/command/ICommandProvider.java
@@ -45,25 +45,14 @@ import fr.bigeon.gclc.exception.InvalidCommandName;
* @author Emmanuel BIGEON */
public interface ICommandProvider {
- /**