001    /*
002     * Copyright 2002-2005 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.binding.form.support;
017    
018    import javax.swing.Icon;
019    
020    import org.apache.commons.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import org.springframework.binding.form.FieldFace;
023    import org.springframework.binding.form.FormModel;
024    import org.springframework.context.NoSuchMessageException;
025    import org.springframework.context.support.DefaultMessageSourceResolvable;
026    import org.springframework.context.support.MessageSourceAccessor;
027    import org.springframework.core.ReflectiveVisitorHelper;
028    import org.springframework.richclient.application.ApplicationServices;
029    import org.springframework.richclient.application.ApplicationServicesLocator;
030    import org.springframework.richclient.core.LabelInfo;
031    import org.springframework.richclient.image.IconSource;
032    
033    /**
034     * An implementation of FieldFaceSource that resolves the FieldFace from the <code>MessageSourceAccessor</code>
035     * provided to the setMessageSourceAccessor method or from the {@link ApplicationServices} singleton if none is
036     * provided.
037     * 
038     * <p>
039     * The various properties of the FieldFace are resolved from a message source using message codes. These codes where
040     * generated by a {@link MessageCodeStrategy}. If no other {@link MessageCodeStrategy} is defined an instance of
041     * {@link DefaultMessageCodeStrategy} will be used
042     * 
043     * @author Oliver Hutchison
044     * @author Mathias Broekelmann
045     */
046    public class MessageSourceFieldFaceSource extends CachingFieldFaceSource {
047    
048        private static final Log log = LogFactory.getLog(MessageSourceFieldFaceSource.class);
049    
050        /**
051         * Name for the FieldFace's <code>displayName</code> property.
052         */
053        private static final String[] DISPLAY_NAME_PROPERTY = { "displayName" };
054    
055        /**
056         * Name for the FieldFace's <code>caption</code> property.
057         */
058        private static final String[] CAPTION_PROPERTY = { "caption" };
059    
060        /**
061         * Name for the FieldFace's <code>description</code> property.
062         */
063        private static final String[] DESCRIPTION_PROPERTY = { "description" };
064    
065        /**
066         * Name for the FieldFace's <code>labelInfo</code> property.
067         */
068        private static final String[] ENCODED_LABEL_PROPERTY = { "label", "" };
069    
070        /**
071         * Name for the FieldFace's <code>icon</code> property.
072         */
073        private static final String[] ICON_PROPERTY = { "icon" };
074    
075        private MessageSourceAccessor messageSourceAccessor;
076    
077        private MessageCodeStrategy messageKeyStrategy;
078    
079        private IconSource iconSource;
080    
081        private final ReflectiveVisitorHelper visitorHelper = new ReflectiveVisitorHelper();
082    
083        /**
084         * Constructs a new MessageSourcePropertyFaceDescriptorSource.
085         */
086        public MessageSourceFieldFaceSource() {
087        }
088    
089        /**
090         * Set the message source that will be used to resolve the FieldFace's properties.
091         */
092        public void setMessageSourceAccessor(MessageSourceAccessor messageSourceAccessor) {
093            this.messageSourceAccessor = messageSourceAccessor;
094        }
095    
096        /**
097         * If a message source was provided to the setMessageSourceAccessor method returns that otherwise returns the
098         * default message source located using the {@link ApplicationServices} singleton
099         */
100        protected MessageSourceAccessor getMessageSourceAccessor() {
101            if (messageSourceAccessor == null) {
102                messageSourceAccessor = (MessageSourceAccessor) ApplicationServicesLocator.services().getService(
103                        MessageSourceAccessor.class);
104            }
105            return messageSourceAccessor;
106        }
107    
108        /**
109         * Set the icon source that will be used to resolve the FieldFace's icon property.
110         */
111        public void setIconSource(IconSource iconSource) {
112            this.iconSource = iconSource;
113        }
114    
115        protected IconSource getIconSource() {
116            if (iconSource == null) {
117                iconSource = (IconSource) ApplicationServicesLocator.services().getService(IconSource.class);
118            }
119            return iconSource;
120        }
121    
122        /**
123         * Returns the value of the required property of the FieldFace. Delegates to the getMessageKeys for the message key
124         * generation strategy. This method uses </code>[contextId + "." + ] fieldPath [ + "." + faceDescriptorProperty[0]]</code>
125         * for the default value
126         */
127        protected String getMessage(String contextId, String fieldPath, String[] faceDescriptorProperty) {
128            String[] keys = getMessageKeys(contextId, fieldPath, faceDescriptorProperty);
129            return getMessageSourceAccessor().getMessage(new DefaultMessageSourceResolvable(keys, null, keys[0]));
130        }
131    
132        /**
133         * Returns the value of the required property of the FieldFace. Delegates to the getMessageKeys for the message key
134         * generation strategy.
135         */
136        protected String getMessage(String contextId, String fieldPath, String[] faceDescriptorProperties,
137                String defaultValue) {
138            String[] keys = getMessageKeys(contextId, fieldPath, faceDescriptorProperties);
139            try {
140                return getMessageSourceAccessor().getMessage(new DefaultMessageSourceResolvable(keys, null, defaultValue));
141            } catch (NoSuchMessageException e) {
142                if (log.isDebugEnabled()) {
143                    log.debug(e.getMessage());
144                }
145                return null;
146            }
147        }
148    
149        /**
150         * Returns an array of message keys that are used to resolve the required property of the FieldFace. The property
151         * will be resolved from the message source using the returned message keys in order.
152         * <p>
153         * Subclasses my override this method to provide an alternative to the default message key generation strategy.
154         */
155        protected String[] getMessageKeys(String contextId, String fieldPath, String[] faceDescriptorProperties) {
156            return getMessageKeyStrategy().getMessageCodes(contextId, fieldPath, faceDescriptorProperties);
157        }
158    
159        protected FieldFace loadFieldFace(String field, String contextId) {
160            String caption = getMessage(contextId, field, CAPTION_PROPERTY, null);
161            String description = getMessage(contextId, field, DESCRIPTION_PROPERTY, null);
162            String encodedLabel = getMessage(contextId, field, ENCODED_LABEL_PROPERTY);
163            if (encodedLabel == null) {
164                // try loading the default value
165                encodedLabel = getMessage(contextId, field, null);
166            }
167            String iconName = getMessage(contextId, field, ICON_PROPERTY, null);
168            Icon icon = null;
169            if (iconName != null) {
170                icon = getIconSource().getIcon(iconName);
171            }
172            LabelInfo labelInfo = LabelInfo.valueOf(encodedLabel);
173            String displayName = getMessage(contextId, field, DISPLAY_NAME_PROPERTY, labelInfo.getText());
174            return new DefaultFieldFace(displayName, caption, description, labelInfo, icon);
175        }
176    
177        protected FieldFace loadFieldFace(String field, Object context) {
178            String contextId = (String) visitorHelper.invokeVisit(this, context);
179            return loadFieldFace(field, contextId);
180        }
181    
182        public MessageCodeStrategy getMessageKeyStrategy() {
183            if (messageKeyStrategy == null) {
184                messageKeyStrategy = new DefaultMessageCodeStrategy();
185            }
186            return messageKeyStrategy;
187        }
188    
189        public void setMessageKeyStrategy(MessageCodeStrategy messageKeyStrategy) {
190            this.messageKeyStrategy = messageKeyStrategy;
191        }
192    
193        // visit methods for getting a context id from various context instances
194    
195        String visit(FormModel formModel) {
196            return formModel.getId();
197        }
198    
199        String visit(CharSequence contextId) {
200            return contextId.toString();
201        }
202    
203        String visitNull() {
204            return null;
205        }
206    }