From 18c7f895646ac086865d8e3a16d9e91d2db5c769 Mon Sep 17 00:00:00 2001 From: Emmanuel Bigeon Date: Thu, 1 Dec 2016 13:48:20 -0500 Subject: [PATCH] Made gclc-swt and -system compatible with gclc-1.3.1 Added an abstract runnable for output forwarding from piped output Signed-off-by: Emmanuel Bigeon --- gclc-swt/pom.xml | 2 +- .../gclc/swt/AOutputForwardRunnable.java | 99 +++++++++ .../fr/bigeon/gclc/swt/ConsoleDelayIO.java | 4 +- .../java/fr/bigeon/gclc/swt/SWTConsole.java | 13 +- .../fr/bigeon/gclc/swt/SWTConsoleView.java | 204 ++++++++++++++++++ .../gclc/swt/HistoryTextKeyListenerTest.java | 47 ---- .../bigeon/gclc/swt/SWTConsoleShellTest.java | 7 +- gclc.system/pom.xml | 2 +- .../bigeon/gclc/system/ExecSystemCommand.java | 5 +- .../bigeon/gclc/manager/ConsoleManager.java | 7 +- .../gclc/tools/AOutputForwardRunnable.java | 96 +++++++++ .../gclc/command/CommandParametersTest.java | 1 + 12 files changed, 428 insertions(+), 59 deletions(-) create mode 100644 gclc-swt/src/main/java/fr/bigeon/gclc/swt/AOutputForwardRunnable.java create mode 100644 gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleView.java create mode 100644 gclc/src/main/java/fr/bigeon/gclc/tools/AOutputForwardRunnable.java diff --git a/gclc-swt/pom.xml b/gclc-swt/pom.xml index 28ae9c8..818a18c 100644 --- a/gclc-swt/pom.xml +++ b/gclc-swt/pom.xml @@ -53,7 +53,7 @@ fr.bigeon gclc - 1.3.0 + 1.3.1 fr.bigeon diff --git a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/AOutputForwardRunnable.java b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/AOutputForwardRunnable.java new file mode 100644 index 0000000..d164d95 --- /dev/null +++ b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/AOutputForwardRunnable.java @@ -0,0 +1,99 @@ +/** + * gclc:fr.bigeon.gclc.tools.AOutputForwardRunnable.java + * Created on: Dec 1, 2016 + */ +package fr.bigeon.gclc.swt; + +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 + * @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 @@ fr.bigeon gclc - 1.2.6 + 1.3.1 GCLC system command diff --git a/gclc.system/src/main/java/net/bigeon/gclc/system/ExecSystemCommand.java b/gclc.system/src/main/java/net/bigeon/gclc/system/ExecSystemCommand.java index ca0af92..0b53e0f 100644 --- a/gclc.system/src/main/java/net/bigeon/gclc/system/ExecSystemCommand.java +++ b/gclc.system/src/main/java/net/bigeon/gclc/system/ExecSystemCommand.java @@ -17,8 +17,7 @@ import fr.bigeon.gclc.exception.CommandRunException; import fr.bigeon.gclc.exception.CommandRunExceptionType; import fr.bigeon.gclc.manager.ConsoleManager; -/**

- * 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 { /**