org.kuali.rice.krad.data.jpa.Filter.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.rice.krad.data.jpa.Filter.java

Source

/**
 * Copyright 2005-2014 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.data.jpa;

import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;
import org.eclipse.persistence.mappings.OneToManyMapping;
import org.eclipse.persistence.mappings.OneToOneMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.AttributeConverter;
import javax.persistence.Convert;
import java.lang.reflect.Field;
import java.text.NumberFormat;
import java.util.List;

/**
 * Takes a filter generator and executes the changes on the class descriptor for a field.
 *
 * @author Kuali Rice Team (rice.collab@kuali.org)
 */
public class Filter {

    private static final Logger LOG = LoggerFactory.getLogger(Filter.class);

    /**
     * Takes a list of filter generators and executes the changes on the class descriptor for a field.
     *
     * @param filterGenerators a list of filter generators.
     * @param descriptor the class descriptor to execute the changes on.
     * @param propertyName the property name of the field to change.
     */
    public static void customizeField(List<FilterGenerator> filterGenerators, ClassDescriptor descriptor,
            String propertyName) {

        Expression exp = null;
        ForeignReferenceMapping mapping = null;

        if (OneToOneMapping.class
                .isAssignableFrom(descriptor.getMappingForAttributeName(propertyName).getClass())) {
            OneToOneMapping databaseMapping = ((OneToOneMapping) descriptor
                    .getMappingForAttributeName(propertyName));
            exp = databaseMapping.buildSelectionCriteria();
            mapping = (ForeignReferenceMapping) databaseMapping;
        } else if (OneToManyMapping.class
                .isAssignableFrom(descriptor.getMappingForAttributeName(propertyName).getClass())) {
            OneToManyMapping databaseMapping = ((OneToManyMapping) descriptor
                    .getMappingForAttributeName(propertyName));
            exp = databaseMapping.buildSelectionCriteria();
            mapping = (ForeignReferenceMapping) databaseMapping;
        } else {
            throw new RuntimeException(
                    "Mapping type not implemented for query customizer for property " + propertyName);
        }

        for (FilterGenerator filterGenerator : filterGenerators) {
            FilterOperators operator = filterGenerator.operator();
            if (!operator.equals(FilterOperators.EQUAL)) {
                throw new UnsupportedOperationException(
                        "Operator " + operator.getValue() + " not supported in Filter");
            }
            String attributeName = filterGenerator.attributeName();
            Object attributeValue = coerce(mapping.getReferenceClass(), attributeName,
                    filterGenerator.attributeValue());
            Class<?> attributeValueClass = filterGenerator.attributeResolverClass();

            if (exp != null && mapping != null) {
                ExpressionBuilder builder = exp.getBuilder();
                if (!attributeValueClass.equals(Void.class)) {
                    try {
                        FilterValue filterValue = (FilterValue) attributeValueClass.newInstance();
                        attributeValue = filterValue.getValue();
                    } catch (Exception e) {
                        throw new RuntimeException(
                                "Cannot find query customizer attribute class" + attributeValueClass);
                    }
                }

                if (attributeValue != null) {
                    Expression addedExpression = builder.get(attributeName).equal(attributeValue);
                    exp = exp.and(addedExpression);
                    mapping.setSelectionCriteria(exp);
                }
            }
        }
    }

    /**
     * Coerces the {@code attributeValue} for the {@code attributeName} based on the field type in the
     * {@code referenceClass}.
     *
     * <p>
     * If the {@code attributeValue} is strictly null or empty (that is, has zero length) then this will just pass it
     * back since it cannot coerce the value further.  This will then search for the field, and if it finds a matching
     * field, it will attempt to first convert using the {@link Convert} converter if available.  If the converter is
     * not available, it will attempt to coerce from the {@code attributeValue} to one of {@link Character},
     * {@link Boolean}, or any of the wrapped number types.  Otherwise, it will just pass the {@code attributeValue}
     * back.
     * </p>
     *
     * @param referenceClass the class to execute the changes on.
     * @param attributeName the attribute name of the field to coerce.
     * @param attributeValue the value to coerce.
     * @return the coerced value.
     */
    private static Object coerce(Class<?> referenceClass, String attributeName, String attributeValue) {
        if (StringUtils.isEmpty(attributeValue)) {
            return attributeValue;
        }

        Field field = null;
        try {
            field = referenceClass.getDeclaredField(attributeName);
        } catch (NoSuchFieldException nsfe) {
            LOG.error("Could not locate the field " + attributeName + " in " + referenceClass.getName(), nsfe);
        }

        if (field != null) {
            Convert convert = field.getAnnotation(Convert.class);

            if (convert != null) {
                return coerceConverterValue(convert, attributeName, attributeValue);
            }

            return coerceValue(field.getType(), attributeName, attributeValue);
        }

        return attributeValue;
    }

    /**
     * Uses the {@link Convert} converter to first translate the {@code attributeValue} to the desired type and then
     * convert it to the database format for searching.
     *
     * @param convert the conversion annotation.
     * @param attributeName the attribute name of the field to coerce.
     * @param attributeValue the value to coerce.
     * @return the coerced value.
     */
    private static Object coerceConverterValue(Convert convert, String attributeName, String attributeValue) {
        try {
            AttributeConverter<Object, String> converter = (AttributeConverter<Object, String>) convert.converter()
                    .newInstance();
            Object entityAttribute = converter.convertToEntityAttribute(attributeValue);
            return converter.convertToDatabaseColumn(entityAttribute);
        } catch (Exception e) {
            LOG.error("Could not create the converter for " + attributeName, e);
        }

        return attributeValue;
    }

    /**
     * Coerces the {@code attributeValue} using the given {@code type}.
     *
     * @param type the type to use to coerce the value.
     * @param attributeName the attribute name of the field to coerce.
     * @param attributeValue the value to coerce.
     * @return the coerced value.
     */
    private static Object coerceValue(Class<?> type, String attributeName, String attributeValue) {
        try {
            if (Character.TYPE.equals(type) || Character.class.isAssignableFrom(type)) {
                return Character.valueOf(attributeValue.charAt(0));
            } else if (Boolean.TYPE.equals(type) || Boolean.class.isAssignableFrom(type)) {
                return Boolean.valueOf(attributeValue);
            } else if (Short.TYPE.equals(type) || Short.class.isAssignableFrom(type)) {
                return Short.valueOf(attributeValue);
            } else if (Integer.TYPE.equals(type) || Integer.class.isAssignableFrom(type)) {
                return Integer.valueOf(attributeValue);
            } else if (Long.TYPE.equals(type) || Long.class.isAssignableFrom(type)) {
                return Long.valueOf(attributeValue);
            } else if (Double.TYPE.equals(type) || Double.class.isAssignableFrom(type)) {
                return Double.valueOf(attributeValue);
            } else if (Float.TYPE.equals(type) || Float.class.isAssignableFrom(type)) {
                return Float.valueOf(attributeValue);
            }
        } catch (NumberFormatException nfe) {
            LOG.error("Could not coerce the value " + attributeValue + " for the field " + attributeName, nfe);
        }

        return attributeValue;
    }

}