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 3048 2007-07-28 18:02:42Z tmorgner $ 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 }