View Javadoc

1   /**
2    * Copyright 2005-2012 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.uif.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.state.StateMapping;
25  import org.kuali.rice.krad.datadictionary.validation.capability.CaseConstrainable;
26  import org.kuali.rice.krad.datadictionary.validation.capability.MustOccurConstrainable;
27  import org.kuali.rice.krad.datadictionary.validation.capability.PrerequisiteConstrainable;
28  import org.kuali.rice.krad.datadictionary.validation.capability.SimpleConstrainable;
29  import org.kuali.rice.krad.datadictionary.validation.capability.ValidCharactersConstrainable;
30  import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
31  import org.kuali.rice.krad.datadictionary.validation.constraint.MustOccurConstraint;
32  import org.kuali.rice.krad.datadictionary.validation.constraint.PrerequisiteConstraint;
33  import org.kuali.rice.krad.datadictionary.validation.constraint.SimpleConstraint;
34  import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
35  import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
36  import org.kuali.rice.krad.datadictionary.validator.ErrorReport;
37  import org.kuali.rice.krad.datadictionary.validator.RDValidator;
38  import org.kuali.rice.krad.datadictionary.validator.TracerToken;
39  import org.kuali.rice.krad.uif.UifConstants;
40  import org.kuali.rice.krad.uif.component.Component;
41  import org.kuali.rice.krad.uif.control.Control;
42  import org.kuali.rice.krad.uif.control.MultiValueControlBase;
43  import org.kuali.rice.krad.uif.control.TextControl;
44  import org.kuali.rice.krad.uif.control.UifKeyValuesFinder;
45  import org.kuali.rice.krad.uif.element.Label;
46  import org.kuali.rice.krad.uif.element.Message;
47  import org.kuali.rice.krad.uif.element.ValidationMessages;
48  import org.kuali.rice.krad.uif.util.ClientValidationUtils;
49  import org.kuali.rice.krad.uif.util.ComponentFactory;
50  import org.kuali.rice.krad.uif.util.ComponentUtils;
51  import org.kuali.rice.krad.uif.util.ConstraintStateUtils;
52  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
53  import org.kuali.rice.krad.uif.view.View;
54  import org.kuali.rice.krad.uif.view.ViewModel;
55  import org.kuali.rice.krad.uif.widget.QuickFinder;
56  import org.kuali.rice.krad.uif.widget.Suggest;
57  import org.kuali.rice.krad.util.ObjectUtils;
58  
59  import java.util.ArrayList;
60  import java.util.List;
61  
62  /**
63   * Field that encapsulates data input/output captured by an attribute within the
64   * application
65   *
66   * <p>
67   * The {@code InputField} provides the majority of the data input/output
68   * for the screen. Through these fields the model can be displayed and updated.
69   * For data input, the field contains a {@link Control} instance will
70   * render an HTML control element(s). The input field also contains a
71   * {@link Label}, summary, and widgets such as a quickfinder (for
72   * looking up values) and inquiry (for getting more information on the value).
73   * {@code InputField} instances can have associated messages (errors)
74   * due to invalid input or business rule failures. Security can also be
75   * configured to restrict who may view the fields value.
76   * </p>
77   *
78   * @author Kuali Rice Team (rice.collab@kuali.org)
79   */
80  public class InputField extends DataField implements SimpleConstrainable, CaseConstrainable, PrerequisiteConstrainable, MustOccurConstrainable, ValidCharactersConstrainable {
81      private static final long serialVersionUID = -3703656713706343840L;
82  
83      // constraint variables
84      private String customValidatorClass;
85      private ValidCharactersConstraint validCharactersConstraint;
86      private CaseConstraint caseConstraint;
87      private List<PrerequisiteConstraint> dependencyConstraints;
88      private List<MustOccurConstraint> mustOccurConstraints;
89      private SimpleConstraint simpleConstraint;
90      private DataType dataType;
91  
92      // display props
93      private Control control;
94      private KeyValuesFinder optionsFinder;
95  
96      private boolean uppercaseValue;
97  
98      private ValidationMessages validationMessages;
99  
100     // messages
101     private String constraintText;
102     private String instructionalText;
103 
104     private Message constraintMessage;
105     private Message instructionalMessage;
106 
107     private AttributeQuery attributeQuery;
108 
109     // widgets
110     private QuickFinder quickfinder;
111     private Suggest suggest;
112 
113     public InputField() {
114         super();
115 
116         simpleConstraint = new SimpleConstraint();
117     }
118 
119     /**
120      * The following initialization is performed:
121      *
122      * <ul>
123      * <li>Initializes instructional and constraint message fields if necessary</li>
124      * </ul>
125      *
126      * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View,
127      *      java.lang.Object)
128      */
129     @Override
130     public void performInitialization(View view, Object model) {
131         super.performInitialization(view, model);
132 
133         if ((StringUtils.isNotBlank(constraintText) || (getPropertyExpression("constraintText") != null)) && (
134                 constraintMessage
135                         == null)) {
136             constraintMessage = ComponentFactory.getConstraintMessage();
137             view.assignComponentIds(constraintMessage);
138         }
139 
140         if ((StringUtils.isNotBlank(instructionalText) || (getPropertyExpression("instructionalText") != null)) && (
141                 instructionalMessage
142                         == null)) {
143             instructionalMessage = ComponentFactory.getInstructionalMessage();
144             view.assignComponentIds(instructionalMessage);
145         }
146     }
147 
148     /**
149      * @see Component#performApplyModel(org.kuali.rice.krad.uif.view.View, Object, org.kuali.rice.krad.uif.component.Component)
150      */
151     @Override
152     public void performApplyModel(View view, Object model, Component parent) {
153         super.performApplyModel(view, model, parent);
154 
155         // Done in apply model so we have the message text for additional rich message processing in Message
156         // Sets message
157         if (StringUtils.isNotBlank(instructionalText) && StringUtils.isBlank(instructionalMessage.getMessageText())) {
158             instructionalMessage.setMessageText(instructionalText);
159         }
160 
161         // Sets constraints
162         if (StringUtils.isNotBlank(constraintText) && StringUtils.isBlank(constraintMessage.getMessageText())) {
163             constraintMessage.setMessageText(constraintText);
164         }
165 
166         // invoke options finder if options not configured on the control
167         List<KeyValue> fieldOptions = new ArrayList<KeyValue>();
168 
169         // use options directly configured on the control first
170         if ((control != null) && control instanceof MultiValueControlBase) {
171             MultiValueControlBase multiValueControl = (MultiValueControlBase) control;
172             if ((multiValueControl.getOptions() != null) && !multiValueControl.getOptions().isEmpty()) {
173                 fieldOptions = multiValueControl.getOptions();
174             }
175         }
176 
177         // if options not configured on the control, invoke configured options finder
178         if (fieldOptions.isEmpty() && (optionsFinder != null)) {
179             if (optionsFinder instanceof UifKeyValuesFinder) {
180                 fieldOptions = ((UifKeyValuesFinder) optionsFinder).getKeyValues((ViewModel) model);
181 
182                 // check if blank option should be added
183                 if (((UifKeyValuesFinder) optionsFinder).isAddBlankOption()) {
184                     fieldOptions.add(0, new ConcreteKeyValue("", ""));
185                 }
186             } else {
187                 fieldOptions = optionsFinder.getKeyValues();
188             }
189 
190             if ((control != null) && control instanceof MultiValueControlBase) {
191                 ((MultiValueControlBase) control).setOptions(fieldOptions);
192             }
193         }
194 
195         // if read only do key/value translation if necessary (if alternative and additional properties not set)
196         if (isReadOnly()
197                 && !fieldOptions.isEmpty()
198                 && StringUtils.isBlank(getReadOnlyDisplayReplacement())
199                 && StringUtils.isBlank(getReadOnlyDisplaySuffix())
200                 && StringUtils.isBlank(getReadOnlyDisplayReplacementPropertyName())
201                 && StringUtils.isBlank(getReadOnlyDisplaySuffixPropertyName())) {
202 
203             Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, getBindingInfo().getBindingPath());
204 
205             // TODO: can we translate Collections? (possibly combining output with delimiter
206             if ((fieldValue != null) && (TypeUtils.isSimpleType(fieldValue.getClass()))) {
207                 for (KeyValue keyValue : fieldOptions) {
208                     if (StringUtils.equals(fieldValue.toString(), keyValue.getKey())) {
209                         setReadOnlyDisplayReplacement(keyValue.getValue());
210                         break;
211                     }
212                 }
213             }
214         }
215     }
216 
217     /**
218      * The following actions are performed:
219      *
220      * <ul>
221      * <li>Set the ids for the various attribute components</li>
222      * <li>Sets up the client side validation for constraints on this field. In
223      * addition, it sets up the messages applied to this field</li>
224      * </ul>
225      *
226      * @see org.kuali.rice.krad.uif.component.ComponentBase#performFinalize(org.kuali.rice.krad.uif.view.View,
227      *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
228      */
229     @Override
230     public void performFinalize(View view, Object model, Component parent) {
231         super.performFinalize(view, model, parent);
232 
233         setupIds();
234         this.addDataAttribute("role", "InputField");
235 
236         // if read only or the control is null no input can be given so no need to setup validation
237         if (isReadOnly() || getControl() == null) {
238             return;
239         }
240 
241         // adjust paths on PrerequisiteConstraint property names
242         adjustPrerequisiteConstraintBinding(dependencyConstraints);
243 
244         // adjust paths on MustOccurConstraints property names
245         adjustMustOccurConstraintBinding(mustOccurConstraints);
246 
247         // adjust paths on CaseConstraint property names
248         if (caseConstraint != null) {
249             String propertyName = getBindingInfo().getPropertyAdjustedBindingPath(caseConstraint.getPropertyName());
250             caseConstraint.setPropertyName(propertyName);
251         }
252 
253         setupFieldQuery();
254 
255         // special requiredness indicator handling, if this was previously not required reset its required
256         // message to be ** for indicating required in the next state
257         String path = view.getStateObjectBindingPath();
258         Object stateObject;
259 
260         if (StringUtils.isNotBlank(path)) {
261             stateObject = ObjectPropertyUtils.getPropertyValue(model, path);
262         } else {
263             stateObject = model;
264         }
265         StateMapping stateMapping = view.getStateMapping();
266 
267         if (stateMapping != null) {
268             String validationState = ConstraintStateUtils.getClientViewValidationState(model, view);
269             SimpleConstraint appliedSimpleConstraint = ConstraintStateUtils.getApplicableConstraint(
270                     this.getSimpleConstraint(), validationState, stateMapping);
271 
272             if (appliedSimpleConstraint != null
273                     && appliedSimpleConstraint.getRequired() != null
274                     && appliedSimpleConstraint.getRequired()) {
275                 SimpleConstraint prevConstraint = ConstraintStateUtils.getApplicableConstraint(
276                         this.getSimpleConstraint(), stateMapping.getCurrentState(stateObject), stateMapping);
277                 if (prevConstraint == null || prevConstraint.getRequired() == null || !prevConstraint.getRequired()) {
278                     this.getFieldLabel().getRequiredMessage().setMessageText("**");
279                 }
280             }
281         }
282 
283         ClientValidationUtils.processAndApplyConstraints(this, view, model);
284     }
285 
286     /**
287      * Adjust paths on the must occur constrain bindings
288      *
289      * @param mustOccurConstraints
290      */
291     protected void adjustMustOccurConstraintBinding(List<MustOccurConstraint> mustOccurConstraints) {
292         if (mustOccurConstraints != null) {
293             for (MustOccurConstraint mustOccurConstraint : mustOccurConstraints) {
294                 adjustPrerequisiteConstraintBinding(mustOccurConstraint.getPrerequisiteConstraints());
295                 adjustMustOccurConstraintBinding(mustOccurConstraint.getMustOccurConstraints());
296             }
297         }
298     }
299 
300     /**
301      * Adjust paths on the prerequisite constraint bindings
302      *
303      * @param prerequisiteConstraints
304      */
305     protected void adjustPrerequisiteConstraintBinding(List<PrerequisiteConstraint> prerequisiteConstraints) {
306         if (prerequisiteConstraints != null) {
307             for (PrerequisiteConstraint prerequisiteConstraint : prerequisiteConstraints) {
308                 String propertyName = getBindingInfo().getPropertyAdjustedBindingPath(
309                         prerequisiteConstraint.getPropertyName());
310                 prerequisiteConstraint.setPropertyName(propertyName);
311             }
312         }
313     }
314 
315     /**
316      * Performs setup of the field attribute query and informational display properties. Paths
317      * are adjusted to match the binding for the this field, and the necessary onblur script for
318      * triggering the query client side is constructed
319      */
320     protected void setupFieldQuery() {
321         if (getAttributeQuery() != null) {
322             // adjust paths on query mappings
323             getAttributeQuery().updateQueryFieldMapping(getBindingInfo());
324             getAttributeQuery().updateReturnFieldMapping(getBindingInfo());
325             getAttributeQuery().updateQueryMethodArgumentFieldList(getBindingInfo());
326 
327             // build onblur script for field query
328             String script = "executeFieldQuery('" + getControl().getId() + "',";
329             script += "'" + getId() + "'," + getAttributeQuery().getQueryFieldMappingJsString() + ",";
330             script += getAttributeQuery().getQueryMethodArgumentFieldsJsString() + ",";
331             script += getAttributeQuery().getReturnFieldMappingJsString() + ");";
332 
333             if (StringUtils.isNotBlank(getControl().getOnBlurScript())) {
334                 script = getControl().getOnBlurScript() + script;
335             }
336             getControl().setOnBlurScript(script);
337         }
338     }
339 
340     /**
341      * Sets the ids on all components the input field uses so they will all
342      * contain this input field's id in their ids. This is useful for jQuery
343      * manipulation.
344      */
345     protected void setupIds() {
346         // update ids so they all match the attribute
347 
348         setNestedComponentIdAndSuffix(getControl(), UifConstants.IdSuffixes.CONTROL);
349         setNestedComponentIdAndSuffix(getValidationMessages(), UifConstants.IdSuffixes.ERRORS);
350         setNestedComponentIdAndSuffix(getFieldLabel(), UifConstants.IdSuffixes.LABEL);
351         setNestedComponentIdAndSuffix(getInstructionalMessage(), UifConstants.IdSuffixes.INSTRUCTIONAL);
352         setNestedComponentIdAndSuffix(getConstraintMessage(), UifConstants.IdSuffixes.CONSTRAINT);
353         setNestedComponentIdAndSuffix(getQuickfinder(), UifConstants.IdSuffixes.QUICK_FINDER);
354         setNestedComponentIdAndSuffix(getSuggest(), UifConstants.IdSuffixes.SUGGEST);
355 
356         if (this.getFieldLabel() != null) {
357             this.getFieldLabel().setLabelForComponentId(this.getControl().getId());
358         }
359 
360         if (this.getControl() != null) {
361             this.getControl().addDataAttribute(UifConstants.DATA_ATTRIBUTE_CONTROL_FOR, this.getId());
362         }
363     }
364 
365     /**
366      * Defaults the properties of the {@code InputField} to the
367      * corresponding properties of its {@code AttributeDefinition}
368      * retrieved from the dictionary (if such an entry exists). If the field
369      * already contains a value for a property, the definitions value is not
370      * used.
371      *
372      * @param view - view instance the field belongs to
373      * @param attributeDefinition - AttributeDefinition instance the property values should be
374      * copied from
375      */
376     @Override
377     public void copyFromAttributeDefinition(View view, AttributeDefinition attributeDefinition) {
378         super.copyFromAttributeDefinition(view, attributeDefinition);
379 
380         // max length
381         if (getMaxLength() == null) {
382             setMaxLength(attributeDefinition.getMaxLength());
383         }
384 
385         // min length
386         if (getMinLength() == null) {
387             setMinLength(attributeDefinition.getMinLength());
388         }
389 
390         // valid characters
391         if (getValidCharactersConstraint() == null) {
392             setValidCharactersConstraint(attributeDefinition.getValidCharactersConstraint());
393         }
394 
395         if (getCaseConstraint() == null) {
396             setCaseConstraint(attributeDefinition.getCaseConstraint());
397         }
398 
399         if (getDependencyConstraints() == null) {
400             setDependencyConstraints(attributeDefinition.getPrerequisiteConstraints());
401         }
402 
403         if (getMustOccurConstraints() == null) {
404             setMustOccurConstraints(attributeDefinition.getMustOccurConstraints());
405         }
406 
407         // required
408         if (getRequired() == null) {
409             setRequired(attributeDefinition.isRequired());
410 
411             //if still null, default to false
412             if (getRequired() == null) {
413                 setRequired(Boolean.FALSE);
414             }
415         }
416 
417         if (getDataType() == null) {
418             setDataType(attributeDefinition.getDataType());
419             //Assume date if dataType is still null and using a DatePicker
420             if (getDataType() == null
421                     && control instanceof TextControl
422                     && ((TextControl) control).getDatePicker() != null) {
423                 setDataType(DataType.DATE);
424             }
425         }
426 
427         // control
428         if ((getControl() == null) && (attributeDefinition.getControlField() != null)) {
429             Control control = attributeDefinition.getControlField();
430             view.assignComponentIds(control);
431 
432             setControl(ComponentUtils.copy(control));
433         }
434 
435         // constraint
436         if (StringUtils.isEmpty(getConstraintText())) {
437             setConstraintText(attributeDefinition.getConstraintText());
438 
439             if (constraintMessage == null) {
440                 constraintMessage = ComponentFactory.getConstraintMessage();
441                 view.assignComponentIds(constraintMessage);
442             }
443             getConstraintMessage().setMessageText(attributeDefinition.getConstraintText());
444         }
445 
446         // options
447         if (getOptionsFinder() == null) {
448             setOptionsFinder(attributeDefinition.getOptionsFinder());
449         }
450 
451         // copy over simple constraint information because we cannot directly use simpleConstraint from
452         // attributeDefinition because the settings in InputField take precedence
453         if (this.getSimpleConstraint().getConstraintStateOverrides() == null) {
454             this.getSimpleConstraint().setConstraintStateOverrides(
455                     attributeDefinition.getSimpleConstraint().getConstraintStateOverrides());
456         }
457 
458         if (this.getSimpleConstraint().getStates().isEmpty()) {
459             this.getSimpleConstraint().setStates(attributeDefinition.getSimpleConstraint().getStates());
460         }
461 
462         if (this.getSimpleConstraint().getMessageKey() == null) {
463             this.getSimpleConstraint().setMessageKey(attributeDefinition.getSimpleConstraint().getMessageKey());
464         }
465 
466         if (this.getSimpleConstraint().getApplyClientSide() == null) {
467             this.getSimpleConstraint().setApplyClientSide(
468                     attributeDefinition.getSimpleConstraint().getApplyClientSide());
469         }
470 
471         if (this.getSimpleConstraint().getValidationMessageParams() == null) {
472             this.getSimpleConstraint().setValidationMessageParams(
473                     attributeDefinition.getSimpleConstraint().getValidationMessageParams());
474         }
475     }
476 
477     /**
478      * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
479      */
480     @Override
481     public List<Component> getComponentsForLifecycle() {
482         List<Component> components = super.getComponentsForLifecycle();
483 
484         components.add(instructionalMessage);
485         components.add(constraintMessage);
486         components.add(control);
487         components.add(validationMessages);
488         components.add(quickfinder);
489         components.add(suggest);
490 
491         return components;
492     }
493 
494     /**
495      * @see DataField#isInputAllowed()
496      */
497     @Override
498     public boolean isInputAllowed() {
499         return true;
500     }
501 
502     /**
503      * {@code Control} instance that should be used to input data for the
504      * field
505      *
506      * <p>
507      * When the field is editable, the control will be rendered so the user can
508      * input a value(s). Controls typically are part of a Form and render
509      * standard HTML control elements such as text input, select, and checkbox
510      * </p>
511      *
512      * @return Control instance
513      */
514     public Control getControl() {
515         return this.control;
516     }
517 
518     /**
519      * Setter for the field's control
520      *
521      * @param control
522      */
523     public void setControl(Control control) {
524         this.control = control;
525     }
526 
527     /**
528      * Field that contains the messages (errors) for the input field. The
529      * {@code ValidationMessages} holds configuration on associated messages along
530      * with information on rendering the messages in the user interface
531      *
532      * @return ValidationMessages instance
533      */
534     public ValidationMessages getValidationMessages() {
535         return this.validationMessages;
536     }
537 
538     /**
539      * Setter for the input field's errors field
540      *
541      * @param validationMessages
542      */
543     public void setValidationMessages(ValidationMessages validationMessages) {
544         this.validationMessages = validationMessages;
545     }
546 
547     /**
548      * Instance of {@code KeyValuesFinder} that should be invoked to
549      * provide a List of values the field can have. Generally used to provide
550      * the options for a multi-value control or to validate the submitted field
551      * value
552      *
553      * @return KeyValuesFinder instance
554      */
555     public KeyValuesFinder getOptionsFinder() {
556         return this.optionsFinder;
557     }
558 
559     /**
560      * Setter for the field's KeyValuesFinder instance
561      *
562      * @param optionsFinder
563      */
564     public void setOptionsFinder(KeyValuesFinder optionsFinder) {
565         this.optionsFinder = optionsFinder;
566     }
567 
568     /**
569      * Setter that takes in the class name for the options finder and creates a
570      * new instance to use as the finder for the input field
571      *
572      * @param optionsFinderClass - the options finder class to set
573      */
574     public void setOptionsFinderClass(Class<? extends KeyValuesFinder> optionsFinderClass) {
575         this.optionsFinder = ObjectUtils.newInstance(optionsFinderClass);
576     }
577 
578     /**
579      * Lookup finder widget for the field
580      *
581      * <p>
582      * The quickfinder widget places a small icon next to the field that allows
583      * the user to bring up a search screen for finding valid field values. The
584      * {@code Widget} instance can be configured to point to a certain
585      * {@code LookupView}, or the framework will attempt to associate the
586      * field with a lookup based on its metadata (in particular its
587      * relationships in the model)
588      * </p>
589      *
590      * @return QuickFinder lookup widget
591      */
592     public QuickFinder getQuickfinder() {
593         return this.quickfinder;
594     }
595 
596     /**
597      * Setter for the lookup widget
598      *
599      * @param quickfinder - the field lookup widget to set
600      */
601     public void setQuickfinder(QuickFinder quickfinder) {
602         this.quickfinder = quickfinder;
603     }
604 
605     /**
606      * Suggest box widget for the input field
607      *
608      * <p>
609      * If enabled (by render flag), as the user inputs data into the
610      * fields control a dynamic query is performed to provide the user
611      * suggestions on values which they can then select
612      * </p>
613      *
614      * <p>
615      * Note the Suggest widget is only valid when using a standard TextControl
616      * </p>
617      *
618      * @return Suggest instance
619      */
620     public Suggest getSuggest() {
621         return suggest;
622     }
623 
624     /**
625      * Setter for the fields suggest widget
626      *
627      * @param suggest - the field suggest widget to  set
628      */
629     public void setSuggest(Suggest suggest) {
630         this.suggest = suggest;
631     }
632 
633     /**
634      * Instructional text that display an explanation of the field usage
635      *
636      * <p>
637      * Text explaining how to use the field, including things like what values should be selected
638      * in certain cases and so on (instructions)
639      * </p>
640      *
641      * @return String instructional message
642      */
643     public String getInstructionalText() {
644         return this.instructionalText;
645     }
646 
647     /**
648      * Setter for the instructional message
649      *
650      * @param instructionalText - the instructional text to set
651      */
652     public void setInstructionalText(String instructionalText) {
653         this.instructionalText = instructionalText;
654     }
655 
656     /**
657      * Message field that displays instructional text
658      *
659      * <p>
660      * This message field can be configured to for adjusting how the instructional text will display. Generally
661      * the styleClasses property will be of most interest
662      * </p>
663      *
664      * @return Message instructional message field
665      */
666     public Message getInstructionalMessage() {
667         return this.instructionalMessage;
668     }
669 
670     /**
671      * Setter for the instructional text message field
672      *
673      * <p>
674      * Note this is the setter for the field that will render the instructional text. The actual text can be
675      * set on the field but can also be set using {@link #setInstructionalText(String)}
676      * </p>
677      *
678      * @param instructionalMessage - the instructional message to set
679      */
680     public void setInstructionalMessage(Message instructionalMessage) {
681         this.instructionalMessage = instructionalMessage;
682     }
683 
684     /**
685      * Text that display a restriction on the value a field can hold
686      *
687      * <p>
688      * For example when the value must be a valid format (phone number, email), certain length, min/max value and
689      * so on this text can be used to indicate the constraint to the user. Generally displays with the control so
690      * it is visible when the user tabs to the field
691      * </p>
692      *
693      * @return String text to display for the constraint message
694      */
695     public String getConstraintText() {
696         return this.constraintText;
697     }
698 
699     /**
700      * Setter for the constraint message text
701      *
702      * @param constraintText - the constraint text to set
703      */
704     public void setConstraintText(String constraintText) {
705         this.constraintText = constraintText;
706     }
707 
708     /**
709      * Message field that displays constraint text
710      *
711      * <p>
712      * This message field can be configured to for adjusting how the constrain text will display. Generally
713      * the styleClasses property will be of most interest
714      * </p>
715      *
716      * @return Message constraint message field
717      */
718     public Message getConstraintMessage() {
719         return this.constraintMessage;
720     }
721 
722     /**
723      * Setter for the constraint text message field
724      *
725      * <p>
726      * Note this is the setter for the field that will render the constraint text. The actual text can be
727      * set on the field but can also be set using {@link #setConstraintText(String)}
728      * </p>
729      *
730      * @param constraintMessage - the constrain message field to set
731      */
732     public void setConstraintMessage(Message constraintMessage) {
733         this.constraintMessage = constraintMessage;
734     }
735 
736     /**
737      * The {@code ValidCharactersConstraint} that applies to this {@code InputField}
738      *
739      * @return the valid characters constraint for this input field
740      */
741     @Override
742     public ValidCharactersConstraint getValidCharactersConstraint() {
743         return this.validCharactersConstraint;
744     }
745 
746     /**
747      * Setter for {@code validCharacterConstraint}
748      *
749      * @param validCharactersConstraint - the {@code ValidCharactersConstraint} to set
750      */
751     public void setValidCharactersConstraint(ValidCharactersConstraint validCharactersConstraint) {
752         this.validCharactersConstraint = validCharactersConstraint;
753     }
754 
755     /**
756      * The {@code CaseConstraint} that applies to this {@code InputField}
757      *
758      * @return the case constraint for this input field
759      */
760     @Override
761     public CaseConstraint getCaseConstraint() {
762         return this.caseConstraint;
763     }
764 
765     /**
766      * Setter for {@code caseConstraint}
767      *
768      * @param caseConstraint - the {@code CaseConstraint} to set
769      */
770     public void setCaseConstraint(CaseConstraint caseConstraint) {
771         this.caseConstraint = caseConstraint;
772     }
773 
774     /**
775      * List of {@code PrerequisiteConstraint} that apply to this {@code InputField}
776      *
777      * @return the dependency constraints for this input field
778      */
779     public List<PrerequisiteConstraint> getDependencyConstraints() {
780         return this.dependencyConstraints;
781     }
782 
783     /**
784      * Setter for {@code dependencyConstraints}
785      *
786      * @param dependencyConstraints - list of {@code PrerequisiteConstraint} to set
787      */
788     public void setDependencyConstraints(List<PrerequisiteConstraint> dependencyConstraints) {
789         this.dependencyConstraints = dependencyConstraints;
790     }
791 
792     /**
793      * List of {@code MustOccurConstraint} that apply to this {@code InputField}
794      *
795      * @return the must occur constraints for this input field
796      */
797     @Override
798     public List<MustOccurConstraint> getMustOccurConstraints() {
799         return this.mustOccurConstraints;
800     }
801 
802     /**
803      * Setter for {@code mustOccurConstraints}
804      *
805      * @param mustOccurConstraints - list of {@code MustOccurConstraint} to set
806      */
807     public void setMustOccurConstraints(List<MustOccurConstraint> mustOccurConstraints) {
808         this.mustOccurConstraints = mustOccurConstraints;
809     }
810 
811     /**
812      * Simple constraints for the input field
813      *
814      * <p>
815      * A simple constraint which store the values for constraints such as required,
816      * min/max length, and min/max value.
817      * </p>
818      *
819      * @return the simple constraint of the input field
820      */
821     @Override
822     public SimpleConstraint getSimpleConstraint() {
823         return this.simpleConstraint;
824     }
825 
826     /**
827      * Setter for simple constraint
828      *
829      * <p>
830      * When a simple constraint is set on this object ALL simple validation
831      * constraints set directly will be overridden - recommended to use this or
832      * the other gets/sets for defining simple constraints, not both.
833      * </p>
834      *
835      * @param simpleConstraint - the simple constraint to set
836      */
837     public void setSimpleConstraint(SimpleConstraint simpleConstraint) {
838         this.simpleConstraint = simpleConstraint;
839     }
840 
841     /**
842      * Maximum number of characters the input field value is allowed to have
843      *
844      * <p>
845      * The maximum length determines the maximum allowable length of the value
846      * for data entry editing purposes.  The maximum length is inclusive and can
847      * be smaller or longer than the actual control size.  The constraint
848      * is enforced on all data types (e.g. a numeric data type needs to meet the
849      * maximum length constraint in which digits and symbols are counted).
850      * </p>
851      *
852      * @return the maximum length of the input field
853      */
854     public Integer getMaxLength() {
855         return simpleConstraint.getMaxLength();
856     }
857 
858     /**
859      * Setter for input field max length
860      *
861      * @param maxLength - the maximum length to set
862      */
863     public void setMaxLength(Integer maxLength) {
864         simpleConstraint.setMaxLength(maxLength);
865     }
866 
867     /**
868      * Minimum number of characters the input field value needs to be
869      *
870      * <p>
871      * The minimum length determines the minimum required length of the value for
872      * data entry editing purposes.  The minimum length is inclusive. The constraint
873      * is enforced on all data types (e.g. a numeric data type needs to meet the
874      * minimum length requirement in which digits and symbols are counted).
875      * </p>
876      *
877      * @return the minimum length of the input field
878      */
879     public Integer getMinLength() {
880         return simpleConstraint.getMinLength();
881     }
882 
883     /**
884      * Setter for input field minimum length
885      *
886      * @param minLength - the minLength to set
887      */
888     public void setMinLength(Integer minLength) {
889         simpleConstraint.setMinLength(minLength);
890     }
891 
892     /**
893      * @see org.kuali.rice.krad.uif.component.ComponentBase#getRequired()
894      */
895     @Override
896     public Boolean getRequired() {
897         return this.simpleConstraint.getRequired();
898     }
899 
900     /**
901      * @see org.kuali.rice.krad.uif.component.ComponentBase#setRequired(java.lang.Boolean)
902      */
903     @Override
904     public void setRequired(Boolean required) {
905         this.simpleConstraint.setRequired(required);
906     }
907 
908     /**
909      * The exclusive minimum value for numeric or date field.
910      *
911      * <p>
912      * The exclusiveMin element determines the minimum allowable value for data
913      * entry editing purposes. This constrain is supported for numeric and
914      * date fields and to be used in conjunction with the appropriate
915      * {@link ValidCharactersConstraint}.
916      *
917      * For numeric constraint the value can be an integer or decimal such as -.001 or 99.
918      * </p>
919      *
920      * @return the exclusive minimum numeric value of the input field
921      */
922     public String getExclusiveMin() {
923         return simpleConstraint.getExclusiveMin();
924     }
925 
926     /**
927      * Setter for the field's exclusive minimum value
928      *
929      * @param exclusiveMin - the minimum value to set
930      */
931     public void setExclusiveMin(String exclusiveMin) {
932         simpleConstraint.setExclusiveMin(exclusiveMin);
933     }
934 
935     /**
936      * The inclusive maximum value for numeric or date field.
937      *
938      * <p>
939      * The inclusiveMax element determines the maximum allowable value for data
940      * entry editing purposes. This constrain is supported for numeric and
941      * date fields and to be used in conjunction with the appropriate
942      * {@link ValidCharactersConstraint}.
943      *
944      * For numeric constraint the value can be an integer or decimal such as -.001 or 99.
945      * </p>
946      *
947      * @return the inclusive maximum numeric value of the input field
948      */
949     public String getInclusiveMax() {
950         return simpleConstraint.getInclusiveMax();
951     }
952 
953     /**
954      * Setter for the field's inclusive maximum value
955      *
956      * @param inclusiveMax - the maximum value to set
957      */
958     public void setInclusiveMax(String inclusiveMax) {
959         simpleConstraint.setInclusiveMax(inclusiveMax);
960     }
961 
962     /**
963      * Attribute query instance configured for this field to dynamically pull information back for
964      * updates other fields or providing messages
965      *
966      * <p>
967      * If field attribute query is not null, associated event script will be generated to trigger the
968      * query from the UI. This will invoke the {@code AttributeQueryService} to
969      * execute the query and return an instance of {@code AttributeQueryResult} that is then
970      * read by the script to update the UI. Typically used to update informational property values or
971      * other field values
972      * </p>
973      *
974      * @return AttributeQuery instance
975      */
976     public AttributeQuery getAttributeQuery() {
977         return attributeQuery;
978     }
979 
980     /**
981      * Setter for this field's attribute query
982      *
983      * @param attributeQuery
984      */
985     public void setAttributeQuery(AttributeQuery attributeQuery) {
986         this.attributeQuery = attributeQuery;
987     }
988 
989     /**
990      * Perform uppercase flag for this field to force input to uppercase.
991      *
992      * <p>
993      * It this flag is set to true the 'text-transform' style on the field will be set to 'uppercase'
994      * which will automatically change any text input into the field to uppercase.
995      * </p>
996      *
997      * @return performUppercase flag
998      */
999     public boolean isUppercaseValue() {
1000         return uppercaseValue;
1001     }
1002 
1003     /**
1004      * Setter for this field's performUppercase flag
1005      *
1006      * @param uppercaseValue - boolean flag
1007      */
1008     public void setUppercaseValue(boolean uppercaseValue) {
1009         this.uppercaseValue = uppercaseValue;
1010     }
1011 
1012     /**
1013      * Returns the full binding path (the path used in the name attribute of the input).
1014      * This differs from propertyName in that it uses BindingInfo to determine the path.
1015      *
1016      * @return full binding path name
1017      */
1018     @Override
1019     public String getName() {
1020         return this.getBindingInfo().getBindingPath();
1021     }
1022 
1023     @Override
1024     public List<PrerequisiteConstraint> getPrerequisiteConstraints() {
1025         return dependencyConstraints;
1026     }
1027 
1028     /**
1029      * This does not have to be set, represents the DataType constraint of this field.
1030      * This is only checked during server side validation.
1031      *
1032      * @param dataType the dataType to set
1033      */
1034     public void setDataType(DataType dataType) {
1035         this.simpleConstraint.setDataType(dataType);
1036     }
1037 
1038     public void setDataType(String dataType) {
1039         this.simpleConstraint.setDataType(DataType.valueOf(dataType));
1040     }
1041 
1042     /**
1043      * Gets the DataType of this InputField, note that DataType set to be date
1044      * when this field is using a date picker with a TextControl and hasnt otherwise been
1045      * explicitly set.
1046      *
1047      * @return
1048      */
1049     public DataType getDataType() {
1050         return this.simpleConstraint.getDataType();
1051     }
1052 
1053     @Override
1054     public boolean isRenderFieldset() {
1055         return super.isRenderFieldset() || (this.isInputAllowed()
1056                 && quickfinder != null
1057                 && quickfinder.isRender()
1058                 && quickfinder.getQuickfinderAction() != null
1059                 && quickfinder.getQuickfinderAction().isRender());
1060     }
1061 
1062     /**
1063      * @see org.kuali.rice.krad.uif.component.Component#completeValidation
1064      */
1065     @Override
1066     public ArrayList<ErrorReport> completeValidation(TracerToken tracer) {
1067         ArrayList<ErrorReport> reports = new ArrayList<ErrorReport>();
1068         tracer.addBean(this);
1069 
1070         // Checks that the control is set
1071         if (getControl() == null) {
1072             if (RDValidator.checkExpressions(this, "control")) {
1073                 ErrorReport error = ErrorReport.createWarning("Control should be set",tracer);
1074                 error.addCurrentValue("control =" + getConstraintText());
1075                 reports.add(error);
1076             }
1077         }
1078 
1079         reports.addAll(super.completeValidation(tracer.getCopy()));
1080 
1081         return reports;
1082     }
1083 }