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.lookup;
17  
18  import java.util.HashMap;
19  import java.util.Map;
20  
21  import org.apache.commons.lang.StringUtils;
22  import org.kuali.rice.core.api.util.ConcreteKeyValue;
23  import org.kuali.rice.krad.datadictionary.AttributeDefinition;
24  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
25  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
26  import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
27  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
28  import org.kuali.rice.krad.uif.UifConstants;
29  import org.kuali.rice.krad.uif.UifPropertyPaths;
30  import org.kuali.rice.krad.uif.control.CheckboxControl;
31  import org.kuali.rice.krad.uif.control.Control;
32  import org.kuali.rice.krad.uif.control.FilterableLookupCriteriaControl;
33  import org.kuali.rice.krad.uif.control.MultiValueControl;
34  import org.kuali.rice.krad.uif.control.RadioGroupControl;
35  import org.kuali.rice.krad.uif.control.TextAreaControl;
36  import org.kuali.rice.krad.uif.element.Message;
37  import org.kuali.rice.krad.uif.field.InputField;
38  import org.kuali.rice.krad.uif.field.InputFieldBase;
39  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
40  import org.kuali.rice.krad.uif.util.ComponentFactory;
41  import org.kuali.rice.krad.uif.util.ComponentUtils;
42  import org.kuali.rice.krad.uif.util.KeyMessage;
43  import org.kuali.rice.krad.uif.util.LifecycleElement;
44  import org.kuali.rice.krad.util.KRADConstants;
45  import org.kuali.rice.krad.util.KRADPropertyConstants;
46  
47  /**
48   * Custom {@link InputField} for criteria fields within a lookup view that adds criteria specific options.
49   *
50   * @author Kuali Rice Team (rice.collab@kuali.org)
51   */
52  @BeanTag(name = "lookupCriteria", parent = "Uif-LookupCriteriaInputField")
53  public class LookupInputField extends InputFieldBase {
54      private static final long serialVersionUID = -8294275596836322699L;
55  
56      private boolean disableWildcardsAndOperators;
57      private boolean addControlSelectAllOption;
58      private boolean ranged;
59  
60      public LookupInputField() {
61          super();
62      }
63  
64      /**
65       * The following actions are performed:
66       *
67       * <ul>
68       * <li>Add all option if enabled and control is multi-value</li>
69       * </ul>
70       *
71       * {@inheritDoc}
72       */
73      @Override
74      public void performFinalize(Object model, LifecycleElement parent) {
75          super.performFinalize(model, parent);
76  
77          // if enabled add option to select all values
78          if (addControlSelectAllOption && (getControl() != null) && getControl() instanceof MultiValueControl) {
79              String allOptionText = KRADServiceLocatorWeb.getMessageService().getMessageText(
80                      UifConstants.MessageKeys.OPTION_ALL);
81  
82              MultiValueControl multiValueControl = (MultiValueControl) getControl();
83              if (multiValueControl.getOptions() != null) {
84                  multiValueControl.getOptions().add(0, new ConcreteKeyValue("", allOptionText));
85              }
86  
87              if (multiValueControl.getRichOptions() != null) {
88                  Message message = ComponentFactory.getMessage();
89                  message.setMessageText(allOptionText);
90                  message.setRenderWrapperTag(false);
91  
92                  multiValueControl.getRichOptions().add(0, new KeyMessage("", allOptionText, message));
93              }
94          }
95      }
96  
97      /**
98       * Invoked during the finalize phase to capture state of the component needs to support post operations.
99       */
100     @Override
101     protected void addComponentPostMetadata() {
102         super.addComponentPostMetadata();
103 
104         Map<String, Map<String, Object>> lookupCriteriaFields = ViewLifecycle.getViewPostMetadata().getLookupCriteria();
105 
106         Map<String, Object> criteriaAttributes = new HashMap<String, Object>();
107         criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID, this.getId());
108 
109         if (this.isDisableWildcardsAndOperators()) {
110             criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.DISABLE_WILDCARDS_AND_OPERATORS, true);
111         }
112 
113         if (this.getRequired()) {
114             criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.REQUIRED, true);
115         }
116 
117         if (this.hasSecureValue()) {
118             criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.SECURE_VALUE, true);
119         }
120 
121         ValidCharactersConstraint validCharactersConstraint = this.getValidCharactersConstraint();
122         if (validCharactersConstraint != null) {
123             criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.VALID_CHARACTERS_CONSTRAINT,
124                     validCharactersConstraint);
125         }
126 
127         lookupCriteriaFields.put(this.getPropertyName(), criteriaAttributes);
128 
129         addHiddenComponentPostMetadata(lookupCriteriaFields);
130     }
131 
132     /**
133      * Add hidden search criteria components.
134      *
135      * @param lookupCriteriaFields
136      */
137     protected void addHiddenComponentPostMetadata(Map<String, Map<String, Object>> lookupCriteriaFields) {
138         for (String hiddenPropertyName: this.getAdditionalHiddenPropertyNames()) {
139             hiddenPropertyName = StringUtils.substringBetween(hiddenPropertyName, UifPropertyPaths.LOOKUP_CRITERIA + "[", "]");
140 
141             // Prevent overwriting of visible components. Note, hidden components are allowed to be overwritten.
142             if (!lookupCriteriaFields.containsKey(hiddenPropertyName)) {
143                 Map<String, Object> criteriaAttributes = new HashMap<String, Object>();
144                 criteriaAttributes.put(UifConstants.LookupCriteriaPostMetadata.HIDDEN, true);
145                 lookupCriteriaFields.put(hiddenPropertyName, criteriaAttributes);
146             }
147         }
148     }
149 
150     /**
151      * Override of InputField copy to setup properties necessary to make the field usable for inputting
152      * search criteria.
153      *
154      * <p>Note super is not being called because we don't want to add restirctions that can cause problems
155      * with the use of wildcard</p>
156      *
157      * {@inheritDoc}
158      */
159     @Override
160     public void copyFromAttributeDefinition(AttributeDefinition attributeDefinition) {
161         // label
162         if (StringUtils.isEmpty(getLabel())) {
163             setLabel(attributeDefinition.getLabel());
164         }
165 
166         // short label
167         if (StringUtils.isEmpty(getShortLabel())) {
168             setShortLabel(attributeDefinition.getShortLabel());
169         }
170 
171         // security
172         if ((attributeDefinition.getAttributeSecurity() != null) && ((getDataFieldSecurity() == null) || (
173                 getDataFieldSecurity().getAttributeSecurity() == null))) {
174             initializeComponentSecurity();
175 
176             getDataFieldSecurity().setAttributeSecurity(attributeDefinition.getAttributeSecurity());
177         }
178 
179         // options
180         if (getOptionsFinder() == null) {
181             setOptionsFinder(attributeDefinition.getOptionsFinder());
182         }
183 
184         // use control from dictionary if not specified and convert for searching
185         if (getControl() == null) {
186             Control control = convertControlToLookupControl(attributeDefinition);
187             setControl(control);
188         }
189 
190         // overwrite maxLength to allow for wildcards and ranges; set a minimum max length unless it is greater than 100
191         setMaxLength(100);
192         if ( attributeDefinition.getMaxLength()!=null && (attributeDefinition.getMaxLength() > 100)) {
193             setMaxLength(attributeDefinition.getMaxLength());
194         }
195 
196         // set default value for active field to true
197         if (getDefaultValue() == null || (getDefaultValue() instanceof String && StringUtils.isEmpty((String)getDefaultValue()))) {
198             if ((StringUtils.equals(getPropertyName(), KRADPropertyConstants.ACTIVE))) {
199                 setDefaultValue(KRADConstants.YES_INDICATOR_VALUE);
200             }
201         }
202     }
203 
204     /**
205      * If control definition is defined on the given attribute definition, converts to an appropriate control for
206      * searching (if necessary) and returns a copy for setting on the field.
207      *
208      * @param attributeDefinition attribute definition instance to retrieve control from
209      * @return Control instance or null if not found
210      */
211     protected static Control convertControlToLookupControl(AttributeDefinition attributeDefinition) {
212         if (attributeDefinition.getControlField() == null) {
213             return null;
214         }
215 
216         Control newControl = null;
217 
218         // convert checkbox to radio with yes/no/both options
219         if (CheckboxControl.class.isAssignableFrom(attributeDefinition.getControlField().getClass())) {
220             newControl = (RadioGroupControl) ComponentFactory.getNewComponentInstance(
221                     ComponentFactory.CHECKBOX_CONVERTED_RADIO_CONTROL);
222         }
223         // text areas get converted to simple text inputs
224         else if (TextAreaControl.class.isAssignableFrom(attributeDefinition.getControlField().getClass())) {
225             newControl = ComponentFactory.getTextControl();
226         } else {
227             newControl = ComponentUtils.copy(attributeDefinition.getControlField(), "");
228         }
229 
230         return newControl;
231     }
232 
233     /**
234      * Invoked before search is carried out to perform any necessary filtering of the criteria.
235      *
236      * @param searchCriteria the search criteria to be filtered
237      * @return map of filtered search criteria
238      */
239     public Map<String, String> filterSearchCriteria(Map<String, String> searchCriteria) {
240         return searchCriteria;
241     }
242 
243     /**
244      * Indicates whether wildcard and other search operators should be disabled (treated as literals) for
245      * the input field.
246      *
247      * @return boolean true if wildcards and search operators should be disabled, false if enabled
248      */
249     @BeanTagAttribute(name = "disableWildcardsAndOperators")
250     public boolean isDisableWildcardsAndOperators() {
251         return this.disableWildcardsAndOperators;
252     }
253 
254     /**
255      * @see LookupInputField#isDisableWildcardsAndOperators()
256      */
257     public void setDisableWildcardsAndOperators(boolean disableWildcardsAndOperators) {
258         this.disableWildcardsAndOperators = disableWildcardsAndOperators;
259     }
260 
261     /**
262      * Indicates whether the option for all values (blank key, 'All' label) should be added to the lookup
263      * field, note this is only supported for {@link org.kuali.rice.krad.uif.control.MultiValueControl} instance.
264      *
265      * @return boolean true if all option should be added, false if not
266      */
267     @BeanTagAttribute(name = "addControlSelectAllOption")
268     public boolean isAddControlSelectAllOption() {
269         return addControlSelectAllOption;
270     }
271 
272     /**
273      * @see LookupInputField#isAddControlSelectAllOption()
274      */
275     public void setAddControlSelectAllOption(boolean addControlSelectAllOption) {
276         this.addControlSelectAllOption = addControlSelectAllOption;
277     }
278 
279     /**
280      * Indicates a field group should be created containing a from and to input field for date search
281      * ranges.
282      *
283      * <p>
284      * When this is set to true, the input field will be replaced by a field group that is created by
285      * copying the prototype {@link org.kuali.rice.krad.lookup.LookupView#getRangeFieldGroupPrototype()}. Within the
286      * field group, an lookup input field will be created for the from field, and this input will be used
287      * as the to date field. Between the two fields a message will be rendered that can be specified using
288      * {@link LookupView#getRangedToMessage()}
289      * </p>
290      *
291      * @return boolean true if ranged field group should be created, false if not
292      */
293     public boolean isRanged() {
294         return ranged;
295     }
296 
297     /**
298      * @see LookupInputField#isRanged()
299      */
300     public void setRanged(boolean ranged) {
301         this.ranged = ranged;
302     }
303 }