/*
* Copyright 2015 DiSiD Technologies S.L.L. All rights reserved.
*
* Project : DiSiD org.gvnix.web.datatables
* SVN Id : $Id$
*/
package org.gvnix.web.datatables.util.impl;
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.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.gvnix.web.datatables.util.EntityManagerProvider;
import org.gvnix.web.datatables.util.QuerydslUtilsBean;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
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;
public class QuerydslUtilsBeanImpl implements QuerydslUtilsBean {
@Autowired
private ConversionService conversionService;
@Autowired
private MessageSource messageSource;
@Autowired
private EntityManagerProvider entityManagerProvider;
private static LoadingCache<Class<?>, BeanWrapper> beanWrappersCache = CacheBuilder
.newBuilder().maximumSize(200)
.build(new CacheLoader<Class<?>, BeanWrapper>() {
public BeanWrapper load(Class<?> key) {
return new BeanWrapperImpl(key);
}
});
/**
* 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;
}
/**
* {@inheritDoc}
*/
@Override
public <T> BooleanBuilder createPredicateByAnd(PathBuilder<T> entity,
Map<String, Object> searchArgs) {
// 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));
}
}
else {
predicate.and(createObjectExpression(entity, key,
valueToSearch, operator));
}
}
return predicate;
}
/**
* {@inheritDoc}
*/
@Override
public <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;
}
/**
* {@inheritDoc}
*/
@Override
public <T> Predicate createFilterExpression(PathBuilder<T> entityPath,
String fieldName, Class<?> fieldType, String searchStr) {
return createFilterExpression(entityPath, fieldName, searchStr);
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> Predicate createFilterExpression(PathBuilder<T> entityPath,
String fieldName, String searchStr) {
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);
}
else if (Boolean.class == fieldType || boolean.class == fieldType) {
return createBooleanExpressionWithOperators(entityPath, fieldName,
searchStr);
}
else if (Number.class.isAssignableFrom(fieldType)
|| NUMBER_PRIMITIVES.contains(fieldType)) {
return createNumberExpressionGenericsWithOperators(entityPath,
fieldName, descriptor, searchStr);
}
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,
datePattern);
return expression;
}
else if (fieldType.isEnum()) {
return createEnumExpression(entityPath, fieldName, searchStr,
(Class<? extends Enum>) fieldType);
}
return null;
}
@Override
public <T> Predicate createSearchExpression(PathBuilder<T> entityPath,
String fieldName, Class<?> fieldType, String searchStr) {
return createSearchExpression(entityPath, fieldName, searchStr);
}
@Override
public <T> Predicate createSearchExpression(PathBuilder<T> entityPath,
String fieldName, String searchStr) {
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);
}
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;
}
@Override
@SuppressWarnings("unchecked")
public <T> Predicate createNumberExpressionGenerics(
PathBuilder<T> entityPath, String fieldName, Class<?> fieldType,
TypeDescriptor descriptor, String searchStr) {
Predicate numberExpression = null;
if (isNumber(searchStr, descriptor)) {
if (BigDecimal.class.isAssignableFrom(fieldType)) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class<BigDecimal>) fieldType, descriptor,
searchStr);
}
if (BigInteger.class.isAssignableFrom(fieldType)) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class<BigInteger>) fieldType, descriptor,
searchStr);
}
if (Byte.class.isAssignableFrom(fieldType)) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class<Byte>) fieldType, descriptor,
searchStr);
}
if (Double.class.isAssignableFrom(fieldType)
|| double.class == fieldType) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class<Double>) fieldType, descriptor,
searchStr);
}
if (Float.class.isAssignableFrom(fieldType)
|| float.class == fieldType) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class<Float>) fieldType, descriptor,
searchStr);
}
if (Integer.class.isAssignableFrom(fieldType)
|| int.class == fieldType) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class<Integer>) fieldType, descriptor,
searchStr);
}
if (Long.class.isAssignableFrom(fieldType)
|| long.class == fieldType) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class<Long>) fieldType, descriptor,
searchStr);
}
if (Short.class.isAssignableFrom(fieldType)
|| short.class == fieldType) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class<Short>) fieldType, descriptor,
searchStr);
}
}
return numberExpression;
}
@Override
@SuppressWarnings("unchecked")
public <T> Predicate createNumberExpressionGenericsWithOperators(
PathBuilder<T> entityPath, String fieldName,
TypeDescriptor descriptor, String searchStr) {
Predicate numberExpression = null;
Class<?> fieldType = descriptor.getType();
if (isNumber(searchStr, descriptor)) {
if (BigDecimal.class.isAssignableFrom(fieldType)) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class<BigDecimal>) fieldType, descriptor,
searchStr);
}
if (BigInteger.class.isAssignableFrom(fieldType)) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class<BigInteger>) fieldType, descriptor,
searchStr);
}
if (Byte.class.isAssignableFrom(fieldType)) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class<Byte>) fieldType, descriptor,
searchStr);
}
if (Double.class.isAssignableFrom(fieldType)
|| double.class == fieldType) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class<Double>) fieldType, descriptor,
searchStr);
}
if (Float.class.isAssignableFrom(fieldType)
|| float.class == fieldType) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class<Float>) fieldType, descriptor,
searchStr);
}
if (Integer.class.isAssignableFrom(fieldType)
|| int.class == fieldType) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class<Integer>) fieldType, descriptor,
searchStr);
}
if (Long.class.isAssignableFrom(fieldType)
|| long.class == fieldType) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class<Long>) fieldType, descriptor,
searchStr);
}
if (Short.class.isAssignableFrom(fieldType)
|| short.class == fieldType) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class<Short>) fieldType, descriptor,
searchStr);
}
}
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);
}
if (BigInteger.class.isAssignableFrom(fieldType)) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class<BigInteger>) fieldType, descriptor,
searchStr);
}
if (Byte.class.isAssignableFrom(fieldType)) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class<Byte>) fieldType, descriptor,
searchStr);
}
if (Double.class.isAssignableFrom(fieldType)
|| double.class == fieldType) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class<Double>) fieldType, descriptor,
searchStr);
}
if (Float.class.isAssignableFrom(fieldType)
|| float.class == fieldType) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class<Float>) fieldType, descriptor,
searchStr);
}
if (Integer.class.isAssignableFrom(fieldType)
|| int.class == fieldType) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class<Integer>) fieldType, descriptor,
searchStr);
}
if (Long.class.isAssignableFrom(fieldType)
|| long.class == fieldType) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class<Long>) fieldType, descriptor,
searchStr);
}
if (Short.class.isAssignableFrom(fieldType)
|| short.class == fieldType) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class<Short>) fieldType, descriptor,
searchStr);
}
}
return numberExpression;
}
/**
* {@inheritDoc}
*/
@Override
public <T> BooleanExpression createObjectExpression(
PathBuilder<T> entityPath, String fieldName, Object searchObj) {
return createObjectExpression(entityPath, fieldName, searchObj, null);
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> BooleanExpression createObjectExpression(
PathBuilder<T> entityPath, String fieldName, Object searchObj,
String operator) {
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;
}
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public <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);
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public <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);
}
/**
* {@inheritDoc}
*/
@Override
public <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);
}
/**
* {@inheritDoc}
*/
@Override
public <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);
}
/**
* {@inheritDoc}
*/
@Override
public <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;
}
/**
* {@inheritDoc}
*/
@Override
public <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;
}
/**
* {@inheritDoc}
*/
@Override
public <T> BooleanExpression createStringExpressionWithOperators(
PathBuilder<T> entityPath, String fieldName, String searchStr) {
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;
}
/**
* {@inheritDoc}
*/
@Override
public <T, N extends Number & Comparable<?>> BooleanExpression createNumberExpression(
PathBuilder<T> entityPath, String fieldName, Class<N> fieldType,
TypeDescriptor descriptor, String searchStr) {
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;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public <T, N extends Number & Comparable<?>> BooleanExpression createNumberExpressionEqual(
PathBuilder<T> entityPath, String fieldName, Class<N> fieldType,
TypeDescriptor descriptor, String searchStr) {
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("%"));
}
}
/**
* {@inheritDoc}
*/
@Override
public <T, C extends 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;
}
/**
* {@inheritDoc}
*/
@Override
public <T, C extends Comparable<?>> BooleanExpression createDateExpressionWithOperators(
PathBuilder<T> entityPath, String fieldName, Class<C> fieldType,
String searchStr, 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;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public <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;
}
/**
* {@inheritDoc}
*/
@Override
public <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;
}
/**
* {@inheritDoc}
*/
@Override
public <T> BooleanExpression createBooleanExpressionWithOperators(
PathBuilder<T> entityPath, String fieldName, String searchStr) {
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;
}
/**
* {@inheritDoc}
*/
@Override
public <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);
}
}
@Override
public <T, E> BooleanExpression doCreateCollectionExpression(
PathBuilder<T> entityPath, String fieldName, Collection<E> values) {
BooleanExpression expression = entityPath.get(fieldName).in(values);
return expression;
}
/**
* {@inheritDoc}
*/
@Override
public <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;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public <T, N extends Number & Comparable<?>> BooleanExpression getNumericFilterExpression(
PathBuilder<T> entityPath, String fieldName, Class<N> fieldType,
TypeDescriptor descriptor, String searchStr) {
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;
}
/**
* {@inheritDoc}
*/
@Override
public <T> Class<?> getFieldType(String fieldName, PathBuilder<T> entity) {
TypeDescriptor descriptor = getTypeDescriptor(fieldName, entity);
return descriptor.getType();
}
/**
* {@inheritDoc}
*/
@Override
public <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);
}
/**
* {@inheritDoc}
*/
@Override
public <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);
}
/**
* {@inheritDoc}
*/
@Override
public <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;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isNumber(String searchStr, TypeDescriptor descriptor) {
return isValidValueFor(searchStr, descriptor, conversionService);
}
protected ConversionService getConversionService() {
return conversionService;
}
protected MessageSource getMessageSource() {
return messageSource;
}
protected EntityManagerProvider getEntityManagerProvider() {
return entityManagerProvider;
}
protected static LoadingCache<Class<?>, BeanWrapper> getBeanWrappersCache() {
return beanWrappersCache;
}
}