com.expressui.core.view.field.DisplayField.java Source code

Java tutorial

Introduction

Here is the source code for com.expressui.core.view.field.DisplayField.java

Source

/*
 * Copyright (c) 2012 Brown Bag Consulting.
 * This file is part of the ExpressUI project.
 * Author: Juan Osuna
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License Version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
 * Brown Bag Consulting, Brown Bag Consulting DISCLAIMS THE WARRANTY OF
 * NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the ExpressUI software without
 * disclosing the source code of your own applications. These activities
 * include: offering paid services to customers as an ASP, providing
 * services from a web application, shipping ExpressUI with a closed
 * source product.
 *
 * For more information, please contact Brown Bag Consulting at this
 * address: juan@brownbagconsulting.com.
 */

package com.expressui.core.view.field;

import com.expressui.core.util.BeanPropertyType;
import com.expressui.core.util.StringUtil;
import com.expressui.core.util.assertion.Assert;
import com.expressui.core.view.field.format.DefaultFormats;
import com.expressui.core.view.form.EntityForm;
import com.vaadin.data.util.PropertyFormatter;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.List;

/**
 * A field for display in the UI, as a column in results table or input field in a form.
 */
public abstract class DisplayField {

    private FieldSet fieldSet;

    private String propertyId;
    private BeanPropertyType beanPropertyType;
    private FormLink formLink;
    private PropertyFormatter propertyFormatter;
    private String label;

    /**
     * Constructs with reference to fieldSet this field belongs to and the property name this field is bound to,
     * often in an entity object.
     *
     * @param fieldSet   fieldSet that contains this field
     * @param propertyId name of the property this field is bound to
     */
    public DisplayField(FieldSet fieldSet, String propertyId) {
        this.fieldSet = fieldSet;
        this.propertyId = propertyId;
        beanPropertyType = BeanPropertyType.getBeanPropertyType(getFieldSet().getType(), propertyId);
        Assert.PROGRAMMING.notNull(beanPropertyType != null, "beanPropertyType must not be null");
    }

    /**
     * Gets FieldSet that contains this field.
     *
     * @return FieldSet that contains this field
     */
    public FieldSet getFieldSet() {
        return fieldSet;
    }

    /**
     * Gets the name of the property this field is bound to, often in an entity object
     *
     * @return name of the property
     */
    public String getPropertyId() {
        return propertyId;
    }

    public String getTypeAndPropertyId() {
        return getFieldSet().getType().getName() + "." + getPropertyId();
    }

    /**
     * Gets static type information about the property this field is bound to.
     *
     * @return bean property type information
     */
    protected BeanPropertyType getBeanPropertyType() {
        return beanPropertyType;
    }

    /**
     * Gets the type of property this field is bound to.
     *
     * @return type of property
     */
    public Class getPropertyType() {
        return beanPropertyType.getType();
    }

    /**
     * Gets the PropertyFormatter used to format values for display and parse values
     * entered by user. If one is not already set, generates a default one automatically from
     * {@link DefaultFormats}.
     *
     * @return Vaadin property formatter
     */
    public PropertyFormatter getPropertyFormatter() {
        if (propertyFormatter == null) {
            propertyFormatter = generateDefaultPropertyFormatter();
        }

        return propertyFormatter;
    }

    /**
     * Sets the PropertyFormatter used to format values for display and parse values
     * entered by user.
     *
     * @param propertyFormatter Vaadin property formatter
     */
    public void setPropertyFormatter(PropertyFormatter propertyFormatter) {
        this.propertyFormatter = propertyFormatter;
    }

    /**
     * Generates the default property formatter for this field. Can be overridden to refine the behavior.
     *
     * @return Vaadin property formatter
     */
    protected PropertyFormatter generateDefaultPropertyFormatter() {
        DefaultFormats defaultFormats = getFieldSet().defaultFormats;

        if (getBeanPropertyType().getBusinessType() == BeanPropertyType.BusinessType.DATE) {
            return defaultFormats.getDateFormat();
        } else if (getBeanPropertyType().getBusinessType() == BeanPropertyType.BusinessType.DATE_TIME) {
            return defaultFormats.getDateTimeFormat();
        } else if (getBeanPropertyType().getBusinessType() == BeanPropertyType.BusinessType.NUMBER) {
            if (getBeanPropertyType().getType().isPrimitive()) {
                return defaultFormats.getNumberFormat(0);
            } else {
                return defaultFormats.getNumberFormat();
            }
        } else if (getBeanPropertyType().getBusinessType() == BeanPropertyType.BusinessType.MONEY) {
            return defaultFormats.getNumberFormat();
        }

        return defaultFormats.getEmptyFormat();
    }

    /**
     * Gets the label used for this field. Generates one automatically, if not already set.
     * Generated one can be derived from the property name in the code, @Label annotation on the bound property
     * or looked up from resource bundle properties file, using the property name as the key.
     * <p/>
     * If I18n is required, then define labels in resource bundle properties files domainMessages/.
     * These messages have priority over annotations. Generating a label from the property name in code is done as a
     * last resort.
     *
     * @return display label
     */
    public String getLabel() {
        if (label == null) {
            label = generateLabelText();
        }

        return label;
    }

    /**
     * Sets the label used for this field, overwriting any automatically generated label.
     *
     * @param label label
     */
    public void setLabel(String label) {
        this.label = label;
    }

    /**
     * Generates or re-generates label, passing in arguments for interpolation using standard {0}, {1}, {2}
     * notation. This feature only works with resource bundle messages defined in domainMessages/.
     *
     * @param args
     */
    public void setLabelArgs(Object... args) {
        setLabel(generateLabelText(args));
    }

    /**
     * Generates label automatically, maybe override to refine default behavior.
     *
     * @param args arguments use for interpolation
     * @return generated label
     */
    protected String generateLabelText(Object... args) {
        String labelText = getLabelTextFromMessageSource(false, args);
        if (labelText == null) {
            labelText = getLabelTextFromAnnotation();
        }
        if (labelText == null) {
            labelText = getLabelTextFromCode();
        }

        return labelText;
    }

    /**
     * Gets name for the type of label or section in a form where the label is found.
     * This is used internally by security admin components to indicate to the user
     * where components are located for assigning permissions.
     *
     * @return label section display name
     */
    protected abstract String getLabelSectionDisplayName();

    private String getLabelTextFromMessageSource(boolean useDefaultLocale, Object... args) {
        List<BeanPropertyType> ancestors = beanPropertyType.getAncestors();
        String label = null;
        for (int i = 0; i < ancestors.size(); i++) {
            BeanPropertyType ancestor = ancestors.get(i);
            Class currentType = ancestor.getContainerType();

            String currentPropertyId = ancestor.getId();
            for (int y = i + 1; y < ancestors.size(); y++) {
                BeanPropertyType bpt = ancestors.get(y);
                currentPropertyId += "." + bpt.getId();
            }

            while (label == null && currentType != null) {
                label = getLabelTextFromMessageSource(useDefaultLocale, currentType, currentPropertyId, args);
                currentType = currentType.getSuperclass();
            }

            if (label != null)
                break;

            Class[] interfaces = ancestor.getContainerType().getInterfaces();
            for (Class anInterface : interfaces) {
                Class currentInterface = anInterface;
                while (label == null && currentInterface != null) {
                    label = getLabelTextFromMessageSource(useDefaultLocale, currentInterface, currentPropertyId,
                            args);
                    currentInterface = currentInterface.getSuperclass();
                }
                if (label != null)
                    break;
            }

            if (label != null)
                break;
        }

        if (label == null) {
            label = getLabelTextFromMessageSource(useDefaultLocale, beanPropertyType.getType(), null, args);
        }

        if (label != null && label.contains("{0}") && args.length == 0) {
            return null;
        } else {
            if (label == null && !useDefaultLocale) {
                return getLabelTextFromMessageSource(true, args);
            } else {
                return label;
            }
        }
    }

    private String getLabelTextFromMessageSource(boolean useDefaultLocale, Class type, String propertyId,
            Object... args) {
        String fullPropertyPath = type.getName() + (propertyId == null ? "" : "." + propertyId);
        if (useDefaultLocale) {
            return getFieldSet().domainMessageSourceNoFallback.getOptionalMessageFromDefaultLocale(fullPropertyPath,
                    args);
        } else {
            return getFieldSet().domainMessageSourceNoFallback.getOptionalMessage(fullPropertyPath, args);
        }
    }

    private String getLabelTextFromAnnotation() {
        Class propertyContainerType = beanPropertyType.getContainerType();
        String propertyIdRelativeToContainerType = beanPropertyType.getId();
        PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(propertyContainerType,
                propertyIdRelativeToContainerType);
        Method method = descriptor.getReadMethod();
        Label labelAnnotation = method.getAnnotation(Label.class);
        if (labelAnnotation == null) {
            return null;
        } else {
            return labelAnnotation.value();
        }
    }

    private String getLabelTextFromCode() {
        String afterPeriod = StringUtil.extractAfterPeriod(getPropertyId());
        return StringUtil.humanizeCamelCase(afterPeriod);
    }

    /**
     * Sets a link to open an entity form related to this field. Enables embedding
     * links in results table to open up related entity form.
     *
     * @param propertyId property id path that is many-to-one relationship with another entity
     * @param entityForm entity form component to open when link is clicked
     */
    public void setFormLink(String propertyId, EntityForm entityForm) {
        formLink = new FormLink(propertyId, entityForm);
        entityForm.postWire();
    }

    /**
     * Gets a link to open an entity form related to this field. Enables embedding
     * links in results table to open up related entity
     *
     * @return form link
     */
    public FormLink getFormLink() {
        return formLink;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (!(o instanceof DisplayField))
            return false;

        DisplayField that = (DisplayField) o;

        return getPropertyId().equals(that.getPropertyId());

    }

    @Override
    public int hashCode() {
        return getPropertyId().hashCode();
    }

    @Override
    public String toString() {
        return "DisplayField{" + "typeAndPropertyId='" + getTypeAndPropertyId() + '\'' + '}';
    }
}