package forge.error; import forge.properties.NewConstants; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.io.*; import java.util.Map; import java.util.Map.Entry; import static forge.properties.ForgeProps.getLocalized; import static forge.properties.ForgeProps.getProperty; import static java.awt.event.InputEvent.CTRL_DOWN_MASK; import static java.awt.event.KeyEvent.VK_S; import static javax.swing.JOptionPane.DEFAULT_OPTION; import static javax.swing.JOptionPane.ERROR_MESSAGE; /** * The class ErrorViewer. Enables showing and saving error messages that occurred in forge. * * @author Clemens Koza * @version V1.0 02.08.2009 */ public class ErrorViewer implements NewConstants, NewConstants.LANG.ErrorViewer { /** Constant <code>nameOS="os.name"</code> */ private static final String nameOS = "os.name"; /** Constant <code>versionOS="os.version"</code> */ private static final String versionOS = "os.version"; /** Constant <code>architectureOS="os.arch"</code> */ private static final String architectureOS = "os.arch"; /** Constant <code>versionJava="java.version"</code> */ private static final String versionJava = "java.version"; /** Constant <code>vendorJava="java.vendor"</code> */ private static final String vendorJava = "java.vendor"; /** Constant <code>ALL_THREADS_ACTION</code> */ public static final Action ALL_THREADS_ACTION = new ShowAllThreadsAction(); /** * Shows an error dialog taking the exception's message as the error message. * * @param ex a {@link java.lang.Throwable} object. */ public static void showError(Throwable ex) { showError(ex, null); } /** * Shows an error dialog creating the error message by a formatting operation. * * @param ex a {@link java.lang.Throwable} object. * @param format a {@link java.lang.String} object. * @param args a {@link java.lang.Object} object. */ public static void showError(Throwable ex, String format, Object... args) { if (ex == null) return; showError(ex, String.format(format, args)); } /** * Shows an error dialog with the specified error message. * * @param ex a {@link java.lang.Throwable} object. * @param message a {@link java.lang.String} object. */ public static void showError(final Throwable ex, String message) { if (ex == null) return; final StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); printError(pw, ex, message); showDialog(sw.toString()); } /** * Shows an error without an exception that caused it. * * @param format a {@link java.lang.String} object. * @param args a {@link java.lang.Object} object. */ public static void showError(String format, Object... args) { showError(String.format(format, args)); } /** * Shows an error without an exception that caused it. * * @param message a {@link java.lang.String} object. */ public static void showError(String message) { showError(new Exception(), message); } /** * Shows an error message for all running threads. * * @param format a {@link java.lang.String} object. * @param args a {@link java.lang.Object} object. */ public static void showErrorAllThreads(String format, Object... args) { showErrorAllThreads(String.format(format, args)); } /** * Shows an error message for all running threads. * * @param message a {@link java.lang.String} object. */ public static void showErrorAllThreads(String message) { final StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); printError(pw, message); showDialog(sw.toString()); } /** * <p>showDialog.</p> * * @param fullMessage a {@link java.lang.String} object. */ private static void showDialog(String fullMessage) { JTextArea area = new JTextArea(fullMessage, 40, 90); area.setFont(new Font("Monospaced", Font.PLAIN, 10)); area.setEditable(false); area.setLineWrap(true); area.setWrapStyleWord(true); //Button is not modified, String gets the automatic listener to hide the dialog Object[] options = { new JButton(new SaveAction(area)), getLocalized(BUTTON_CLOSE), new JButton(new ExitAction())}; JOptionPane pane = new JOptionPane(new JScrollPane(area), ERROR_MESSAGE, DEFAULT_OPTION, null, options, options[1]); JDialog dlg = pane.createDialog(null, getLocalized(TITLE)); dlg.setResizable(true); dlg.setVisible(true); dlg.dispose(); } /** * Prints the error message for the specified exception to the print writer * * @param pw a {@link java.io.PrintWriter} object. * @param ex a {@link java.lang.Throwable} object. * @param message a {@link java.lang.String} object. */ private static void printError(PrintWriter pw, Throwable ex, String message) { if (message != null) System.err.println(message); ex.printStackTrace(); pw.printf(getLocalized(MESSAGE), getProperty(FORUM), getProperty(MAIL), message != null ? message : ex.getMessage(), getProperty(VERSION), System.getProperty(nameOS), System.getProperty(versionOS), System.getProperty(architectureOS), System.getProperty(versionJava), System.getProperty(vendorJava)); ex.printStackTrace(pw); } /** * Prints the error message to the print writer, showing all running threads' stack traces. * * @param pw a {@link java.io.PrintWriter} object. * @param message a {@link java.lang.String} object. */ private static void printError(PrintWriter pw, String message) { System.err.println(message); pw.printf(getLocalized(MESSAGE), getProperty(FORUM), getProperty(MAIL), message, getProperty(VERSION), System.getProperty(nameOS), System.getProperty(versionOS), System.getProperty(architectureOS), System.getProperty(versionJava), System.getProperty(vendorJava)); Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces(); for (Entry<Thread, StackTraceElement[]> e : traces.entrySet()) { pw.println(); pw.printf("%s (%s):%n", e.getKey().getName(), e.getKey().getId()); for (StackTraceElement el : e.getValue()) { pw.println(el); } } } private static class SaveAction extends AbstractAction { private static final long serialVersionUID = 9146834661273525959L; private static JFileChooser c; private JTextArea area; public SaveAction(JTextArea area) { super(getLocalized(BUTTON_SAVE)); putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(VK_S, CTRL_DOWN_MASK)); this.area = area; } public void actionPerformed(ActionEvent e) { if (c == null) c = new JFileChooser(); File f; for (int i = 0; ; i++) { String name = String.format("%TF-%02d.txt", System.currentTimeMillis(), i); f = new File(name); if (!f.exists()) break; } c.setSelectedFile(f); c.showSaveDialog(null); f = c.getSelectedFile(); try { BufferedWriter bw = new BufferedWriter(new FileWriter(f)); bw.write(area.getText()); bw.close(); } catch (IOException ex) { showError(ex, getLocalized(ERRORS.SAVE_MESSAGE)); } } } private static class ExitAction extends AbstractAction { private static final long serialVersionUID = 276202595758381626L; public ExitAction() { super(getLocalized(BUTTON_EXIT)); } public void actionPerformed(ActionEvent e) { System.exit(0); } } private static class ShowAllThreadsAction extends AbstractAction { private static final long serialVersionUID = 5638147106706803363L; public ShowAllThreadsAction() { super(getLocalized(SHOW_ERROR)); } public void actionPerformed(ActionEvent e) { showErrorAllThreads(getLocalized(ERRORS.SHOW_MESSAGE)); } } }