+ * This forwarding can be interrupted without closing the piped manager. + * + * @author Emmanuel Bigeon + * @deprecated since version 1.3.2 of gclc, this class has been integrated in + * the main content. */ +@Deprecated +public abstract class AOutputForwardRunnable implements Runnable { + + /** The class logger */ + private static final Logger LOGGER = Logger + .getLogger(AOutputForwardRunnable.class.getName()); + /** The default timeout (one tenth of second). */ + private static final long DEFAULT_TIMEOUT = 100; + /** The manager. */ + private final PipedConsoleManager manager; + /** The timeout */ + private final long timeout; + + /** Create a forward runnable with the given timeout. + *
+ * Short timeout will be very responsive to the application actual messages, + * but may use computation time if the application is not verbose. Long + * timeout will save computation time, but will read batches of messages at + * once if the application is verbose. The right length for the timeout is + * likely to depend on the application and the use of it. + *
+ * If you do not know what timeout length to use, please use the + * {@link #AOutputForwardRunnable(PipedConsoleManager)} constructor. + * + * @param manager the manager + * @param timeout the timeout between message requests. */ + public AOutputForwardRunnable(PipedConsoleManager manager, long timeout) { + super(); + this.manager = manager; + this.timeout = timeout; + } + + /** Create a forwarding runnable. + * + * @param manager the manager */ + public AOutputForwardRunnable(PipedConsoleManager manager) { + super(); + this.manager = manager; + timeout = DEFAULT_TIMEOUT; + } + + @Override + public void run() { + try { + while (isRunning()) { + while (isRunning() && !manager.available()) { + waitASec(); + } + if (!isRunning()) { + return; + } + String m = manager.readNextLine(); + forwardLine(m); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Unexpected problem in manager", //$NON-NLS-1$ + e); + } + } + + /** @param m the line to forward */ + protected abstract void forwardLine(String m); + + /** @return if the thread should keep running */ + protected abstract boolean isRunning(); + + /** a method to wait some time */ + protected void waitASec() { + try { + synchronized (this) { + wait(timeout); + } + } catch (InterruptedException e) { + LOGGER.log(Level.SEVERE, "Interrupted wait", //$NON-NLS-1$ + e); + return; + } + } + +} diff --git a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/ConsoleDelayIO.java b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/ConsoleDelayIO.java index 51630e4..daf47e3 100644 --- a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/ConsoleDelayIO.java +++ b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/ConsoleDelayIO.java @@ -38,8 +38,6 @@ */ package fr.bigeon.gclc.swt; -import fr.bigeon.gclc.manager.ConsoleManager; - /** This class represents an object used to send commands to a console * application. *
@@ -47,7 +45,7 @@ import fr.bigeon.gclc.manager.ConsoleManager; * and set, and then validate the input. * * @author Emmanuel Bigeon */ -public interface ConsoleDelayIO extends ConsoleManager { +public interface ConsoleDelayIO { /** Actually send the input as the prompt next input. */ void validateInput(); diff --git a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsole.java b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsole.java index 8dc47f3..f6232a0 100644 --- a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsole.java +++ b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsole.java @@ -53,12 +53,14 @@ import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import fr.bigeon.gclc.ConsoleApplication; +import fr.bigeon.gclc.manager.ConsoleManager; /** A SWT component to connect to gclc {@link ConsoleApplication} *
* * @author Emmanuel Bigeon */ -public class SWTConsole extends Composite implements ConsoleDelayIO { +public class SWTConsole extends Composite + implements ConsoleDelayIO, ConsoleManager { /** * */ @@ -352,4 +354,13 @@ public class SWTConsole extends Composite implements ConsoleDelayIO { return consoleInput.getText(); } + /* (non-Javadoc) + * @see fr.bigeon.gclc.manager.ConsoleManager#interruptPrompt() */ + @Override + public void interruptPrompt() { + synchronized (promptLock) { + promptLock.notify(); + } + } + } diff --git a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleView.java b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleView.java new file mode 100644 index 0000000..b5749c3 --- /dev/null +++ b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleView.java @@ -0,0 +1,204 @@ +/* + * 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. + */ +/** + * gclc-swt:fr.bigeon.gclc.swt.SWTConsole.java + * Created on: Apr 18, 2015 + */ +package fr.bigeon.gclc.swt; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Text; + +import fr.bigeon.gclc.ConsoleApplication; +import fr.bigeon.gclc.manager.PipedConsoleManager; + +/** A SWT component to connect to gclc {@link ConsoleApplication} + *
+ *
+ * @author Emmanuel Bigeon */
+public class SWTConsoleView extends Composite implements ConsoleDelayIO {
+ /** The local implementation of the forwarding runnable
+ *
+ * @author Emmanuel Bigeon */
+ @SuppressWarnings("deprecation")
+ private final class ToSWTConsoleForwarRunnable
+ extends AOutputForwardRunnable {
+ /** The running status */
+ private boolean running = true;
+
+ /** @param manager the manager */
+ public ToSWTConsoleForwarRunnable(PipedConsoleManager manager) {
+ super(manager);
+ }
+
+ @Override
+ protected void forwardLine(String m) {
+ appendConsoleOutput(m);
+ }
+
+ @Override
+ protected boolean isRunning() {
+ return running && !isDisposed();
+ }
+
+ /** @param running the running to set */
+ public void setRunning(boolean running) {
+ this.running = running;
+ }
+ }
+
+ /** The class logger */
+ private static final Logger LOGGER = Logger
+ .getLogger(SWTConsoleView.class.getName());
+ /** The console output text field */
+ private final Text consoleOutput;
+ /** The console input text field */
+ private final Text consoleInput;
+ /** The actual manager */
+ private PipedConsoleManager manager;
+ /** The forwarding runnable */
+ private ToSWTConsoleForwarRunnable forward;
+
+ /** Create the composite.
+ *
+ * @param parent the prent composite
+ * @param style the composite style */
+ public SWTConsoleView(Composite parent, int style) {
+ super(parent, style);
+
+ setLayout(new GridLayout(1, false));
+
+ consoleOutput = new Text(this, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP |
+ SWT.V_SCROLL | SWT.MULTI);
+ consoleOutput.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true,
+ 1, 1));
+ consoleOutput.setRedraw(true);
+ consoleOutput.addFocusListener(new FocusAdapter() {
+ @SuppressWarnings("synthetic-access")
+ @Override
+ public void focusGained(FocusEvent e) {
+ consoleInput.setFocus();
+ }
+ });
+
+ consoleInput = new Text(this, SWT.BORDER);
+ consoleInput.setLayoutData(
+ new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ consoleInput.addKeyListener(new HistoryTextKeyListener(this));
+ }
+
+ /** @param manager the manager to set */
+ public void setManager(PipedConsoleManager manager) {
+ this.manager = manager;
+ if (forward != null) {
+ forward.setRunning(false);
+ }
+ forward = new ToSWTConsoleForwarRunnable(manager);
+ Thread th = new Thread(forward, "gclcToSWT"); //$NON-NLS-1$
+ th.start();
+ }
+
+ /** @param next the next message */
+ protected void appendConsoleOutput(final String next) {
+ Display.getDefault().syncExec(new Runnable() {
+ @SuppressWarnings("synthetic-access")
+ @Override
+ public void run() {
+ consoleOutput.append(System.lineSeparator() + next);
+ }
+ });
+ }
+
+ /**
+ *
+ */
+ @Override
+ public void validateInput() {
+ try {
+ manager.type(getInput());
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, "Unable to input value to console", e); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ protected void checkSubclass() {
+ // Disable the check that prevents subclassing of SWT components
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Composite#setFocus() */
+ @Override
+ public boolean setFocus() {
+ return consoleInput.setFocus();
+ }
+
+ /** @param string the text */
+ public void setText(String string) {
+ consoleInput.setText(string);
+ }
+
+ /**
+ *
+ */
+ public void validateCommand() {
+ validateInput();
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.swt.ConsoleDelayIO#setInput(java.lang.String) */
+ @Override
+ public void setInput(String input) {
+ consoleInput.setText(input);
+ consoleInput.setSelection(input.length());
+ }
+
+ /* (non-Javadoc)
+ * @see fr.bigeon.gclc.swt.ConsoleDelayIO#getInput() */
+ @Override
+ public String getInput() {
+ return consoleInput.getText();
+ }
+}
diff --git a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/HistoryTextKeyListenerTest.java b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/HistoryTextKeyListenerTest.java
index da2bd2b..80b3760 100644
--- a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/HistoryTextKeyListenerTest.java
+++ b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/HistoryTextKeyListenerTest.java
@@ -40,8 +40,6 @@ package fr.bigeon.gclc.swt;
import static org.junit.Assert.assertEquals;
-import java.io.IOException;
-
import org.eclipse.swt.SWT;
import org.junit.Test;
@@ -58,51 +56,6 @@ public class HistoryTextKeyListenerTest {
ConsoleDelayIO io = new ConsoleDelayIO() {
private String input = "";
- @Override
- public void setPrompt(String prompt) {
- //
- }
-
- @Override
- public String prompt(String message) throws IOException {
- return null;
- }
-
- @Override
- public String prompt() throws IOException {
- return null;
- }
-
- @Override
- public void println(String message) throws IOException {
- //
- }
-
- @Override
- public void println() throws IOException {
- //
- }
-
- @Override
- public void print(String text) throws IOException {
- //
- }
-
- @Override
- public boolean isClosed() {
- return false;
- }
-
- @Override
- public String getPrompt() {
- return null;
- }
-
- @Override
- public void close() throws IOException {
- //
- }
-
@Override
public void validateInput() {
input = "";
diff --git a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java
index d57364b..5bcc1cf 100644
--- a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java
+++ b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java
@@ -48,6 +48,7 @@ import org.junit.Test;
import fr.bigeon.gclc.ConsoleApplication;
import fr.bigeon.gclc.command.Command;
+import fr.bigeon.gclc.command.ExitCommand;
import fr.bigeon.gclc.exception.CommandRunException;
import fr.bigeon.gclc.exception.InvalidCommandName;
@@ -69,7 +70,8 @@ public class SWTConsoleShellTest {
swtConsole.setPrompt(":");
try {
final ConsoleApplication appl = new ConsoleApplication(swtConsole,
- "exit", "Hello", "See you");
+ "Hello", "See you");
+ appl.add(new ExitCommand("exit", appl));
appl.add(new Command("long") {
@Override
@@ -197,7 +199,8 @@ public class SWTConsoleShellTest {
final SWTConsole swtConsole = (SWTConsole) shell.getManager();
try {
final ConsoleApplication appl = new ConsoleApplication(swtConsole,
- "exit", "Hello", "See you");
+ "Hello", "See you");
+ appl.add(new ExitCommand("exit", appl));
appl.add(new Command("long") {
@Override
diff --git a/gclc.system/pom.xml b/gclc.system/pom.xml
index 6f9c583..f8656c8 100644
--- a/gclc.system/pom.xml
+++ b/gclc.system/pom.xml
@@ -23,7 +23,7 @@
- * TODO +/** A command that will execute a system command. * * @author Emmanuel Bigeon */ public class ExecSystemCommand extends Command { @@ -79,7 +78,7 @@ public class ExecSystemCommand extends Command { } }); th.start(); - manager.setPrompt(""); + manager.setPrompt(""); //$NON-NLS-1$ final OutputStream os = proc.getOutputStream(); try (BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(os))) { diff --git a/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java b/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java index 18c4c8a..0fcf97b 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java +++ b/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java @@ -95,6 +95,11 @@ public interface ConsoleManager { boolean isClosed(); /** Indicate to the manager that is should interrompt the prompting, if - * possible. */ + * possible. + *
+ * The pending {@link #prompt()} or {@link #prompt(String)} operations + * should return immediatly. However the returned value can be anything + * (from the partial prompt content to an empty string or even a null + * pointer). */ void interruptPrompt(); } diff --git a/gclc/src/main/java/fr/bigeon/gclc/tools/AOutputForwardRunnable.java b/gclc/src/main/java/fr/bigeon/gclc/tools/AOutputForwardRunnable.java new file mode 100644 index 0000000..6bbde38 --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/tools/AOutputForwardRunnable.java @@ -0,0 +1,96 @@ +/** + * gclc:fr.bigeon.gclc.tools.AOutputForwardRunnable.java + * Created on: Dec 1, 2016 + */ +package fr.bigeon.gclc.tools; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import fr.bigeon.gclc.manager.PipedConsoleManager; + +/** An incomplete implematation used to forward messages from a piped console. + *
+ * This forwarding can be interrupted without closing the piped manager. + * + * @author Emmanuel Bigeon */ +public abstract class AOutputForwardRunnable implements Runnable { + + /** The class logger */ + private static final Logger LOGGER = Logger + .getLogger(AOutputForwardRunnable.class.getName()); + /** The default timeout (one tenth of second). */ + private static final long DEFAULT_TIMEOUT = 100; + /** The manager. */ + private final PipedConsoleManager manager; + /** The timeout */ + private final long timeout; + + /** Create a forward runnable with the given timeout. + *
+ * Short timeout will be very responsive to the application actual messages, + * but may use computation time if the application is not verbose. Long + * timeout will save computation time, but will read batches of messages at + * once if the application is verbose. The right length for the timeout is + * likely to depend on the application and the use of it. + *
+ * If you do not know what timeout length to use, please use the + * {@link #AOutputForwardRunnable(PipedConsoleManager)} constructor. + * + * @param manager the manager + * @param timeout the timeout between message requests. */ + public AOutputForwardRunnable(PipedConsoleManager manager, long timeout) { + super(); + this.manager = manager; + this.timeout = timeout; + } + + /** Create a forwarding runnable. + * + * @param manager the manager */ + public AOutputForwardRunnable(PipedConsoleManager manager) { + super(); + this.manager = manager; + timeout = DEFAULT_TIMEOUT; + } + + @Override + public void run() { + try { + while (isRunning()) { + while (isRunning() && !manager.available()) { + waitASec(); + } + if (!isRunning()) { + return; + } + String m = manager.readNextLine(); + forwardLine(m); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Unexpected problem in manager", //$NON-NLS-1$ + e); + } + } + + /** @param m the line to forward */ + protected abstract void forwardLine(String m); + + /** @return if the thread should keep running */ + protected abstract boolean isRunning(); + + /** a method to wait some time */ + protected void waitASec() { + try { + synchronized (this) { + wait(timeout); + } + } catch (InterruptedException e) { + LOGGER.log(Level.SEVERE, "Interrupted wait", //$NON-NLS-1$ + e); + return; + } + } + +} diff --git a/gclc/src/test/java/fr/bigeon/gclc/command/CommandParametersTest.java b/gclc/src/test/java/fr/bigeon/gclc/command/CommandParametersTest.java index 782259b..b60602d 100644 --- a/gclc/src/test/java/fr/bigeon/gclc/command/CommandParametersTest.java +++ b/gclc/src/test/java/fr/bigeon/gclc/command/CommandParametersTest.java @@ -55,6 +55,7 @@ import org.junit.Test; * @author Emmanuel Bigeon * */ +@SuppressWarnings({"static-method", "nls"}) public class CommandParametersTest { /**