1
22 package org.crosswire.common.swing;
23
24 import java.awt.BorderLayout;
25 import java.awt.Component;
26 import java.awt.Cursor;
27 import java.awt.Dimension;
28 import java.awt.FlowLayout;
29 import java.awt.Font;
30 import java.awt.Frame;
31 import java.awt.event.ActionEvent;
32 import java.awt.event.ActionListener;
33 import java.awt.event.ItemEvent;
34 import java.awt.event.ItemListener;
35 import java.io.File;
36 import java.io.FileReader;
37 import java.io.IOException;
38 import java.io.LineNumberReader;
39 import java.text.MessageFormat;
40 import java.util.ArrayList;
41 import java.util.List;
42
43 import javax.swing.BorderFactory;
44 import javax.swing.DefaultComboBoxModel;
45 import javax.swing.JButton;
46 import javax.swing.JCheckBox;
47 import javax.swing.JComboBox;
48 import javax.swing.JDialog;
49 import javax.swing.JLabel;
50 import javax.swing.JList;
51 import javax.swing.JPanel;
52 import javax.swing.JScrollPane;
53 import javax.swing.JSplitPane;
54 import javax.swing.JTextArea;
55 import javax.swing.ListSelectionModel;
56 import javax.swing.SwingUtilities;
57 import javax.swing.event.ListSelectionEvent;
58 import javax.swing.event.ListSelectionListener;
59
60 import org.crosswire.common.util.FileUtil;
61 import org.crosswire.common.util.Reporter;
62 import org.crosswire.common.util.ReporterEvent;
63 import org.crosswire.common.util.ReporterListener;
64 import org.crosswire.common.util.StackTrace;
65 import org.crosswire.common.xml.XMLUtil;
66
67
74 public final class ExceptionPane extends JPanel {
75
78 private ExceptionPane(Throwable ex) {
79 this.ex = ex;
80 initialise();
81 setDisplayedException(ex);
82 }
83
84
87 private void initialise() {
88 String exmsg = MessageFormat.format("<html><font size=\"-1\">{0}</font> {1}",
89 CWMsg.gettext("An error has occurred:"), ExceptionPane.getHTMLDescription(ex)
91 );
92
93 JLabel message = new JLabel();
95 message.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
96 message.setText(exmsg);
97 message.setIcon(GuiUtil.getIcon("toolbarButtonGraphics/general/Stop24.gif"));
98 message.setIconTextGap(20);
99
100 JPanel banner = new JPanel(new BorderLayout());
101 banner.add(message, BorderLayout.CENTER);
102 list = new JList();
103 list.setVisibleRowCount(6);
104 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
105 Font courier = new Font("Monospaced", Font.PLAIN, 12);
106 list.setFont(courier);
107
108 JPanel buttons = new JPanel(new BorderLayout());
109
110 okBox = new JPanel(new FlowLayout());
111 buttons.add(okBox, BorderLayout.CENTER);
112
113 detail = new JCheckBox();
115 detail.addItemListener(new SelectedItemListener(this));
116 detail.setText(CWMsg.gettext("Details"));
118 if (detailShown) {
119 buttons.add(detail, BorderLayout.LINE_START);
120 }
121
122 upper = new JPanel(new BorderLayout());
123 upper.add(banner, BorderLayout.NORTH);
124 upper.add(buttons, BorderLayout.CENTER);
125
126 List<Throwable> causes = new ArrayList<Throwable>();
127 Throwable throwable = ex;
128 while (throwable != null) {
129 causes.add(throwable);
130 throwable = throwable.getCause();
131 }
132 Throwable[] exs = causes.toArray(new Throwable[causes.size()]);
133
134 JComboBox traces = new JComboBox();
135 traces.setModel(new DefaultComboBoxModel(exs));
136 traces.addActionListener(new SelectActionListener(this, traces));
137
138 JPanel heading = new JPanel(new BorderLayout());
139 heading.add(traces, BorderLayout.CENTER);
140
141 lower = new JPanel(new BorderLayout());
142 lower.add(heading, BorderLayout.NORTH);
143
144 if (sources.length == 0) {
147 lower.add(new CWScrollPane(list), BorderLayout.CENTER);
148 } else {
149 label = new JLabel();
151 label.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
152 label.setFont(courier);
153 label.setText(CWMsg.gettext("No File"));
155 text = new JTextArea();
156 text.setEditable(false);
157 text.setFont(courier);
158
159 JScrollPane textScroll = new CWScrollPane(text);
160 textScroll.setColumnHeaderView(label);
161
162 JSplitPane split = new FixedSplitPane();
163 split.setResizeWeight(0.2D);
165 split.setOrientation(JSplitPane.VERTICAL_SPLIT);
166 split.setContinuousLayout(true);
167 split.setTopComponent(new CWScrollPane(list));
168 split.setBottomComponent(textScroll);
169 split.setBorder(BorderFactory.createEmptyBorder());
170 split.setPreferredSize(new Dimension(500, 300));
171 lower.add(split, BorderLayout.CENTER);
172 }
173
174 this.setLayout(new BorderLayout());
175 this.add(upper, BorderLayout.NORTH);
176 }
177
178
181 protected synchronized void changeDetail() {
182 if (detail.isSelected()) {
183 ExceptionPane.this.add(lower, BorderLayout.CENTER);
184 } else {
185 ExceptionPane.this.remove(lower);
186 }
187
188 GuiUtil.getDialog(ExceptionPane.this).pack();
189 }
190
191
194 protected synchronized void setDisplayedException(Throwable ex) {
195 StackTrace st = new StackTrace(ex);
196 if (sources.length > 0) {
197 list.addListSelectionListener(new ExceptionPane.CustomLister(st, text, label));
198 }
199 list.setModel(new StackTraceListModel(st));
200 }
201
202
210 public static void showExceptionDialog(Component parent, Throwable ex) {
211 final ExceptionPane pane = new ExceptionPane(ex);
212
213 Frame root = GuiUtil.getFrame(parent);
215
216 String error = CWMsg.gettext("Error");
218
219 final JDialog dialog = new JDialog(root, error, true);
224 dialog.getRootPane().setLayout(new BorderLayout());
225 dialog.getRootPane().setBorder(BorderFactory.createMatteBorder(5, 5, 5, 5, pane.upper.getBackground()));
226 dialog.getRootPane().add(pane, BorderLayout.CENTER);
227
228 final ActionFactory actions = new ActionFactory(pane);
229
230 JButton ok = actions.createJButton(actions.addAction("OK", CWMsg.gettext("OK")), new ActionListener() {
232 public void actionPerformed(ActionEvent e) {
233 dialog.dispose();
234 }
235 });
236
237 pane.okBox.add(ok);
238 dialog.getRootPane().setDefaultButton(ok);
239
240 GuiUtil.centerOnScreen(dialog);
241 GuiUtil.applyDefaultOrientation(dialog);
242 dialog.pack();
243 dialog.setVisible(true);
244 }
245
246
252 public static boolean isDetailShown() {
253 return ExceptionPane.detailShown;
254 }
255
256
263 public static void setDetailShown(boolean detailShown) {
264 ExceptionPane.detailShown = detailShown;
265 }
266
267
273 public static void setSourcePath(File[] sourcePath) {
274 ExceptionPane.sources = sourcePath.clone();
275 }
276
277
282 public static File[] getSourcePath() {
283 return sources.clone();
284 }
285
286
294 public static synchronized void setHelpDeskListener(boolean joined) {
295 if (joined) {
296 Reporter.addReporterListener(li);
297 } else {
298 Reporter.removeReporterListener(li);
299 }
300 }
301
302
305 public static String getHTMLDescription(Throwable ex) {
306 StringBuilder retcode = new StringBuilder();
307
308 String msg = ex.getMessage();
310 if (msg == null || "".equals(msg)) {
311 msg = CWMsg.gettext("No description available.");
314 }
315 String orig = XMLUtil.escape(msg);
316 msg = orig.replaceAll("\n", "<br>");
317
318 retcode.append("<br>");
319 retcode.append(msg);
320
321 Throwable nex = ex.getCause();
323 if (nex != null) {
324 retcode.append("<p><br><font size=\"-1\">");
325 retcode.append(CWMsg.gettext("This was caused by:"));
327 retcode.append("</font>");
328 retcode.append(getHTMLDescription(nex));
329 }
330
331 return retcode.toString();
332 }
333
334
337 private static final class SelectedItemListener implements ItemListener {
338
341 public SelectedItemListener(ExceptionPane ep) {
342 pane = ep;
343 }
344
345
348 public void itemStateChanged(ItemEvent ev) {
349 pane.changeDetail();
350 }
351
352 private ExceptionPane pane;
353 }
354
355
358 private static final class SelectActionListener implements ActionListener {
359
363 public SelectActionListener(ExceptionPane ep, JComboBox cb) {
364 pane = ep;
365 traces = cb;
366 }
367
368
371 public void actionPerformed(ActionEvent ev) {
372 Throwable th = (Throwable) traces.getSelectedItem();
373 pane.setDisplayedException(th);
374 }
375
376 private ExceptionPane pane;
377 private JComboBox traces;
378 }
379
380
384 private static final class CustomLister implements ListSelectionListener {
385
396 public CustomLister(StackTrace st, JTextArea text, JLabel label) {
397 this.st = st;
398 this.mytext = text;
399 this.mylabel = label;
400 }
401
402
405 public void valueChanged(ListSelectionEvent ev) {
406 if (ev.getValueIsAdjusting()) {
407 return;
408 }
409
410 SwingUtilities.getRoot(mylabel).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
412
413 JList lst = (JList) ev.getSource();
415 int level = lst.getSelectedIndex();
416 String name = st.getClassName(level);
417
418 if (name.indexOf('$') != -1) {
419 name = name.substring(0, name.indexOf('$'));
420 }
421
422 int line_num = st.getLineNumber(level);
423 String orig = name;
424 Integer errorLine = Integer.valueOf(line_num);
425 mylabel.setText(CWMsg.gettext("No File"));
427
428 name = File.separator + orig.replace('.', File.separatorChar) + FileUtil.EXTENSION_JAVA;
430 File[] srcs = ExceptionPane.getSourcePath();
431 for (int i = 0; i < srcs.length; i++) {
432 File file = new File(srcs[i], name);
433 if (file.isFile() && file.canRead()) {
434 StringBuilder data = new StringBuilder();
436
437 int selection_start = 0;
439 int selection_end = 0;
440
441 LineNumberReader in = null;
442 try {
443 String found = CWMsg.gettext("Error on line {0} in file {1}", errorLine, file.getCanonicalPath());
447 mylabel.setText(found);
448 in = new LineNumberReader(new FileReader(file));
449 while (true) {
450 String line = in.readLine();
451 if (line == null) {
452 break;
453 }
454 data.append(line).append('\n');
455
456 int current_line = in.getLineNumber();
457 if (current_line == line_num - 1) {
458 selection_start = data.length();
459 }
460 if (current_line == line_num) {
461 selection_end = data.length() - 1;
462 }
463 }
464 } catch (IOException ex) {
465 data.append(ex.getMessage());
466 } finally {
467 if (in != null) {
468 try {
469 in.close();
470 } catch (IOException e) {
471 data.append(e.getMessage());
472 }
473 }
474 }
475
476 mytext.setText(data.toString());
478 mytext.setSelectionStart(selection_start);
479 mytext.setSelectionEnd(selection_end);
480
481 SwingUtilities.getRoot(mylabel).setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
482 return;
483 }
484 }
485
486 StringBuilder error = new StringBuilder(CWMsg.gettext("Cannot open source for: {0}, line: {1}\n", st.getClassName(level), errorLine));
490 for (int i = 0; i < srcs.length; i++) {
491 error.append(CWMsg.gettext("Tried: {0}\n", srcs[i].getAbsolutePath() + name));
495 }
496
497 mytext.setText(error.toString());
498 SwingUtilities.getRoot(mylabel).setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
499 }
500
501
504 private StackTrace st;
505
506
509 private JTextArea mytext;
510
511
514 private JLabel mylabel;
515 }
516
517
520 static final class ExceptionPaneReporterListener implements ReporterListener {
521
527 public void reportException(ReporterEvent ev) {
528 SwingUtilities.invokeLater(new ExceptionRunner(ev));
530 }
531
532
538 public void reportMessage(ReporterEvent ev) {
539 SwingUtilities.invokeLater(new MessageRunner(ev));
541 }
542 }
543
544
547 private static final class ExceptionRunner implements Runnable {
548
551 public ExceptionRunner(ReporterEvent ev) {
552 event = ev;
553 }
554
555
560 public void run() {
561 if (event.getSource() instanceof Component) {
562 showExceptionDialog((Component) event.getSource(), event.getException());
563 } else {
564 showExceptionDialog(null, event.getException());
565 }
566 }
567
568 private ReporterEvent event;
569 }
570
571
574 private static final class MessageRunner implements Runnable {
575
578 public MessageRunner(ReporterEvent ev) {
579 event = ev;
580 }
581
582
585 public void run() {
586 if (event.getSource() instanceof Component) {
587 CWOptionPane.showMessageDialog((Component) event.getSource(), event.getMessage());
588 } else {
589 CWOptionPane.showMessageDialog(null, event.getMessage());
590 }
591 }
592
593 private ReporterEvent event;
594 }
595
596
599 private Throwable ex;
600
601 private JList list;
603 private JPanel upper;
604 private JLabel label;
605 private JTextArea text;
606 private JPanel okBox;
607 private JCheckBox detail;
608 private JPanel lower;
609
610
613 private static boolean detailShown;
614
615
618 private static File[] sources = new File[0];
619
620
623 private static ExceptionPaneReporterListener li = new ExceptionPaneReporterListener();
624
625
628 private static final long serialVersionUID = 3258126947203495219L;
629 }
630