org.broadleafcommerce.openadmin.dto.Entity.java Source code

Java tutorial

Introduction

Here is the source code for org.broadleafcommerce.openadmin.dto.Entity.java

Source

/*
 * #%L
 * BroadleafCommerce Open Admin Platform
 * %%
 * Copyright (C) 2009 - 2013 Broadleaf Commerce
 * %%
 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
 * 
 * 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.
 * #L%
 */
package org.broadleafcommerce.openadmin.dto;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.broadleafcommerce.common.util.BLCMapUtils;
import org.broadleafcommerce.common.util.TypedClosure;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Generic DTO for a domain object. Each property of the domain object is represented by the 'properties' instance variable
 * which allows for further display metadata to be stored.
 * 
 * @author jfischer
 * @see {@link Property}
 *
 */
public class Entity implements Serializable {

    protected static final long serialVersionUID = 1L;

    protected String[] type;
    protected Property[] properties;
    protected boolean isDirty = false;
    protected Date deployDate;
    protected Boolean isDeleted = false;
    protected Boolean isInactive = false;
    protected Boolean isActive = false;
    protected boolean multiPartAvailableOnThread = false;
    protected boolean isValidationFailure = false;
    protected Map<String, List<String>> validationErrors = new HashMap<String, List<String>>();
    protected List<String> globalValidationErrors = new ArrayList<String>();

    protected Map<String, Property> pMap = null;

    public String[] getType() {
        return type;
    }

    public void setType(String[] type) {
        if (type != null && type.length > 0) {
            Arrays.sort(type);
        }
        this.type = type;
    }

    public Map<String, Property> getPMap() {
        if (pMap == null) {
            pMap = BLCMapUtils.keyedMap(properties, new TypedClosure<String, Property>() {
                @Override
                public String getKey(Property value) {
                    return value.getName();
                }
            });
        }
        return pMap;
    }

    public Property[] getProperties() {
        return properties;
    }

    public void setProperties(Property[] properties) {
        this.properties = properties;
        pMap = null;
    }

    public void mergeProperties(String prefix, Entity entity) {
        int j = 0;
        Property[] merged = new Property[properties.length + entity.getProperties().length];
        for (Property property : properties) {
            merged[j] = property;
            j++;
        }
        for (Property property : entity.getProperties()) {
            property.setName(prefix != null ? prefix + "." + property.getName() : "" + property.getName());
            merged[j] = property;
            j++;
        }
        properties = merged;
    }

    /**
     * Replaces all property values in this entity with the values from the given entity. This also resets the {@link #pMap}
     * 
     * @param entity
     */
    public void overridePropertyValues(Entity entity) {
        for (Property property : entity.getProperties()) {
            Property myProperty = findProperty(property.getName());
            if (myProperty != null) {
                myProperty.setValue(property.getValue());
                myProperty.setRawValue(property.getRawValue());
            }
        }
        pMap = null;
    }

    public Property findProperty(String name) {
        if (properties == null) {
            return null;
        }
        Arrays.sort(properties, new Comparator<Property>() {
            @Override
            public int compare(Property o1, Property o2) {
                if (o1 == null && o2 == null) {
                    return 0;
                } else if (o1 == null) {
                    return 1;
                } else if (o2 == null) {
                    return -1;
                }
                return o1.getName().compareTo(o2.getName());
            }
        });
        Property searchProperty = new Property();
        searchProperty.setName(name);
        int index = Arrays.binarySearch(properties, searchProperty, new Comparator<Property>() {
            @Override
            public int compare(Property o1, Property o2) {
                if (o1 == null && o2 == null) {
                    return 0;
                } else if (o1 == null) {
                    return 1;
                } else if (o2 == null) {
                    return -1;
                }
                return o1.getName().compareTo(o2.getName());
            }
        });
        if (index >= 0) {
            return properties[index];
        }
        return null;
    }

    public void addProperty(Property property) {
        boolean exists = findProperty(property.getName()) != null;
        Property[] allProps = getProperties();
        Property[] newProps = new Property[exists ? allProps.length : allProps.length + 1];
        int count = 0;
        for (int j = 0; j < allProps.length; j++) {
            if (!allProps[j].getName().equals(property.getName())) {
                newProps[count] = allProps[j];
                count++;
            }
        }
        newProps[newProps.length - 1] = property;
        setProperties(newProps);
    }

    public Property removeProperty(String name) {
        boolean exists = findProperty(name) != null;
        Property response = null;
        if (exists) {
            Property[] allProps = getProperties();
            Property[] newProps = new Property[allProps.length - 1];
            int count = 0;
            for (int j = 0; j < allProps.length; j++) {
                if (!allProps[j].getName().equals(name)) {
                    newProps[count] = allProps[j];
                    count++;
                } else {
                    response = allProps[j];
                }
            }
            setProperties(newProps);
        }
        return response;
    }

    /**
     * Adds a single validation error to this entity. This will also set the entire
     * entity in an error state by invoking {@link #setValidationFailure(boolean)}.
     * 
     * @param fieldName - the field that is in error. This works on top-level properties (like a 'manufacturer' field on a
     * Product entity) but can also work on properties gleaned from a related entity (like
     * 'defaultSku.weight.weightUnitOfMeasure' on a Product entity)
     * @param errorOrErrorKey - the error message to present to a user. Could be the actual error message or a key to a
     * property in messages.properties to support different locales
     */
    public void addValidationError(String fieldName, String errorOrErrorKey) {
        Map<String, List<String>> fieldErrors = getPropertyValidationErrors();
        List<String> errorMessages = fieldErrors.get(fieldName);
        if (errorMessages == null) {
            errorMessages = new ArrayList<String>();
            fieldErrors.put(fieldName, errorMessages);
        }
        errorMessages.add(errorOrErrorKey);
        setValidationFailure(true);
    }

    public boolean isDirty() {
        return isDirty;
    }

    public void setDirty(boolean dirty) {
        isDirty = dirty;
    }

    public boolean isMultiPartAvailableOnThread() {
        return multiPartAvailableOnThread;
    }

    public void setMultiPartAvailableOnThread(boolean multiPartAvailableOnThread) {
        this.multiPartAvailableOnThread = multiPartAvailableOnThread;
    }

    /**
     * 
     * @return if this entity has failed validation. This will also check the {@link #getPropertyValidationErrors()} map and 
     * {@link #getGlobalValidationErrors()} if this boolean has not been explicitly set
     */
    public boolean isValidationFailure() {
        if (MapUtils.isNotEmpty(getPropertyValidationErrors())
                || CollectionUtils.isNotEmpty(getGlobalValidationErrors())) {
            isValidationFailure = true;
        }
        return isValidationFailure;
    }

    public void setValidationFailure(boolean validationFailure) {
        isValidationFailure = validationFailure;
    }

    /**
     * @deprecated use {@link #getPropertyValidationErrors()} instead
     * @return
     */
    @Deprecated
    public Map<String, List<String>> getValidationErrors() {
        return getPropertyValidationErrors();
    }

    /**
     * Validation error map where the key corresponds to the property that failed validation (which could be dot-separated)
     * and the value corresponds to a list of the error messages, in the case of multiple errors on the same field.
     * 
     * For instance, you might have a configuration where the field is both a Required validator and a regex validator.
     * The validation map in this case might contain something like:
     *      
     *      defaultSku.name => ['This field is required', 'Cannot have numbers in name']
     * 
     * @return a map keyed by property name to the list of error messages for that property
     */
    public Map<String, List<String>> getPropertyValidationErrors() {
        return validationErrors;
    }

    /**
     * @deprecated use {@link #setPropertyValidationErrors(Map)} instead
     */
    @Deprecated
    public void setValidationErrors(Map<String, List<String>> validationErrors) {
        setPropertyValidationErrors(validationErrors);
    }

    /**
     * Completely reset the validation errors for this Entity. In most cases it is more appropriate to use the convenience
     * method for adding a single error via {@link #addValidationError(String, String)}. This will also set the entire
     * entity in an error state by invoking {@link #setValidationFailure(boolean)}.
     * 
     * @param validationErrors
     * @see #addValidationError(String, String)
     */
    public void setPropertyValidationErrors(Map<String, List<String>> validationErrors) {
        if (MapUtils.isNotEmpty(validationErrors)) {
            setValidationFailure(true);
        }
        this.validationErrors = validationErrors;
    }

    /**
     * Adds a validation error to this entity that is not tied to any specific property. If you need to tie this to a
     * property then you should use {@link #addValidationError(String, String)} instead.
     * @param errorOrErrorKey
     */
    public void addGlobalValidationError(String errorOrErrorKey) {
        setValidationFailure(true);
        globalValidationErrors.add(errorOrErrorKey);
    }

    /**
     * Similar to {@link #addGlobalValidationError(String)} except with a list of errors
     * @param errorOrErrorKeys
     */
    public void addGlobalValidationErrors(List<String> errorOrErrorKeys) {
        setValidationFailure(true);
        globalValidationErrors.addAll(errorOrErrorKeys);
    }

    public List<String> getGlobalValidationErrors() {
        return globalValidationErrors;
    }

    public void setGlobalValidationErrors(List<String> globalValidationErrors) {
        this.globalValidationErrors = globalValidationErrors;
    }

    public Boolean getActive() {
        return isActive;
    }

    public void setActive(Boolean active) {
        isActive = active;
    }

    public Boolean getDeleted() {
        return isDeleted;
    }

    public void setDeleted(Boolean deleted) {
        isDeleted = deleted;
    }

    public Boolean getInactive() {
        return isInactive;
    }

    public void setInactive(Boolean inactive) {
        isInactive = inactive;
    }

    public Date getDeployDate() {
        return deployDate;
    }

    public void setDeployDate(Date deployDate) {
        this.deployDate = deployDate;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Entity{");
        sb.append("isValidationFailure=").append(isValidationFailure);
        sb.append(", isDirty=").append(isDirty);
        sb.append(", properties=").append(Arrays.toString(properties));
        sb.append(", type=").append(Arrays.toString(type));
        sb.append('}');
        return sb.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null)
            return false;
        if (!getClass().isAssignableFrom(o.getClass()))
            return false;

        Entity entity = (Entity) o;

        if (isDirty != entity.isDirty)
            return false;
        if (isValidationFailure != entity.isValidationFailure)
            return false;
        if (multiPartAvailableOnThread != entity.multiPartAvailableOnThread)
            return false;
        if (deployDate != null ? !deployDate.equals(entity.deployDate) : entity.deployDate != null)
            return false;
        if (isActive != null ? !isActive.equals(entity.isActive) : entity.isActive != null)
            return false;
        if (isDeleted != null ? !isDeleted.equals(entity.isDeleted) : entity.isDeleted != null)
            return false;
        if (isInactive != null ? !isInactive.equals(entity.isInactive) : entity.isInactive != null)
            return false;
        if (pMap != null ? !pMap.equals(entity.pMap) : entity.pMap != null)
            return false;
        if (!Arrays.equals(properties, entity.properties))
            return false;
        if (!Arrays.equals(type, entity.type))
            return false;
        if (validationErrors != null ? !validationErrors.equals(entity.validationErrors)
                : entity.validationErrors != null)
            return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = type != null ? Arrays.hashCode(type) : 0;
        result = 31 * result + (properties != null ? Arrays.hashCode(properties) : 0);
        result = 31 * result + (isDirty ? 1 : 0);
        result = 31 * result + (deployDate != null ? deployDate.hashCode() : 0);
        result = 31 * result + (isDeleted != null ? isDeleted.hashCode() : 0);
        result = 31 * result + (isInactive != null ? isInactive.hashCode() : 0);
        result = 31 * result + (isActive != null ? isActive.hashCode() : 0);
        result = 31 * result + (multiPartAvailableOnThread ? 1 : 0);
        result = 31 * result + (isValidationFailure ? 1 : 0);
        result = 31 * result + (validationErrors != null ? validationErrors.hashCode() : 0);
        result = 31 * result + (pMap != null ? pMap.hashCode() : 0);
        return result;
    }
}