18
18
import java .util .LinkedHashMap ;
19
19
import java .util .Map ;
20
20
21
+ import org .springframework .data .repository .query .parser .Part ;
22
+ import org .springframework .data .repository .query .parser .Part .Type ;
21
23
import org .springframework .util .Assert ;
22
24
import org .springframework .util .ClassUtils ;
23
25
24
26
/**
27
+ * Support for query by example (QBE).
28
+ *
25
29
* @author Christoph Strobl
26
30
* @param <T>
27
31
*/
28
32
public class Example <T > {
29
33
30
34
private final T probe ;
31
35
32
- private NullHandling nullHandling = NullHandling . IGNORE_NULL ;
36
+ private NullHandler nullHandler = NullHandler . IGNORE ;
33
37
private StringMatcher defaultStringMatcher = StringMatcher .DEFAULT ;
38
+ private PropertySpecifiers propertySpecifiers = new PropertySpecifiers ();
34
39
35
40
private boolean ignoreCase = false ;
36
41
37
- private Map <String , PropertySpecifier > propertySpecifiers = new LinkedHashMap <String , PropertySpecifier >();
38
-
42
+ /**
43
+ * Create a new {@link Example} including all non-null properties by default.
44
+ *
45
+ * @param probe The example to use. Must not be {@literal null}.
46
+ */
39
47
public <S extends T > Example (S probe ) {
40
48
41
49
Assert .notNull (probe , "Probe must not be null!" );
42
50
this .probe = probe ;
43
51
}
44
52
53
+ /**
54
+ * Get the example used.
55
+ *
56
+ * @return never {@literal null}.
57
+ */
45
58
public T getProbe () {
46
59
return probe ;
47
60
}
48
61
49
- public NullHandling getNullHandling () {
50
- return nullHandling ;
62
+ /**
63
+ * Get defined null handling.
64
+ *
65
+ * @return never {@literal null}
66
+ */
67
+ public NullHandler getNullHandler () {
68
+ return nullHandler ;
51
69
}
52
70
71
+ /**
72
+ * Get defined {@link StringMatcher}.
73
+ *
74
+ * @return never {@literal null}.
75
+ */
53
76
public StringMatcher getDefaultStringMatcher () {
54
77
return defaultStringMatcher ;
55
78
}
56
79
80
+ /**
81
+ * @return {@literal true} if {@link String} should be matched with ignore case option.
82
+ */
57
83
public boolean isIngnoreCaseEnabled () {
58
84
return this .ignoreCase ;
59
85
}
60
86
87
+ /**
88
+ * @param path Dot-Path to property.
89
+ * @return {@literal true} in case {@link PropertySpecifier} defined for given path.
90
+ */
61
91
public boolean hasPropertySpecifier (String path ) {
62
- return propertySpecifiers .containsKey (path );
92
+ return propertySpecifiers .hasSpecifierForPath (path );
63
93
}
64
94
65
- public PropertySpecifier getPropertySpecifier (String propertyPath ) {
66
- return this .propertySpecifiers .get (propertyPath );
95
+ /**
96
+ * @param path Dot-Path to property.
97
+ * @return {@literal null} when no {@link PropertySpecifier} defined for path.
98
+ */
99
+ public PropertySpecifier getPropertySpecifier (String path ) {
100
+ return propertySpecifiers .getForPath (path );
67
101
}
68
102
103
+ /**
104
+ * @return true if at least one {@link PropertySpecifier} defined.
105
+ */
69
106
public boolean hasPropertySpecifiers () {
70
- return ! this .propertySpecifiers .isEmpty ();
107
+ return this .propertySpecifiers .hasValues ();
71
108
}
72
109
110
+ /**
111
+ * Get the actual type for the example used.
112
+ *
113
+ * @return
114
+ */
73
115
@ SuppressWarnings ("unchecked" )
74
116
public Class <? extends T > getProbeType () {
75
117
return (Class <? extends T >) ClassUtils .getUserClass (probe .getClass ());
76
118
}
77
119
120
+ /**
121
+ * Create a new {@link Example} including all non-null properties by default.
122
+ *
123
+ * @param probe must not be {@literal null}.
124
+ * @return
125
+ */
78
126
public static <S extends T , T > Example <T > exampleOf (S probe ) {
79
127
return new Example <T >(probe );
80
128
}
81
129
130
+ /**
131
+ * Create a new {@link Example} including all non-null properties, excluding explicitly named properties to ignore.
132
+ *
133
+ * @param probe must not be {@literal null}.
134
+ * @return
135
+ */
82
136
public static <S extends T , T > Example <T > exampleOf (S probe , String ... ignoredProperties ) {
83
137
return new Builder <T >(probe ).ignore (ignoredProperties ).get ();
84
138
}
85
139
140
+ /**
141
+ * Create new {@link Builder} for specifying {@link Example}.
142
+ *
143
+ * @param probe must not be {@literal null}.
144
+ * @return
145
+ * @see Builder
146
+ */
86
147
public static <S extends T , T > Builder <S > newExampleOf (S probe ) {
87
148
return new Builder <S >(probe );
88
149
}
89
150
151
+ /**
152
+ * Builder for specifying desired behavior of {@link Example}.
153
+ *
154
+ * @author Christoph Strobl
155
+ * @param <T>
156
+ */
90
157
public static class Builder <T > {
91
158
92
159
private Example <T > example ;
@@ -95,50 +162,103 @@ public static class Builder<T> {
95
162
example = new Example <T >(probe );
96
163
}
97
164
98
- public Builder <T > with (NullHandling nullHandling ) {
165
+ /**
166
+ * Sets {@link NullHandler} used for {@link Example}.
167
+ *
168
+ * @param nullHandling
169
+ * @return
170
+ * @see Builder#nullHandling(NullHandler)
171
+ */
172
+ public Builder <T > with (NullHandler nullHandling ) {
99
173
return nullHandling (nullHandling );
100
174
}
101
175
176
+ /**
177
+ * Sets default {@link StringMatcher} used for {@link Example}.
178
+ *
179
+ * @param stringMatcher
180
+ * @return
181
+ * @see Builder#stringMatcher(StringMatcher)
182
+ */
102
183
public Builder <T > with (StringMatcher stringMatcher ) {
103
184
return stringMatcher (stringMatcher );
104
185
}
105
186
106
- public Builder <T > with (PropertySpecifier specifier ) {
107
- return specify (specifier );
187
+ /**
188
+ * Adds {@link PropertySpecifier} used for {@link Example}.
189
+ *
190
+ * @param specifier
191
+ * @return
192
+ * @see Builder#specify(PropertySpecifier...)
193
+ */
194
+ public Builder <T > with (PropertySpecifier ... specifiers ) {
195
+ return specify (specifiers );
108
196
}
109
197
110
- public Builder <T > nullHandling (NullHandling nullHandling ) {
198
+ /**
199
+ * Sets {@link NullHandler} used for {@link Example}.
200
+ *
201
+ * @param nullHandling Defaulted to {@link NullHandler#INCLUDE} in case of {@literal null}.
202
+ * @return
203
+ */
204
+ public Builder <T > nullHandling (NullHandler nullHandling ) {
111
205
112
- example .nullHandling = nullHandling == null ? NullHandling . IGNORE_NULL : nullHandling ;
206
+ example .nullHandler = nullHandling == null ? NullHandler . IGNORE : nullHandling ;
113
207
return this ;
114
208
}
115
209
210
+ /**
211
+ * Sets the default {@link StringMatcher} used for {@link Example}.
212
+ *
213
+ * @param stringMatcher Defaulted to {@link StringMatcher#DEFAULT} in case of {@literal null}.
214
+ * @return
215
+ */
116
216
public Builder <T > stringMatcher (StringMatcher stringMatcher ) {
117
-
118
- example .defaultStringMatcher = stringMatcher == null ? StringMatcher .DEFAULT : stringMatcher ;
119
- return this ;
217
+ return stringMatcher (stringMatcher , example .ignoreCase );
120
218
}
121
219
220
+ /**
221
+ * Sets the default {@link StringMatcher} used for {@link Example}.
222
+ *
223
+ * @param stringMatcher Defaulted to {@link StringMatcher#DEFAULT} in case of {@literal null}.
224
+ * @param ignoreCase
225
+ * @return
226
+ */
122
227
public Builder <T > stringMatcher (StringMatcher stringMatching , boolean ignoreCase ) {
123
228
124
229
example .defaultStringMatcher = stringMatching == null ? StringMatcher .DEFAULT : stringMatching ;
125
230
example .ignoreCase = ignoreCase ;
126
231
return this ;
127
232
}
128
233
234
+ /**
235
+ * @return
236
+ */
129
237
public Builder <T > ignoreCase () {
130
238
example .ignoreCase = true ;
131
239
return this ;
132
240
}
133
241
242
+ /**
243
+ * Define specific property handling.
244
+ *
245
+ * @param specifiers
246
+ * @return
247
+ */
134
248
public Builder <T > specify (PropertySpecifier ... specifiers ) {
135
249
136
250
for (PropertySpecifier specifier : specifiers ) {
137
- example .propertySpecifiers .put ( specifier . getPath (), specifier );
251
+ example .propertySpecifiers .add ( specifier );
138
252
}
139
253
return this ;
140
254
}
141
255
256
+ /**
257
+ * Ignore given properties.
258
+ *
259
+ * @param ignoredProperties
260
+ * @return
261
+ */
142
262
public Builder <T > ignore (String ... ignoredProperties ) {
143
263
144
264
for (String ignoredProperty : ignoredProperties ) {
@@ -148,29 +268,26 @@ public Builder<T> ignore(String... ignoredProperties) {
148
268
return this ;
149
269
}
150
270
271
+ /**
272
+ * @return {@link Example} as defined.
273
+ */
151
274
public Example <T > get () {
152
275
return this .example ;
153
276
}
154
277
}
155
278
156
279
/**
157
- * Match modes indicates inclusion of complex objects .
280
+ * Null handling for creating criterion out of an {@link Example} .
158
281
*
159
282
* @author Christoph Strobl
160
283
*/
161
- public static enum NullHandling {
162
- /**
163
- * Strict matching will use partially filled objects as reference.
164
- */
165
- INCLUDE_NULL ,
166
- /**
167
- * Lenient matching will inspected nested objects and extract path if needed.
168
- */
169
- IGNORE_NULL
284
+ public static enum NullHandler {
285
+
286
+ INCLUDE , IGNORE
170
287
}
171
288
172
289
/**
173
- * Match modes indicates treatment of {@link String} values.
290
+ * Match modes for treatment of {@link String} values.
174
291
*
175
292
* @author Christoph Strobl
176
293
*/
@@ -179,35 +296,74 @@ public static enum StringMatcher {
179
296
/**
180
297
* Store specific default.
181
298
*/
182
- DEFAULT ,
299
+ DEFAULT ( null ) ,
183
300
/**
184
301
* Matches the exact string
185
302
*/
186
- EXACT ,
303
+ EXACT ( Type . SIMPLE_PROPERTY ) ,
187
304
/**
188
305
* Matches string starting with pattern
189
306
*/
190
- STARTING ,
307
+ STARTING ( Type . STARTING_WITH ) ,
191
308
/**
192
309
* Matches string ending with pattern
193
310
*/
194
- ENDING ,
311
+ ENDING ( Type . ENDING_WITH ) ,
195
312
/**
196
313
* Matches string containing pattern
197
314
*/
198
- CONTAINING ,
315
+ CONTAINING ( Type . CONTAINING ) ,
199
316
/**
200
317
* Treats strings as regular expression patterns
201
318
*/
202
- REGEX
319
+ REGEX (Type .REGEX );
320
+
321
+ private Type type ;
322
+
323
+ private StringMatcher (Type type ) {
324
+ this .type = type ;
325
+ }
326
+
327
+ /**
328
+ * Get the according {@link Part.Type}.
329
+ *
330
+ * @return {@literal null} for {@link StringMatcher#DEFAULT}.
331
+ */
332
+ public Type getPartType () {
333
+ return type ;
334
+ }
335
+
203
336
}
204
337
205
338
public static class ExcludingValueTransformer implements PropertyValueTransformer {
206
339
207
340
@ Override
208
- public Object tranform (Object source ) {
341
+ public Object convert (Object source ) {
209
342
return null ;
210
343
}
211
344
}
212
345
346
+ static class PropertySpecifiers {
347
+
348
+ private Map <String , PropertySpecifier > propertySpecifiers = new LinkedHashMap <String , PropertySpecifier >();
349
+
350
+ public void add (PropertySpecifier specifier ) {
351
+
352
+ Assert .notNull (specifier , "PropertySpecifier must not be null!" );
353
+ propertySpecifiers .put (specifier .getPath (), specifier );
354
+ }
355
+
356
+ public boolean hasSpecifierForPath (String path ) {
357
+ return propertySpecifiers .containsKey (path );
358
+ }
359
+
360
+ public PropertySpecifier getForPath (String path ) {
361
+ return propertySpecifiers .get (path );
362
+ }
363
+
364
+ public boolean hasValues () {
365
+ return !propertySpecifiers .isEmpty ();
366
+ }
367
+ }
368
+
213
369
}
0 commit comments