jalview 2.5 release banner
[jalview.git] / src / jalview / gui / Console.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.gui;
19
20 import java.io.*;
21 import java.awt.*;
22 import java.awt.event.*;
23 import javax.swing.*;
24
25 import org.apache.log4j.SimpleLayout;
26
27 /**
28  * Simple Jalview Java Console. Version 1 - allows viewing of console output
29  * after desktop is created. Acquired with thanks from RJHM's site
30  * http://www.comweb.nl/java/Console/Console.html A simple Java Console for your
31  * application (Swing version) Requires Java 1.1.5 or higher Disclaimer the use
32  * of this source is at your own risk. Permision to use and distribute into your
33  * own applications RJHM van den Bergh , rvdb@comweb.nl
34  */
35
36 public class Console extends WindowAdapter implements WindowListener,
37         ActionListener, Runnable
38 {
39   private JFrame frame;
40
41   private JTextArea textArea;
42
43   int lines = 0;
44
45   int lim = 1000;
46
47   private Thread reader;
48
49   private Thread reader2;
50
51   private boolean quit;
52
53   private final PrintStream stdout = System.out;
54
55   private final PrintStream stderr = System.err;
56
57   private final PipedInputStream pin = new PipedInputStream();
58
59   private final PipedInputStream pin2 = new PipedInputStream();
60
61   Thread errorThrower; // just for testing (Throws an Exception at this Console
62
63   // are we attached to some parent Desktop
64   Desktop parent = null;
65
66   public Console()
67   {
68     // create all components and add them
69     Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
70     frame = initFrame("Java Console", screenSize.width / 2,
71             screenSize.height / 2, -1, -1);
72     initConsole(true);
73   }
74
75   private void initConsole(boolean visible)
76   {
77     // CutAndPasteTransfer cpt = new CutAndPasteTransfer();
78     // textArea = cpt.getTextArea();
79     textArea = new JTextArea();
80     textArea.setEditable(false);
81     JButton button = new JButton("clear");
82
83     // frame = cpt;
84     frame.getContentPane().setLayout(new BorderLayout());
85     frame.getContentPane().add(new JScrollPane(textArea),
86             BorderLayout.CENTER);
87     frame.getContentPane().add(button, BorderLayout.SOUTH);
88     frame.setVisible(visible);
89
90     frame.addWindowListener(this);
91     button.addActionListener(this);
92
93     try
94     {
95       PipedOutputStream pout = new PipedOutputStream(this.pin);
96       System.setOut(new PrintStream(pout, true));
97     } catch (java.io.IOException io)
98     {
99       textArea.append("Couldn't redirect STDOUT to this console\n"
100               + io.getMessage());
101     } catch (SecurityException se)
102     {
103       textArea.append("Couldn't redirect STDOUT to this console\n"
104               + se.getMessage());
105     }
106
107     try
108     {
109       PipedOutputStream pout2 = new PipedOutputStream(this.pin2);
110       System.setErr(new PrintStream(pout2, true));
111     } catch (java.io.IOException io)
112     {
113       textArea.append("Couldn't redirect STDERR to this console\n"
114               + io.getMessage());
115     } catch (SecurityException se)
116     {
117       textArea.append("Couldn't redirect STDERR to this console\n"
118               + se.getMessage());
119     }
120
121     quit = false; // signals the Threads that they should exit
122
123     // Starting two seperate threads to read from the PipedInputStreams
124     //
125     reader = new Thread(this);
126     reader.setDaemon(true);
127     reader.start();
128     //
129     reader2 = new Thread(this);
130     reader2.setDaemon(true);
131     reader2.start();
132   }
133
134   public void test()
135   {
136     // testing part
137     // you may omit this part for your application
138     // 
139
140     System.out.println("Hello World 2");
141     System.out.println("All fonts available to Graphic2D:\n");
142     GraphicsEnvironment ge = GraphicsEnvironment
143             .getLocalGraphicsEnvironment();
144     String[] fontNames = ge.getAvailableFontFamilyNames();
145     for (int n = 0; n < fontNames.length; n++)
146       System.out.println(fontNames[n]);
147     // Testing part: simple an error thrown anywhere in this JVM will be printed
148     // on the Console
149     // We do it with a seperate Thread becasue we don't wan't to break a Thread
150     // used by the Console.
151     System.out.println("\nLets throw an error on this console");
152     errorThrower = new Thread(this);
153     errorThrower.setDaemon(true);
154     errorThrower.start();
155   }
156
157   private JFrame initFrame(String string, int i, int j, int x, int y)
158   {
159     JFrame frame = new JFrame(string);
160     frame.setName(string);
161     if (x == -1)
162       x = (int) (i / 2);
163     if (y == -1)
164       y = (int) (j / 2);
165     frame.setBounds(x, y, i, j);
166     return frame;
167   }
168
169   /**
170    * attach a console to the desktop - the desktop will open it if requested.
171    * 
172    * @param desktop
173    */
174   public Console(Desktop desktop)
175   {
176     parent = desktop;
177     // window name - get x,y,width, height possibly scaled
178     Rectangle bounds = desktop.getLastKnownDimensions("JAVA_CONSOLE_");
179     if (bounds == null)
180     {
181       frame = initFrame("Jalview Java Console", desktop.getWidth() / 2,
182               desktop.getHeight() / 4, desktop.getX(), desktop.getY());
183     }
184     else
185     {
186       frame = initFrame("Jalview Java Console", bounds.width,
187               bounds.height, bounds.x, bounds.y);
188     }
189     // desktop.add(frame);
190     initConsole(false);
191     JalviewAppender jappender = new JalviewAppender();
192     jappender.setLayout(new SimpleLayout());
193     JalviewAppender.setTextArea(textArea);
194     org.apache.log4j.Logger.getRootLogger().addAppender(jappender);
195   }
196
197   public synchronized void stopConsole()
198   {
199     quit = true;
200     this.notifyAll(); // stop all threads
201     try
202     {
203       reader.join(10);
204       pin.close();
205     } catch (Exception e)
206     {
207     }
208     try
209     {
210       reader2.join(10);
211       pin2.close();
212     } catch (Exception e)
213     {
214     }
215     // System.exit(0);
216   }
217
218   public synchronized void windowClosed(WindowEvent evt)
219   {
220     frame.setVisible(false);
221     if (parent == null)
222     {
223
224       stopConsole();
225     }
226     else
227     {
228       parent.showConsole(false);
229     }
230   }
231
232   public synchronized void windowClosing(WindowEvent evt)
233   {
234     frame.setVisible(false); // default behaviour of JFrame
235     // frame.dispose();
236   }
237
238   public synchronized void actionPerformed(ActionEvent evt)
239   {
240     trimBuffer(true);
241     // textArea.setText("");
242   }
243
244   public synchronized void run()
245   {
246     try
247     {
248       while (Thread.currentThread() == reader)
249       {
250         try
251         {
252           this.wait(100);
253         } catch (InterruptedException ie)
254         {
255         }
256         while (pin.available() != 0)
257         {
258           String input = this.readLine(pin);
259           stdout.print(input);
260           textArea.append(input);
261           // lines++;
262         }
263         if (quit)
264           return;
265         trimBuffer(false);
266       }
267
268       while (Thread.currentThread() == reader2)
269       {
270         try
271         {
272           this.wait(100);
273         } catch (InterruptedException ie)
274         {
275         }
276         while (pin2.available() != 0)
277         {
278           String input = this.readLine(pin2);
279           stderr.print(input);
280           textArea.append(input);
281           // lines++;
282         }
283         if (quit)
284           return;
285         trimBuffer(false);
286       }
287     } catch (Exception e)
288     {
289       textArea.append("\nConsole reports an Internal error.");
290       textArea.append("The error is: " + e.getMessage());
291       lines += 2;
292       stderr.println("Console reports an Internal error.\nThe error is: "
293               + e);
294     }
295
296     // just for testing (Throw a Nullpointer after 1 second)
297     if (Thread.currentThread() == errorThrower)
298     {
299       try
300       {
301         this.wait(1000);
302       } catch (InterruptedException ie)
303       {
304       }
305       throw new NullPointerException(
306               "Application test: throwing an NullPointerException It should arrive at the console");
307     }
308   }
309
310   private synchronized void trimBuffer(boolean clear)
311   {
312     // trim the buffer
313     if (clear || lines > lim)
314     {
315       try
316       {
317         if (lines > 5)
318         {
319           // minimum length for truncation/clearing
320           String header = textArea.getText(0, textArea.getLineEndOffset(5))
321                   + "\n..Truncated..\n"; // keep first 5 lines for startup info
322           int truncate;
323           if (!clear)
324           {
325             truncate = textArea.getLineEndOffset(lines - lim - 7);
326             textArea.setText(header
327                     + textArea.getText(truncate, textArea.getText()
328                             .length()
329                             - truncate));
330           }
331           else
332           {
333             textArea.setText(header);
334           }
335         }
336
337       } catch (Exception e)
338       {
339         e.printStackTrace();
340       }
341       lines = textArea.getLineCount();
342     }
343
344   }
345
346   public synchronized String readLine(PipedInputStream in)
347           throws IOException
348   {
349     String input = "";
350     int lp = -1;
351     do
352     {
353       int available = in.available();
354       if (available == 0)
355         break;
356       byte b[] = new byte[available];
357       in.read(b);
358       input = input + new String(b, 0, b.length);
359       while ((lp = input.indexOf("\n", lp + 1)) > -1)
360       {
361         lines++;
362       }
363     } while (!input.endsWith("\n") && !input.endsWith("\r\n") && !quit);
364     return input;
365   }
366
367   public static void main(String[] arg)
368   {
369     new Console().test(); // create console with not reference
370
371   }
372
373   public void setVisible(boolean selected)
374   {
375     frame.setVisible(selected);
376     if (selected == true)
377     {
378       frame.toFront();
379     }
380   }
381
382   public Rectangle getBounds()
383   {
384     if (frame != null)
385     {
386       return frame.getBounds();
387     }
388     return null;
389   }
390 }