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