2020
2121package com .github .fracpete .jshell ;
2222
23+ import com .github .fracpete .processoutput4j .core .StreamingProcessOutputType ;
24+ import com .github .fracpete .processoutput4j .core .StreamingProcessOwner ;
25+ import com .github .fracpete .processoutput4j .output .StreamingProcessOutput ;
2326import nz .ac .waikato .cms .core .FileUtils ;
27+ import nz .ac .waikato .cms .core .Utils ;
2428import nz .ac .waikato .cms .gui .core .BaseFileChooser ;
2529import nz .ac .waikato .cms .gui .core .BaseFrame ;
2630import nz .ac .waikato .cms .gui .core .BasePanel ;
4448import java .awt .GridLayout ;
4549import java .awt .event .ActionEvent ;
4650import java .io .File ;
51+ import java .nio .file .Files ;
52+ import java .util .ArrayList ;
53+ import java .util .List ;
4754
4855/**
4956 * Panel for performing scripting via jshell. Requires Java 9.
5360 * @author FracPete (fracpete at gmail dot com)
5461 */
5562public class JShellPanel
56- extends BasePanel {
63+ extends BasePanel
64+ implements StreamingProcessOwner {
5765
5866 /** whether scripting is available. */
5967 protected Boolean m_Available ;
@@ -91,6 +99,9 @@ public class JShellPanel
9199 /** for the jshell output. */
92100 protected JTextArea m_TextOutput ;
93101
102+ /** executes the script. */
103+ protected transient StreamingProcessOutput m_Execution ;
104+
94105 /**
95106 * Initializes the members.
96107 */
@@ -202,7 +213,7 @@ protected void finishInit() {
202213 protected void updateButtons () {
203214 boolean running ;
204215
205- running = false ; // TODO
216+ running = ( m_Execution != null );
206217
207218 // script
208219 m_ButtonScriptLoad .setEnabled (!running );
@@ -234,7 +245,15 @@ public void loadScript() {
234245 * @param script the script to load
235246 */
236247 public void loadScript (File script ) {
237- // TODO
248+ List <String > lines ;
249+
250+ try {
251+ lines = Files .readAllLines (script .toPath ());
252+ m_TextCode .setText (Utils .flatten (lines , "\n " ));
253+ }
254+ catch (Exception e ) {
255+ GUIHelper .showErrorMessage (this , "Failed to load script from: " + script , e );
256+ }
238257
239258 updateButtons ();
240259 }
@@ -243,19 +262,95 @@ public void loadScript(File script) {
243262 * Lets the user save the script to a file.
244263 */
245264 public void saveScript () {
246- // TODO
265+ int retVal ;
266+ File script ;
267+ String msg ;
268+
269+ retVal = m_FileChooserScript .showSaveDialog (this );
270+ if (retVal != BaseFileChooser .APPROVE_OPTION )
271+ return ;
272+
273+ script = m_FileChooserScript .getSelectedFile ();
274+ msg = FileUtils .writeToFileMsg (script .getAbsolutePath (), m_TextCode .getText (), false , null );
275+ if (msg != null )
276+ GUIHelper .showErrorMessage (this , "Failed to save script to : " + script + "\n " + msg );
247277
248278 updateButtons ();
249279 }
250280
281+ /**
282+ * Executes the script.
283+ */
251284 public void runScript () {
252- // TODO
285+ List <String > cmd ;
286+ final File tmpFile ;
287+ String code ;
288+ String msg ;
289+ ProcessBuilder builder ;
290+ Runnable run ;
291+
292+ clearScriptOutput ();
293+
294+ // create tmp file name
295+ try {
296+ tmpFile = File .createTempFile ("jshell-" , ".jsh" );
297+ }
298+ catch (Exception e ) {
299+ GUIHelper .showErrorMessage (this , "Failed to create temporary file for script!\n Cannot execute script!" , e );
300+ return ;
301+ }
302+
303+ // ensure that "/exit is in code"
304+ code = m_TextCode .getText ();
305+ if (!code .toLowerCase ().contains ("/exit" ))
306+ code += "\n /exit\n " ;
307+
308+ // save script to tmp file
309+ msg = FileUtils .writeToFileMsg (tmpFile .getAbsolutePath (), code , false , null );
310+ if (msg != null ) {
311+ tmpFile .delete ();
312+ GUIHelper .showErrorMessage (this , "Failed to write script to temporary file: " + tmpFile + "\n " + msg );
313+ return ;
314+ }
315+
316+ // build commandline for jshell
317+ cmd = new ArrayList <>();
318+ cmd .add (getExecutable ());
319+ cmd .add ("--class-path" );
320+ cmd .add (System .getProperty ("java.class.path" ));
321+ cmd .add (tmpFile .getAbsolutePath ());
322+
323+ builder = new ProcessBuilder ();
324+ builder .command (cmd );
325+ m_Execution = new StreamingProcessOutput (this );
326+
327+ run = new Runnable () {
328+ @ Override
329+ public void run () {
330+ try {
331+ m_Execution .monitor (builder );
332+ }
333+ catch (Exception e ) {
334+ GUIHelper .showErrorMessage (JShellPanel .this , "Failed to execute script!" , e );
335+ }
336+ m_Execution = null ;
337+ tmpFile .delete ();
338+ updateButtons ();
339+ }
340+ };
341+ new Thread (run ).start ();
253342
254343 updateButtons ();
255344 }
256345
346+ /**
347+ * Stops a running script.
348+ */
257349 public void stopScript () {
258- // TODO
350+ if (m_Execution != null ) {
351+ m_Execution .destroy ();
352+ m_Execution = null ;
353+ }
259354
260355 updateButtons ();
261356 }
@@ -311,11 +406,36 @@ public String getExecutable() {
311406 public boolean isAvailable () {
312407 if (m_Available == null ) {
313408 m_Available = JavaVersion .JAVA_RECENT .atLeast (JavaVersion .JAVA_9 )
314- && new File (getExecutable ()).exists ();
409+ && new File (getExecutable ()).exists ()
410+ && !System .getProperty ("java.class.path" ).isEmpty ();
315411 }
316412 return m_Available ;
317413 }
318414
415+ /**
416+ * Returns what output from the process to forward.
417+ *
418+ * @return the output type
419+ */
420+ public StreamingProcessOutputType getOutputType () {
421+ return StreamingProcessOutputType .BOTH ;
422+ }
423+
424+ /**
425+ * Processes the incoming line.
426+ *
427+ * @param line the line to process
428+ * @param stdout whether stdout or stderr
429+ */
430+ public void processOutput (String line , boolean stdout ) {
431+ boolean moveToEnd ;
432+
433+ moveToEnd = (m_TextOutput .getDocument ().getLength () == m_TextOutput .getCaretPosition ());
434+ m_TextOutput .append ((stdout ? "[OUT] " : "[ERR] " ) + line + "\n " );
435+ if (moveToEnd )
436+ m_TextOutput .setCaretPosition (m_TextOutput .getDocument ().getLength ());
437+ }
438+
319439 /**
320440 * For testing only.
321441 *
0 commit comments