001: /*****************************************************************************
002: * Java Plug-in Framework (JPF)
003: * Copyright (C) 2004-2007 Dmitry Olshansky
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *****************************************************************************/package org.java.plugin.boot;
019:
020: import java.awt.BorderLayout;
021: import java.awt.Component;
022: import java.awt.FlowLayout;
023: import java.awt.Frame;
024: import java.awt.event.ActionEvent;
025: import java.awt.event.KeyEvent;
026: import java.awt.event.MouseAdapter;
027: import java.awt.event.MouseEvent;
028: import java.lang.reflect.InvocationTargetException;
029: import java.sql.SQLException;
030: import java.util.Collection;
031: import java.util.Date;
032: import java.util.Map;
033: import java.util.TreeMap;
034: import javax.swing.AbstractAction;
035: import javax.swing.BoxLayout;
036: import javax.swing.DefaultListModel;
037: import javax.swing.JButton;
038: import javax.swing.JComponent;
039: import javax.swing.JDialog;
040: import javax.swing.JLabel;
041: import javax.swing.JList;
042: import javax.swing.JOptionPane;
043: import javax.swing.JPanel;
044: import javax.swing.JRootPane;
045: import javax.swing.JScrollPane;
046: import javax.swing.JTabbedPane;
047: import javax.swing.JTextArea;
048: import javax.swing.KeyStroke;
049: import org.java.plugin.util.ResourceManager;
050:
051: /**
052: * Helper class to display detailed message about application error.
053: *
054: * @version $Id$
055: */
056: public class ErrorDialog extends JDialog {
057: private static final long serialVersionUID = 7142861251076530780L;
058:
059: /**
060: * Displays error dialogue to the user.
061: * @param parentComponent parent component, may be <code>null</code>
062: * @param title window title
063: * @param message error message
064: */
065: public static void showError(final Component parentComponent,
066: final String title, final String message) {
067: showError(parentComponent, title, message, null, null);
068: }
069:
070: /**
071: * Displays error dialogue to the user.
072: * @param parentComponent parent component, may be <code>null</code>
073: * @param title window title
074: * @param message error message
075: * @param data error data, {@link Collection collections} and arrays are
076: * handled specially, all other objects are shown using
077: * <code>toString()</code> method
078: */
079: public static void showError(final Component parentComponent,
080: final String title, final String message, final Object data) {
081: showError(parentComponent, title, message, data, null);
082: }
083:
084: /**
085: * Displays error dialogue to the user.
086: * @param parentComponent parent component, may be <code>null</code>
087: * @param title window title
088: * @param data error data, {@link Collection collections} and arrays are
089: * handled specially, all other objects are shown using
090: * <code>toString()</code> method
091: * @param error an error to be shown in details section
092: */
093: public static void showError(final Component parentComponent,
094: final String title, final Object data, final Throwable error) {
095: String message = error.getMessage();
096: if ((message == null) || (message.trim().length() == 0)) {
097: message = error.toString();
098: }
099: showError(parentComponent, title, message, data, error);
100: }
101:
102: /**
103: * Displays error dialogue to the user.
104: * @param parentComponent parent component, may be <code>null</code>
105: * @param title window title
106: * @param error an error to be shown in details section
107: */
108: public static void showError(final Component parentComponent,
109: final String title, final Throwable error) {
110: String message = error.getMessage();
111: if ((message == null) || (message.trim().length() == 0)) {
112: message = error.toString();
113: }
114: showError(parentComponent, title, message, error);
115: }
116:
117: /**
118: * Displays error dialogue to the user.
119: * @param parentComponent parent component, may be <code>null</code>
120: * @param title window title
121: * @param message error message
122: * @param error an error to be shown in details section
123: */
124: public static void showError(final Component parentComponent,
125: final String title, final String message,
126: final Throwable error) {
127: showError(parentComponent, title, message, null, error);
128: }
129:
130: /**
131: * Displays error dialogue to the user.
132: * @param parentComponent parent component, may be <code>null</code>
133: * @param title window title
134: * @param message error message
135: * @param data error data, {@link Collection collections} and arrays are
136: * handled specially, all other objects are shown using
137: * <code>toString()</code> method
138: * @param error an error to be shown in details section
139: */
140: public static void showError(final Component parentComponent,
141: final String title, final String message,
142: final Object data, final Throwable error) {
143: Frame frame = (parentComponent != null) ? JOptionPane
144: .getFrameForComponent(parentComponent) : JOptionPane
145: .getRootFrame();
146: new ErrorDialog(frame, title, message, data, error, false)
147: .setVisible(true);
148: }
149:
150: /**
151: * Displays error dialogue to the user and lets him to make a decision with
152: * "Yes" and "No" buttons. The question should be in the given message.
153: * @param parentComponent parent component, may be <code>null</code>
154: * @param title window title
155: * @param message error message
156: * @return <code>true</code> if user chooses "Yes" answer
157: */
158: public static boolean showWarning(final Component parentComponent,
159: final String title, final String message) {
160: return showWarning(parentComponent, title, message, null, null);
161: }
162:
163: /**
164: * Displays error dialogue to the user and lets him to make a decision with
165: * "Yes" and "No" buttons. The question should be in the given message.
166: * @param parentComponent parent component, may be <code>null</code>
167: * @param title window title
168: * @param message error message
169: * @param data error data, {@link Collection collections} and arrays are
170: * handled specially, all other objects are shown using
171: * <code>toString()</code> method
172: * @return <code>true</code> if user chooses "Yes" answer
173: */
174: public static boolean showWarning(final Component parentComponent,
175: final String title, final String message, final Object data) {
176: return showWarning(parentComponent, title, message, data, null);
177: }
178:
179: /**
180: * Displays error dialogue to the user and lets him to make a decision with
181: * "Yes" and "No" buttons. The question should be in the given message.
182: * @param parentComponent parent component, may be <code>null</code>
183: * @param title window title
184: * @param message error message
185: * @param error an error to be shown in details section
186: * @return <code>true</code> if user chooses "Yes" answer
187: */
188: public static boolean showWarning(final Component parentComponent,
189: final String title, final String message,
190: final Throwable error) {
191: return showWarning(parentComponent, title, message, null, error);
192: }
193:
194: /**
195: * Displays error dialogue to the user and lets him to make a decision with
196: * "Yes" and "No" buttons. The question should be in the given message.
197: * @param parentComponent parent component, may be <code>null</code>
198: * @param title window title
199: * @param message error message
200: * @param data error data, {@link Collection collections} and arrays are
201: * handled specially, all other objects are shown using
202: * <code>toString()</code> method
203: * @param error an error to be shown in details section
204: * @return <code>true</code> if user chooses "Yes" answer
205: */
206: public static boolean showWarning(final Component parentComponent,
207: final String title, final String message,
208: final Object data, final Throwable error) {
209: Frame frame = (parentComponent != null) ? JOptionPane
210: .getFrameForComponent(parentComponent) : JOptionPane
211: .getRootFrame();
212: ErrorDialog dialog = new ErrorDialog(frame, title, message,
213: data, error, true);
214: dialog.setVisible(true);
215: return dialog.yesBtnPressed;
216: }
217:
218: /**
219: * Utility method to get detailed error report.
220: * @param t exception instance, may be <code>null</code>
221: * @return detailed error message with most important system information
222: * included
223: */
224: public static String getErrorDetails(final Throwable t) {
225: StringBuilder sb = new StringBuilder();
226: String nl = System.getProperty("line.separator"); //$NON-NLS-1$
227: sb.append(new Date()).append(nl);
228: if (t != null) {
229: // Print exception details.
230: sb.append(nl).append(
231: "-----------------------------------------------") //$NON-NLS-1$
232: .append(nl);
233: sb.append("Exception details.").append(nl).append(nl); //$NON-NLS-1$
234: sb
235: .append("Class: ").append(t.getClass().getName()).append(nl); //$NON-NLS-1$
236: sb.append("Message: ").append(t.getMessage()).append(nl); //$NON-NLS-1$
237: printError(t, "Stack trace:", sb); //$NON-NLS-1$
238: }
239: // Print system properties.
240: sb.append(nl).append(
241: "-----------------------------------------------") //$NON-NLS-1$
242: .append(nl);
243: sb.append("System properties:").append(nl).append(nl); //$NON-NLS-1$
244: for (Map.Entry<Object, Object> entry : new TreeMap<Object, Object>(
245: System.getProperties()).entrySet()) {
246: sb.append(entry.getKey()).append("=") //$NON-NLS-1$
247: .append(entry.getValue()).append(nl);
248: }
249: // Print runtime info.
250: sb.append(nl).append(
251: "-----------------------------------------------") //$NON-NLS-1$
252: .append(nl);
253: sb.append("Runtime info:").append(nl).append(nl); //$NON-NLS-1$
254: Runtime rt = Runtime.getRuntime();
255: sb.append("Memory TOTAL / FREE / MAX: ") //$NON-NLS-1$
256: .append(rt.totalMemory()).append(" / ") //$NON-NLS-1$
257: .append(rt.freeMemory()).append(" / ") //$NON-NLS-1$
258: .append(rt.maxMemory()).append(nl);
259: sb.append("Available processors: ") //$NON-NLS-1$
260: .append(rt.availableProcessors()).append(nl);
261: sb.append("System class loader: ").append("" //$NON-NLS-1$ //$NON-NLS-2$
262: + ClassLoader.getSystemClassLoader()).append(nl);
263: sb.append("Thread context class loader: ").append("" //$NON-NLS-1$ //$NON-NLS-2$
264: + Thread.currentThread().getContextClassLoader())
265: .append(nl);
266: sb.append("Security manager: ").append("" //$NON-NLS-1$ //$NON-NLS-2$
267: + System.getSecurityManager()).append(nl);
268: return sb.toString();
269: }
270:
271: /**
272: * Prints detailed stack trace to the given buffer.
273: * @param t exception instance, may be <code>null</code>
274: * @param header stack trace caption
275: * @param sb output text buffer
276: */
277: public static void printError(final Throwable t,
278: final String header, final StringBuilder sb) {
279: if (t == null) {
280: return;
281: }
282: String nl = System.getProperty("line.separator"); //$NON-NLS-1$
283: sb.append(nl).append(header).append(nl).append(nl);
284: StackTraceElement[] stackTrace = t.getStackTrace();
285: for (int i = 0; i < stackTrace.length; i++) {
286: sb.append(stackTrace[i].toString()).append(nl);
287: }
288: Throwable next = t.getCause();
289: printError(next, "Caused by " + next, sb); //$NON-NLS-1$
290: if (t instanceof SQLException) {
291: // Handle SQLException specifically.
292: next = ((SQLException) t).getNextException();
293: printError(next, "Next exception: " + next, sb); //$NON-NLS-1$
294: } else if (t instanceof InvocationTargetException) {
295: // Handle InvocationTargetException specifically.
296: next = ((InvocationTargetException) t).getTargetException();
297: printError(next, "Target exception: " + next, sb); //$NON-NLS-1$
298: }
299: }
300:
301: private javax.swing.JPanel jContentPane = null;
302: private JPanel jPanel = null;
303: private JLabel messageLabel = null;
304: private JLabel errorLabel = null;
305: private JPanel jPanel1 = null;
306: private JButton closeButton = null;
307: private JScrollPane jScrollPane = null;
308: JTextArea jTextArea = null;
309: private JTabbedPane jTabbedPane = null;
310: private JPanel jPanelInfo = null;
311: private JScrollPane jScrollPane2 = null;
312: private JList jList = null;
313: private JLabel dataLabel = null;
314: boolean yesBtnPressed = false;
315: private JButton yesButton = null;
316:
317: private ErrorDialog(final Frame owner, final String title,
318: final String message, final Object data, final Throwable t,
319: boolean yesNo) {
320: super ((owner != null) ? owner : JOptionPane.getRootFrame());
321: initialize();
322: setLocationRelativeTo(getOwner());
323: jTabbedPane.setTitleAt(0, ResourceManager.getMessage(
324: Boot.PACKAGE_NAME, "infoTabLabel")); //$NON-NLS-1$
325: jTabbedPane.setTitleAt(1, ResourceManager.getMessage(
326: Boot.PACKAGE_NAME, "detailsTabLabel")); //$NON-NLS-1$
327: setTitle(title);
328: messageLabel.setText(message);
329: if (t != null) {
330: errorLabel.setText(ResourceManager.getMessage(
331: Boot.PACKAGE_NAME, "errorLabel", t)); //$NON-NLS-1$
332: } else {
333: getJPanel().remove(errorLabel);
334: }
335: if (data instanceof Collection) {
336: DefaultListModel model = new DefaultListModel();
337: for (Object object : (Collection) data) {
338: model.addElement(object);
339: }
340: jList.setModel(model);
341: getJPanel().remove(dataLabel);
342: } else if (data instanceof Object[]) {
343: DefaultListModel model = new DefaultListModel();
344: for (Object object : (Object[]) data)
345: model.addElement(object);
346: jList.setModel(model);
347: getJPanel().remove(dataLabel);
348: } else if (data != null) {
349: dataLabel.setText(data.toString());
350: getJPanelInfo().remove(getJScrollPane());
351: } else {
352: getJPanel().remove(dataLabel);
353: getJPanelInfo().remove(getJScrollPane());
354: }
355: jTextArea.setText(getErrorDetails(t));
356: jTextArea.setCaretPosition(0);
357: if (yesNo) {
358: closeButton.setText(ResourceManager.getMessage(
359: Boot.PACKAGE_NAME, "noLabel")); //$NON-NLS-1$
360: yesButton.setText(ResourceManager.getMessage(
361: Boot.PACKAGE_NAME, "yesLabel")); //$NON-NLS-1$
362: } else {
363: getJPanel1().remove(yesButton);
364: closeButton.setText(ResourceManager.getMessage(
365: Boot.PACKAGE_NAME, "closeLabel")); //$NON-NLS-1$
366: }
367: }
368:
369: private void initialize() {
370: this
371: .setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
372: this .setModal(true);
373: this .setTitle("An error has occurred"); //$NON-NLS-1$
374: this .setSize(460, 280);
375: this .setContentPane(getJContentPane());
376: getRootPane().setWindowDecorationStyle(JRootPane.ERROR_DIALOG);
377: getRootPane().setDefaultButton(closeButton);
378: getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
379: .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
380: "doCloseDefault"); //$NON-NLS-1$
381: getRootPane().getActionMap().put("doCloseDefault", //$NON-NLS-1$
382: new AbstractAction() {
383: private static final long serialVersionUID = -9167454634726502084L;
384:
385: public void actionPerformed(final ActionEvent evt) {
386: dispose();
387: }
388: });
389: getRootPane().setDefaultButton(getCloseButton());
390: }
391:
392: private javax.swing.JPanel getJContentPane() {
393: if (jContentPane == null) {
394: BorderLayout borderLayout2 = new BorderLayout();
395: jContentPane = new javax.swing.JPanel();
396: jContentPane.setLayout(borderLayout2);
397: borderLayout2.setVgap(2);
398: jContentPane.add(getJPanel1(), java.awt.BorderLayout.SOUTH);
399: jContentPane.add(getJTabbedPane(),
400: java.awt.BorderLayout.CENTER);
401: }
402: return jContentPane;
403: }
404:
405: private JPanel getJPanel() {
406: if (jPanel == null) {
407: dataLabel = new JLabel();
408: dataLabel.setText("JLabel"); //$NON-NLS-1$
409: errorLabel = new JLabel();
410: messageLabel = new JLabel();
411: jPanel = new JPanel();
412: jPanel.setLayout(new BoxLayout(getJPanel(),
413: BoxLayout.Y_AXIS));
414: messageLabel.setText("JLabel"); //$NON-NLS-1$
415: errorLabel.setText("JLabel"); //$NON-NLS-1$
416: jPanel.add(messageLabel, null);
417: jPanel.add(errorLabel, null);
418: jPanel.add(dataLabel, null);
419: }
420: return jPanel;
421: }
422:
423: private JPanel getJPanel1() {
424: if (jPanel1 == null) {
425: FlowLayout flowLayout = new FlowLayout();
426: flowLayout.setAlignment(java.awt.FlowLayout.RIGHT);
427: jPanel1 = new JPanel();
428: jPanel1.setLayout(flowLayout);
429: jPanel1.add(getYesButton(), null);
430: jPanel1.add(getCloseButton(), null);
431: }
432: return jPanel1;
433: }
434:
435: private JButton getCloseButton() {
436: if (closeButton == null) {
437: closeButton = new JButton();
438: closeButton.setText("Close"); //$NON-NLS-1$
439: closeButton.setSelected(true);
440: closeButton
441: .addActionListener(new java.awt.event.ActionListener() {
442: public void actionPerformed(
443: java.awt.event.ActionEvent e) {
444: dispose();
445: }
446: });
447: }
448: return closeButton;
449: }
450:
451: private JScrollPane getJScrollPane() {
452: if (jScrollPane == null) {
453: jScrollPane = new JScrollPane();
454: jScrollPane.setViewportView(getJList());
455: }
456: return jScrollPane;
457: }
458:
459: private JTextArea getJTextArea() {
460: if (jTextArea == null) {
461: jTextArea = new JTextArea();
462: jTextArea.setBackground(java.awt.SystemColor.control);
463: jTextArea.setEditable(false);
464: jTextArea.setOpaque(false);
465: jTextArea.addMouseListener(new MouseAdapter() {
466: @Override
467: public void mousePressed(final MouseEvent evt) {
468: if (!evt.isPopupTrigger()) {
469: return;
470: }
471: copyText();
472: }
473:
474: @Override
475: public void mouseReleased(final MouseEvent evt) {
476: if (!evt.isPopupTrigger()) {
477: return;
478: }
479: copyText();
480: }
481:
482: private void copyText() {
483: if (jTextArea.getSelectedText() != null) {
484: jTextArea.copy();
485: return;
486: }
487: jTextArea.setSelectionStart(0);
488: jTextArea.setSelectionEnd(jTextArea.getText()
489: .length());
490: jTextArea.copy();
491: jTextArea.setSelectionEnd(0);
492: }
493: });
494: }
495: return jTextArea;
496: }
497:
498: private JTabbedPane getJTabbedPane() {
499: if (jTabbedPane == null) {
500: jTabbedPane = new JTabbedPane();
501: jTabbedPane.addTab("Info", null, getJPanelInfo(), null); //$NON-NLS-1$
502: jTabbedPane
503: .addTab("Details", null, getJScrollPane2(), null); //$NON-NLS-1$
504: }
505: return jTabbedPane;
506: }
507:
508: private JPanel getJPanelInfo() {
509: if (jPanelInfo == null) {
510: jPanelInfo = new JPanel();
511: jPanelInfo.setLayout(new BorderLayout());
512: jPanelInfo.add(getJPanel(), java.awt.BorderLayout.NORTH);
513: jPanelInfo.add(getJScrollPane(),
514: java.awt.BorderLayout.CENTER);
515: }
516: return jPanelInfo;
517: }
518:
519: private JScrollPane getJScrollPane2() {
520: if (jScrollPane2 == null) {
521: jScrollPane2 = new JScrollPane();
522: jScrollPane2.setViewportView(getJTextArea());
523: }
524: return jScrollPane2;
525: }
526:
527: private JList getJList() {
528: if (jList == null) {
529: jList = new JList();
530: }
531: return jList;
532: }
533:
534: private JButton getYesButton() {
535: if (yesButton == null) {
536: yesButton = new JButton();
537: yesButton.setText("Yes"); //$NON-NLS-1$
538: yesButton
539: .addActionListener(new java.awt.event.ActionListener() {
540: public void actionPerformed(
541: java.awt.event.ActionEvent e) {
542: yesBtnPressed = true;
543: dispose();
544: }
545: });
546: }
547: return yesButton;
548: }
549: } // @jve:decl-index=0:visual-constraint="10,10"
|