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