View Javadoc

1   /*
2    * Copyright 2004-2008 the Seasar Foundation and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.seasar.uruma.ui.dialogs;
17  
18  import java.io.InputStream;
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.eclipse.jface.dialogs.IDialogConstants;
23  import org.eclipse.jface.dialogs.IconAndMessageDialog;
24  import org.eclipse.jface.viewers.ILabelProvider;
25  import org.eclipse.jface.viewers.ILabelProviderListener;
26  import org.eclipse.jface.viewers.ITreeContentProvider;
27  import org.eclipse.jface.viewers.StructuredSelection;
28  import org.eclipse.jface.viewers.TreeViewer;
29  import org.eclipse.jface.viewers.Viewer;
30  import org.eclipse.swt.SWT;
31  import org.eclipse.swt.dnd.Clipboard;
32  import org.eclipse.swt.dnd.TextTransfer;
33  import org.eclipse.swt.dnd.Transfer;
34  import org.eclipse.swt.events.SelectionEvent;
35  import org.eclipse.swt.events.SelectionListener;
36  import org.eclipse.swt.graphics.Image;
37  import org.eclipse.swt.graphics.Point;
38  import org.eclipse.swt.layout.GridData;
39  import org.eclipse.swt.layout.GridLayout;
40  import org.eclipse.swt.widgets.Button;
41  import org.eclipse.swt.widgets.Composite;
42  import org.eclipse.swt.widgets.Control;
43  import org.eclipse.swt.widgets.Display;
44  import org.eclipse.swt.widgets.Menu;
45  import org.eclipse.swt.widgets.MenuItem;
46  import org.eclipse.swt.widgets.Shell;
47  import org.eclipse.swt.widgets.Tree;
48  import org.seasar.uruma.core.UrumaConstants;
49  import org.seasar.uruma.util.MessageUtil;
50  
51  /**
52   * 例外スタックトレースを表示するためのダイアログです。<br />
53   * 
54   * @author y-komori
55   */
56  public class UrumaErrorDialog extends IconAndMessageDialog implements
57          UrumaConstants {
58      protected static final int DETAILS_ITEM_COUNT = 10;
59  
60      protected static final String NESTING_INDENT = "  ";
61  
62      protected String title;
63  
64      protected Button detailsButton;
65  
66      protected TreeViewer detailTreeViewer;
67  
68      protected boolean detailsCreated;
69  
70      protected Throwable rootThrowable;
71  
72      protected List<Throwable> causes = new ArrayList<Throwable>();
73  
74      protected Clipboard clipboard;
75  
76      protected Image errorIcon;
77  
78      protected Image dialogIcon;
79  
80      /**
81       * {@link UrumaErrorDialog} を構築します。<br />
82       * 
83       * @param parentShell
84       *            親 {@link Shell} オブジェクト
85       * @param title
86       *            ダイアログタイトル
87       * @param message
88       *            メッセージ
89       * @param ex
90       *            例外オブジェクト
91       */
92      public UrumaErrorDialog(final Shell parentShell, final String title,
93              final String message, final Throwable ex) {
94          super(parentShell);
95          this.title = (title != null ? title : "Error");
96          this.message = (message != null ? message : "");
97          this.rootThrowable = ex;
98          getCause(ex);
99          getImageIcons(parentShell);
100 
101         setShellStyle(getShellStyle() | SWT.RESIZE);
102     }
103 
104     protected void getCause(final Throwable throwable) {
105         Throwable cause = throwable.getCause();
106         if ((cause != null) && !causes.contains(cause)) {
107             causes.add(cause);
108             getCause(cause);
109         }
110     }
111 
112     protected void getImageIcons(final Shell shell) {
113         ClassLoader loader = getClass().getClassLoader();
114 
115         InputStream is = loader.getResourceAsStream("images/error.png");
116         errorIcon = new Image(shell.getDisplay(), is);
117 
118         is = loader.getResourceAsStream("images/uruma16.png");
119         dialogIcon = new Image(shell.getDisplay(), is);
120     }
121 
122     /*
123      * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
124      */
125     @Override
126     protected void configureShell(final Shell shell) {
127         super.configureShell(shell);
128         shell.setText(this.title);
129         shell.setImage(dialogIcon);
130     }
131 
132     /*
133      * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
134      */
135     @Override
136     protected Control createDialogArea(final Composite parent) {
137         createMessageArea(parent);
138 
139         // create a composite with standard margins and spacing
140         Composite composite = new Composite(parent, SWT.NONE);
141         GridLayout layout = new GridLayout();
142         layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
143         layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
144         layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
145         layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
146         layout.numColumns = 2;
147         composite.setLayout(layout);
148         GridData childData = new GridData(SWT.FILL, SWT.FILL, true, false);
149         childData.horizontalSpan = 2;
150         composite.setLayoutData(childData);
151         composite.setFont(parent.getFont());
152 
153         return composite;
154     }
155 
156     /*
157      * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
158      */
159     @Override
160     protected void createButtonsForButtonBar(final Composite parent) {
161         // create OK and Details buttons
162         createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
163                 true);
164         createDetailsButton(parent);
165     }
166 
167     protected void createDetailsButton(final Composite parent) {
168         detailsButton = createButton(parent, IDialogConstants.DETAILS_ID,
169                 IDialogConstants.SHOW_DETAILS_LABEL, false);
170     }
171 
172     protected void toggleDetailsArea() {
173         Point windowSize = getShell().getSize();
174         Point oldSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
175         if (detailsCreated) {
176             detailTreeViewer.getTree().dispose();
177             detailsCreated = false;
178             detailsButton.setText(IDialogConstants.SHOW_DETAILS_LABEL);
179         } else {
180             detailTreeViewer = createDetailsTreeViewer((Composite) getContents());
181             detailTreeViewer.setInput("");
182             detailsButton.setText(IDialogConstants.HIDE_DETAILS_LABEL);
183             detailsCreated = true;
184         }
185         Point newSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
186         getShell()
187                 .setSize(
188                         new Point(windowSize.x, windowSize.y
189                                 + (newSize.y - oldSize.y)));
190     }
191 
192     protected TreeViewer createDetailsTreeViewer(final Composite parent) {
193         TreeViewer viewer = new TreeViewer(parent, SWT.SINGLE
194                 | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
195         Tree tree = viewer.getTree();
196 
197         GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
198         data.heightHint = tree.getItemHeight() * DETAILS_ITEM_COUNT;
199         data.horizontalSpan = 2;
200         tree.setLayoutData(data);
201         tree.setFont(parent.getFont());
202         createContextMenu(tree);
203 
204         viewer.setContentProvider(new DetailsContentProvider());
205         viewer.setLabelProvider(new DetailLabelProvider());
206         viewer.setAutoExpandLevel(2);
207         return viewer;
208     }
209 
210     protected void createContextMenu(final Tree tree) {
211         Menu copyMenu = new Menu(tree);
212 
213         MenuItem allCopyItem = new MenuItem(copyMenu, SWT.NONE);
214         allCopyItem.addSelectionListener(new SelectionListener() {
215             public void widgetSelected(final SelectionEvent e) {
216                 copyToClipboard(rootThrowable);
217             }
218 
219             public void widgetDefaultSelected(final SelectionEvent e) {
220                 widgetSelected(e);
221             }
222         });
223         allCopyItem.setText(MessageUtil.getMessageWithBundleName(
224                 URUMA_MESSAGE_BASE, "COPY_ALL"));
225 
226         MenuItem copyItem = new MenuItem(copyMenu, SWT.NONE);
227         copyItem.addSelectionListener(new SelectionListener() {
228             public void widgetSelected(final SelectionEvent e) {
229                 StructuredSelection selection = (StructuredSelection) detailTreeViewer
230                         .getSelection();
231                 if (selection != null) {
232                     Object selected = selection.getFirstElement();
233                     copyToClipboard(selected);
234                 }
235             }
236 
237             public void widgetDefaultSelected(final SelectionEvent e) {
238                 widgetSelected(e);
239             }
240         });
241         copyItem.setText(MessageUtil.getMessageWithBundleName(
242                 URUMA_MESSAGE_BASE, "COPY_SELECTION"));
243 
244         tree.setMenu(copyMenu);
245     }
246 
247     private void copyToClipboard(final Object selected) {
248         if (clipboard != null) {
249             clipboard.dispose();
250         }
251         if (selected == null) {
252             return;
253         }
254 
255         StringBuffer statusBuffer = new StringBuffer(8192);
256         if (selected instanceof Throwable) {
257             populateCopyBuffer(statusBuffer, (Throwable) selected);
258         } else if (selected instanceof StackTraceElement) {
259             populateCopyBuffer(statusBuffer, (StackTraceElement) selected);
260         }
261 
262         clipboard = new Clipboard(detailTreeViewer.getTree().getDisplay());
263         clipboard.setContents(new Object[] { statusBuffer.toString() },
264                 new Transfer[] { TextTransfer.getInstance() });
265 
266     }
267 
268     protected void populateCopyBuffer(final StringBuffer buffer,
269             final Throwable throwable) {
270         buffer.append(throwable.getClass().getName());
271         String message = throwable.getMessage();
272         if (message != null) {
273             buffer.append(" : ");
274             buffer.append(message);
275         }
276         buffer.append("\n");
277 
278         StackTraceElement[] elements = throwable.getStackTrace();
279         if (elements != null) {
280             for (StackTraceElement stackTraceElement : elements) {
281                 populateCopyBuffer(buffer, stackTraceElement);
282             }
283         }
284 
285         Throwable cause = throwable.getCause();
286         if (cause != null) {
287             buffer.append("\n");
288             populateCopyBuffer(buffer, cause);
289         }
290     }
291 
292     protected void populateCopyBuffer(final StringBuffer buffer,
293             final StackTraceElement element) {
294         buffer.append(NESTING_INDENT);
295         buffer.append("at " + element + "\n");
296     }
297 
298     /*
299      * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
300      */
301     @Override
302     protected void buttonPressed(final int id) {
303         if (id == IDialogConstants.DETAILS_ID) {
304             toggleDetailsArea();
305         } else {
306             super.buttonPressed(id);
307         }
308     }
309 
310     /*
311      * @see org.eclipse.jface.dialogs.Dialog#close()
312      */
313     @Override
314     public boolean close() {
315         if (clipboard != null) {
316             clipboard.dispose();
317         }
318         if ((errorIcon != null) && !errorIcon.isDisposed()) {
319             errorIcon.dispose();
320         }
321         if ((dialogIcon != null) && !dialogIcon.isDisposed()) {
322             dialogIcon.dispose();
323         }
324         return super.close();
325     }
326 
327     /*
328      * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getImage()
329      */
330     @Override
331     protected Image getImage() {
332         return getSystemImage(SWT.ICON_ERROR);
333     }
334 
335     protected Image getSystemImage(final int id) {
336         Shell shell = getShell();
337         final Display display;
338         if (shell == null) {
339             shell = getParentShell();
340         }
341         if (shell == null) {
342             display = Display.getCurrent();
343         } else {
344             display = shell.getDisplay();
345         }
346 
347         final Image[] image = new Image[1];
348         display.syncExec(new Runnable() {
349             public void run() {
350                 image[0] = display.getSystemImage(id);
351             }
352         });
353 
354         return image[0];
355     }
356 
357     /**
358      * 例外表示ツリー用のコンテントプロバイダです。<br />
359      * 
360      * @author y-komori
361      */
362     class DetailsContentProvider implements ITreeContentProvider {
363         /*
364          * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
365          */
366         public Object[] getChildren(final Object parentElement) {
367             if (parentElement instanceof String) {
368                 return new Object[] { rootThrowable };
369             } else if (parentElement == rootThrowable) {
370                 if (causes.size() > 0) {
371                     return causes.toArray();
372                 } else {
373                     return rootThrowable.getStackTrace();
374                 }
375             } else if (parentElement instanceof Throwable) {
376                 return ((Throwable) parentElement).getStackTrace();
377             } else {
378                 return null;
379             }
380         }
381 
382         /*
383          * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
384          */
385         public Object getParent(final Object element) {
386             return null;
387         }
388 
389         /*
390          * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
391          */
392         public boolean hasChildren(final Object element) {
393             if ((element instanceof String) || (element instanceof Throwable)) {
394                 return true;
395             } else {
396                 return false;
397             }
398         }
399 
400         /*
401          * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
402          */
403         public Object[] getElements(final Object inputElement) {
404             return getChildren(inputElement);
405         }
406 
407         /*
408          * @see org.eclipse.jface.viewers.IContentProvider#dispose()
409          */
410         public void dispose() {
411         }
412 
413         /*
414          * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
415          *      java.lang.Object, java.lang.Object)
416          */
417         public void inputChanged(final Viewer viewer, final Object oldInput,
418                 final Object newInput) {
419         }
420     }
421 
422     /**
423      * 例外表示ツリー用のラベルプロバイダです。<br />
424      * 
425      * @author y-komori
426      */
427     class DetailLabelProvider implements ILabelProvider {
428         /*
429          * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
430          */
431         public Image getImage(final Object element) {
432             if (element instanceof Throwable) {
433                 return errorIcon;
434             } else {
435                 return null;
436             }
437         }
438 
439         /*
440          * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
441          */
442         public String getText(final Object element) {
443             if (element instanceof Throwable) {
444                 return element.getClass().getName() + " "
445                         + ((Throwable) element).getMessage();
446             } else if (element instanceof StackTraceElement) {
447                 StackTraceElement st = (StackTraceElement) element;
448                 String line = "at " + st.toString();
449                 return line;
450             } else {
451                 return NULL_STRING;
452             }
453         }
454 
455         /*
456          * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
457          */
458         public void addListener(final ILabelProviderListener listener) {
459         }
460 
461         /*
462          * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
463          */
464         public void dispose() {
465         }
466 
467         /*
468          * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
469          *      java.lang.String)
470          */
471         public boolean isLabelProperty(final Object element,
472                 final String property) {
473             return false;
474         }
475 
476         /*
477          * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
478          */
479         public void removeListener(final ILabelProviderListener listener) {
480         }
481     }
482 }