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.beans.PropertyEditor;
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.apache.commons.lang.StringEscapeUtils;
24  import org.apache.commons.lang.StringUtils;
25  import org.apache.commons.lang3.ArrayUtils;
26  import org.kuali.rice.core.api.exception.RiceRuntimeException;
27  import org.kuali.rice.core.api.util.type.TypeUtils;
28  import org.kuali.rice.krad.bo.DataObjectRelationship;
29  import org.kuali.rice.krad.bo.KualiCode;
30  import org.kuali.rice.krad.datadictionary.AttributeDefinition;
31  import org.kuali.rice.krad.datadictionary.mask.MaskFormatter;
32  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
33  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
34  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
35  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
36  import org.kuali.rice.krad.datadictionary.validator.Validator;
37  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
38  import org.kuali.rice.krad.uif.UifConstants;
39  import org.kuali.rice.krad.uif.component.BindingInfo;
40  import org.kuali.rice.krad.uif.component.ComponentSecurity;
41  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
42  import org.kuali.rice.krad.uif.lifecycle.ViewPostMetadata;
43  import org.kuali.rice.krad.uif.util.ComponentFactory;
44  import org.kuali.rice.krad.uif.util.LifecycleAwareList;
45  import org.kuali.rice.krad.uif.util.LifecycleElement;
46  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
47  import org.kuali.rice.krad.uif.util.ViewModelUtils;
48  import org.kuali.rice.krad.uif.view.View;
49  import org.kuali.rice.krad.uif.view.ViewModel;
50  import org.kuali.rice.krad.uif.widget.Help;
51  import org.kuali.rice.krad.uif.widget.Inquiry;
52  import org.kuali.rice.krad.uif.widget.Tooltip;
53  import org.kuali.rice.krad.util.KRADPropertyConstants;
54  import org.kuali.rice.krad.util.KRADUtils;
55  import org.kuali.rice.krad.valuefinder.ValueFinder;
56  import org.kuali.rice.krad.web.form.InquiryForm;
57  
58  /**
59   * Field that renders data from the application, such as the value of a data object property
60   *
61   * @author Kuali Rice Team (rice.collab@kuali.org)
62   */
63  @BeanTags({@BeanTag(name = "data", parent = "Uif-DataField"),
64          @BeanTag(name = "dataLabelTop", parent = "Uif-DataField-LabelTop"),
65          @BeanTag(name = "dataLabelRight", parent = "Uif-DataField-LabelRight"),
66          @BeanTag(name = "dataNoLabel", parent = "Uif-DataField-withoutLabel")})
67  public class DataFieldBase extends FieldBase implements DataField {
68      private static final long serialVersionUID = -4129678891948564724L;
69  
70      // binding
71      private String propertyName;
72      private BindingInfo bindingInfo;
73  
74      private String dictionaryAttributeName;
75      private String dictionaryObjectEntry;
76  
77      // value props
78      private Object defaultValue;
79      private Class<? extends ValueFinder> defaultValueFinderClass;
80      private List<Object> defaultValues;
81      private String forcedValue;
82  
83      private PropertyEditor propertyEditor;
84  
85      private boolean addHiddenWhenReadOnly;
86  
87      // read only display properties
88      protected String readOnlyDisplayReplacementPropertyName;
89      protected String readOnlyDisplaySuffixPropertyName;
90  
91      private String readOnlyDisplayReplacement;
92      private String readOnlyDisplaySuffix;
93  
94      private String readOnlyListDisplayType;
95      private String readOnlyListDelimiter;
96  
97      private boolean applyMask;
98      private MaskFormatter maskFormatter;
99  
100     private List<String> additionalHiddenPropertyNames;
101     private List<String> propertyNamesForAdditionalDisplay;
102 
103     private boolean escapeHtmlInPropertyValue;
104     private boolean multiLineReadOnlyDisplay;
105 
106     // widgets
107     private Inquiry inquiry;
108     private boolean enableAutoInquiry;
109 
110     private Help help;
111 
112     // Optional span render flags
113     private boolean renderInfoMessageSpan;
114     private boolean renderMarkerIconSpan;
115 
116     private String sortAs;
117 
118     public DataFieldBase() {
119         super();
120 
121         enableAutoInquiry = true;
122         escapeHtmlInPropertyValue = true;
123 
124         additionalHiddenPropertyNames = Collections.emptyList();
125         propertyNamesForAdditionalDisplay = Collections.emptyList();
126     }
127 
128     /**
129      * {@inheritDoc}
130      */
131     @Override
132     public void performInitialization(Object model) {
133         super.performInitialization(model);
134 
135         if (bindingInfo != null) {
136             bindingInfo.setDefaults(ViewLifecycle.getView(), getPropertyName());
137         }
138     }
139     
140     /**
141      * {@inheritDoc}
142      */
143     @Override
144     public void afterEvaluateExpression() {
145         // set to true before calling super.
146         if (getReadOnly() == null) {
147             setReadOnly(true);
148         }
149         
150         super.afterEvaluateExpression();
151     }
152 
153     /**
154      * {@inheritDoc}
155      */
156     @Override
157     public void performApplyModel(Object model, LifecycleElement parent) {
158         super.performApplyModel(model, parent);
159 
160         if (enableAutoInquiry && this.inquiry == null && Boolean.TRUE.equals(getReadOnly()) && hasAutoInquiryRelationship()) {
161             this.inquiry = ComponentFactory.getInquiry();
162         }
163 
164         if (isAddHiddenWhenReadOnly()) {
165             setReadOnly(true);
166             getAdditionalHiddenPropertyNames().add(getPropertyName());
167         }
168     }
169 
170     /**
171      * {@inheritDoc}
172      */
173     @Override
174     public void performFinalize(Object model, LifecycleElement parent) {
175         super.performFinalize(model, parent);
176 
177         // adjust the path for hidden fields and add as accessible paths
178         List<String> hiddenPropertyPaths = new ArrayList<String>();
179         for (String hiddenPropertyName : getAdditionalHiddenPropertyNames()) {
180             String hiddenPropertyPath = getBindingInfo().getPropertyAdjustedBindingPath(hiddenPropertyName);
181             hiddenPropertyPaths.add(hiddenPropertyPath);
182 
183             if (isRender() || StringUtils.isNotBlank(getProgressiveRender())) {
184                 ViewLifecycle.getViewPostMetadata().addAccessibleBindingPath(hiddenPropertyPath);
185             }
186         }
187         this.additionalHiddenPropertyNames = hiddenPropertyPaths;
188 
189         // adjust paths on informational property names
190         List<String> informationalPropertyPaths = new ArrayList<String>();
191         for (String infoPropertyName : getPropertyNamesForAdditionalDisplay()) {
192             String infoPropertyPath = getBindingInfo().getPropertyAdjustedBindingPath(infoPropertyName);
193             informationalPropertyPaths.add(infoPropertyPath);
194         }
195         this.propertyNamesForAdditionalDisplay = informationalPropertyPaths;
196 
197         // process read-only lists and additional and alternate display values
198         boolean hasPropertyEditor = getPropertyEditor() != null;
199         boolean hasReadOnlyDisplayReplacement = StringUtils.isNotBlank(getReadOnlyDisplayReplacement());
200         boolean hasReadOnlyDisplayReplacementPropertyName = StringUtils.isNotBlank(
201                 getReadOnlyDisplayReplacementPropertyName());
202         String bindingPath = getBindingInfo().getBindingPath();
203         Class<?> type = StringUtils.isNotEmpty(bindingPath) ? ObjectPropertyUtils.getPropertyType(model, bindingPath) : null;
204         boolean isReadOnlyList = Boolean.TRUE.equals(getReadOnly()) && type != null && List.class.isAssignableFrom(type);
205 
206         if (!hasPropertyEditor && !hasReadOnlyDisplayReplacement && !hasReadOnlyDisplayReplacementPropertyName && isReadOnlyList) {
207             Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, bindingPath);
208             List<?> list = fieldValue != null ? (List<?>) fieldValue : Collections.emptyList();
209 
210             processReadOnlyListDisplay(model, list);
211         } else {
212             setAlternateAndAdditionalDisplayValue(ViewLifecycle.getView(), model);
213         }
214 
215         if (this.getFieldLabel() != null && StringUtils.isNotBlank(this.getId())) {
216             this.getFieldLabel().setLabelForComponentId(this.getId() + UifConstants.IdSuffixes.CONTROL);
217         }
218 
219         if (model instanceof ViewModel) {
220             View view = ViewLifecycle.getView();
221             if(((ViewModel) model).isApplyDefaultValues()) {
222 
223                 // apply default field values to view
224                 view.getViewHelperService().populateDefaultValueForField(model, this,
225                         this.getBindingInfo().getBindingPath());
226 
227                 // ensure default values are only applied once
228                 ((ViewModel) model).setApplyDefaultValues(false);
229             }
230         }
231         
232         ViewPostMetadata viewPostMetadata = ViewLifecycle.getViewPostMetadata();
233         if (isRender() && viewPostMetadata != null) {
234             viewPostMetadata.addRenderedPropertyPath(getBindingInfo().getBindingPath());
235         }
236     }
237 
238     /**
239      * This method is called when the list is readOnly as determined in DataField's performFinalize method.  This
240      * method
241      * should be overridden to perform any additional processing to the values before calling
242      * generateReadOnlyListDisplayReplacement.  The default implementation calls it directly with the originalList.
243      *
244      * @param model the model
245      * @param originalList originalList of values
246      */
247     protected void processReadOnlyListDisplay(Object model, List<?> originalList) {
248         this.setReadOnlyDisplayReplacement(generateReadOnlyListDisplayReplacement(originalList));
249     }
250 
251     /**
252      * Generates the html to be used and sets the readOnlyDisplayReplacement for DataFields that contain lists and
253      * do not have their own readOnlyDisplayReplacement defined.  The type of html generated is based on the options
254      * set on readOnlyListDisplayType and readOnlyListDelimiter.
255      *
256      * @param list the list to be converted to readOnly html
257      */
258     protected String generateReadOnlyListDisplayReplacement(List<?> list) {
259         //Default to delimited if nothing is set
260         if (getReadOnlyListDisplayType() == null) {
261             this.setReadOnlyListDisplayType(UifConstants.ReadOnlyListTypes.DELIMITED.name());
262         }
263 
264         String generatedHtml = "";
265 
266         //begin generation setup
267         if (!list.isEmpty()) {
268             if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.UL.name())) {
269                 generatedHtml = "<ul class='uif-readOnlyStringList'>";
270             } else if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.OL.name())) {
271                 generatedHtml = "<ol class='uif-readOnlyStringList'>";
272             } else if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.BREAK.name())) {
273                 setReadOnlyListDelimiter("<br/>");
274             } else if (this.getReadOnlyListDelimiter() == null) {
275                 setReadOnlyListDelimiter(", ");
276             }
277         }
278 
279         //iterate through each value
280         for (Object value : list) {
281             //if blank skip
282             if (!TypeUtils.isSimpleType(value.getClass()) || StringUtils.isBlank(value.toString())) {
283                 continue;
284             }
285 
286             //handle mask if any
287             if (isApplyMask()) {
288                 value = getMaskFormatter().maskValue(value);
289             }
290 
291             //TODO the value should use the formatted text property value we would expect to see instead of toString
292             //two types - delimited and html list
293             if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.UL.name())
294                     || getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.OL.name())) {
295                 generatedHtml = generatedHtml + "<li>" + StringEscapeUtils.escapeHtml(value.toString()) + "</li>";
296             } else {
297                 //no matching needed - delimited is always the fallback and break uses same logic
298                 generatedHtml = generatedHtml + StringEscapeUtils.escapeHtml(value.toString())
299                         + this.getReadOnlyListDelimiter();
300             }
301         }
302 
303         //end the generation
304         if (!list.isEmpty()) {
305             if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.UL.name())) {
306                 generatedHtml = generatedHtml + "</ul>";
307             } else if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.OL.name())) {
308                 generatedHtml = generatedHtml + "</ol>";
309             } else {
310                 generatedHtml = StringUtils.removeEnd(generatedHtml, this.getReadOnlyListDelimiter());
311             }
312         }
313 
314         if (StringUtils.isBlank(generatedHtml)) {
315             generatedHtml = "&nbsp;";
316         }
317 
318         return generatedHtml;
319     }
320 
321     /**
322      * Sets alternate and additional property value for this field.
323      *
324      * <p>
325      * If <code>AttributeSecurity</code> present in this field, make sure the current user has permission to view the
326      * field value. If user doesn't have permission to view the value, mask the value as configured and set it
327      * as alternate value for display. If security doesn't exists for this field but
328      * <code>alternateDisplayPropertyName</code> present, get its value and format it based on that
329      * fields formatting and set for display.
330      * </p>
331      *
332      * <p>
333      * For additional display value, if <code>AttributeSecurity</code> not present, sets the value if
334      * <code>additionalDisplayPropertyName</code> present. If not present, check whether this field is a
335      * <code>KualiCode</code> and get the relationship configured in the datadictionary file and set the name
336      * additional display value which will be displayed along with the code. If additional display property not
337      * present, check whether this field is has <code>MultiValueControlBase</code>. If yes, get the Label
338      * for the value and set it as additional display value.
339      * </p>
340      *
341      * @param view the current view instance
342      * @param model model instance
343      */
344     protected void setAlternateAndAdditionalDisplayValue(View view, Object model) {
345         // if alternate or additional display values set don't use property names
346         if (StringUtils.isNotBlank(readOnlyDisplayReplacement) || StringUtils.isNotBlank(readOnlyDisplaySuffix)) {
347             return;
348         }
349 
350         // check whether field value needs to be masked, and if so apply masking as alternateDisplayValue
351         if (isApplyMask()) {
352             Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, getBindingInfo().getBindingPath());
353             if (getMaskFormatter() != null) {
354                 readOnlyDisplayReplacement = getMaskFormatter().maskValue(fieldValue);
355             }
356 
357             return;
358         }
359 
360         // if not read only, return without trying to set alternate and additional values
361         if (!Boolean.TRUE.equals(getReadOnly())) {
362             return;
363         }
364 
365         // if field is not secure, check for alternate and additional display properties
366         if (StringUtils.isNotBlank(getReadOnlyDisplayReplacementPropertyName())) {
367             String alternateDisplayPropertyPath = getBindingInfo().getPropertyAdjustedBindingPath(
368                     getReadOnlyDisplayReplacementPropertyName());
369 
370             Object alternateFieldValue = ObjectPropertyUtils.getPropertyValue(model, alternateDisplayPropertyPath);
371             if (alternateFieldValue != null) {
372                 // TODO: format by type
373                 readOnlyDisplayReplacement = alternateFieldValue.toString();
374             }
375         }
376 
377         // perform automatic translation for code references if enabled on view
378         if (StringUtils.isBlank(getReadOnlyDisplaySuffixPropertyName()) && view.isTranslateCodesOnReadOnlyDisplay()) {
379             // check for any relationship present for this field and it's of type KualiCode
380             Class<?> parentObjectClass = ViewModelUtils.getParentObjectClassForMetadata(view, model, this);
381             DataObjectRelationship relationship =
382                     KRADServiceLocatorWeb.getLegacyDataAdapter().getDataObjectRelationship(null,
383                             parentObjectClass, getBindingInfo().getBindingName(), "", true, false, false);
384 
385             if (relationship != null
386                     && getPropertyName().startsWith(relationship.getParentAttributeName())
387                     && KualiCode.class.isAssignableFrom(relationship.getRelatedClass())) {
388                 readOnlyDisplaySuffixPropertyName =
389                         relationship.getParentAttributeName() + "." + KRADPropertyConstants.NAME;
390             }
391         }
392 
393         // now check for an additional display property and if set get the value
394         if (StringUtils.isNotBlank(getReadOnlyDisplaySuffixPropertyName())) {
395             String additionalDisplayPropertyPath = getBindingInfo().getPropertyAdjustedBindingPath(
396                     getReadOnlyDisplaySuffixPropertyName());
397 
398             Object additionalFieldValue = ObjectPropertyUtils.getPropertyValue(model, additionalDisplayPropertyPath);
399             if (additionalFieldValue != null) {
400                 // TODO: format by type
401                 readOnlyDisplaySuffix = additionalFieldValue.toString();
402             }
403         }
404     }
405 
406     /**
407      * {@inheritDoc}
408      */
409     @Override
410     public void copyFromAttributeDefinition(AttributeDefinition attributeDefinition) {
411         // label
412         if (StringUtils.isEmpty(getLabel())) {
413             setLabel(attributeDefinition.getLabel());
414         }
415 
416         // short label
417         if (StringUtils.isEmpty(getShortLabel())) {
418             setShortLabel(attributeDefinition.getShortLabel());
419         }
420 
421         // security
422         if ((attributeDefinition.getAttributeSecurity() != null) && ((getDataFieldSecurity() == null) || (
423                 getDataFieldSecurity().getAttributeSecurity() == null))) {
424             initializeComponentSecurity();
425 
426             getDataFieldSecurity().setAttributeSecurity(attributeDefinition.getAttributeSecurity());
427         }
428 
429         // alternate property name
430         if (getReadOnlyDisplayReplacementPropertyName() == null && StringUtils.isNotBlank(
431                 attributeDefinition.getAlternateDisplayAttributeName())) {
432             setReadOnlyDisplayReplacementPropertyName(attributeDefinition.getAlternateDisplayAttributeName());
433         }
434 
435         // additional property display name
436         if (getReadOnlyDisplaySuffixPropertyName() == null && StringUtils.isNotBlank(
437                 attributeDefinition.getAdditionalDisplayAttributeName())) {
438             setReadOnlyDisplaySuffixPropertyName(attributeDefinition.getAdditionalDisplayAttributeName());
439         }
440 
441         // property editor
442         if (getPropertyEditor() == null) {
443             setPropertyEditor(attributeDefinition.getPropertyEditor());
444         }
445     }
446 
447     /**
448      * {@inheritDoc}
449      */
450     @Override
451     public boolean isInputAllowed() {
452         return false;
453     }
454 
455     /**
456      * {@inheritDoc}
457      */
458     @Override
459     @BeanTagAttribute
460     public String getPropertyName() {
461         return this.propertyName;
462     }
463 
464     /**
465      * {@inheritDoc}
466      */
467     @Override
468     public void setPropertyName(String propertyName) {
469         this.propertyName = propertyName;
470     }
471 
472     /**
473      * {@inheritDoc}
474      */
475     @Override
476     @BeanTagAttribute
477     public PropertyEditor getPropertyEditor() {
478         return propertyEditor;
479     }
480 
481     /**
482      * {@inheritDoc}
483      */
484     @Override
485     public void setPropertyEditor(PropertyEditor propertyEditor) {
486         this.propertyEditor = propertyEditor;
487     }
488 
489     /**
490      * {@inheritDoc}
491      */
492     @Override
493     public void setPropertyEditorClass(Class<? extends PropertyEditor> propertyEditorClass) {
494         this.propertyEditor = KRADUtils.createNewObjectFromClass(propertyEditorClass);
495     }
496 
497     /**
498      * {@inheritDoc}
499      */
500     @Override
501     @BeanTagAttribute
502     public BindingInfo getBindingInfo() {
503         return this.bindingInfo;
504     }
505 
506     /**
507      * {@inheritDoc}
508      */
509     @Override
510     public void setBindingInfo(BindingInfo bindingInfo) {
511         this.bindingInfo = bindingInfo;
512     }
513 
514     /**
515      * {@inheritDoc}
516      */
517     @Override
518     public String getName() {
519         return this.getBindingInfo().getBindingPath();
520     }
521 
522     /**
523      * {@inheritDoc}
524      */
525     @Override
526     @BeanTagAttribute
527     public String getDictionaryAttributeName() {
528         return this.dictionaryAttributeName;
529     }
530 
531     /**
532      * {@inheritDoc}
533      */
534     @Override
535     public void setDictionaryAttributeName(String dictionaryAttributeName) {
536         this.dictionaryAttributeName = dictionaryAttributeName;
537     }
538 
539     /**
540      * {@inheritDoc}
541      */
542     @Override
543     @BeanTagAttribute
544     public String getDictionaryObjectEntry() {
545         return this.dictionaryObjectEntry;
546     }
547 
548     /**
549      * {@inheritDoc}
550      */
551     @Override
552     public void setDictionaryObjectEntry(String dictionaryObjectEntry) {
553         this.dictionaryObjectEntry = dictionaryObjectEntry;
554     }
555 
556     /**
557      * {@inheritDoc}
558      */
559     @Override
560     @BeanTagAttribute
561     public Object getDefaultValue() {
562         return this.defaultValue;
563     }
564 
565     /**
566      * {@inheritDoc}
567      */
568     @Override
569     public void setDefaultValue(Object defaultValue) {
570         this.defaultValue = defaultValue;
571     }
572 
573     /**
574      * {@inheritDoc}
575      */
576     @Override
577     @BeanTagAttribute
578     public Class<? extends ValueFinder> getDefaultValueFinderClass() {
579         return this.defaultValueFinderClass;
580     }
581 
582     /**
583      * {@inheritDoc}
584      */
585     @Override
586     public void setDefaultValueFinderClass(Class<? extends ValueFinder> defaultValueFinderClass) {
587         this.defaultValueFinderClass = defaultValueFinderClass;
588     }
589 
590     /**
591      * {@inheritDoc}
592      */
593     @Override
594     @BeanTagAttribute
595     public List<Object> getDefaultValues() {
596         return this.defaultValues;
597     }
598 
599     /**
600      * {@inheritDoc}
601      */
602     public void setDefaultValues(List<Object> defaultValues) {
603         this.defaultValues = defaultValues;
604     }
605 
606     /**
607      * {@inheritDoc}
608      */
609     @Override
610     public String getForcedValue() {
611         return forcedValue;
612     }
613 
614     /**
615      * {@inheritDoc}
616      */
617     @Override
618     public void setForcedValue(String forcedValue) {
619         this.forcedValue = forcedValue;
620     }
621 
622     /**
623      * {@inheritDoc}
624      */
625     @Override
626     @BeanTagAttribute
627     public String getHelpSummary() {
628         return this.help.getTooltipHelpContent();
629     }
630 
631     /**
632      * {@inheritDoc}
633      */
634     @Override
635     public void setHelpSummary(String helpSummary) {
636         this.help.setTooltipHelpContent(helpSummary);
637     }
638 
639     /**
640      * {@inheritDoc}
641      */
642     @Override
643     public DataFieldSecurity getDataFieldSecurity() {
644         return (DataFieldSecurity) super.getComponentSecurity();
645     }
646 
647     /**
648      * {@inheritDoc}
649      */
650     @Override
651     public void setComponentSecurity(ComponentSecurity componentSecurity) {
652         if ((componentSecurity != null) && !(componentSecurity instanceof DataFieldSecurity)) {
653             throw new RiceRuntimeException("Component security for DataField should be instance of DataFieldSecurity");
654         }
655 
656         super.setComponentSecurity(componentSecurity);
657     }
658 
659     /**
660      * @see org.kuali.rice.krad.uif.component.ComponentBase#initializeComponentSecurity()
661      */
662     @Override
663     protected void initializeComponentSecurity() {
664         if (getComponentSecurity() == null) {
665             setComponentSecurity(KRADUtils.createNewObjectFromClass(DataFieldSecurity.class));
666         }
667     }
668 
669     /**
670      * {@inheritDoc}
671      */
672     @Override
673     @BeanTagAttribute
674     public boolean isAddHiddenWhenReadOnly() {
675         return addHiddenWhenReadOnly;
676     }
677 
678     /**
679      * {@inheritDoc}
680      */
681     @Override
682     public void setAddHiddenWhenReadOnly(boolean addHiddenWhenReadOnly) {
683         this.addHiddenWhenReadOnly = addHiddenWhenReadOnly;
684     }
685 
686     /**
687      * {@inheritDoc}
688      */
689     @Override
690     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
691     public Inquiry getInquiry() {
692         return this.inquiry;
693     }
694 
695     /**
696      * {@inheritDoc}
697      */
698     @Override
699     public void setInquiry(Inquiry inquiry) {
700         this.inquiry = inquiry;
701     }
702 
703     /**
704      * {@inheritDoc}
705      */
706     @Override
707     public boolean isEnableAutoInquiry() {
708         return enableAutoInquiry;
709     }
710 
711     /**
712      * {@inheritDoc}
713      */
714     @Override
715     public void setEnableAutoInquiry(boolean enableAutoInquiry) {
716         this.enableAutoInquiry = enableAutoInquiry;
717     }
718 
719     /**
720      * {@inheritDoc}
721      */
722     @Override
723     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
724     public Help getHelp() {
725         return this.help;
726     }
727 
728     /**
729      * {@inheritDoc}
730      */
731     @Override
732     public void setHelp(Help help) {
733         this.help = help;
734     }
735 
736     /**
737      * {@inheritDoc}
738      */
739     @Override
740     @BeanTagAttribute
741     public boolean isRenderInfoMessageSpan() {
742         return renderInfoMessageSpan;
743     }
744 
745     /**
746      * {@inheritDoc}
747      */
748     @Override
749     public void setRenderInfoMessageSpan(boolean renderInfoMessageSpan) {
750         this.renderInfoMessageSpan = renderInfoMessageSpan;
751     }
752 
753     /**
754      * {@inheritDoc}
755      */
756     @Override
757     @BeanTagAttribute
758     public boolean isRenderMarkerIconSpan() {
759         return renderMarkerIconSpan;
760     }
761 
762     /**
763      * {@inheritDoc}
764      */
765     @Override
766     public void setRenderMarkerIconSpan(boolean renderMarkerIconSpan) {
767         this.renderMarkerIconSpan = renderMarkerIconSpan;
768     }
769 
770     /**
771      * {@inheritDoc}
772      */
773     @Override
774     public void setTooltipOfComponent(Tooltip tooltip) {
775         getFieldLabel().setToolTip(tooltip);
776     }
777 
778     /**
779      * {@inheritDoc}
780      */
781     @Override
782     public String getHelpTitle() {
783         return this.getLabel();
784     }
785 
786     /**
787      * {@inheritDoc}
788      */
789     @Override
790     @BeanTagAttribute
791     public String getReadOnlyDisplaySuffixPropertyName() {
792         return this.readOnlyDisplaySuffixPropertyName;
793     }
794 
795     /**
796      * {@inheritDoc}
797      */
798     @Override
799     public void setReadOnlyDisplaySuffixPropertyName(String readOnlyDisplaySuffixPropertyName) {
800         this.readOnlyDisplaySuffixPropertyName = readOnlyDisplaySuffixPropertyName;
801     }
802 
803     /**
804      * {@inheritDoc}
805      */
806     @Override
807     @BeanTagAttribute
808     public String getReadOnlyDisplayReplacementPropertyName() {
809         return this.readOnlyDisplayReplacementPropertyName;
810     }
811 
812     /**
813      * {@inheritDoc}
814      */
815     @Override
816     public void setReadOnlyDisplayReplacementPropertyName(String readOnlyDisplayReplacementPropertyName) {
817         this.readOnlyDisplayReplacementPropertyName = readOnlyDisplayReplacementPropertyName;
818     }
819 
820     /**
821      * {@inheritDoc}
822      */
823     @Override
824     @BeanTagAttribute
825     public String getReadOnlyDisplayReplacement() {
826         return readOnlyDisplayReplacement;
827     }
828 
829     /**
830      * {@inheritDoc}
831      */
832     @Override
833     public void setReadOnlyDisplayReplacement(String value) {
834         this.readOnlyDisplayReplacement = value;
835     }
836 
837     /**
838      * {@inheritDoc}
839      */
840     @Override
841     @BeanTagAttribute
842     public String getReadOnlyDisplaySuffix() {
843         return readOnlyDisplaySuffix;
844     }
845 
846     /**
847      * {@inheritDoc}
848      */
849     @Override
850     public void setReadOnlyDisplaySuffix(String value) {
851         this.readOnlyDisplaySuffix = value;
852     }
853 
854     /**
855      * {@inheritDoc}
856      */
857     @Override
858     @BeanTagAttribute
859     public String getReadOnlyListDisplayType() {
860         return readOnlyListDisplayType;
861     }
862 
863     /**
864      * {@inheritDoc}
865      */
866     @Override
867     public void setReadOnlyListDisplayType(String readOnlyListDisplayType) {
868         this.readOnlyListDisplayType = readOnlyListDisplayType;
869     }
870 
871     /**
872      * {@inheritDoc}
873      */
874     @Override
875     @BeanTagAttribute
876     public String getReadOnlyListDelimiter() {
877         return readOnlyListDelimiter;
878     }
879 
880     /**
881      * {@inheritDoc}
882      */
883     @Override
884     public void setReadOnlyListDelimiter(String readOnlyListDelimiter) {
885         this.readOnlyListDelimiter = readOnlyListDelimiter;
886     }
887 
888     /**
889      * {@inheritDoc}
890      */
891     @Override
892     @BeanTagAttribute
893     public boolean isApplyMask() {
894         return applyMask;
895     }
896 
897     /**
898      * {@inheritDoc}
899      */
900     @Override
901     public void setApplyMask(boolean applyMask) {
902         this.applyMask = applyMask;
903     }
904 
905     /**
906      * {@inheritDoc}
907      */
908     @Override
909     @BeanTagAttribute
910     public MaskFormatter getMaskFormatter() {
911         return maskFormatter;
912     }
913 
914     /**
915      * {@inheritDoc}
916      */
917     @Override
918     public void setMaskFormatter(MaskFormatter maskFormatter) {
919         this.maskFormatter = maskFormatter;
920     }
921 
922     /**
923      * {@inheritDoc}
924      */
925     @Override
926     @BeanTagAttribute
927     public List<String> getAdditionalHiddenPropertyNames() {
928         if (additionalHiddenPropertyNames == Collections.EMPTY_LIST && isMutable(true)) {
929             additionalHiddenPropertyNames = new LifecycleAwareList<String>(this);
930         }
931         
932         return additionalHiddenPropertyNames;
933     }
934 
935     /**
936      * {@inheritDoc}
937      */
938     @Override
939     public void setAdditionalHiddenPropertyNames(List<String> additionalHiddenPropertyNames) {
940         if (additionalHiddenPropertyNames == null) {
941             this.additionalHiddenPropertyNames = Collections.emptyList();
942         } else {
943             this.additionalHiddenPropertyNames =
944                     new LifecycleAwareList<String>(this, additionalHiddenPropertyNames);
945         }
946     }
947 
948     /**
949      * {@inheritDoc}
950      */
951     @Override
952     @BeanTagAttribute
953     public List<String> getPropertyNamesForAdditionalDisplay() {
954         if (propertyNamesForAdditionalDisplay == Collections.EMPTY_LIST && isMutable(true)) {
955             propertyNamesForAdditionalDisplay = new LifecycleAwareList<String>(this);
956         }
957         
958         return propertyNamesForAdditionalDisplay;
959     }
960 
961     /**
962      * {@inheritDoc}
963      */
964     @Override
965     public void setPropertyNamesForAdditionalDisplay(List<String> propertyNamesForAdditionalDisplay) {
966         if (propertyNamesForAdditionalDisplay == null) {
967             this.propertyNamesForAdditionalDisplay = Collections.emptyList();
968         } else {
969             this.propertyNamesForAdditionalDisplay =
970                     new LifecycleAwareList<String>(this, propertyNamesForAdditionalDisplay);
971         }
972     }
973     
974     /**
975      * {@inheritDoc}
976      */
977     @Override
978     @BeanTagAttribute
979     public boolean isEscapeHtmlInPropertyValue() {
980         return this.escapeHtmlInPropertyValue;
981     }
982 
983     /**
984      * {@inheritDoc}
985      */
986     @Override
987     public void setEscapeHtmlInPropertyValue(boolean escapeHtmlInPropertyValue) {
988         this.escapeHtmlInPropertyValue = escapeHtmlInPropertyValue;
989     }
990 
991     /**
992      * {@inheritDoc}
993      */
994     @Override
995     @BeanTagAttribute
996     public boolean isMultiLineReadOnlyDisplay() {
997         return multiLineReadOnlyDisplay;
998     }
999 
1000     /**
1001      * {@inheritDoc}
1002      */
1003     @Override
1004     public void setMultiLineReadOnlyDisplay(boolean multiLineReadOnlyDisplay) {
1005         this.multiLineReadOnlyDisplay = multiLineReadOnlyDisplay;
1006     }
1007 
1008     /**
1009      * {@inheritDoc}
1010      */
1011     @Override
1012     public boolean hasSecureValue() {
1013         boolean hasHideAuthz = false;
1014 
1015         if (getDataFieldSecurity() != null) {
1016             boolean isViewAuthz = false;
1017             boolean isViewInLineAuthz = false;
1018             boolean isHide = false;
1019 
1020             if (getDataFieldSecurity().isViewAuthz() != null) {
1021                 isViewAuthz = getDataFieldSecurity().isViewAuthz().booleanValue();
1022             }
1023 
1024             if (getDataFieldSecurity().isViewInLineAuthz() != null) {
1025                 isViewInLineAuthz = getDataFieldSecurity().isViewInLineAuthz().booleanValue();
1026             }
1027 
1028             if (getDataFieldSecurity().getAttributeSecurity() != null) {
1029                 isHide = getDataFieldSecurity().getAttributeSecurity().isHide();
1030             }
1031 
1032             hasHideAuthz = isViewAuthz || isViewInLineAuthz || isHide;
1033         }
1034 
1035         return isApplyMask() || (hasHideAuthz && isHidden());
1036     }
1037 
1038     /**
1039      * {@inheritDoc}
1040      */
1041     @Override
1042     public boolean isRenderFieldset() {
1043         return (!Boolean.TRUE.equals(this.getReadOnly())
1044                 && inquiry != null
1045                 && inquiry.isRender()
1046                 && inquiry.getInquiryLink() != null
1047                 && inquiry.getInquiryLink().isRender()) || (help != null
1048                 && help.isRender()
1049                 && help.getHelpAction() != null
1050                 && help.getHelpAction().isRender());
1051     }
1052 
1053     /**
1054      * {@inheritDoc}
1055      */
1056     @Override
1057     @BeanTagAttribute(name = "sortAs")
1058     public String getSortAs() {
1059         return sortAs;
1060     }
1061 
1062     /**
1063      * {@inheritDoc}
1064      */
1065     @Override
1066     public void setSortAs(String sortAs) {
1067         if (!(sortAs.equals(UifConstants.TableToolsValues.CURRENCY) || sortAs.equals(UifConstants.TableToolsValues.DATE) || sortAs.equals(UifConstants.TableToolsValues.NUMERIC) || sortAs.equals(UifConstants.TableToolsValues.STRING))) {
1068             throw new IllegalArgumentException("invalid sortAs value of " + sortAs + ", allowed: " + UifConstants.TableToolsValues.CURRENCY + "|" + UifConstants.TableToolsValues.DATE + "|" + UifConstants.TableToolsValues.NUMERIC + "|" + UifConstants.TableToolsValues.STRING);
1069         }
1070         this.sortAs = sortAs;
1071     }
1072 
1073     /**
1074      * {@inheritDoc}
1075      */
1076     @Override
1077     public void completeValidation(ValidationTrace tracer) {
1078         tracer.addBean(this);
1079 
1080         // Checks that the property is connected to the field
1081         if (getPropertyName() == null) {
1082             if (!Validator.checkExpressions(this, "propertyName")) {
1083                 String currentValues[] = {"propertyName = " + getPropertyName()};
1084                 tracer.createError("Property name not set", currentValues);
1085             }
1086         }
1087 
1088         // Checks that the default values  present
1089 /*        if (getDefaultValue() != null && getDefaultValues() != null) {
1090             String currentValues[] =
1091                     {"defaultValue =" + getDefaultValue(), "defaultValues Size =" + getDefaultValues().length};
1092             tracer.createWarning("Both Default Value and Default Values set", currentValues);
1093         }*/
1094 
1095         // Checks that a mask formatter is set if the data field is to be masked
1096         if (isApplyMask()) {
1097             if (maskFormatter == null) {
1098                 String currentValues[] = {"applyMask =" + isApplyMask(), "maskFormatter =" + maskFormatter};
1099                 tracer.createWarning("Apply mask is true, but no value is set for maskFormatter", currentValues);
1100             }
1101         }
1102 
1103         super.completeValidation(tracer.getCopy());
1104     }
1105 
1106     /**
1107      * Determines whether or not to create an automatic inquiry widget for this field within the current lifecycle.
1108      * 
1109      * @return True if an automatic inquiry widget should be created for this field on the current lifecycle.
1110      */
1111     protected boolean hasAutoInquiryRelationship() {
1112         if (getBindingInfo() == null) {
1113             return false;
1114         }
1115 
1116         View view = ViewLifecycle.getView();
1117         Object model = ViewLifecycle.getModel();
1118         Object object = ViewModelUtils.getParentObjectForMetadata(view, model, this);
1119 
1120         // Do checks for inquiry when read only
1121         if (Boolean.TRUE.equals(getReadOnly())) {
1122             String bindingPath = getBindingInfo().getBindingPath();
1123 
1124             if (StringUtils.isBlank(bindingPath) || bindingPath.equals("null")) {
1125                 return false;
1126             }
1127 
1128             Object propertyValue;
1129 
1130             // do not render the inquiry if the field value is null
1131             try {
1132                 propertyValue = ObjectPropertyUtils.getPropertyValue(model, bindingPath);
1133 
1134                 if ((propertyValue == null) || StringUtils.isBlank(propertyValue.toString())) {
1135                     return false;
1136                 }
1137             } catch (Exception e) {
1138                 // if we can't get the value just swallow the exception and don't set an inquiry
1139                 return false;
1140             }
1141 
1142             // do not render the inquiry link if it is the same as its parent
1143             if (view.getViewTypeName() == UifConstants.ViewType.INQUIRY) {
1144                 InquiryForm inquiryForm = (InquiryForm) model;
1145                 String[] parameterValues = inquiryForm.getInitialRequestParameters().get(propertyName);
1146 
1147                 // do not render the inquiry if current data object is the same as its parent and the request parameter
1148                 // contains the parent field value
1149                 if (StringUtils.equals(inquiryForm.getDataObjectClassName(), dictionaryObjectEntry)
1150                         && ArrayUtils.contains(parameterValues, propertyValue.toString())) {
1151                     return false;
1152                 }
1153             }
1154         }
1155 
1156         return object != null && KRADServiceLocatorWeb.getViewDictionaryService().isInquirable(object.getClass());
1157     }
1158 
1159 }