View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.control;
17  
18  import org.apache.commons.lang.ObjectUtils;
19  import org.kuali.rice.core.api.util.AbstractKeyValue;
20  import org.kuali.rice.core.api.util.KeyValue;
21  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
22  import org.kuali.rice.krad.uif.UifConstants;
23  import org.kuali.rice.krad.uif.component.Component;
24  import org.kuali.rice.krad.uif.container.Container;
25  import org.kuali.rice.krad.uif.element.Message;
26  import org.kuali.rice.krad.uif.field.InputField;
27  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
28  import org.kuali.rice.krad.uif.util.ComponentFactory;
29  import org.kuali.rice.krad.uif.util.ComponentUtils;
30  import org.kuali.rice.krad.uif.util.KeyMessage;
31  import org.kuali.rice.krad.uif.util.LifecycleElement;
32  import org.kuali.rice.krad.uif.util.UifKeyValueLocation;
33  import org.kuali.rice.krad.uif.util.UifOptionGroupLabel;
34  import org.kuali.rice.krad.uif.util.UrlInfo;
35  import org.kuali.rice.krad.uif.view.ExpressionEvaluator;
36  import org.kuali.rice.krad.uif.view.View;
37  
38  import java.util.ArrayList;
39  import java.util.List;
40  
41  /**
42   * Base class for controls that accept/display multiple values
43   *
44   * @author Kuali Rice Team (rice.collab@kuali.org)
45   */
46  public abstract class MultiValueControlBase extends ControlBase implements MultiValueControl {
47      private static final long serialVersionUID = -8691367056245775455L;
48  
49      private List<KeyValue> options;
50      private List<KeyMessage> richOptions;
51      private List<Component> inlineComponents;
52  
53      private List<Message> internalMessageComponents;
54  
55      private boolean locationSelect = false;
56  
57      public MultiValueControlBase() {
58          super();
59      }
60  
61      /**
62       * Process rich message content that may be in the options, by creating and initializing the richOptions
63       *
64       * {@inheritDoc}
65       */
66      @Override
67      public void performApplyModel(Object model, LifecycleElement parent) {
68          super.performApplyModel(model, parent);
69          getStyleClassesAsString();
70          if (options != null && richOptions == null) {
71              richOptions = new ArrayList<KeyMessage>();
72              internalMessageComponents = new ArrayList<Message>();
73  
74              for (KeyValue option : options) {
75  
76                  // do this??
77                  if (option instanceof UifOptionGroupLabel) {
78                      continue;
79                  }
80  
81                  Message message = ComponentFactory.getMessage();
82  
83                  String key = option.getKey();
84                  if (key.contains(UifConstants.EL_PLACEHOLDER_PREFIX)) {
85                      key = ViewLifecycle.getExpressionEvaluator().evaluateExpression(this.getContext(), key).toString();
86                  }
87  
88                  String value = option.getValue();
89                  if (value.contains(UifConstants.EL_PLACEHOLDER_PREFIX)) {
90                      value = ViewLifecycle.getExpressionEvaluator().evaluateExpression(this.getContext(), value).toString();
91                  }
92  
93                  message.setMessageText(value);
94                  message.setInlineComponents(inlineComponents);
95                  message.setRenderWrapperTag(false);
96  
97                  // if the option is a sub-class of AbstractKeyValue class, then we also include the disabled attribute
98                  if(AbstractKeyValue.class.isAssignableFrom(option.getClass()) && ((AbstractKeyValue)option).isDisabled()) {
99                      richOptions.add(new KeyMessage(key, value, message, ((AbstractKeyValue)option).isDisabled()));
100                 } else {
101                     richOptions.add(new KeyMessage(key, value, message));
102                 }
103 
104                 internalMessageComponents.add(message);
105             }
106         }
107     }
108 
109     /**
110      * Adds appropriate parent data to inputs internal to the controls that may be in rich content of options
111      *
112      * {@inheritDoc}
113      */
114     @Override
115     public void performFinalize(Object model, LifecycleElement parent) {
116         super.performFinalize(model, parent);
117 
118         View view = ViewLifecycle.getView();
119         ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator();
120 
121         if (options != null && !options.isEmpty()) {
122             for (KeyValue option : options) {
123                 if (option instanceof UifKeyValueLocation) {
124                     locationSelect = true;
125 
126                     UrlInfo url = ((UifKeyValueLocation) option).getLocation();
127 
128                     ViewLifecycle.getExpressionEvaluator().populatePropertyExpressionsFromGraph(url, false);
129                     expressionEvaluator.evaluateExpressionsOnConfigurable(view, url, view.getContext());
130                 }
131             }
132         }
133 
134         if (richOptions == null || richOptions.isEmpty()) {
135             return;
136         }
137 
138         //Messages included in options which have have rich message content need to be aware of their parent for
139         //validation purposes
140         for (KeyMessage richOption : richOptions) {
141             List<Component> components = richOption.getMessage().getMessageComponentStructure();
142 
143             if (components != null && !components.isEmpty()) {
144                 for (Component c : components) {
145                     if (c instanceof Container || c instanceof InputField) {
146                         c.addDataAttribute(UifConstants.DataAttributes.PARENT, parent.getId());
147                     }
148                 }
149             }
150         }
151 
152     }
153 
154     /**
155      * @see MultiValueControl#getOptions()
156      */
157     @BeanTagAttribute
158     public List<KeyValue> getOptions() {
159         return this.options;
160     }
161 
162     /**
163      * {@inheritDoc}
164      */
165     public void setOptions(List<KeyValue> options) {
166         this.options = options;
167     }
168 
169     /**
170      * Gets the inlineComponents which represent components that can be referenced in an option's value
171      * by index
172      *
173      * @return the components that can be used in rich values of options
174      */
175     @BeanTagAttribute
176     public List<Component> getInlineComponents() {
177         return inlineComponents;
178     }
179 
180     /**
181      * Sets the inlineComponents which represent components that can be referenced in an option's value
182      * by index
183      *
184      * @param inlineComponents
185      */
186     public void setInlineComponents(List<Component> inlineComponents) {
187         this.inlineComponents = inlineComponents;
188     }
189 
190     /**
191      * @see MultiValueControl#getRichOptions()
192      */
193     public List<KeyMessage> getRichOptions() {
194         return richOptions;
195     }
196 
197     /**
198      * Sets the richOptions.  This will always override/ignore options if set.
199      *
200      * <p><b>Messages MUST be defined</b> when using this setter, do not use this setter for most cases
201      * as setting options through setOptions, with a richMessage value, is appropriate in MOST cases.  This
202      * setter is only available for full control.</p>
203      *
204      * @param richOptions with their messages predefined
205      */
206     public void setRichOptions(List<KeyMessage> richOptions) {
207         this.richOptions = richOptions;
208     }
209 
210     /**
211      * Used by reflection during the lifecycle to get internal message components that may be contained in options
212      *
213      * <p>There are no references to this method in the code, this is intentional.  DO NOT REMOVE.</p>
214      *
215      * @return the internal message components, if any
216      */
217     public List<Message> getInternalMessageComponents() {
218         return internalMessageComponents;
219     }
220 
221     /**
222      * If true, this select represents a location select (navigate on select of option)
223      *
224      * @return true if this is a location select
225      */
226     public boolean isLocationSelect() {
227         return locationSelect;
228     }
229 
230     /**
231      * Sets the location select (navigate on select of option)
232      *
233      * @param locationSelect
234      */
235     protected void setLocationSelect(boolean locationSelect) {
236         this.locationSelect = locationSelect;
237     }
238 }