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 &lt;, &gt; and &amp; characters
171         */
172        private static String escapeXml(String input)
173        {
174            return input == null ? "" : input.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
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>", "&lt;init&gt;") + "\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>", "&lt;init&gt;") + "\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    }