org.gvnix.web.datatables.util.QuerydslUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.gvnix.web.datatables.util.QuerydslUtils.java

Source

/*
 * gvNIX. Spring Roo based RAD tool for Generalitat Valenciana
 * Copyright (C) 2013 Generalitat Valenciana
 *
 * 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.
 */
package org.gvnix.web.datatables.util;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.mysema.query.BooleanBuilder;
import com.mysema.query.types.Order;
import com.mysema.query.types.OrderSpecifier;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.expr.BooleanExpression;
import com.mysema.query.types.path.DatePath;
import com.mysema.query.types.path.NumberPath;
import com.mysema.query.types.path.PathBuilder;

/**
 * Querydsl utility functions
 * 
 * @author gvNIX team
 * 
 * @deprecated use {@link QuerydslUtilsBean} instead
 */
@Deprecated
public class QuerydslUtils {

    public static final String OPERATOR_GOE = "goe";
    public static final String OPERATOR_LOE = "loe";
    public static final String OPERATOR_ISNULL = "isnull";
    public static final String OPERATOR_NOTNULL = "notnull";
    public static final String G_FIL_OPE_ISNULL = "global.filters.operations.all.isnull";
    public static final String G_FIL_OPE_NOTNULL = "global.filters.operations.all.notnull";

    public static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);

    private static LoadingCache<Class<?>, BeanWrapper> beanWrappersCache = CacheBuilder.newBuilder()
            .maximumSize(200).build(new CacheLoader<Class<?>, BeanWrapper>() {
                public BeanWrapper load(Class<?> key) {
                    return new BeanWrapperImpl(key);
                }
            });

    public static final Set<Class<?>> NUMBER_PRIMITIVES = new HashSet<Class<?>>(
            Arrays.asList(new Class<?>[] { int.class, long.class, double.class, float.class, short.class }));

    public static final String OPERATOR_PREFIX = "_operator_";

    private static final String SEPARATOR_FIELDS = ".";

    public static final String[] FULL_DATE_PATTERNS = new String[] { "dd-MM-yyyy HH:mm:ss", "dd/MM/yyyy HH:mm:ss",
            "MM-dd-yyyy HH:mm:ss", "MM/dd/yyyy HH:mm:ss", "dd-MM-yyyy HH:mm", "dd/MM/yyyy HH:mm",
            "MM-dd-yyyy HH:mm", "MM/dd/yyyy HH:mm", "dd-MM-yyyy", "dd/MM/yyyy", "MM-dd-yyyy", "MM/dd/yyyy",
            "dd-MMMM-yyyy HH:mm:ss", "dd/MMMM/yyyy HH:mm:ss", "MMMM-dd-yyyy HH:mm:ss", "MMMM/dd/yyyy HH:mm:ss",
            "dd-MMMM-yyyy HH:mm", "dd/MMMM/yyyy HH:mm", "MMMM-dd-yyyy HH:mm", "MMMM/dd/yyyy HH:mm", "dd-MMMM-yyyy",
            "dd/MMMM/yyyy", "MMMM-dd-yyyy", "MMMM/dd/yyyy" };

    public static final String[] FULL_DATE_PATTERNS_WITH_TIME = new String[] { "dd-MM-yyyy HH:mm:ss",
            "dd/MM/yyyy HH:mm:ss", "MM-dd-yyyy HH:mm:ss", "MM/dd/yyyy HH:mm:ss", "dd-MM-yyyy HH:mm",
            "dd/MM/yyyy HH:mm", "MM-dd-yyyy HH:mm", "MM/dd/yyyy HH:mm", "dd-MMMM-yyyy HH:mm:ss",
            "dd/MMMM/yyyy HH:mm:ss", "MMMM-dd-yyyy HH:mm:ss", "MMMM/dd/yyyy HH:mm:ss", "dd-MMMM-yyyy HH:mm",
            "dd/MMMM/yyyy HH:mm", "MMMM-dd-yyyy HH:mm", "MMMM/dd/yyyy HH:mm" };

    public static final String[] FULL_DATE_PAT_WO_TIME = new String[] { "dd-MM-yyyy", "dd/MM/yyyy", "MM-dd-yyyy",
            "MMMM/dd/yyyy", "dd-MMMM-yyyy", "dd/MMMM/yyyy", "MMMM-dd-yyyy", "MMMM/dd/yyyy" };

    public static final String[] DAY_AND_MONTH_DATE_PATTERNS = new String[] { "dd-MM", "dd/MM", "MM-dd", "MM/dd",
            "dd-MMMM", "dd/MMMM", "MMMM-dd", "MMMM/dd" };

    public static final String[] MONTH_AND_YEAR_DATE_PATTERNS = new String[] { "MM-yyyy", "MM/yyyy", "MMMM-yyyy",
            "MMMM/yyyy" };

    /**
     * Get BeanWrapper instance for klass. <b>Warning<b>: BeanWrapper returned
     * is not Thread-safe!!!
     * 
     * @param klass
     * @return
     */
    private static BeanWrapper getBeanWrapper(Class<?> klass) {
        BeanWrapper beanWrapper;
        try {
            beanWrapper = beanWrappersCache.get(klass);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
        return beanWrapper;
    }

    /**
     * Creates a WHERE clause by the intersection of the given search-arguments
     * 
     * @param entity Entity {@link PathBuilder}. It represents the entity for
     *        class generation and alias-usage for path generation.
     *        <p/>
     *        Example: To retrieve a {@code Customer} with the first name 'Bob'
     *        entity must be a {@link PathBuilder} created for {@code Customer}
     *        class and searchArgs must contain the entry
     *        {@code 'firstName':'Bob'}
     * @param searchArgs Search arguments to be used to create the WHERE clause.
     *        It can contain {@code _operator_} entries for each field that want
     *        to use its own operator. By default {@code EQUALS} operator is
     *        used.
     *        <p/>
     *        Operator entry example: {@code _operator_weight = LT} the
     *        expression for {@code weight} field will do a less-than value
     *        comparison
     * @param conversionService required to transform values
     * @return the WHERE clause
     */
    public static <T> BooleanBuilder createPredicateByAnd(PathBuilder<T> entity, Map<String, Object> searchArgs,
            ConversionService conversionService) {

        // Using BooleanBuilder, a cascading builder for
        // Predicate expressions
        BooleanBuilder predicate = new BooleanBuilder();
        if (searchArgs == null || searchArgs.isEmpty()) {
            return predicate;
        }

        // Build the predicate
        for (Entry<String, Object> entry : searchArgs.entrySet()) {
            String key = entry.getKey();
            // can
            // contain "_operator_"
            // entries for each
            // field
            Object valueToSearch = entry.getValue();
            String operator = (String) searchArgs.get(OPERATOR_PREFIX.concat(key));

            // If value to search is a collection, creates a predicate for
            // each object of the collection
            if (valueToSearch instanceof Collection) {
                @SuppressWarnings("unchecked")
                Collection<Object> valueColl = (Collection<Object>) valueToSearch;
                for (Object valueObj : valueColl) {
                    predicate.and(createObjectExpression(entity, key, valueObj, operator, conversionService));
                }
            } else {
                predicate.and(createObjectExpression(entity, key, valueToSearch, operator, conversionService));
            }
        }
        return predicate;
    }

    /**
     * Creates a WHERE clause to specify given {@code fieldName} must be equal
     * to one element of the provided Collection.
     * 
     * @param entity Entity {@link PathBuilder}. It represents the entity for
     *        class generation and alias-usage for path generation.
     *        <p/>
     *        Example: To retrieve a {@code Customer} with the first name 'Bob'
     *        entity must be a {@link PathBuilder} created for {@code Customer}
     *        class and searchArgs must contain the entry
     *        {@code 'firstName':'Bob'}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param values the Set of values to find the given field name, may be null
     * @return the WHERE clause
     */
    public static <T, E> BooleanBuilder createPredicateByIn(PathBuilder<T> entity, String fieldName,
            Set<E> values) {

        // Using BooleanBuilder, a cascading builder for
        // Predicate expressions
        BooleanBuilder predicate = new BooleanBuilder();
        if (StringUtils.isEmpty(fieldName) || values.isEmpty()) {
            return predicate;
        }

        // Build the predicate
        predicate.and(createCollectionExpression(entity, fieldName, values));

        return predicate;
    }

    /**
     * Utility for constructing where clause expressions.
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param fieldType Property value {@code Class}
     * @param searchStr the value to find, may be null
     * @return Predicate
     * @deprecated
     */
    public static <T> Predicate createExpression(PathBuilder<T> entityPath, String fieldName, Class<?> fieldType,
            String searchStr) {
        return createExpression(entityPath, fieldName, searchStr, null);
    }

    /**
     * Utility for constructing where clause expressions.
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param fieldType Property value {@code Class}
     * @param searchStr the value to find, may be null
     * @return Predicate
     * @deprecated
     */
    public static <T> Predicate createExpression(PathBuilder<T> entityPath, String fieldName, String searchStr) {
        return createExpression(entityPath, fieldName, searchStr, null);
    }

    /**
     * Utility for constructing where clause expressions.
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param fieldType Property value {@code Class}
     * @param searchStr the value to find, may be null
     * @return Predicate
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static <T> Predicate createExpression(PathBuilder<T> entityPath, String fieldName, String searchStr,
            ConversionService conversionService) {

        TypeDescriptor descriptor = getTypeDescriptor(fieldName, entityPath);
        if (descriptor == null) {
            throw new IllegalArgumentException(
                    String.format("Can't found field '%s' on entity '%s'", fieldName, entityPath.getType()));
        }
        Class<?> fieldType = descriptor.getType();

        // Check for field type in order to delegate in custom-by-type
        // create expression method
        if (String.class == fieldType) {
            return createStringLikeExpression(entityPath, fieldName, searchStr);
        } else if (Boolean.class == fieldType || boolean.class == fieldType) {
            return createBooleanExpression(entityPath, fieldName, searchStr);
        } else if (Number.class.isAssignableFrom(fieldType) || NUMBER_PRIMITIVES.contains(fieldType)) {
            return createNumberExpressionGenerics(entityPath, fieldName, fieldType, descriptor, searchStr,
                    conversionService);
        } else if (Date.class.isAssignableFrom(fieldType) || Calendar.class.isAssignableFrom(fieldType)) {
            BooleanExpression expression = createDateExpression(entityPath, fieldName, (Class<Date>) fieldType,
                    searchStr);
            return expression;
        }

        else if (fieldType.isEnum()) {
            return createEnumExpression(entityPath, fieldName, searchStr, (Class<? extends Enum>) fieldType);
        }
        return null;
    }

    /**
     * Utility for constructing where clause expressions.
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param fieldType Property value {@code Class}
     * @param searchStr the value to find, may be null
     * @return Predicate
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static <T> Predicate createExpression(PathBuilder<T> entityPath, String fieldName, String searchStr,
            ConversionService conversionService, MessageSource messageSource) {

        TypeDescriptor descriptor = getTypeDescriptor(fieldName, entityPath);
        if (descriptor == null) {
            throw new IllegalArgumentException(
                    String.format("Can't found field '%s' on entity '%s'", fieldName, entityPath.getType()));
        }
        Class<?> fieldType = descriptor.getType();

        // Check for field type in order to delegate in custom-by-type
        // create expression method
        if (String.class == fieldType) {
            return createStringExpressionWithOperators(entityPath, fieldName, searchStr, conversionService,
                    messageSource);
        } else if (Boolean.class == fieldType || boolean.class == fieldType) {
            return createBooleanExpressionWithOperators(entityPath, fieldName, searchStr, conversionService,
                    messageSource);
        } else if (Number.class.isAssignableFrom(fieldType) || NUMBER_PRIMITIVES.contains(fieldType)) {
            return createNumberExpressionGenericsWithOperators(entityPath, fieldName, descriptor, searchStr,
                    conversionService, messageSource);
        } else if (Date.class.isAssignableFrom(fieldType) || Calendar.class.isAssignableFrom(fieldType)) {
            String datePattern = "dd/MM/yyyy";
            if (messageSource != null) {
                datePattern = messageSource.getMessage("global.filters.operations.date.pattern", null,
                        LocaleContextHolder.getLocale());
            }
            BooleanExpression expression = createDateExpressionWithOperators(entityPath, fieldName,
                    (Class<Date>) fieldType, searchStr, conversionService, messageSource, datePattern);
            return expression;
        }

        else if (fieldType.isEnum()) {
            return createEnumExpression(entityPath, fieldName, searchStr, (Class<? extends Enum>) fieldType);
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static <T> Predicate createNumberExpressionGenerics(PathBuilder<T> entityPath, String fieldName,
            Class<?> fieldType, TypeDescriptor descriptor, String searchStr, ConversionService conversionService) {
        Predicate numberExpression = null;

        if (isNumber(searchStr, conversionService, descriptor)) {
            if (BigDecimal.class.isAssignableFrom(fieldType)) {
                numberExpression = createNumberExpression(entityPath, fieldName, (Class<BigDecimal>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (BigInteger.class.isAssignableFrom(fieldType)) {
                numberExpression = createNumberExpression(entityPath, fieldName, (Class<BigInteger>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Byte.class.isAssignableFrom(fieldType)) {
                numberExpression = createNumberExpression(entityPath, fieldName, (Class<Byte>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Double.class.isAssignableFrom(fieldType) || double.class == fieldType) {
                numberExpression = createNumberExpression(entityPath, fieldName, (Class<Double>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Float.class.isAssignableFrom(fieldType) || float.class == fieldType) {
                numberExpression = createNumberExpression(entityPath, fieldName, (Class<Float>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Integer.class.isAssignableFrom(fieldType) || int.class == fieldType) {
                numberExpression = createNumberExpression(entityPath, fieldName, (Class<Integer>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Long.class.isAssignableFrom(fieldType) || long.class == fieldType) {
                numberExpression = createNumberExpression(entityPath, fieldName, (Class<Long>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Short.class.isAssignableFrom(fieldType) || short.class == fieldType) {
                numberExpression = createNumberExpression(entityPath, fieldName, (Class<Short>) fieldType,
                        descriptor, searchStr, conversionService);
            }
        }
        return numberExpression;
    }

    @SuppressWarnings("unchecked")
    public static <T> Predicate createNumberExpressionGenericsWithOperators(PathBuilder<T> entityPath,
            String fieldName, TypeDescriptor descriptor, String searchStr, ConversionService conversionService,
            MessageSource messageSource) {
        Predicate numberExpression = null;

        Class<?> fieldType = descriptor.getType();

        if (isNumber(searchStr, conversionService, descriptor)) {
            if (BigDecimal.class.isAssignableFrom(fieldType)) {
                numberExpression = createNumberExpressionEqual(entityPath, fieldName, (Class<BigDecimal>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (BigInteger.class.isAssignableFrom(fieldType)) {
                numberExpression = createNumberExpressionEqual(entityPath, fieldName, (Class<BigInteger>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Byte.class.isAssignableFrom(fieldType)) {
                numberExpression = createNumberExpressionEqual(entityPath, fieldName, (Class<Byte>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Double.class.isAssignableFrom(fieldType) || double.class == fieldType) {
                numberExpression = createNumberExpressionEqual(entityPath, fieldName, (Class<Double>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Float.class.isAssignableFrom(fieldType) || float.class == fieldType) {
                numberExpression = createNumberExpressionEqual(entityPath, fieldName, (Class<Float>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Integer.class.isAssignableFrom(fieldType) || int.class == fieldType) {
                numberExpression = createNumberExpressionEqual(entityPath, fieldName, (Class<Integer>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Long.class.isAssignableFrom(fieldType) || long.class == fieldType) {
                numberExpression = createNumberExpressionEqual(entityPath, fieldName, (Class<Long>) fieldType,
                        descriptor, searchStr, conversionService);
            }
            if (Short.class.isAssignableFrom(fieldType) || short.class == fieldType) {
                numberExpression = createNumberExpressionEqual(entityPath, fieldName, (Class<Short>) fieldType,
                        descriptor, searchStr, conversionService);
            }
        } else {
            // If is not a number, can be possible that exists a filter
            // expression.
            if (BigDecimal.class.isAssignableFrom(fieldType)) {
                numberExpression = getNumericFilterExpression(entityPath, fieldName, (Class<BigDecimal>) fieldType,
                        descriptor, searchStr, conversionService, messageSource);
            }
            if (BigInteger.class.isAssignableFrom(fieldType)) {
                numberExpression = getNumericFilterExpression(entityPath, fieldName, (Class<BigInteger>) fieldType,
                        descriptor, searchStr, conversionService, messageSource);
            }
            if (Byte.class.isAssignableFrom(fieldType)) {
                numberExpression = getNumericFilterExpression(entityPath, fieldName, (Class<Byte>) fieldType,
                        descriptor, searchStr, conversionService, messageSource);
            }
            if (Double.class.isAssignableFrom(fieldType) || double.class == fieldType) {
                numberExpression = getNumericFilterExpression(entityPath, fieldName, (Class<Double>) fieldType,
                        descriptor, searchStr, conversionService, messageSource);
            }
            if (Float.class.isAssignableFrom(fieldType) || float.class == fieldType) {
                numberExpression = getNumericFilterExpression(entityPath, fieldName, (Class<Float>) fieldType,
                        descriptor, searchStr, conversionService, messageSource);
            }
            if (Integer.class.isAssignableFrom(fieldType) || int.class == fieldType) {
                numberExpression = getNumericFilterExpression(entityPath, fieldName, (Class<Integer>) fieldType,
                        descriptor, searchStr, conversionService, messageSource);
            }
            if (Long.class.isAssignableFrom(fieldType) || long.class == fieldType) {
                numberExpression = getNumericFilterExpression(entityPath, fieldName, (Class<Long>) fieldType,
                        descriptor, searchStr, conversionService, messageSource);
            }
            if (Short.class.isAssignableFrom(fieldType) || short.class == fieldType) {
                numberExpression = getNumericFilterExpression(entityPath, fieldName, (Class<Short>) fieldType,
                        descriptor, searchStr, conversionService, messageSource);
            }
        }
        return numberExpression;
    }

    /**
     * Return equal expression for {@code entityPath.fieldName}.
     * <p/>
     * Expr: {@code entityPath.fieldName eq searchObj}
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param searchObj the value to find, may be null
     * @return BooleanExpression
     */
    public static <T> BooleanExpression createObjectExpression(PathBuilder<T> entityPath, String fieldName,
            Object searchObj, ConversionService conversionService) {
        return createObjectExpression(entityPath, fieldName, searchObj, null, conversionService);
    }

    /**
     * Return an expression for {@code entityPath.fieldName} with the
     * {@code operator} or "equal" by default.
     * <p/>
     * Expr: {@code entityPath.fieldName eq searchObj}
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param searchObj the value to find, may be null
     * @param operator the operator to use into the expression. Supported
     *        operators:
     *        <ul>
     *        <li>For all types: {@code eq}, {@code in}, {@code ne},
     *        {@code notIn}, {@code isNull} and {@code isNotNull}.</li> <li> For
     *        strings and numbers: {@code goe}, {@code gt}, {@code loe},
     *        {@code lt} and {@code like}.</li> <li> For booleans: {@code goe},
     *        {@code gt}, {@code loe} and {@code lt}.</li> <li> For dates:
     *        {@code goe}, {@code gt}, {@code before}, {@code loe}, {@code lt}
     *        and {@code after}. </li>
     *        </ul>
     * @return BooleanExpression
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static <T> BooleanExpression createObjectExpression(PathBuilder<T> entityPath, String fieldName,
            Object searchObj, String operator, ConversionService conversionService) {
        if (searchObj == null) {
            return null;
        }

        TypeDescriptor typeDescriptor = getTypeDescriptor(fieldName, entityPath);
        if (typeDescriptor == null) {
            throw new IllegalArgumentException(
                    String.format("Can't found field '%s' on entity '%s'", fieldName, entityPath.getType()));
        }

        if (StringUtils.isBlank(operator) || StringUtils.equalsIgnoreCase(operator, "eq")) {
            return entityPath.get(fieldName).eq(searchObj);
        } else if (StringUtils.equalsIgnoreCase(operator, "in")) {
            return entityPath.get(fieldName).in(searchObj);
        } else if (StringUtils.equalsIgnoreCase(operator, "ne")) {
            return entityPath.get(fieldName).ne(searchObj);
        } else if (StringUtils.equalsIgnoreCase(operator, "notIn")) {
            return entityPath.get(fieldName).notIn(searchObj);
        } else if (StringUtils.equalsIgnoreCase(operator, OPERATOR_ISNULL)) {
            return entityPath.get(fieldName).isNull();
        } else if (StringUtils.equalsIgnoreCase(operator, "isNotNull")) {
            return entityPath.get(fieldName).isNotNull();
        }

        Class<?> fieldType = getFieldType(fieldName, entityPath);
        if (String.class == fieldType && String.class == searchObj.getClass()) {
            return createStringExpression(entityPath, fieldName, searchObj, operator);
        } else if ((Boolean.class == fieldType || boolean.class == fieldType)
                && String.class == searchObj.getClass()) {
            return createBooleanExpression(entityPath, fieldName, searchObj, operator);
        } else if ((Number.class.isAssignableFrom(fieldType) || NUMBER_PRIMITIVES.contains(fieldType))
                && String.class == searchObj.getClass()
                && isValidValueFor((String) searchObj, typeDescriptor, conversionService)) {
            return createNumericExpression(entityPath, fieldName, searchObj, operator, fieldType);

        } else if ((Date.class.isAssignableFrom(fieldType) || Calendar.class.isAssignableFrom(fieldType))
                && String.class == searchObj.getClass()) {
            return createDateExpression(entityPath, fieldName, searchObj, operator, fieldType);
        } else if (fieldType.isEnum() && String.class == searchObj.getClass()) {
            return createEnumExpression(entityPath, fieldName, (String) searchObj,
                    (Class<? extends Enum>) fieldType);
        }

        return entityPath.get(fieldName).eq(searchObj);
    }

    /**
     * Check if a string is valid for a type <br/>
     * If conversion service is not provided try to check by apache commons
     * utilities. <b>TODO</b> in this (no-conversionService) case just
     * implemented for numerics
     * 
     * @param string
     * @param typeDescriptor
     * @param conversionService (optional)
     * @return
     */
    private static boolean isValidValueFor(String string, TypeDescriptor typeDescriptor,
            ConversionService conversionService) {
        if (conversionService != null) {
            try {
                conversionService.convert(string, STRING_TYPE_DESCRIPTOR, typeDescriptor);
            } catch (ConversionException e) {
                return false;
            }
            return true;
        } else {
            Class<?> fieldType = typeDescriptor.getType();
            if (Number.class.isAssignableFrom(fieldType) || NUMBER_PRIMITIVES.contains(fieldType)) {
                return NumberUtils.isNumber(string);
            }
            // TODO implement other types
            return true;
        }
    }

    /**
     * Return an expression for {@code entityPath.fieldName} (for Dates) with
     * the {@code operator} or "equal" by default.
     * <p/>
     * Expr: {@code entityPath.fieldName eq searchObj}
     * 
     * @param entityPath
     * @param fieldName
     * @param searchObj
     * @param operator
     * @param fieldType
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> BooleanExpression createDateExpression(PathBuilder<T> entityPath, String fieldName,
            Object searchObj, String operator, Class<?> fieldType) {
        DatePath<Date> dateExpression = entityPath.getDate(fieldName, (Class<Date>) fieldType);
        try {
            Date value = DateUtils.parseDateStrictly((String) searchObj, FULL_DATE_PATTERNS);
            if (StringUtils.equalsIgnoreCase(operator, OPERATOR_GOE)) {
                return dateExpression.goe(value);
            } else if (StringUtils.equalsIgnoreCase(operator, "gt")
                    || StringUtils.equalsIgnoreCase(operator, "after")) {
                return dateExpression.gt(value);
            } else if (StringUtils.equalsIgnoreCase(operator, OPERATOR_LOE)) {
                return dateExpression.loe(value);
            } else if (StringUtils.equalsIgnoreCase(operator, "lt")
                    || StringUtils.equalsIgnoreCase(operator, "before")) {
                return dateExpression.lt(value);
            }
        } catch (ParseException e) {
            return entityPath.get(fieldName).eq(searchObj);
        }
        return entityPath.get(fieldName).eq(searchObj);
    }

    /**
     * Return an expression for {@code entityPath.fieldName} (for Numerics) with
     * the {@code operator} or "equal" by default.
     * <p/>
     * Expr: {@code entityPath.fieldName eq searchObj}
     * 
     * @param entityPath
     * @param fieldName
     * @param searchObj
     * @param operator
     * @param fieldType
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static <T> BooleanExpression createNumericExpression(PathBuilder<T> entityPath, String fieldName,
            Object searchObj, String operator, Class<?> fieldType) {
        NumberPath numberExpression = null;
        if (BigDecimal.class.isAssignableFrom(fieldType)) {
            numberExpression = entityPath.getNumber(fieldName, (Class<BigDecimal>) fieldType);
        } else if (BigInteger.class.isAssignableFrom(fieldType)) {
            numberExpression = entityPath.getNumber(fieldName, (Class<BigInteger>) fieldType);
        } else if (Byte.class.isAssignableFrom(fieldType)) {
            numberExpression = entityPath.getNumber(fieldName, (Class<Byte>) fieldType);
        } else if (Double.class.isAssignableFrom(fieldType) || double.class == fieldType) {
            numberExpression = entityPath.getNumber(fieldName, (Class<Double>) fieldType);
        } else if (Float.class.isAssignableFrom(fieldType) || float.class == fieldType) {
            numberExpression = entityPath.getNumber(fieldName, (Class<Float>) fieldType);
        } else if (Integer.class.isAssignableFrom(fieldType) || int.class == fieldType) {
            numberExpression = entityPath.getNumber(fieldName, (Class<Integer>) fieldType);
        } else if (Long.class.isAssignableFrom(fieldType) || long.class == fieldType) {
            numberExpression = entityPath.getNumber(fieldName, (Class<Long>) fieldType);
        } else if (Short.class.isAssignableFrom(fieldType) || short.class == fieldType) {
            numberExpression = entityPath.getNumber(fieldName, (Class<Short>) fieldType);
        }
        if (numberExpression != null) {
            Number value = NumberUtils.createNumber((String) searchObj);
            if (StringUtils.equalsIgnoreCase(operator, OPERATOR_GOE)) {
                return numberExpression.goe(value);
            } else if (StringUtils.equalsIgnoreCase(operator, "gt")) {
                return numberExpression.gt(value);
            } else if (StringUtils.equalsIgnoreCase(operator, "like")) {
                return numberExpression.like((String) searchObj);
            } else if (StringUtils.equalsIgnoreCase(operator, OPERATOR_LOE)) {
                return numberExpression.loe(value);
            } else if (StringUtils.equalsIgnoreCase(operator, "lt")) {
                return numberExpression.lt(value);
            }
        }
        return entityPath.get(fieldName).eq(searchObj);
    }

    /**
     * Return an expression for {@code entityPath.fieldName} (for Booleans) with
     * the {@code operator} or "equal" by default.
     * <p/>
     * Expr: {@code entityPath.fieldName eq searchObj}
     * 
     * @param entityPath
     * @param fieldName
     * @param searchObj
     * @param operator
     * @return
     */
    public static <T> BooleanExpression createBooleanExpression(PathBuilder<T> entityPath, String fieldName,
            Object searchObj, String operator) {
        Boolean value = BooleanUtils.toBooleanObject((String) searchObj);
        if (value != null) {
            if (StringUtils.equalsIgnoreCase(operator, OPERATOR_GOE)) {
                return entityPath.getBoolean(fieldName).goe(value);
            } else if (StringUtils.equalsIgnoreCase(operator, "gt")) {
                return entityPath.getBoolean(fieldName).gt(value);
            } else if (StringUtils.equalsIgnoreCase(operator, OPERATOR_LOE)) {
                return entityPath.getBoolean(fieldName).loe(value);
            } else if (StringUtils.equalsIgnoreCase(operator, "lt")) {
                return entityPath.getBoolean(fieldName).lt(value);
            }
        }
        return entityPath.get(fieldName).eq(searchObj);
    }

    /**
     * Return an expression for {@code entityPath.fieldName} (for Strings) with
     * the {@code operator} or "equal" by default.
     * <p/>
     * Expr: {@code entityPath.fieldName eq searchObj}
     * 
     * @param entityPath
     * @param fieldName
     * @param searchObj
     * @param operator
     * @return
     */
    public static <T> BooleanExpression createStringExpression(PathBuilder<T> entityPath, String fieldName,
            Object searchObj, String operator) {
        if (StringUtils.equalsIgnoreCase(operator, OPERATOR_GOE)) {
            return entityPath.getString(fieldName).goe((String) searchObj);
        } else if (StringUtils.equalsIgnoreCase(operator, "gt")) {
            return entityPath.getString(fieldName).gt((String) searchObj);
        } else if (StringUtils.equalsIgnoreCase(operator, OPERATOR_LOE)) {
            return entityPath.getString(fieldName).loe((String) searchObj);
        } else if (StringUtils.equalsIgnoreCase(operator, "lt")) {
            return entityPath.getString(fieldName).lt((String) searchObj);
        } else if (StringUtils.equalsIgnoreCase(operator, "like")) {
            return entityPath.getString(fieldName).like((String) searchObj);
        }
        return entityPath.get(fieldName).eq(searchObj);
    }

    /**
     * Return equal expression for {@code entityPath.fieldName}.
     * <p/>
     * Expr: {@code entityPath.fieldName eq 'searchStr'}
     * <p/>
     * Equal operation is case insensitive.
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param searchStr the value to find, may be null
     * @return BooleanExpression
     */
    public static <T> BooleanExpression createStringExpression(PathBuilder<T> entityPath, String fieldName,
            String searchStr) {
        if (StringUtils.isEmpty(searchStr)) {
            return null;
        }
        BooleanExpression expression = entityPath.getString(fieldName).lower().eq(searchStr.toLowerCase());
        return expression;
    }

    /**
     * Return like expression for {@code entityPath.fieldName}.
     * <p/>
     * Expr: {@code entityPath.fieldName like ('%' + searchStr + '%')}
     * <p/>
     * Like operation is case insensitive.
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param searchStr the value to find, may be null
     * @return BooleanExpression
     */
    public static <T> BooleanExpression createStringLikeExpression(PathBuilder<T> entityPath, String fieldName,
            String searchStr) {
        if (StringUtils.isEmpty(searchStr)) {
            return null;
        }
        String str = "%".concat(searchStr.toLowerCase()).concat("%");
        BooleanExpression expression = entityPath.getString(fieldName).lower().like(str);
        return expression;
    }

    /**
     * Return like expression for {@code entityPath.fieldName}.
     * <p/>
     * Expr: {@code entityPath.fieldName like ('%' + searchStr + '%')}
     * <p/>
     * Like operation is case insensitive.
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param searchStr the value to find, may be null
     * @return BooleanExpression
     */
    public static <T> BooleanExpression createStringExpressionWithOperators(PathBuilder<T> entityPath,
            String fieldName, String searchStr, ConversionService conversionService, MessageSource messageSource) {
        if (StringUtils.isEmpty(searchStr)) {
            return null;
        }

        // All operations
        String endsOperation = "ENDS";
        String startsOperation = "STARTS";
        String containsOperation = "CONTAINS";
        String isEmptyOperation = "ISEMPTY";
        String isNotEmptyOperation = "ISNOTEMPTY";
        String isNullOperation = OPERATOR_ISNULL;
        String isNotNullOperation = OPERATOR_NOTNULL;

        if (messageSource != null) {
            endsOperation = messageSource.getMessage("global.filters.operations.string.ends", null,
                    LocaleContextHolder.getLocale());
            startsOperation = messageSource.getMessage("global.filters.operations.string.starts", null,
                    LocaleContextHolder.getLocale());
            containsOperation = messageSource.getMessage("global.filters.operations.string.contains", null,
                    LocaleContextHolder.getLocale());
            isEmptyOperation = messageSource.getMessage("global.filters.operations.string.isempty", null,
                    LocaleContextHolder.getLocale());
            isNotEmptyOperation = messageSource.getMessage("global.filters.operations.string.isnotempty", null,
                    LocaleContextHolder.getLocale());
            isNullOperation = messageSource.getMessage(G_FIL_OPE_ISNULL, null, LocaleContextHolder.getLocale());
            isNotNullOperation = messageSource.getMessage(G_FIL_OPE_NOTNULL, null, LocaleContextHolder.getLocale());
        }

        // If written expression is ENDS operation
        Pattern endsOperator = Pattern.compile(String.format("%s[(](.+)[)]$", endsOperation));
        Matcher endsMatcher = endsOperator.matcher(searchStr);

        if (endsMatcher.matches()) {
            // Getting value
            String value = endsMatcher.group(1);

            String str = "%".concat(value.toLowerCase());
            return entityPath.getString(fieldName).lower().like(str);
        }

        // If written expression is STARTS operation
        Pattern startsOperator = Pattern.compile(String.format("%s[(](.+)[)]$", startsOperation));
        Matcher startsMatcher = startsOperator.matcher(searchStr);

        if (startsMatcher.matches()) {
            // Getting value
            String value = startsMatcher.group(1);

            String str = value.toLowerCase().concat("%");
            return entityPath.getString(fieldName).lower().like(str);
        }

        // If written expression is CONTAINS operation
        Pattern containsOperator = Pattern.compile(String.format("%s[(](.+)[)]$", containsOperation));
        Matcher containsMatcher = containsOperator.matcher(searchStr);

        if (containsMatcher.matches()) {
            // Getting value
            String value = containsMatcher.group(1);

            String str = "%".concat(value.toLowerCase()).concat("%");
            return entityPath.getString(fieldName).lower().like(str);
        }

        // If written expression is ISEMPTY operation
        Pattern isEmptyOperator = Pattern.compile(String.format("%s", isEmptyOperation));
        Matcher isEmptyMatcher = isEmptyOperator.matcher(searchStr);
        if (isEmptyMatcher.matches()) {
            return entityPath.getString(fieldName).isEmpty().or(entityPath.getString(fieldName).isNull());

        }

        // If written expression is ISNOTEMPTY operation
        Pattern isNotEmptyOperator = Pattern.compile(String.format("%s", isNotEmptyOperation));
        Matcher isNotEmptyMatcher = isNotEmptyOperator.matcher(searchStr);
        if (isNotEmptyMatcher.matches()) {
            return entityPath.getString(fieldName).isNotEmpty().and(entityPath.getString(fieldName).isNotNull());

        }

        // If written expression is ISNULL operation
        Pattern isNullOperator = Pattern.compile(String.format("%s", isNullOperation));
        Matcher isNullMatcher = isNullOperator.matcher(searchStr);
        if (isNullMatcher.matches()) {
            return entityPath.getString(fieldName).isNull();

        }

        // If written expression is ISNOTNULL operation
        Pattern isNotNullOperator = Pattern.compile(String.format("%s", isNotNullOperation));
        Matcher isNotNullMatcher = isNotNullOperator.matcher(searchStr);
        if (isNotNullMatcher.matches()) {
            return entityPath.getString(fieldName).isNotNull();

        }

        // If written expression is a symbol operation expression

        // Getting expressions with symbols
        Pattern symbolOperator = Pattern.compile("[=]?(.+)");
        Matcher symbolMatcher = symbolOperator.matcher(searchStr);

        if (symbolMatcher.matches()) {

            String value = symbolMatcher.group(1);

            // operator is not necessary. Always is =
            return entityPath.getString(fieldName).lower().eq(value.toLowerCase());
        }

        return null;
    }

    /**
     * Return where clause expression for number properties by casting it to
     * string before check its value.
     * <p/>
     * Querydsl Expr:
     * {@code entityPath.fieldName.stringValue() like ('%' + searchStr + '%')}
     * Database operation:
     * {@code str(entity.fieldName) like ('%' + searchStr + '%')}
     * <p/>
     * Like operation is case sensitive.
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code weight} in {@code Pet} entity, {@code age} in
     *        {@code Pet.owner} entity.
     * @param searchStr the value to find, may be null
     * @return PredicateOperation
     */
    public static <T, N extends java.lang.Number & java.lang.Comparable<?>> BooleanExpression createNumberExpression(
            PathBuilder<T> entityPath, String fieldName, Class<N> fieldType, TypeDescriptor descriptor,
            String searchStr, ConversionService conversionService) {
        if (StringUtils.isBlank(searchStr)) {
            return null;
        }
        NumberPath<N> numberExpression = entityPath.getNumber(fieldName, fieldType);

        BooleanExpression expression = null;

        if (conversionService != null) {
            try {
                Object number = conversionService.convert(searchStr, STRING_TYPE_DESCRIPTOR, descriptor);
                if (number == null) {
                    expression = numberExpression.stringValue().like("%".concat(searchStr).concat("%"));
                } else {
                    String toSearch = number.toString();
                    if (number instanceof BigDecimal && ((BigDecimal) number).scale() > 1) {
                        // For bigDecimal trim 0 in decimal part
                        toSearch = StringUtils.stripEnd(toSearch, "0");
                        if (StringUtils.endsWith(toSearch, ".")) {
                            // prevent "#." strings
                            toSearch = toSearch.concat("0");
                        }
                    }
                    expression = numberExpression.stringValue().like("%".concat(toSearch).concat("%"));
                }
            } catch (ConversionException e) {
                expression = numberExpression.stringValue().like("%".concat(searchStr).concat("%"));
            }
        } else {
            expression = numberExpression.stringValue().like("%".concat(searchStr).concat("%"));
        }
        return expression;
    }

    /**
         * Return where clause expression for number properties by casting it to
         * string before check its value.
         * <p/>
         * Querydsl Expr:
         * {@code entityPath.fieldName.stringValue() eq searchStr
         * Database operation:
         * {@code str(entity.fieldName) = searchStr
         * <p/>
         * Like operation is case sensitive.
         *
         * @param entityPath Full path to entity and associations. For example:
         *        {@code Pet} , {@code Pet.owner}
         * @param fieldName Property name in the given entity path. For example:
         *        {@code weight} in {@code Pet} entity, {@code age} in
         *        {@code Pet.owner} entity.
         * @param searchStr the value to find, may be null
         * @return PredicateOperation
         */
    @SuppressWarnings("unchecked")
    public static <T, N extends java.lang.Number & java.lang.Comparable<?>> BooleanExpression createNumberExpressionEqual(
            PathBuilder<T> entityPath, String fieldName, Class<N> fieldType, TypeDescriptor descriptor,
            String searchStr, ConversionService conversionService) {
        if (StringUtils.isEmpty(searchStr)) {
            return null;
        }
        NumberPath<N> numberExpression = entityPath.getNumber(fieldName, fieldType);

        TypeDescriptor strDesc = STRING_TYPE_DESCRIPTOR;

        if (conversionService != null) {
            try {
                return numberExpression.eq((N) conversionService.convert(searchStr, strDesc, descriptor));
            } catch (ConversionException ex) {
                return numberExpression.stringValue().like("%".concat(searchStr).concat("%"));
            }
        } else {
            return numberExpression.stringValue().like("%".concat(searchStr).concat("%"));
        }
    }

    /**
     * Return where clause expression for date properties, trying to parse the
     * value to find to date and comparing it to the value of the date; if the
     * value to find cannot be parsed to date, then try to cast the value to
     * string before check it.
     * <p/>
     * <ul>
     * <li>
     * If value to find {@code searchStr} can be parsed using the patterns
     * <em>dd-MM-yyyy HH:mm:ss</em> or <em>dd-MM-yyyy HH:mm</em> or
     * <em>dd-MM-yyyy</em> to {@code searchDate}, then search by specific date:
     * <p/>
     * - Querydsl Expr: {@code entityPath.fieldName = searchDate}
     * <p/>
     * - Database operation: {@code entity.fieldName = searchDate}</li>
     * <li>
     * If value to find {@code searchStr} can be parsed using the pattern
     * <em>dd-MM</em> to {@code searchDate}, then search by specific day and
     * month:
     * <p/>
     * - Querydsl Expr:
     * {@code entityPath.fieldName.dayOfMonth() = searchDate.day and entityPath.fieldName.month() = searchDate.month}
     * <p/>
     * - Database operation:
     * {@code dayofmonth(entity.fieldName) = searchDate.day && month(entity.fieldName) = searchDate.month}
     * </li>
     * <li>
     * If value to find {@code searchStr} can be parsed using the pattern
     * <em>MM-aaaa</em> to {@code searchDate}, then obtain the first day of the
     * month for that year and the last day of the month for that year and check
     * that value is into between theses values:
     * <p/>
     * - Querydsl Expr:
     * {@code entityPath.fieldName.between(searchDate.firstDayOfMonth, searchDate.lastDayOfMonth)}
     * <p/>
     * - Database operation:
     * {@code entity.fieldName between searchDate.firstDayOfMonth and searchDate.lastDayOfMonth}
     * </li>
     * <li>
     * If value to find cannot be parsed as date, then try to cast the value to
     * string before check it:
     * <p/>
     * - Querydsl Expr:
     * {@code entityPath.fieldName.stringValue() like ('%' + searchStr + '%')}
     * <p/>
     * - Database operation:
     * {@code str(entity.fieldName) like ('%' + searchStr + '%')}
     * <p/>
     * Note that like operation is case sensitive.</li>
     * </ul>
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code weight} in {@code Pet} entity, {@code age} in
     *        {@code Pet.owner} entity.
     * @param searchStr the value to find, may be null
     * @return PredicateOperation
     */
    public static <T, C extends java.lang.Comparable<?>> BooleanExpression createDateExpression(
            PathBuilder<T> entityPath, String fieldName, Class<C> fieldType, String searchStr) {
        if (StringUtils.isEmpty(searchStr)) {
            return null;
        }

        DatePath<C> dateExpression = entityPath.getDate(fieldName, fieldType);

        BooleanExpression expression;

        // Search by full date
        String[] parsePatterns = null;
        try {
            parsePatterns = FULL_DATE_PATTERNS_WITH_TIME;
            Date searchDate = DateUtils.parseDateStrictly(searchStr, parsePatterns);
            Calendar searchCal = Calendar.getInstance();
            searchCal.setTime(searchDate);
            expression = dateExpression.eq((fieldType.cast(searchCal)));
        } catch (Exception e) {
            // do nothing, and try the next parsing
            expression = null;
        }

        if (expression == null) {
            try {
                parsePatterns = FULL_DATE_PAT_WO_TIME;
                Date searchDate = DateUtils.parseDateStrictly(searchStr, parsePatterns);
                Calendar searchCal = Calendar.getInstance();
                searchCal.setTime(searchDate);
                expression = dateExpression.dayOfMonth().eq(searchCal.get(Calendar.DAY_OF_MONTH))
                        .and(dateExpression.month().eq(searchCal.get(Calendar.MONTH) + 1))
                        .and(dateExpression.year().eq(searchCal.get(Calendar.YEAR)));
            } catch (Exception e) {
                // do nothing, and try the next parsing
                expression = null;
            }
        }

        if (expression == null) {
            // Search by day and month
            parsePatterns = DAY_AND_MONTH_DATE_PATTERNS;
            try {
                Date searchDate = DateUtils.parseDateStrictly(searchStr, parsePatterns);
                Calendar searchCal = Calendar.getInstance();
                searchCal.setTime(searchDate);
                expression = dateExpression.dayOfMonth().eq(searchCal.get(Calendar.DAY_OF_MONTH))
                        .and(dateExpression.month().eq(searchCal.get(Calendar.MONTH) + 1));
            } catch (Exception e) {
                // do nothing, and try the next parsing
                expression = null;
            }
        }

        // Search by month and year
        if (expression == null) {
            parsePatterns = MONTH_AND_YEAR_DATE_PATTERNS;
            try {
                Date searchDate = DateUtils.parseDateStrictly(searchStr, parsePatterns);
                Calendar searchCal = Calendar.getInstance();
                searchCal.setTime(searchDate);

                // from 1st day of the month
                Calendar monthStartCal = Calendar.getInstance();
                monthStartCal.set(searchCal.get(Calendar.YEAR), searchCal.get(Calendar.MONTH), 1, 23, 59, 59);
                monthStartCal.set(Calendar.MILLISECOND, 999);

                // to last day of the month
                Calendar monthEndCal = Calendar.getInstance();
                monthEndCal.set(searchCal.get(Calendar.YEAR), (searchCal.get(Calendar.MONTH) + 1), 1, 23, 59, 59);
                monthEndCal.set(Calendar.MILLISECOND, 999);

                expression = dateExpression.between(fieldType.cast(monthStartCal), fieldType.cast(monthEndCal));
            } catch (Exception e) {
                // do nothing, and try the next parsing
                expression = null;
            }
        }

        // Search by year
        // NOT NEEDED; JUST USE DEFAULT EXPRESSION
        if (expression == null) {
            // Default expression
            expression = dateExpression.stringValue().like("%".concat(searchStr).concat("%"));
        }

        return expression;
    }

    /**
     * Return where clause expression for date properties, trying to parse the
     * value to find to date and comparing it to the value of the date; if the
     * value to find cannot be parsed to date, then try to cast the value to
     * string before check it.
     * <p/>
     * <ul>
     * <li>
     * If value to find {@code searchStr} can be parsed using the patterns
     * <em>dd-MM-yyyy HH:mm:ss</em> or <em>dd-MM-yyyy HH:mm</em> or
     * <em>dd-MM-yyyy</em> to {@code searchDate}, then search by specific date:
     * <p/>
     * - Querydsl Expr: {@code entityPath.fieldName = searchDate}
     * <p/>
     * - Database operation: {@code entity.fieldName = searchDate}</li>
     * <li>
     * If value to find {@code searchStr} can be parsed using the pattern
     * <em>dd-MM</em> to {@code searchDate}, then search by specific day and
     * month:
     * <p/>
     * - Querydsl Expr:
     * {@code entityPath.fieldName.dayOfMonth() = searchDate.day and entityPath.fieldName.month() = searchDate.month}
     * <p/>
     * - Database operation:
     * {@code dayofmonth(entity.fieldName) = searchDate.day && month(entity.fieldName) = searchDate.month}
     * </li>
     * <li>
     * If value to find {@code searchStr} can be parsed using the pattern
     * <em>MM-aaaa</em> to {@code searchDate}, then obtain the first day of the
     * month for that year and the last day of the month for that year and check
     * that value is into between theses values:
     * <p/>
     * - Querydsl Expr:
     * {@code entityPath.fieldName.between(searchDate.firstDayOfMonth, searchDate.lastDayOfMonth)}
     * <p/>
     * - Database operation:
     * {@code entity.fieldName between searchDate.firstDayOfMonth and searchDate.lastDayOfMonth}
     * </li>
     * <li>
     * If value to find cannot be parsed as date, then try to cast the value to
     * string before check it:
     * <p/>
     * - Querydsl Expr:
     * {@code entityPath.fieldName.stringValue() like ('%' + searchStr + '%')}
     * <p/>
     * - Database operation:
     * {@code str(entity.fieldName) like ('%' + searchStr + '%')}
     * <p/>
     * Note that like operation is case sensitive.</li>
     * </ul>
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code weight} in {@code Pet} entity, {@code age} in
     *        {@code Pet.owner} entity.
     * @param searchStr the value to find, may be null
     * @return PredicateOperation
     */
    public static <T, C extends java.lang.Comparable<?>> BooleanExpression createDateExpressionWithOperators(
            PathBuilder<T> entityPath, String fieldName, Class<C> fieldType, String searchStr,
            ConversionService conversionService, MessageSource messageSource, String datePattern) {
        if (StringUtils.isEmpty(searchStr)) {
            return null;
        }

        DatePath<C> dateExpression = entityPath.getDate(fieldName, fieldType);

        // Getting simpleDateFormat
        DateFormat dateFormat = new SimpleDateFormat(datePattern);

        // All possible operations
        String date = "DATE";
        String year = "YEAR";
        String month = "MONTH";
        String day = "DAY";
        String between = "BETWEEN";
        String isNullOperation = OPERATOR_ISNULL;
        String isNotNullOperation = OPERATOR_NOTNULL;

        if (messageSource != null) {
            date = messageSource.getMessage("global.filters.operations.date.date", null,
                    LocaleContextHolder.getLocale());
            year = messageSource.getMessage("global.filters.operations.date.year", null,
                    LocaleContextHolder.getLocale());
            month = messageSource.getMessage("global.filters.operations.date.month", null,
                    LocaleContextHolder.getLocale());
            day = messageSource.getMessage("global.filters.operations.date.day", null,
                    LocaleContextHolder.getLocale());
            between = messageSource.getMessage("global.filters.operations.date.between", null,
                    LocaleContextHolder.getLocale());
            isNullOperation = messageSource.getMessage(G_FIL_OPE_ISNULL, null, LocaleContextHolder.getLocale());
            isNotNullOperation = messageSource.getMessage(G_FIL_OPE_NOTNULL, null, LocaleContextHolder.getLocale());
        }

        // If written expression is ISNULL operation
        Pattern isNullOperator = Pattern.compile(String.format("%s", isNullOperation));
        Matcher isNullMatcher = isNullOperator.matcher(searchStr);
        if (isNullMatcher.matches()) {
            return dateExpression.isNull();

        }

        // If written expression is ISNOTNULL operation
        Pattern isNotNullOperator = Pattern.compile(String.format("%s", isNotNullOperation));
        Matcher isNotNullMatcher = isNotNullOperator.matcher(searchStr);
        if (isNotNullMatcher.matches()) {
            return dateExpression.isNotNull();

        }

        // Creating regex to get DATE operator
        Pattern dateOperator = Pattern.compile(String.format("%s[(]([\\d\\/]*)[)]", date));
        Matcher dateMatcher = dateOperator.matcher(searchStr);

        if (dateMatcher.matches()) {
            try {
                String dateValue = dateMatcher.group(1);
                Date dateToFilter = dateFormat.parse(dateValue);

                Calendar searchCal = Calendar.getInstance();
                searchCal.setTime(dateToFilter);

                return dateExpression.eq(conversionService.convert(searchCal, fieldType));

            } catch (ParseException e) {
                return null;
            }
        }

        // Creating regex to get YEAR operator
        Pattern yearOperator = Pattern.compile(String.format("%s[(]([\\d]*)[)]", year));
        Matcher yearMatcher = yearOperator.matcher(searchStr);

        if (yearMatcher.matches()) {

            String value = yearMatcher.group(1);

            return dateExpression.year().eq(Integer.parseInt(value));
        }

        // Creating regex to get MONTH operator
        Pattern monthOperator = Pattern.compile(String.format("%s[(]([\\d]*)[)]", month));
        Matcher monthMatcher = monthOperator.matcher(searchStr);

        if (monthMatcher.matches()) {

            String value = monthMatcher.group(1);

            return dateExpression.month().eq(Integer.parseInt(value));
        }

        // Creating regex to get DAY operator
        Pattern dayOperator = Pattern.compile(String.format("%s[(]([\\d]*)[)]", day));
        Matcher dayMatcher = dayOperator.matcher(searchStr);

        if (dayMatcher.matches()) {

            String value = dayMatcher.group(1);

            return dateExpression.dayOfMonth().eq(Integer.parseInt(value));
        }

        // Creating regex to get BETWEEN operator
        Pattern betweenOperator = Pattern.compile(String.format("%s[(]([\\d\\/]*);([\\d\\/]*)[)]", between));
        Matcher betweenMatcher = betweenOperator.matcher(searchStr);

        if (betweenMatcher.matches()) {

            String valueFrom = betweenMatcher.group(1);
            String valueTo = betweenMatcher.group(2);

            if (StringUtils.isNotBlank(valueFrom) && StringUtils.isNotBlank(valueTo)) {

                try {

                    Date dateFrom = dateFormat.parse(valueFrom);
                    Date dateTo = dateFormat.parse(valueTo);

                    Calendar dateFromCal = Calendar.getInstance();
                    dateFromCal.setTime(dateFrom);

                    Calendar dateToCal = Calendar.getInstance();
                    dateToCal.setTime(dateTo);

                    return dateExpression.between(conversionService.convert(dateFromCal, fieldType),
                            conversionService.convert(dateToCal, fieldType));

                } catch (Exception e) {
                    return null;
                }
            }

        }

        return null;
    }

    /**
     * Return where clause expression for non-String
     * {@code entityPath.fieldName} by transforming it to text before check its
     * value.
     * <p/>
     * Expr:
     * {@code entityPath.fieldName.as(String.class) like ('%' + searchStr + '%')}
     * <p/>
     * Like operation is case insensitive.
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code weight} in {@code Pet} entity, {@code age} in
     *        {@code Pet.owner} entity.
     * @param searchStr the value to find, may be null
     * @param enumClass Enumeration type. Needed to enumeration values
     * @return BooleanExpression
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static <T> BooleanExpression createEnumExpression(PathBuilder<T> entityPath, String fieldName,
            String searchStr, Class<? extends Enum> enumClass) {
        if (StringUtils.isEmpty(searchStr)) {
            return null;
        }
        // Filter string to search than cannot be a identifier
        if (!StringUtils.isAlphanumeric(StringUtils.lowerCase(searchStr))) {
            return null;
        }

        // TODO i18n of enum name

        // normalize search string
        searchStr = StringUtils.trim(searchStr).toLowerCase();

        // locate enums matching by name
        Set matching = new HashSet();

        Enum<?> enumValue;
        String enumStr;

        for (Field enumField : enumClass.getDeclaredFields()) {
            if (enumField.isEnumConstant()) {
                enumStr = enumField.getName();
                enumValue = Enum.valueOf(enumClass, enumStr);

                // Check enum name contains string to search
                if (enumStr.toLowerCase().contains(searchStr)) {
                    // Add to matching enum
                    matching.add(enumValue);
                    continue;
                }

                // Check using toString
                enumStr = enumValue.toString();
                if (enumStr.toLowerCase().contains(searchStr)) {
                    // Add to matching enum
                    matching.add(enumValue);
                }
            }
        }
        if (matching.isEmpty()) {
            return null;
        }

        // create a enum in matching condition
        BooleanExpression expression = entityPath.get(fieldName).in(matching);
        return expression;
    }

    /**
     * Return where clause expression for {@code Boolean} fields by transforming
     * the given {@code searchStr} to {@code Boolean} before check its value.
     * <p/>
     * Expr: {@code entityPath.fieldName eq (TRUE | FALSE)}
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code weight} in {@code Pet} entity, {@code age} in
     *        {@code Pet.owner} entity.
     * @param searchStr the boolean value to find, may be null. Supported string
     *        are: si, yes, true, on, no, false, off
     * @return BooleanExpression
     */
    public static <T> BooleanExpression createBooleanExpression(PathBuilder<T> entityPath, String fieldName,
            String searchStr) {
        if (StringUtils.isBlank(searchStr)) {
            return null;
        }

        Boolean value = null;

        // I18N: Spanish (normalize search value: trim start-end and lower case)
        if ("si".equals(StringUtils.trim(searchStr).toLowerCase())) {
            value = Boolean.TRUE;
        } else {
            value = BooleanUtils.toBooleanObject(searchStr);
        }

        // if cannot parse to boolean or null input
        if (value == null) {
            return null;
        }

        BooleanExpression expression = entityPath.getBoolean(fieldName).eq(value);
        return expression;
    }

    /**
     * Return where clause expression for {@code Boolean} fields by transforming
     * the given {@code searchStr} to {@code Boolean} before check its value.
     * <p/>
     * Expr: {@code entityPath.fieldName eq (TRUE | FALSE)}
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code weight} in {@code Pet} entity, {@code age} in
     *        {@code Pet.owner} entity.
     * @param searchStr the boolean value to find, may be null. Supported string
     *        are: si, yes, true, on, no, false, off
     * @return BooleanExpression
     */
    public static <T> BooleanExpression createBooleanExpressionWithOperators(PathBuilder<T> entityPath,
            String fieldName, String searchStr, ConversionService conversionService, MessageSource messageSource) {
        if (StringUtils.isBlank(searchStr)) {
            return null;
        }

        // Getting all operations
        String trueOperation = "TRUE";
        String falseOperation = "FALSE";
        String isNullOperation = OPERATOR_ISNULL;
        String isNotNullOperation = OPERATOR_NOTNULL;

        if (messageSource != null) {
            trueOperation = messageSource.getMessage("global.filters.operations.boolean.true", null,
                    LocaleContextHolder.getLocale());
            falseOperation = messageSource.getMessage("global.filters.operations.boolean.false", null,
                    LocaleContextHolder.getLocale());
            isNullOperation = messageSource.getMessage(G_FIL_OPE_ISNULL, null, LocaleContextHolder.getLocale());
            isNotNullOperation = messageSource.getMessage(G_FIL_OPE_NOTNULL, null, LocaleContextHolder.getLocale());
        }

        // If written function is TRUE
        Pattern trueOperator = Pattern.compile(String.format("%s", trueOperation));
        Matcher trueMatcher = trueOperator.matcher(searchStr);

        if (trueMatcher.matches()) {
            return entityPath.getBoolean(fieldName).eq(Boolean.TRUE);
        }

        // If written function is FALSE
        Pattern falseOperator = Pattern.compile(String.format("%s", falseOperation));
        Matcher falseMatcher = falseOperator.matcher(searchStr);

        if (falseMatcher.matches()) {
            return entityPath.getBoolean(fieldName).eq(Boolean.FALSE);
        }

        // If written expression is ISNULL operation
        Pattern isNullOperator = Pattern.compile(String.format("%s", isNullOperation));
        Matcher isNullMatcher = isNullOperator.matcher(searchStr);
        if (isNullMatcher.matches()) {
            return entityPath.getBoolean(fieldName).isNull();

        }

        // If written expression is ISNOTNULL operation
        Pattern isNotNullOperator = Pattern.compile(String.format("%s", isNotNullOperation));
        Matcher isNotNullMatcher = isNotNullOperator.matcher(searchStr);
        if (isNotNullMatcher.matches()) {
            return entityPath.getBoolean(fieldName).isNotNull();

        }

        return null;
    }

    /**
     * Return IN expression for {@code entityPath.fieldName}.
     * <p/>
     * Expr: <br/>
     * entityPath.fieldName IN ( values ) <br/>
     * <br/>
     * If values.size() > 500 its generates: <br/>
     * Expr: <br/>
     * (entityPath.fieldName IN ( values[0-500] ) OR [entityPath.fieldName IN (
     * values[501-100]... ])) <br/>
     * <br/>
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code name} in {@code Pet} entity, {@code firstName} in
     *        {@code Pet.owner} entity.
     * @param values the Set of values to find the given field name, may be null
     * @return BooleanExpression
     */
    public static <T, E> BooleanExpression createCollectionExpression(PathBuilder<T> entityPath, String fieldName,
            Collection<E> values) {
        if (StringUtils.isEmpty(fieldName) || values.isEmpty()) {
            return null;
        }

        if (values.size() > 500) {
            BooleanExpression expression = null;
            Iterable<List<E>> collectionParts = Iterables.partition(values, 500);
            for (List<E> part : collectionParts) {
                if (expression == null) {
                    expression = doCreateCollectionExpression(entityPath, fieldName, part);
                } else {
                    expression = expression.or(doCreateCollectionExpression(entityPath, fieldName, part));
                }
            }
            return expression;
        } else {
            return doCreateCollectionExpression(entityPath, fieldName, values);
        }
    }

    public static <T, E> BooleanExpression doCreateCollectionExpression(PathBuilder<T> entityPath, String fieldName,
            Collection<E> values) {
        BooleanExpression expression = entityPath.get(fieldName).in(values);
        return expression;
    }

    /**
     * Create an order-by-element in a Query instance
     * 
     * @param entityPath Full path to entity and associations. For example:
     *        {@code Pet} , {@code Pet.owner}
     * @param fieldName Property name in the given entity path. For example:
     *        {@code weight} in {@code Pet} entity, {@code age} in
     *        {@code Pet.owner} entity.
     * @param fieldType Property value {@code Class}. Must implements
     *        {@link Comparable}
     * @param order ascending or descending order
     * @return
     */
    public static <T, E extends Comparable<?>> OrderSpecifier<?> createOrderSpecifier(PathBuilder<T> entityPath,
            String fieldName, Class<E> fieldType, Order order) {
        OrderSpecifier<?> orderBy = null;

        // Get the OrderSpecifier
        if (order == Order.ASC) {
            orderBy = entityPath.getComparable(fieldName, fieldType).asc();
        } else if (order == Order.DESC) {
            orderBy = entityPath.getComparable(fieldName, fieldType).desc();
        }
        return orderBy;
    }

    /**
     * This method returns the query expression based on String expression
     * user-written.
     * 
     * Expression can be "=", ">", "<", ">=", "<=", "<>", "!=",
     * "ENTRENUMERO(n1;n2)"
     * 
     * @param searchStr
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T, N extends java.lang.Number & java.lang.Comparable<?>> BooleanExpression getNumericFilterExpression(
            PathBuilder<T> entityPath, String fieldName, Class<N> fieldType, TypeDescriptor descriptor,
            String searchStr, ConversionService conversionService, MessageSource messageSource) {

        if (StringUtils.isEmpty(searchStr)) {
            return null;
        }

        TypeDescriptor strDesc = STRING_TYPE_DESCRIPTOR;

        NumberPath<N> numberExpression = entityPath.getNumber(fieldName, fieldType);

        // If written expression is a symbol operation expression

        // Getting expressions with symbols
        Pattern symbolOperator = Pattern.compile("([!=><][=>]?)([-]?[\\d.,]*)");
        Matcher symbolMatcher = symbolOperator.matcher(searchStr);

        if (symbolMatcher.matches()) {

            String symbolExpression = symbolMatcher.group(1);
            String value = symbolMatcher.group(2);

            if (!StringUtils.isBlank(value)) {

                Object valueConverted = conversionService.convert(value, strDesc, descriptor);

                if (symbolExpression.equals("=") || symbolExpression.equals("==")) {
                    return numberExpression.eq((N) valueConverted);
                } else if (symbolExpression.equals(">") || symbolExpression.equals(">>")) {
                    return numberExpression.gt((N) valueConverted);
                } else if (symbolExpression.equals("<")) {
                    return numberExpression.lt((N) valueConverted);
                } else if (symbolExpression.equals(">=")) {
                    return numberExpression.goe((N) valueConverted);
                } else if (symbolExpression.equals("<=")) {
                    return numberExpression.loe((N) valueConverted);
                } else if (symbolExpression.equals("!=") || symbolExpression.equals("<>")) {
                    return numberExpression.ne((N) valueConverted);
                }
            }
        }

        // Get all operations
        String isNullOperation = OPERATOR_ISNULL;
        String isNotNullOperation = OPERATOR_NOTNULL;
        String betweenOperation = "BETWEEN";

        if (messageSource != null) {
            isNullOperation = messageSource.getMessage(G_FIL_OPE_ISNULL, null, LocaleContextHolder.getLocale());
            isNotNullOperation = messageSource.getMessage(G_FIL_OPE_NOTNULL, null, LocaleContextHolder.getLocale());
            betweenOperation = messageSource.getMessage("global.filters.operations.number.between", null,
                    LocaleContextHolder.getLocale());
        }

        // If written function is BETWEEN function
        Pattern betweenFunctionOperator = Pattern
                .compile(String.format("%s[(]([-]?[\\d.,]*);([-]?[\\d.,]*)[)]", betweenOperation));
        Matcher betweenFunctionMatcher = betweenFunctionOperator.matcher(searchStr);

        if (betweenFunctionMatcher.matches()) {
            // Getting valueFrom and valueTo
            String valueFrom = betweenFunctionMatcher.group(1);
            String valueTo = betweenFunctionMatcher.group(2);

            Object valueFromConverted = conversionService.convert(valueFrom, strDesc, descriptor);
            Object valueToConverted = conversionService.convert(valueTo, strDesc, descriptor);

            if (!StringUtils.isBlank(valueFrom) && !StringUtils.isBlank(valueTo)) {
                return numberExpression.between((N) valueFromConverted, (N) valueToConverted);
            }
        }

        // If written expression is ISNULL operation
        Pattern isNullOperator = Pattern.compile(String.format("%s", isNullOperation));
        Matcher isNullMatcher = isNullOperator.matcher(searchStr);
        if (isNullMatcher.matches()) {
            return numberExpression.isNull();

        }

        // If written expression is ISNOTNULL operation
        Pattern isNotNullOperator = Pattern.compile(String.format("%s", isNotNullOperation));
        Matcher isNotNullMatcher = isNotNullOperator.matcher(searchStr);
        if (isNotNullMatcher.matches()) {
            return numberExpression.isNotNull();

        }

        return null;
    }

    /**
     * Obtains the class type of the property named as {@code fieldName} of the
     * entity.
     * 
     * @param fieldName the field name.
     * @param entity the entity with a property named as {@code fieldName}
     * @return the class type
     */
    public static <T> Class<?> getFieldType(String fieldName, PathBuilder<T> entity) {
        TypeDescriptor descriptor = getTypeDescriptor(fieldName, entity);
        return descriptor.getType();
    }

    /**
     * Obtains the class type of the property named as {@code fieldName} of the
     * entity.
     * 
     * @param fieldName the field name.
     * @param entity the entity with a property named as {@code fieldName}
     * @return the class type
     */
    public static <T> Class<?> getFieldType1(String fieldName, PathBuilder<T> entity) {
        Class<?> entityType = entity.getType();
        String fieldNameToFindType = fieldName;

        // Makes the array of classes to find fieldName agains them
        Class<?>[] classArray = ArrayUtils.<Class<?>>toArray(entityType);
        if (fieldName.contains(SEPARATOR_FIELDS)) {
            String[] fieldNameSplitted = StringUtils.split(fieldName, SEPARATOR_FIELDS);
            for (int i = 0; i < fieldNameSplitted.length - 1; i++) {
                Class<?> fieldType = BeanUtils.findPropertyType(fieldNameSplitted[i],
                        ArrayUtils.<Class<?>>toArray(entityType));
                classArray = ArrayUtils.add(classArray, fieldType);
                entityType = fieldType;
            }
            fieldNameToFindType = fieldNameSplitted[fieldNameSplitted.length - 1];
        }

        return BeanUtils.findPropertyType(fieldNameToFindType, classArray);
    }

    /**
     * Obtains the descriptor of the filtered field
     * 
     * @param fieldName
     * @param entity
     * @return
     */
    public static <T> TypeDescriptor getTypeDescriptor(String fieldName, PathBuilder<T> entity) {
        Class<?> entityType = entity.getType();
        if (entityType == Object.class) {
            // Remove from path the root "entity" alias
            String fromRootPath = entity.toString().replaceFirst("^[^.]+[.]", "");
            TypeDescriptor fromRoot = getTypeDescriptor(fromRootPath, entity.getRoot().getType());
            if (fromRoot == null) {
                return null;
            }
            entityType = fromRoot.getType();
        }
        return getTypeDescriptor(fieldName, entityType);
    }

    /**
     * Obtains the descriptor of the filtered field
     * 
     * @param fieldName
     * @param entityType
     * @return
     */
    public static <T> TypeDescriptor getTypeDescriptor(String fieldName, Class<T> entityType) {
        String fieldNameToFindType = fieldName;
        BeanWrapper beanWrapper = getBeanWrapper(entityType);

        TypeDescriptor fieldDescriptor = null;
        Class<?> propType = null;
        // Find recursive the las beanWrapper
        if (fieldName.contains(SEPARATOR_FIELDS)) {
            String[] fieldNameSplitted = StringUtils.split(fieldName, SEPARATOR_FIELDS);
            for (int i = 0; i < fieldNameSplitted.length - 1; i++) {
                propType = beanWrapper.getPropertyType(fieldNameSplitted[i]);
                if (propType == null) {
                    throw new IllegalArgumentException(String.format("Property %s not found in %s (request %s.%s)",
                            fieldNameSplitted[i], beanWrapper.getWrappedClass(), entityType, fieldName));
                }
                beanWrapper = getBeanWrapper(propType);
            }
            fieldNameToFindType = fieldNameSplitted[fieldNameSplitted.length - 1];
        }
        fieldDescriptor = beanWrapper.getPropertyTypeDescriptor(fieldNameToFindType);

        return fieldDescriptor;
    }

    /**
     * This method checks if the search string can be converted to a number
     * using conversionService with locale.
     * 
     * @param searchStr
     * @param conversionService
     * @param descriptor
     * @return
     */
    public static boolean isNumber(String searchStr, ConversionService conversionService,
            TypeDescriptor descriptor) {

        return isValidValueFor(searchStr, descriptor, conversionService);

    }
}