| 
15 | 15 |  */  | 
16 | 16 | package org.springframework.data.querydsl.binding;  | 
17 | 17 | 
 
  | 
18 |  | -import java.beans.PropertyDescriptor;  | 
19 |  | -import java.util.ArrayList;  | 
20 |  | -import java.util.Collection;  | 
21 |  | -import java.util.Collections;  | 
22 |  | -import java.util.List;  | 
23 |  | -import java.util.Map;  | 
24 |  | -import java.util.Optional;  | 
25 |  | -import java.util.concurrent.ConcurrentHashMap;  | 
26 |  | - | 
 | 18 | +import com.querydsl.core.BooleanBuilder;  | 
 | 19 | +import com.querydsl.core.types.Path;  | 
 | 20 | +import com.querydsl.core.types.Predicate;  | 
27 | 21 | import org.springframework.beans.PropertyValues;  | 
28 | 22 | import org.springframework.core.convert.ConversionService;  | 
29 |  | -import org.springframework.core.convert.Property;  | 
30 |  | -import org.springframework.core.convert.TypeDescriptor;  | 
31 |  | -import org.springframework.data.mapping.PropertyPath;  | 
32 | 23 | import org.springframework.data.querydsl.EntityPathResolver;  | 
33 | 24 | import org.springframework.data.util.TypeInformation;  | 
34 |  | -import org.springframework.lang.Nullable;  | 
35 | 25 | import org.springframework.util.Assert;  | 
36 |  | -import org.springframework.util.ClassUtils;  | 
37 | 26 | import org.springframework.util.MultiValueMap;  | 
38 |  | -import org.springframework.util.ObjectUtils;  | 
39 | 27 | 
 
  | 
40 |  | -import com.querydsl.core.BooleanBuilder;  | 
41 |  | -import com.querydsl.core.types.Path;  | 
42 |  | -import com.querydsl.core.types.Predicate;  | 
 | 28 | +import java.util.Collection;  | 
 | 29 | +import java.util.Map;  | 
 | 30 | +import java.util.Optional;  | 
 | 31 | +import java.util.concurrent.ConcurrentHashMap;  | 
43 | 32 | 
 
  | 
44 | 33 | /**  | 
45 | 34 |  * Builder assembling {@link Predicate} out of {@link PropertyValues}.  | 
 | 
50 | 39 |  * @author Johannes Englmeier  | 
51 | 40 |  * @since 1.11  | 
52 | 41 |  */  | 
53 |  | -public class QuerydslPredicateBuilder {  | 
 | 42 | +public class QuerydslPredicateBuilder implements QuerydslPredicateBuilderCustomizer {  | 
54 | 43 | 
 
  | 
55 |  | -	private final ConversionService conversionService;  | 
56 |  | -	private final MultiValueBinding<Path<? extends Object>, Object> defaultBinding;  | 
57 |  | -	private final Map<PathInformation, Path<?>> paths;  | 
58 |  | -	private final EntityPathResolver resolver;  | 
 | 44 | +	protected final ConversionService conversionService;  | 
 | 45 | +	protected final MultiValueBinding<Path<? extends Object>, Object> defaultBinding;  | 
 | 46 | +	protected final Map<PathInformation, Path<?>> paths;  | 
 | 47 | +	protected final EntityPathResolver resolver;  | 
59 | 48 | 
 
  | 
60 | 49 | 	/**  | 
61 | 50 | 	 * Creates a new {@link QuerydslPredicateBuilder} for the given {@link ConversionService} and  | 
@@ -111,145 +100,12 @@ public Predicate getPredicate(TypeInformation<?> type, MultiValueMap<String, ?>  | 
111 | 100 | 				continue;  | 
112 | 101 | 			}  | 
113 | 102 | 
 
  | 
114 |  | -			Collection<Object> value = convertToPropertyPathSpecificType(entry.getValue(), propertyPath);  | 
115 |  | -			Optional<Predicate> predicate = invokeBinding(propertyPath, bindings, value);  | 
 | 103 | +			Collection<Object> value = convertToPropertyPathSpecificType(entry.getValue(), propertyPath, conversionService);  | 
 | 104 | +			Optional<Predicate> predicate = invokeBinding(propertyPath, bindings, value, resolver, defaultBinding);  | 
116 | 105 | 
 
  | 
117 | 106 | 			predicate.ifPresent(builder::and);  | 
118 | 107 | 		}  | 
119 | 108 | 
 
  | 
120 | 109 | 		return getPredicate(builder);  | 
121 | 110 | 	}  | 
122 |  | - | 
123 |  | -	/**  | 
124 |  | -	 * Returns whether the given {@link Predicate} represents an empty predicate instance.  | 
125 |  | -	 *  | 
126 |  | -	 * @param predicate  | 
127 |  | -	 * @return  | 
128 |  | -	 * @since 2.5.3  | 
129 |  | -	 * @see BooleanBuilder  | 
130 |  | -	 */  | 
131 |  | -	public static boolean isEmpty(Predicate predicate) {  | 
132 |  | -		return new BooleanBuilder().equals(predicate);  | 
133 |  | -	}  | 
134 |  | - | 
135 |  | -	/**  | 
136 |  | -	 * Invokes the binding of the given values, for the given {@link PropertyPath} and {@link QuerydslBindings}.  | 
137 |  | -	 *  | 
138 |  | -	 * @param dotPath must not be {@literal null}.  | 
139 |  | -	 * @param bindings must not be {@literal null}.  | 
140 |  | -	 * @param values must not be {@literal null}.  | 
141 |  | -	 * @return  | 
142 |  | -	 */  | 
143 |  | -	protected Optional<Predicate> invokeBinding(PathInformation dotPath, QuerydslBindings bindings,  | 
144 |  | -												Collection<Object> values) {  | 
145 |  | - | 
146 |  | -		Path<?> path = getPath(dotPath, bindings);  | 
147 |  | - | 
148 |  | -		return bindings.getBindingForPath(dotPath).orElse(defaultBinding).bind(path, values);  | 
149 |  | -	}  | 
150 |  | - | 
151 |  | -	/**  | 
152 |  | -	 * Returns the {@link Path} for the given {@link PropertyPath} and {@link QuerydslBindings}. Will try to obtain the  | 
153 |  | -	 * {@link Path} from the bindings first but fall back to reifying it from the PropertyPath in case no specific binding  | 
154 |  | -	 * has been configured.  | 
155 |  | -	 *  | 
156 |  | -	 * @param path must not be {@literal null}.  | 
157 |  | -	 * @param bindings must not be {@literal null}.  | 
158 |  | -	 * @return  | 
159 |  | -	 */  | 
160 |  | -	private Path<?> getPath(PathInformation path, QuerydslBindings bindings) {  | 
161 |  | - | 
162 |  | -		Optional<Path<?>> resolvedPath = bindings.getExistingPath(path);  | 
163 |  | - | 
164 |  | -		return resolvedPath.orElseGet(() -> paths.computeIfAbsent(path, it -> it.reifyPath(resolver)));  | 
165 |  | -	}  | 
166 |  | - | 
167 |  | -	/**  | 
168 |  | -	 * Converts the given source values into a collection of elements that are of the given {@link PropertyPath}'s type.  | 
169 |  | -	 * Considers a single element list with an empty object an empty collection because this basically indicates the  | 
170 |  | -	 * property having been submitted but no value provided.  | 
171 |  | -	 *  | 
172 |  | -	 * @param source must not be {@literal null}.  | 
173 |  | -	 * @param path must not be {@literal null}.  | 
174 |  | -	 * @return  | 
175 |  | -	 */  | 
176 |  | -	protected Collection<Object> convertToPropertyPathSpecificType(List<?> source, PathInformation path) {  | 
177 |  | - | 
178 |  | -		if (source.isEmpty() || isSingleElementCollectionWithEmptyItem(source)) {  | 
179 |  | -			return Collections.emptyList();  | 
180 |  | -		}  | 
181 |  | - | 
182 |  | -		TypeDescriptor targetType = getTargetTypeDescriptor(path);  | 
183 |  | -		Collection<Object> target = new ArrayList<>(source.size());  | 
184 |  | - | 
185 |  | -		for (Object value : source) {  | 
186 |  | -			target.add(getValue(targetType, value));  | 
187 |  | -		}  | 
188 |  | - | 
189 |  | -		return target;  | 
190 |  | -	}  | 
191 |  | - | 
192 |  | -	@Nullable  | 
193 |  | -	private Object getValue(TypeDescriptor targetType, Object value) {  | 
194 |  | - | 
195 |  | -		if (ClassUtils.isAssignableValue(targetType.getType(), value)) {  | 
196 |  | -			return value;  | 
197 |  | -		}  | 
198 |  | - | 
199 |  | -		if (conversionService.canConvert(value.getClass(), targetType.getType())) {  | 
200 |  | -			return conversionService.convert(value, TypeDescriptor.forObject(value), targetType);  | 
201 |  | -		}  | 
202 |  | - | 
203 |  | -		return value;  | 
204 |  | -	}  | 
205 |  | - | 
206 |  | -	/**  | 
207 |  | -	 * Returns the target {@link TypeDescriptor} for the given {@link PathInformation} by either inspecting the field or  | 
208 |  | -	 * property (the latter preferred) to pick up annotations potentially defined for formatting purposes.  | 
209 |  | -	 *  | 
210 |  | -	 * @param path must not be {@literal null}.  | 
211 |  | -	 * @return  | 
212 |  | -	 */  | 
213 |  | -	private static TypeDescriptor getTargetTypeDescriptor(PathInformation path) {  | 
214 |  | - | 
215 |  | -		PropertyDescriptor descriptor = path.getLeafPropertyDescriptor();  | 
216 |  | - | 
217 |  | -		Class<?> owningType = path.getLeafParentType();  | 
218 |  | -		String leafProperty = path.getLeafProperty();  | 
219 |  | - | 
220 |  | -		TypeDescriptor result = descriptor == null //  | 
221 |  | -				? TypeDescriptor  | 
222 |  | -						.nested(org.springframework.data.util.ReflectionUtils.findRequiredField(owningType, leafProperty), 0)  | 
223 |  | -				: TypeDescriptor  | 
224 |  | -						.nested(new Property(owningType, descriptor.getReadMethod(), descriptor.getWriteMethod(), leafProperty), 0);  | 
225 |  | - | 
226 |  | -		if (result == null) {  | 
227 |  | -			throw new IllegalStateException(String.format("Could not obtain TypeDescriptor for PathInformation %s", path));  | 
228 |  | -		}  | 
229 |  | - | 
230 |  | -		return result;  | 
231 |  | -	}  | 
232 |  | - | 
233 |  | -	/**  | 
234 |  | -	 * Returns whether the given collection has exactly one element that is empty (i.e. doesn't contain text). This is  | 
235 |  | -	 * basically an indicator that a request parameter has been submitted but no value for it.  | 
236 |  | -	 *  | 
237 |  | -	 * @param source must not be {@literal null}.  | 
238 |  | -	 * @return  | 
239 |  | -	 */  | 
240 |  | -	protected static boolean isSingleElementCollectionWithEmptyItem(List<?> source) {  | 
241 |  | -		return source.size() == 1 && ObjectUtils.isEmpty(source.get(0));  | 
242 |  | -	}  | 
243 |  | - | 
244 |  | -	/**  | 
245 |  | -	 * Returns the {@link Predicate} from {@link BooleanBuilder}.  | 
246 |  | -	 *  | 
247 |  | -	 * @param builder  | 
248 |  | -	 * @return  | 
249 |  | -	 */  | 
250 |  | -	protected static Predicate getPredicate(BooleanBuilder builder) {  | 
251 |  | - | 
252 |  | -		Predicate predicate = builder.getValue();  | 
253 |  | -		return predicate == null ? new BooleanBuilder() : predicate;  | 
254 |  | -	}  | 
255 | 111 | }  | 
0 commit comments