|
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