001package arcommon.exception; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.Dimension; 006import java.awt.GridBagLayout; 007import java.awt.Rectangle; 008import java.awt.event.ActionEvent; 009import java.awt.event.ActionListener; 010import java.util.ArrayList; 011import java.util.List; 012 013import javax.swing.Icon; 014import javax.swing.JButton; 015import javax.swing.JLabel; 016import javax.swing.JPanel; 017import javax.swing.JScrollPane; 018import javax.swing.JTextArea; 019import javax.swing.SwingUtilities; 020import javax.swing.border.EmptyBorder; 021 022import arcommon.ClipboardUtil; 023import arcommon.ReflectionUtil; 024import arcommon.Util; 025import arcommon.ui.other.GUIUtil; 026import arcommon.ui.window.AbstractJDialog; 027 028public class ExceptionViewDialog extends AbstractJDialog { 029 030 private static final int VIEW_IN_DIALOG_LIMIT = 10; 031 032 public static boolean autoShowDetails = false; 033 034 private static ExceptionViewDialog exceptionViewDialog; 035 036 public static void viewException(final ExceptionData exceptionData, final String title) { 037 Runnable runnable = new Runnable() { 038 public void run() { 039 boolean created = false; 040 synchronized (ExceptionViewDialog.class) { 041 if (exceptionViewDialog != null && exceptionViewDialog.isVisible()) { 042 exceptionViewDialog.addException(exceptionData); 043 } else { 044 exceptionViewDialog = new ExceptionViewDialog(exceptionData, title); 045 created = true; 046 } 047 } 048 049 if (created && exceptionViewDialog != null) { 050 exceptionViewDialog.viewTheDialog(); 051 } 052 } 053 }; 054 SwingUtilities.invokeLater(runnable); 055 } 056 057 private List<ExceptionData> exceptions; 058 059 private boolean showingMultipleErrors; 060 private boolean showingDetails; 061 062 private JScrollPane scrollPane; 063 private JTextArea textArea; 064 private JPanel labelPanel; 065 private JLabel label; 066 private JButton detailsJButton; 067 private JButton closeJButton; 068 069 public ExceptionViewDialog(ExceptionData exceptionData, String title) { 070 super(title != null ? title : "Error"); 071 072 exceptions = new ArrayList<ExceptionData>(); 073 exceptions.add(exceptionData); 074 075 getContentPane().setLayout(new BorderLayout(3, 3)); 076 077 scrollPane = new JScrollPane(); 078 scrollPane.setBorder(new EmptyBorder(0, 2, 0, 2)); 079 080 textArea = new JTextArea(); 081 scrollPane.setViewportView(textArea); 082 textArea.setEditable(false); 083 textArea.setEnabled(false); 084 textArea.setDisabledTextColor(Color.BLACK); 085 textArea.setBackground(new Color(238, 238, 238)); 086 087 labelPanel = new JPanel(); 088 getContentPane().add(labelPanel, BorderLayout.CENTER); 089 labelPanel.setLayout(new GridBagLayout()); 090 091 label = new JLabel("<html><body><center>Sorry, but an error occurred.</center><br><br><center>Please try again.</center><br><br><center>If the error occurs again, please report it.<center><br><br><center>It is recommended to restart the application before you continue.<center></body></html>"); 092 labelPanel.add(label, null); 093 094 JPanel southPanel = new JPanel(); 095 getContentPane().add(southPanel, BorderLayout.SOUTH); 096 southPanel.setLayout(new BorderLayout()); 097 098 JPanel buttonPanel = new JPanel(); 099 southPanel.add(buttonPanel, BorderLayout.EAST); 100 101 detailsJButton = new JButton("Show details"); 102 buttonPanel.add(detailsJButton); 103 detailsJButton.addActionListener(new ActionListener() { 104 public void actionPerformed(ActionEvent e1) { 105 eventShowDetailsActionPerformed(e1); 106 } 107 }); 108 109 JButton copyJButton = new JButton("Copy details"); 110 buttonPanel.add(copyJButton); 111 copyJButton.addActionListener(new ActionListener() { 112 public void actionPerformed(ActionEvent e1) { 113 eventCopyDetailsActionPerformed(e1); 114 } 115 }); 116 117 closeJButton = new JButton("Close"); 118 buttonPanel.add(closeJButton); 119 closeJButton.addActionListener(new ActionListener() { 120 public void actionPerformed(ActionEvent e1) { 121 eventCloseActionPerformed(e1); 122 } 123 }); 124 125 getRootPane().setDefaultButton(closeJButton); 126 127 if (Util.getJavaVersion() < 11.0F) { 128 Class<?> defaultLookup = ReflectionUtil.loadClass("sun.swing.DefaultLookup", false); 129 if (defaultLookup != null) { 130 // Icon icon = (Icon)DefaultLookup.get(getRootPane(), getRootPane().getUI(), "OptionPane.errorIcon"); 131 Icon icon = (Icon)ReflectionUtil.invokeStaticMethod(defaultLookup, "get", getRootPane(), getRootPane().getUI(), "OptionPane.errorIcon"); 132 JLabel iconLabel = new JLabel(icon); 133 iconLabel.setIcon(icon); 134 getContentPane().add(iconLabel, BorderLayout.WEST); 135 } 136 } 137 138 setPreferredSize(new Dimension(700, 300)); 139 140 setModal(true); 141 142 if (autoShowDetails) { 143 eventShowDetailsActionPerformed(null); 144 } 145 } 146 147 public void viewTheDialog() { 148 GUIUtil.packAndCenterOnScreen(this); 149 closeJButton.requestFocusInWindow(); 150 setVisible(true); 151 } 152 153 private synchronized String exceptionsToString() { 154 String text = ""; 155 for (int i = 0; i < exceptions.size(); i++) { 156 ExceptionData data = exceptions.get(i); 157 158 if (text.length() > 0) { 159 text += System.getProperty("line.separator"); 160 } 161 162 if (exceptions.size() > 1) { 163 text += "Exception " + (i + 1) + ":" + System.getProperty("line.separator"); 164 } 165 166 String exceptionStackToString = data.exceptionStackToString(true); 167 text += exceptionStackToString; 168 } 169 170 if (text.endsWith(System.getProperty("line.separator"))) { 171 text = text.substring(0, text.length() - System.getProperty("line.separator").length()); 172 } 173 174 return text; 175 } 176 177 private void eventCopyDetailsActionPerformed(ActionEvent e1) { 178 String text = exceptionsToString(); 179 180 ClipboardUtil.writeTextToClipboard(text); 181 182 GUIUtil.showInformation("Error copied to the clipboard"); 183 } 184 185 private void eventShowDetailsActionPerformed(ActionEvent e1) { 186 showingDetails = true; 187 detailsJButton.setVisible(false); 188 189 setSize(new Dimension(800, 500)); 190 setLocationRelativeTo(null); 191 192 getContentPane().remove(labelPanel); 193 getContentPane().add(scrollPane, BorderLayout.CENTER); 194 195 showDetails(); 196 } 197 198 private void showDetails() { 199 String text = exceptionsToString(); 200 textArea.setText(text); 201 202 Runnable runnable = new Runnable() { 203 public void run() { 204 textArea.scrollRectToVisible(new Rectangle(0, 0, 1, 1)); 205 } 206 }; 207 SwingUtilities.invokeLater(runnable); 208 } 209 210 private void eventCloseActionPerformed(ActionEvent e) { 211 dispose(); 212 } 213 214 @Override 215 public void dispose() { 216 synchronized (ExceptionViewDialog.class) { 217 if (exceptionViewDialog == this) { 218 exceptionViewDialog = null; 219 } 220 } 221 super.dispose(); 222 } 223 224 public synchronized void addException(ExceptionData exceptionData) { 225 if (exceptions.size() >= VIEW_IN_DIALOG_LIMIT) { 226 System.err.println("TOO MANY EXCEPTION OCCURRED => Ignore"); 227 return ; 228 } 229 230 exceptions.add(exceptionData); 231 232 Runnable runnable = new Runnable() { 233 public void run() { 234 if (showingDetails) { 235 showDetails(); 236 } else { 237 if (!showingMultipleErrors) { 238 showingMultipleErrors = true; 239 label.setText("<html><body><center>Sorry, but multiple errors has occurred.</center><br><br><center>Please try again.</center><br><br><center>If the errors occur again, please report them.<center><br><br><center>It is recommended to restart the application before you continue.<center></body></html>"); 240 } 241 } 242 } 243 }; 244 SwingUtilities.invokeLater(runnable); 245 } 246 247 private static final long serialVersionUID = 8204185499689626823L; 248}