org.sparkcommerce.openadmin.server.service.persistence.module.provider.BasicFieldPersistenceProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.sparkcommerce.openadmin.server.service.persistence.module.provider.BasicFieldPersistenceProvider.java

Source

/*
 * #%L
 * SparkCommerce Open Admin Platform
 * %%
 * Copyright (C) 2009 - 2013 Spark 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.sparkcommerce.openadmin.server.service.persistence.module.provider;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sparkcommerce.common.admin.domain.AdminMainEntity;
import org.sparkcommerce.common.money.Money;
import org.sparkcommerce.common.presentation.client.ForeignKeyRestrictionType;
import org.sparkcommerce.common.presentation.client.PersistencePerspectiveItemType;
import org.sparkcommerce.common.presentation.client.SupportedFieldType;
import org.sparkcommerce.openadmin.dto.BasicFieldMetadata;
import org.sparkcommerce.openadmin.dto.FieldMetadata;
import org.sparkcommerce.openadmin.dto.FilterAndSortCriteria;
import org.sparkcommerce.openadmin.dto.ForeignKey;
import org.sparkcommerce.openadmin.dto.Property;
import org.sparkcommerce.openadmin.server.service.persistence.PersistenceException;
import org.sparkcommerce.openadmin.server.service.persistence.module.EmptyFilterValues;
import org.sparkcommerce.openadmin.server.service.persistence.module.FieldManager;
import org.sparkcommerce.openadmin.server.service.persistence.module.FieldNotAvailableException;
import org.sparkcommerce.openadmin.server.service.persistence.module.criteria.FieldPath;
import org.sparkcommerce.openadmin.server.service.persistence.module.criteria.FilterMapping;
import org.sparkcommerce.openadmin.server.service.persistence.module.criteria.Restriction;
import org.sparkcommerce.openadmin.server.service.persistence.module.criteria.RestrictionType;
import org.sparkcommerce.openadmin.server.service.persistence.module.criteria.predicate.IsNotNullPredicateProvider;
import org.sparkcommerce.openadmin.server.service.persistence.module.criteria.predicate.IsNullPredicateProvider;
import org.sparkcommerce.openadmin.server.service.persistence.module.provider.request.AddSearchMappingRequest;
import org.sparkcommerce.openadmin.server.service.persistence.module.provider.request.ExtractValueRequest;
import org.sparkcommerce.openadmin.server.service.persistence.module.provider.request.PopulateValueRequest;
import org.sparkcommerce.openadmin.server.service.type.FieldProviderResponse;
import org.hibernate.Session;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author Jeff Fischer
 */
@Component("blBasicFieldPersistenceProvider")
@Scope("prototype")
public class BasicFieldPersistenceProvider extends FieldPersistenceProviderAdapter {

    protected static final Log LOG = LogFactory.getLog(BasicFieldPersistenceProvider.class);

    protected boolean canHandlePersistence(PopulateValueRequest populateValueRequest, Serializable instance) {
        BasicFieldMetadata metadata = populateValueRequest.getMetadata();
        Property property = populateValueRequest.getProperty();
        //don't handle map fields here - we'll get them in a separate provider
        boolean response = detectBasicType(metadata, property);
        if (!response) {
            //we'll allow this provider to handle money filter mapping for persistence
            response = metadata.getFieldType() == SupportedFieldType.MONEY;
        }
        return response;
    }

    protected boolean detectBasicType(FieldMetadata md, Property property) {
        if (!(md instanceof BasicFieldMetadata)) {
            return false;
        }
        BasicFieldMetadata metadata = (BasicFieldMetadata) md;
        return (metadata.getFieldType() == SupportedFieldType.BOOLEAN
                || metadata.getFieldType() == SupportedFieldType.DATE
                || metadata.getFieldType() == SupportedFieldType.INTEGER
                || metadata.getFieldType() == SupportedFieldType.DECIMAL
                || metadata.getFieldType() == SupportedFieldType.EMAIL
                || metadata.getFieldType() == SupportedFieldType.FOREIGN_KEY
                || metadata.getFieldType() == SupportedFieldType.ADDITIONAL_FOREIGN_KEY
                || metadata.getFieldType() == SupportedFieldType.STRING
                || metadata.getFieldType() == SupportedFieldType.CODE
                || metadata.getFieldType() == SupportedFieldType.HTML
                || metadata.getFieldType() == SupportedFieldType.HTML_BASIC
                || metadata.getFieldType() == SupportedFieldType.MONEY
                || metadata.getFieldType() == SupportedFieldType.ID)
                && (property == null || !property.getName().contains(FieldManager.MAPFIELDSEPARATOR));
    }

    protected boolean detectAdditionalSearchTypes(FieldMetadata md, Property property) {
        if (!(md instanceof BasicFieldMetadata)) {
            return false;
        }
        BasicFieldMetadata metadata = (BasicFieldMetadata) md;
        return (metadata.getFieldType() == SupportedFieldType.BROADLEAF_ENUMERATION
                || metadata.getFieldType() == SupportedFieldType.EXPLICIT_ENUMERATION
                || metadata.getFieldType() == SupportedFieldType.DATA_DRIVEN_ENUMERATION)
                && (property == null || !property.getName().contains(FieldManager.MAPFIELDSEPARATOR));
    }

    protected boolean canHandleExtraction(ExtractValueRequest extractValueRequest, Property property) {
        BasicFieldMetadata metadata = extractValueRequest.getMetadata();
        //don't handle map fields here - we'll get them in a separate provider
        return detectBasicType(metadata, property);
    }

    protected boolean canHandleSearchMapping(AddSearchMappingRequest addSearchMappingRequest,
            List<FilterMapping> filterMappings) {
        FieldMetadata metadata = addSearchMappingRequest.getMergedProperties()
                .get(addSearchMappingRequest.getPropertyName());
        Property property = null;
        //don't handle map fields here - we'll get them in a separate provider
        boolean response = detectBasicType(metadata, property) || detectAdditionalSearchTypes(metadata, property);

        return response;
    }

    @Override
    public FieldProviderResponse populateValue(PopulateValueRequest populateValueRequest, Serializable instance) {
        if (!canHandlePersistence(populateValueRequest, instance)) {
            return FieldProviderResponse.NOT_HANDLED;
        }
        boolean dirty = false;
        try {
            Property prop = populateValueRequest.getProperty();
            Object origValue = populateValueRequest.getFieldManager().getFieldValue(instance, prop.getName());
            switch (populateValueRequest.getMetadata().getFieldType()) {
            case BOOLEAN:
                boolean v = Boolean.valueOf(populateValueRequest.getRequestedValue());
                prop.setOriginalValue(String.valueOf(origValue));
                prop.setOriginalDisplayValue(prop.getOriginalValue());
                try {
                    dirty = checkDirtyState(populateValueRequest, instance, v);
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(), v);
                } catch (IllegalArgumentException e) {
                    char c = v ? 'Y' : 'N';
                    dirty = checkDirtyState(populateValueRequest, instance, c);
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(), c);
                }
                break;
            case DATE:
                Date date = (Date) populateValueRequest.getFieldManager().getFieldValue(instance,
                        populateValueRequest.getProperty().getName());
                String oldValue = null;
                if (date != null) {
                    oldValue = populateValueRequest.getDataFormatProvider().getSimpleDateFormatter().format(date);
                }
                prop.setOriginalValue(oldValue);
                prop.setOriginalDisplayValue(prop.getOriginalValue());
                dirty = !StringUtils.equals(oldValue, populateValueRequest.getRequestedValue());
                populateValueRequest.getFieldManager().setFieldValue(instance,
                        populateValueRequest.getProperty().getName(), populateValueRequest.getDataFormatProvider()
                                .getSimpleDateFormatter().parse(populateValueRequest.getRequestedValue()));
                break;
            case DECIMAL:
                if (origValue != null) {
                    prop.setOriginalValue(String.valueOf(origValue));
                    prop.setOriginalDisplayValue(prop.getOriginalValue());
                }
                if (BigDecimal.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    DecimalFormat format = populateValueRequest.getDataFormatProvider().getDecimalFormatter();
                    format.setParseBigDecimal(true);
                    BigDecimal val = (BigDecimal) format.parse(populateValueRequest.getRequestedValue());
                    dirty = checkDirtyState(populateValueRequest, instance, val);

                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(), val);
                    format.setParseBigDecimal(false);
                } else {
                    Double val = populateValueRequest.getDataFormatProvider().getDecimalFormatter()
                            .parse(populateValueRequest.getRequestedValue()).doubleValue();
                    dirty = checkDirtyState(populateValueRequest, instance, val);
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(), val);
                }
                break;
            case MONEY:
                if (origValue != null) {
                    prop.setOriginalValue(String.valueOf(origValue));
                    prop.setOriginalDisplayValue(prop.getOriginalValue());
                }
                if (BigDecimal.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    DecimalFormat format = populateValueRequest.getDataFormatProvider().getDecimalFormatter();
                    format.setParseBigDecimal(true);
                    BigDecimal val = (BigDecimal) format.parse(populateValueRequest.getRequestedValue());
                    dirty = checkDirtyState(populateValueRequest, instance, val);
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(), val);
                    format.setParseBigDecimal(true);
                } else if (Double.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    Double val = populateValueRequest.getDataFormatProvider().getDecimalFormatter()
                            .parse(populateValueRequest.getRequestedValue()).doubleValue();
                    dirty = checkDirtyState(populateValueRequest, instance, val);
                    LOG.warn("The requested Money field is of type double and could result in a loss of precision."
                            + " Spark recommends that the type of all Money fields are 'BigDecimal' in order to avoid"
                            + " this loss of precision that could occur.");
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(), val);
                } else {
                    DecimalFormat format = populateValueRequest.getDataFormatProvider().getDecimalFormatter();
                    format.setParseBigDecimal(true);
                    BigDecimal val = (BigDecimal) format.parse(populateValueRequest.getRequestedValue());
                    dirty = checkDirtyState(populateValueRequest, instance, val);
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(), new Money(val));
                    format.setParseBigDecimal(false);
                }
                break;
            case INTEGER:
                if (origValue != null) {
                    prop.setOriginalValue(String.valueOf(origValue));
                    prop.setOriginalDisplayValue(prop.getOriginalValue());
                }
                if (int.class.isAssignableFrom(populateValueRequest.getReturnType())
                        || Integer.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    dirty = checkDirtyState(populateValueRequest, instance,
                            Integer.valueOf(populateValueRequest.getRequestedValue()));
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(),
                            Integer.valueOf(populateValueRequest.getRequestedValue()));
                } else if (byte.class.isAssignableFrom(populateValueRequest.getReturnType())
                        || Byte.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    dirty = checkDirtyState(populateValueRequest, instance,
                            Byte.valueOf(populateValueRequest.getRequestedValue()));
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(),
                            Byte.valueOf(populateValueRequest.getRequestedValue()));
                } else if (short.class.isAssignableFrom(populateValueRequest.getReturnType())
                        || Short.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    dirty = checkDirtyState(populateValueRequest, instance,
                            Short.valueOf(populateValueRequest.getRequestedValue()));
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(),
                            Short.valueOf(populateValueRequest.getRequestedValue()));
                } else if (long.class.isAssignableFrom(populateValueRequest.getReturnType())
                        || Long.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    dirty = checkDirtyState(populateValueRequest, instance,
                            Long.valueOf(populateValueRequest.getRequestedValue()));
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(),
                            Long.valueOf(populateValueRequest.getRequestedValue()));
                }
                break;
            case CODE:
                // **NOTE** We want to fall through in this case, do not break.
                setNonDisplayableValues(populateValueRequest);
            case STRING:
            case HTML_BASIC:
            case HTML:
            case EMAIL:
                if (origValue != null) {
                    prop.setOriginalValue(String.valueOf(origValue));
                    prop.setOriginalDisplayValue(prop.getOriginalValue());
                }
                dirty = checkDirtyState(populateValueRequest, instance, populateValueRequest.getRequestedValue());
                populateValueRequest.getFieldManager().setFieldValue(instance,
                        populateValueRequest.getProperty().getName(), populateValueRequest.getRequestedValue());
                break;
            case FOREIGN_KEY: {
                if (origValue != null) {
                    prop.setOriginalValue(String.valueOf(origValue));
                }
                Serializable foreignInstance;
                if (StringUtils.isEmpty(populateValueRequest.getRequestedValue())) {
                    foreignInstance = null;
                } else {
                    if (SupportedFieldType.INTEGER.toString()
                            .equals(populateValueRequest.getMetadata().getSecondaryType().toString())) {
                        foreignInstance = populateValueRequest.getPersistenceManager().getDynamicEntityDao()
                                .retrieve(Class.forName(populateValueRequest.getMetadata().getForeignKeyClass()),
                                        Long.valueOf(populateValueRequest.getRequestedValue()));
                    } else {
                        foreignInstance = populateValueRequest.getPersistenceManager().getDynamicEntityDao()
                                .retrieve(Class.forName(populateValueRequest.getMetadata().getForeignKeyClass()),
                                        populateValueRequest.getRequestedValue());
                    }
                }

                if (Collection.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    Collection collection;
                    try {
                        collection = (Collection) populateValueRequest.getFieldManager().getFieldValue(instance,
                                populateValueRequest.getProperty().getName());
                    } catch (FieldNotAvailableException e) {
                        throw new IllegalArgumentException(e);
                    }
                    if (!collection.contains(foreignInstance)) {
                        collection.add(foreignInstance);
                        dirty = true;
                    }
                } else if (Map.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    throw new IllegalArgumentException("Map structures are not supported for foreign key fields.");
                } else {
                    dirty = checkDirtyState(populateValueRequest, instance, foreignInstance);
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(), foreignInstance);
                }
                break;
            }
            case ADDITIONAL_FOREIGN_KEY: {
                Serializable foreignInstance;
                if (StringUtils.isEmpty(populateValueRequest.getRequestedValue())) {
                    foreignInstance = null;
                } else {
                    if (SupportedFieldType.INTEGER.toString()
                            .equals(populateValueRequest.getMetadata().getSecondaryType().toString())) {
                        foreignInstance = populateValueRequest.getPersistenceManager().getDynamicEntityDao()
                                .retrieve(Class.forName(populateValueRequest.getMetadata().getForeignKeyClass()),
                                        Long.valueOf(populateValueRequest.getRequestedValue()));
                    } else {
                        foreignInstance = populateValueRequest.getPersistenceManager().getDynamicEntityDao()
                                .retrieve(Class.forName(populateValueRequest.getMetadata().getForeignKeyClass()),
                                        populateValueRequest.getRequestedValue());
                    }
                }

                // Best guess at grabbing the original display value
                String fkProp = populateValueRequest.getMetadata().getForeignKeyDisplayValueProperty();
                Object origDispVal = null;
                if (origValue != null) {
                    if (AdminMainEntity.MAIN_ENTITY_NAME_PROPERTY.equals(fkProp)) {
                        if (origValue instanceof AdminMainEntity) {
                            origDispVal = ((AdminMainEntity) origValue).getMainEntityName();
                        }
                    } else {
                        origDispVal = populateValueRequest.getFieldManager().getFieldValue(origValue, fkProp);
                    }
                }
                if (origDispVal != null) {
                    prop.setOriginalDisplayValue(String.valueOf(origDispVal));
                    Session session = populateValueRequest.getPersistenceManager().getDynamicEntityDao()
                            .getStandardEntityManager().unwrap(Session.class);
                    prop.setOriginalValue(String.valueOf(session.getIdentifier(foreignInstance)));
                }

                if (Collection.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    Collection collection;
                    try {
                        collection = (Collection) populateValueRequest.getFieldManager().getFieldValue(instance,
                                populateValueRequest.getProperty().getName());
                    } catch (FieldNotAvailableException e) {
                        throw new IllegalArgumentException(e);
                    }
                    if (!collection.contains(foreignInstance)) {
                        collection.add(foreignInstance);
                        dirty = true;
                    }
                } else if (Map.class.isAssignableFrom(populateValueRequest.getReturnType())) {
                    throw new IllegalArgumentException("Map structures are not supported for foreign key fields.");
                } else {
                    dirty = checkDirtyState(populateValueRequest, instance, foreignInstance);
                    populateValueRequest.getFieldManager().setFieldValue(instance,
                            populateValueRequest.getProperty().getName(), foreignInstance);
                }
                break;
            }
            case ID:
                if (populateValueRequest.getSetId()) {
                    switch (populateValueRequest.getMetadata().getSecondaryType()) {
                    case INTEGER:
                        dirty = checkDirtyState(populateValueRequest, instance,
                                Long.valueOf(populateValueRequest.getRequestedValue()));
                        populateValueRequest.getFieldManager().setFieldValue(instance,
                                populateValueRequest.getProperty().getName(),
                                Long.valueOf(populateValueRequest.getRequestedValue()));
                        break;
                    case STRING:
                        dirty = checkDirtyState(populateValueRequest, instance,
                                populateValueRequest.getRequestedValue());
                        populateValueRequest.getFieldManager().setFieldValue(instance,
                                populateValueRequest.getProperty().getName(),
                                populateValueRequest.getRequestedValue());
                        break;
                    }
                }
                break;
            }
        } catch (Exception e) {
            throw new PersistenceException(e);
        }
        populateValueRequest.getProperty().setIsDirty(dirty);
        return FieldProviderResponse.HANDLED;
    }

    @Override
    public FieldProviderResponse extractValue(ExtractValueRequest extractValueRequest, Property property)
            throws PersistenceException {
        if (!canHandleExtraction(extractValueRequest, property)) {
            return FieldProviderResponse.NOT_HANDLED;
        }
        try {
            if (extractValueRequest.getRequestedValue() != null) {
                String val = null;
                if (extractValueRequest.getMetadata().getForeignKeyCollection()) {
                    ((BasicFieldMetadata) property.getMetadata())
                            .setFieldType(extractValueRequest.getMetadata().getFieldType());
                } else if (extractValueRequest.getMetadata().getFieldType().equals(SupportedFieldType.BOOLEAN)
                        && extractValueRequest.getRequestedValue() instanceof Character) {
                    val = (extractValueRequest.getRequestedValue().equals('Y')) ? "true" : "false";
                } else if (Date.class.isAssignableFrom(extractValueRequest.getRequestedValue().getClass())) {
                    val = extractValueRequest.getDataFormatProvider().getSimpleDateFormatter()
                            .format((Date) extractValueRequest.getRequestedValue());
                } else if (Timestamp.class.isAssignableFrom(extractValueRequest.getRequestedValue().getClass())) {
                    val = extractValueRequest.getDataFormatProvider().getSimpleDateFormatter()
                            .format(new Date(((Timestamp) extractValueRequest.getRequestedValue()).getTime()));
                } else if (Calendar.class.isAssignableFrom(extractValueRequest.getRequestedValue().getClass())) {
                    val = extractValueRequest.getDataFormatProvider().getSimpleDateFormatter()
                            .format(((Calendar) extractValueRequest.getRequestedValue()).getTime());
                } else if (Double.class.isAssignableFrom(extractValueRequest.getRequestedValue().getClass())) {
                    val = extractValueRequest.getDataFormatProvider().getDecimalFormatter()
                            .format(extractValueRequest.getRequestedValue());
                } else if (BigDecimal.class.isAssignableFrom(extractValueRequest.getRequestedValue().getClass())) {
                    BigDecimal decimal = (BigDecimal) extractValueRequest.getRequestedValue();
                    DecimalFormat format = extractValueRequest.getDataFormatProvider().getDecimalFormatter();
                    //track all the decimal places in the scale of the BigDecimal - even if they're all zeros
                    StringBuilder sb = new StringBuilder();
                    sb.append("0");
                    if (decimal.scale() > 0) {
                        sb.append(".");
                        for (int j = 0; j < decimal.scale(); j++) {
                            sb.append("0");
                        }
                    }
                    format.applyPattern(sb.toString());
                    val = format.format(extractValueRequest.getRequestedValue());
                } else if (extractValueRequest.getMetadata().getForeignKeyClass() != null) {
                    try {
                        val = extractValueRequest.getFieldManager()
                                .getFieldValue(extractValueRequest.getRequestedValue(),
                                        extractValueRequest.getMetadata().getForeignKeyProperty())
                                .toString();
                        //see if there's a name property and use it for the display value
                        String entityName = null;
                        if (extractValueRequest.getRequestedValue() instanceof AdminMainEntity) {
                            entityName = ((AdminMainEntity) extractValueRequest.getRequestedValue())
                                    .getMainEntityName();
                        }

                        Object temp = null;
                        if (!StringUtils
                                .isEmpty(extractValueRequest.getMetadata().getForeignKeyDisplayValueProperty())) {
                            String nameProperty = extractValueRequest.getMetadata()
                                    .getForeignKeyDisplayValueProperty();
                            try {
                                temp = extractValueRequest.getFieldManager()
                                        .getFieldValue(extractValueRequest.getRequestedValue(), nameProperty);
                            } catch (FieldNotAvailableException e) {
                                //do nothing
                            }
                        }

                        if (temp == null && StringUtils.isEmpty(entityName)) {
                            try {
                                temp = extractValueRequest.getFieldManager()
                                        .getFieldValue(extractValueRequest.getRequestedValue(), "name");
                            } catch (FieldNotAvailableException e) {
                                //do nothing
                            }
                        }

                        if (temp != null) {
                            extractValueRequest.setDisplayVal(temp.toString());
                        } else if (!StringUtils.isEmpty(entityName)) {
                            extractValueRequest.setDisplayVal(entityName);
                        }
                    } catch (FieldNotAvailableException e) {
                        throw new IllegalArgumentException(e);
                    }
                } else {
                    val = extractValueRequest.getRequestedValue().toString();
                }
                property.setValue(val);
                property.setDisplayValue(extractValueRequest.getDisplayVal());
            }
        } catch (IllegalAccessException e) {
            throw new PersistenceException(e);
        }
        return FieldProviderResponse.HANDLED;
    }

    @Override
    public FieldProviderResponse addSearchMapping(AddSearchMappingRequest addSearchMappingRequest,
            List<FilterMapping> filterMappings) {
        if (!canHandleSearchMapping(addSearchMappingRequest, filterMappings)) {
            return FieldProviderResponse.NOT_HANDLED;
        }
        Class clazz;
        try {
            clazz = Class.forName(addSearchMappingRequest.getMergedProperties()
                    .get(addSearchMappingRequest.getPropertyName()).getInheritedFromType());
        } catch (ClassNotFoundException e) {
            throw new PersistenceException(e);
        }
        Field field = addSearchMappingRequest.getFieldManager().getField(clazz,
                addSearchMappingRequest.getPropertyName());
        Class<?> targetType = null;
        if (field != null) {
            targetType = field.getType();
        }
        BasicFieldMetadata metadata = (BasicFieldMetadata) addSearchMappingRequest.getMergedProperties()
                .get(addSearchMappingRequest.getPropertyName());

        FilterAndSortCriteria fasc = addSearchMappingRequest.getRequestedCto()
                .get(addSearchMappingRequest.getPropertyName());

        FilterMapping filterMapping = new FilterMapping().withInheritedFromClass(clazz)
                .withFullPropertyName(addSearchMappingRequest.getPropertyName())
                .withFilterValues(fasc.getFilterValues()).withSortDirection(fasc.getSortDirection());
        filterMappings.add(filterMapping);

        if (fasc.hasSpecialFilterValue()) {
            filterMapping.setDirectFilterValues(new EmptyFilterValues());

            // Handle special values on a case by case basis
            List<String> specialValues = fasc.getSpecialFilterValues();
            if (specialValues.contains(FilterAndSortCriteria.IS_NULL_FILTER_VALUE)) {
                filterMapping
                        .setRestriction(new Restriction().withPredicateProvider(new IsNullPredicateProvider()));
            }
            if (specialValues.contains(FilterAndSortCriteria.IS_NOT_NULL_FILTER_VALUE)) {
                filterMapping
                        .setRestriction(new Restriction().withPredicateProvider(new IsNotNullPredicateProvider()));
            }
        } else {
            switch (metadata.getFieldType()) {
            case BOOLEAN:
                if (targetType == null || targetType.equals(Boolean.class) || targetType.equals(boolean.class)) {
                    filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                            RestrictionType.BOOLEAN.getType(), addSearchMappingRequest.getPropertyName()));
                } else {
                    filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                            RestrictionType.CHARACTER.getType(), addSearchMappingRequest.getPropertyName()));
                }
                break;
            case DATE:
                filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory()
                        .getRestriction(RestrictionType.DATE.getType(), addSearchMappingRequest.getPropertyName()));
                break;
            case DECIMAL:
            case MONEY:
                filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                        RestrictionType.DECIMAL.getType(), addSearchMappingRequest.getPropertyName()));
                break;
            case INTEGER:
                filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory()
                        .getRestriction(RestrictionType.LONG.getType(), addSearchMappingRequest.getPropertyName()));
                break;
            case BROADLEAF_ENUMERATION:
                filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                        RestrictionType.STRING_EQUAL.getType(), addSearchMappingRequest.getPropertyName()));
                break;
            default:
                filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                        RestrictionType.STRING_LIKE.getType(), addSearchMappingRequest.getPropertyName()));
                break;
            case FOREIGN_KEY:
                if (!addSearchMappingRequest.getRequestedCto().get(addSearchMappingRequest.getPropertyName())
                        .getFilterValues().isEmpty()) {
                    ForeignKey foreignKey = (ForeignKey) addSearchMappingRequest.getPersistencePerspective()
                            .getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.FOREIGNKEY);
                    if (metadata.getForeignKeyCollection()) {
                        if (ForeignKeyRestrictionType.COLLECTION_SIZE_EQ.toString()
                                .equals(foreignKey.getRestrictionType().toString())) {
                            filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory()
                                    .getRestriction(RestrictionType.COLLECTION_SIZE_EQUAL.getType(),
                                            addSearchMappingRequest.getPropertyName()));
                            filterMapping.setFieldPath(new FieldPath());
                        } else {
                            filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory()
                                    .getRestriction(RestrictionType.LONG.getType(),
                                            addSearchMappingRequest.getPropertyName()));
                            filterMapping.setFieldPath(
                                    new FieldPath().withTargetProperty(addSearchMappingRequest.getPropertyName()
                                            + "." + metadata.getForeignKeyProperty()));
                        }
                    } else if (addSearchMappingRequest.getRequestedCto()
                            .get(addSearchMappingRequest.getPropertyName()).getFilterValues().get(0) == null
                            || "null".equals(addSearchMappingRequest.getRequestedCto()
                                    .get(addSearchMappingRequest.getPropertyName()).getFilterValues().get(0))) {
                        filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                                RestrictionType.IS_NULL_LONG.getType(), addSearchMappingRequest.getPropertyName()));
                    } else if (metadata.getSecondaryType() == SupportedFieldType.STRING) {
                        filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                                RestrictionType.STRING_EQUAL.getType(), addSearchMappingRequest.getPropertyName()));
                        filterMapping.setFieldPath(
                                new FieldPath().withTargetProperty(addSearchMappingRequest.getPropertyName() + "."
                                        + metadata.getForeignKeyProperty()));
                    } else {
                        filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                                RestrictionType.LONG_EQUAL.getType(), addSearchMappingRequest.getPropertyName()));
                        filterMapping.setFieldPath(
                                new FieldPath().withTargetProperty(addSearchMappingRequest.getPropertyName() + "."
                                        + metadata.getForeignKeyProperty()));
                    }
                }
                break;
            case ADDITIONAL_FOREIGN_KEY:
                int additionalForeignKeyIndexPosition = Arrays.binarySearch(
                        addSearchMappingRequest.getPersistencePerspective().getAdditionalForeignKeys(),
                        new ForeignKey(addSearchMappingRequest.getPropertyName(), null, null),
                        new Comparator<ForeignKey>() {
                            @Override
                            public int compare(ForeignKey o1, ForeignKey o2) {
                                return o1.getManyToField().compareTo(o2.getManyToField());
                            }
                        });
                ForeignKey foreignKey = null;
                if (additionalForeignKeyIndexPosition >= 0) {
                    foreignKey = addSearchMappingRequest.getPersistencePerspective()
                            .getAdditionalForeignKeys()[additionalForeignKeyIndexPosition];
                }
                // in the case of a to-one lookup, an explicit ForeignKey is not passed in. The system should then
                // default to just using a ForeignKeyRestrictionType.ID_EQ
                if (metadata.getForeignKeyCollection()) {
                    if (ForeignKeyRestrictionType.COLLECTION_SIZE_EQ.toString()
                            .equals(foreignKey.getRestrictionType().toString())) {
                        filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                                RestrictionType.COLLECTION_SIZE_EQUAL.getType(),
                                addSearchMappingRequest.getPropertyName()));
                        filterMapping.setFieldPath(new FieldPath());
                    } else {
                        filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                                RestrictionType.LONG.getType(), addSearchMappingRequest.getPropertyName()));
                        filterMapping.setFieldPath(
                                new FieldPath().withTargetProperty(addSearchMappingRequest.getPropertyName() + "."
                                        + metadata.getForeignKeyProperty()));
                    }
                } else if (CollectionUtils
                        .isEmpty(addSearchMappingRequest.getRequestedCto()
                                .get(addSearchMappingRequest.getPropertyName()).getFilterValues())
                        || addSearchMappingRequest.getRequestedCto().get(addSearchMappingRequest.getPropertyName())
                                .getFilterValues().get(0) == null
                        || "null".equals(addSearchMappingRequest.getRequestedCto()
                                .get(addSearchMappingRequest.getPropertyName()).getFilterValues().get(0))) {
                    filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                            RestrictionType.IS_NULL_LONG.getType(), addSearchMappingRequest.getPropertyName()));
                } else if (metadata.getSecondaryType() == SupportedFieldType.STRING) {
                    filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                            RestrictionType.STRING_EQUAL.getType(), addSearchMappingRequest.getPropertyName()));
                    filterMapping.setFieldPath(new FieldPath().withTargetProperty(
                            addSearchMappingRequest.getPropertyName() + "." + metadata.getForeignKeyProperty()));
                } else {
                    filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                            RestrictionType.LONG_EQUAL.getType(), addSearchMappingRequest.getPropertyName()));
                    filterMapping.setFieldPath(new FieldPath().withTargetProperty(
                            addSearchMappingRequest.getPropertyName() + "." + metadata.getForeignKeyProperty()));
                }
                break;
            case ID:
                switch (metadata.getSecondaryType()) {
                case INTEGER:
                    filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                            RestrictionType.LONG_EQUAL.getType(), addSearchMappingRequest.getPropertyName()));
                    break;
                case STRING:
                    filterMapping.setRestriction(addSearchMappingRequest.getRestrictionFactory().getRestriction(
                            RestrictionType.STRING_EQUAL.getType(), addSearchMappingRequest.getPropertyName()));
                    break;
                }
                break;
            }
        }
        return FieldProviderResponse.HANDLED;
    }

    @Override
    public int getOrder() {
        return FieldPersistenceProvider.BASIC;
    }
}