org.kuali.rice.krad.uif.field.DataField.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.rice.krad.uif.field.DataField.java

Source

/**
 * Copyright 2005-2013 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.rice.krad.uif.field;

import org.apache.commons.lang.StringUtils;
import org.kuali.rice.core.api.exception.RiceRuntimeException;
import org.kuali.rice.core.api.util.type.TypeUtils;
import org.kuali.rice.krad.bo.DataObjectRelationship;
import org.kuali.rice.krad.bo.KualiCode;
import org.kuali.rice.krad.datadictionary.AttributeDefinition;
import org.kuali.rice.krad.datadictionary.mask.MaskFormatter;
import org.kuali.rice.krad.datadictionary.parse.BeanTag;
import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
import org.kuali.rice.krad.datadictionary.parse.BeanTags;
import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
import org.kuali.rice.krad.datadictionary.validator.Validator;
import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
import org.kuali.rice.krad.uif.UifConstants;
import org.kuali.rice.krad.uif.component.BindingInfo;
import org.kuali.rice.krad.uif.component.Component;
import org.kuali.rice.krad.uif.component.ComponentSecurity;
import org.kuali.rice.krad.uif.component.DataBinding;
import org.kuali.rice.krad.uif.util.ComponentFactory;
import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
import org.kuali.rice.krad.uif.util.ViewModelUtils;
import org.kuali.rice.krad.uif.view.View;
import org.kuali.rice.krad.uif.widget.Help;
import org.kuali.rice.krad.uif.widget.Helpable;
import org.kuali.rice.krad.uif.widget.Inquiry;
import org.kuali.rice.krad.uif.widget.Tooltip;
import org.kuali.rice.krad.util.KRADPropertyConstants;
import org.kuali.rice.krad.util.ObjectUtils;
import org.kuali.rice.krad.valuefinder.ValueFinder;

import java.beans.PropertyEditor;
import java.util.ArrayList;
import java.util.List;

/**
 * Field that renders data from the application, such as the value of a data object property
 *
 * @author Kuali Rice Team (rice.collab@kuali.org)
 */
@BeanTags({ @BeanTag(name = "dataField-bean", parent = "Uif-DataField"),
        @BeanTag(name = "dataField-labelTop-bean", parent = "Uif-DataField-LabelTop"),
        @BeanTag(name = "dataField-labelRight-bean", parent = "Uif-DataField-LabelRight"),
        @BeanTag(name = "dataField-withoutLabel-bean", parent = "Uif-DataField-withoutLabel") })
public class DataField extends FieldBase implements DataBinding, Helpable {
    private static final long serialVersionUID = -4129678891948564724L;

    // binding
    private String propertyName;
    private BindingInfo bindingInfo;

    private String dictionaryAttributeName;
    private String dictionaryObjectEntry;

    // value props
    private String defaultValue;
    private Class<? extends ValueFinder> defaultValueFinderClass;
    private Object[] defaultValues;
    private String forcedValue;

    private PropertyEditor propertyEditor;

    private boolean addHiddenWhenReadOnly;

    // read only display properties
    protected String readOnlyDisplayReplacementPropertyName;
    protected String readOnlyDisplaySuffixPropertyName;

    private String readOnlyDisplayReplacement;
    private String readOnlyDisplaySuffix;

    private String readOnlyListDisplayType;
    private String readOnlyListDelimiter;

    private boolean applyMask;
    private MaskFormatter maskFormatter;

    private List<String> additionalHiddenPropertyNames;
    private List<String> propertyNamesForAdditionalDisplay;

    private boolean escapeHtmlInPropertyValue;
    private boolean multiLineReadOnlyDisplay;

    // widgets
    private Inquiry inquiry;
    private boolean enableAutoInquiry;

    private Help help;

    public DataField() {
        super();

        enableAutoInquiry = true;
        escapeHtmlInPropertyValue = true;

        additionalHiddenPropertyNames = new ArrayList<String>();
        propertyNamesForAdditionalDisplay = new ArrayList<String>();
    }

    /**
     * The following initialization is performed:
     *
     * <ul>
     * <li>Set defaults for binding</li>
     * <li>Default the model path if not set</li>
     * </ul>
     *
     * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View,
     *      java.lang.Object)
     */
    @Override
    public void performInitialization(View view, Object model) {
        super.performInitialization(view, model);

        if (bindingInfo != null) {
            bindingInfo.setDefaults(view, getPropertyName());
        }
    }

    /**
     * The following updates are done here:
     *
     * <ul>
     * <li>If readOnlyHidden set to true, set field to readonly and add to hidden property names</li>
     * </ul>
     */
    public void performApplyModel(View view, Object model, Component parent) {
        super.performApplyModel(view, model, parent);

        if (this.enableAutoInquiry && (this.inquiry == null) && isReadOnly()) {
            buildAutomaticInquiry(view, model, false);
        }

        if (isAddHiddenWhenReadOnly()) {
            setReadOnly(true);
            getAdditionalHiddenPropertyNames().add(getPropertyName());
        }
    }

    /**
     * The following actions are performed:
     *
     * <ul>
     * <li>Set the ids for the various attribute components</li>
     * <li>Sets up the client side validation for constraints on this field. In
     * addition, it sets up the messages applied to this field</li>
     * <li>If this field is of type list and readOnly, generates the appropriate readOnly output.  Handles other
     * readOnlyReplacement cases, as well.</li>
     * </ul>
     *
     * @see org.kuali.rice.krad.uif.component.ComponentBase#performFinalize(org.kuali.rice.krad.uif.view.View,
     *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
     */
    @Override
    public void performFinalize(View view, Object model, Component parent) {
        super.performFinalize(view, model, parent);

        // adjust the path for hidden fields
        // TODO: should this check the view#readOnly?
        List<String> hiddenPropertyPaths = new ArrayList<String>();
        for (String hiddenPropertyName : getAdditionalHiddenPropertyNames()) {
            String hiddenPropertyPath = getBindingInfo().getPropertyAdjustedBindingPath(hiddenPropertyName);
            hiddenPropertyPaths.add(hiddenPropertyPath);
        }
        this.additionalHiddenPropertyNames = hiddenPropertyPaths;

        // adjust paths on informational property names
        List<String> informationalPropertyPaths = new ArrayList<String>();
        for (String infoPropertyName : getPropertyNamesForAdditionalDisplay()) {
            String infoPropertyPath = getBindingInfo().getPropertyAdjustedBindingPath(infoPropertyName);
            informationalPropertyPaths.add(infoPropertyPath);
        }
        this.propertyNamesForAdditionalDisplay = informationalPropertyPaths;

        //Special processing for List<?> readOnly
        Class<?> type = ObjectPropertyUtils.getPropertyType(model, getBindingInfo().getBindingPath());
        if (this.isReadOnly() && type != null && List.class.isAssignableFrom(type)
                && StringUtils.isBlank(getReadOnlyDisplayReplacement())
                && StringUtils.isBlank(getReadOnlyDisplayReplacementPropertyName())) {
            //get the list
            Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, getBindingInfo().getBindingPath());

            //check for null, empty or non-simple type content (not supported by DataField)
            if (fieldValue != null && fieldValue instanceof List<?> && !((List) fieldValue).isEmpty()) {
                List<?> list = (List<?>) fieldValue;
                processReadOnlyListDisplay(model, list);
            } else {
                this.setReadOnlyDisplayReplacement("&nbsp;");
            }

        } else {
            // Additional and Alternate display value
            setAlternateAndAdditionalDisplayValue(view, model);
        }

        if (this.getFieldLabel() != null && StringUtils.isNotBlank(this.getId())) {
            this.getFieldLabel().setLabelForComponentId(this.getId() + UifConstants.IdSuffixes.CONTROL);
        }
    }

    /**
     * Creates a new {@link org.kuali.rice.krad.uif.widget.Inquiry} and then invokes the lifecycle process for
     * the inquiry to determine if a relationship was found, if so the inquiry is assigned to the field
     *
     * @param view view instance being processed
     * @param model object containing the view data
     * @param enableDirectInquiry whether direct inquiry should be enabled if an inquiry is found
     */
    protected void buildAutomaticInquiry(View view, Object model, boolean enableDirectInquiry) {
        Inquiry autoInquiry = ComponentFactory.getInquiry();

        view.getViewHelperService().spawnSubLifecyle(view, model, autoInquiry, this, null, null);

        // if render flag is true, that means the inquiry was able to find a relationship
        if (autoInquiry.isRender()) {
            this.inquiry = autoInquiry;
        }
    }

    /**
     * This method is called when the list is readOnly as determined in DataField's performFinalize method.  This
     * method
     * should be overridden to perform any additional processing to the values before calling
     * generateReadOnlyListDisplayReplacement.  The default implementation calls it directly with the originalList.
     *
     * @param model the model
     * @param originalList originalList of values
     */
    protected void processReadOnlyListDisplay(Object model, List<?> originalList) {
        this.setReadOnlyDisplayReplacement(generateReadOnlyListDisplayReplacement(originalList));
    }

    /**
     * Generates the html to be used and sets the readOnlyDisplayReplacement for DataFields that contain lists and
     * do not have their own readOnlyDisplayReplacement defined.  The type of html generated is based on the options
     * set on readOnlyListDisplayType and readOnlyListDelimiter.
     *
     * @param list the list to be converted to readOnly html
     */
    protected String generateReadOnlyListDisplayReplacement(List<?> list) {
        String generatedHtml = "";

        //Default to delimited if nothing is set
        if (getReadOnlyListDisplayType() == null) {
            this.setReadOnlyListDisplayType(UifConstants.ReadOnlyListTypes.DELIMITED.name());
        }

        //begin generation setup
        if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.UL.name())) {
            generatedHtml = "<ul class='uif-readOnlyStringList'>";
        } else if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.OL.name())) {
            generatedHtml = "<ol class='uif-readOnlyStringList'>";
        } else if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.BREAK.name())) {
            setReadOnlyListDelimiter("<br/>");
        } else if (this.getReadOnlyListDelimiter() == null) {
            setReadOnlyListDelimiter(", ");
        }

        //iterate through each value
        for (Object value : list) {
            //if blank skip
            if (!TypeUtils.isSimpleType(value.getClass()) || StringUtils.isBlank(value.toString())) {
                continue;
            }

            //handle mask if any
            if (isApplyMask()) {
                value = getMaskFormatter().maskValue(value);
            }

            //TODO the value should use the formatted text property value we would expect to see instead of toString
            //two types - delimited and html list
            if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.UL.name())
                    || getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.OL.name())) {
                generatedHtml = generatedHtml + "<li>" + value.toString() + "</li>";
            } else {
                //no matching needed - delimited is always the fallback and break uses same logic
                generatedHtml = generatedHtml + value.toString() + this.getReadOnlyListDelimiter();
            }
        }

        //end the generation
        if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.UL.name())) {
            generatedHtml = generatedHtml + "</ul>";
        } else if (getReadOnlyListDisplayType().equalsIgnoreCase(UifConstants.ReadOnlyListTypes.OL.name())) {
            generatedHtml = generatedHtml + "</ol>";
        } else {
            generatedHtml = StringUtils.removeEnd(generatedHtml, this.getReadOnlyListDelimiter());
        }

        if (StringUtils.isNotBlank(generatedHtml)) {
            return generatedHtml;
        } else {
            //this must be done or the ftl will skip and throw error
            return "&nbsp;";
        }
    }

    /**
     * Sets alternate and additional property value for this field.
     *
     * <p>
     * If <code>AttributeSecurity</code> present in this field, make sure the current user has permission to view the
     * field value. If user doesn't have permission to view the value, mask the value as configured and set it
     * as alternate value for display. If security doesn't exists for this field but
     * <code>alternateDisplayPropertyName</code> present, get its value and format it based on that
     * fields formatting and set for display.
     * </p>
     *
     * <p>
     * For additional display value, if <code>AttributeSecurity</code> not present, sets the value if
     * <code>additionalDisplayPropertyName</code> present. If not present, check whether this field is a
     * <code>KualiCode</code> and get the relationship configured in the datadictionary file and set the name
     * additional display value which will be displayed along with the code. If additional display property not
     * present, check whether this field is has <code>MultiValueControlBase</code>. If yes, get the Label
     * for the value and set it as additional display value.
     * </p>
     *
     * @param view the current view instance
     * @param model model instance
     */
    protected void setAlternateAndAdditionalDisplayValue(View view, Object model) {
        // if alternate or additional display values set don't use property names
        if (StringUtils.isNotBlank(readOnlyDisplayReplacement) || StringUtils.isNotBlank(readOnlyDisplaySuffix)) {
            return;
        }

        // check whether field value needs to be masked, and if so apply masking as alternateDisplayValue
        if (isApplyMask()) {
            Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, getBindingInfo().getBindingPath());
            readOnlyDisplayReplacement = getMaskFormatter().maskValue(fieldValue);

            return;
        }

        // if not read only, return without trying to set alternate and additional values
        if (!isReadOnly()) {
            return;
        }

        // if field is not secure, check for alternate and additional display properties
        if (StringUtils.isNotBlank(getReadOnlyDisplayReplacementPropertyName())) {
            String alternateDisplayPropertyPath = getBindingInfo()
                    .getPropertyAdjustedBindingPath(getReadOnlyDisplayReplacementPropertyName());

            Object alternateFieldValue = ObjectPropertyUtils.getPropertyValue(model, alternateDisplayPropertyPath);
            if (alternateFieldValue != null) {
                // TODO: format by type
                readOnlyDisplayReplacement = alternateFieldValue.toString();
            }
        }

        // perform automatic translation for code references if enabled on view
        if (StringUtils.isBlank(getReadOnlyDisplaySuffixPropertyName())
                && view.isTranslateCodesOnReadOnlyDisplay()) {
            // check for any relationship present for this field and it's of type KualiCode
            Class<?> parentObjectClass = ViewModelUtils.getParentObjectClassForMetadata(view, model, this);
            DataObjectRelationship relationship = KRADServiceLocatorWeb.getDataObjectMetaDataService()
                    .getDataObjectRelationship(null, parentObjectClass, getBindingInfo().getBindingName(), "", true,
                            false, false);

            if (relationship != null && getPropertyName().startsWith(relationship.getParentAttributeName())
                    && KualiCode.class.isAssignableFrom(relationship.getRelatedClass())) {
                readOnlyDisplaySuffixPropertyName = relationship.getParentAttributeName() + "."
                        + KRADPropertyConstants.NAME;
            }
        }

        // now check for an additional display property and if set get the value
        if (StringUtils.isNotBlank(getReadOnlyDisplaySuffixPropertyName())) {
            String additionalDisplayPropertyPath = getBindingInfo()
                    .getPropertyAdjustedBindingPath(getReadOnlyDisplaySuffixPropertyName());

            Object additionalFieldValue = ObjectPropertyUtils.getPropertyValue(model,
                    additionalDisplayPropertyPath);
            if (additionalFieldValue != null) {
                // TODO: format by type
                readOnlyDisplaySuffix = additionalFieldValue.toString();
            }
        }
    }

    /**
     * Defaults the properties of the <code>DataField</code> to the
     * corresponding properties of its <code>AttributeDefinition</code>
     * retrieved from the dictionary (if such an entry exists). If the field
     * already contains a value for a property, the definitions value is not
     * used.
     *
     * @param view view instance the field belongs to
     * @param attributeDefinition AttributeDefinition instance the property values should be
     * copied from
     */
    public void copyFromAttributeDefinition(View view, AttributeDefinition attributeDefinition) {
        // label
        if (StringUtils.isEmpty(getLabel())) {
            setLabel(attributeDefinition.getLabel());
        }

        // short label
        if (StringUtils.isEmpty(getShortLabel())) {
            setShortLabel(attributeDefinition.getShortLabel());
        }

        // security
        if (getDataFieldSecurity().getAttributeSecurity() == null) {
            getDataFieldSecurity().setAttributeSecurity(attributeDefinition.getAttributeSecurity());
        }

        // alternate property name
        if (getReadOnlyDisplayReplacementPropertyName() == null
                && StringUtils.isNotBlank(attributeDefinition.getAlternateDisplayAttributeName())) {
            setReadOnlyDisplayReplacementPropertyName(attributeDefinition.getAlternateDisplayAttributeName());
        }

        // additional property display name
        if (getReadOnlyDisplaySuffixPropertyName() == null
                && StringUtils.isNotBlank(attributeDefinition.getAdditionalDisplayAttributeName())) {
            setReadOnlyDisplaySuffixPropertyName(attributeDefinition.getAdditionalDisplayAttributeName());
        }

        // property editor
        if (getPropertyEditor() == null) {
            setPropertyEditor(attributeDefinition.getPropertyEditor());
        }
    }

    /**
     * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
     */
    @Override
    public List<Component> getComponentsForLifecycle() {
        List<Component> components = super.getComponentsForLifecycle();

        components.add(inquiry);
        components.add(help);

        return components;
    }

    /**
     * Indicates whether the data field instance allows input, subclasses should override and set to
     * true if input is allowed
     *
     * @return true if input is allowed, false if read only
     */
    public boolean isInputAllowed() {
        return false;
    }

    /**
     * @see org.kuali.rice.krad.uif.component.DataBinding#getPropertyName()
     */
    @BeanTagAttribute(name = "propertyName")
    public String getPropertyName() {
        return this.propertyName;
    }

    /**
     * Setter for the component's property name
     *
     * @param propertyName
     */
    public void setPropertyName(String propertyName) {
        this.propertyName = propertyName;
    }

    /**
     * Performs formatting of the field value for display and then converting the value back to its
     * expected type from a string
     *
     * <p>
     * Note property editors exist and are already registered for the basic Java types and the
     * common Kuali types such as [@link KualiDecimal}. Registration with this property is only
     * needed for custom property editors
     * </p>
     *
     * @return PropertyEditor property editor instance to use for this field
     */
    @BeanTagAttribute(name = "propertyEditor", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
    public PropertyEditor getPropertyEditor() {
        return propertyEditor;
    }

    /**
     * Setter for the custom property editor to use for the field
     *
     * @param propertyEditor
     */
    public void setPropertyEditor(PropertyEditor propertyEditor) {
        this.propertyEditor = propertyEditor;
    }

    /**
     * Convenience setter for configuring a property editor by class
     *
     * @param propertyEditorClass
     */
    public void setPropertyEditorClass(Class<? extends PropertyEditor> propertyEditorClass) {
        this.propertyEditor = ObjectUtils.newInstance(propertyEditorClass);
    }

    /**
     * @see org.kuali.rice.krad.uif.component.DataBinding#getBindingInfo()
     */
    @BeanTagAttribute(name = "bindingInfo", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
    public BindingInfo getBindingInfo() {
        return this.bindingInfo;
    }

    /**
     * Setter for the field's binding info
     *
     * @param bindingInfo
     */
    public void setBindingInfo(BindingInfo bindingInfo) {
        this.bindingInfo = bindingInfo;
    }

    /**
     * Returns the full binding path (the path used in the name attribute of the input).
     * This differs from propertyName in that it uses BindingInfo to determine the path.
     *
     * @return full binding path name
     */
    public String getName() {
        return this.getBindingInfo().getBindingPath();
    }

    /**
     * Name of the attribute within the data dictionary the attribute field is
     * associated with
     *
     * <p>
     * During the initialize phase for the <code>View</code>, properties for
     * attribute fields are defaulted from a corresponding
     * <code>AttributeDefinition</code> in the data dictionary. Based on the
     * propertyName and parent object class the framework attempts will
     * determine the attribute definition that is associated with the field and
     * set this property. However this property can also be set in the fields
     * configuration to use another dictionary attribute.
     * </p>
     *
     * <p>
     * The attribute name is used along with the dictionary object entry to find
     * the <code>AttributeDefinition</code>
     * </p>
     *
     * @return attribute name
     */
    @BeanTagAttribute(name = "dictionaryAttributeName")
    public String getDictionaryAttributeName() {
        return this.dictionaryAttributeName;
    }

    /**
     * Setter for the dictionary attribute name
     *
     * @param dictionaryAttributeName
     */
    public void setDictionaryAttributeName(String dictionaryAttributeName) {
        this.dictionaryAttributeName = dictionaryAttributeName;
    }

    /**
     * Object entry name in the data dictionary the associated attribute is
     * apart of
     *
     * <p>
     * During the initialize phase for the <code>View</code>, properties for
     * attribute fields are defaulted from a corresponding
     * <code>AttributeDefinition</code> in the data dictionary. Based on the
     * parent object class the framework will determine the object entry for the
     * associated attribute. However the object entry can be set in the field's
     * configuration to use another object entry for the attribute
     * </p>
     *
     * <p>
     * The attribute name is used along with the dictionary object entry to find
     * the <code>AttributeDefinition</code>
     * </p>
     *
     * @return String
     */
    @BeanTagAttribute(name = "dictionaryObjectEntry")
    public String getDictionaryObjectEntry() {
        return this.dictionaryObjectEntry;
    }

    /**
     * Setter for the dictionary object entry
     *
     * @param dictionaryObjectEntry
     */
    public void setDictionaryObjectEntry(String dictionaryObjectEntry) {
        this.dictionaryObjectEntry = dictionaryObjectEntry;
    }

    /**
     * Default value for the model property the field points to
     *
     * <p>
     * When a new <code>View</code> instance is requested, the corresponding
     * model will be newly created. During this initialization process the value
     * for the model property will be set to the given default value (if set)
     * </p>
     *
     * @return default value
     */
    @BeanTagAttribute(name = "defaultValue")
    public String getDefaultValue() {
        return this.defaultValue;
    }

    /**
     * Setter for the fields default value
     *
     * @param defaultValue
     */
    public void setDefaultValue(String defaultValue) {
        this.defaultValue = defaultValue;
    }

    /**
     * Gives Class that should be invoked to produce the default value for the
     * field
     *
     * @return default value finder class
     */
    @BeanTagAttribute(name = "defaultValueFinderClass")
    public Class<? extends ValueFinder> getDefaultValueFinderClass() {
        return this.defaultValueFinderClass;
    }

    /**
     * Setter for the default value finder class
     *
     * @param defaultValueFinderClass
     */
    public void setDefaultValueFinderClass(Class<? extends ValueFinder> defaultValueFinderClass) {
        this.defaultValueFinderClass = defaultValueFinderClass;
    }

    /**
     * Array of default values for the model property the field points to
     *
     * <p>
     * When a new <code>View</code> instance is requested, the corresponding
     * model will be newly created. During this initialization process the value
     * for the model property will be set to the given default values (if set)
     * </p>
     *
     * @return default value
     */
    @BeanTagAttribute(name = "defaultValues", type = BeanTagAttribute.AttributeType.LISTBEAN)
    public Object[] getDefaultValues() {
        return this.defaultValues;
    }

    /**
     * Setter for the fields default values
     *
     * @param defaultValues
     */
    public void setDefaultValues(Object[] defaultValues) {
        this.defaultValues = defaultValues;
    }

    public String getForcedValue() {
        return forcedValue;
    }

    public void setForcedValue(String forcedValue) {
        this.forcedValue = forcedValue;
    }

    /**
     * Summary help text for the field
     *
     * @return summary help text
     */
    @BeanTagAttribute(name = "helpSummary")
    public String getHelpSummary() {
        return this.help.getTooltipHelpContent();
    }

    /**
     * Setter for the summary help text
     *
     * @param helpSummary
     */
    public void setHelpSummary(String helpSummary) {
        this.help.setTooltipHelpContent(helpSummary);
    }

    /**
     * Data Field Security object that indicates what authorization (permissions) exist for the field
     *
     * @return DataFieldSecurity instance
     */
    public DataFieldSecurity getDataFieldSecurity() {
        return (DataFieldSecurity) super.getComponentSecurity();
    }

    /**
     * Override to assert a {@link DataFieldSecurity} instance is set
     *
     * @param componentSecurity instance of DataFieldSecurity
     */
    @Override
    public void setComponentSecurity(ComponentSecurity componentSecurity) {
        if (!(componentSecurity instanceof DataFieldSecurity)) {
            throw new RiceRuntimeException(
                    "Component security for DataField should be instance of DataFieldSecurity");
        }

        super.setComponentSecurity(componentSecurity);
    }

    /**
     * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentSecurityClass()
     */
    @Override
    protected Class<? extends ComponentSecurity> getComponentSecurityClass() {
        return DataFieldSecurity.class;
    }

    /**
     * Indicates the field should be read-only but also a hidden should be generated for the field
     *
     * <p>
     * Useful for when a value is just displayed but is needed by script
     * </p>
     *
     * @return true if field should be readOnly hidden, false if not
     */
    @BeanTagAttribute(name = "addHiddenWhenReadOnly")
    public boolean isAddHiddenWhenReadOnly() {
        return addHiddenWhenReadOnly;
    }

    /**
     * Setter for the read-only hidden indicator
     *
     * @param addHiddenWhenReadOnly
     */
    public void setAddHiddenWhenReadOnly(boolean addHiddenWhenReadOnly) {
        this.addHiddenWhenReadOnly = addHiddenWhenReadOnly;
    }

    /**
     * Inquiry widget for the field
     *
     * <p>
     * The inquiry widget will render a link for the field value when read-only
     * that points to the associated inquiry view for the field. The inquiry can
     * be configured to point to a certain <code>InquiryView</code>, or the
     * framework will attempt to associate the field with a inquiry based on its
     * metadata (in particular its relationships in the model)
     * </p>
     *
     * @return Inquiry field inquiry
     */
    @BeanTagAttribute(name = "inquiry", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
    public Inquiry getInquiry() {
        return this.inquiry;
    }

    /**
     * Setter for the inquiry widget
     *
     * @param inquiry
     */
    public void setInquiry(Inquiry inquiry) {
        this.inquiry = inquiry;
    }

    /**
     * Indicates whether inquiries should be automatically set when a relationship for the field's property
     * is found
     *
     * <p>
     * Note this only applies when the {@link #getInquiry()} widget has not been configured (is null)
     * and is set to true by default
     * </p>
     *
     * @return true if auto inquiries are enabled, false if not
     */
    public boolean isEnableAutoInquiry() {
        return enableAutoInquiry;
    }

    /**
     * Setter for enabling automatic inquiries
     *
     * @param enableAutoInquiry
     */
    public void setEnableAutoInquiry(boolean enableAutoInquiry) {
        this.enableAutoInquiry = enableAutoInquiry;
    }

    /**
     * Help configuration object for the datafield
     *
     * <p>
     * External help information can be configured for the datafield. The
     * <code>Help</code> object can the configuration for rendering a link to
     * that help information.
     * </p>
     *
     * @return Help for datafield
     */
    @Override
    @BeanTagAttribute(name = "help", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
    public Help getHelp() {
        return this.help;
    }

    /**
     * Setter for the datafield help content
     *
     * @param help
     */
    @Override
    public void setHelp(Help help) {
        this.help = help;
    }

    /**
     * For data fields the help tooltip is placed on the label.
     *
     * @see org.kuali.rice.krad.uif.widget.Helpable#setTooltipOfComponent(org.kuali.rice.krad.uif.widget.Tooltip))
     */
    @Override
    @BeanTagAttribute(name = "tooltipOfComponent", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
    public void setTooltipOfComponent(Tooltip tooltip) {
        getFieldLabel().setToolTip(tooltip);
    }

    /**
     * Return the field label for the help title
     *
     * @return field label
     * @see org.kuali.rice.krad.uif.widget.Helpable#setTooltipOfComponent(org.kuali.rice.krad.uif.widget.Tooltip)
     */
    @Override
    public String getHelpTitle() {
        return this.getLabel();
    }

    /**
     * Additional display attribute name, which will be displayed next to the actual field value
     * when the field is readonly with hyphen in between like PropertyValue - AdditionalPropertyValue
     *
     * @param readOnlyDisplaySuffixPropertyName name of the additional display property
     */
    public void setReadOnlyDisplaySuffixPropertyName(String readOnlyDisplaySuffixPropertyName) {
        this.readOnlyDisplaySuffixPropertyName = readOnlyDisplaySuffixPropertyName;
    }

    /**
     * Returns the additional display attribute name to be displayed when the field is readonly
     *
     * @return additional display attribute name
     */
    @BeanTagAttribute(name = "readOnlyDisplaceSuffixPropertyName")
    public String getReadOnlyDisplaySuffixPropertyName() {
        return this.readOnlyDisplaySuffixPropertyName;
    }

    /**
     * Sets the alternate display attribute name to be displayed when the field is readonly.
     * This properties value will be displayed instead of actual fields value when the field is readonly.
     *
     * @param readOnlyDisplayReplacementPropertyName alternate display property name
     */
    public void setReadOnlyDisplayReplacementPropertyName(String readOnlyDisplayReplacementPropertyName) {
        this.readOnlyDisplayReplacementPropertyName = readOnlyDisplayReplacementPropertyName;
    }

    /**
     * Returns the alternate display attribute name to be displayed when the field is readonly.
     *
     * @return alternate Display Property Name
     */
    @BeanTagAttribute(name = "readOnlyDisplayReplacementPropertyName")
    public String getReadOnlyDisplayReplacementPropertyName() {
        return this.readOnlyDisplayReplacementPropertyName;
    }

    /**
     * Returns the alternate display value
     *
     * @return the alternate display value set for this field
     */
    @BeanTagAttribute(name = "readOnlyDisplayReplacement")
    public String getReadOnlyDisplayReplacement() {
        return readOnlyDisplayReplacement;
    }

    /**
     * Setter for the alternative display value
     *
     * @param value
     */
    public void setReadOnlyDisplayReplacement(String value) {
        this.readOnlyDisplayReplacement = value;
    }

    /**
     * Returns the additional display value.
     *
     * @return the additional display value set for this field
     */
    @BeanTagAttribute(name = "readOnlyDispalySuffix")
    public String getReadOnlyDisplaySuffix() {
        return readOnlyDisplaySuffix;
    }

    /**
     * Setter for the additional display value
     *
     * @param value
     */
    public void setReadOnlyDisplaySuffix(String value) {
        this.readOnlyDisplaySuffix = value;
    }

    /**
     * Gets the readOnlyListDisplayType.
     *
     * <p>When this is not set, the list will default to the delimited list display with a default of comma and space
     * (", ") - if readOnlyListDelimiter is not set as well.  The type can be set as the following:
     * <ul>
     * <li>"DELIMITED" - list will be output with delimiters between each item defined by readOnlyListDelimiter</li>
     * <li>"BREAK" - list will be output with breaks between each item</li>
     * <li>"OL" - list will be output in ordered list format (numbered)</li>
     * <li>"UL" - list will be output in unordered list format (bulleted)</li>
     * </ul>
     * </p>
     *
     * @return the display type to use
     */
    public String getReadOnlyListDisplayType() {
        return readOnlyListDisplayType;
    }

    /**
     * Set the readOnlyListDisplayType
     *
     * @param readOnlyListDisplayType
     */
    public void setReadOnlyListDisplayType(String readOnlyListDisplayType) {
        this.readOnlyListDisplayType = readOnlyListDisplayType;
    }

    /**
     * The readOnlyListDelimiter is used to set the delimiter used when "DELIMITED" type is set for
     * readOnlyListDisplayType
     *
     * @return the delimiter to use in readOnly list output with "DELIMITED" type set
     */
    public String getReadOnlyListDelimiter() {
        return readOnlyListDelimiter;
    }

    /**
     * Set the readOnlyListDelimiter
     *
     * @param readOnlyListDelimiter
     */
    public void setReadOnlyListDelimiter(String readOnlyListDelimiter) {
        this.readOnlyListDelimiter = readOnlyListDelimiter;
    }

    /**
     * Indicates whether the value for the field should be masked (or partially masked) on display
     *
     * <p>
     * If set to true, the field value will be masked by applying the configured {@link #getMaskFormatter()}
     * </p>
     *
     * <p>
     * If a KIM permission exists that should be checked to determine whether the value should be masked or not,
     * this value should not be set but instead the mask or partialMask property on {@link #getComponentSecurity()}
     * should be set to true. This indicates there is a mask permission that should be consulted. If the user
     * does not have the permission, this flag will be set to true by the framework and the value masked using
     * the mask formatter configured on the security object
     * </p>
     *
     * @return true if the field value should be masked, false if not
     */
    @BeanTagAttribute(name = "applyMask")
    public boolean isApplyMask() {
        return applyMask;
    }

    /**
     * Setter for the apply value mask flag
     *
     * @param applyMask
     */
    public void setApplyMask(boolean applyMask) {
        this.applyMask = applyMask;
    }

    /**
     * MaskFormatter instance that will be used to mask the field value when {@link #isApplyMask()} is true
     *
     * <p>
     * Note in cases where the mask is applied due to security (KIM permissions), the mask or partial mask formatter
     * configured on {@link #getComponentSecurity()} will be used instead of this mask formatter
     * </p>
     *
     * @return MaskFormatter instance
     */
    @BeanTagAttribute(name = "maskFormatter", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
    public MaskFormatter getMaskFormatter() {
        return maskFormatter;
    }

    /**
     * Setter for the MaskFormatter instance to apply when the value is masked
     *
     * @param maskFormatter
     */
    public void setMaskFormatter(MaskFormatter maskFormatter) {
        this.maskFormatter = maskFormatter;
    }

    /**
     * Allows specifying hidden property names without having to specify as a
     * field in the group config (that might impact layout)
     *
     * @return hidden property names
     */
    @BeanTagAttribute(name = "additionalHiddenPropertyNames", type = BeanTagAttribute.AttributeType.LISTVALUE)
    public List<String> getAdditionalHiddenPropertyNames() {
        return additionalHiddenPropertyNames;
    }

    /**
     * Setter for the hidden property names
     *
     * @param additionalHiddenPropertyNames
     */
    public void setAdditionalHiddenPropertyNames(List<String> additionalHiddenPropertyNames) {
        this.additionalHiddenPropertyNames = additionalHiddenPropertyNames;
    }

    /**
     * List of property names whose values should be displayed read-only under this field
     *
     * <p>
     * In the attribute field template for each information property name given its values is
     * outputted read-only. Informational property values can also be updated dynamically with
     * the use of field attribute query
     * </p>
     *
     * <p>
     * Simple property names can be given if the property has the same binding parent as this
     * field, in which case the binding path will be adjusted by the framework. If the property
     * names starts with org.kuali.rice.krad.uif.UifConstants#NO_BIND_ADJUST_PREFIX, no binding
     * prefix will be added.
     * </p>
     *
     * @return informational property names
     */
    @BeanTagAttribute(name = "propertyNamesForAdditionalDisplay", type = BeanTagAttribute.AttributeType.LISTVALUE)
    public List<String> getPropertyNamesForAdditionalDisplay() {
        return propertyNamesForAdditionalDisplay;
    }

    /**
     * Setter for the list of informational property names
     *
     * @param propertyNamesForAdditionalDisplay
     */
    public void setPropertyNamesForAdditionalDisplay(List<String> propertyNamesForAdditionalDisplay) {
        this.propertyNamesForAdditionalDisplay = propertyNamesForAdditionalDisplay;
    }

    /**
     * Sets HTML escaping for this property value. HTML escaping will be handled in alternate and additional fields
     * also.
     */
    public void setEscapeHtmlInPropertyValue(boolean escapeHtmlInPropertyValue) {
        this.escapeHtmlInPropertyValue = escapeHtmlInPropertyValue;
    }

    /**
     * Returns true if HTML escape allowed for this field
     *
     * @return true if escaping allowed
     */
    @BeanTagAttribute(name = "escapeHtmlInPropertyValue")
    public boolean isEscapeHtmlInPropertyValue() {
        return this.escapeHtmlInPropertyValue;
    }

    /**
     * Returns true if this field is of type {@code TextAreaControl}.
     *
     * <p>
     * Used to preserve text formatting in a textarea when the view
     * is readOnly by enclosing the text in a </pre> tag.
     * </p>
     *
     * @return true if the field is of type {@code TextAreaControl}
     */
    public boolean isMultiLineReadOnlyDisplay() {
        return multiLineReadOnlyDisplay;
    }

    /**
     * Setter for multiLineReadOnlyDisplay
     *
     * @param multiLineReadOnlyDisplay
     */
    public void setMultiLineReadOnlyDisplay(boolean multiLineReadOnlyDisplay) {
        this.multiLineReadOnlyDisplay = multiLineReadOnlyDisplay;
    }

    /**
     * Indicates whether the value for the field is secure
     *
     * <p>
     * A value will be secured if masking has been applied (by configuration or a failed KIM permission) or the field
     * has been marked as hidden due to an authorization check
     * </p>
     *
     * @return true if value is secure, false if not
     */
    public boolean hasSecureValue() {
        return isApplyMask() || ((getComponentSecurity().isViewAuthz() || getDataFieldSecurity().isViewInLineAuthz()
                || ((getDataFieldSecurity().getAttributeSecurity() != null)
                        && getDataFieldSecurity().getAttributeSecurity().isHide()))
                && isHidden());
    }

    public boolean isRenderFieldset() {
        return (!this.isReadOnly() && inquiry != null && inquiry.isRender() && inquiry.getInquiryLink() != null
                && inquiry.getInquiryLink().isRender())
                || (help != null && help.isRender() && help.getHelpAction() != null
                        && help.getHelpAction().isRender());
    }

    /**
     * @see org.kuali.rice.krad.uif.component.Component#completeValidation
     */
    @Override
    public void completeValidation(ValidationTrace tracer) {
        tracer.addBean(this);

        // Checks that the property is connected to the field
        if (getPropertyName() == null) {
            if (!Validator.checkExpressions(this, "propertyName")) {
                String currentValues[] = { "propertyName = " + getPropertyName() };
                tracer.createError("Property name not set", currentValues);
            }
        }

        // Checks that the default values  present
        if (getDefaultValue() != null && getDefaultValues() != null) {
            String currentValues[] = { "defaultValue =" + getDefaultValue(),
                    "defaultValues Size =" + getDefaultValues().length };
            tracer.createWarning("Both Default Value and Default Values set", currentValues);
        }

        // Checks that a mask formatter is set if the data field is to be masked
        if (isApplyMask()) {
            if (maskFormatter == null) {
                String currentValues[] = { "applyMask =" + isApplyMask(), "maskFormatter =" + maskFormatter };
                tracer.createWarning("Apply mask is true, but no value is set for maskFormatter", currentValues);
            }
        }

        super.completeValidation(tracer.getCopy());
    }

    /**
     * @see org.kuali.rice.krad.uif.component.ComponentBase#copy()
     */
    @Override
    protected <T> void copyProperties(T component) {
        super.copyProperties(component);
        DataField dataFieldCopy = (DataField) component;
        dataFieldCopy.setAddHiddenWhenReadOnly(this.addHiddenWhenReadOnly);
        dataFieldCopy.setAdditionalHiddenPropertyNames(new ArrayList<String>(this.additionalHiddenPropertyNames));
        dataFieldCopy.setApplyMask(this.applyMask);

        if (this.maskFormatter != null) {
            dataFieldCopy.setMaskFormatter(this.maskFormatter);
        }

        if (this.bindingInfo != null) {
            dataFieldCopy.setBindingInfo((BindingInfo) this.bindingInfo.copy());
        }

        dataFieldCopy.setDefaultValue(this.defaultValue);

        if (this.defaultValues != null) {
            dataFieldCopy.setDefaultValues(this.defaultValues);
        }

        dataFieldCopy.setDictionaryAttributeName(this.dictionaryAttributeName);
        dataFieldCopy.setDictionaryObjectEntry(this.dictionaryObjectEntry);
        dataFieldCopy.setEnableAutoInquiry(this.enableAutoInquiry);
        dataFieldCopy.setEscapeHtmlInPropertyValue(this.escapeHtmlInPropertyValue);
        dataFieldCopy.setForcedValue(this.forcedValue);
        dataFieldCopy.setMultiLineReadOnlyDisplay(this.multiLineReadOnlyDisplay);

        if (this.propertyEditor != null) {
            dataFieldCopy.setPropertyEditor(this.propertyEditor);
        }

        dataFieldCopy.setPropertyName(this.propertyName);

        if (this.propertyNamesForAdditionalDisplay != null) {
            dataFieldCopy.setPropertyNamesForAdditionalDisplay(
                    new ArrayList<String>(this.propertyNamesForAdditionalDisplay));
        }

        dataFieldCopy.setReadOnlyDisplayReplacement(this.readOnlyDisplayReplacement);
        dataFieldCopy.setReadOnlyDisplayReplacementPropertyName(this.readOnlyDisplayReplacementPropertyName);
        dataFieldCopy.setReadOnlyDisplaySuffix(this.readOnlyDisplaySuffix);
        dataFieldCopy.setReadOnlyDisplaySuffixPropertyName(this.readOnlyDisplaySuffixPropertyName);
        dataFieldCopy.setReadOnlyListDelimiter(this.readOnlyListDelimiter);
        dataFieldCopy.setReadOnlyListDisplayType(this.readOnlyListDisplayType);
        dataFieldCopy.setDefaultValueFinderClass(this.defaultValueFinderClass);

        if (this.help != null) {
            dataFieldCopy.setHelp((Help) this.help.copy());
        }

        if (this.inquiry != null) {
            dataFieldCopy.setInquiry((Inquiry) this.inquiry.copy());
        }
    }
}