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