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