001 package org.springframework.richclient.util; 002 003 import org.jdesktop.swingx.JXErrorPane; 004 import org.jdesktop.swingx.error.ErrorInfo; 005 import org.springframework.beans.BeanUtils; 006 import org.springframework.beans.BeanWrapper; 007 import org.springframework.beans.BeanWrapperImpl; 008 import org.springframework.beans.BeansException; 009 import org.springframework.binding.form.FormModel; 010 import org.springframework.context.ApplicationContext; 011 import org.springframework.context.MessageSourceResolvable; 012 import org.springframework.context.support.DefaultMessageSourceResolvable; 013 import org.springframework.context.support.MessageSourceAccessor; 014 import org.springframework.richclient.application.Application; 015 import org.springframework.richclient.application.ApplicationServicesLocator; 016 import org.springframework.richclient.application.ApplicationWindow; 017 import org.springframework.richclient.application.config.ApplicationObjectConfigurer; 018 import org.springframework.richclient.application.support.DefaultApplicationServices; 019 import org.springframework.richclient.command.AbstractCommand; 020 import org.springframework.richclient.command.ActionCommand; 021 import org.springframework.richclient.command.config.CommandButtonConfigurer; 022 import org.springframework.richclient.command.config.CommandConfigurer; 023 import org.springframework.richclient.command.config.CommandFaceDescriptor; 024 import org.springframework.richclient.command.support.DataEditorWidgetViewCommand; 025 import org.springframework.richclient.exceptionhandling.EmailNotifierErrorReporter; 026 import org.springframework.richclient.factory.ButtonFactory; 027 import org.springframework.richclient.factory.DefaultButtonFactory; 028 import org.springframework.richclient.image.IconSource; 029 import org.springframework.richclient.widget.editor.DefaultDataEditorWidget; 030 import org.springframework.util.StringUtils; 031 032 import javax.swing.*; 033 import java.awt.*; 034 import java.sql.SQLException; 035 import java.text.MessageFormat; 036 import java.util.*; 037 import java.util.List; 038 import java.util.logging.Level; 039 040 public class RcpSupport 041 { 042 public static final String ERROR_KEY = "error"; 043 044 // standard message keys 045 public static final String LABEL = "label"; 046 public static final String HEADER = "header"; 047 public static final String TEXT = "text"; 048 public static final String TITLE = "title"; 049 public static final String MESSAGE = "message"; 050 public static final String DETAIL = "detail"; 051 public static final String CAPTION = "caption"; 052 public static final String ICON = "icon"; 053 054 public static final int YES_OPTION = 0; 055 public static final int OK_OPTION = 0; 056 public static final int NO_OPTION = 1; 057 public static final int CANCEL_OPTION = 2; 058 059 private static MessageSourceAccessor messageSourceAccessor; 060 private static CommandConfigurer commandConfigurer; 061 private static ApplicationContext applicationContext; 062 private static ApplicationObjectConfigurer applicationObjectConfigurer; 063 private static IconSource iconSource; 064 065 private static Map<Integer, Object[]> optionsMap; 066 067 static 068 { 069 optionsMap = new HashMap<Integer, Object[]>(); 070 String yes = getMessage(null, "OptionPane.yesButtonText", LABEL); 071 String no = getMessage(null, "OptionPane.noButtonText", LABEL); 072 String cancel = getMessage(null, "OptionPane.cancelButtonText", LABEL); 073 String ok = getMessage(null, "OptionPane.okButtonText", LABEL); 074 075 optionsMap.put(Integer.valueOf(JOptionPane.DEFAULT_OPTION), new Object[]{ok}); 076 optionsMap.put(Integer.valueOf(JOptionPane.YES_NO_OPTION), new Object[]{yes, no}); 077 optionsMap.put(Integer.valueOf(JOptionPane.YES_NO_CANCEL_OPTION), new Object[]{yes, no, cancel}); 078 optionsMap.put(Integer.valueOf(JOptionPane.OK_CANCEL_OPTION), new Object[]{ok, cancel}); 079 } 080 081 public static String[] getMessageKeys(String id, String name, String type) 082 { 083 boolean idNotEmpty = (id == null) || id.trim().equals("") ? false : true; 084 String[] keys = new String[idNotEmpty ? 3 : 2]; 085 int i = 0; 086 if (idNotEmpty) 087 { 088 keys[i++] = id + "." + name + "." + type; 089 } 090 keys[i++] = name + "." + type; 091 keys[i] = name; 092 return keys; 093 } 094 095 public static String getMessage(String id, String name, String type) 096 { 097 String[] messageKeys = getMessageKeys(id, name, type); 098 if (messageSourceAccessor == null) 099 messageSourceAccessor = (MessageSourceAccessor) ApplicationServicesLocator.services().getService( 100 MessageSourceAccessor.class); 101 102 return messageSourceAccessor.getMessage(new DefaultMessageSourceResolvable(messageKeys, null, 103 messageKeys[messageKeys.length - 1])); 104 } 105 106 public static String getMessage(MessageSourceResolvable msr) 107 { 108 if (messageSourceAccessor == null) 109 messageSourceAccessor = (MessageSourceAccessor) ApplicationServicesLocator.services().getService( 110 MessageSourceAccessor.class); 111 112 return messageSourceAccessor.getMessage(msr); 113 } 114 115 public static String getMessage(String id) 116 { 117 if (messageSourceAccessor == null) 118 messageSourceAccessor = (MessageSourceAccessor) ApplicationServicesLocator.services().getService( 119 MessageSourceAccessor.class); 120 121 return messageSourceAccessor.getMessage(id, ""); 122 } 123 124 public static String getMessage(String id, String name, String type, Object[] params) 125 { 126 String message = getMessage(id, name, type); 127 if (params != null) 128 return MessageFormat.format(message, params); 129 return message; 130 } 131 132 public static void configure(AbstractCommand command) 133 { 134 if (commandConfigurer == null) 135 commandConfigurer = (CommandConfigurer) ApplicationServicesLocator.services().getService( 136 CommandConfigurer.class); 137 commandConfigurer.configure(command); 138 } 139 140 public static void configure(Object object, String id) 141 { 142 if (applicationObjectConfigurer == null) 143 applicationObjectConfigurer = (ApplicationObjectConfigurer) ApplicationServicesLocator.services() 144 .getService(ApplicationObjectConfigurer.class); 145 applicationObjectConfigurer.configure(object, id); 146 } 147 148 public static Icon getIcon(String key) 149 { 150 if (iconSource == null) 151 iconSource = (IconSource) ApplicationServicesLocator.services().getService(IconSource.class); 152 return iconSource.getIcon(key); 153 } 154 155 public static <T> T getBean(String id) 156 { 157 if (applicationContext == null) 158 applicationContext = ((DefaultApplicationServices) ApplicationServicesLocator.services()) 159 .getApplicationContext(); 160 return (T) applicationContext.getBean(id); 161 } 162 163 public static <T> T getCommand(String commandId) 164 { 165 return (T) Application.instance().getActiveWindow().getCommandManager().getCommand(commandId); 166 } 167 168 /** 169 * Converts the incoming string to an escaped output string. This method is far from perfect, only 170 * escaping <, > and & characters 171 */ 172 private static String escapeXml(String input) 173 { 174 return input == null ? "" : input.replace("&", "&").replace("<", "<").replace(">", ">"); 175 } 176 177 /** 178 * Creates and returns HTML representing the details of this incident info. This method is only called if 179 * the details needs to be generated: ie: the detailed error message property of the incident info is 180 * null. 181 */ 182 private static String getDetailsAsHTML(String title, Level level, Throwable e) 183 { 184 if (e != null) 185 { 186 // convert the stacktrace into a more pleasent bit of HTML 187 StringBuffer html = new StringBuffer("<html>"); 188 html.append("<h2>" + escapeXml(title) + "</h2>"); 189 html.append("<HR size='1' noshade>"); 190 html.append("<div></div>"); 191 html.append("<b>Message:</b>"); 192 html.append("<pre>"); 193 html.append(" " + escapeXml(e.toString())); 194 html.append("</pre>"); 195 html.append("<b>Level:</b>"); 196 html.append("<pre>"); 197 html.append(" " + level); 198 html.append("</pre>"); 199 html.append("<b>Stack Trace:</b>"); 200 html.append("<pre>"); 201 for (StackTraceElement el : e.getStackTrace()) 202 { 203 html.append(" " + el.toString().replace("<init>", "<init>") + "\n"); 204 } 205 if (e.getCause() != null) 206 { 207 html.append("</pre>"); 208 html.append("<b>Cause:</b>"); 209 html.append("<pre>"); 210 html.append(e.getCause().getMessage()); 211 html.append("</pre><pre>"); 212 for (StackTraceElement el : e.getCause().getStackTrace()) 213 { 214 html.append(" " + el.toString().replace("<init>", "<init>") + "\n"); 215 } 216 } 217 html.append("</pre></html>"); 218 return html.toString(); 219 } 220 else 221 { 222 return null; 223 } 224 } 225 226 public static void handleException(Throwable t) 227 { 228 Application.instance().getLifecycleAdvisor().getRegisterableExceptionHandler().uncaughtException( 229 Thread.currentThread(), t); 230 } 231 232 public static void showErrorDialog(Throwable t) 233 { 234 String title = RcpSupport.getMessage(null, RcpSupport.ERROR_KEY, RcpSupport.TITLE); 235 String shortMessage = RcpSupport.getMessage(t.getClass().getName() + "." + RcpSupport.MESSAGE); 236 if (shortMessage == null || "".equals(shortMessage)) 237 { 238 shortMessage = t.getMessage(); 239 if (shortMessage == null || "".equals(shortMessage)) 240 { 241 shortMessage = RcpSupport.getMessage(null, RcpSupport.ERROR_KEY, RcpSupport.MESSAGE); 242 } 243 } 244 showErrorDialog(null, new ErrorInfo(title, shortMessage, getDetailsAsHTML(title, Level.SEVERE, t), 245 null, t, Level.SEVERE, null)); 246 } 247 248 public static void showSQLExceptionErrorDialog(SQLException sqlException) 249 { 250 String title = RcpSupport.getMessage(null, RcpSupport.ERROR_KEY, RcpSupport.TITLE); 251 String shortMessage = RcpSupport.getMessage(sqlException.getClass().getName() + "." 252 + sqlException.getErrorCode() + "." + RcpSupport.MESSAGE); 253 if (!StringUtils.hasText(shortMessage)) 254 { 255 shortMessage = RcpSupport 256 .getMessage(sqlException.getClass().getName() + "." + RcpSupport.MESSAGE); 257 shortMessage += "\nSQL error " + sqlException.getErrorCode(); 258 } 259 if (shortMessage == null || "".equals(shortMessage)) 260 { 261 shortMessage = sqlException.getMessage(); 262 if (shortMessage == null || "".equals(shortMessage)) 263 { 264 shortMessage = RcpSupport.getMessage(null, RcpSupport.ERROR_KEY, RcpSupport.MESSAGE); 265 } 266 } 267 showErrorDialog(null, new ErrorInfo(title, shortMessage, getDetailsAsHTML(title, Level.SEVERE, 268 sqlException), null, sqlException, Level.SEVERE, null)); 269 } 270 271 public static void showErrorDialogResolveMessages(String id) 272 { 273 String title = RcpSupport.getMessage(id, RcpSupport.ERROR_KEY, RcpSupport.TITLE); 274 String message = RcpSupport.getMessage(id, RcpSupport.ERROR_KEY, RcpSupport.MESSAGE); 275 String detail = RcpSupport.getMessage(id, RcpSupport.ERROR_KEY, RcpSupport.DETAIL); 276 detail = detail == RcpSupport.ERROR_KEY ? "" : detail; 277 showErrorDialog(title, message, detail); 278 } 279 280 public static void showErrorDialog(String message) 281 { 282 showErrorDialog(message, (String) null); 283 } 284 285 public static void showErrorDialog(String message, String detail) 286 { 287 showErrorDialog(RcpSupport.getMessage(null, RcpSupport.ERROR_KEY, RcpSupport.TITLE), message, detail); 288 } 289 290 public static void showErrorDialog(String id, Throwable cause) 291 { 292 String title = RcpSupport.getMessage(id, RcpSupport.ERROR_KEY, RcpSupport.TITLE); 293 String message = RcpSupport.getMessage(id, RcpSupport.ERROR_KEY, RcpSupport.MESSAGE); 294 showErrorDialog(null, new ErrorInfo(title, message, getDetailsAsHTML(title, Level.SEVERE, cause), 295 null, cause, Level.SEVERE, null)); 296 } 297 298 public static void showErrorDialog(String title, String message, String detail) 299 { 300 showErrorDialog(null, new ErrorInfo(title, message, detail, null, null, Level.SEVERE, null)); 301 } 302 303 public static void showErrorDialog(Component parent, ErrorInfo errorInfo) 304 { 305 306 if (parent == null) 307 { 308 if (org.springframework.richclient.application.Application.isLoaded()) 309 { 310 ApplicationWindow activeWindow = org.springframework.richclient.application.Application 311 .instance().getActiveWindow(); 312 if (activeWindow != null) 313 parent = activeWindow.getControl(); 314 } 315 } 316 317 JXErrorPane pane = new JXErrorPane(); 318 pane.setErrorInfo(errorInfo); 319 pane.setErrorReporter(new EmailNotifierErrorReporter()); 320 321 JXErrorPane.showDialog(parent, pane); 322 } 323 324 /** 325 * @see RcpSupport#showWarningDialog(Component, String, int) 326 */ 327 public static int showWarningDialog(String id, int optionType) 328 { 329 return showWarningDialog(Application.instance().getActiveWindow().getControl(), id, optionType); 330 } 331 332 /** 333 * @see RcpSupport#showWarningDialog(Component, String, int) 334 */ 335 public static int showWarningDialog(Component parent, String id, int optionType) 336 { 337 return showWarningDialog(parent, id, null, optionType); 338 } 339 340 public static int showWarningDialog(Component parent, String id, Object[] parameters, int optionType) 341 { 342 String message = getMessage(null, id, TEXT, parameters); 343 String title = getMessage(null, id, TITLE); 344 return JOptionPane.showConfirmDialog(parent, message, title, optionType, JOptionPane.WARNING_MESSAGE); 345 } 346 347 /** 348 * @see RcpSupport#showWarningDialog(Component, String, Object[], int, int) 349 */ 350 public static int showWarningDialog(String id, int optionType, int initialValue) 351 { 352 return showWarningDialog(Application.instance().getActiveWindow().getControl(), id, null, optionType, 353 initialValue); 354 } 355 356 public static int showWarningDialog(Component parent, String id, Object[] parameters, int optionType, 357 int initialValue) 358 { 359 Object[] options = optionsMap.get(Integer.valueOf(optionType)); 360 String message = getMessage(null, id, TEXT, parameters); 361 String title = getMessage(null, id, TITLE); 362 363 if (optionType == JOptionPane.OK_CANCEL_OPTION && initialValue == CANCEL_OPTION) 364 initialValue = 1; 365 366 if (initialValue >= options.length) 367 throw new IllegalArgumentException( 368 "De waarde van het argument initialValue is niet gekend door het gekozen optionType"); 369 370 return JOptionPane.showOptionDialog(parent, message, title, optionType, JOptionPane.WARNING_MESSAGE, 371 null, options, options[initialValue]); 372 } 373 374 public static ActionCommand createDummyCommand(final String id, final String msg) 375 { 376 ActionCommand newCommand = new ActionCommand(id) 377 { 378 379 protected void doExecuteCommand() 380 { 381 System.out.println(msg); 382 } 383 }; 384 ((CommandConfigurer) ApplicationServicesLocator.services().getService(CommandConfigurer.class)) 385 .configure(newCommand); 386 return newCommand; 387 } 388 389 public static void showWarningDialog(String id) 390 { 391 showWarningDialog(Application.instance().getActiveWindow().getControl(), id); 392 } 393 394 public static void showWarningDialog(Component parent, String id) 395 { 396 showWarningDialog(parent, id, null); 397 } 398 399 public static void showWarningDialog(Component parent, String id, Object[] parameters) 400 { 401 String message = getMessage(null, id, TEXT, parameters); 402 String title = getMessage(null, id, TITLE); 403 JOptionPane.showMessageDialog(parent, message, title, JOptionPane.WARNING_MESSAGE); 404 } 405 406 public static int showConfirmationDialog(String id) 407 { 408 return showConfirmationDialog(Application.instance().getActiveWindow().getControl(), id); 409 } 410 411 public static int showConfirmationDialog(Component parent, String id) 412 { 413 return showConfirmationDialog(parent, id, null); 414 } 415 416 public static int showConfirmationDialog(Component parent, String id, Object[] parameters) 417 { 418 return showConfirmationDialog(parent, id, parameters, JOptionPane.YES_NO_CANCEL_OPTION); 419 } 420 421 public static int showConfirmationDialog(Component parent, String id, Object[] parameters, int optionType) 422 { 423 String message = getMessage(null, id, TEXT, parameters); 424 String title = getMessage(null, id, TITLE); 425 return JOptionPane.showConfirmDialog(parent, message, title, optionType); 426 } 427 428 public static void showMessageDialog(Component parent, String id, Object[] parameters, int optionType) 429 { 430 String message = getMessage(null, id, TEXT, parameters); 431 String title = getMessage(null, id, TITLE); 432 JOptionPane.showMessageDialog(parent, message, title, optionType); 433 } 434 435 /** 436 * This method tries to map the values of the given object on the valueModels of the formModel. Instead of 437 * setting the object as a backing object, all valueModels are processed one by one and the corresponding 438 * property value is fetched from the objectToMap and set on that valueModel. This triggers the usual 439 * buffering etc. just as if the user entered the values. 440 * 441 * @param formModel 442 * @param objectToMap 443 */ 444 public static void mapObjectOnFormModel(FormModel formModel, Object objectToMap) 445 { 446 BeanWrapper beanWrapper = new BeanWrapperImpl(objectToMap); 447 for (String fieldName : (Set<String>) formModel.getFieldNames()) 448 { 449 try 450 { 451 formModel.getValueModel(fieldName).setValue(beanWrapper.getPropertyValue(fieldName)); 452 } 453 catch (BeansException be) 454 { 455 // silently ignoring, just mapping values, so if there's one missing, don't bother 456 } 457 } 458 } 459 460 private static Object clonePublicCloneableObject(Object value) 461 { 462 try 463 { 464 return ((PublicCloneable) value).clone(); 465 } 466 catch (CloneNotSupportedException e) 467 { 468 throw new RuntimeException("Clone not support for object " + value.getClass()); 469 } 470 } 471 472 public static Object getClone(Object value) 473 { 474 if (value instanceof PublicCloneable) 475 { 476 return clonePublicCloneableObject(value); 477 } 478 else if (value instanceof Collection) 479 { 480 Collection valueCollection = (Collection) value; 481 Collection clonedCollection; 482 if (valueCollection instanceof java.util.List) 483 { 484 clonedCollection = (valueCollection instanceof LinkedList) 485 ? new LinkedList() 486 : new ArrayList(); 487 } 488 else if (valueCollection instanceof Set) 489 { 490 clonedCollection = (valueCollection instanceof SortedSet) 491 ? new TreeSet() 492 : (valueCollection instanceof LinkedHashSet) ? new LinkedHashSet() : new HashSet(); 493 } 494 else 495 { 496 clonedCollection = (Collection) BeanUtils.instantiateClass(value.getClass()); 497 } 498 for (Object valueElement : valueCollection) 499 { 500 if (valueElement instanceof PublicCloneable) 501 { 502 clonedCollection.add(clonePublicCloneableObject(valueElement)); 503 } 504 else 505 { 506 // there isn't much else we can do? 507 clonedCollection.add(valueElement); 508 } 509 } 510 return clonedCollection; 511 } 512 else if (value instanceof Map) 513 { 514 Map valueMap = (Map) value; 515 Map clonedMap = (valueMap instanceof SortedMap) 516 ? new TreeMap() 517 : (valueMap instanceof LinkedHashMap) ? new LinkedHashMap() : new HashMap(); 518 for (Iterator entryIter = valueMap.entrySet().iterator(); entryIter.hasNext();) 519 { 520 Map.Entry entry = (Map.Entry) entryIter.next(); 521 if (entry.getValue() instanceof PublicCloneable) 522 { 523 clonedMap.put(entry.getKey(), clonePublicCloneableObject(entry.getValue())); 524 } 525 else 526 { 527 // there isn't much else we can do? 528 clonedMap.put(entry.getKey(), entry.getValue()); 529 } 530 } 531 return clonedMap; 532 } 533 return value; 534 } 535 536 public static JPanel createIconButtonPanel(List<? extends AbstractCommand> commands) 537 { 538 JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT)); 539 ButtonFactory factory = new DefaultButtonFactory(); 540 CommandButtonConfigurer configurer = new CommandButtonConfigurer() 541 { 542 543 public void configure(AbstractButton button, AbstractCommand command, 544 CommandFaceDescriptor faceDescriptor) 545 { 546 faceDescriptor.configureIcon(button); 547 button.setToolTipText(faceDescriptor.getCaption()); 548 } 549 }; 550 for (AbstractCommand command : commands) 551 { 552 buttons.add(command.createButton(factory, configurer)); 553 } 554 return buttons; 555 } 556 557 public static JComponent createDummyPanel(String vakske) 558 { 559 JPanel dummy = new JPanel(); 560 dummy.add(new JLabel(vakske)); 561 return dummy; 562 } 563 564 /** 565 * Toon een dataeditor in de huidige applicationwindow op basis van een command 566 * 567 * @param command 568 * De command die de dataeditor toont (mag niet null zijn) 569 * @param filter 570 * Eventueel initieel filterobject voor de dataeditor 571 * @param defaultSelectedObject 572 * Eventueel initieel geselecteerd item in de lijst 573 * 574 * @author ldo 575 * @since 0.4.4 576 */ 577 public static void executeViewDataEditorCommand(DataEditorWidgetViewCommand command, Object filter, 578 Object defaultSelectedObject) 579 { 580 org.springframework.util.Assert.notNull(command, "Command mag niet null zijn!"); 581 Map<String, Object> dataEditorParameters = new HashMap<String, Object>(2); 582 dataEditorParameters.put(DefaultDataEditorWidget.PARAMETER_FILTER, filter); 583 dataEditorParameters.put(DefaultDataEditorWidget.PARAMETER_DEFAULT_SELECTED_OBJECT, 584 defaultSelectedObject); 585 Map<String, Object> commandParameters = new HashMap<String, Object>(1); 586 commandParameters.put(DefaultDataEditorWidget.PARAMETER_MAP, dataEditorParameters); 587 command.execute(commandParameters); 588 } 589 }