001 /*
002 * LogArea.java
003 *
004 * Copyright (c) 1995-2010, The University of Sheffield. See the file
005 * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006 *
007 * This file is part of GATE (see http://gate.ac.uk/), and is free
008 * software, licenced under the GNU Library General Public License,
009 * Version 2, June 1991 (in the distribution as file licence.html,
010 * and also available at http://gate.ac.uk/gate/licence.html).
011 *
012 * Cristian URSU, 26/03/2001
013 *
014 * $Id: LogArea.java 12006 2009-12-01 17:24:28Z thomas_heitz $
015 *
016 */
017
018 package gate.gui;
019
020 import java.awt.Color;
021 import java.awt.Rectangle;
022 import java.awt.event.*;
023 import java.io.*;
024
025 import javax.swing.*;
026 import javax.swing.text.*;
027
028 import gate.swing.XJTextPane;
029 import gate.util.Err;
030 import gate.util.Out;
031
032 /**
033 * This class is used to log all messages from GATE. When an object of this
034 * class is created, it redirects the output of {@link gate.util.Out} &
035 * {@link gate.util.Err}.
036 * The output from Err is written with <font color="red">red</font> and the
037 * one from Out is written in <b>black</b>.
038 */
039 public class LogArea extends XJTextPane {
040
041 /** Field needed in inner classes*/
042 protected LogArea thisLogArea = null;
043
044 /** The popup menu with various actions*/
045 protected JPopupMenu popup = null;
046
047 /** Start position from the document. */
048 protected Position startPos;
049 /** End position from the document. */
050 protected Position endPos;
051
052 /** The original printstream on System.out */
053 protected PrintStream originalOut;
054
055 /** The original printstream on System.err */
056 protected PrintStream originalErr;
057 /** This fields defines the Select all behaviour*/
058 protected SelectAllAction selectAllAction = null;
059
060 /** This fields defines the copy behaviour*/
061 protected CopyAction copyAction = null;
062
063 /** This fields defines the clear all behaviour*/
064 protected ClearAllAction clearAllAction = null;
065
066 /** Constructs a LogArea object and captures the output from Err and Out. The
067 * output from System.out & System.err is not captured.
068 */
069 public LogArea(){
070 thisLogArea = this;
071 this.setEditable(false);
072
073 LogAreaOutputStream err = new LogAreaOutputStream(true);
074 LogAreaOutputStream out = new LogAreaOutputStream(false);
075
076 // Redirecting Err
077 try{
078 Err.setPrintWriter(new UTF8PrintWriter(err,true));
079 }catch(UnsupportedEncodingException uee){
080 uee.printStackTrace();
081 }
082 // Redirecting Out
083 try{
084 Out.setPrintWriter(new UTF8PrintWriter(out,true));
085 }catch(UnsupportedEncodingException uee){
086 uee.printStackTrace();
087 }
088
089 // Redirecting System.out
090 originalOut = System.out;
091 try{
092 System.setOut(new UTF8PrintStream(out, true));
093 }catch(UnsupportedEncodingException uee){
094 uee.printStackTrace();
095 }
096
097 // Redirecting System.err
098 originalErr = System.err;
099 try{
100 System.setErr(new UTF8PrintStream(err, true));
101 }catch(UnsupportedEncodingException uee){
102 uee.printStackTrace(originalErr);
103 }
104 popup = new JPopupMenu();
105 selectAllAction = new SelectAllAction();
106 copyAction = new CopyAction();
107 clearAllAction = new ClearAllAction();
108 startPos = getDocument().getStartPosition();
109 endPos = getDocument().getEndPosition();
110
111 popup.add(selectAllAction);
112 popup.add(copyAction);
113 popup.addSeparator();
114 popup.add(clearAllAction);
115 initListeners();
116 }// LogArea
117
118 /**
119 * Overriddent to fetch new start and end Positions when the document is
120 * changed.
121 */
122 public void setDocument(Document d) {
123 super.setDocument(d);
124 startPos = d.getStartPosition();
125 endPos = d.getEndPosition();
126 }
127
128 public void setStyledDocument(StyledDocument d) {
129 this.setDocument(d);
130 }
131
132 /** Init all listeners for this object*/
133 public void initListeners(){
134 super.initListeners();
135 this.addMouseListener(new MouseAdapter(){
136 public void mouseClicked(MouseEvent e){
137 if(SwingUtilities.isRightMouseButton(e)){
138 popup.show(thisLogArea, e.getPoint().x, e.getPoint().y);
139 }//End if
140 }// end mouseClicked()
141 });// End addMouseListener();
142 }
143
144 /** Returns the original printstream on System.err */
145 public PrintStream getOriginalErr() {
146 return originalErr;
147 }
148
149 /** Returns the original printstream on System.out */
150 public PrintStream getOriginalOut() {
151 return originalOut;
152 }// initListeners();
153
154 /** Inner class that defines the behaviour of SelectAll action.*/
155 protected class SelectAllAction extends AbstractAction{
156 public SelectAllAction(){
157 super("Select all");
158 }// SelectAll
159 public void actionPerformed(ActionEvent e){
160 thisLogArea.selectAll();
161 }// actionPerformed();
162 }// End class SelectAllAction
163
164 /** Inner class that defines the behaviour of copy action.*/
165 protected class CopyAction extends AbstractAction{
166 public CopyAction(){
167 super("Copy");
168 }// CopyAction
169 public void actionPerformed(ActionEvent e){
170 thisLogArea.copy();
171 }// actionPerformed();
172 }// End class CopyAction
173
174 /**
175 * A runnable that adds a bit of text to the area; needed so we can write
176 * from the Swing thread.
177 */
178 protected class SwingWriter implements Runnable{
179 SwingWriter(String text, Style style){
180 this.text = text;
181 this.style = style;
182 }
183
184 public void run(){
185 try{
186 if(endPos.getOffset() > 1){
187 Rectangle place = modelToView(endPos.getOffset() - 1);
188 if(place != null) scrollRectToVisible(place);
189 }
190 } catch(BadLocationException e) {
191 // ignore a BLE at this point, just don't bother scrolling
192 originalErr.println("Exception encountered when trying to scroll to "
193 + "end of messages pane: " + e);
194 }
195
196 try {
197 // endPos is always one past the real end position because of the
198 // implicit newline character at the end of any Document
199 getDocument().insertString(endPos.getOffset() - 1, text, style);
200 } catch(BadLocationException e){
201 // a BLE here is a real problem
202 handleBadLocationException(e, text, style);
203 }// End try
204 }
205 String text;
206 Style style;
207 }
208
209 /**
210 * Try and recover from a BadLocationException thrown when inserting a string
211 * into the log area. This method must only be called on the AWT event
212 * handling thread.
213 */
214 private void handleBadLocationException(BadLocationException e,
215 String textToInsert, Style style) {
216 originalErr.println("BadLocationException encountered when writing to "
217 + "the log area: " + e);
218 originalErr.println("trying to recover...");
219
220 Document newDocument = new DefaultStyledDocument();
221 try {
222 StringBuilder sb = new StringBuilder();
223 sb.append("An error occurred when trying to write a message to the log area. The log\n");
224 sb.append("has been cleared to try and recover from this problem.\n\n");
225 sb.append(textToInsert);
226
227 newDocument.insertString(0, sb.toString(), style);
228 }
229 catch(BadLocationException e2) {
230 // oh dear, all bets are off now...
231 e2.printStackTrace(originalErr);
232 return;
233 }
234 // replace the log area's document with the new one
235 setDocument(newDocument);
236 }
237
238 /**
239 * A print writer that uses UTF-8 to convert from char[] to byte[]
240 */
241 public static class UTF8PrintWriter extends PrintWriter{
242 public UTF8PrintWriter(OutputStream out)
243 throws UnsupportedEncodingException{
244 this(out, true);
245 }
246
247 public UTF8PrintWriter(OutputStream out, boolean autoFlush)
248 throws UnsupportedEncodingException{
249 super(new BufferedWriter(new OutputStreamWriter(out, "UTF-8")),
250 autoFlush);
251 }
252 }
253
254 /**
255 * A print writer that uses UTF-8 to convert from char[] to byte[]
256 */
257 public static class UTF8PrintStream extends PrintStream{
258 public UTF8PrintStream(OutputStream out)
259 throws UnsupportedEncodingException{
260 this(out, true);
261 }
262
263 public UTF8PrintStream(OutputStream out, boolean autoFlush)
264 throws UnsupportedEncodingException{
265 super(out, autoFlush);
266 }
267
268 /**
269 * Overriden so it uses UTF-8 when converting a string to byte[]
270 * @param s the string to be printed
271 */
272 public void print(String s) {
273 try{
274 write(s.getBytes("UTF-8"));
275 }catch(UnsupportedEncodingException uee){
276 //support for UTF-8 is guaranteed by the JVM specification
277 }catch(IOException ioe){
278 //print streams don't throw exceptions
279 setError();
280 }
281 }
282
283 /**
284 * Overriden so it uses UTF-8 when converting a char[] to byte[]
285 * @param s the string to be printed
286 */
287 public void print(char s[]) {
288 print(String.valueOf(s));
289 }
290 }
291
292 /** Inner class that defines the behaviour of clear all action.*/
293 protected class ClearAllAction extends AbstractAction{
294 public ClearAllAction(){
295 super("Clear all");
296 }// ClearAllAction
297 public void actionPerformed(ActionEvent e){
298 try{
299 thisLogArea.getDocument().remove(startPos.getOffset(),endPos.getOffset() - startPos.getOffset() - 1);
300 } catch (BadLocationException e1){
301 // it's OK to print this exception to the current log area
302 e1.printStackTrace(Err.getPrintWriter());
303 }// End try
304 }// actionPerformed();
305 }// End class ClearAllAction
306
307 /** Inner class that defines the behaviour of an OutputStream that writes to
308 * the LogArea.
309 */
310 class LogAreaOutputStream extends OutputStream{
311 /** This field dictates the style on how to write */
312 private boolean isErr = false;
313 /** Char style*/
314 private Style style = null;
315
316 /** Constructs an Out or Err LogAreaOutputStream*/
317 public LogAreaOutputStream(boolean anIsErr){
318 isErr = anIsErr;
319 if (isErr){
320 style = addStyle("error", getStyle("default"));
321 StyleConstants.setForeground(style, Color.red);
322 }else {
323 style = addStyle("out",getStyle("default"));
324 StyleConstants.setForeground(style, Color.black);
325 }// End if
326 }// LogAreaOutputStream
327
328 /** Writes an int which must be a the code of a char, into the LogArea,
329 * using the style specified in constructor. The int is downcast to a byte.
330 */
331 public void write(int charCode){
332 // charCode int must be a char. Let us be sure of that
333 charCode &= 0x000000FF;
334 // Convert the byte to a char before put it into the log area
335 char c = (char)charCode;
336 // Insert it in the log Area
337 SwingUtilities.invokeLater(new SwingWriter(String.valueOf(c), style));
338 }// write(int charCode)
339
340 /** Writes an array of bytes into the LogArea,
341 * using the style specified in constructor.
342 */
343 public void write(byte[] data, int offset, int length){
344 // Insert the string to the log area
345 try{
346 SwingUtilities.invokeLater(new SwingWriter(new String(data,offset,
347 length, "UTF-8"),
348 style));
349 }catch(UnsupportedEncodingException uee){
350 // should never happen - all JREs are required to support UTF-8
351 uee.printStackTrace(originalErr);
352 }
353 }// write(byte[] data, int offset, int length)
354 }////End class LogAreaOutputStream
355 }//End class LogArea
|