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 2725 2007-04-01 18:49:29Z taqua $
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    }