/* Copyright (C) 2003-2011 JabRef contributors.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package net.sf.jabref.util;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
import javax.swing.*;
import net.sf.jabref.Globals;
/**
* This class redirects the System.err stream so it goes both the way it normally
* goes, and into a ByteArrayOutputStream. We can use this stream to display any
* error messages and stack traces to the user. Such an error console can be
* useful in getting complete bug reports, especially from Windows users,
* without asking users to run JabRef in a command window to catch the error info.
*
* It also offers a separate tab for the log output.
*
* User: alver
* Date: Mar 1, 2006
* Time: 11:13:03 PM
*/
public class ErrorConsole extends Handler {
ByteArrayOutputStream errByteStream = new ByteArrayOutputStream();
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
ArrayList<String> logOutput = new ArrayList<String>();
String logOutputCache = "";
boolean logOutputCacheRefreshNeeded = true;
SimpleFormatter fmt = new SimpleFormatter();
private static final int MAXLOGLINES = 500;
private static ErrorConsole instance = null;
public static ErrorConsole getInstance() {
if (instance == null)
instance = new ErrorConsole();
return instance;
}
private ErrorConsole() {
PrintStream myErr = new PrintStream(errByteStream);
PrintStream tee = new TeeStream(System.err, myErr);
System.setErr(tee);
myErr = new PrintStream(outByteStream);
tee = new TeeStream(System.out, myErr);
System.setOut(tee);
}
private String getErrorMessages() {
return errByteStream.toString();
}
private String getOutput() {
return outByteStream.toString();
}
private String getLog() {
if (logOutputCacheRefreshNeeded) {
StringBuilder sb = new StringBuilder();
for(String line: logOutput) {
sb.append(line);
}
logOutputCache = sb.toString();
}
return logOutputCache;
}
/**
*
* @param tabbed the tabbed pane to add the tab to
* @param output the text to display in the tab
* @param ifEmpty Text to output if textbox is emtpy. may be null
*/
private void addTextArea(JTabbedPane tabbed, String title, String output, String ifEmpty) {
JTextArea ta = new JTextArea(output);
ta.setEditable(false);
if ((ifEmpty!=null) && (ta.getText().length() == 0)) {
ta.setText(ifEmpty);
}
JScrollPane sp = new JScrollPane(ta);
tabbed.addTab(title, sp);
}
public void displayErrorConsole(JFrame parent) {
JTabbedPane tabbed = new JTabbedPane();
addTextArea(tabbed, Globals.lang("Output"), getOutput(), null);
addTextArea(tabbed, Globals.lang("Exceptions"), getErrorMessages(),
Globals.lang("No exceptions have ocurred."));
addTextArea(tabbed, Globals.lang("Log"), getLog(), null);
tabbed.setPreferredSize(new Dimension(500,500));
JOptionPane.showMessageDialog(parent, tabbed,
Globals.lang("Program output"), JOptionPane.ERROR_MESSAGE);
}
class ErrorConsoleAction extends AbstractAction {
JFrame frame;
public ErrorConsoleAction(JFrame frame) {
super(Globals.menuTitle("Show error console"));
putValue(SHORT_DESCRIPTION, Globals.lang("Display all error messages"));
this.frame = frame;
}
public void actionPerformed(ActionEvent e) {
displayErrorConsole(frame);
}
}
public AbstractAction getAction(JFrame parent) {
return new ErrorConsoleAction(parent);
}
// All writes to this print stream are copied to two print streams
public class TeeStream extends PrintStream {
PrintStream out;
public TeeStream(PrintStream out1, PrintStream out2) {
super(out1);
this.out = out2;
}
public void write(byte buf[], int off, int len) {
try {
super.write(buf, off, len);
out.write(buf, off, len);
} catch (Exception e) {
}
}
public void flush() {
super.flush();
out.flush();
}
}
/* * * methods for Logging (required by Handler) * * */
@Override
public void close() throws SecurityException {
}
@Override
public void flush() {
}
@Override
public void publish(LogRecord record) {
String msg = fmt.format(record);
logOutput.add(msg);
if (logOutput.size() < MAXLOGLINES) {
// if we did not yet reach MAXLOGLINES, we just append the string to the cache
logOutputCache = logOutputCache + msg;
} else {
// if we reached MAXLOGLINES, we switch to the "real" caching method and remove old lines
logOutputCacheRefreshNeeded = true;
while (logOutput.size() > MAXLOGLINES) {
// if log is too large, remove first line
// we need a while loop as the formatter may output more than one line
logOutput.remove(0);
}
}
logOutputCacheRefreshNeeded = true;
}
}