Source for org.jfree.report.modules.gui.swing.common.ExceptionDialog

   1: /**
   2:  * ========================================
   3:  * JFreeReport : a free Java report library
   4:  * ========================================
   5:  *
   6:  * Project Info:  http://reporting.pentaho.org/
   7:  *
   8:  * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
   9:  *
  10:  * This library is free software; you can redistribute it and/or modify it under the terms
  11:  * of the GNU Lesser General Public License as published by the Free Software Foundation;
  12:  * either version 2.1 of the License, or (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  15:  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16:  * See the GNU Lesser General Public License for more details.
  17:  *
  18:  * You should have received a copy of the GNU Lesser General Public License along with this
  19:  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  20:  * Boston, MA 02111-1307, USA.
  21:  *
  22:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  23:  * in the United States and other countries.]
  24:  *
  25:  * ------------
  26:  * $Id: ExceptionDialog.java 2725 2007-04-01 18:49:29Z taqua $
  27:  * ------------
  28:  * (C) Copyright 2000-2005, by Object Refinery Limited.
  29:  * (C) Copyright 2005-2007, by Pentaho Corporation.
  30:  */
  31: 
  32: package org.jfree.report.modules.gui.swing.common;
  33: 
  34: import java.awt.Color;
  35: import java.awt.Dimension;
  36: import java.awt.FlowLayout;
  37: import java.awt.GridBagConstraints;
  38: import java.awt.GridBagLayout;
  39: import java.awt.event.ActionEvent;
  40: import java.io.PrintWriter;
  41: import java.io.StringWriter;
  42: import javax.swing.AbstractAction;
  43: import javax.swing.BorderFactory;
  44: import javax.swing.JButton;
  45: import javax.swing.JDialog;
  46: import javax.swing.JLabel;
  47: import javax.swing.JPanel;
  48: import javax.swing.JScrollPane;
  49: import javax.swing.JTextArea;
  50: import javax.swing.UIManager;
  51: 
  52: import org.jfree.ui.FloatingButtonEnabler;
  53: import org.jfree.ui.action.ActionButton;
  54: import org.jfree.util.Log;
  55: 
  56: /**
  57:  * The exception dialog is used to display an exception and the exceptions stacktrace to
  58:  * the user.
  59:  *
  60:  * @author Thomas Morgner
  61:  */
  62: public class ExceptionDialog extends JDialog
  63: {
  64:   /**
  65:    * OK action.
  66:    */
  67:   private final class OKAction extends AbstractAction
  68:   {
  69:     /**
  70:      * Default constructor.
  71:      */
  72:     private OKAction ()
  73:     {
  74:       putValue(NAME, UIManager.getDefaults().getString("OptionPane.okButtonText"));
  75:     }
  76: 
  77:     /**
  78:      * Receives notification that an action event has occurred.
  79:      *
  80:      * @param event the action event.
  81:      */
  82:     public void actionPerformed (final ActionEvent event)
  83:     {
  84:       setVisible(false);
  85:     }
  86:   }
  87: 
  88:   /**
  89:    * Details action.
  90:    */
  91:   private final class DetailsAction extends AbstractAction
  92:   {
  93:     /**
  94:      * Default constructor.
  95:      */
  96:     private DetailsAction ()
  97:     {
  98:       putValue(NAME, ">>");
  99:     }
 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: }