001: /*
002: * ExceptionErrorDialog.java
003: *
004: * Copyright (C) 2002, 2003, 2004, 2005, 2006 Takis Diakoumis
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: */
021:
022: package org.underworldlabs.swing;
023:
024: import java.awt.BorderLayout;
025: import java.awt.Container;
026: import java.awt.Dimension;
027: import java.awt.Frame;
028: import java.awt.GridBagConstraints;
029: import java.awt.GridBagLayout;
030: import java.awt.Insets;
031: import java.awt.Point;
032: import java.awt.Toolkit;
033: import java.awt.datatransfer.StringSelection;
034: import java.awt.event.ActionEvent;
035: import java.awt.event.ActionListener;
036: import java.awt.event.ComponentEvent;
037: import java.awt.event.ComponentListener;
038: import java.io.PrintWriter;
039: import java.io.StringWriter;
040: import java.sql.SQLException;
041: import java.util.StringTokenizer;
042: import java.util.Vector;
043: import javax.swing.Icon;
044: import javax.swing.JButton;
045: import javax.swing.JDialog;
046: import javax.swing.JLabel;
047: import javax.swing.JPanel;
048: import javax.swing.JScrollPane;
049: import javax.swing.JTextArea;
050: import javax.swing.UIManager;
051: import org.underworldlabs.swing.util.IconUtilities;
052:
053: /* ----------------------------------------------------------
054: * CVS NOTE: Changes to the CVS repository prior to the
055: * release of version 3.0.0beta1 has meant a
056: * resetting of CVS revision numbers.
057: * ----------------------------------------------------------
058: */
059:
060: /**
061: * Generic error dialog box displaying the stack trace.
062: *
063: * @author Takis Diakoumis
064: * @version $Revision: 1.8 $
065: * @date $Date: 2006/09/26 13:18:38 $
066: */
067: public class ExceptionErrorDialog extends JDialog implements
068: ActionListener, ComponentListener {
069:
070: /** the error message */
071: private String message;
072:
073: /** the exception list */
074: private Vector<Throwable> exceptions;
075:
076: /** empty exception indicating the last in a chain */
077: private Throwable noMoreExceptions;
078:
079: /** the stack trace text pane */
080: private JTextArea textPane;
081:
082: /** the show stack button */
083: private JButton showStackButton;
084:
085: /** the close button */
086: private JButton closeButton;
087:
088: /** the next excpetion button */
089: private JButton nextButton;
090:
091: /** the previous excpetion button */
092: private JButton previousButton;
093:
094: /** the paste stack button */
095: private JButton pasteButton;
096:
097: /** the stack trace panel */
098: private JPanel stackTracePanel;
099:
100: /** the default height */
101: private int defaultHeight;
102:
103: /** the default width */
104: private int defaultWidth;
105:
106: /** the current exception index (chained exceptions) */
107: private int selectedIndex;
108:
109: /** Creates a new instance of ExceptionErrorDialog */
110: public ExceptionErrorDialog(Frame owner, String message,
111: Throwable exception) {
112: super (owner, "Error Message", true);
113: this .message = message;
114:
115: exceptions = new Vector<Throwable>();
116: // we want the underlying cause
117: if (exception.getCause() != null) {
118: exceptions.add(exception.getCause());
119: } else {
120: exceptions.add(exception);
121: }
122: selectedIndex = 0;
123:
124: try {
125: init();
126: } catch (Exception e) {
127: e.printStackTrace();
128: }
129: }
130:
131: private void init() throws Exception {
132: Icon errorIcon = UIManager.getIcon("OptionPane.errorIcon");
133: if (errorIcon == null) {
134: // if we don't have one (some LAFs), try the warning icon
135: errorIcon = UIManager.getIcon("OptionPane.warningIcon");
136: }
137:
138: closeButton = new JButton("Close");
139: closeButton.addActionListener(this );
140:
141: showStackButton = new JButton("Show Stack Trace") {
142: Insets margin;
143:
144: public Dimension getPreferredSize() {
145: Dimension dim = super .getPreferredSize();
146: dim.width = 150;
147: return dim;
148: }
149: };
150: showStackButton.addActionListener(this );
151:
152: // format the text
153: StringBuffer sb = new StringBuffer();
154: sb.append("<html><table border=\"0\" cellpadding=\"2\">");
155:
156: String delim = "\n";
157: boolean wasDelim = true;
158: StringTokenizer st = new StringTokenizer(message, delim, true);
159: while (st.hasMoreTokens()) {
160: String token = st.nextToken();
161: if (delim.equals(token)) {
162: if (wasDelim) {
163: sb.append("<tr><td></td></tr>");
164: }
165: wasDelim = true;
166: continue;
167: }
168: sb.append("<tr><td>");
169: sb.append(token);
170: sb.append("</td></tr>");
171: wasDelim = false;
172: }
173:
174: sb.append("</table></html>");
175:
176: JPanel base = new JPanel(new GridBagLayout());
177: GridBagConstraints gbc = new GridBagConstraints();
178: gbc.insets.left = 5;
179: gbc.insets.right = 20;
180: gbc.gridy++;
181: gbc.anchor = GridBagConstraints.NORTHWEST;
182: base.add(new JLabel(errorIcon), gbc);
183: gbc.gridx = 1;
184: gbc.insets.left = 0;
185: gbc.insets.right = 0;
186: gbc.weightx = 1.0;
187: gbc.fill = GridBagConstraints.HORIZONTAL;
188: gbc.gridwidth = GridBagConstraints.REMAINDER;
189: base.add(new JLabel(sb.toString()), gbc);
190: gbc.gridy++;
191:
192: gbc.gridx = 1;
193: gbc.insets.top = 15;
194: gbc.insets.right = 5;
195: gbc.insets.bottom = 0;
196: gbc.gridwidth = 1;
197: gbc.anchor = GridBagConstraints.EAST;
198: gbc.fill = GridBagConstraints.NONE;
199: base.add(showStackButton, gbc);
200: gbc.gridx = 2;
201: gbc.weightx = 0;
202: gbc.insets.right = 0;
203: base.add(closeButton, gbc);
204:
205: stackTracePanel = new JPanel(new GridBagLayout());
206: stackTracePanel.setVisible(false);
207:
208: JPanel stackTraceBase = new JPanel(new BorderLayout());
209: stackTraceBase.add(stackTracePanel, BorderLayout.CENTER);
210:
211: Container contentPane = getContentPane();
212: contentPane.setLayout(new GridBagLayout());
213:
214: gbc.gridy = 0;
215: gbc.gridx = 0;
216: gbc.insets.top = 10;
217: gbc.insets.left = 5;
218: gbc.insets.bottom = 5;
219: gbc.insets.right = 5;
220: gbc.fill = GridBagConstraints.HORIZONTAL;
221: gbc.anchor = GridBagConstraints.NORTHWEST;
222: gbc.weightx = 1.0;
223: contentPane.add(base, gbc);
224: gbc.gridy++;
225: gbc.weighty = 1.0;
226: gbc.insets.top = 0;
227: gbc.fill = GridBagConstraints.BOTH;
228: contentPane.add(stackTraceBase, gbc);
229:
230: addComponentListener(this );
231:
232: //this.setLayout(new BorderLayout());
233: //add(contentPane, BorderLayout.CENTER);
234:
235: pack();
236: Dimension size = getSize();
237:
238: // get the absolute center position and adjust
239: // for possible dialog expansion on stack trace
240: Point location = GUIUtils
241: .getLocationForDialog(getOwner(), size);
242: location.y -= (STACK_HEIGHT / 2);
243: setLocation(location);
244:
245: // set the height and width for resets
246: defaultHeight = size.height;
247: defaultWidth = 450;
248:
249: setVisible(true);
250: }
251:
252: public Dimension getMinimumSize() {
253: return new Dimension(400, getSize().height);
254: }
255:
256: /**
257: * Builds the stack trace text pane and associated
258: * buttons in the case of SQLExceptions.
259: */
260: private void buildStackTracePanel() {
261: if (textPane == null) {
262: textPane = new JTextArea();
263: textPane.setMargin(new Insets(2, 2, 2, 2));
264: textPane.setEditable(false);
265: textPane.setWrapStyleWord(false);
266: textPane.setBackground(getBackground());
267: JScrollPane scroller = new JScrollPane(textPane);
268:
269: GridBagConstraints gbc = new GridBagConstraints();
270: gbc.fill = GridBagConstraints.BOTH;
271: gbc.anchor = GridBagConstraints.NORTHWEST;
272: gbc.gridwidth = GridBagConstraints.REMAINDER;
273: gbc.gridy++;
274: gbc.weightx = 1.0;
275: gbc.weighty = 1.0;
276: stackTracePanel.add(scroller, gbc);
277:
278: pasteButton = new RolloverButton(IconUtilities
279: .loadDefaultIconResource("Paste16.gif", true),
280: "Paste stack to clipboard");
281: pasteButton.addActionListener(this );
282:
283: gbc.gridy++;
284: gbc.insets.top = 2;
285: gbc.weighty = 0;
286: gbc.weightx = 0;
287: gbc.gridwidth = 1;
288: gbc.fill = GridBagConstraints.NONE;
289: gbc.anchor = GridBagConstraints.WEST;
290: stackTracePanel.add(pasteButton, gbc);
291:
292: if (exceptions.get(selectedIndex) instanceof SQLException) {
293: SQLException sqlExc = (SQLException) exceptions
294: .get(selectedIndex);
295: if (sqlExc.getNextException() != null) {
296: nextButton = new JButton("Next Exception");
297: nextButton.addActionListener(this );
298: previousButton = new JButton("Previous Exception");
299: previousButton.addActionListener(this );
300: previousButton.setEnabled(false);
301:
302: //gbc.gridy++;
303: gbc.insets.top = 5;
304: gbc.insets.right = 5;
305: gbc.insets.left = 0;
306: gbc.weighty = 0;
307: gbc.gridwidth = 1;
308: gbc.weightx = 1.0;
309: gbc.fill = GridBagConstraints.NONE;
310: gbc.anchor = GridBagConstraints.EAST;
311: stackTracePanel.add(previousButton, gbc);
312: gbc.weightx = 0;
313: gbc.gridx = 1;
314: gbc.insets.right = 0;
315: stackTracePanel.add(nextButton, gbc);
316: }
317: }
318: }
319: }
320:
321: /** the stack trace pane height */
322: private static final int STACK_HEIGHT = 220;
323:
324: /**
325: * Prints the specified exception's stack trace
326: * to the text pane.
327: *
328: * @param e - the exception to be printed
329: */
330: private void printException(Throwable e) {
331: if (e != null && e != noMoreExceptions) {
332: StringWriter sw = new StringWriter();
333: PrintWriter out = new PrintWriter(sw);
334: e.printStackTrace(out);
335: textPane.setText(sw.toString());
336: } else {
337: textPane.setText("Exception stack trace not available.");
338: }
339: textPane.setCaretPosition(0);
340: }
341:
342: public void actionPerformed(ActionEvent e) {
343: Object source = e.getSource();
344:
345: if (source == showStackButton) {
346: buildStackTracePanel();
347:
348: if (stackTracePanel.isVisible()) {
349: stackTracePanel.setVisible(false);
350: setSize(new Dimension(getWidth(), defaultHeight));
351: showStackButton.setText("Show Stack Trace");
352: } else {
353: stackTracePanel.setVisible(true);
354: showStackButton.setText("Hide Stack Trace");
355: setSize(new Dimension(getWidth(), defaultHeight
356: + (STACK_HEIGHT + 30)));
357:
358: printException(exceptions.get(selectedIndex));
359: }
360:
361: } else if (source == nextButton) {
362: selectedIndex++;
363: if (exceptions.size() - 1 < selectedIndex) {
364: SQLException sqlException = (SQLException) exceptions
365: .get(selectedIndex - 1);
366: SQLException nextSQLException = sqlException
367: .getNextException();
368:
369: if (nextSQLException == null) {
370: // add the dummy to the end
371: if (noMoreExceptions == null) {
372: noMoreExceptions = new Throwable();
373: exceptions.add(noMoreExceptions);
374: }
375: } else {
376: exceptions.add(nextSQLException);
377: }
378:
379: }
380:
381: Throwable currentException = exceptions.get(selectedIndex);
382: printException(currentException);
383:
384: if (currentException == noMoreExceptions
385: || currentException == null) {
386: nextButton.setEnabled(false);
387: }
388: previousButton.setEnabled(true);
389: } else if (source == previousButton) {
390: selectedIndex--;
391: Throwable currentException = exceptions.get(selectedIndex);
392: printException(currentException);
393:
394: if (selectedIndex == 0) {
395: previousButton.setEnabled(false);
396: }
397: nextButton.setEnabled(true);
398: } else if (source == pasteButton) {
399: Toolkit.getDefaultToolkit().getSystemClipboard()
400: .setContents(
401: new StringSelection(textPane.getText()),
402: null);
403: } else if (source == closeButton) {
404: dispose();
405: }
406: }
407:
408: /**
409: * Invoked when the component's size changes.
410: */
411: public void componentResized(ComponentEvent e) {
412: Dimension _size = getSize();
413: boolean resizeRequired = false;
414: if (_size.height < defaultHeight) {
415: _size.height = defaultHeight;
416: resizeRequired = true;
417: }
418:
419: if (_size.width < defaultWidth) {
420: _size.width = defaultWidth;
421: resizeRequired = true;
422: }
423:
424: if (resizeRequired) {
425: setSize(_size);
426: }
427: }
428:
429: /**
430: * Invoked when the component's position changes.
431: */
432: public void componentMoved(ComponentEvent e) {
433: }
434:
435: /**
436: * Invoked when the component has been made visible.
437: */
438: public void componentShown(ComponentEvent e) {
439: }
440:
441: /**
442: * Invoked when the component has been made invisible.
443: */
444: public void componentHidden(ComponentEvent e) {
445: }
446:
447: }
|