import javax.swing.* ;
import java.awt.* ;
import java.awt.event.* ;
/**
* The JExceptionDialog class provides easy-to-use, single-call methods
* for displaying error dialogs based on an exception. The dialogs
* will contain a "Show Details" button that shows the exception's
* stack trace when clicked.
*
* There are two variants of the dialog showing method: showDialog
* and showFatalDialog. The latter should be used if your program
* cannot recover from the exception and needs to shut down.
*
* Example usage:
*
* try {
// Dangerous code here
} catch (SomeException e) {
JExceptionDialog.showDialog(e, null) ;
}
* JExceptionDialog also provides a method for getting the String
* that would be printed to System.err if an exception's printStackTrace method
* was called as a String.
*
* @author Michael Davies (coolguy5678)
*/
public class JExceptionDialog extends JDialog {
private Exception baseException ;
private JButton okButton ;
private JButton detailsButton ;
private JTextArea detailsArea ;
private JScrollPane detailsScroll ;
private boolean detailsIsActive ;
/**
* The title of any exception dialogs that are displayed
* without specifying a title. You may want to set
* this to the name of your application.
*
* The default value is "Error".
*/
public static String defaultTitle = "Error" ;
/**
* The error message of any exception dialogs that are displayed
* without specifying a message.
*
* The default value is "An unexpected error has occurred.".
*/
public static String defaultErrorMessage = "An unexpected error has occurred." ;
/**
* The error message of any fatal exception dialogs that are displayed
* without specifying a message.
*
* The default value is
* "An unexpected fatal error has occurred, and the program must close."
*/
public static String defaultFatalErrorMessage = "A fatal error has occurred, " +
"and the program must close.";
/**
* The Icon that is used on the dialogs.
*
*
* The default is the "error" Icon from
* the look and feel.
*/
public static Icon errorIcon = UIManager.getLookAndFeel().getDefaults().getIcon("OptionPane.errorIcon") ;
//----------
private JExceptionDialog(Exception baseException, Frame owner, String title, String error, String okTitle) {
super(owner, title, true) ;
this.baseException = baseException ;
detailsIsActive = false ;
// Set up the content pane
JPanel cp = new JPanel() ;
cp.setLayout(new BoxLayout(cp, BoxLayout.Y_AXIS)) ;
// Add some padding
cp.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// Add the error message
JLabel label = new JLabel(error, errorIcon, JLabel.CENTER) ;
label.setAlignmentX(Component.CENTER_ALIGNMENT) ;
cp.add(label) ;
cp.add(Box.createRigidArea(new Dimension(0, 10))) ;
// Set up the panel for the buttons
JPanel buttonPanel = new JPanel() ;
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS)) ;
buttonPanel.add(Box.createHorizontalGlue()) ; // Place buttons on the right edge
// Set up the OK button
okButton = new JButton(okTitle) ;
okButton.addActionListener(list) ;
okButton.setActionCommand("ok") ;
buttonPanel.add(okButton) ;
buttonPanel.add(Box.createRigidArea(new Dimension(10, 0))) ;
// Set up the Details button
detailsButton = new JButton("Show Details") ;
detailsButton.addActionListener(list) ;
detailsButton.setActionCommand("details") ;
buttonPanel.add(detailsButton) ;
// Add the button panel
buttonPanel.add(Box.createRigidArea(new Dimension(10, 0))) ;
cp.add(buttonPanel) ;
cp.add(Box.createRigidArea(new Dimension(0, 10))) ;
// Set up the details text area
detailsArea = new JTextArea(getExceptionStackTrace(baseException)) ;
detailsArea.setEditable(false) ;
detailsScroll = new JScrollPane(detailsArea) ;
// Final things
setContentPane(cp) ;
setResizable(false) ;
pack() ;
// Set size of details to our width by 100
detailsScroll.setMinimumSize(new Dimension(getWidth(), 100)) ;
detailsScroll.setPreferredSize(new Dimension(getWidth(), 100)) ;
detailsScroll.setMaximumSize(new Dimension(getWidth(), 100)) ;
}
//----------
private ActionListener list = new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Go away if OK was pressed
if (e.getActionCommand().equals("ok")) {
setVisible(false) ;
dispose() ;
}
// Or toggle the details
else if (e.getActionCommand().equals("details")) {
detailsIsActive = !detailsIsActive ;
if (detailsIsActive) {
getContentPane().add(detailsScroll) ;
detailsButton.setText("Hide Details") ;
} else {
getContentPane().remove(detailsScroll) ;
detailsButton.setText("Show Details") ;
}
pack() ;
}
}
} ;
//----------
/**
* Get the text that would be printed to System.err
* if e.printStackTrace() was called, as a String.
*
* @param e the Throwable to get the stack trace String of
* @return the String description of the stack trace
*/
public static String getExceptionStackTrace(Throwable e) {
StringBuffer s = new StringBuffer() ;
// Borrowed this code from Throwable.printStackTrace
s.append(e + "\n") ;
StackTraceElement[] trace = e.getStackTrace() ;
for (int i = 0 ; i < trace.length ; i++) s.append("\tat " + trace[i] + "\n") ;
Throwable ourCause = e.getCause() ;
if (ourCause != null) appendStackTraceAsCause(ourCause, trace, s) ;
return s.toString() ;
}
private static void appendStackTraceAsCause(Throwable e, StackTraceElement[] causedTrace, StringBuffer s) {
// Borrowed this code from Throwable.printStackTraceAsCause
StackTraceElement[] trace = e.getStackTrace() ;
int m = trace.length - 1, n = causedTrace.length - 1 ;
while (m >= 0 && n >= 0 && trace[m].equals(causedTrace[n])) {
m-- ; n-- ;
}
int framesInCommon = trace.length - 1 - m ;
s.append("Caused by: " + e + "\n") ;
for (int i = 0 ; i <= m ; i++) s.append("\tat " + trace[i] + "\n") ;
if (framesInCommon != 0) s.append("\t... " + framesInCommon + " more\n") ;
Throwable ourCause = e.getCause() ;
if (ourCause != null) appendStackTraceAsCause(ourCause, trace, s) ;
}
/**
* Display an error dialog.
*
* @param baseException the exception on which to base the dialog
* @param owner the owner of the exception dialog (can be null)
* @param title the title of the dialog
* @param errorMessage the error message to display on the dialog
* @throws NullPointerException if either baseException, title or errorMessage is null
*/
public static void showDialog(Exception baseException, Frame owner, String title, String errorMessage) {
if (baseException == null || title == null || errorMessage == null) {
throw new NullPointerException() ;
}
new JExceptionDialog(baseException, owner, title, errorMessage, "OK").setVisible(true) ;
}
/**
* Display an error dialog, using the default title and error message.
*
* @param baseException the exception on which to base the dialog
* @param owner the owner of the exception dialog (can be null)
* @throws NullPointerException if either argument is null
*/
public static void showDialog(Exception baseException, Frame owner) {
showDialog(baseException, owner, defaultTitle, defaultErrorMessage) ;
}
/**
* Display an error dialog for a fatal error, and exit.
*
* @param baseException the exception on which to base the dialog
* @param owner the owner of the exception dialog (can be null)
* @param title the title of the dialog
* @param errorMessage the error message to display on the dialog
* @param errorCode the error code to exit with
* @throws NullPointerException if either baseException, title or errorMessage is null
*/
public static void showFatalDialog(Exception baseException, Frame owner, String title, String errorMessage, int errorCode) {
if (baseException == null || title == null || errorMessage == null) {
throw new NullPointerException() ;
}
new JExceptionDialog(baseException, owner, title, errorMessage, "Exit").setVisible(true) ;
System.exit(errorCode) ;
}
/**
* Display an error dialog for a fatal error, and exit, using the default title, error message,
* and an error code of -1.
*
* @param baseException the exception on which to base the dialog
* @param owner the owner of the exception dialog (can be null)
* @throws NullPointerException if either argument is null
*/
public static void showFatalDialog(Exception baseException, Frame owner) {
showFatalDialog(baseException, owner, defaultTitle, defaultFatalErrorMessage, -1) ;
}
}