View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.field;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  import java.util.Map;
21  
22  import org.apache.commons.collections.CollectionUtils;
23  import org.apache.commons.lang.StringUtils;
24  import org.kuali.rice.core.api.data.DataType;
25  import org.kuali.rice.core.api.util.ConcreteKeyValue;
26  import org.kuali.rice.core.api.util.KeyValue;
27  import org.kuali.rice.core.api.util.type.TypeUtils;
28  import org.kuali.rice.krad.bo.DataObjectRelationship;
29  import org.kuali.rice.krad.datadictionary.AttributeDefinition;
30  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
31  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
32  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
33  import org.kuali.rice.krad.datadictionary.state.StateMapping;
34  import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
35  import org.kuali.rice.krad.datadictionary.validation.constraint.MustOccurConstraint;
36  import org.kuali.rice.krad.datadictionary.validation.constraint.PrerequisiteConstraint;
37  import org.kuali.rice.krad.datadictionary.validation.constraint.SimpleConstraint;
38  import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
39  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
40  import org.kuali.rice.krad.datadictionary.validator.Validator;
41  import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
42  import org.kuali.rice.krad.service.DataDictionaryService;
43  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
44  import org.kuali.rice.krad.uif.CssConstants;
45  import org.kuali.rice.krad.uif.UifConstants;
46  import org.kuali.rice.krad.uif.UifPropertyPaths;
47  import org.kuali.rice.krad.uif.component.Component;
48  import org.kuali.rice.krad.uif.component.DelayedCopy;
49  import org.kuali.rice.krad.uif.control.Control;
50  import org.kuali.rice.krad.uif.control.MultiValueControlBase;
51  import org.kuali.rice.krad.uif.control.SelectControl;
52  import org.kuali.rice.krad.uif.control.TextAreaControl;
53  import org.kuali.rice.krad.uif.control.TextControl;
54  import org.kuali.rice.krad.uif.control.UifKeyValuesFinder;
55  import org.kuali.rice.krad.uif.element.Action;
56  import org.kuali.rice.krad.uif.element.FieldValidationMessages;
57  import org.kuali.rice.krad.uif.element.Label;
58  import org.kuali.rice.krad.uif.element.Link;
59  import org.kuali.rice.krad.uif.element.Message;
60  import org.kuali.rice.krad.uif.element.ValidationMessages;
61  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
62  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleRestriction;
63  import org.kuali.rice.krad.uif.lifecycle.ViewPostMetadata;
64  import org.kuali.rice.krad.uif.util.ClientValidationUtils;
65  import org.kuali.rice.krad.uif.util.ComponentFactory;
66  import org.kuali.rice.krad.uif.util.ComponentUtils;
67  import org.kuali.rice.krad.uif.util.ConstraintStateUtils;
68  import org.kuali.rice.krad.uif.util.ContextUtils;
69  import org.kuali.rice.krad.uif.util.LifecycleElement;
70  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
71  import org.kuali.rice.krad.uif.util.ViewModelUtils;
72  import org.kuali.rice.krad.uif.view.View;
73  import org.kuali.rice.krad.uif.view.ViewModel;
74  import org.kuali.rice.krad.uif.widget.QuickFinder;
75  import org.kuali.rice.krad.uif.widget.Suggest;
76  import org.kuali.rice.krad.util.KRADConstants;
77  import org.kuali.rice.krad.util.KRADUtils;
78  import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
79  import org.kuali.rice.krad.web.form.UifFormBase;
80  
81  /**
82   * Field that encapsulates data input/output captured by an attribute within the
83   * application
84   *
85   * <p>
86   * The {@code InputField} provides the majority of the data input/output
87   * for the screen. Through these fields the model can be displayed and updated.
88   * For data input, the field contains a {@link Control} instance will
89   * render an HTML control element(s). The input field also contains a
90   * {@link Label}, summary, and widgets such as a quickfinder (for
91   * looking up values) and inquiry (for getting more information on the value).
92   * {@code InputField} instances can have associated messages (errors)
93   * due to invalid input or business rule failures. Security can also be
94   * configured to restrict who may view the fields valnue.
95   * </p>
96   *
97   * @author Kuali Rice Team (rice.collab@kuali.org)
98   */
99  @BeanTags({@BeanTag(name = "input", parent = "Uif-InputField"),
100         @BeanTag(name = "inputLabelTop", parent = "Uif-InputField-LabelTop"),
101         @BeanTag(name = "inputLabelRight", parent = "Uif-InputField-LabelRight"),
102         @BeanTag(name = "checkboxInput", parent = "Uif-CheckboxInputField")})
103 public class InputFieldBase extends DataFieldBase implements InputField {
104     private static final long serialVersionUID = -3703656713706343840L;
105 
106     // constraint variables
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 boolean inlineEdit;
118     private boolean ajaxInlineEdit;
119 
120     private KeyValuesFinder optionsFinder;
121 
122     private boolean uppercaseValue;
123     private boolean disableNativeAutocomplete;
124 
125     @DelayedCopy
126     private FieldValidationMessages validationMessages;
127 
128     // messages
129     private String constraintText;
130     private String instructionalText;
131 
132     private Message constraintMessage;
133     private Message instructionalMessage;
134 
135     private String helperText;
136 
137     private AttributeQuery attributeQuery;
138 
139     // widgets
140     private boolean enableAutoDirectInquiry;
141 
142     private QuickFinder quickfinder;
143     private boolean enableAutoQuickfinder;
144 
145     private Suggest suggest;
146 
147     private boolean widgetInputOnly;
148 
149     private boolean renderInputAddonGroup;
150     private List<String> postInputCssClasses;
151     private List<Component> postInputAddons;
152 
153     public InputFieldBase() {
154         super();
155 
156         simpleConstraint = new SimpleConstraint();
157 
158         enableAutoDirectInquiry = true;
159         enableAutoQuickfinder = true;
160     }
161 
162     /**
163      * {@inheritDoc}
164      */
165     @Override
166     public void performInitialization(Object model) {
167         super.performInitialization(model);
168 
169         if ((StringUtils.isNotBlank(constraintText) || (getPropertyExpression("constraintText") != null)) && (
170                 constraintMessage
171                         == null)) {
172             constraintMessage = ComponentFactory.getConstraintMessage();
173         }
174 
175         if ((StringUtils.isNotBlank(instructionalText) || (getPropertyExpression("instructionalText") != null)) && (
176                 instructionalMessage
177                         == null)) {
178             instructionalMessage = ComponentFactory.getInstructionalMessage();
179         }
180     }
181 
182     /**
183      * {@inheritDoc}
184      */
185     @Override
186     public void afterEvaluateExpression() {
187         // populate readOnly from parent before calling super, to prevent DataField
188         // from forcing to true.
189         if (getReadOnly() == null) {
190             Component parent = ViewLifecycle.getPhase().getParent();
191             setReadOnly(parent == null ? null : parent.getReadOnly());
192         }
193 
194         super.afterEvaluateExpression();
195     }
196 
197     /**
198      * {@inheritDoc}
199      */
200     @Override
201     public void performApplyModel(Object model, LifecycleElement parent) {
202         super.performApplyModel(model, parent);
203 
204         // Done in apply model so we have the message text for additional rich message processing in Message
205         // Sets message
206         if (StringUtils.isNotBlank(instructionalText) && instructionalMessage != null && StringUtils.isBlank(
207                 instructionalMessage.getMessageText())) {
208             instructionalMessage.setMessageText(instructionalText);
209         }
210 
211         // Sets constraints
212         if (StringUtils.isNotBlank(constraintText) && constraintMessage != null && StringUtils.isBlank(
213                 constraintMessage.getMessageText())) {
214             constraintMessage.setMessageText(constraintText);
215         }
216 
217         // invoke options finder if options not configured on the control
218         List<KeyValue> fieldOptions = new ArrayList<KeyValue>();
219 
220         // use options directly configured on the control first
221         if ((control != null) && control instanceof MultiValueControlBase) {
222             MultiValueControlBase multiValueControl = (MultiValueControlBase) control;
223             if ((multiValueControl.getOptions() != null) && !multiValueControl.getOptions().isEmpty()) {
224                 fieldOptions = multiValueControl.getOptions();
225             }
226         }
227 
228         // set multiLineReadOnlyDisplay to true to preserve text formatting
229         if (control instanceof TextAreaControl) {
230             setMultiLineReadOnlyDisplay(true);
231         }
232 
233         // if options not configured on the control, invoke configured options finder
234         if (fieldOptions.isEmpty() && (optionsFinder != null)) {
235             if (optionsFinder instanceof UifKeyValuesFinder) {
236                 fieldOptions = ((UifKeyValuesFinder) optionsFinder).getKeyValues((ViewModel) model, this);
237 
238                 // check if blank option should be added
239                 if (((UifKeyValuesFinder) optionsFinder).isAddBlankOption()) {
240                     fieldOptions.add(0, new ConcreteKeyValue("", ""));
241                 }
242             } else {
243                 fieldOptions = optionsFinder.getKeyValues();
244             }
245 
246             if ((control != null) && control instanceof MultiValueControlBase) {
247                 ((MultiValueControlBase) control).setOptions(fieldOptions);
248             }
249         }
250 
251         if (enableAutoDirectInquiry && this.getInquiry() == null && hasAutoInquiryRelationship()) {
252             setInquiry(ComponentFactory.getInquiry());
253         }
254 
255         if (enableAutoQuickfinder && this.getQuickfinder() == null && hasAutoQuickfinderRelationship()) {
256             setQuickfinder(ComponentFactory.getQuickFinder());
257             ContextUtils.pushAllToContextDeep(quickfinder, this.getContext());
258         }
259 
260         // if read only do key/value translation if necessary (if alternative and additional properties not set)
261         if (Boolean.TRUE.equals(getReadOnly())
262                 && !fieldOptions.isEmpty()
263                 && StringUtils.isBlank(getReadOnlyDisplayReplacement())
264                 && StringUtils.isBlank(getReadOnlyDisplaySuffix())
265                 && StringUtils.isBlank(getReadOnlyDisplayReplacementPropertyName())
266                 && StringUtils.isBlank(getReadOnlyDisplaySuffixPropertyName())) {
267 
268             Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, getBindingInfo().getBindingPath());
269 
270             // TODO: can we translate Collections? (possibly combining output with delimiter
271             if ((fieldValue != null) && (TypeUtils.isSimpleType(fieldValue.getClass()))) {
272                 for (KeyValue keyValue : fieldOptions) {
273                     if (StringUtils.equals(fieldValue.toString(), keyValue.getKey())) {
274                         setReadOnlyDisplayReplacement(keyValue.getValue());
275                         break;
276                     }
277                 }
278             }
279         }
280 
281         if(control != null && quickfinder != null && quickfinder.getQuickfinderAction() != null) {
282             String disabledExpression = control.getPropertyExpression("disabled");
283             if(StringUtils.isNotBlank(disabledExpression)) {
284                 quickfinder.getQuickfinderAction().getPropertyExpressions().put("disabled", disabledExpression);
285             }  else {
286                 quickfinder.getQuickfinderAction().setDisabled(control.isDisabled());
287             }
288         }
289 
290     }
291 
292     /**
293      * {@inheritDoc}
294      */
295     @Override
296     public void performFinalize(Object model, LifecycleElement parent) {
297         super.performFinalize(model, parent);
298 
299         setupIds();
300 
301         this.addDataAttribute(UifConstants.DataAttributes.ROLE, UifConstants.RoleTypes.INPUT_FIELD);
302 
303         boolean ajaxInlineEditRefresh = ajaxInlineEdit && ((UifFormBase)model).getUpdateComponentId() != null &&
304                 ((UifFormBase)model).getUpdateComponentId().equals(this.getId());
305 
306         // if read only or the control is null no input can be given so no need to setup validation
307         if ((Boolean.TRUE.equals(getReadOnly()) && !inlineEdit && !ajaxInlineEditRefresh) || getControl() == null) {
308             return;
309         }
310 
311         if (StringUtils.isNotBlank(helperText) && (getControl() != null)) {
312             getControl().getCssClasses().add(CssConstants.Classes.HAS_HELPER);
313         }
314 
315         DataDictionaryService dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
316         if (this.getDictionaryObjectEntry() != null && this.getDictionaryAttributeName() != null) {
317             AttributeDefinition ad = dataDictionaryService.getAttributeDefinition(this.getDictionaryObjectEntry(),
318                     this.getDictionaryAttributeName());
319 
320             Map<String, String> propertyExpressions = this.getPropertyExpressions();
321 
322             if (propertyExpressions.containsKey(UifPropertyPaths.DICTIONARY_ATTR_NAME)) {
323                 // call copyFromAttributeDefinition since the dictionaryAttributeName expression had not yet
324                 // been evaluated when this field was initialized.
325                 copyFromAttributeDefinition(ad);
326             }
327 
328             if (ad.getForceUppercase() || uppercaseValue) {
329                 Object currentPropertyValue = ObjectPropertyUtils.getPropertyValue(model,
330                         getBindingInfo().getBindingPath());
331                 if (currentPropertyValue instanceof String) {
332                     String uppercasedValue = ((String) currentPropertyValue).toUpperCase();
333                     ObjectPropertyUtils.setPropertyValue(model, getBindingInfo().getBindingPath(), uppercasedValue);
334                 }
335             }
336         }
337 
338         // browser's native autocomplete causes issues with the suggest plugin
339         if ((suggest != null) && suggest.isSuggestConfigured()) {
340             setDisableNativeAutocomplete(true);
341         }
342 
343         // adjust paths on PrerequisiteConstraint property names
344         adjustPrerequisiteConstraintBinding(dependencyConstraints);
345 
346         // adjust paths on MustOccurConstraints property names
347         adjustMustOccurConstraintBinding(mustOccurConstraints);
348 
349         // adjust paths on CaseConstraint property names
350         if (caseConstraint != null) {
351             String propertyName = getBindingInfo().getPropertyAdjustedBindingPath(caseConstraint.getPropertyName());
352             caseConstraint.setPropertyName(propertyName);
353         }
354 
355         View view = ViewLifecycle.getView();
356 
357         setupFieldQuery(view);
358 
359         // special requiredness indicator handling, if this was previously not required reset its required
360         // message to be ** for indicating required in the next state
361         String path = view.getStateObjectBindingPath();
362         Object stateObject;
363 
364         if (StringUtils.isNotBlank(path)) {
365             stateObject = ObjectPropertyUtils.getPropertyValue(model, path);
366         } else {
367             stateObject = model;
368         }
369         StateMapping stateMapping = view.getStateMapping();
370         String nextStateReqIndicator = (String) KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean(
371                 UifConstants.REQUIRED_NEXT_STATE_INDICATOR_ID);
372 
373         if (stateMapping != null) {
374             String validationState = ConstraintStateUtils.getClientViewValidationState(model, view);
375             SimpleConstraint appliedSimpleConstraint = ConstraintStateUtils.getApplicableConstraint(
376                     this.getSimpleConstraint(), validationState, stateMapping);
377 
378             if (appliedSimpleConstraint != null
379                     && appliedSimpleConstraint.getRequired() != null
380                     && appliedSimpleConstraint.getRequired()) {
381                 SimpleConstraint prevConstraint = ConstraintStateUtils.getApplicableConstraint(
382                         this.getSimpleConstraint(), stateMapping.getCurrentState(stateObject), stateMapping);
383                 if (prevConstraint == null || prevConstraint.getRequired() == null || !prevConstraint.getRequired()) {
384                     this.getFieldLabel().setRequiredIndicator(nextStateReqIndicator);
385                 }
386             }
387         }
388 
389         ClientValidationUtils.processAndApplyConstraints(this, view, model);
390 
391         if (inlineEdit || ajaxInlineEdit) {
392             this.addDataAttribute(UifConstants.DataAttributes.INLINE_EDIT, "true");
393         }
394 
395         // Generate validation messages
396         if (validationMessages != null) {
397             // Messages will not use tooltip for inline edit cases
398             if (inlineEdit || ajaxInlineEdit) {
399                 validationMessages.setUseTooltip(false);
400             }
401 
402             validationMessages.generateMessages(view, model, this);
403         }
404 
405         addComponentPostMetadata();
406 
407         if (this.getHelp() != null && StringUtils.isNotBlank(this.getHelp().getExternalHelpUrl())) {
408             this.setRenderInputAddonGroup(true);
409         }
410     }
411 
412     /**
413      * Invoked during the finalize phase to capture state of the component needs to support post operations.
414      */
415     protected void addComponentPostMetadata() {
416         ViewPostMetadata viewPostMetadata = ViewLifecycle.getViewPostMetadata();
417 
418         viewPostMetadata.getInputFieldIds().add(this.getId());
419 
420         viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.LABEL, this.getLabel());
421 
422         viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.PATH, this.getName());
423 
424         viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.SIMPLE_CONSTRAINT,
425                 this.getSimpleConstraint());
426 
427         viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.VALID_CHARACTER_CONSTRAINT,
428                 this.getValidCharactersConstraint());
429 
430         viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.CASE_CONSTRAINT, this.getCaseConstraint());
431 
432         viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.MUST_OCCUR_CONSTRAINTS,
433                 this.getMustOccurConstraints());
434 
435         viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.PREREQ_CONSTSTRAINTS,
436                 this.getPrerequisiteConstraints());
437 
438         viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.INPUT_FIELD_ATTRIBUTE_QUERY,
439                 attributeQuery);
440 
441         if (this.suggest != null) {
442             viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.SUGGEST, this.suggest.getPostData());
443         }
444 
445         viewPostMetadata.addComponentPostData(this, UifConstants.PostMetadata.INPUT_FIELD_IS_UPPERCASE,
446                 isUppercaseValue());
447 
448         if ((isRender() || StringUtils.isNotBlank(getProgressiveRender())) && !isHidden() && (!Boolean.TRUE.equals(
449                 getReadOnly()) || inlineEdit || ajaxInlineEdit)) {
450             viewPostMetadata.addAccessibleBindingPath(getBindingInfo().getBindingPath());
451         }
452     }
453 
454     /**
455      * Overrides processReadOnlyListDisplay to handle MultiValueControls by creating the list of values from values
456      * instead of the keys of the options selected (makes the list "human-readable").  Otherwise it just passes the
457      * list ahead as normal if this InputField does not use a MultiValueControl.
458      *
459      * @param model the model
460      * @param originalList originalList of values
461      */
462     @Override
463     protected void processReadOnlyListDisplay(Object model, List<?> originalList) {
464         //Special handling for option based fields
465         if ((control != null) && control instanceof MultiValueControlBase) {
466             List<String> newList = new ArrayList<String>();
467             List<KeyValue> fieldOptions = ((MultiValueControlBase) control).getOptions();
468 
469             if (fieldOptions == null || fieldOptions.isEmpty()) {
470                 return;
471             }
472 
473             for (Object fieldValue : originalList) {
474                 for (KeyValue keyValue : fieldOptions) {
475                     if (fieldValue != null && StringUtils.equals(fieldValue.toString(), keyValue.getKey())) {
476                         newList.add(keyValue.getValue());
477                         break;
478                     }
479                 }
480             }
481             this.setReadOnlyDisplayReplacement(super.generateReadOnlyListDisplayReplacement(newList));
482         } else {
483             this.setReadOnlyDisplayReplacement(super.generateReadOnlyListDisplayReplacement(originalList));
484         }
485     }
486 
487     /**
488      * Overriding to check quickfinder when masked is being applied. If quickfinder is configured set the component
489      * to widgetInputOnly, else set to readOnly
490      *
491      * {@inheritDoc}
492      */
493     @Override
494     protected void setAlternateAndAdditionalDisplayValue(View view, Object model) {
495         // if alternate or additional display values set don't override
496         if (StringUtils.isNotBlank(getReadOnlyDisplayReplacement()) || StringUtils.isNotBlank(
497                 getReadOnlyDisplaySuffix())) {
498             return;
499         }
500 
501         if (isApplyMask()) {
502             if ((this.quickfinder != null) && StringUtils.isNotBlank(this.quickfinder.getDataObjectClassName())) {
503                 setWidgetInputOnly(true);
504             } else {
505                 String maintenanceAction = null;
506                 if (view.getViewTypeName().equals(UifConstants.ViewType.MAINTENANCE)) {
507                     maintenanceAction =((MaintenanceDocumentForm) model).getMaintenanceAction();
508                 }
509 
510                 if ((!view.getViewTypeName().equals(UifConstants.ViewType.LOOKUP)) &&
511                     (!KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction))) {
512                         setReadOnly(true);
513                 }
514             }
515         }
516 
517         super.setAlternateAndAdditionalDisplayValue(view, model);
518     }
519 
520     /**
521      * Adjust paths on the must occur constrain bindings
522      *
523      * @param mustOccurConstraints
524      */
525     protected void adjustMustOccurConstraintBinding(List<MustOccurConstraint> mustOccurConstraints) {
526         if (mustOccurConstraints != null) {
527             for (MustOccurConstraint mustOccurConstraint : mustOccurConstraints) {
528                 adjustPrerequisiteConstraintBinding(mustOccurConstraint.getPrerequisiteConstraints());
529                 adjustMustOccurConstraintBinding(mustOccurConstraint.getMustOccurConstraints());
530             }
531         }
532     }
533 
534     /**
535      * Adjust paths on the prerequisite constraint bindings
536      *
537      * @param prerequisiteConstraints
538      */
539     protected void adjustPrerequisiteConstraintBinding(List<PrerequisiteConstraint> prerequisiteConstraints) {
540         if (prerequisiteConstraints != null) {
541             for (PrerequisiteConstraint prerequisiteConstraint : prerequisiteConstraints) {
542                 String propertyName = getBindingInfo().getPropertyAdjustedBindingPath(
543                         prerequisiteConstraint.getPropertyName());
544                 prerequisiteConstraint.setPropertyName(propertyName);
545             }
546         }
547     }
548 
549     /**
550      * Performs setup of the field attribute query and informational display properties.
551      *
552      * <p>Paths are adjusted to match the binding for the this field, and the necessary onblur script for
553      * triggering the query client side is constructed</p>
554      *
555      * @param view view instance the input field is associated with
556      */
557     protected void setupFieldQuery(View view) {
558         if (getAttributeQuery() != null) {
559             getAttributeQuery().defaultQueryTarget(view.getViewHelperService());
560 
561             // adjust paths on query mappings
562             getAttributeQuery().updateQueryFieldMapping(getBindingInfo());
563             getAttributeQuery().updateReturnFieldMapping(getBindingInfo());
564             getAttributeQuery().updateQueryMethodArgumentFieldList(getBindingInfo());
565 
566             // build onblur script for field query
567             String script = "executeFieldQuery('" + getControl().getId() + "',";
568             script += "'" + getId() + "'," + getAttributeQuery().getQueryFieldMappingJsString() + ",";
569             script += getAttributeQuery().getQueryMethodArgumentFieldsJsString() + ",";
570             script += getAttributeQuery().getReturnFieldMappingJsString() + ");";
571 
572             // show the span wich will contain the info
573             this.setRenderInfoMessageSpan(true);
574 
575             if (StringUtils.isNotBlank(getControl().getOnBlurScript())) {
576                 script = getControl().getOnBlurScript() + script;
577             }
578             getControl().setOnBlurScript(script);
579         }
580     }
581 
582     /**
583      * Sets the ids on all components the input field uses so they will all
584      * contain this input field's id in their ids. This is useful for jQuery
585      * manipulation.
586      */
587     protected void setupIds() {
588         // update ids so they all match the attribute
589 
590         setNestedComponentIdAndSuffix(getControl(), UifConstants.IdSuffixes.CONTROL);
591         setNestedComponentIdAndSuffix(getFieldLabel(), UifConstants.IdSuffixes.LABEL);
592         setNestedComponentIdAndSuffix(getInstructionalMessage(), UifConstants.IdSuffixes.INSTRUCTIONAL);
593         setNestedComponentIdAndSuffix(getConstraintMessage(), UifConstants.IdSuffixes.CONSTRAINT);
594         setNestedComponentIdAndSuffix(getQuickfinder(), UifConstants.IdSuffixes.QUICK_FINDER);
595         setNestedComponentIdAndSuffix(getSuggest(), UifConstants.IdSuffixes.SUGGEST);
596 
597         if (this.getControl() != null) {
598             this.getControl().addDataAttribute(UifConstants.DataAttributes.CONTROL_FOR, this.getId());
599         }
600     }
601 
602     /**
603      * {@inheritDoc}
604      */
605     @Override
606     public void copyFromAttributeDefinition(AttributeDefinition attributeDefinition) {
607         super.copyFromAttributeDefinition(attributeDefinition);
608 
609         // max length
610         if (getMaxLength() == null) {
611             setMaxLength(attributeDefinition.getMaxLength());
612         }
613 
614         // min length
615         if (getMinLength() == null) {
616             setMinLength(attributeDefinition.getMinLength());
617         }
618 
619         // valid characters
620         if (getValidCharactersConstraint() == null) {
621             setValidCharactersConstraint(attributeDefinition.getValidCharactersConstraint());
622         }
623 
624         if (getCaseConstraint() == null) {
625             setCaseConstraint(attributeDefinition.getCaseConstraint());
626         }
627 
628         if (getDependencyConstraints() == null) {
629             setDependencyConstraints(attributeDefinition.getPrerequisiteConstraints());
630         }
631 
632         if (getMustOccurConstraints() == null) {
633             setMustOccurConstraints(attributeDefinition.getMustOccurConstraints());
634         }
635 
636         // required
637         if (getRequired() == null) {
638             setRequired(attributeDefinition.isRequired());
639 
640             //if still null, default to false
641             if (getRequired() == null) {
642                 setRequired(Boolean.FALSE);
643             }
644         }
645 
646         if (getDataType() == null) {
647             setDataType(attributeDefinition.getDataType());
648             //Assume date if dataType is still null and using a DatePicker
649             if (getDataType() == null
650                     && control instanceof TextControl
651                     && ((TextControl) control).getDatePicker() != null) {
652                 setDataType(DataType.DATE);
653             }
654         }
655 
656         // control
657         if ((getControl() == null) && (attributeDefinition.getControlField() != null)) {
658             Control control = ComponentUtils.copy(attributeDefinition.getControlField());
659             setControl(control);
660         }
661 
662         // constraint
663         if (StringUtils.isEmpty(getConstraintText())) {
664             setConstraintText(attributeDefinition.getConstraintText());
665 
666             if (constraintMessage == null) {
667                 constraintMessage = ComponentFactory.getConstraintMessage();
668             }
669 
670             getConstraintMessage().setMessageText(attributeDefinition.getConstraintText());
671         }
672 
673         // options
674         if (getOptionsFinder() == null) {
675             setOptionsFinder(attributeDefinition.getOptionsFinder());
676         }
677 
678         // copy over simple constraint information because we cannot directly use simpleConstraint from
679         // attributeDefinition because the settings in InputField take precedence
680         if (this.getSimpleConstraint().getConstraintStateOverrides() == null) {
681             this.getSimpleConstraint().setConstraintStateOverrides(
682                     attributeDefinition.getSimpleConstraint().getConstraintStateOverrides());
683         }
684 
685         if (this.getSimpleConstraint().getStates().isEmpty()) {
686             this.getSimpleConstraint().setStates(attributeDefinition.getSimpleConstraint().getStates());
687         }
688 
689         if (this.getSimpleConstraint().getMessageKey() == null) {
690             this.getSimpleConstraint().setMessageKey(attributeDefinition.getSimpleConstraint().getMessageKey());
691         }
692 
693         if (this.getSimpleConstraint().getApplyClientSide() == null) {
694             this.getSimpleConstraint().setApplyClientSide(
695                     attributeDefinition.getSimpleConstraint().getApplyClientSide());
696         }
697 
698         if (this.getSimpleConstraint().getValidationMessageParams() == null) {
699             this.getSimpleConstraint().setValidationMessageParams(
700                     attributeDefinition.getSimpleConstraint().getValidationMessageParams());
701         }
702     }
703 
704     /**
705      * {@inheritDoc}
706      */
707     @Override
708     public boolean isInputAllowed() {
709         return true;
710     }
711 
712     /**
713      * {@inheritDoc}
714      */
715     @Override
716     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.BYTYPE)
717     public Control getControl() {
718         return this.control;
719     }
720 
721     /**
722      * {@inheritDoc}
723      */
724     @Override
725     public void setControl(Control control) {
726         this.control = control;
727     }
728 
729     /**
730      * {@inheritDoc}
731      */
732     @BeanTagAttribute
733     public boolean isInlineEdit() {
734         return inlineEdit;
735     }
736 
737     /**
738      * {@inheritDoc}
739      */
740     public void setInlineEdit(boolean inlineEdit) {
741         this.inlineEdit = inlineEdit;
742     }
743 
744     /**
745      * {@inheritDoc}
746      */
747     @BeanTagAttribute
748     public boolean isAjaxInlineEdit() {
749         return ajaxInlineEdit;
750     }
751 
752     /**
753      * {@inheritDoc}
754      */
755     public void setAjaxInlineEdit(boolean ajaxInlineEdit) {
756         this.ajaxInlineEdit = ajaxInlineEdit;
757     }
758 
759     /**
760      * {@inheritDoc}
761      */
762     @Override
763     @ViewLifecycleRestriction
764     @BeanTagAttribute
765     public FieldValidationMessages getValidationMessages() {
766         return this.validationMessages;
767     }
768 
769     /**
770      * {@inheritDoc}
771      */
772     @Override
773     public void setValidationMessages(FieldValidationMessages validationMessages) {
774         this.validationMessages = validationMessages;
775     }
776 
777     /**
778      * {@inheritDoc}
779      */
780     @Override
781     @BeanTagAttribute
782     public KeyValuesFinder getOptionsFinder() {
783         return this.optionsFinder;
784     }
785 
786     /**
787      * {@inheritDoc}
788      */
789     @Override
790     public void setOptionsFinder(KeyValuesFinder optionsFinder) {
791         this.optionsFinder = optionsFinder;
792     }
793 
794     /**
795      * {@inheritDoc}
796      */
797     @Override
798     @BeanTagAttribute
799     public Class<? extends KeyValuesFinder> getOptionsFinderClass() {
800         if (this.optionsFinder != null) {
801             return this.optionsFinder.getClass();
802         } else {
803             return null;
804         }
805     }
806 
807     /**
808      * {@inheritDoc}
809      */
810     @Override
811     public void setOptionsFinderClass(Class<? extends KeyValuesFinder> optionsFinderClass) {
812         this.optionsFinder = KRADUtils.createNewObjectFromClass(optionsFinderClass);
813     }
814 
815     /**
816      * {@inheritDoc}
817      */
818     @Override
819     @BeanTagAttribute
820     public boolean isEnableAutoDirectInquiry() {
821         return enableAutoDirectInquiry;
822     }
823 
824     /**
825      * {@inheritDoc}
826      */
827     @Override
828     public void setEnableAutoDirectInquiry(boolean enableAutoDirectInquiry) {
829         this.enableAutoDirectInquiry = enableAutoDirectInquiry;
830     }
831 
832     /**
833      * {@inheritDoc}
834      */
835     @Override
836     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
837     public QuickFinder getQuickfinder() {
838         return this.quickfinder;
839     }
840 
841     /**
842      * {@inheritDoc}
843      */
844     @Override
845     public void setQuickfinder(QuickFinder quickfinder) {
846         this.quickfinder = quickfinder;
847     }
848 
849     /**
850      * {@inheritDoc}
851      */
852     @Override
853     @BeanTagAttribute
854     public boolean isEnableAutoQuickfinder() {
855         return enableAutoQuickfinder;
856     }
857 
858     /**
859      * {@inheritDoc}
860      */
861     @Override
862     public void setEnableAutoQuickfinder(boolean enableAutoQuickfinder) {
863         this.enableAutoQuickfinder = enableAutoQuickfinder;
864     }
865 
866     /**
867      * {@inheritDoc}
868      */
869     @Override
870     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
871     public Suggest getSuggest() {
872         return suggest;
873     }
874 
875     /**
876      * {@inheritDoc}
877      */
878     @Override
879     public void setSuggest(Suggest suggest) {
880         this.suggest = suggest;
881     }
882 
883     /**
884      * {@inheritDoc}
885      */
886     @Override
887     @BeanTagAttribute
888     public boolean isWidgetInputOnly() {
889         return this.widgetInputOnly;
890     }
891 
892     /**
893      * {@inheritDoc}
894      */
895     @Override
896     public void setWidgetInputOnly(boolean widgetInputOnly) {
897         this.widgetInputOnly = widgetInputOnly;
898     }
899 
900     /**
901      * {@inheritDoc}
902      */
903     @Override
904     @BeanTagAttribute
905     public boolean isRenderInputAddonGroup() {
906         return renderInputAddonGroup;
907     }
908 
909     /**
910      * {@inheritDoc}
911      */
912     @Override
913     public void setRenderInputAddonGroup(boolean renderInputAddonGroup) {
914         this.renderInputAddonGroup = renderInputAddonGroup;
915     }
916 
917     /**
918      * {@inheritDoc}
919      */
920     @Override
921     @BeanTagAttribute
922     public List<String> getPostInputCssClasses() {
923         return postInputCssClasses;
924     }
925 
926     /**
927      * {@inheritDoc}
928      */
929     @Override
930     public String getPostInputCssClassesAsString() {
931         if (postInputCssClasses != null) {
932             return StringUtils.join(postInputCssClasses, " ");
933         }
934 
935         return "";
936     }
937 
938     /**
939      * {@inheritDoc}
940      */
941     @Override
942     public void setPostInputCssClasses(List<String> postInputCssClasses) {
943         this.postInputCssClasses = postInputCssClasses;
944     }
945 
946     /**
947      * {@inheritDoc}
948      */
949     @Override
950     @BeanTagAttribute
951     public List<Component> getPostInputAddons() {
952         return postInputAddons;
953     }
954 
955     /**
956      * {@inheritDoc}
957      */
958     @Override
959     public void setPostInputAddons(List<Component> postInputAddons) {
960         this.postInputAddons = postInputAddons;
961     }
962 
963     /**
964      * {@inheritDoc}
965      */
966     @Override
967     public void addPostInputAddon(Component addOn) {
968         if (postInputAddons == null) {
969             postInputAddons = new ArrayList<Component>();
970         }
971 
972         postInputAddons.add(addOn);
973     }
974 
975     /**
976      * {@inheritDoc}
977      */
978     @Override
979     @BeanTagAttribute
980     public String getInstructionalText() {
981         return this.instructionalText;
982     }
983 
984     /**
985      * {@inheritDoc}
986      */
987     @Override
988     public void setInstructionalText(String instructionalText) {
989         this.instructionalText = instructionalText;
990     }
991 
992     /**
993      * {@inheritDoc}
994      */
995     @Override
996     @BeanTagAttribute
997     public Message getInstructionalMessage() {
998         return this.instructionalMessage;
999     }
1000 
1001     /**
1002      * {@inheritDoc}
1003      */
1004     @Override
1005     public void setInstructionalMessage(Message instructionalMessage) {
1006         this.instructionalMessage = instructionalMessage;
1007     }
1008 
1009     /**
1010      * {@inheritDoc}
1011      */
1012     @Override
1013     @BeanTagAttribute
1014     public String getHelperText() {
1015         return helperText;
1016     }
1017 
1018     /**
1019      * {@inheritDoc}
1020      */
1021     @Override
1022     public void setHelperText(String helperText) {
1023         this.helperText = helperText;
1024     }
1025 
1026     /**
1027      * {@inheritDoc}
1028      */
1029     @Override
1030     @BeanTagAttribute
1031     public String getConstraintText() {
1032         return this.constraintText;
1033     }
1034 
1035     /**
1036      * {@inheritDoc}
1037      */
1038     @Override
1039     public void setConstraintText(String constraintText) {
1040         this.constraintText = constraintText;
1041     }
1042 
1043     /**
1044      * {@inheritDoc}
1045      */
1046     @Override
1047     @BeanTagAttribute
1048     public Message getConstraintMessage() {
1049         return this.constraintMessage;
1050     }
1051 
1052     /**
1053      * {@inheritDoc}
1054      */
1055     @Override
1056     public void setConstraintMessage(Message constraintMessage) {
1057         this.constraintMessage = constraintMessage;
1058     }
1059 
1060     /**
1061      * {@inheritDoc}
1062      */
1063     @Override
1064     @BeanTagAttribute
1065     public ValidCharactersConstraint getValidCharactersConstraint() {
1066         return this.validCharactersConstraint;
1067     }
1068 
1069     /**
1070      * {@inheritDoc}
1071      */
1072     @Override
1073     public void setValidCharactersConstraint(ValidCharactersConstraint validCharactersConstraint) {
1074         this.validCharactersConstraint = validCharactersConstraint;
1075     }
1076 
1077     /**
1078      * {@inheritDoc}
1079      */
1080     @Override
1081     @BeanTagAttribute
1082     public CaseConstraint getCaseConstraint() {
1083         return this.caseConstraint;
1084     }
1085 
1086     /**
1087      * {@inheritDoc}
1088      */
1089     @Override
1090     public void setCaseConstraint(CaseConstraint caseConstraint) {
1091         this.caseConstraint = caseConstraint;
1092     }
1093 
1094     /**
1095      * {@inheritDoc}
1096      */
1097     @Override
1098     @BeanTagAttribute
1099     public List<PrerequisiteConstraint> getDependencyConstraints() {
1100         return this.dependencyConstraints;
1101     }
1102 
1103     /**
1104      * {@inheritDoc}
1105      */
1106     @Override
1107     public void setDependencyConstraints(List<PrerequisiteConstraint> dependencyConstraints) {
1108         this.dependencyConstraints = dependencyConstraints;
1109     }
1110 
1111     /**
1112      * {@inheritDoc}
1113      */
1114     @Override
1115     public List<PrerequisiteConstraint> getPrerequisiteConstraints() {
1116         return dependencyConstraints;
1117     }
1118 
1119     /**
1120      * {@inheritDoc}
1121      */
1122     @Override
1123     @BeanTagAttribute
1124     public List<MustOccurConstraint> getMustOccurConstraints() {
1125         return this.mustOccurConstraints;
1126     }
1127 
1128     /**
1129      * {@inheritDoc}
1130      */
1131     @Override
1132     public void setMustOccurConstraints(List<MustOccurConstraint> mustOccurConstraints) {
1133         this.mustOccurConstraints = mustOccurConstraints;
1134     }
1135 
1136     /**
1137      * {@inheritDoc}
1138      */
1139     @Override
1140     @BeanTagAttribute
1141     public SimpleConstraint getSimpleConstraint() {
1142         return this.simpleConstraint;
1143     }
1144 
1145     /**
1146      * {@inheritDoc}
1147      */
1148     @Override
1149     public void setSimpleConstraint(SimpleConstraint simpleConstraint) {
1150         this.simpleConstraint = simpleConstraint;
1151     }
1152 
1153     /**
1154      * {@inheritDoc}
1155      */
1156     @Override
1157     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1158     public DataType getDataType() {
1159         return this.simpleConstraint.getDataType();
1160     }
1161 
1162     /**
1163      * {@inheritDoc}
1164      */
1165     @Override
1166     public void setDataType(DataType dataType) {
1167         this.simpleConstraint.setDataType(dataType);
1168     }
1169 
1170     /**
1171      * {@inheritDoc}
1172      */
1173     @Override
1174     public void setDataType(String dataType) {
1175         this.simpleConstraint.setDataType(DataType.valueOf(dataType));
1176     }
1177 
1178     /**
1179      * {@inheritDoc}
1180      */
1181     @Override
1182     @BeanTagAttribute
1183     public Integer getMaxLength() {
1184         return simpleConstraint.getMaxLength();
1185     }
1186 
1187     /**
1188      * {@inheritDoc}
1189      */
1190     @Override
1191     public void setMaxLength(Integer maxLength) {
1192         simpleConstraint.setMaxLength(maxLength);
1193     }
1194 
1195     /**
1196      * {@inheritDoc}
1197      */
1198     @Override
1199     @BeanTagAttribute
1200     public Integer getMinLength() {
1201         return simpleConstraint.getMinLength();
1202     }
1203 
1204     /**
1205      * {@inheritDoc}
1206      */
1207     @Override
1208     public void setMinLength(Integer minLength) {
1209         simpleConstraint.setMinLength(minLength);
1210     }
1211 
1212     /**
1213      * {@inheritDoc}
1214      */
1215     @Override
1216     @BeanTagAttribute
1217     public Boolean getRequired() {
1218         return this.simpleConstraint.getRequired();
1219     }
1220 
1221     /**
1222      * {@inheritDoc}
1223      */
1224     @Override
1225     public void setRequired(Boolean required) {
1226         this.simpleConstraint.setRequired(required);
1227     }
1228 
1229     /**
1230      * {@inheritDoc}
1231      */
1232     @Override
1233     @BeanTagAttribute
1234     public String getExclusiveMin() {
1235         return simpleConstraint.getExclusiveMin();
1236     }
1237 
1238     /**
1239      * {@inheritDoc}
1240      */
1241     @Override
1242     public void setExclusiveMin(String exclusiveMin) {
1243         simpleConstraint.setExclusiveMin(exclusiveMin);
1244     }
1245 
1246     /**
1247      * {@inheritDoc}
1248      */
1249     @Override
1250     @BeanTagAttribute
1251     public String getInclusiveMax() {
1252         return simpleConstraint.getInclusiveMax();
1253     }
1254 
1255     /**
1256      * {@inheritDoc}
1257      */
1258     @Override
1259     public void setInclusiveMax(String inclusiveMax) {
1260         simpleConstraint.setInclusiveMax(inclusiveMax);
1261     }
1262 
1263     /**
1264      * {@inheritDoc}
1265      */
1266     @Override
1267     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
1268     public AttributeQuery getAttributeQuery() {
1269         return attributeQuery;
1270     }
1271 
1272     /**
1273      * {@inheritDoc}
1274      */
1275     @Override
1276     public void setAttributeQuery(AttributeQuery attributeQuery) {
1277         this.attributeQuery = attributeQuery;
1278     }
1279 
1280     /**
1281      * {@inheritDoc}
1282      */
1283     @Override
1284     @BeanTagAttribute
1285     public boolean isUppercaseValue() {
1286         return uppercaseValue;
1287     }
1288 
1289     /**
1290      * {@inheritDoc}
1291      */
1292     @Override
1293     public void setUppercaseValue(boolean uppercaseValue) {
1294         this.uppercaseValue = uppercaseValue;
1295     }
1296 
1297     /**
1298      * {@inheritDoc}
1299      */
1300     @Override
1301     @BeanTagAttribute
1302     public boolean isDisableNativeAutocomplete() {
1303         return disableNativeAutocomplete;
1304     }
1305 
1306     /**
1307      * {@inheritDoc}
1308      */
1309     @Override
1310     public void setDisableNativeAutocomplete(boolean disableNativeAutocomplete) {
1311         this.disableNativeAutocomplete = disableNativeAutocomplete;
1312     }
1313 
1314     /**
1315      * {@inheritDoc}
1316      */
1317     @Override
1318     public boolean isRenderFieldset() {
1319         return super.isRenderFieldset() || (quickfinder != null
1320                 && quickfinder.isRender()
1321                 && quickfinder.getQuickfinderAction() != null
1322                 && quickfinder.getQuickfinderAction().isRender());
1323     }
1324 
1325     /**
1326      * {@inheritDoc}
1327      */
1328     @Override
1329     public void completeValidation(ValidationTrace tracer) {
1330         tracer.addBean(this);
1331 
1332         // Checks that the control is set
1333         if (getControl() == null) {
1334             if (Validator.checkExpressions(this, "control")) {
1335                 String currentValues[] = {"propertyName =" + getPropertyName()};
1336                 tracer.createWarning("Control should be set", currentValues);
1337             }
1338         }
1339 
1340 
1341         if (getControl() != null && !(getControl() instanceof TextControl
1342                 || getControl() instanceof TextAreaControl
1343                 || getControl() instanceof SelectControl)){
1344 
1345             if (CollectionUtils.isNotEmpty(this.getPostInputAddons())) {
1346                 String currentValues[] = {"propertyName =" + getPropertyName()};
1347                 tracer.createWarning("Inputs which are not text or select should not use post input addons for "
1348                         + "user experience reasons", currentValues);
1349             }
1350         }
1351 
1352         super.completeValidation(tracer.getCopy());
1353     }
1354 
1355 
1356     /**
1357      * Determines wheter or not to create an automatic quickfinder widget for this field within the current lifecycle.
1358      *
1359      * @return True if an automatic quickfinder widget should be created for this field on the current lifecycle.
1360      */
1361     protected boolean hasAutoQuickfinderRelationship() {
1362         String propertyName = getBindingInfo().getBindingName();
1363 
1364         // get object instance and class for parent
1365         View view = ViewLifecycle.getView();
1366         Object model = ViewLifecycle.getModel();
1367         Object parentObject = ViewModelUtils.getParentObjectForMetadata(view, model, this);
1368         Class<?> parentObjectClass = null;
1369         if (parentObject != null) {
1370             parentObjectClass = parentObject.getClass();
1371         }
1372 
1373         // get relationship from metadata service
1374         @SuppressWarnings("deprecation")
1375         DataObjectRelationship relationship = null;
1376         if (parentObject != null) {
1377             relationship = KRADServiceLocatorWeb.getLegacyDataAdapter().getDataObjectRelationship(parentObject,
1378                     parentObjectClass, propertyName, "", true, true, false);
1379         }
1380 
1381         return relationship != null;
1382     }
1383 
1384 }