From 9df8a32b855ef734a9d9a778def66ea9e36c4f7c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 10 Mar 2022 10:21:46 +0100 Subject: [PATCH] Accept `Object` values in `QuerydslPredicateBuilder`. We now accept Object-typed values to allow broader reuse of QuerydslPredicateBuilder. The conversion into the actual value considers the origin type and checks assignability before employing the ConversionService. Closes #2573 --- .../binding/QuerydslPredicateBuilder.java | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java b/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java index bd8bb3a8c..546d63828 100644 --- a/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java +++ b/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java @@ -32,9 +32,11 @@ import org.springframework.core.convert.TypeDescriptor; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.querydsl.EntityPathResolver; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; +import org.springframework.util.ObjectUtils; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Path; @@ -81,8 +83,7 @@ public class QuerydslPredicateBuilder { * @param bindings the {@link QuerydslBindings} for the predicate. * @return the {@link Predicate}. */ - public Predicate getPredicate(TypeInformation type, MultiValueMap values, - QuerydslBindings bindings) { + public Predicate getPredicate(TypeInformation type, MultiValueMap values, QuerydslBindings bindings) { Assert.notNull(bindings, "Context must not be null!"); @@ -92,9 +93,9 @@ public class QuerydslPredicateBuilder { return getPredicate(builder); } - for (Entry> entry : values.entrySet()) { + for (Entry> entry : values.entrySet()) { - if (isSingleElementCollectionWithoutText(entry.getValue())) { + if (isSingleElementCollectionWithEmptyItem(entry.getValue())) { continue; } @@ -165,33 +166,44 @@ public class QuerydslPredicateBuilder { /** * Converts the given source values into a collection of elements that are of the given {@link PropertyPath}'s type. - * Considers a single element list with an empty {@link String} an empty collection because this basically indicates - * the property having been submitted but no value provided. + * Considers a single element list with an empty object an empty collection because this basically indicates the + * property having been submitted but no value provided. * * @param source must not be {@literal null}. * @param path must not be {@literal null}. * @return */ - private Collection convertToPropertyPathSpecificType(List source, PathInformation path) { + private Collection convertToPropertyPathSpecificType(List source, PathInformation path) { Class targetType = path.getLeafType(); - if (source.isEmpty() || isSingleElementCollectionWithoutText(source)) { + if (source.isEmpty() || isSingleElementCollectionWithEmptyItem(source)) { return Collections.emptyList(); } Collection target = new ArrayList<>(source.size()); - for (String value : source) { - - target.add(conversionService.canConvert(String.class, targetType) - ? conversionService.convert(value, TypeDescriptor.forObject(value), getTargetTypeDescriptor(path)) - : value); + for (Object value : source) { + target.add(getValue(path, targetType, value)); } return target; } + @Nullable + private Object getValue(PathInformation path, Class targetType, Object value) { + + if (ClassUtils.isAssignableValue(targetType, value)) { + return value; + } + + if (conversionService.canConvert(value.getClass(), targetType)) { + return conversionService.convert(value, TypeDescriptor.forObject(value), getTargetTypeDescriptor(path)); + } + + return value; + } + /** * Returns the target {@link TypeDescriptor} for the given {@link PathInformation} by either inspecting the field or * property (the latter preferred) to pick up annotations potentially defined for formatting purposes. @@ -213,21 +225,21 @@ public class QuerydslPredicateBuilder { .nested(new Property(owningType, descriptor.getReadMethod(), descriptor.getWriteMethod(), leafProperty), 0); if (result == null) { - throw new IllegalStateException(String.format("Could not obtain TypeDesciptor for PathInformation %s!", path)); + throw new IllegalStateException(String.format("Could not obtain TypeDescriptor for PathInformation %s!", path)); } return result; } /** - * Returns whether the given collection has exactly one element that doesn't contain any text. This is basically an - * indicator that a request parameter has been submitted but no value for it. + * Returns whether the given collection has exactly one element that is empty (i.e. doesn't contain text). This is + * basically an indicator that a request parameter has been submitted but no value for it. * * @param source must not be {@literal null}. * @return */ - private static boolean isSingleElementCollectionWithoutText(List source) { - return source.size() == 1 && !StringUtils.hasLength(source.get(0)); + private static boolean isSingleElementCollectionWithEmptyItem(List source) { + return source.size() == 1 && ObjectUtils.isEmpty(source.get(0)); } /**