1   /*
2    *  LogArea.java
3    *
4    *  Copyright (c) 1998-2001, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Cristian URSU, 26/03/2001
12   *
13   *  $Id: LogArea.java,v 1.23 2002/06/01 11:25:48 valyt Exp $
14   *
15   */
16  
17  package gate.gui;
18  
19  import java.awt.*;
20  import java.awt.event.*;
21  import java.io.*;
22  
23  import javax.swing.*;
24  import javax.swing.text.*;
25  
26  import gate.swing.*;
27  import gate.util.*;
28  
29  /**
30    * This class is used to log all messages from GATE. When an object of this
31    * class is created, it redirects the output of {@link gate.util.Out} &
32    * {@link gate.util.Err}.
33    * The output from Err is written with <font color="red">red</font> and the
34    * one from Out is written in <b>black</b>.
35    */
36  public class LogArea extends XJTextPane {
37  
38    /** Field needed in inner classes*/
39    protected LogArea thisLogArea = null;
40  
41    /** The popup menu with various actions*/
42    protected JPopupMenu popup = null;
43  
44    /** The original printstream on System.out */
45    protected PrintStream originalOut;
46  
47    /** The original printstream on System.err */
48    protected PrintStream originalErr;
49    /** This fields defines the Select all behaviour*/
50    protected SelectAllAction selectAllAction = null;
51  
52    /** This fields defines the copy  behaviour*/
53    protected CopyAction copyAction = null;
54  
55    /** This fields defines the clear all  behaviour*/
56    protected ClearAllAction clearAllAction = null;
57  
58    /** Constructs a LogArea object and captures the output from Err and Out. The
59      * output from System.out & System.err is not captured.
60      */
61    public LogArea(){
62      thisLogArea = this;
63      this.setEditable(false);
64  
65      LogAreaOutputStream err = new LogAreaOutputStream(true);
66      LogAreaOutputStream out = new LogAreaOutputStream(false);
67  
68      // Redirecting Err
69      try{
70        Err.setPrintWriter(new UTF8PrintWriter(err,true));
71      }catch(UnsupportedEncodingException uee){
72        uee.printStackTrace();
73      }
74      // Redirecting Out
75      try{
76        Out.setPrintWriter(new UTF8PrintWriter(out,true));
77      }catch(UnsupportedEncodingException uee){
78        uee.printStackTrace();
79      }
80  
81      // Redirecting System.out
82      originalOut = System.out;
83      try{
84        System.setOut(new UTF8PrintStream(out, true));
85      }catch(UnsupportedEncodingException uee){
86        uee.printStackTrace();
87      }
88  
89      // Redirecting System.err
90      originalErr = System.err;
91      try{
92        System.setErr(new UTF8PrintStream(err, true));
93      }catch(UnsupportedEncodingException uee){
94        uee.printStackTrace();
95      }
96      popup = new JPopupMenu();
97      selectAllAction = new SelectAllAction();
98      copyAction = new CopyAction();
99      clearAllAction = new ClearAllAction();
100 
101     popup.add(selectAllAction);
102     popup.add(copyAction);
103     popup.addSeparator();
104     popup.add(clearAllAction);
105     initListeners();
106   }// LogArea
107 
108   /** Init all listeners for this object*/
109   public void initListeners(){
110     super.initListeners();
111     this.addMouseListener(new MouseAdapter(){
112       public void mouseClicked(MouseEvent e){
113         if(SwingUtilities.isRightMouseButton(e)){
114           popup.show(thisLogArea, e.getPoint().x, e.getPoint().y);
115         }//End if
116       }// end mouseClicked()
117     });// End addMouseListener();
118   }
119 
120   /** Returns the original printstream on System.err */
121   public PrintStream getOriginalErr() {
122     return originalErr;
123   }
124 
125   /** Returns the original printstream on System.out */
126   public PrintStream getOriginalOut() {
127     return originalOut;
128   }// initListeners();
129 
130   /** Inner class that defines the behaviour of SelectAll action.*/
131   protected class SelectAllAction extends AbstractAction{
132     public SelectAllAction(){
133       super("Select all");
134     }// SelectAll
135     public void actionPerformed(ActionEvent e){
136       thisLogArea.selectAll();
137     }// actionPerformed();
138   }// End class SelectAllAction
139 
140   /** Inner class that defines the behaviour of copy action.*/
141   protected class CopyAction extends AbstractAction{
142     public CopyAction(){
143       super("Copy");
144     }// CopyAction
145     public void actionPerformed(ActionEvent e){
146       thisLogArea.copy();
147     }// actionPerformed();
148   }// End class CopyAction
149 
150   /**
151    * A runnable that adds a bit of text to the area; needed so we can write
152    * from the Swing thread.
153    */
154   protected class SwingWriter implements Runnable{
155     SwingWriter(String text, Style style){
156       this.text = text;
157       this.style = style;
158     }
159 
160     public void run(){
161       try{
162         if(getDocument().getLength() > 0){
163           Rectangle place = modelToView(getDocument().getLength() - 1);
164           if(place != null) scrollRectToVisible(place);
165         }
166         getDocument().insertString(getDocument().getLength(), text, style);
167       } catch(BadLocationException e){
168           e.printStackTrace(System.err);
169       }// End try
170     }
171     String text;
172     Style style;
173   }
174 
175   /**
176    * A print writer that uses UTF-8 to convert from char[] to byte[]
177    */
178   public static class UTF8PrintWriter extends PrintWriter{
179     public UTF8PrintWriter(OutputStream out)
180            throws UnsupportedEncodingException{
181       this(out, true);
182     }
183 
184     public UTF8PrintWriter(OutputStream out, boolean autoFlush)
185            throws UnsupportedEncodingException{
186       super(new BufferedWriter(new OutputStreamWriter(out, "UTF-8")),
187             autoFlush);
188     }
189   }
190 
191   /**
192    * A print writer that uses UTF-8 to convert from char[] to byte[]
193    */
194   public static class UTF8PrintStream extends PrintStream{
195     public UTF8PrintStream(OutputStream out)
196            throws UnsupportedEncodingException{
197       this(out, true);
198     }
199 
200     public UTF8PrintStream(OutputStream out, boolean autoFlush)
201            throws UnsupportedEncodingException{
202       super(out, autoFlush);
203     }
204 
205     /**
206      * Overriden so it uses UTF-8 when converting a string to byte[]
207      * @param s the string to be printed
208      */
209     public void print(String s) {
210       try{
211         write(s.getBytes("UTF-8"));
212       }catch(UnsupportedEncodingException uee){
213         //support for UTF-8 is guaranteed by the JVM specification
214       }catch(IOException ioe){
215         //print streams don't throw exceptions
216         setError();
217       }
218     }
219 
220     /**
221      * Overriden so it uses UTF-8 when converting a char[] to byte[]
222      * @param s the string to be printed
223      */
224     public void print(char s[]) {
225       print(String.valueOf(s));
226     }
227   }
228 
229   /** Inner class that defines the behaviour of clear all action.*/
230   protected class ClearAllAction extends AbstractAction{
231     public ClearAllAction(){
232       super("Clear all");
233     }// ClearAllAction
234     public void actionPerformed(ActionEvent e){
235       try{
236         thisLogArea.getDocument().remove(0,thisLogArea.getDocument().getLength());
237       } catch (BadLocationException e1){
238         e1.printStackTrace(Err.getPrintWriter());
239       }// End try
240     }// actionPerformed();
241   }// End class ClearAllAction
242 
243   /** Inner class that defines the behaviour of an OutputStream that writes to
244    *  the LogArea.
245    */
246   class LogAreaOutputStream extends OutputStream{
247     /** This field dictates the style on how to write */
248     private boolean isErr = false;
249     /** Char style*/
250     private Style style = null;
251 
252     /** Constructs an Out or Err LogAreaOutputStream*/
253     public LogAreaOutputStream(boolean anIsErr){
254       isErr = anIsErr;
255       if (isErr){
256         style = addStyle("error", getStyle("default"));
257         StyleConstants.setForeground(style, Color.red);
258       }else {
259         style = addStyle("out",getStyle("default"));
260         StyleConstants.setForeground(style, Color.black);
261       }// End if
262     }// LogAreaOutputStream
263 
264     /** Writes an int which must be a the code of a char, into the LogArea,
265      *  using the style specified in constructor. The int is downcast to a byte.
266      */
267     public void write(int charCode){
268       // charCode int must be a char. Let us be sure of that
269       charCode &= 0x000000FF;
270       // Convert the byte to a char before put it into the log area
271       char c = (char)charCode;
272       // Insert it in the log Area
273       SwingUtilities.invokeLater(new SwingWriter(String.valueOf(c), style));
274     }// write(int charCode)
275 
276     /** Writes an array of bytes into the LogArea,
277      *  using the style specified in constructor.
278      */
279     public void write(byte[] data, int offset, int length){
280       // Insert the string to the log area
281       try{
282         SwingUtilities.invokeLater(new SwingWriter(new String(data,offset,
283                                                               length, "UTF-8"),
284                                                    style));
285       }catch(UnsupportedEncodingException uee){
286         uee.printStackTrace();
287       }
288     }// write(byte[] data, int offset, int length)
289   }////End class LogAreaOutputStream
290 }//End class LogArea