DATACMNS-669 - Added support to bind Querydsl Predicates to Spring MVC controllers.

Adding @QuerydslPredicate to a parameter within SpringMVC Controller when having registered the QueryDslPredicateArgumentResolver allows to generate a Querydsl Predicate based on the request parameters.

Parameters will be converted into their corresponding property types before using the as predicate value. Given more than one attribute will connect those using 'and'. Collections of values will force usage of 'in' while single values on collection like properties result in 'contains'.

The base type for building the Querydsl Path is extracted from the methods return type. In case of non domain type return values it can optionally be specified via @QueydslPredicate(root = …). Specific Predicate conversions can be registered via '@QuerydslSpecification'.

This allows to sneak in specific property path handlings more easily.

new QueryDslPredicateSpecification() {
  {
    define(new StringPath("address.city"), (path, value) -> path.like(value.toString()));
  }
};

Renamed resolver, accessor etc. to express operation of binding properties to a resolved path and introduced explicit binding context which allows reuse of predicate builder within the ArgumentResolver.

Added registration of QuerydslPredicateArgumentResolver directly in SpringDataWebConfiguration reusing the provided ConversionService.

Added Javadoc and changed visibility of non public API and types.

Original pull request: #132.
This commit is contained in:
Christoph Strobl
2015-07-06 15:13:20 +02:00
committed by Oliver Gierke
parent 30b4cf53fc
commit e645501174
14 changed files with 1521 additions and 1 deletions

View File

@@ -26,10 +26,12 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.geo.format.DistanceFormatter;
import org.springframework.data.geo.format.PointFormatter;
import org.springframework.data.querydsl.QueryDslUtils;
import org.springframework.data.repository.support.DomainClassConverter;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.data.web.ProxyingHandlerMethodArgumentResolver;
import org.springframework.data.web.SortHandlerMethodArgumentResolver;
import org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@@ -97,6 +99,10 @@ public class SpringDataWebConfiguration extends WebMvcConfigurerAdapter {
argumentResolvers.add(sortResolver());
argumentResolvers.add(pageableResolver());
if (QueryDslUtils.QUERY_DSL_PRESENT) {
argumentResolvers.add(new QuerydslPredicateArgumentResolver(conversionService.getObject()));
}
ProxyingHandlerMethodArgumentResolver resolver = new ProxyingHandlerMethodArgumentResolver(
conversionService.getObject());
resolver.setBeanFactory(context);

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2015 the original author or authors.
*
* 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.springframework.data.web.querydsl;
import com.mysema.query.types.Path;
import com.mysema.query.types.Predicate;
/**
* {@link QuerydslBinding} creates a {@link Predicate} out of given {@link Path} and value. Used for specific parameter
* treatment in {@link QuerydslBindings}. <br />
*
* @author Christoph Strobl
* @since 1.11
*/
public interface QuerydslBinding<T extends Path<?>> {
/**
* @param path {@link Path} to the property. Must not be {@literal null}.
* @param value
* @return
*/
Predicate bind(T path, Object value);
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2015 the original author or authors.
*
* 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.springframework.data.web.querydsl;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.mysema.query.types.Path;
/**
* {@link QuerydslBindingContext} holds parameters required for executing {@link QuerydslBinding} on a specific property
* and {@link Path}.
*
* @author Christoph Strobl
* @since 1.11
*/
class QuerydslBindingContext {
private final TypeInformation<?> rootTypeInformation;
private final ConversionService conversionService;
private final QuerydslBindings bindings;
/**
* @param rootTypeInformation Root type information. Must not be {@literal null}.
* @param bindings Path specific bindings. Defaulted to {@link QuerydslBindings} if {@literal null}.
* @param conversionService {@link ConversionService} to be used for converting request parameters into target type.
* Defaulted to {@link DefaultConversionService} if {@literal null}.
*/
public QuerydslBindingContext(TypeInformation<?> rootTypeInformation, QuerydslBindings bindings,
ConversionService conversionService) {
Assert.notNull(rootTypeInformation, "TypeInformation must not be null!");
this.conversionService = conversionService == null ? new DefaultConversionService() : conversionService;
this.rootTypeInformation = rootTypeInformation;
this.bindings = bindings == null ? new QuerydslBindings() : bindings;
}
/**
* {@link ConversionService} to be used for converting parameter values.
*
* @return never {@literal null}.
*/
public ConversionService getConversionService() {
return this.conversionService;
}
/**
* Root type information
*
* @return never {@literal null}.
*/
public TypeInformation<?> getTypeInformation() {
return rootTypeInformation;
}
/**
* {@link QuerydslBindings} to be considered.
*
* @return never {@literal null}.
*/
public QuerydslBindings getBindings() {
return bindings;
}
/**
* Specific {@link QuerydslBinding} for {@link PropertyPath}.
*
* @param path must not be {@literal null}.
* @return {@literal null} when no specific {@link QuerydslBinding} set for path.
* @see QuerydslBindings#getBindingForPath(PropertyPath)
*/
public QuerydslBinding<? extends Path<?>> getBindingForPath(PropertyPath path) {
return bindings.getBindingForPath(path);
}
/**
* Specific {@link Path} for {@link PropertyPath}.
*
* @param path
* @return
* @see QuerydslBindings#getPath(PropertyPath)
*/
public Path<?> getPathForPropertyPath(PropertyPath path) {
return bindings.getPath(path);
}
/**
* Checks visibility of given path against {@link QuerydslBindings}.
*
* @param path must not be {@literal null}.
* @return true when given path is visible by declaration.
* @see QuerydslBindings#isPathVisible(PropertyPath)
*/
public boolean isPathVisible(PropertyPath path) {
return bindings.isPathVisible(path);
}
/**
* Get the actual raw type of the used root.
*
* @return
* @see TypeInformation#getActualType()
*/
public Class<?> getTargetType() {
return rootTypeInformation.getActualType().getType();
}
}

View File

@@ -0,0 +1,248 @@
/*
* Copyright 2015 the original author or authors.
*
* 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.springframework.data.web.querydsl;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.mysema.query.types.Path;
/**
* {@link QuerydslBindings} allows definition of path specific {@link QuerydslBinding}. *
*
* <pre>
* <code>
* new QuerydslBindings() {
* {
* bind(QUser.user.address.city, (path, value) -> path.like(value.toString()));
* bind(new StringPath("address.city"), (path, value) -> path.like(value.toString()));
* }
* }
* </code>
* </pre>
*
* @author Christoph Strobl
* @since 1.11
*/
public class QuerydslBindings {
private final Map<String, PathAndBinding> pathSpecs;
private final Map<Class<?>, PathAndBinding> typeSpecs;
private Set<String> whiteList;
private final Set<String> blackList;
public QuerydslBindings() {
this.pathSpecs = new LinkedHashMap<String, PathAndBinding>();
this.typeSpecs = new LinkedHashMap<Class<?>, PathAndBinding>();
this.whiteList = new HashSet<String>();
this.blackList = new HashSet<String>();
}
/**
* @param path
* @param builder
*/
protected QuerydslBindings bind(String path, QuerydslBinding<? extends Path<?>> builder) {
Assert.hasText(path, "Cannot bind to null/empty path");
this.pathSpecs.put(path, new PathAndBinding(null, builder));
return this;
}
/**
* @param type
* @param builder
*/
protected <T> QuerydslBindings bind(Class<T> type, QuerydslBinding<? extends Path<T>> builder) {
Assert.notNull(type, "Cannot bind to null type!");
this.typeSpecs.put(type, new PathAndBinding(null, builder));
return this;
}
/**
* @param path
* @param builder
*/
protected <T extends Path<?>> QuerydslBindings bind(T path, QuerydslBinding<T> builder) {
Assert.notNull(path, "Cannot bind to null path!");
this.pathSpecs.put(extractPropertyPath(path), new PathAndBinding(path, builder));
return this;
}
/**
* Exclude properties from binding. <br />
* Exclusion of all properties of a nested type can be done by exclusion on a higher level. Eg. {@code address} would
* exclude both {@code address.city} and {@code address.street}.
*
* @param properties
*/
protected QuerydslBindings excluding(String... properties) {
this.blackList.addAll(Arrays.asList(properties));
return this;
}
/**
* Include properties for binding. <br />
* Include the property considered a binding candidate.
*
* @param properties
*/
protected QuerydslBindings including(String... properties) {
this.whiteList.addAll(Arrays.asList(properties));
return this;
}
/**
* Checks if a given {@link PropertyPath} should be visible for binding values.
*
* @param path
* @return
*/
public boolean isPathVisible(PropertyPath path) {
List<String> segments = Arrays.asList(path.toDotPath().split("\\."));
for (int i = 1; i <= segments.size(); i++) {
if (!isPathVisible(StringUtils.collectionToDelimitedString(segments.subList(0, i), "."))) {
// check if full path is on whitelist if though partial one is not
if (!whiteList.isEmpty()) {
return whiteList.contains(path.toDotPath());
}
return false;
}
}
return true;
}
private boolean isPathVisible(String path) {
if (whiteList.isEmpty() && blackList.isEmpty()) {
return true;
}
if (!blackList.isEmpty()) {
if (blackList.contains(path)) {
if (!whiteList.isEmpty() && whiteList.contains(path)) {
return true;
}
return false;
}
return true;
}
if (!whiteList.isEmpty()) {
if (whiteList.contains(path)) {
return true;
}
return false;
}
return true;
}
/**
* Returns the {@link QuerydslBinding} for the given {@link PropertyPath}. Prefers a path configured for the specific
* path but falls back to the builder registered for a given type.
*
* @param path must not be {@literal null}.
* @return
*/
public QuerydslBinding<? extends Path<?>> getBindingForPath(PropertyPath path) {
Assert.notNull(path, "PropertyPath must not be null!");
PathAndBinding pathAndBinding = pathSpecs.get(path.toDotPath());
if (pathAndBinding != null) {
return pathAndBinding.getBinding();
}
pathAndBinding = typeSpecs.get(path.getLeafProperty().getType());
return pathAndBinding == null ? null : pathAndBinding.getBinding();
}
Path<?> getPath(PropertyPath path) {
return getPathForStringPath(path.toDotPath());
}
private Path<?> getPathForStringPath(String path) {
PathAndBinding pathAndBuilder = pathSpecs.get(path);
if (pathAndBuilder == null) {
return null;
}
return pathAndBuilder.getPath();
}
private String extractPropertyPath(Path<?> path) {
if (path == null) {
return "";
}
if (path.getMetadata().getParent() != null && !path.getMetadata().getParent().getMetadata().isRoot()) {
return extractPropertyPath(path.getMetadata().getParent()) + "." + path.getMetadata().getName();
}
return path.getMetadata().getName();
}
/**
* @author Christoph Strobl
* @since 1.11
*/
private static class PathAndBinding {
private final Path<?> path;
private final QuerydslBinding<? extends Path<?>> binding;
public PathAndBinding(Path<?> path, QuerydslBinding<? extends Path<?>> binding) {
this.path = path;
this.binding = binding;
}
public Path<?> getPath() {
return path;
}
public QuerydslBinding<? extends Path<?>> getBinding() {
return binding;
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2015 the original author or authors.
*
* 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.springframework.data.web.querydsl;
import java.util.Collection;
import com.mysema.query.types.Path;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.expr.SimpleExpression;
import com.mysema.query.types.path.CollectionPathBase;
/**
* Default implementation of {@link QuerydslBinding} creating {@link Predicate} based on the {@link Path}s type. Binds:
* <ul>
* <li><i>{@literal null}</i> as {@link SimpleExpression#isNull()}.</li>
* <li><i>{@link java.lang.Object}</i> as {@link SimpleExpression#eq()} on simple properties.</li>
* <li><i>{@link java.lang.Object}</i> as {@link SimpleExpression#contains()} on collection properties.</li>
* <li><i>{@link java.util.Collection}</i> as {@link SimpleExpression#in()} on simple properties.</li>
* </ul>
*
* @author Christoph Strobl
* @since 1.11
*/
class QuerydslDefaultBinding implements QuerydslBinding<Path<?>> {
/*
* (non-Javadoc)
* @see org.springframework.data.web.querydsl.QueryDslPredicateBuilder#buildPredicate(org.springframework.data.mapping.PropertyPath, java.lang.Object)
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public Predicate bind(Path<?> path, Object source) {
if (source == null && path instanceof SimpleExpression) {
return ((SimpleExpression) path).isNull();
}
if (path instanceof CollectionPathBase) {
return ((CollectionPathBase) path).contains(source);
}
if (path instanceof SimpleExpression) {
if (source instanceof Collection) {
return ((SimpleExpression) path).in((Collection) source);
}
return ((SimpleExpression) path).eq(source);
}
throw new IllegalArgumentException(String.format("Cannot create predicate for path '%s' with type '%s'.", path,
path.getMetadata().getPathType()));
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2015 the original author or authors.
*
* 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.springframework.data.web.querydsl;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* {@link java.lang.annotation.Annotation} to specify aspects of {@link com.mysema.query.types.Predicate} used in Spring
* MVC {@link org.springframework.web.servlet.mvc.Controller}.
*
* @author Christoph Strobl
* @since 1.11
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface QuerydslPredicate {
/**
* Root type to be used for {@link com.mysema.query.types.Path}
*
* @return
*/
Class<?> root() default Object.class;
/**
* Configuration class providing options on a per field base.
*
* @return
*/
Class<? extends QuerydslBindings> bindings() default QuerydslBindings.class;
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 2015 the original author or authors.
*
* 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.springframework.data.web.querydsl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import com.mysema.query.types.Predicate;
/**
* {@link HandlerMethodArgumentResolver} to allow injection of {@link com.mysema.query.types.Predicate} into Spring MVC
* controller methods.
*
* @author Christoph Strobl
* @since 1.11
*/
public class QuerydslPredicateArgumentResolver implements HandlerMethodArgumentResolver {
private final QuerydslPredicateBuilder predicateBuilder;
private final ConversionService conversionService;
/**
* @param conversionService Defaulted to {@link DefaultConversionService} if {@literal null}.
*/
public QuerydslPredicateArgumentResolver(ConversionService conversionService) {
this.conversionService = conversionService == null ? new DefaultConversionService() : conversionService;
this.predicateBuilder = new QuerydslPredicateBuilder();
}
/*
* (non-Javadoc)
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (Predicate.class.equals(parameter.getParameterType())) {
return true;
}
if (parameter.hasParameterAnnotation(QuerydslPredicate.class)) {
throw new IllegalArgumentException(String.format(
"Parameter at position %s must be of type Predicate but was %s.", parameter.getParameterIndex(),
parameter.getParameterType()));
}
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory)
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return predicateBuilder.getPredicate(new MutablePropertyValues(webRequest.getParameterMap()),
createBindingContext(parameter));
}
private QuerydslBindingContext createBindingContext(MethodParameter parameter) throws InstantiationException,
IllegalAccessException {
return new QuerydslBindingContext(extractTypeInfo(parameter), extractBindings(parameter), conversionService);
}
private TypeInformation<?> extractTypeInfo(MethodParameter parameter) {
Class<?> type = parameter.getParameterAnnotation(QuerydslPredicate.class).root();
return type == Object.class ? ClassTypeInformation.fromReturnTypeOf(parameter.getMethod()) : ClassTypeInformation
.from(type);
}
private QuerydslBindings extractBindings(MethodParameter parameter) throws InstantiationException,
IllegalAccessException {
return BeanUtils.instantiateClass(parameter.getParameterAnnotation(QuerydslPredicate.class).bindings());
}
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright 2015 the original author or authors.
*
* 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.springframework.data.web.querydsl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import com.mysema.query.BooleanBuilder;
import com.mysema.query.types.Path;
import com.mysema.query.types.PathMetadata;
import com.mysema.query.types.PathMetadataFactory;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.path.CollectionPath;
import com.mysema.query.types.path.NumberPath;
import com.mysema.query.types.path.PathBuilder;
import com.mysema.query.types.path.PathBuilderFactory;
import com.mysema.query.types.path.SimplePath;
import com.mysema.query.types.path.StringPath;
/**
* Builder assembling {@link Predicate} out of {@link PropertyValues}.
*
* @author Christoph Strobl
* @since 1.11
*/
class QuerydslPredicateBuilder {
private final QuerydslBinding<?> defaultBinding;
public QuerydslPredicateBuilder() {
this.defaultBinding = new QuerydslDefaultBinding();
}
/**
* @param values
* @param context
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Predicate getPredicate(PropertyValues values, QuerydslBindingContext context) {
Assert.notNull(context, "Context must not be null!");
if (values.isEmpty()) {
return new BooleanBuilder();
}
BooleanBuilder builder = new BooleanBuilder();
for (PropertyValue propertyValue : values.getPropertyValues()) {
PropertyPath propertyPath = PropertyPath.from(propertyValue.getName(), context.getTargetType());
if (context.isPathVisible(propertyPath)) {
Object value = convertToPropertyPathSpecificType(propertyValue.getValue(), propertyPath, context);
QuerydslBinding binding = getPredicateBuilderForPath(propertyPath, context);
builder.and(binding.bind(getPath(propertyPath, context), value));
}
}
return builder.getValue();
}
private QuerydslBinding<? extends Path<?>> getPredicateBuilderForPath(PropertyPath dotPath,
QuerydslBindingContext context) {
QuerydslBinding<? extends Path<?>> binding = context.getBindingForPath(dotPath);
return binding == null ? defaultBinding : binding;
}
private Path<?> getPath(PropertyPath propertyPath, QuerydslBindingContext context) {
Path<?> path = context.getPathForPropertyPath(propertyPath);
if (path != null) {
return path;
}
return new PropertyPathPathBuilder(context.getTargetType()).forProperty(propertyPath);
}
private Object convertToPropertyPathSpecificType(Object source, PropertyPath path, QuerydslBindingContext context) {
Object value = source;
if (ObjectUtils.isArray(value)) {
List<?> list = CollectionUtils.arrayToList(value);
if (!list.isEmpty() && list.size() == 1) {
value = list.get(0);
} else {
value = list;
}
}
if (value instanceof Collection) {
return potentiallyConvertCollectionValues((Collection<?>) value, path.getType(), context);
}
return potentiallyConvertValue(value, path.getType(), context);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private Collection<?> potentiallyConvertCollectionValues(Collection<?> source, Class<?> targetType,
QuerydslBindingContext context) {
Collection target = new ArrayList(source.size());
for (Object value : source) {
target.add(potentiallyConvertValue(value, targetType, context));
}
return target;
}
private Object potentiallyConvertValue(Object source, Class<?> targetType, QuerydslBindingContext context) {
return context.getConversionService().canConvert(source.getClass(), targetType)
? context.getConversionService().convert(source, targetType) : source;
}
/**
* {@link PropertyPathPathBuilder} creates a typed {@link Path} based on type information contained in
* {@link PropertyPath}.
*
* @author Christoph Strobl
*/
@SuppressWarnings("rawtypes")
static class PropertyPathPathBuilder {
final PathBuilder<?> pathBuilder;
public PropertyPathPathBuilder(Class<?> type) {
pathBuilder = new PathBuilderFactory().create(type);
}
@SuppressWarnings({ "unchecked" })
public Path<?> forProperty(PropertyPath source) {
PathMetadata<?> metadata = PathMetadataFactory.forVariable(source.toDotPath());
Path<?> path = null;
if (source.isCollection()) {
path = pathBuilder.get(new CollectionPath(Collection.class, source.getType(), metadata));
} else if (ClassUtils.isAssignable(String.class, source.getType())) {
path = pathBuilder.get(new StringPath(metadata));
} else if (ClassUtils.isAssignable(Number.class, source.getType())) {
path = pathBuilder.get(new NumberPath(source.getType(), metadata));
} else {
path = pathBuilder.get(new SimplePath(source.getType(), metadata));
}
return path;
}
}
}