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}