Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.gvnix.web.datatables.util.QuerydslUtils Maven / Gradle / Ivy
/*
* 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, BeanWrapper> beanWrappersCache = CacheBuilder
.newBuilder().maximumSize(200)
.build(new CacheLoader, BeanWrapper>() {
public BeanWrapper load(Class> key) {
return new BeanWrapperImpl(key);
}
});
public static final Set> NUMBER_PRIMITIVES = new HashSet>(
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. Warning: 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.
*
* 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.
*
* 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 BooleanBuilder createPredicateByAnd(
PathBuilder entity, Map 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 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 valueColl = (Collection) 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.
*
* 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 BooleanBuilder createPredicateByIn(
PathBuilder entity, String fieldName, Set 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 Predicate createExpression(PathBuilder 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 Predicate createExpression(PathBuilder 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 Predicate createExpression(PathBuilder 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) 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 Predicate createExpression(PathBuilder 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) 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 Predicate createNumberExpressionGenerics(
PathBuilder 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) fieldType, descriptor,
searchStr, conversionService);
}
if (BigInteger.class.isAssignableFrom(fieldType)) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Byte.class.isAssignableFrom(fieldType)) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Double.class.isAssignableFrom(fieldType)
|| double.class == fieldType) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Float.class.isAssignableFrom(fieldType)
|| float.class == fieldType) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Integer.class.isAssignableFrom(fieldType)
|| int.class == fieldType) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Long.class.isAssignableFrom(fieldType)
|| long.class == fieldType) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Short.class.isAssignableFrom(fieldType)
|| short.class == fieldType) {
numberExpression = createNumberExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
}
return numberExpression;
}
@SuppressWarnings("unchecked")
public static Predicate createNumberExpressionGenericsWithOperators(
PathBuilder 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) fieldType, descriptor,
searchStr, conversionService);
}
if (BigInteger.class.isAssignableFrom(fieldType)) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Byte.class.isAssignableFrom(fieldType)) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Double.class.isAssignableFrom(fieldType)
|| double.class == fieldType) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Float.class.isAssignableFrom(fieldType)
|| float.class == fieldType) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Integer.class.isAssignableFrom(fieldType)
|| int.class == fieldType) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Long.class.isAssignableFrom(fieldType)
|| long.class == fieldType) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService);
}
if (Short.class.isAssignableFrom(fieldType)
|| short.class == fieldType) {
numberExpression = createNumberExpressionEqual(entityPath,
fieldName, (Class) 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) fieldType, descriptor,
searchStr, conversionService, messageSource);
}
if (BigInteger.class.isAssignableFrom(fieldType)) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService, messageSource);
}
if (Byte.class.isAssignableFrom(fieldType)) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService, messageSource);
}
if (Double.class.isAssignableFrom(fieldType)
|| double.class == fieldType) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService, messageSource);
}
if (Float.class.isAssignableFrom(fieldType)
|| float.class == fieldType) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService, messageSource);
}
if (Integer.class.isAssignableFrom(fieldType)
|| int.class == fieldType) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService, messageSource);
}
if (Long.class.isAssignableFrom(fieldType)
|| long.class == fieldType) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService, messageSource);
}
if (Short.class.isAssignableFrom(fieldType)
|| short.class == fieldType) {
numberExpression = getNumericFilterExpression(entityPath,
fieldName, (Class) fieldType, descriptor,
searchStr, conversionService, messageSource);
}
}
return numberExpression;
}
/**
* Return equal expression for {@code entityPath.fieldName}.
*
* 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 BooleanExpression createObjectExpression(
PathBuilder 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.
*
* 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:
*
* For all types: {@code eq}, {@code in}, {@code ne},
* {@code notIn}, {@code isNull} and {@code isNotNull}. For
* strings and numbers: {@code goe}, {@code gt}, {@code loe},
* {@code lt} and {@code like}. For booleans: {@code goe},
* {@code gt}, {@code loe} and {@code lt}. For dates:
* {@code goe}, {@code gt}, {@code before}, {@code loe}, {@code lt}
* and {@code after}.
*
* @return BooleanExpression
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static BooleanExpression createObjectExpression(
PathBuilder 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
* If conversion service is not provided try to check by apache commons
* utilities. TODO 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.
*
* Expr: {@code entityPath.fieldName eq searchObj}
*
* @param entityPath
* @param fieldName
* @param searchObj
* @param operator
* @param fieldType
* @return
*/
@SuppressWarnings("unchecked")
public static BooleanExpression createDateExpression(
PathBuilder entityPath, String fieldName, Object searchObj,
String operator, Class> fieldType) {
DatePath dateExpression = entityPath.getDate(fieldName,
(Class) 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.
*
* Expr: {@code entityPath.fieldName eq searchObj}
*
* @param entityPath
* @param fieldName
* @param searchObj
* @param operator
* @param fieldType
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static BooleanExpression createNumericExpression(
PathBuilder entityPath, String fieldName, Object searchObj,
String operator, Class> fieldType) {
NumberPath numberExpression = null;
if (BigDecimal.class.isAssignableFrom(fieldType)) {
numberExpression = entityPath.getNumber(fieldName,
(Class) fieldType);
}
else if (BigInteger.class.isAssignableFrom(fieldType)) {
numberExpression = entityPath.getNumber(fieldName,
(Class) fieldType);
}
else if (Byte.class.isAssignableFrom(fieldType)) {
numberExpression = entityPath.getNumber(fieldName,
(Class) fieldType);
}
else if (Double.class.isAssignableFrom(fieldType)
|| double.class == fieldType) {
numberExpression = entityPath.getNumber(fieldName,
(Class) fieldType);
}
else if (Float.class.isAssignableFrom(fieldType)
|| float.class == fieldType) {
numberExpression = entityPath.getNumber(fieldName,
(Class) fieldType);
}
else if (Integer.class.isAssignableFrom(fieldType)
|| int.class == fieldType) {
numberExpression = entityPath.getNumber(fieldName,
(Class) fieldType);
}
else if (Long.class.isAssignableFrom(fieldType)
|| long.class == fieldType) {
numberExpression = entityPath.getNumber(fieldName,
(Class) fieldType);
}
else if (Short.class.isAssignableFrom(fieldType)
|| short.class == fieldType) {
numberExpression = entityPath.getNumber(fieldName,
(Class) 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.
*
* Expr: {@code entityPath.fieldName eq searchObj}
*
* @param entityPath
* @param fieldName
* @param searchObj
* @param operator
* @return
*/
public static BooleanExpression createBooleanExpression(
PathBuilder 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.
*
* Expr: {@code entityPath.fieldName eq searchObj}
*
* @param entityPath
* @param fieldName
* @param searchObj
* @param operator
* @return
*/
public static BooleanExpression createStringExpression(
PathBuilder 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}.
*
* Expr: {@code entityPath.fieldName eq 'searchStr'}
*
* 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 BooleanExpression createStringExpression(
PathBuilder 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}.
*
* Expr: {@code entityPath.fieldName like ('%' + searchStr + '%')}
*
* 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 BooleanExpression createStringLikeExpression(
PathBuilder 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}.
*
* Expr: {@code entityPath.fieldName like ('%' + searchStr + '%')}
*
* 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 BooleanExpression createStringExpressionWithOperators(
PathBuilder 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.
*
* Querydsl Expr:
* {@code entityPath.fieldName.stringValue() like ('%' + searchStr + '%')}
* Database operation:
* {@code str(entity.fieldName) like ('%' + searchStr + '%')}
*
* 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 > BooleanExpression createNumberExpression(
PathBuilder entityPath, String fieldName, Class fieldType,
TypeDescriptor descriptor, String searchStr,
ConversionService conversionService) {
if (StringUtils.isBlank(searchStr)) {
return null;
}
NumberPath 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.
*
* Querydsl Expr:
* {@code entityPath.fieldName.stringValue() eq searchStr
* Database operation:
* {@code str(entity.fieldName) = searchStr
*
* 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 > BooleanExpression createNumberExpressionEqual(
PathBuilder entityPath, String fieldName, Class fieldType,
TypeDescriptor descriptor, String searchStr,
ConversionService conversionService) {
if (StringUtils.isEmpty(searchStr)) {
return null;
}
NumberPath 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.
*
*
*
* If value to find {@code searchStr} can be parsed using the patterns
* dd-MM-yyyy HH:mm:ss or dd-MM-yyyy HH:mm or
* dd-MM-yyyy to {@code searchDate}, then search by specific date:
*
* - Querydsl Expr: {@code entityPath.fieldName = searchDate}
*
* - Database operation: {@code entity.fieldName = searchDate}
*
* If value to find {@code searchStr} can be parsed using the pattern
* dd-MM to {@code searchDate}, then search by specific day and
* month:
*
* - Querydsl Expr:
* {@code entityPath.fieldName.dayOfMonth() = searchDate.day and entityPath.fieldName.month() = searchDate.month}
*
* - Database operation:
* {@code dayofmonth(entity.fieldName) = searchDate.day && month(entity.fieldName) = searchDate.month}
*
*
* If value to find {@code searchStr} can be parsed using the pattern
* MM-aaaa 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:
*
* - Querydsl Expr:
* {@code entityPath.fieldName.between(searchDate.firstDayOfMonth, searchDate.lastDayOfMonth)}
*
* - Database operation:
* {@code entity.fieldName between searchDate.firstDayOfMonth and searchDate.lastDayOfMonth}
*
*
* If value to find cannot be parsed as date, then try to cast the value to
* string before check it:
*
* - Querydsl Expr:
* {@code entityPath.fieldName.stringValue() like ('%' + searchStr + '%')}
*
* - Database operation:
* {@code str(entity.fieldName) like ('%' + searchStr + '%')}
*
* Note that 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 > BooleanExpression createDateExpression(
PathBuilder entityPath, String fieldName, Class fieldType,
String searchStr) {
if (StringUtils.isEmpty(searchStr)) {
return null;
}
DatePath 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.
*
*
*
* If value to find {@code searchStr} can be parsed using the patterns
* dd-MM-yyyy HH:mm:ss or dd-MM-yyyy HH:mm or
* dd-MM-yyyy to {@code searchDate}, then search by specific date:
*
* - Querydsl Expr: {@code entityPath.fieldName = searchDate}
*
* - Database operation: {@code entity.fieldName = searchDate}
*
* If value to find {@code searchStr} can be parsed using the pattern
* dd-MM to {@code searchDate}, then search by specific day and
* month:
*
* - Querydsl Expr:
* {@code entityPath.fieldName.dayOfMonth() = searchDate.day and entityPath.fieldName.month() = searchDate.month}
*
* - Database operation:
* {@code dayofmonth(entity.fieldName) = searchDate.day && month(entity.fieldName) = searchDate.month}
*
*
* If value to find {@code searchStr} can be parsed using the pattern
* MM-aaaa 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:
*
* - Querydsl Expr:
* {@code entityPath.fieldName.between(searchDate.firstDayOfMonth, searchDate.lastDayOfMonth)}
*
* - Database operation:
* {@code entity.fieldName between searchDate.firstDayOfMonth and searchDate.lastDayOfMonth}
*
*
* If value to find cannot be parsed as date, then try to cast the value to
* string before check it:
*
* - Querydsl Expr:
* {@code entityPath.fieldName.stringValue() like ('%' + searchStr + '%')}
*
* - Database operation:
* {@code str(entity.fieldName) like ('%' + searchStr + '%')}
*
* Note that 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 > BooleanExpression createDateExpressionWithOperators(
PathBuilder entityPath, String fieldName, Class fieldType,
String searchStr, ConversionService conversionService,
MessageSource messageSource, String datePattern) {
if (StringUtils.isEmpty(searchStr)) {
return null;
}
DatePath 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.
*
* Expr:
* {@code entityPath.fieldName.as(String.class) like ('%' + searchStr + '%')}
*
* 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 BooleanExpression createEnumExpression(
PathBuilder 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.
*
* 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 BooleanExpression createBooleanExpression(
PathBuilder 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.
*
* 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 BooleanExpression createBooleanExpressionWithOperators(
PathBuilder 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}.
*
* Expr:
* entityPath.fieldName IN ( values )
*
* If values.size() > 500 its generates:
* Expr:
* (entityPath.fieldName IN ( values[0-500] ) OR [entityPath.fieldName IN (
* values[501-100]... ]))
*
*
* @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 BooleanExpression createCollectionExpression(
PathBuilder entityPath, String fieldName, Collection values) {
if (StringUtils.isEmpty(fieldName) || values.isEmpty()) {
return null;
}
if (values.size() > 500) {
BooleanExpression expression = null;
Iterable> collectionParts = Iterables
.partition(values, 500);
for (List 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 BooleanExpression doCreateCollectionExpression(
PathBuilder entityPath, String fieldName, Collection 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 > OrderSpecifier> createOrderSpecifier(
PathBuilder entityPath, String fieldName, Class 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 > BooleanExpression getNumericFilterExpression(
PathBuilder entityPath, String fieldName, Class fieldType,
TypeDescriptor descriptor, String searchStr,
ConversionService conversionService, MessageSource messageSource) {
if (StringUtils.isEmpty(searchStr)) {
return null;
}
TypeDescriptor strDesc = STRING_TYPE_DESCRIPTOR;
NumberPath 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 Class> getFieldType(String fieldName,
PathBuilder 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 Class> getFieldType1(String fieldName,
PathBuilder entity) {
Class> entityType = entity.getType();
String fieldNameToFindType = fieldName;
// Makes the array of classes to find fieldName agains them
Class>[] classArray = ArrayUtils.> 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.> 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 TypeDescriptor getTypeDescriptor(String fieldName,
PathBuilder 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 TypeDescriptor getTypeDescriptor(String fieldName,
Class 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);
}
}