-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBasicChecks.java
More file actions
528 lines (465 loc) · 21.2 KB
/
Copy pathBasicChecks.java
File metadata and controls
528 lines (465 loc) · 21.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
/*
* BSD 2-Clause License
*
* Copyright (c) 2021, Ondrej Fischer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package fluent.validation;
import fluent.validation.processor.Factory;
import fluent.validation.result.ResultFactory;
import java.util.*;
import static fluent.validation.Transformation.dontTransformNull;
import static java.util.Arrays.asList;
/**
* Factory of ready to use basic checks.
* These include:
* 1. General object checks
* 2. Logical operators (horizontal composition)
* 3. Transformation checks (vertical composition)
* 4. Exception checks
* 5. Value extractor
*/
@Factory
public final class BasicChecks {
private BasicChecks() {}
private static final Check<Object> IS_NULL = sameInstance(null);
private static final Check<Object> NOT_NULL = not(IS_NULL);
private static final Check<Object> ANYTHING = new Anything<>();
/* ------------------------------------------------------------------------------------------------------
* Simple check builders using predicate and description.
* ------------------------------------------------------------------------------------------------------ */
/**
* Define a transparent check using provided predicate and string expectation description.
*
* @param predicate Predicate used to test the supplied data.
* @param expectationDescription Function, that provides description of the expectation.
* @param <D> Type of the data to be tested by the created expectation.
* @return New expectation.
*/
public static <D> Check<D> nullableCheck(Predicate<D> predicate, String expectationDescription) {
return new PredicateCheck<>(expectationDescription, predicate);
}
/**
* Require, that tested data meet provided requirement before application of a check.
* If the requirement is not met, composed check returns false, and the "actual" check is not evaluated.
* If the requirement is met, then "actual" check is evaluated, and the result is returned.
*
* Error description:
* If requirement is not met, then error contains description of it's mismatch.
* If requirement is met, then error contains only mismatch of the "actual" check, while requirement stays silent.
*
* The behavior is like Java's and operator, which doesn't evaluate second operand if first one is false.
* The and() operator check as opposed to this one, evaluates always both operands to get mismatch details from
* both of them. Keep that in mind and properly decide, in which situation to use which operator.
*
* @param requirement Check, that must return true in order to evaluate the "actual" one.
* @param check "Actual" check to be evaluated on tested data, if requirement is met.
* @param <D> Type of the data to be tested by the created check.
* @return Composed check.
*/
public static <D> Check<D> require(Check<? super D> requirement, Check<? super D> check) {
return new DoubleCheck<>(requirement, check);
}
/**
* Require, that tested value is not null, before application of the provided check on it.
* If the tested value is null, then it returns false without evaluating the check.
* If the tested value is not null, then the provided check is applied, and result is returned.
*
* @param check Check to apply on the value, if it's not null.
* @param <D> Type of the data to be tested by the created check.
* @return Composed check.
*/
public static <D> Check<D> requireNotNull(Check<D> check) {
return require(isNotNull(), check);
}
/**
* Build a custom check using it's description and pure Java predicate. As the Predicate doesn't
* provide any transparency, it's enriched with the description.
*
* @param predicate Implementation of the check logic.
* @param expectationDescription Description of the expectation represented by this check.
* @param <D> Type of the data to be checked.
* @return Custom check.
*/
public static <D> Check<D> check(Predicate<D> predicate, String expectationDescription) {
return requireNotNull(nullableCheck(predicate, expectationDescription));
}
/* ------------------------------------------------------------------------------------------------------
* General object conditions.
* ------------------------------------------------------------------------------------------------------
*/
/**
* Create a check, that checks if the tested value is exactly the same instance as expected.
* This check doesn't use `equals()` method, but reference comparison operator ==.
*
* @param expectedInstance Expected instance.
* @param <D> Type of the tested data.
* @return New check.
*/
public static <D> Check<D> sameInstance(D expectedInstance) {
return nullableCheck(data -> data == expectedInstance, "<" + expectedInstance + ">");
}
/**
* Create a check, that checks if the tested value is equal to expected value.
* It will return true as long as the equals() method does.
*
* @param expectedValue Expected value.
* @param <D> Type of the tested data.
* @return New check.
*/
public static <D> Check<D> equalTo(D expectedValue) {
return expectedValue == null ? sameInstance(null) : nullableCheck(expectedValue::equals, "<" + expectedValue + ">");
}
/**
* Alias for `equalTo`, which may be more readable in some constructions.
*
* @param expectedValue Expected value.
* @param <D> Type of the tested data.
* @return New check.
* @see #equalTo(Object)
*/
public static <D> Check<D> is(D expectedValue) {
return equalTo(expectedValue);
}
/**
* Check, if tested data is null.
* @return New check.
*/
public static Check<Object> isNull() {
return IS_NULL;
}
/**
* Check, if tested data is not null.
* @return New check.
*/
public static Check<Object> isNotNull() {
return NOT_NULL;
}
/**
* Check, that doesn't care of the tested value, and always returns true.
* @return New check.
*/
public static Check<Object> anything() {
return ANYTHING;
}
private static Check<Object> instanceOf(String prefix, Class<?> expectedClass) {
return nullableCheck(expectedClass::isInstance, prefix + " " + expectedClass);
}
/**
* Check, that tested data is instance of provided class (either the class itself or any subclass).
*
* @param expectedClass Expected class.
* @return New check.
*/
public static Check<Object> instanceOf(Class<?> expectedClass) {
return instanceOf("instance of", expectedClass);
}
/**
* Alias of `instanceOf` which may be more readable in some context.
*
* @param expectedClass Expected class.
* @return New check.
*/
public static Check<Object> isA(Class<?> expectedClass) {
return instanceOf("is a", expectedClass);
}
/**
* Alias of `instanceOf` which may be more readable in some context.
*
* @param expectedClass Expected class.
* @return New check.
*/
public static Check<Object> isAn(Class<?> expectedClass) {
return instanceOf("is an", expectedClass);
}
/**
* Alias of `instanceOf` which may be more readable in some context.
*
* @param expectedClass Expected class.
* @return New check.
*/
public static Check<Object> a(Class<?> expectedClass) {
return isA(expectedClass);
}
/**
* Alias of `instanceOf` which may be more readable in some context.
*
* @param expectedClass Expected class.
* @return New check.
*/
public static Check<Object> an(Class<?> expectedClass) {
return isAn(expectedClass);
}
/**
* Check, that tested data is exactly of the provided type, but not any subcalss of it.
* @param expectedClass Expected class.
* @return New check.
*/
public static Check<Object> sameClass(Class<?> expectedClass) {
return has("class", Object::getClass).matching(equalTo(expectedClass));
}
/* ------------------------------------------------------------------------------------------------------
* Logical operators for composition of conditions.
* ------------------------------------------------------------------------------------------------------ */
/**
* Check, that provided positive check doesn't match checked data.
*
* @param positiveCheck Positive check to evaluate, what shouldn't match.
* @param <D> Type of the data to be tested by the created check.
* @return Negated expectation.
*/
public static <D> Check<D> not(Check<D> positiveCheck) {
return new NegativeCheck<>(positiveCheck);
}
/**
* Check, that provided positive value isn't equal to checked data.
*
* @param positiveValue Positive value, which shouldn't be equal to checked data.
* @param <D> Type of the data to be tested by the created check.
* @return Negated expectation.
*/
public static <D> Check<D> not(D positiveValue) {
return not(equalTo(positiveValue));
}
/**
* Create matcher, that matches, if tested object meets one of provided alternatives (or).
*
* @param alternatives Expected alternatives.
* @param <D> Type of the data to test using this expectation.
* @return Expectation with alternatives.
*/
public static <D> Check<D> oneOf(Collection<D> alternatives) {
return nullableCheck(alternatives::contains, "One of " + alternatives);
}
/**
* Create matcher, that matches, if tested object meets one of provided alternatives (or).
*
* @param alternatives Expected alternatives.
* @param <D> Type of the data to test using this expectation.
* @return Expectation with alternatives.
*/
@SafeVarargs
public static <D> Check<D> oneOf(D... alternatives) {
return oneOf(new HashSet<>(asList(alternatives)));
}
@SuppressWarnings("unchecked")
private static <D> Check<D> multipleOperands(Iterable<Check<? super D>> operands, boolean andOperator) {
Iterator<Check<? super D>> iterator = operands.iterator();
if(!iterator.hasNext()) {
return BasicChecks.nullableCheck(data -> andOperator, "empty " + (andOperator ? "allOf" : "anyOf") + " formula");
}
Check<D> next = (Check<D>) iterator.next();
while(iterator.hasNext()) {
// Here operator precedence is explicit.
next = andOperator ? new And<>(next, iterator.next()) : new Or<>(next, iterator.next());
}
return next;
}
/**
* General OR operator of multiple checks.
* The created check returns true if any of the provided operands (checks) returns true on the tested data,
* and only if none of the operands returns true, then the resulting check returns false.
*
* Corner case:
* If no operands are provided, then it returns false (as none of them evaluated to true).
*
* @param operands Individual checks to evaluate and apply logical or on their results.
* @param <D> Type of the tested data.
* @return New check with the described logic.
*/
public static <D> Check<D> anyOf(Iterable<Check<? super D>> operands) {
return multipleOperands(operands, false);
}
/**
* General OR operator of multiple checks.
* The created check returns true if any of the provided operands (checks) returns true on the tested data,
* and only if none of the operands returns true, then the resulting check returns false.
*
* Corner case:
* If no operands are provided, then it returns false (as none of them evaluated to true).
*
* @param operands Individual checks to evaluate and apply logical or on their results.
* @param <D> Type of the tested data.
* @return New check with the described logic.
*/
@SafeVarargs
public static <D> Check<D> anyOf(Check<? super D>... operands) {
return anyOf((Iterable<Check<? super D>>)asList(operands));
}
/**
* General AND operator of multiple checks.
* The created check returns true if all the provided operands (checks) return true on the tested data,
* and if any of the operands returns false, then the resulting check returns false.
*
* Corner case:
* If no operands are provided, then it returns true (as none of them evaluated to false).
*
* @param operands Individual checks to evaluate and apply logical and on their results.
* @param <D> Type of the tested data.
* @return New check with the described logic.
*/
public static <D> Check<D> allOf(Iterable<Check<? super D>> operands) {
return multipleOperands(operands, true);
}
/**
* General AND operator of multiple checks.
* The created check returns true if all the provided operands (checks) return true on the tested data,
* and if any of the operands returns false, then the resulting check returns false.
*
* Corner case:
* If no operands are provided, then it returns true (as none of them evaluated to false).
*
* @param operands Individual checks to evaluate and apply logical and on their results.
* @param <D> Type of the tested data.
* @return New check with the described logic.
*/
@SafeVarargs
public static <D> Check<D> allOf(Check<? super D>... operands) {
return allOf((Iterable<Check<? super D>>)asList(operands));
}
/* ------------------------------------------------------------------------------------------------------
* Composition of conditions using a transformation and check for the result.
* ------------------------------------------------------------------------------------------------------ */
/**
* Lowest level transformation.
* Create a new check using transformation of the original object, e.g. by accessing it's field, and partial check
* applied on the result of the transformation.
*
* @param transformation Function transforming the original object.
* @param check Check to apply on the result of the transformation.
* @param <D> Type of the original tested object.
* @param <V> Type of the transformation result.
* @return Composed check applicable on the original (whole) object.
*/
public static <D, V> Check<D> transform(Transformation<? super D, V> transformation, Check<? super V> check) {
return new TransformedCheck<>(transformation, check);
}
/**
* Convenient composition using transformation of the original object, and check on the result.
*
* @param name Name (description) of the transformation.
* @param transformation Function transforming the original object.
* @param check Check to apply on the result of the transformation.
* @param <D> Type of the original tested object.
* @param <V> Type of the transformation result.
* @return Composed check applicable on the original (whole) object.
*/
public static <D, V> Check<D> compose(String name, Transformation<? super D, V> transformation, Check<? super V> check) {
return name == null ? transform(transformation, check) : new NamedCheck<>(name, transform(transformation, check));
}
/**
* Simplified composition using transformation of the original object, and check on the result.
*
* @param transformation Function transforming the original object.
* @param check Check to apply on the result of the transformation.
* @param <D> Type of the original tested object.
* @param <V> Type of the transformation result.
* @return Composed check applicable on the original (whole) object.
*/
public static <D, V> Check<D> compose(Transformation<? super D, V> transformation, Check<? super V> check) {
return new NamedCheck<>(transformation.getMethodName(), transform(transformation, check));
}
public static <D, V> TransformationBuilder<V, CheckBuilder<D>> has(String name, Transformation<? super D, V> transformation) {
return condition -> new CheckBuilder.Impl<>(requireNotNull(compose(name, transformation, condition)));
}
public static <D, V> TransformationBuilder<V, CheckBuilder<D>> has(Transformation<? super D, V> transformation) {
return has(transformation.getMethodName(), transformation);
}
public static <D, V> TransformationBuilder<V, CheckBuilder<D>> nullableHas(String name, Transformation<? super D, V> transformation) {
return condition -> new CheckBuilder.Impl<>(compose(name, dontTransformNull(transformation), condition));
}
public static <D, V> TransformationBuilder<V, CheckBuilder<D>> nullableHas(Transformation<? super D, V> transformation) {
return nullableHas(transformation.getMethodName(), transformation);
}
/**
* Fluent builder of a check, that will assure, that provided object is instance of required class, and if yes,
* it will cast it to that class, and apply provided check on the required type.
*
* @param type Required Java class / type
* @param <V> Type to check, and cast to.
* @return Builder of a object check, that accepts check on the required type.
*/
public static <V> TransformationBuilder<V, Check<V>> as(Class<V> type) {
return condition -> require(instanceOf(type), compose("as " + type.getSimpleName(), type::cast, condition));
}
/* ------------------------------------------------------------------------------------------------------
* Checks for exceptions.
* ------------------------------------------------------------------------------------------------------ */
/**
* Check of exception (throwable) message.
*
* @param check String check of the message content.
* @return Check on Throwable.
*/
public static Check<Throwable> message(Check<? super String> check) {
return compose("message", Throwable::getMessage, check);
}
/**
* Check of a code, which should throw an exception (throwable).
* The created check will actually run the code passed to test, and return true if the code throws exception, which
* matches specified criteria.
*
* @param check Check on the throwable.
* @return ThrowingCheck applicable on provided code, allowing to build further criteria.
*/
public static ThrowingCheck throwing(Check<? super Throwable> check) {
return new ThrowingCheck(check);
}
/**
* Check of a code, which should throw an exception (throwable) of expected type.
* The created check will actually run the code passed to test, and return true if the code throws exception of the
* required type.
*
* @param expectedType Expected type of the thrown exception.
* @return ThrowingCheck applicable on provided code, allowing to build further criteria.
*/
public static ThrowingCheck throwing(Class<? extends Throwable> expectedType) {
return throwing(a(expectedType));
}
/* ------------------------------------------------------------------------------------------------------
* Builders and other helpers.
* ------------------------------------------------------------------------------------------------------ */
public static <D> Check<D> createBuilder() {
return new Anything<>();
}
public static <D> Check<D> createBuilderWith(Check<D> check) {
return check;
}
public static <D> Check<D> which(Check<D> check) {
return createBuilderWith(check);
}
public static <D> CheckBuilder<D> dsl() {
return new CheckBuilder.Impl<>();
}
public static <D> Value<D> value() {
return new Value<>();
}
public static <D> Check<D> softCheck(Check<D> check) {
return new SoftCheck<>(check);
}
public static <D> Check<D> customResultFactory(Check<D> check, ResultFactory customResultFactory) {
return new CustomResultFactoryCheck<>(check, customResultFactory);
}
}