001/*
002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved.
003 * 
004 * http://www.izforge.com/izpack/
005 * http://developer.berlios.de/projects/izpack/
006 * 
007 * Copyright 2002 Jan Blok
008 * 
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *     http://www.apache.org/licenses/LICENSE-2.0
014 *     
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 */
021
022package com.izforge.izpack.util;
023
024import java.awt.Dimension;
025import java.awt.Font;
026import java.awt.Toolkit;
027import java.awt.event.KeyEvent;
028import java.awt.event.KeyListener;
029import java.io.BufferedOutputStream;
030import java.io.BufferedReader;
031import java.io.IOException;
032import java.io.InputStream;
033import java.io.InputStreamReader;
034import java.io.OutputStreamWriter;
035import java.io.PipedInputStream;
036import java.io.PipedOutputStream;
037import java.io.PrintStream;
038import java.io.PrintWriter;
039
040import javax.swing.JFrame;
041import javax.swing.JScrollPane;
042import javax.swing.JTextArea;
043import javax.swing.SwingUtilities;
044import javax.swing.event.DocumentEvent;
045import javax.swing.event.DocumentListener;
046import javax.swing.text.Document;
047import javax.swing.text.Segment;
048
049public final class Console
050{
051
052    public static final int INITIAL_WIDTH = 800;
053
054    public static final int INITIAL_HEIGHT = 600;
055
056    public static void main(String[] args)
057    {
058        Runtime rt = Runtime.getRuntime();
059        Process p = null;
060        try
061        {
062
063            /*
064             * Start a new process in which to execute the commands in cmd, using the environment in
065             * env and use pwd as the current working directory.
066             */
067            p = rt.exec(args);// , env, pwd);
068            new Console(p);
069            System.exit(p.exitValue());
070        }
071        catch (IOException e)
072        {
073            /*
074             * Couldn't even get the command to start. Most likely it couldn't be found because of a
075             * typo.
076             */
077            System.out.println("Error starting: " + args[0]);
078            System.out.println(e);
079        }
080    }
081
082    private StdOut so;
083
084    private StdOut se;
085
086    public String getOutputData()
087    {
088        if (so != null)
089        {
090            return so.getData();
091        }
092        else
093        {
094            return "";
095        }
096    }
097
098    public String getErrorData()
099    {
100        if (se != null)
101        {
102            return se.getData();
103        }
104        else
105        {
106            return "";
107        }
108    }
109
110    public Console(Process p)
111    {
112        JFrame frame = new JFrame();
113        frame.setTitle("Console");
114        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
115        frame.setLocation(screenSize.width / 2 - INITIAL_WIDTH / 2, screenSize.height / 2
116                - INITIAL_HEIGHT / 2);
117        ConsoleTextArea cta = new ConsoleTextArea();
118        JScrollPane scroll = new JScrollPane(cta);
119        scroll.setPreferredSize(new Dimension(INITIAL_WIDTH, INITIAL_HEIGHT));
120        frame.getContentPane().add(scroll);
121        frame.pack();
122
123        // From here down your shell should be pretty much
124        // as it is written here!
125        /*
126         * Start up StdOut, StdIn and StdErr threads that write the output generated by the process
127         * p to the screen, and feed the keyboard input into p.
128         */
129        so = new StdOut(p, cta);
130        se = new StdOut(p, cta);
131        StdIn si = new StdIn(p, cta);
132        so.start();
133        se.start();
134        si.start();
135
136        // Wait for the process p to complete.
137        try
138        {
139            frame.setVisible(true);
140            p.waitFor();
141        }
142        catch (InterruptedException e)
143        {
144            /*
145             * Something bad happened while the command was executing.
146             */
147            System.out.println("Error during execution");
148            System.out.println(e);
149        }
150
151        /*
152         * Now signal the StdOut, StdErr and StdIn threads that the process is done, and wait for
153         * them to complete.
154         */
155        try
156        {
157            so.done();
158            se.done();
159            si.done();
160            so.join();
161            se.join();
162            si.join();
163        }
164        catch (InterruptedException e)
165        {
166            // Something bad happend to one of the Std threads.
167            System.out.println("Error in StdOut, StdErr or StdIn.");
168            System.out.println(e);
169        }
170        frame.setVisible(false);
171    }
172}
173
174class StdIn extends Thread
175{
176
177    private BufferedReader kb;
178
179    private boolean processRunning;
180
181    private PrintWriter op;
182
183    public StdIn(Process p, ConsoleTextArea cta)
184    {
185        setDaemon(true);
186        InputStreamReader ir = new InputStreamReader(cta.getIn());
187        kb = new BufferedReader(ir);
188
189        BufferedOutputStream os = new BufferedOutputStream(p.getOutputStream());
190        op = new PrintWriter((new OutputStreamWriter(os)), true);
191        processRunning = true;
192    }
193
194    public void run()
195    {
196        try
197        {
198            while (kb.ready() || processRunning)
199            {
200                if (kb.ready())
201                {
202                    op.println(kb.readLine());
203                }
204            }
205        }
206        catch (IOException e)
207        {
208            System.err.println("Problem reading standard input.");
209            System.err.println(e);
210        }
211    }
212
213    public void done()
214    {
215        processRunning = false;
216    }
217}
218
219class StdOut extends Thread
220{
221
222    private InputStreamReader output;
223
224    private boolean processRunning;
225
226    private ConsoleTextArea cta;
227
228    private StringBuffer data;
229
230    public StdOut(Process p, ConsoleTextArea cta)
231    {
232        setDaemon(true);
233        output = new InputStreamReader(p.getInputStream());
234        this.cta = cta;
235        processRunning = true;
236        data = new StringBuffer();
237    }
238
239    public void run()
240    {
241        try
242        {
243            /*
244             * Loop as long as there is output from the process to be displayed or as long as the
245             * process is still running even if there is presently no output.
246             */
247            while (output.ready() || processRunning)
248            {
249
250                // If there is output get it and display it.
251                if (output.ready())
252                {
253                    char[] array = new char[255];
254                    int num = output.read(array);
255                    if (num != -1)
256                    {
257                        String s = new String(array, 0, num);
258                        data.append(s);
259                        SwingUtilities.invokeAndWait(new ConsoleWrite(cta, s));
260                    }
261                }
262            }
263        }
264        catch (Exception e)
265        {
266            System.err.println("Problem writing to standard output.");
267            System.err.println(e);
268        }
269    }
270
271    public void done()
272    {
273        processRunning = false;
274    }
275
276    public String getData()
277    {
278        return data.toString();
279    }
280}
281
282class ConsoleWrite implements Runnable
283{
284
285    private ConsoleTextArea textArea;
286
287    private String str;
288
289    public ConsoleWrite(ConsoleTextArea textArea, String str)
290    {
291        this.textArea = textArea;
292        this.str = str;
293    }
294
295    public void run()
296    {
297        textArea.write(str);
298    }
299};
300
301class ConsoleWriter extends java.io.OutputStream
302{
303
304    private ConsoleTextArea textArea;
305
306    private StringBuffer buffer;
307
308    public ConsoleWriter(ConsoleTextArea textArea)
309    {
310        this.textArea = textArea;
311        buffer = new StringBuffer();
312    }
313
314    public synchronized void write(int ch)
315    {
316        buffer.append((char) ch);
317        if (ch == '\n')
318        {
319            flushBuffer();
320        }
321    }
322
323    public synchronized void write(char[] data, int off, int len)
324    {
325        for (int i = off; i < len; i++)
326        {
327            buffer.append(data[i]);
328            if (data[i] == '\n')
329            {
330                flushBuffer();
331            }
332        }
333    }
334
335    public synchronized void flush()
336    {
337        if (buffer.length() > 0)
338        {
339            flushBuffer();
340        }
341    }
342
343    public void close()
344    {
345        flush();
346    }
347
348    private void flushBuffer()
349    {
350        String str = buffer.toString();
351        buffer.setLength(0);
352        SwingUtilities.invokeLater(new ConsoleWrite(textArea, str));
353    }
354};
355
356class ConsoleTextArea extends JTextArea implements KeyListener, DocumentListener
357{
358
359    /**
360     * 
361     */
362    private static final long serialVersionUID = 3258410625414475827L;
363
364    private ConsoleWriter console1;
365
366    private ConsoleWriter console2;
367
368    private PrintStream out;
369
370    private PrintStream err;
371
372    private PrintWriter inPipe;
373
374    private PipedInputStream in;
375
376    private java.util.Vector history;
377
378    private int historyIndex = -1;
379
380    private int outputMark = 0;
381
382    public void select(int start, int end)
383    {
384        requestFocus();
385        super.select(start, end);
386    }
387
388    public ConsoleTextArea()
389    {
390        super();
391        history = new java.util.Vector();
392        console1 = new ConsoleWriter(this);
393        console2 = new ConsoleWriter(this);
394        out = new PrintStream(console1);
395        err = new PrintStream(console2);
396        PipedOutputStream outPipe = new PipedOutputStream();
397        inPipe = new PrintWriter(outPipe);
398        in = new PipedInputStream();
399        try
400        {
401            outPipe.connect(in);
402        }
403        catch (IOException exc)
404        {
405            exc.printStackTrace();
406        }
407        getDocument().addDocumentListener(this);
408        addKeyListener(this);
409        setLineWrap(true);
410        setFont(new Font("Monospaced", 0, 12));
411    }
412
413    void returnPressed()
414    {
415        Document doc = getDocument();
416        int len = doc.getLength();
417        Segment segment = new Segment();
418        try
419        {
420            synchronized (doc)
421            {
422                doc.getText(outputMark, len - outputMark, segment);
423            }
424        }
425        catch (javax.swing.text.BadLocationException ignored)
426        {
427            ignored.printStackTrace();
428        }
429        if (segment.count > 0)
430        {
431            history.addElement(segment.toString());
432        }
433        historyIndex = history.size();
434        inPipe.write(segment.array, segment.offset, segment.count);
435        append("\n");
436        synchronized (doc)
437        {
438            outputMark = doc.getLength();
439        }
440        inPipe.write("\n");
441        inPipe.flush();
442        console1.flush();
443    }
444
445    public void eval(String str)
446    {
447        inPipe.write(str);
448        inPipe.write("\n");
449        inPipe.flush();
450        console1.flush();
451    }
452
453    public void keyPressed(KeyEvent e)
454    {
455        int code = e.getKeyCode();
456        if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_LEFT)
457        {
458            if (outputMark == getCaretPosition())
459            {
460                e.consume();
461            }
462        }
463        else if (code == KeyEvent.VK_HOME)
464        {
465            int caretPos = getCaretPosition();
466            if (caretPos == outputMark)
467            {
468                e.consume();
469            }
470            else if (caretPos > outputMark)
471            {
472                if (!e.isControlDown())
473                {
474                    if (e.isShiftDown())
475                    {
476                        moveCaretPosition(outputMark);
477                    }
478                    else
479                    {
480                        setCaretPosition(outputMark);
481                    }
482                    e.consume();
483                }
484            }
485        }
486        else if (code == KeyEvent.VK_ENTER)
487        {
488            returnPressed();
489            e.consume();
490        }
491        else if (code == KeyEvent.VK_UP)
492        {
493            historyIndex--;
494            if (historyIndex >= 0)
495            {
496                if (historyIndex >= history.size())
497                {
498                    historyIndex = history.size() - 1;
499                }
500                if (historyIndex >= 0)
501                {
502                    String str = (String) history.elementAt(historyIndex);
503                    int len = getDocument().getLength();
504                    replaceRange(str, outputMark, len);
505                    int caretPos = outputMark + str.length();
506                    select(caretPos, caretPos);
507                }
508                else
509                {
510                    historyIndex++;
511                }
512            }
513            else
514            {
515                historyIndex++;
516            }
517            e.consume();
518        }
519        else if (code == KeyEvent.VK_DOWN)
520        {
521            int caretPos = outputMark;
522            if (history.size() > 0)
523            {
524                historyIndex++;
525                if (historyIndex < 0)
526                {
527                    historyIndex = 0;
528                }
529                int len = getDocument().getLength();
530                if (historyIndex < history.size())
531                {
532                    String str = (String) history.elementAt(historyIndex);
533                    replaceRange(str, outputMark, len);
534                    caretPos = outputMark + str.length();
535                }
536                else
537                {
538                    historyIndex = history.size();
539                    replaceRange("", outputMark, len);
540                }
541            }
542            select(caretPos, caretPos);
543            e.consume();
544        }
545    }
546
547    public void keyTyped(KeyEvent e)
548    {
549        int keyChar = e.getKeyChar();
550        if (keyChar == 0x8 /* KeyEvent.VK_BACK_SPACE */)
551        {
552            if (outputMark == getCaretPosition())
553            {
554                e.consume();
555            }
556        }
557        else if (getCaretPosition() < outputMark)
558        {
559            setCaretPosition(outputMark);
560        }
561    }
562
563    public void keyReleased(KeyEvent e)
564    {
565    }
566
567    public synchronized void write(String str)
568    {
569        insert(str, outputMark);
570        int len = str.length();
571        outputMark += len;
572        select(outputMark, outputMark);
573    }
574
575    public synchronized void insertUpdate(DocumentEvent e)
576    {
577        int len = e.getLength();
578        int off = e.getOffset();
579        if (outputMark > off)
580        {
581            outputMark += len;
582        }
583    }
584
585    public synchronized void removeUpdate(DocumentEvent e)
586    {
587        int len = e.getLength();
588        int off = e.getOffset();
589        if (outputMark > off)
590        {
591            if (outputMark >= off + len)
592            {
593                outputMark -= len;
594            }
595            else
596            {
597                outputMark = off;
598            }
599        }
600    }
601
602    public void postUpdateUI()
603    {
604        // this attempts to cleanup the damage done by updateComponentTreeUI
605        requestFocus();
606        setCaret(getCaret());
607        synchronized (this)
608        {
609            select(outputMark, outputMark);
610        }
611    }
612
613    public void changedUpdate(DocumentEvent e)
614    {
615    }
616
617    public InputStream getIn()
618    {
619        return in;
620    }
621
622    public PrintStream getOut()
623    {
624        return out;
625    }
626
627    public PrintStream getErr()
628    {
629        return err;
630    }
631
632};