001    /**
002     * Copyright 2005-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
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,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krad.uif.control;
017    
018    import org.kuali.rice.core.api.util.KeyValue;
019    import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
020    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
021    import org.kuali.rice.krad.uif.UifConstants;
022    import org.kuali.rice.krad.uif.component.Component;
023    import org.kuali.rice.krad.uif.container.Container;
024    import org.kuali.rice.krad.uif.element.Message;
025    import org.kuali.rice.krad.uif.field.InputField;
026    import org.kuali.rice.krad.uif.util.ComponentFactory;
027    import org.kuali.rice.krad.uif.util.ExpressionUtils;
028    import org.kuali.rice.krad.uif.util.KeyMessage;
029    import org.kuali.rice.krad.uif.util.UrlInfo;
030    import org.kuali.rice.krad.uif.util.UifKeyValueLocation;
031    import org.kuali.rice.krad.uif.view.View;
032    
033    import java.util.ArrayList;
034    import java.util.List;
035    
036    /**
037     * Base class for controls that accept/display multiple values
038     *
039     * @author Kuali Rice Team (rice.collab@kuali.org)
040     */
041    public abstract class MultiValueControlBase extends ControlBase implements MultiValueControl {
042        private static final long serialVersionUID = -8691367056245775455L;
043    
044        private List<KeyValue> options;
045        private List<KeyMessage> richOptions;
046        private List<Component> inlineComponents;
047    
048        private boolean locationSelect = false;
049    
050        public MultiValueControlBase() {
051            super();
052        }
053    
054        /**
055         * Process rich message content that may be in the options, by creating and initializing the richOptions
056         *
057         * @see org.kuali.rice.krad.uif.component.ComponentBase#performApplyModel(org.kuali.rice.krad.uif.view.View,
058         *      Object, org.kuali.rice.krad.uif.component.Component)
059         */
060        @Override
061        public void performApplyModel(View view, Object model, Component parent) {
062            super.performApplyModel(view, model, parent);
063    
064            if (options != null && richOptions == null) {
065                richOptions = new ArrayList<KeyMessage>();
066    
067                for (KeyValue option : options) {
068                    Message message = ComponentFactory.getMessage();
069                    view.assignComponentIds(message);
070                    message.setMessageText(option.getValue());
071                    message.setInlineComponents(inlineComponents);
072                    message.setGenerateSpan(false);
073    
074                    view.getViewHelperService().performComponentInitialization(view, model, message);
075                    richOptions.add(new KeyMessage(option.getKey(), option.getValue(), message));
076                }
077            }
078        }
079    
080        /**
081         * Adds appropriate parent data to inputs internal to the controls that may be in rich content of options
082         *
083         * @see org.kuali.rice.krad.uif.component.Component#performFinalize(org.kuali.rice.krad.uif.view.View, Object,
084         *      org.kuali.rice.krad.uif.component.Component)
085         */
086        @Override
087        public void performFinalize(View view, Object model, Component parent) {
088            super.performFinalize(view, model, parent);
089    
090            if (options != null && !options.isEmpty()) {
091                for (KeyValue option : options) {
092                    if (option instanceof UifKeyValueLocation) {
093                        locationSelect = true;
094                        UrlInfo url = ((UifKeyValueLocation) option).getLocation();
095                        ExpressionUtils.populatePropertyExpressionsFromGraph(url, false);
096                        KRADServiceLocatorWeb.getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, url,
097                                model, view.getContext());
098                    }
099                }
100            }
101    
102            if (richOptions == null || richOptions.isEmpty()) {
103                return;
104            }
105    
106            //Messages included in options which have have rich message content need to be aware of their parent for
107            //validation purposes
108            for (KeyMessage richOption : richOptions) {
109                List<Component> components = richOption.getMessage().getMessageComponentStructure();
110    
111                if (components != null && !components.isEmpty()) {
112                    for (Component c : components) {
113                        if (c instanceof Container || c instanceof InputField) {
114                            c.addDataAttribute(UifConstants.DataAttributes.PARENT, parent.getId());
115                        }
116                    }
117                }
118            }
119    
120        }
121    
122        /**
123         * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
124         */
125        @Override
126        public List<Component> getComponentsForLifecycle() {
127            List<Component> components = super.getComponentsForLifecycle();
128    
129            if (richOptions != null) {
130                for (KeyMessage richOption : richOptions) {
131                    components.add(richOption.getMessage());
132                }
133            }
134    
135            return components;
136        }
137    
138        /**
139         * @see MultiValueControl#getOptions()
140         */
141        @BeanTagAttribute(name = "options", type = BeanTagAttribute.AttributeType.LISTBEAN)
142        public List<KeyValue> getOptions() {
143            return this.options;
144        }
145    
146        /**
147         * @see MultiValueControl#setOptions(java.util.List<org.kuali.rice.core.api.util.KeyValue>)
148         */
149        public void setOptions(List<KeyValue> options) {
150            this.options = options;
151        }
152    
153        /**
154         * Gets the inlineComponents which represent components that can be referenced in an option's value
155         * by index
156         *
157         * @return the components that can be used in rich values of options
158         */
159        @BeanTagAttribute(name = "inlineComponents", type = BeanTagAttribute.AttributeType.LISTBEAN)
160        public List<Component> getInlineComponents() {
161            return inlineComponents;
162        }
163    
164        /**
165         * Sets the inlineComponents which represent components that can be referenced in an option's value
166         * by index
167         *
168         * @param inlineComponents
169         */
170        public void setInlineComponents(List<Component> inlineComponents) {
171            this.inlineComponents = inlineComponents;
172        }
173    
174        /**
175         * @see MultiValueControl#getRichOptions()
176         */
177        public List<KeyMessage> getRichOptions() {
178            return richOptions;
179        }
180    
181        /**
182         * Sets the richOptions.  This will always override/ignore options if set.
183         *
184         * <p><b>Messages MUST be defined</b> when using this setter, do not use this setter for most cases
185         * as setting options through setOptions, with a richMessage value, is appropriate in MOST cases.  This
186         * setter is only available for full control.</p>
187         *
188         * @param richOptions with their messages predefined
189         */
190        public void setRichOptions(List<KeyMessage> richOptions) {
191            this.richOptions = richOptions;
192        }
193    
194        /**
195         * If true, this select represents a location select (navigate on select of option)
196         *
197         * @return true if this is a location select
198         */
199        public boolean isLocationSelect() {
200            return locationSelect;
201        }
202    }