/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2008
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package alma.acs.logging.dialogs.error;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
import com.cosylab.logging.engine.log.LogTypeHelper;
/**
* The dialog to show the errors.
* <P>
* The dialog stores errors till maxLength chars is reached.
* If the log exceeds that number, they are automatically flushed in
* a file in <code>ACS_TMP</code>.
* <P>
* The dimension defaults to <code>ERROR_LOG_DEFAULT_SIZE</code> and can be changed setting
* the <code>ERRORLOG_SIZE_PROP_NAME</code> property.
* <P>
* The content of the dialog can be saved and cleared.
*
*
* @author acaproni
*
*/
public class ErrorLogDialog extends JDialog implements ActionListener {
/**
* The log text appears into a <code>JtextArea</code>
*/
private final JTextArea logTA= new JTextArea("", 20, 60);
/**
* The document of the TextArea
*/
private PlainDocument document = new PlainDocument();
/**
* The button to close (hide) the dialog
*/
private JButton closeBtn;
/**
* The button to erase all the logs (i.e. the logs shown in the window and
* those flushed on the temporary file)
*/
private JButton cleanAllBtn;
/**
* / The button to save all the logs in a file (it saves the content of the
* temporary log file on disk and the content of the text area)
* <P>
* The button is enabled only if some log has been flushed on disk
*/
private JButton saveAllBtn;
/**
* The toolbar
*/
private JToolBar toolBar;
/**
* The maximum dimension of the log to keep in memory.
* <P>
* Its value is read from a property.
* If the value is 0, the error log is unlimited
*/
private long maxLength;
/**
* / The name of the property defining the size of the error log
*/
private final String ERRORLOG_SIZE_PROP_NAME = "jlog.errorlog.size";
/**
* / The default maximum size of the ERROR LOG
*/
private final long ERROR_LOG_DEFAULT_SIZE = 50 *1000000;
/**
* The file to flush logs
*/
private ErrorLogFile outFile = null;
/**
* Constructor
*
* @param owner The owner of the dialog
* @param title The title
* @param modal Modal type
*/
public ErrorLogDialog(Frame owner, String title, boolean modal) {
super(owner, title, modal);
this.setDefaultCloseOperation(HIDE_ON_CLOSE);
maxLength = Long.getLong(ERRORLOG_SIZE_PROP_NAME, ERROR_LOG_DEFAULT_SIZE);
System.out.println("Max length of in-memory error log "+maxLength);
// Create the temporary file
outFile = new ErrorLogFile(30,"jlog.",".error.log",System.getProperty("ACS.tmp"),true,false);
initGUI();
pack();
setVisible(false);
toFront();
}
/**
* Builds the content of the GUI
*
*/
private void initGUI() {
ImageIcon icon = new ImageIcon(LogTypeHelper.class.getResource("/errorLogIcon.png"));
setIconImage(icon.getImage());
synchronized(logTA) {
logTA.setDocument(document);
logTA.setEditable(false);
}
JScrollPane logSP = new JScrollPane(logTA);
JRootPane mainPnl = this.getRootPane();
mainPnl.setLayout(new BorderLayout());
mainPnl.add(logSP,BorderLayout.CENTER);
// Close button
JPanel btnPnl = new JPanel(new FlowLayout());
closeBtn = new JButton("Close");
closeBtn.addActionListener(this);
btnPnl.add(closeBtn);
mainPnl.add(btnPnl,BorderLayout.SOUTH);
// Build the toolbar
toolBar = new JToolBar();
JPanel toolBarPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
ImageIcon cleanIcon=new ImageIcon(ErrorLogDialog.class.getResource("/delete.png"));
cleanAllBtn = new JButton("<HTML><Font size=\"-1\">Clean</FONT></HTML>",cleanIcon);
cleanAllBtn.addActionListener(this);
cleanAllBtn.setToolTipText("Delete log");
ImageIcon saveIcon=new ImageIcon(ErrorLogDialog.class.getResource("/disk.png"));
saveAllBtn = new JButton("<HTML><Font size=\"-1\">Save</FONT></HTML>",saveIcon);
saveAllBtn.addActionListener(this);
saveAllBtn.setToolTipText("Save log");
toolBarPanel.add(cleanAllBtn);
toolBarPanel.add(saveAllBtn);
toolBar.add(toolBarPanel);
mainPnl.add(toolBar, BorderLayout.PAGE_START);
rationalizeButtons();
}
/**
* Add a String to the error log and show/hide the dialog
* if it is the first time an error is added
*
* @param str The string to append in the TextArea
* @param show Show/hide the dialog
*/
public synchronized void appendText(String str) {
class RunAsyncAppend implements Runnable {
private String theString;
public RunAsyncAppend(String str) {
theString=str;
}
public void run() {
int len = document.getLength();
// Check the number of chars in the widget
synchronized(logTA) {
if (maxLength>0 && len>maxLength) {
// Flush some chars in the file
String strToFlush;
try {
strToFlush = logTA.getText(0, theString.length());
} catch (BadLocationException ble) {
System.out.println("Error removing text from the TextArea: "+ble.getMessage());
ble.printStackTrace();
return;
}
logTA.replaceRange("", 0, theString.length());
try {
outFile.append(strToFlush);
} catch (Throwable t) {
JOptionPane.showInternalMessageDialog(
logTA,
"<HTML>Error saving errors in temp file: <I>"+
t.getMessage()+
"</I><BR>saving is limited to the content of the text area.</HTML>",
"Error saving errors ",
JOptionPane.INFORMATION_MESSAGE);
}
}
logTA.append(theString);
}
rationalizeButtons();
}
};
RunAsyncAppend runAppend = new RunAsyncAppend(str);
SwingUtilities.invokeLater(runAppend);
}
/**
* Clear the log in the windows and the log in the file
*
*/
public synchronized void clearAll() {
Runnable clearTA = new Runnable() {
public void run() {
synchronized (logTA) {
if (document.getLength()>0) {
logTA.replaceRange("", 0, document.getLength());
}
}
}
};
SwingUtilities.invokeLater(clearTA);
outFile.clear();
rationalizeButtons();
}
/**
* @see java.awt.event.ActionListener
* @see java.awt.event.ActionEvent
*/
public void actionPerformed(ActionEvent e) {
if (e.getSource()==closeBtn) {
setVisible(false);
} else if (e.getSource()==saveAllBtn) {
saveAllLog();
} else if (e.getSource()==cleanAllBtn) {
clearAll();
} else {
System.err.println("Action unknown");
}
}
/**
* Save the content of the temporary file and the content of the text area
* into a file
*
*/
private void saveAllLog() {
File f=null;
JFileChooser chooser = new JFileChooser();
chooser.setMultiSelectionEnabled(false);
chooser.setDialogTitle("Save error log");
int returnVal = chooser.showOpenDialog(this);
if(returnVal == JFileChooser.APPROVE_OPTION) {
f = chooser.getSelectedFile();
} else {
return;
}
FileOutputStream fStream=null;
try {
fStream = new FileOutputStream(f);
} catch (FileNotFoundException fnfe) {
JOptionPane.showMessageDialog(this, "Error opening the file", "Error saving the log", JOptionPane.ERROR_MESSAGE);
System.out.println(fnfe.getMessage());
fnfe.printStackTrace();
return;
}
BufferedOutputStream bufOutStream = new BufferedOutputStream(fStream);
// Save the log from the temporary file
try {
outFile.copy(bufOutStream);
} catch (Exception e) {
System.err.println("Error saving the logs on disk: "+e.getMessage());
JOptionPane.showMessageDialog(this, "Error saving logs from temp file", "Error saving error log", JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
return;
}
// Save the content of the window
synchronized (logTA) {
try {
bufOutStream.write(logTA.getText().getBytes());
} catch (IOException ioe) {
JOptionPane.showMessageDialog(this, "Error writing on file", "Error saving the log", JOptionPane.ERROR_MESSAGE);
System.out.println(ioe.getMessage());
ioe.printStackTrace();
return;
}
}
try {
bufOutStream.flush();
bufOutStream.close();
} catch (IOException ioe) {
JOptionPane.showMessageDialog(this, "Error closing the file", "Error saving the log", JOptionPane.ERROR_MESSAGE);
System.out.println(ioe.getMessage());
ioe.printStackTrace();
return;
}
}
/**
* Enable/Disable the buttons in the toolbar depending on the
* content of the text area and the existence of the temporary
* file
*/
private void rationalizeButtons() {
Runnable ratio = new Runnable() {
public void run() {
saveAllBtn.setEnabled(document.getLength()>0);
cleanAllBtn.setEnabled(document.getLength()>0);
}
};
SwingUtilities.invokeLater(ratio);
}
/* (non-Javadoc)
* @see java.awt.Window#dispose()
*/
@Override
public void dispose() {
if (outFile!=null) {
outFile.close();
}
super.dispose();
}
/**
* Make the dialog visible and position it over the passed component
*
* @param visible If <code>true</code> show the component
* @param c The component to show this dialog over
*/
public void setVisible(boolean visible, Component c) {
super.setVisible(visible);
if (visible) {
setLocationRelativeTo(c);
}
}
}