001 /**
002 * ========================================
003 * JFreeReport : a free Java report library
004 * ========================================
005 *
006 * Project Info: http://reporting.pentaho.org/
007 *
008 * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
009 *
010 * This library is free software; you can redistribute it and/or modify it under the terms
011 * of the GNU Lesser General Public License as published by the Free Software Foundation;
012 * either version 2.1 of the License, or (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016 * See the GNU Lesser General Public License for more details.
017 *
018 * You should have received a copy of the GNU Lesser General Public License along with this
019 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020 * Boston, MA 02111-1307, USA.
021 *
022 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023 * in the United States and other countries.]
024 *
025 * ------------
026 * $Id: ExceptionDialog.java,v 1.4 2007/04/01 18:49:30 taqua Exp $
027 * ------------
028 * (C) Copyright 2000-2005, by Object Refinery Limited.
029 * (C) Copyright 2005-2007, by Pentaho Corporation.
030 */
031
032 package org.jfree.report.modules.gui.swing.common;
033
034 import java.awt.Color;
035 import java.awt.Dimension;
036 import java.awt.FlowLayout;
037 import java.awt.GridBagConstraints;
038 import java.awt.GridBagLayout;
039 import java.awt.event.ActionEvent;
040 import java.io.PrintWriter;
041 import java.io.StringWriter;
042 import javax.swing.AbstractAction;
043 import javax.swing.BorderFactory;
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
052 import org.jfree.ui.FloatingButtonEnabler;
053 import org.jfree.ui.action.ActionButton;
054 import org.jfree.util.Log;
055
056 /**
057 * The exception dialog is used to display an exception and the exceptions stacktrace to
058 * the user.
059 *
060 * @author Thomas Morgner
061 */
062 public class ExceptionDialog extends JDialog
063 {
064 /**
065 * OK action.
066 */
067 private final class OKAction extends AbstractAction
068 {
069 /**
070 * Default constructor.
071 */
072 private OKAction ()
073 {
074 putValue(NAME, UIManager.getDefaults().getString("OptionPane.okButtonText"));
075 }
076
077 /**
078 * Receives notification that an action event has occurred.
079 *
080 * @param event the action event.
081 */
082 public void actionPerformed (final ActionEvent event)
083 {
084 setVisible(false);
085 }
086 }
087
088 /**
089 * Details action.
090 */
091 private final class DetailsAction extends AbstractAction
092 {
093 /**
094 * Default constructor.
095 */
096 private DetailsAction ()
097 {
098 putValue(NAME, ">>");
099 }
100
101 /**
102 * Receives notification that an action event has occurred.
103 *
104 * @param event the action event.
105 */
106 public void actionPerformed (final ActionEvent event)
107 {
108 setScrollerVisible(!(isScrollerVisible()));
109 if (isScrollerVisible())
110 {
111 putValue(NAME, "<<");
112 }
113 else
114 {
115 putValue(NAME, ">>");
116 }
117 adjustSize();
118 }
119 }
120
121 /**
122 * A UI component for displaying the stack trace.
123 */
124 private final JTextArea backtraceArea;
125
126 /**
127 * A UI component for displaying the message.
128 */
129 private final JLabel messageLabel;
130
131 /**
132 * The exception.
133 */
134 private Exception currentEx;
135
136 /**
137 * An action associated with the 'Details' button.
138 */
139 private DetailsAction detailsAction;
140
141 /**
142 * A scroll pane.
143 */
144 private final JScrollPane scroller;
145
146 /**
147 * A filler panel.
148 */
149 private final JPanel filler;
150
151 /**
152 * The default dialog.
153 */
154 private static ExceptionDialog defaultDialog;
155
156 /**
157 * Creates a new ExceptionDialog.
158 */
159 public ExceptionDialog ()
160 {
161 setModal(true);
162 detailsAction = new DetailsAction();
163
164 messageLabel = new JLabel();
165 backtraceArea = new JTextArea();
166
167 scroller = new JScrollPane(backtraceArea);
168 scroller.setVisible(false);
169
170 final JPanel detailPane = new JPanel();
171 detailPane.setLayout(new GridBagLayout());
172 GridBagConstraints gbc = new GridBagConstraints();
173 gbc.anchor = GridBagConstraints.CENTER;
174 gbc.fill = GridBagConstraints.NONE;
175 gbc.weightx = 0;
176 gbc.weighty = 0;
177 gbc.gridx = 0;
178 gbc.gridy = 0;
179 final JLabel icon = new JLabel(UIManager.getDefaults().getIcon("OptionPane.errorIcon"));
180 icon.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
181 detailPane.add(icon, gbc);
182
183 gbc = new GridBagConstraints();
184 gbc.anchor = GridBagConstraints.WEST;
185 gbc.fill = GridBagConstraints.NONE;
186 gbc.weightx = 1;
187 gbc.weighty = 1;
188 gbc.gridx = 1;
189 gbc.gridy = 0;
190 detailPane.add(messageLabel);
191
192 gbc = new GridBagConstraints();
193 gbc.anchor = GridBagConstraints.SOUTH;
194 gbc.fill = GridBagConstraints.HORIZONTAL;
195 gbc.weightx = 0;
196 gbc.weighty = 0;
197 gbc.gridx = 0;
198 gbc.gridy = 2;
199 gbc.gridwidth = 2;
200 detailPane.add(createButtonPane(), gbc);
201
202 filler = new JPanel();
203 filler.setPreferredSize(new Dimension(0, 0));
204 filler.setBackground(Color.green);
205 gbc = new GridBagConstraints();
206 gbc.anchor = GridBagConstraints.NORTH;
207 gbc.fill = GridBagConstraints.HORIZONTAL;
208 gbc.weightx = 1;
209 gbc.weighty = 5;
210 gbc.gridx = 0;
211 gbc.gridy = 3;
212 gbc.gridwidth = 2;
213 detailPane.add(filler, gbc);
214
215 gbc = new GridBagConstraints();
216 gbc.anchor = GridBagConstraints.SOUTHWEST;
217 gbc.fill = GridBagConstraints.BOTH;
218 gbc.weightx = 1;
219 gbc.weighty = 5;
220 gbc.gridx = 0;
221 gbc.gridy = 4;
222 gbc.gridwidth = 2;
223 detailPane.add(scroller, gbc);
224
225 setContentPane(detailPane);
226 }
227
228 /**
229 * Adjusts the size of the dialog to fit the with of the contained message and
230 * stacktrace.
231 */
232 public void adjustSize ()
233 {
234 final Dimension scSize = scroller.getPreferredSize();
235 final Dimension cbase = filler.getPreferredSize();
236 cbase.width = Math.max(scSize.width, cbase.width);
237 cbase.height = 0;
238 filler.setMinimumSize(cbase);
239 pack();
240
241 }
242
243 /**
244 * Defines, whether the scroll pane of the exception stack trace area is visible.
245 *
246 * @param b true, if the scroller should be visible, false otherwise.
247 */
248 protected void setScrollerVisible (final boolean b)
249 {
250 scroller.setVisible(b);
251 }
252
253 /**
254 * Checks, whether the scroll pane of the exception stack trace area is visible.
255 *
256 * @return true, if the scroller is visible, false otherwise.
257 */
258 protected boolean isScrollerVisible ()
259 {
260 return scroller.isVisible();
261 }
262
263 /**
264 * Initializes the buttonpane.
265 *
266 * @return a panel containing the 'OK' and 'Details' buttons.
267 */
268 private JPanel createButtonPane ()
269 {
270 final JPanel buttonPane = new JPanel();
271 buttonPane.setLayout(new FlowLayout(2));
272 buttonPane.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
273 final OKAction okAction = new OKAction();
274
275 final JButton ok = new ActionButton(okAction);
276 final JButton details = new ActionButton(detailsAction);
277
278 FloatingButtonEnabler.getInstance().addButton(ok);
279 FloatingButtonEnabler.getInstance().addButton(details);
280
281 buttonPane.add(ok);
282 buttonPane.add(details);
283 return buttonPane;
284 }
285
286 /**
287 * Sets the message for this exception dialog. The message is displayed on the main
288 * page.
289 *
290 * @param mesg the message.
291 */
292 public void setMessage (final String mesg)
293 {
294 messageLabel.setText(mesg);
295 }
296
297 /**
298 * Returns the message for this exception dialog. The message is displayed on the main
299 * page.
300 *
301 * @return the message.
302 */
303 public String getMessage ()
304 {
305 return messageLabel.getText();
306 }
307
308 /**
309 * Sets the exception for this dialog. If no exception is set, the "Detail" button is
310 * disabled and the stacktrace text cleared. Else the stacktraces text is read into the
311 * detail message area.
312 *
313 * @param e the exception.
314 */
315 public void setException (final Exception e)
316 {
317 currentEx = e;
318 if (e == null)
319 {
320 detailsAction.setEnabled(false);
321 backtraceArea.setText("");
322 }
323 else
324 {
325 backtraceArea.setText(readFromException(e));
326 }
327 }
328
329 /**
330 * Reads the stacktrace text from the exception.
331 *
332 * @param e the exception.
333 * @return the stack trace.
334 */
335 private String readFromException (final Exception e)
336 {
337 String text = "No backtrace available";
338 try
339 {
340 final StringWriter writer = new StringWriter();
341 final PrintWriter pwriter = new PrintWriter(writer);
342 e.printStackTrace(pwriter);
343 text = writer.toString();
344 writer.close();
345 }
346 catch (Exception ex)
347 {
348 Log.info("ExceptionDialog: exception suppressed.");
349 }
350 return text;
351 }
352
353 /**
354 * Returns the exception that was the reason for this dialog to show up.
355 *
356 * @return the exception.
357 */
358 public Exception getException ()
359 {
360 return currentEx;
361 }
362
363 /**
364 * Shows an default dialog with the given message and title and the exceptions
365 * stacktrace in the detail area.
366 *
367 * @param title the title.
368 * @param message the message.
369 * @param e the exception.
370 */
371 public static void showExceptionDialog
372 (final String title, final String message, final Exception e)
373 {
374 if (defaultDialog == null)
375 {
376 defaultDialog = new ExceptionDialog();
377 }
378 if (e != null)
379 {
380 Log.error("UserError", e);
381 }
382 defaultDialog.setTitle(title);
383 defaultDialog.setMessage(message);
384 defaultDialog.setException(e);
385 defaultDialog.adjustSize();
386 defaultDialog.setVisible(true);
387 }
388
389 }