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.field;
17  
18  import com.google.common.collect.Lists;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.uif.DataType;
21  import org.kuali.rice.core.api.util.ConcreteKeyValue;
22  import org.kuali.rice.core.api.util.KeyValue;
23  import org.kuali.rice.core.api.util.type.TypeUtils;
24  import org.kuali.rice.krad.datadictionary.AttributeDefinition;
25  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
26  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
27  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
28  import org.kuali.rice.krad.datadictionary.state.StateMapping;
29  import org.kuali.rice.krad.datadictionary.validation.capability.CaseConstrainable;
30  import org.kuali.rice.krad.datadictionary.validation.capability.MustOccurConstrainable;
31  import org.kuali.rice.krad.datadictionary.validation.capability.PrerequisiteConstrainable;
32  import org.kuali.rice.krad.datadictionary.validation.capability.SimpleConstrainable;
33  import org.kuali.rice.krad.datadictionary.validation.capability.ValidCharactersConstrainable;
34  import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
35  import org.kuali.rice.krad.datadictionary.validation.constraint.MustOccurConstraint;
36  import org.kuali.rice.krad.datadictionary.validation.constraint.PrerequisiteConstraint;
37  import org.kuali.rice.krad.datadictionary.validation.constraint.SimpleConstraint;
38  import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
39  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
40  import org.kuali.rice.krad.datadictionary.validator.Validator;
41  import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
42  import org.kuali.rice.krad.uif.UifConstants;
43  import org.kuali.rice.krad.uif.component.Component;
44  import org.kuali.rice.krad.uif.control.Control;
45  import org.kuali.rice.krad.uif.control.MultiValueControlBase;
46  import org.kuali.rice.krad.uif.control.TextAreaControl;
47  import org.kuali.rice.krad.uif.control.TextControl;
48  import org.kuali.rice.krad.uif.control.UifKeyValuesFinder;
49  import org.kuali.rice.krad.uif.element.Label;
50  import org.kuali.rice.krad.uif.element.Message;
51  import org.kuali.rice.krad.uif.element.ValidationMessages;
52  import org.kuali.rice.krad.uif.util.ClientValidationUtils;
53  import org.kuali.rice.krad.uif.util.CloneUtils;
54  import org.kuali.rice.krad.uif.util.ComponentFactory;
55  import org.kuali.rice.krad.uif.util.ComponentUtils;
56  import org.kuali.rice.krad.uif.util.ConstraintStateUtils;
57  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
58  import org.kuali.rice.krad.uif.view.View;
59  import org.kuali.rice.krad.uif.view.ViewModel;
60  import org.kuali.rice.krad.uif.widget.QuickFinder;
61  import org.kuali.rice.krad.uif.widget.Suggest;
62  import org.kuali.rice.krad.util.ObjectUtils;
63  
64  import java.util.ArrayList;
65  import java.util.List;
66  
67  /**
68   * Field that encapsulates data input/output captured by an attribute within the
69   * application
70   *
71   * <p>
72   * The {@code InputField} provides the majority of the data input/output
73   * for the screen. Through these fields the model can be displayed and updated.
74   * For data input, the field contains a {@link Control} instance will
75   * render an HTML control element(s). The input field also contains a
76   * {@link Label}, summary, and widgets such as a quickfinder (for
77   * looking up values) and inquiry (for getting more information on the value).
78   * {@code InputField} instances can have associated messages (errors)
79   * due to invalid input or business rule failures. Security can also be
80   * configured to restrict who may view the fields valnue.
81   * </p>
82   *
83   * @author Kuali Rice Team (rice.collab@kuali.org)
84   */
85  @BeanTags({@BeanTag(name = "inputField-bean", parent = "Uif-InputField"),
86          @BeanTag(name = "inputField-labelTop-bean", parent = "Uif-InputField-LabelTop"),
87          @BeanTag(name = "inputField-labelRight-bean", parent = "Uif-InputField-LabelRight"),
88          @BeanTag(name = "checkboxInputField-bean", parent = "Uif-CheckboxInputField"),
89          @BeanTag(name = "dialogResponse-bean", parent = "Uif-DialogResponse"),
90          @BeanTag(name = "dialogExplanation-bean", parent = "Uif-DialogExplanation"),
91          @BeanTag(name = "documentNumber-bean", parent = "Uif-DocumentNumber"),
92          @BeanTag(name = "documentStatus-bean", parent = "Uif-DocumentStatus"),
93          @BeanTag(name = "documentInitiatorNetworkId-bean", parent = "Uif-DocumentInitiatorNetworkId"),
94          @BeanTag(name = "documentCreateDate-bean", parent = "Uif-DocumentCreateDate"),
95          @BeanTag(name = "documentTemplateNumber-bean", parent = "Uif-DocumentTemplateNumber"),
96          @BeanTag(name = "documentDescription-bean", parent = "Uif-DocumentDescription"),
97          @BeanTag(name = "documentExplaination-bean", parent = "Uif-DocumentExplaination"),
98          @BeanTag(name = "organizationDocumentNumber-bean", parent = "Uif-OrganizationDocumentNumber"),
99          @BeanTag(name = "selectCollectionItemField-bean", parent = "Uif-SelectCollectionItemField")})
100 public class InputField extends DataField implements SimpleConstrainable, CaseConstrainable, PrerequisiteConstrainable, MustOccurConstrainable, ValidCharactersConstrainable {
101     private static final long serialVersionUID = -3703656713706343840L;
102 
103     // constraint variables
104     private String customValidatorClass;
105     private ValidCharactersConstraint validCharactersConstraint;
106     private CaseConstraint caseConstraint;
107     private List<PrerequisiteConstraint> dependencyConstraints;
108     private List<MustOccurConstraint> mustOccurConstraints;
109     private SimpleConstraint simpleConstraint;
110     private DataType dataType;
111 
112     // display props
113     private Control control;
114     private KeyValuesFinder optionsFinder;
115 
116     private boolean uppercaseValue;
117     private boolean disableNativeAutocomplete;
118 
119     private ValidationMessages validationMessages;
120 
121     // messages
122     private String constraintText;
123     private String instructionalText;
124 
125     private Message constraintMessage;
126     private Message instructionalMessage;
127 
128     private AttributeQuery attributeQuery;
129 
130     // widgets
131     private boolean enableAutoDirectInquiry;
132 
133     private QuickFinder quickfinder;
134     private boolean enableAutoQuickfinder;
135 
136     private Suggest suggest;
137 
138     private boolean widgetInputOnly;
139 
140     public InputField() {
141         super();
142 
143         simpleConstraint = new SimpleConstraint();
144 
145         enableAutoDirectInquiry = true;
146         enableAutoQuickfinder = true;
147     }
148 
149     /**
150      * The following initialization is performed:
151      *
152      * <ul>
153      * <li>Initializes instructional and constraint message fields if necessary</li>
154      * </ul>
155      *
156      * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View,
157      *      java.lang.Object)
158      */
159     @Override
160     public void performInitialization(View view, Object model) {
161         super.performInitialization(view, model);
162 
163         if ((StringUtils.isNotBlank(constraintText) || (getPropertyExpression("constraintText") != null)) && (
164                 constraintMessage
165                         == null)) {
166             constraintMessage = ComponentFactory.getConstraintMessage();
167             view.assignComponentIds(constraintMessage);
168         }
169 
170         if ((StringUtils.isNotBlank(instructionalText) || (getPropertyExpression("instructionalText") != null)) && (
171                 instructionalMessage
172                         == null)) {
173             instructionalMessage = ComponentFactory.getInstructionalMessage();
174             view.assignComponentIds(instructionalMessage);
175         }
176 
177     }
178 
179     /**
180      * @see Component#performApplyModel(org.kuali.rice.krad.uif.view.View, Object, org.kuali.rice.krad.uif.component.Component)
181      */
182     @Override
183     public void performApplyModel(View view, Object model, Component parent) {
184         super.performApplyModel(view, model, parent);
185 
186         // Done in apply model so we have the message text for additional rich message processing in Message
187         // Sets message
188         if (StringUtils.isNotBlank(instructionalText) && StringUtils.isBlank(instructionalMessage.getMessageText())) {
189             instructionalMessage.setMessageText(instructionalText);
190         }
191 
192         // Sets constraints
193         if (StringUtils.isNotBlank(constraintText) && StringUtils.isBlank(constraintMessage.getMessageText())) {
194             constraintMessage.setMessageText(constraintText);
195         }
196 
197         // invoke options finder if options not configured on the control
198         List<KeyValue> fieldOptions = new ArrayList<KeyValue>();
199 
200         // use options directly configured on the control first
201         if ((control != null) && control instanceof MultiValueControlBase) {
202             MultiValueControlBase multiValueControl = (MultiValueControlBase) control;
203             if ((multiValueControl.getOptions() != null) && !multiValueControl.getOptions().isEmpty()) {
204                 fieldOptions = multiValueControl.getOptions();
205             }
206         }
207 
208         // set multiLineReadOnlyDisplay to true to preserve text formatting
209         if (control instanceof TextAreaControl) {
210             setMultiLineReadOnlyDisplay(true);
211         }
212 
213         // if options not configured on the control, invoke configured options finder
214         if (fieldOptions.isEmpty() && (optionsFinder != null)) {
215             if (optionsFinder instanceof UifKeyValuesFinder) {
216                 fieldOptions = ((UifKeyValuesFinder) optionsFinder).getKeyValues((ViewModel) model, this);
217 
218                 // check if blank option should be added
219                 if (((UifKeyValuesFinder) optionsFinder).isAddBlankOption()) {
220                     fieldOptions.add(0, new ConcreteKeyValue("", ""));
221                 }
222             } else {
223                 fieldOptions = optionsFinder.getKeyValues();
224             }
225 
226             if ((control != null) && control instanceof MultiValueControlBase) {
227                 ((MultiValueControlBase) control).setOptions(fieldOptions);
228             }
229         }
230 
231         if (this.enableAutoDirectInquiry && (getInquiry() == null) && !isReadOnly()) {
232             buildAutomaticInquiry(view, model, true);
233         }
234 
235         if (this.enableAutoQuickfinder && (getQuickfinder() == null)) {
236             buildAutomaticQuickfinder(view, model);
237         }
238 
239         // if read only do key/value translation if necessary (if alternative and additional properties not set)
240         if (isReadOnly()
241                 && !fieldOptions.isEmpty()
242                 && StringUtils.isBlank(getReadOnlyDisplayReplacement())
243                 && StringUtils.isBlank(getReadOnlyDisplaySuffix())
244                 && StringUtils.isBlank(getReadOnlyDisplayReplacementPropertyName())
245                 && StringUtils.isBlank(getReadOnlyDisplaySuffixPropertyName())) {
246 
247             Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, getBindingInfo().getBindingPath());
248 
249             // TODO: can we translate Collections? (possibly combining output with delimiter
250             if ((fieldValue != null) && (TypeUtils.isSimpleType(fieldValue.getClass()))) {
251                 for (KeyValue keyValue : fieldOptions) {
252                     if (StringUtils.equals(fieldValue.toString(), keyValue.getKey())) {
253                         setReadOnlyDisplayReplacement(keyValue.getValue());
254                         break;
255                     }
256                 }
257             }
258         }
259     }
260 
261     /**
262      * The following actions are performed:
263      *
264      * <ul>
265      * <li>Set the ids for the various attribute components</li>
266      * <li>Sets up the client side validation for constraints on this field. In
267      * addition, it sets up the messages applied to this field</li>
268      * <li>Disable native autocomplete with the suggest widget is configured</li>
269      * </ul>
270      *
271      * @see org.kuali.rice.krad.uif.component.ComponentBase#performFinalize(org.kuali.rice.krad.uif.view.View,
272      *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
273      */
274     @Override
275     public void performFinalize(View view, Object model, Component parent) {
276         super.performFinalize(view, model, parent);
277 
278         setupIds();
279         this.addDataAttribute(UifConstants.DataAttributes.ROLE, UifConstants.RoleTypes.INPUT_FIELD);
280 
281         // if read only or the control is null no input can be given so no need to setup validation
282         if (isReadOnly() || getControl() == null) {
283             return;
284         }
285 
286         if (uppercaseValue) {
287             Object currentPropertyValue = ObjectPropertyUtils.getPropertyValue(model, getBindingInfo().getBindingPath());
288             if (currentPropertyValue instanceof String) {
289                 String uppercasedValue = ((String) currentPropertyValue).toUpperCase();
290                 ObjectPropertyUtils.setPropertyValue(model, getBindingInfo().getBindingPath(), uppercasedValue);
291             }
292         }
293 
294         // browser's native autocomplete causes issues with the suggest plugin
295         if ((suggest != null) && suggest.isSuggestConfigured()) {
296            setDisableNativeAutocomplete(true);
297         }
298 
299         // adjust paths on PrerequisiteConstraint property names
300         adjustPrerequisiteConstraintBinding(dependencyConstraints);
301 
302         // adjust paths on MustOccurConstraints property names
303         adjustMustOccurConstraintBinding(mustOccurConstraints);
304 
305         // adjust paths on CaseConstraint property names
306         if (caseConstraint != null) {
307             String propertyName = getBindingInfo().getPropertyAdjustedBindingPath(caseConstraint.getPropertyName());
308             caseConstraint.setPropertyName(propertyName);
309         }
310 
311         setupFieldQuery();
312 
313         // special requiredness indicator handling, if this was previously not required reset its required
314         // message to be ** for indicating required in the next state
315         String path = view.getStateObjectBindingPath();
316         Object stateObject;
317 
318         if (StringUtils.isNotBlank(path)) {
319             stateObject = ObjectPropertyUtils.getPropertyValue(model, path);
320         } else {
321             stateObject = model;
322         }
323         StateMapping stateMapping = view.getStateMapping();
324 
325         if (stateMapping != null) {
326             String validationState = ConstraintStateUtils.getClientViewValidationState(model, view);
327             SimpleConstraint appliedSimpleConstraint = ConstraintStateUtils.getApplicableConstraint(
328                     this.getSimpleConstraint(), validationState, stateMapping);
329 
330             if (appliedSimpleConstraint != null
331                     && appliedSimpleConstraint.getRequired() != null
332                     && appliedSimpleConstraint.getRequired()) {
333                 SimpleConstraint prevConstraint = ConstraintStateUtils.getApplicableConstraint(
334                         this.getSimpleConstraint(), stateMapping.getCurrentState(stateObject), stateMapping);
335                 if (prevConstraint == null || prevConstraint.getRequired() == null || !prevConstraint.getRequired()) {
336                     this.getFieldLabel().getRequiredMessage().setMessageText("**");
337                 }
338             }
339         }
340 
341         ClientValidationUtils.processAndApplyConstraints(this, view, model);
342     }
343 
344     /**
345      * Creates a new {@link org.kuali.rice.krad.uif.widget.QuickFinder} and then invokes the lifecycle process for
346      * the quickfinder to determine if a relationship was found, if so the quickfinder is assigned to the field
347      *
348      * @param view view instance being processed
349      * @param model object containing the view data
350      */
351     protected void buildAutomaticQuickfinder(View view, Object model) {
352         QuickFinder autoQuickfinder = ComponentFactory.getQuickFinder();
353 
354         view.getViewHelperService().spawnSubLifecyle(view, model, autoQuickfinder, this,
355                 UifConstants.ViewPhases.INITIALIZE, UifConstants.ViewPhases.APPLY_MODEL);
356 
357         // if render flag is true, that means the quickfinder was able to find a relationship
358         if (autoQuickfinder.isRender()) {
359             this.quickfinder = autoQuickfinder;
360         }
361     }
362 
363     /**
364      * Overrides processReadOnlyListDisplay to handle MultiValueControls by creating the list of values from values
365      * instead of the keys of the options selected (makes the list "human-readable").  Otherwise it just passes the
366      * list ahead as normal if this InputField does not use a MultiValueControl.
367      *
368      * @param model the model
369      * @param originalList originalList of values
370      */
371     @Override
372     protected void processReadOnlyListDisplay(Object model, List<?> originalList) {
373         //Special handling for option based fields
374         if ((control != null) && control instanceof MultiValueControlBase) {
375             List<String> newList = new ArrayList<String>();
376             List<KeyValue> fieldOptions = ((MultiValueControlBase) control).getOptions();
377 
378             if (fieldOptions == null || fieldOptions.isEmpty()) {
379                 return;
380             }
381 
382             for (Object fieldValue : originalList) {
383                 for (KeyValue keyValue : fieldOptions) {
384                     if (fieldValue != null && StringUtils.equals(fieldValue.toString(), keyValue.getKey())) {
385                         newList.add(keyValue.getValue());
386                         break;
387                     }
388                 }
389             }
390             this.setReadOnlyDisplayReplacement(super.generateReadOnlyListDisplayReplacement(newList));
391         } else {
392             this.setReadOnlyDisplayReplacement(super.generateReadOnlyListDisplayReplacement(originalList));
393         }
394     }
395 
396     /**
397      * Overridding to check quickfinder when masked is being applied. If quickfinder is configured set the component
398      * to widgetInputOnly, else set to readOnlh
399      *
400      * @see DataField#setAlternateAndAdditionalDisplayValue(org.kuali.rice.krad.uif.view.View, java.lang.Object)
401      */
402     @Override
403     protected void setAlternateAndAdditionalDisplayValue(View view, Object model) {
404         // if alternate or additional display values set don't override
405         if (StringUtils.isNotBlank(getReadOnlyDisplayReplacement()) || StringUtils.isNotBlank(
406                 getReadOnlyDisplaySuffix())) {
407             return;
408         }
409 
410         if (isApplyMask()) {
411             if ((this.quickfinder != null) && StringUtils.isNotBlank(this.quickfinder.getDataObjectClassName())) {
412                 setWidgetInputOnly(true);
413             } else {
414                 setReadOnly(true);
415             }
416         }
417 
418         super.setAlternateAndAdditionalDisplayValue(view, model);
419     }
420 
421     /**
422      * Adjust paths on the must occur constrain bindings
423      *
424      * @param mustOccurConstraints
425      */
426     protected void adjustMustOccurConstraintBinding(List<MustOccurConstraint> mustOccurConstraints) {
427         if (mustOccurConstraints != null) {
428             for (MustOccurConstraint mustOccurConstraint : mustOccurConstraints) {
429                 adjustPrerequisiteConstraintBinding(mustOccurConstraint.getPrerequisiteConstraints());
430                 adjustMustOccurConstraintBinding(mustOccurConstraint.getMustOccurConstraints());
431             }
432         }
433     }
434 
435     /**
436      * Adjust paths on the prerequisite constraint bindings
437      *
438      * @param prerequisiteConstraints
439      */
440     protected void adjustPrerequisiteConstraintBinding(List<PrerequisiteConstraint> prerequisiteConstraints) {
441         if (prerequisiteConstraints != null) {
442             for (PrerequisiteConstraint prerequisiteConstraint : prerequisiteConstraints) {
443                 String propertyName = getBindingInfo().getPropertyAdjustedBindingPath(
444                         prerequisiteConstraint.getPropertyName());
445                 prerequisiteConstraint.setPropertyName(propertyName);
446             }
447         }
448     }
449 
450     /**
451      * Performs setup of the field attribute query and informational display properties. Paths
452      * are adjusted to match the binding for the this field, and the necessary onblur script for
453      * triggering the query client side is constructed
454      */
455     protected void setupFieldQuery() {
456         if (getAttributeQuery() != null) {
457             // adjust paths on query mappings
458             getAttributeQuery().updateQueryFieldMapping(getBindingInfo());
459             getAttributeQuery().updateReturnFieldMapping(getBindingInfo());
460             getAttributeQuery().updateQueryMethodArgumentFieldList(getBindingInfo());
461 
462             // build onblur script for field query
463             String script = "executeFieldQuery('" + getControl().getId() + "',";
464             script += "'" + getId() + "'," + getAttributeQuery().getQueryFieldMappingJsString() + ",";
465             script += getAttributeQuery().getQueryMethodArgumentFieldsJsString() + ",";
466             script += getAttributeQuery().getReturnFieldMappingJsString() + ");";
467 
468             if (StringUtils.isNotBlank(getControl().getOnBlurScript())) {
469                 script = getControl().getOnBlurScript() + script;
470             }
471             getControl().setOnBlurScript(script);
472         }
473     }
474 
475     /**
476      * Sets the ids on all components the input field uses so they will all
477      * contain this input field's id in their ids. This is useful for jQuery
478      * manipulation.
479      */
480     protected void setupIds() {
481         // update ids so they all match the attribute
482 
483         setNestedComponentIdAndSuffix(getControl(), UifConstants.IdSuffixes.CONTROL);
484         setNestedComponentIdAndSuffix(getValidationMessages(), UifConstants.IdSuffixes.ERRORS);
485         setNestedComponentIdAndSuffix(getFieldLabel(), UifConstants.IdSuffixes.LABEL);
486         setNestedComponentIdAndSuffix(getInstructionalMessage(), UifConstants.IdSuffixes.INSTRUCTIONAL);
487         setNestedComponentIdAndSuffix(getConstraintMessage(), UifConstants.IdSuffixes.CONSTRAINT);
488         setNestedComponentIdAndSuffix(getQuickfinder(), UifConstants.IdSuffixes.QUICK_FINDER);
489         setNestedComponentIdAndSuffix(getSuggest(), UifConstants.IdSuffixes.SUGGEST);
490 
491         if (this.getControl() != null) {
492             this.getControl().addDataAttribute(UifConstants.DataAttributes.CONTROL_FOR, this.getId());
493         }
494     }
495 
496     /**
497      * Defaults the properties of the {@code InputField} to the
498      * corresponding properties of its {@code AttributeDefinition}
499      * retrieved from the dictionary (if such an entry exists). If the field
500      * already contains a value for a property, the definitions value is not
501      * used.
502      *
503      * @param view view instance the field belongs to
504      * @param attributeDefinition AttributeDefinition instance the property values should be
505      * copied from
506      */
507     @Override
508     public void copyFromAttributeDefinition(View view, AttributeDefinition attributeDefinition) {
509         super.copyFromAttributeDefinition(view, attributeDefinition);
510 
511         // max length
512         if (getMaxLength() == null) {
513             setMaxLength(attributeDefinition.getMaxLength());
514         }
515 
516         // min length
517         if (getMinLength() == null) {
518             setMinLength(attributeDefinition.getMinLength());
519         }
520 
521         // valid characters
522         if (getValidCharactersConstraint() == null) {
523             setValidCharactersConstraint(attributeDefinition.getValidCharactersConstraint());
524         }
525 
526         if (getCaseConstraint() == null) {
527             setCaseConstraint(attributeDefinition.getCaseConstraint());
528         }
529 
530         if (getDependencyConstraints() == null) {
531             setDependencyConstraints(attributeDefinition.getPrerequisiteConstraints());
532         }
533 
534         if (getMustOccurConstraints() == null) {
535             setMustOccurConstraints(attributeDefinition.getMustOccurConstraints());
536         }
537 
538         // required
539         if (getRequired() == null) {
540             setRequired(attributeDefinition.isRequired());
541 
542             //if still null, default to false
543             if (getRequired() == null) {
544                 setRequired(Boolean.FALSE);
545             }
546         }
547 
548         if (getDataType() == null) {
549             setDataType(attributeDefinition.getDataType());
550             //Assume date if dataType is still null and using a DatePicker
551             if (getDataType() == null
552                     && control instanceof TextControl
553                     && ((TextControl) control).getDatePicker() != null) {
554                 setDataType(DataType.DATE);
555             }
556         }
557 
558         // control
559         if ((getControl() == null) && (attributeDefinition.getControlField() != null)) {
560             Control control = attributeDefinition.getControlField();
561             view.assignComponentIds(control);
562 
563             setControl(ComponentUtils.copy(control));
564         }
565 
566         // constraint
567         if (StringUtils.isEmpty(getConstraintText())) {
568             setConstraintText(attributeDefinition.getConstraintText());
569 
570             if (constraintMessage == null) {
571                 constraintMessage = ComponentFactory.getConstraintMessage();
572                 view.assignComponentIds(constraintMessage);
573             }
574             getConstraintMessage().setMessageText(attributeDefinition.getConstraintText());
575         }
576 
577         // options
578         if (getOptionsFinder() == null) {
579             setOptionsFinder(attributeDefinition.getOptionsFinder());
580         }
581 
582         // copy over simple constraint information because we cannot directly use simpleConstraint from
583         // attributeDefinition because the settings in InputField take precedence
584         if (this.getSimpleConstraint().getConstraintStateOverrides() == null) {
585             this.getSimpleConstraint().setConstraintStateOverrides(
586                     attributeDefinition.getSimpleConstraint().getConstraintStateOverrides());
587         }
588 
589         if (this.getSimpleConstraint().getStates().isEmpty()) {
590             this.getSimpleConstraint().setStates(attributeDefinition.getSimpleConstraint().getStates());
591         }
592 
593         if (this.getSimpleConstraint().getMessageKey() == null) {
594             this.getSimpleConstraint().setMessageKey(attributeDefinition.getSimpleConstraint().getMessageKey());
595         }
596 
597         if (this.getSimpleConstraint().getApplyClientSide() == null) {
598             this.getSimpleConstraint().setApplyClientSide(
599                     attributeDefinition.getSimpleConstraint().getApplyClientSide());
600         }
601 
602         if (this.getSimpleConstraint().getValidationMessageParams() == null) {
603             this.getSimpleConstraint().setValidationMessageParams(
604                     attributeDefinition.getSimpleConstraint().getValidationMessageParams());
605         }
606     }
607 
608     /**
609      * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
610      */
611     @Override
612     public List<Component> getComponentsForLifecycle() {
613         List<Component> components = super.getComponentsForLifecycle();
614 
615         components.add(instructionalMessage);
616         components.add(constraintMessage);
617         components.add(control);
618         components.add(validationMessages);
619         components.add(quickfinder);
620         components.add(suggest);
621 
622         return components;
623     }
624 
625     /**
626      * @see DataField#isInputAllowed()
627      */
628     @Override
629     public boolean isInputAllowed() {
630         return true;
631     }
632 
633     /**
634      * {@code Control} instance that should be used to input data for the
635      * field
636      *
637      * <p>
638      * When the field is editable, the control will be rendered so the user can
639      * input a value(s). Controls typically are part of a Form and render
640      * standard HTML control elements such as text input, select, and checkbox
641      * </p>
642      *
643      * @return Control instance
644      */
645     @BeanTagAttribute(name = "control", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
646     public Control getControl() {
647         return this.control;
648     }
649 
650     /**
651      * Setter for the field's control
652      *
653      * @param control
654      */
655     public void setControl(Control control) {
656         this.control = control;
657     }
658 
659     /**
660      * Field that contains the messages (errors) for the input field. The
661      * {@code ValidationMessages} holds configuration on associated messages along
662      * with information on rendering the messages in the user interface
663      *
664      * @return ValidationMessages instance
665      */
666     @BeanTagAttribute(name = "validationMessages", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
667     public ValidationMessages getValidationMessages() {
668         return this.validationMessages;
669     }
670 
671     /**
672      * Setter for the input field's errors field
673      *
674      * @param validationMessages
675      */
676     public void setValidationMessages(ValidationMessages validationMessages) {
677         this.validationMessages = validationMessages;
678     }
679 
680     /**
681      * Instance of {@code KeyValuesFinder} that should be invoked to
682      * provide a List of values the field can have. Generally used to provide
683      * the options for a multi-value control or to validate the submitted field
684      * value
685      *
686      * @return KeyValuesFinder instance
687      */
688     @BeanTagAttribute(name = "optionsFinder", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
689     public KeyValuesFinder getOptionsFinder() {
690         return this.optionsFinder;
691     }
692 
693     /**
694      * Setter for the field's KeyValuesFinder instance
695      *
696      * @param optionsFinder
697      */
698     public void setOptionsFinder(KeyValuesFinder optionsFinder) {
699         this.optionsFinder = optionsFinder;
700     }
701 
702     /**
703      * Get the class of the optionsFinder being used by this InputField
704      *
705      * @return the class of the set optionsFinder, if not set or not applicable, returns null
706      */
707     @BeanTagAttribute(name = "optionsFinderClass")
708     public Class<? extends KeyValuesFinder> getOptionsFinderClass(){
709         if(this.optionsFinder != null){
710             return this.optionsFinder.getClass();
711         }
712         else{
713             return null;
714         }
715     }
716 
717     /**
718      * Setter that takes in the class name for the options finder and creates a
719      * new instance to use as the finder for the input field
720      *
721      * @param optionsFinderClass the options finder class to set
722      */
723     public void setOptionsFinderClass(Class<? extends KeyValuesFinder> optionsFinderClass) {
724         this.optionsFinder = ObjectUtils.newInstance(optionsFinderClass);
725     }
726 
727     /**
728      * Indicates whether direct inquiries should be automatically set when a relationship for
729      * the field's property is found
730      *
731      * <p>
732      * Note this only applies when the {@link #getInquiry()} widget has not been configured (is null)
733      * and is set to true by default
734      * </p>
735      *
736      * @return true if auto direct inquiries are enabled, false if not
737      */
738     public boolean isEnableAutoDirectInquiry() {
739         return enableAutoDirectInquiry;
740     }
741 
742     /**
743      * Setter for enabling automatic direct inquiries
744      *
745      * @param enableAutoDirectInquiry
746      */
747     public void setEnableAutoDirectInquiry(boolean enableAutoDirectInquiry) {
748         this.enableAutoDirectInquiry = enableAutoDirectInquiry;
749     }
750 
751     /**
752      * Lookup finder widget for the field
753      *
754      * <p>
755      * The quickfinder widget places a small icon next to the field that allows
756      * the user to bring up a search screen for finding valid field values. The
757      * {@code Widget} instance can be configured to point to a certain
758      * {@code LookupView}, or the framework will attempt to associate the
759      * field with a lookup based on its metadata (in particular its
760      * relationships in the model)
761      * </p>
762      *
763      * @return QuickFinder lookup widget
764      */
765     @BeanTagAttribute(name = "quickfinder", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
766     public QuickFinder getQuickfinder() {
767         return this.quickfinder;
768     }
769 
770     /**
771      * Setter for the lookup widget
772      *
773      * @param quickfinder the field lookup widget to set
774      */
775     public void setQuickfinder(QuickFinder quickfinder) {
776         this.quickfinder = quickfinder;
777     }
778 
779     /**
780      * Indicates whether quickfinders should be automatically set when a relationship for the field's
781      * property is found
782      *
783      * <p>
784      * Note this only applies when the {@link #getQuickfinder()} widget has not been configured (is null)
785      * and is set to true by default
786      * </p>
787      *
788      * @return true if auto quickfinders are enabled, false if not
789      */
790     public boolean isEnableAutoQuickfinder() {
791         return enableAutoQuickfinder;
792     }
793 
794     /**
795      * Setter for enabling automatic quickfinders
796      *
797      * @param enableAutoQuickfinder
798      */
799     public void setEnableAutoQuickfinder(boolean enableAutoQuickfinder) {
800         this.enableAutoQuickfinder = enableAutoQuickfinder;
801     }
802 
803     /**
804      * Suggest box widget for the input field
805      *
806      * <p>
807      * If enabled (by render flag), as the user inputs data into the
808      * fields control a dynamic query is performed to provide the user
809      * suggestions on values which they can then select
810      * </p>
811      *
812      * <p>
813      * Note the Suggest widget is only valid when using a standard TextControl
814      * </p>
815      *
816      * @return Suggest instance
817      */
818     @BeanTagAttribute(name = "suggest", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
819     public Suggest getSuggest() {
820         return suggest;
821     }
822 
823     /**
824      * Setter for the fields suggest widget
825      *
826      * @param suggest the field suggest widget to  set
827      */
828     public void setSuggest(Suggest suggest) {
829         this.suggest = suggest;
830     }
831 
832     /**
833      * Indicates indicates whether the field can only be updated through a widget
834      *
835      * widgetInputOnly behaves similar to ReadOnly with the exception that the value of the input field
836      * can be changed via the associated widget (e.g. spinner, date picker, quickfinder, etc).
837      *
838      * @return true if only widget input is allowed, false otherwise
839      */
840     @BeanTagAttribute(name = "widgetInputOnly")
841     public boolean isWidgetInputOnly() {
842         return this.widgetInputOnly;
843     }
844 
845     /**
846      * Setter for the widget input only indicator
847      *
848      * @param widgetInputOnly
849      */
850     public void setWidgetInputOnly(boolean widgetInputOnly) {
851         this.widgetInputOnly = widgetInputOnly;
852     }
853 
854     /**
855      * Instructional text that display an explanation of the field usage
856      *
857      * <p>
858      * Text explaining how to use the field, including things like what values should be selected
859      * in certain cases and so on (instructions)
860      * </p>
861      *
862      * @return instructional message
863      */
864     @BeanTagAttribute(name = "instructionalText")
865     public String getInstructionalText() {
866         return this.instructionalText;
867     }
868 
869     /**
870      * Setter for the instructional message
871      *
872      * @param instructionalText the instructional text to set
873      */
874     public void setInstructionalText(String instructionalText) {
875         this.instructionalText = instructionalText;
876     }
877 
878     /**
879      * Message field that displays instructional text
880      *
881      * <p>
882      * This message field can be configured to for adjusting how the instructional text will display. Generally
883      * the styleClasses property will be of most interest
884      * </p>
885      *
886      * @return instructional message field
887      */
888     @BeanTagAttribute(name = "instructionalMessage", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
889     public Message getInstructionalMessage() {
890         return this.instructionalMessage;
891     }
892 
893     /**
894      * Setter for the instructional text message field
895      *
896      * <p>
897      * Note this is the setter for the field that will render the instructional text. The actual text can be
898      * set on the field but can also be set using {@link #setInstructionalText(String)}
899      * </p>
900      *
901      * @param instructionalMessage the instructional message to set
902      */
903     public void setInstructionalMessage(Message instructionalMessage) {
904         this.instructionalMessage = instructionalMessage;
905     }
906 
907     /**
908      * Text that display a restriction on the value a field can hold
909      *
910      * <p>
911      * For example when the value must be a valid format (phone number, email), certain length, min/max value and
912      * so on this text can be used to indicate the constraint to the user. Generally displays with the control so
913      * it is visible when the user tabs to the field
914      * </p>
915      *
916      * @return text to display for the constraint message
917      */
918     @BeanTagAttribute(name = "constraintText")
919     public String getConstraintText() {
920         return this.constraintText;
921     }
922 
923     /**
924      * Setter for the constraint message text
925      *
926      * @param constraintText the constraint text to set
927      */
928     public void setConstraintText(String constraintText) {
929         this.constraintText = constraintText;
930     }
931 
932     /**
933      * Message field that displays constraint text
934      *
935      * <p>
936      * This message field can be configured to for adjusting how the constrain text will display. Generally
937      * the styleClasses property will be of most interest
938      * </p>
939      *
940      * @return constraint message field
941      */
942     @BeanTagAttribute(name = "constraintMessage", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
943     public Message getConstraintMessage() {
944         return this.constraintMessage;
945     }
946 
947     /**
948      * Setter for the constraint text message field
949      *
950      * <p>
951      * Note this is the setter for the field that will render the constraint text. The actual text can be
952      * set on the field but can also be set using {@link #setConstraintText(String)}
953      * </p>
954      *
955      * @param constraintMessage the constrain message field to set
956      */
957     public void setConstraintMessage(Message constraintMessage) {
958         this.constraintMessage = constraintMessage;
959     }
960 
961     /**
962      * The {@code ValidCharactersConstraint} that applies to this {@code InputField}
963      *
964      * @return the valid characters constraint for this input field
965      */
966     @Override
967     @BeanTagAttribute(name = "validCharactersConstraint", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
968     public ValidCharactersConstraint getValidCharactersConstraint() {
969         return this.validCharactersConstraint;
970     }
971 
972     /**
973      * Setter for {@code validCharacterConstraint}
974      *
975      * @param validCharactersConstraint the {@code ValidCharactersConstraint} to set
976      */
977     public void setValidCharactersConstraint(ValidCharactersConstraint validCharactersConstraint) {
978         this.validCharactersConstraint = validCharactersConstraint;
979     }
980 
981     /**
982      * The {@code CaseConstraint} that applies to this {@code InputField}
983      *
984      * @return the case constraint for this input field
985      */
986     @Override
987     @BeanTagAttribute(name = "caseConstraint", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
988     public CaseConstraint getCaseConstraint() {
989         return this.caseConstraint;
990     }
991 
992     /**
993      * Setter for {@code caseConstraint}
994      *
995      * @param caseConstraint the {@code CaseConstraint} to set
996      */
997     public void setCaseConstraint(CaseConstraint caseConstraint) {
998         this.caseConstraint = caseConstraint;
999     }
1000 
1001     /**
1002      * List of {@code PrerequisiteConstraint} that apply to this {@code InputField}
1003      *
1004      * @return the dependency constraints for this input field
1005      */
1006     @BeanTagAttribute(name = "dependencyConstraints", type = BeanTagAttribute.AttributeType.LISTBEAN)
1007     public List<PrerequisiteConstraint> getDependencyConstraints() {
1008         return this.dependencyConstraints;
1009     }
1010 
1011     /**
1012      * Setter for {@code dependencyConstraints}
1013      *
1014      * @param dependencyConstraints list of {@code PrerequisiteConstraint} to set
1015      */
1016     public void setDependencyConstraints(List<PrerequisiteConstraint> dependencyConstraints) {
1017         this.dependencyConstraints = dependencyConstraints;
1018     }
1019 
1020     @Override
1021     public List<PrerequisiteConstraint> getPrerequisiteConstraints() {
1022         return dependencyConstraints;
1023     }
1024 
1025     /**
1026      * List of {@code MustOccurConstraint} that apply to this {@code InputField}
1027      *
1028      * @return the must occur constraints for this input field
1029      */
1030     @Override
1031     @BeanTagAttribute(name = "mustOccurConstraints", type = BeanTagAttribute.AttributeType.LISTBEAN)
1032     public List<MustOccurConstraint> getMustOccurConstraints() {
1033         return this.mustOccurConstraints;
1034     }
1035 
1036     /**
1037      * Setter for {@code mustOccurConstraints}
1038      *
1039      * @param mustOccurConstraints list of {@code MustOccurConstraint} to set
1040      */
1041     public void setMustOccurConstraints(List<MustOccurConstraint> mustOccurConstraints) {
1042         this.mustOccurConstraints = mustOccurConstraints;
1043     }
1044 
1045     /**
1046      * Simple constraints for the input field
1047      *
1048      * <p>
1049      * A simple constraint which store the values for constraints such as required,
1050      * min/max length, and min/max value.
1051      * </p>
1052      *
1053      * @return the simple constraint of the input field
1054      */
1055     @Override
1056     @BeanTagAttribute(name = "simpleConstraint", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1057     public SimpleConstraint getSimpleConstraint() {
1058         return this.simpleConstraint;
1059     }
1060 
1061     /**
1062      * Setter for simple constraint
1063      *
1064      * <p>
1065      * When a simple constraint is set on this object ALL simple validation
1066      * constraints set directly will be overridden - recommended to use this or
1067      * the other gets/sets for defining simple constraints, not both.
1068      * </p>
1069      *
1070      * @param simpleConstraint the simple constraint to set
1071      */
1072     public void setSimpleConstraint(SimpleConstraint simpleConstraint) {
1073         this.simpleConstraint = simpleConstraint;
1074     }
1075 
1076     /**
1077      * This does not have to be set, represents the DataType constraint of this field.
1078      * This is only checked during server side validation.
1079      *
1080      * @param dataType the dataType to set
1081      */
1082     public void setDataType(DataType dataType) {
1083         this.simpleConstraint.setDataType(dataType);
1084     }
1085 
1086     public void setDataType(String dataType) {
1087         this.simpleConstraint.setDataType(DataType.valueOf(dataType));
1088     }
1089 
1090     /**
1091      * Gets the DataType of this InputField, note that DataType set to be date
1092      * when this field is using a date picker with a TextControl and has not otherwise been
1093      * explicitly set.
1094      *
1095      * @return DataType
1096      */
1097     @BeanTagAttribute(name = "dataType", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1098     public DataType getDataType() {
1099         return this.simpleConstraint.getDataType();
1100     }
1101 
1102     /**
1103      * Maximum number of characters the input field value is allowed to have
1104      *
1105      * <p>
1106      * The maximum length determines the maximum allowable length of the value
1107      * for data entry editing purposes.  The maximum length is inclusive and can
1108      * be smaller or longer than the actual control size.  The constraint
1109      * is enforced on all data types (e.g. a numeric data type needs to meet the
1110      * maximum length constraint in which digits and symbols are counted).
1111      * </p>
1112      *
1113      * @return the maximum length of the input field
1114      */
1115     @BeanTagAttribute(name = "maxLength")
1116     public Integer getMaxLength() {
1117         return simpleConstraint.getMaxLength();
1118     }
1119 
1120     /**
1121      * Setter for input field max length
1122      *
1123      * @param maxLength the maximum length to set
1124      */
1125     public void setMaxLength(Integer maxLength) {
1126         simpleConstraint.setMaxLength(maxLength);
1127     }
1128 
1129     /**
1130      * Minimum number of characters the input field value needs to be
1131      *
1132      * <p>
1133      * The minimum length determines the minimum required length of the value for
1134      * data entry editing purposes.  The minimum length is inclusive. The constraint
1135      * is enforced on all data types (e.g. a numeric data type needs to meet the
1136      * minimum length requirement in which digits and symbols are counted).
1137      * </p>
1138      *
1139      * @return the minimum length of the input field
1140      */
1141     @BeanTagAttribute(name = "minLength")
1142     public Integer getMinLength() {
1143         return simpleConstraint.getMinLength();
1144     }
1145 
1146     /**
1147      * Setter for input field minimum length
1148      *
1149      * @param minLength the minLength to set
1150      */
1151     public void setMinLength(Integer minLength) {
1152         simpleConstraint.setMinLength(minLength);
1153     }
1154 
1155     /**
1156      * @see org.kuali.rice.krad.uif.component.ComponentBase#getRequired()
1157      */
1158     @Override
1159     @BeanTagAttribute(name = "required")
1160     public Boolean getRequired() {
1161         return this.simpleConstraint.getRequired();
1162     }
1163 
1164     /**
1165      * @see org.kuali.rice.krad.uif.component.ComponentBase#setRequired(java.lang.Boolean)
1166      */
1167     @Override
1168     public void setRequired(Boolean required) {
1169         this.simpleConstraint.setRequired(required);
1170     }
1171 
1172     /**
1173      * The exclusive minimum value for numeric or date field.
1174      *
1175      * <p>
1176      * The exclusiveMin element determines the minimum allowable value for data
1177      * entry editing purposes. This constrain is supported for numeric and
1178      * date fields and to be used in conjunction with the appropriate
1179      * {@link ValidCharactersConstraint}.
1180      *
1181      * For numeric constraint the value can be an integer or decimal such as -.001 or 99.
1182      * </p>
1183      *
1184      * @return the exclusive minimum numeric value of the input field
1185      */
1186     @BeanTagAttribute(name = "exclusiveMin")
1187     public String getExclusiveMin() {
1188         return simpleConstraint.getExclusiveMin();
1189     }
1190 
1191     /**
1192      * Setter for the field's exclusive minimum value
1193      *
1194      * @param exclusiveMin the minimum value to set
1195      */
1196     public void setExclusiveMin(String exclusiveMin) {
1197         simpleConstraint.setExclusiveMin(exclusiveMin);
1198     }
1199 
1200     /**
1201      * The inclusive maximum value for numeric or date field.
1202      *
1203      * <p>
1204      * The inclusiveMax element determines the maximum allowable value for data
1205      * entry editing purposes. This constrain is supported for numeric and
1206      * date fields and to be used in conjunction with the appropriate
1207      * {@link ValidCharactersConstraint}.
1208      *
1209      * For numeric constraint the value can be an integer or decimal such as -.001 or 99.
1210      * </p>
1211      *
1212      * @return the inclusive maximum numeric value of the input field
1213      */
1214     @BeanTagAttribute(name = "inclusiveMax")
1215     public String getInclusiveMax() {
1216         return simpleConstraint.getInclusiveMax();
1217     }
1218 
1219     /**
1220      * Setter for the field's inclusive maximum value
1221      *
1222      * @param inclusiveMax the maximum value to set
1223      */
1224     public void setInclusiveMax(String inclusiveMax) {
1225         simpleConstraint.setInclusiveMax(inclusiveMax);
1226     }
1227 
1228     /**
1229      * Attribute query instance configured for this field to dynamically pull information back for
1230      * updates other fields or providing messages
1231      *
1232      * <p>
1233      * If field attribute query is not null, associated event script will be generated to trigger the
1234      * query from the UI. This will invoke the {@code AttributeQueryService} to
1235      * execute the query and return an instance of {@code AttributeQueryResult} that is then
1236      * read by the script to update the UI. Typically used to update informational property values or
1237      * other field values
1238      * </p>
1239      *
1240      * @return AttributeQuery instance
1241      */
1242     @BeanTagAttribute(name = "attributeQuery", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1243     public AttributeQuery getAttributeQuery() {
1244         return attributeQuery;
1245     }
1246 
1247     /**
1248      * Setter for this field's attribute query
1249      *
1250      * @param attributeQuery
1251      */
1252     public void setAttributeQuery(AttributeQuery attributeQuery) {
1253         this.attributeQuery = attributeQuery;
1254     }
1255 
1256     /**
1257      * Perform uppercase flag for this field to force input to uppercase.
1258      *
1259      * <p>
1260      * It this flag is set to true the 'text-transform' style on the field will be set to 'uppercase'
1261      * which will automatically change any text input into the field to uppercase.
1262      * </p>
1263      *
1264      * @return performUppercase flag
1265      */
1266     @BeanTagAttribute(name = "uppercaseValue")
1267     public boolean isUppercaseValue() {
1268         return uppercaseValue;
1269     }
1270 
1271     /**
1272      * Setter for this field's performUppercase flag
1273      *
1274      * @param uppercaseValue boolean flag
1275      */
1276     public void setUppercaseValue(boolean uppercaseValue) {
1277         this.uppercaseValue = uppercaseValue;
1278     }
1279 
1280     /**
1281      * Indicates whether the browser autocomplete functionality should be disabled for the
1282      * input field (adds autocomplete="off")
1283      *
1284      * <p>
1285      * The browser's native autocomplete functionality can cause issues with security fields and also fields
1286      * with the UIF suggest widget enabled
1287      * </p>
1288      *
1289      * @return true if the native autocomplete should be turned off for the input field, false if not
1290      */
1291     public boolean isDisableNativeAutocomplete() {
1292         return disableNativeAutocomplete;
1293     }
1294 
1295     /**
1296      * Setter to disable browser autocomplete for the input field
1297      *
1298      * @param disableNativeAutocomplete
1299      */
1300     public void setDisableNativeAutocomplete(boolean disableNativeAutocomplete) {
1301         this.disableNativeAutocomplete = disableNativeAutocomplete;
1302     }
1303 
1304     @Override
1305     public boolean isRenderFieldset() {
1306         return super.isRenderFieldset() || (quickfinder != null
1307                 && quickfinder.isRender()
1308                 && quickfinder.getQuickfinderAction() != null
1309                 && quickfinder.getQuickfinderAction().isRender());
1310     }
1311 
1312     /**
1313      * @see org.kuali.rice.krad.uif.component.ComponentBase#copy()
1314      */
1315     @Override
1316     protected <T> void copyProperties(T component) {
1317         super.copyProperties(component);
1318 
1319         InputField inputFieldCopy = (InputField) component;
1320 
1321         inputFieldCopy.setCustomValidatorClass(this.customValidatorClass);
1322         inputFieldCopy.setValidCharactersConstraint(CloneUtils.deepClone(this.validCharactersConstraint));
1323         inputFieldCopy.setCaseConstraint(CloneUtils.deepClone(this.caseConstraint));
1324 
1325         if (dependencyConstraints != null) {
1326             List<PrerequisiteConstraint> dependencyConstraintsCopy = Lists.newArrayListWithExpectedSize(
1327                     dependencyConstraints.size());
1328 
1329             for (PrerequisiteConstraint dependencyConstraint : dependencyConstraints) {
1330                 dependencyConstraintsCopy.add(CloneUtils.deepClone(dependencyConstraint));
1331             }
1332 
1333             inputFieldCopy.setDependencyConstraints(dependencyConstraintsCopy);
1334         }
1335 
1336         if (mustOccurConstraints != null) {
1337             List<MustOccurConstraint> mustOccurConstraintsCopy = Lists.newArrayListWithExpectedSize(
1338                     mustOccurConstraints.size());
1339 
1340             for (MustOccurConstraint mustOccurConstraint : mustOccurConstraints) {
1341                 mustOccurConstraintsCopy.add(CloneUtils.deepClone(mustOccurConstraint));
1342             }
1343 
1344             inputFieldCopy.setMustOccurConstraints(mustOccurConstraintsCopy);
1345         }
1346 
1347         inputFieldCopy.setSimpleConstraint(CloneUtils.deepClone(this.simpleConstraint));
1348         inputFieldCopy.setDataType(this.dataType);
1349 
1350         // display props
1351         if (this.control != null) {
1352             inputFieldCopy.setControl((Control) this.control.copy());
1353         }
1354 
1355         inputFieldCopy.setOptionsFinder(this.optionsFinder);
1356         inputFieldCopy.setUppercaseValue(this.uppercaseValue);
1357         inputFieldCopy.setDisableNativeAutocomplete(this.disableNativeAutocomplete);
1358 
1359         if (this.validationMessages != null) {
1360             inputFieldCopy.setValidationMessages((ValidationMessages) this.validationMessages.copy());
1361         }
1362 
1363         // messages
1364         inputFieldCopy.setConstraintText(this.constraintText);
1365         inputFieldCopy.setInstructionalText(this.instructionalText);
1366 
1367         if (this.constraintMessage != null) {
1368             inputFieldCopy.setConstraintMessage((Message) this.constraintMessage.copy());
1369         }
1370 
1371         if (this.instructionalMessage != null) {
1372             inputFieldCopy.setInstructionalMessage((Message) this.instructionalMessage.copy());
1373         }
1374 
1375         if (this.attributeQuery != null) {
1376             inputFieldCopy.setAttributeQuery((AttributeQuery) this.attributeQuery.copy());
1377         }
1378 
1379         inputFieldCopy.setEnableAutoDirectInquiry(this.enableAutoDirectInquiry);
1380 
1381         // widgets
1382         if (this.quickfinder != null) {
1383             inputFieldCopy.setQuickfinder((QuickFinder) this.quickfinder.copy());
1384         }
1385 
1386         inputFieldCopy.setEnableAutoQuickfinder(this.enableAutoQuickfinder);
1387 
1388         if (this.suggest != null) {
1389             inputFieldCopy.setSuggest((Suggest) this.suggest.copy());
1390         }
1391 
1392         inputFieldCopy.setWidgetInputOnly(this.widgetInputOnly);
1393     }
1394 
1395     /**
1396      * @see org.kuali.rice.krad.uif.component.Component#completeValidation
1397      */
1398     @Override
1399     public void completeValidation(ValidationTrace tracer) {
1400         tracer.addBean(this);
1401 
1402         // Checks that the control is set
1403         if (getControl() == null) {
1404             if (Validator.checkExpressions(this, "control")) {
1405                 String currentValues[] = {"control =" + getConstraintText()};
1406                 tracer.createWarning("Control should be set", currentValues);
1407             }
1408         }
1409 
1410         super.completeValidation(tracer.getCopy());
1411     }
1412 
1413     public void setCustomValidatorClass(String customValidatorClass) {
1414         this.customValidatorClass = customValidatorClass;
1415     }
1416 }