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 }