001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.uif.field;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.exception.RiceRuntimeException;
020import org.kuali.rice.krad.bo.DataObjectRelationship;
021import org.kuali.rice.krad.bo.KualiCode;
022import org.kuali.rice.krad.datadictionary.AttributeDefinition;
023import org.kuali.rice.krad.datadictionary.mask.MaskFormatter;
024import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
025import org.kuali.rice.krad.uif.component.BindingInfo;
026import org.kuali.rice.krad.uif.component.Component;
027import org.kuali.rice.krad.uif.component.ComponentSecurity;
028import org.kuali.rice.krad.uif.component.DataBinding;
029import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
030import org.kuali.rice.krad.uif.util.ViewModelUtils;
031import org.kuali.rice.krad.uif.view.View;
032import org.kuali.rice.krad.uif.widget.Inquiry;
033import org.kuali.rice.krad.util.KRADPropertyConstants;
034import org.kuali.rice.krad.util.ObjectUtils;
035import org.kuali.rice.krad.valuefinder.ValueFinder;
036
037import java.beans.PropertyEditor;
038import java.util.ArrayList;
039import java.util.List;
040
041/**
042 * Field that renders data from the application, such as the value of a data object property
043 *
044 * @author Kuali Rice Team (rice.collab@kuali.org)
045 */
046public class DataField extends FieldBase implements DataBinding {
047    private static final long serialVersionUID = -4129678891948564724L;
048
049    // binding
050    private String propertyName;
051    private BindingInfo bindingInfo;
052
053    private String dictionaryAttributeName;
054    private String dictionaryObjectEntry;
055
056    // value props
057    private String defaultValue;
058    private Class<? extends ValueFinder> defaultValueFinderClass;
059
060    private PropertyEditor propertyEditor;
061
062    private boolean readOnlyHidden;
063
064    // alternate and additional display properties
065    protected String alternateDisplayPropertyName;
066    protected String additionalDisplayPropertyName;
067
068    private String alternateDisplayValue;
069    private String additionalDisplayValue;
070
071    private boolean applyValueMask;
072    private MaskFormatter maskFormatter;
073
074    private List<String> hiddenPropertyNames;
075    private List<String> informationalDisplayPropertyNames;
076
077    private boolean escapeHtmlInPropertyValue = true;
078
079    private String helpSummary;
080    private String helpDescription;
081
082    // widgets
083    private Inquiry fieldInquiry;
084
085    public DataField() {
086        super();
087
088        readOnlyHidden = false;
089        applyValueMask = false;
090
091        hiddenPropertyNames = new ArrayList<String>();
092        informationalDisplayPropertyNames = new ArrayList<String>();
093    }
094
095    /**
096     * The following initialization is performed:
097     *
098     * <ul>
099     * <li>Set defaults for binding</li>
100     * <li>Default the model path if not set</li>
101     * </ul>
102     *
103     * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View,
104     *      java.lang.Object)
105     */
106    @Override
107    public void performInitialization(View view, Object model) {
108        super.performInitialization(view, model);
109
110        if (bindingInfo != null) {
111            bindingInfo.setDefaults(view, getPropertyName());
112        }
113    }
114
115    /**
116     * The following updates are done here:
117     *
118     * <ul>
119     * <li>If readOnlyHidden set to true, set field to readonly and add to hidden property names</li>
120     * </ul>
121     */
122    public void performApplyModel(View view, Object model, Component parent) {
123        super.performApplyModel(view, model, parent);
124
125        if (isReadOnlyHidden()) {
126            setReadOnly(true);
127            getHiddenPropertyNames().add(getPropertyName());
128        }
129    }
130
131    /**
132     * The following actions are performed:
133     *
134     * <ul>
135     * <li>Set the ids for the various attribute components</li>
136     * <li>Sets up the client side validation for constraints on this field. In
137     * addition, it sets up the messages applied to this field</li>
138     * </ul>
139     *
140     * @see org.kuali.rice.krad.uif.component.ComponentBase#performFinalize(org.kuali.rice.krad.uif.view.View,
141     *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
142     */
143    @Override
144    public void performFinalize(View view, Object model, Component parent) {
145        super.performFinalize(view, model, parent);
146
147        // adjust the path for hidden fields
148        // TODO: should this check the view#readOnly?
149        List<String> hiddenPropertyPaths = new ArrayList<String>();
150        for (String hiddenPropertyName : getHiddenPropertyNames()) {
151            String hiddenPropertyPath = getBindingInfo().getPropertyAdjustedBindingPath(hiddenPropertyName);
152            hiddenPropertyPaths.add(hiddenPropertyPath);
153        }
154        this.hiddenPropertyNames = hiddenPropertyPaths;
155
156        // adjust paths on informational property names
157        List<String> informationalPropertyPaths = new ArrayList<String>();
158        for (String infoPropertyName : getInformationalDisplayPropertyNames()) {
159            String infoPropertyPath = getBindingInfo().getPropertyAdjustedBindingPath(infoPropertyName);
160            informationalPropertyPaths.add(infoPropertyPath);
161        }
162        this.informationalDisplayPropertyNames = informationalPropertyPaths;
163
164        // Additional and Alternate display value
165        setAlternateAndAdditionalDisplayValue(view, model);
166    }
167
168    /**
169     * Sets alternate and additional property value for this field.
170     *
171     * <p>
172     * If <code>AttributeSecurity</code> present in this field, make sure the current user has permission to view the
173     * field value. If user doesn't have permission to view the value, mask the value as configured and set it
174     * as alternate value for display. If security doesn't exists for this field but
175     * <code>alternateDisplayPropertyName</code> present, get its value and format it based on that
176     * fields formatting and set for display.
177     * </p>
178     *
179     * <p>
180     * For additional display value, if <code>AttributeSecurity</code> not present, sets the value if
181     * <code>additionalDisplayPropertyName</code> present. If not present, check whether this field is a
182     * <code>KualiCode</code> and get the relationship configured in the datadictionary file and set the name
183     * additional display value which will be displayed along with the code. If additional display property not
184     * present, check whether this field is has <code>MultiValueControlBase</code>. If yes, get the Label
185     * for the value and set it as additional display value.
186     * </p>
187     *
188     * @param view - the current view instance
189     * @param model - model instance
190     */
191    protected void setAlternateAndAdditionalDisplayValue(View view, Object model) {
192        // if alternate or additional display values set don't use property names
193        if (StringUtils.isNotBlank(alternateDisplayValue) || StringUtils.isNotBlank(additionalDisplayValue)) {
194            return;
195        }
196
197        // check whether field value needs to be masked, and if so apply masking as alternateDisplayValue
198        if (isApplyValueMask()) {
199            Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, getBindingInfo().getBindingPath());
200            alternateDisplayValue = getMaskFormatter().maskValue(fieldValue);
201
202            // mask values are forced to be readonly
203            setReadOnly(true);
204            return;
205        }
206
207        // if not read only, return without trying to set alternate and additional values
208        if (!isReadOnly()) {
209            return;
210        }
211
212        // if field is not secure, check for alternate and additional display properties
213        if (StringUtils.isNotBlank(getAlternateDisplayPropertyName())) {
214            String alternateDisplayPropertyPath = getBindingInfo().getPropertyAdjustedBindingPath(
215                    getAlternateDisplayPropertyName());
216
217            Object alternateFieldValue = ObjectPropertyUtils.getPropertyValue(model, alternateDisplayPropertyPath);
218            if (alternateFieldValue != null) {
219                // TODO: format by type
220                alternateDisplayValue = alternateFieldValue.toString();
221            }
222        }
223
224        // perform automatic translation for code references if enabled on view
225        if (StringUtils.isBlank(getAdditionalDisplayPropertyName()) && view.isTranslateCodes()) {
226            // check for any relationship present for this field and it's of type KualiCode
227            Class<?> parentObjectClass = ViewModelUtils.getParentObjectClassForMetadata(view, model, this);
228            DataObjectRelationship relationship =
229                    KRADServiceLocatorWeb.getDataObjectMetaDataService().getDataObjectRelationship(null,
230                            parentObjectClass, getBindingInfo().getBindingName(), "", true, false, false);
231
232            if (relationship != null
233                    && getPropertyName().startsWith(relationship.getParentAttributeName())
234                    && KualiCode.class.isAssignableFrom(relationship.getRelatedClass())) {
235                additionalDisplayPropertyName =
236                        relationship.getParentAttributeName() + "." + KRADPropertyConstants.NAME;
237            }
238        }
239
240        // now check for an additional display property and if set get the value
241        if (StringUtils.isNotBlank(getAdditionalDisplayPropertyName())) {
242            String additionalDisplayPropertyPath = getBindingInfo().getPropertyAdjustedBindingPath(
243                    getAdditionalDisplayPropertyName());
244
245            Object additionalFieldValue = ObjectPropertyUtils.getPropertyValue(model, additionalDisplayPropertyPath);
246            if (additionalFieldValue != null) {
247                // TODO: format by type
248                additionalDisplayValue = additionalFieldValue.toString();
249            }
250        }
251    }
252
253    /**
254     * Defaults the properties of the <code>DataField</code> to the
255     * corresponding properties of its <code>AttributeDefinition</code>
256     * retrieved from the dictionary (if such an entry exists). If the field
257     * already contains a value for a property, the definitions value is not
258     * used.
259     *
260     * @param view - view instance the field belongs to
261     * @param attributeDefinition - AttributeDefinition instance the property values should be
262     * copied from
263     */
264    public void copyFromAttributeDefinition(View view, AttributeDefinition attributeDefinition) {
265        // label
266        if (StringUtils.isEmpty(getLabel())) {
267            setLabel(attributeDefinition.getLabel());
268        }
269
270        // short label
271        if (StringUtils.isEmpty(getShortLabel())) {
272            setShortLabel(attributeDefinition.getShortLabel());
273        }
274
275        // summary
276        if (StringUtils.isEmpty(getHelpSummary())) {
277            setHelpSummary(attributeDefinition.getSummary());
278        }
279
280        // description
281        if (StringUtils.isEmpty(getHelpDescription())) {
282            setHelpDescription(attributeDefinition.getDescription());
283        }
284
285        // security
286        if (getDataFieldSecurity().getAttributeSecurity() == null) {
287            getDataFieldSecurity().setAttributeSecurity(attributeDefinition.getAttributeSecurity());
288        }
289
290        // alternate property name
291        if (getAlternateDisplayPropertyName() == null && StringUtils.isNotBlank(
292                attributeDefinition.getAlternateDisplayAttributeName())) {
293            setAlternateDisplayPropertyName(attributeDefinition.getAlternateDisplayAttributeName());
294        }
295
296        // additional property display name
297        if (getAdditionalDisplayPropertyName() == null && StringUtils.isNotBlank(
298                attributeDefinition.getAdditionalDisplayAttributeName())) {
299            setAdditionalDisplayPropertyName(attributeDefinition.getAdditionalDisplayAttributeName());
300        }
301
302        // property editor
303        if (getPropertyEditor() == null) {
304            setPropertyEditor(attributeDefinition.getPropertyEditor());
305        }
306    }
307
308    /**
309     * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
310     */
311    @Override
312    public List<Component> getComponentsForLifecycle() {
313        List<Component> components = super.getComponentsForLifecycle();
314
315        components.add(fieldInquiry);
316
317        return components;
318    }
319
320    /**
321     * Indicates whether the data field instance allows input, subclasses should override and set to
322     * true if input is allowed
323     *
324     * @return boolean true if input is allowed, false if read only
325     */
326    public boolean isInputAllowed() {
327        return false;
328    }
329
330    /**
331     * @see org.kuali.rice.krad.uif.component.DataBinding#getPropertyName()
332     */
333    public String getPropertyName() {
334        return this.propertyName;
335    }
336
337    /**
338     * Setter for the component's property name
339     *
340     * @param propertyName
341     */
342    public void setPropertyName(String propertyName) {
343        this.propertyName = propertyName;
344    }
345
346    /**
347     * Performs formatting of the field value for display and then converting the value back to its
348     * expected type from a string
349     *
350     * <p>
351     * Note property editors exist and are already registered for the basic Java types and the
352     * common Kuali types such as [@link KualiDecimal}. Registration with this property is only
353     * needed for custom property editors
354     * </p>
355     *
356     * @return PropertyEditor property editor instance to use for this field
357     */
358    public PropertyEditor getPropertyEditor() {
359        return propertyEditor;
360    }
361
362    /**
363     * Setter for the custom property editor to use for the field
364     *
365     * @param propertyEditor
366     */
367    public void setPropertyEditor(PropertyEditor propertyEditor) {
368        this.propertyEditor = propertyEditor;
369    }
370
371    /**
372     * Convenience setter for configuring a property editor by class
373     *
374     * @param propertyEditorClass
375     */
376    public void setPropertyEditorClass(Class<? extends PropertyEditor> propertyEditorClass) {
377        this.propertyEditor = ObjectUtils.newInstance(propertyEditorClass);
378    }
379
380    /**
381     * @see org.kuali.rice.krad.uif.component.DataBinding#getBindingInfo()
382     */
383    public BindingInfo getBindingInfo() {
384        return this.bindingInfo;
385    }
386
387    /**
388     * Setter for the field's binding info
389     *
390     * @param bindingInfo
391     */
392    public void setBindingInfo(BindingInfo bindingInfo) {
393        this.bindingInfo = bindingInfo;
394    }
395
396    /**
397     * Name of the attribute within the data dictionary the attribute field is
398     * associated with
399     *
400     * <p>
401     * During the initialize phase for the <code>View</code>, properties for
402     * attribute fields are defaulted from a corresponding
403     * <code>AttributeDefinition</code> in the data dictionary. Based on the
404     * propertyName and parent object class the framework attempts will
405     * determine the attribute definition that is associated with the field and
406     * set this property. However this property can also be set in the fields
407     * configuration to use another dictionary attribute.
408     * </p>
409     *
410     * <p>
411     * The attribute name is used along with the dictionary object entry to find
412     * the <code>AttributeDefinition</code>
413     * </p>
414     *
415     * @return String attribute name
416     */
417    public String getDictionaryAttributeName() {
418        return this.dictionaryAttributeName;
419    }
420
421    /**
422     * Setter for the dictionary attribute name
423     *
424     * @param dictionaryAttributeName
425     */
426    public void setDictionaryAttributeName(String dictionaryAttributeName) {
427        this.dictionaryAttributeName = dictionaryAttributeName;
428    }
429
430    /**
431     * Object entry name in the data dictionary the associated attribute is
432     * apart of
433     *
434     * <p>
435     * During the initialize phase for the <code>View</code>, properties for
436     * attribute fields are defaulted from a corresponding
437     * <code>AttributeDefinition</code> in the data dictionary. Based on the
438     * parent object class the framework will determine the object entry for the
439     * associated attribute. However the object entry can be set in the field's
440     * configuration to use another object entry for the attribute
441     * </p>
442     *
443     * <p>
444     * The attribute name is used along with the dictionary object entry to find
445     * the <code>AttributeDefinition</code>
446     * </p>
447     *
448     * @return
449     */
450    public String getDictionaryObjectEntry() {
451        return this.dictionaryObjectEntry;
452    }
453
454    /**
455     * Setter for the dictionary object entry
456     *
457     * @param dictionaryObjectEntry
458     */
459    public void setDictionaryObjectEntry(String dictionaryObjectEntry) {
460        this.dictionaryObjectEntry = dictionaryObjectEntry;
461    }
462
463    /**
464     * Default value for the model property the field points to
465     *
466     * <p>
467     * When a new <code>View</code> instance is requested, the corresponding
468     * model will be newly created. During this initialization process the value
469     * for the model property will be set to the given default value (if set)
470     * </p>
471     *
472     * @return String default value
473     */
474    public String getDefaultValue() {
475        return this.defaultValue;
476    }
477
478    /**
479     * Setter for the fields default value
480     *
481     * @param defaultValue
482     */
483    public void setDefaultValue(String defaultValue) {
484        this.defaultValue = defaultValue;
485    }
486
487    /**
488     * Gives Class that should be invoked to produce the default value for the
489     * field
490     *
491     * @return Class<? extends ValueFinder> default value finder class
492     */
493    public Class<? extends ValueFinder> getDefaultValueFinderClass() {
494        return this.defaultValueFinderClass;
495    }
496
497    /**
498     * Setter for the default value finder class
499     *
500     * @param defaultValueFinderClass
501     */
502    public void setDefaultValueFinderClass(Class<? extends ValueFinder> defaultValueFinderClass) {
503        this.defaultValueFinderClass = defaultValueFinderClass;
504    }
505
506    /**
507     * Summary help text for the field
508     *
509     * @return String summary help text
510     */
511    public String getHelpSummary() {
512        return helpSummary;
513    }
514
515    /**
516     * Setter for the summary help text
517     *
518     * @param helpSummary
519     */
520    public void setHelpSummary(String helpSummary) {
521        this.helpSummary = helpSummary;
522    }
523
524    /**
525     * Full help information text for the field
526     *
527     * @return String help description text
528     */
529    public String getHelpDescription() {
530        return this.helpDescription;
531    }
532
533    /**
534     * Setter for the help description text
535     *
536     * @param helpDescription
537     */
538    public void setHelpDescription(String helpDescription) {
539        this.helpDescription = helpDescription;
540    }
541
542    /**
543     * Data Field Security object that indicates what authorization (permissions) exist for the field
544     *
545     * @return DataFieldSecurity instance
546     */
547    public DataFieldSecurity getDataFieldSecurity() {
548        return (DataFieldSecurity) super.getComponentSecurity();
549    }
550
551    /**
552     * Override to assert a {@link DataFieldSecurity} instance is set
553     *
554     * @param componentSecurity - instance of DataFieldSecurity
555     */
556    @Override
557    public void setComponentSecurity(ComponentSecurity componentSecurity) {
558        if (!(componentSecurity instanceof DataFieldSecurity)) {
559            throw new RiceRuntimeException("Component security for DataField should be instance of DataFieldSecurity");
560        }
561
562        super.setComponentSecurity(componentSecurity);
563    }
564
565    @Override
566    protected Class<? extends ComponentSecurity> getComponentSecurityClass() {
567        return DataFieldSecurity.class;
568    }
569
570    /**
571     * Indicates the field should be read-only but also a hidden should be generated for the field
572     *
573     * <p>
574     * Useful for when a value is just displayed but is needed by script
575     * </p>
576     *
577     * @return boolean true if field should be readOnly hidden, false if not
578     */
579    public boolean isReadOnlyHidden() {
580        return readOnlyHidden;
581    }
582
583    /**
584     * Setter for the read-only hidden indicator
585     *
586     * @param readOnlyHidden
587     */
588    public void setReadOnlyHidden(boolean readOnlyHidden) {
589        this.readOnlyHidden = readOnlyHidden;
590    }
591
592    /**
593     * Inquiry widget for the field
594     *
595     * <p>
596     * The inquiry widget will render a link for the field value when read-only
597     * that points to the associated inquiry view for the field. The inquiry can
598     * be configured to point to a certain <code>InquiryView</code>, or the
599     * framework will attempt to associate the field with a inquiry based on its
600     * metadata (in particular its relationships in the model)
601     * </p>
602     *
603     * @return Inquiry field inquiry
604     */
605    public Inquiry getFieldInquiry() {
606        return this.fieldInquiry;
607    }
608
609    /**
610     * Setter for the inquiry widget
611     *
612     * @param fieldInquiry
613     */
614    public void setFieldInquiry(Inquiry fieldInquiry) {
615        this.fieldInquiry = fieldInquiry;
616    }
617
618    /**
619     * Additional display attribute name, which will be displayed next to the actual field value
620     * when the field is readonly with hypen inbetween like PropertyValue - AdditionalPropertyValue
621     *
622     * @param additionalDisplayPropertyName - Name of the additional display property
623     */
624    public void setAdditionalDisplayPropertyName(String additionalDisplayPropertyName) {
625        this.additionalDisplayPropertyName = additionalDisplayPropertyName;
626    }
627
628    /**
629     * Returns the additional display attribute name to be displayed when the field is readonly
630     *
631     * @return Additional Display Attribute Name
632     */
633    public String getAdditionalDisplayPropertyName() {
634        return this.additionalDisplayPropertyName;
635    }
636
637    /**
638     * Sets the alternate display attribute name to be displayed when the field is readonly.
639     * This properties value will be displayed instead of actual fields value when the field is readonly.
640     *
641     * @param alternateDisplayPropertyName - alternate display property name
642     */
643    public void setAlternateDisplayPropertyName(String alternateDisplayPropertyName) {
644        this.alternateDisplayPropertyName = alternateDisplayPropertyName;
645    }
646
647    /**
648     * Returns the alternate display attribute name to be displayed when the field is readonly.
649     *
650     * @return alternate Display Property Name
651     */
652    public String getAlternateDisplayPropertyName() {
653        return this.alternateDisplayPropertyName;
654    }
655
656    /**
657     * Returns the alternate display value
658     *
659     * @return the alternate display value set for this field
660     */
661    public String getAlternateDisplayValue() {
662        return alternateDisplayValue;
663    }
664
665    /**
666     * Setter for the alternative display value
667     *
668     * @param value
669     */
670    public void setAlternateDisplayValue(String value) {
671        this.alternateDisplayValue = value;
672    }
673
674    /**
675     * Returns the additional display value.
676     *
677     * @return the additional display value set for this field
678     */
679    public String getAdditionalDisplayValue() {
680        return additionalDisplayValue;
681    }
682
683    /**
684     * Setter for the additional display value
685     *
686     * @param value
687     */
688    public void setAdditionalDisplayValue(String value) {
689        this.additionalDisplayValue = value;
690    }
691
692    /**
693     * Indicates whether the value for the field should be masked (or partially masked) on display
694     *
695     * <p>
696     * If set to true, the field value will be masked by applying the configured {@link #getMaskFormatter()}
697     * </p>
698     *
699     * <p>
700     * If a KIM permission exists that should be checked to determine whether the value should be masked or not,
701     * this value should not be set but instead the mask or partialMask property on {@link #getComponentSecurity()}
702     * should be set to true. This indicates there is a mask permission that should be consulted. If the user
703     * does not have the permission, this flag will be set to true by the framework and the value masked using
704     * the mask formatter configured on the security object
705     * </p>
706     *
707     * @return boolean true if the field value should be masked, false if not
708     */
709    public boolean isApplyValueMask() {
710        return applyValueMask;
711    }
712
713    /**
714     * Setter for the apply value mask flag
715     *
716     * @param applyValueMask
717     */
718    public void setApplyValueMask(boolean applyValueMask) {
719        this.applyValueMask = applyValueMask;
720    }
721
722    /**
723     * MaskFormatter instance that will be used to mask the field value when {@link #isApplyValueMask()} is true
724     *
725     * <p>
726     * Note in cases where the mask is applied due to security (KIM permissions), the mask or partial mask formatter
727     * configured on {@link #getComponentSecurity()} will be used instead of this mask formatter
728     * </p>
729     *
730     * @return MaskFormatter instance
731     */
732    public MaskFormatter getMaskFormatter() {
733        return maskFormatter;
734    }
735
736    /**
737     * Setter for the MaskFormatter instance to apply when the value is masked
738     *
739     * @param maskFormatter
740     */
741    public void setMaskFormatter(MaskFormatter maskFormatter) {
742        this.maskFormatter = maskFormatter;
743    }
744
745    /**
746     * Allows specifying hidden property names without having to specify as a
747     * field in the group config (that might impact layout)
748     *
749     * @return List<String> hidden property names
750     */
751    public List<String> getHiddenPropertyNames() {
752        return hiddenPropertyNames;
753    }
754
755    /**
756     * Setter for the hidden property names
757     *
758     * @param hiddenPropertyNames
759     */
760    public void setHiddenPropertyNames(List<String> hiddenPropertyNames) {
761        this.hiddenPropertyNames = hiddenPropertyNames;
762    }
763
764    /**
765     * List of property names whose values should be displayed read-only under this field
766     *
767     * <p>
768     * In the attribute field template for each information property name given its values is
769     * outputted read-only. Informational property values can also be updated dynamically with
770     * the use of field attribute query
771     * </p>
772     *
773     * <p>
774     * Simple property names can be given if the property has the same binding parent as this
775     * field, in which case the binding path will be adjusted by the framework. If the property
776     * names starts with org.kuali.rice.krad.uif.UifConstants#NO_BIND_ADJUST_PREFIX, no binding
777     * prefix will be added.
778     * </p>
779     *
780     * @return List<String> informational property names
781     */
782    public List<String> getInformationalDisplayPropertyNames() {
783        return informationalDisplayPropertyNames;
784    }
785
786    /**
787     * Setter for the list of informational property names
788     *
789     * @param informationalDisplayPropertyNames
790     */
791    public void setInformationalDisplayPropertyNames(List<String> informationalDisplayPropertyNames) {
792        this.informationalDisplayPropertyNames = informationalDisplayPropertyNames;
793    }
794
795    /**
796     * Sets HTML escaping for this property value. HTML escaping will be handled in alternate and additional fields
797     * also.
798     */
799    public void setEscapeHtmlInPropertyValue(boolean escapeHtmlInPropertyValue) {
800        this.escapeHtmlInPropertyValue = escapeHtmlInPropertyValue;
801    }
802
803    /**
804     * Returns true if HTML escape allowed for this field
805     *
806     * @return true if escaping allowed
807     */
808    public boolean isEscapeHtmlInPropertyValue() {
809        return this.escapeHtmlInPropertyValue;
810    }
811
812    /**
813     * Indicates whether the value for the field is secure
814     *
815     * <p>
816     * A value will be secured if masking has been applied (by configuration or a failed KIM permission) or the field
817     * has been marked as hidden due to an authorization check
818     * </p>
819     *
820     * @return boolean true if value is secure, false if not
821     */
822    public boolean hasSecureValue() {
823        return isApplyValueMask() || ((getComponentSecurity().isViewAuthz()
824                || getDataFieldSecurity().isViewInLineAuthz()
825                || ((getDataFieldSecurity().getAttributeSecurity() != null) && getDataFieldSecurity()
826                .getAttributeSecurity().isHide())) && isHidden());
827    }
828
829}