Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
BuilderSupport |
|
| 1.3968253968253967;1.397 | ||||
BuilderSupport$AllOfPredicate |
|
| 1.3968253968253967;1.397 | ||||
BuilderSupport$AlwaysPredicate |
|
| 1.3968253968253967;1.397 | ||||
BuilderSupport$AnyOfPredicate |
|
| 1.3968253968253967;1.397 | ||||
BuilderSupport$EmptyPredicate |
|
| 1.3968253968253967;1.397 | ||||
BuilderSupport$EqualsPredicate |
|
| 1.3968253968253967;1.397 | ||||
BuilderSupport$InPredicate |
|
| 1.3968253968253967;1.397 | ||||
BuilderSupport$MatchesPredicate |
|
| 1.3968253968253967;1.397 | ||||
BuilderSupport$MaxLengthPredicate |
|
| 1.3968253968253967;1.397 | ||||
BuilderSupport$NotPredicate |
|
| 1.3968253968253967;1.397 | ||||
BuilderSupport$WithdrawnPredicate |
|
| 1.3968253968253967;1.397 |
1 | /** | |
2 | The contents of this file are subject to the Mozilla Public License Version 1.1 | |
3 | (the "License"); you may not use this file except in compliance with the License. | |
4 | You may obtain a copy of the License at http://www.mozilla.org/MPL/ | |
5 | Software distributed under the License is distributed on an "AS IS" basis, | |
6 | WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the | |
7 | specific language governing rights and limitations under the License. | |
8 | ||
9 | The Original Code is "BuilderSupport.java". Description: | |
10 | "Abstract base class for Validation Rules" | |
11 | ||
12 | The Initial Developer of the Original Code is University Health Network. Copyright (C) | |
13 | 2012. All Rights Reserved. | |
14 | ||
15 | Contributor(s): ______________________________________. | |
16 | ||
17 | Alternatively, the contents of this file may be used under the terms of the | |
18 | GNU General Public License (the "GPL"), in which case the provisions of the GPL are | |
19 | applicable instead of those above. If you wish to allow use of your version of this | |
20 | file only under the terms of the GPL and not to allow others to use your version | |
21 | of this file under the MPL, indicate your decision by deleting the provisions above | |
22 | and replace them with the notice and other provisions required by the GPL License. | |
23 | If you do not delete the provisions above, a recipient may use your version of | |
24 | this file under either the MPL or the GPL. | |
25 | */ | |
26 | package ca.uhn.hl7v2.validation.builder; | |
27 | ||
28 | import java.io.Serializable; | |
29 | import java.util.Arrays; | |
30 | import java.util.Collection; | |
31 | import java.util.regex.Pattern; | |
32 | ||
33 | import ca.uhn.hl7v2.validation.ValidationException; | |
34 | ||
35 | /** | |
36 | * Abstract base class for Validation Rule building that provides factory methods for | |
37 | * {@link Predicate}s. | |
38 | * | |
39 | * @author Christian Ohr | |
40 | */ | |
41 | @SuppressWarnings("serial") | |
42 | 0 | public abstract class BuilderSupport implements Serializable { |
43 | ||
44 | 104635 | protected BuilderSupport() { |
45 | 104635 | } |
46 | ||
47 | /** | |
48 | * @param expected expected value | |
49 | * @return a predicate that evaluates to <code>true</code> if the expected value equals the | |
50 | * actual value | |
51 | */ | |
52 | public Predicate isEqual(Object expected) { | |
53 | 155 | return new EqualsPredicate(expected); |
54 | } | |
55 | ||
56 | /** | |
57 | * @param expected expected value | |
58 | * @return a predicate that evaluates to <code>true</code> if the expected value | |
59 | * case-insensitively equals the actual value | |
60 | */ | |
61 | public Predicate isEqualIgnoreCase(Object expected) { | |
62 | 30 | return new EqualsPredicate(expected, true); |
63 | } | |
64 | ||
65 | /** | |
66 | * @return a predicate that evaluates to <code>true</code> if the actual value is null, has zero | |
67 | * length or is explicitly "empty" as HL7 defines it (""). | |
68 | */ | |
69 | public Predicate empty() { | |
70 | 37950 | return new EmptyPredicate(); |
71 | } | |
72 | ||
73 | /** | |
74 | * @param predicate the predicate to evaluate if not empty | |
75 | * @return a predicate that evaluates to <code>true</code> if the actual value is empty or the | |
76 | * passed predicate evaluates to true. | |
77 | */ | |
78 | public Predicate emptyOr(Predicate predicate) { | |
79 | 37930 | return anyOf(empty(), predicate); |
80 | } | |
81 | ||
82 | /** | |
83 | * @param regex regular expression | |
84 | * @return a predicate that evaluates to <code>true</code> if the actual value matches the | |
85 | * regular expression | |
86 | */ | |
87 | public Predicate matches(String regex) { | |
88 | 20 | return new MatchesPredicate(regex); |
89 | } | |
90 | ||
91 | /** | |
92 | * @param regex regular expression | |
93 | * @param description custom descriptiom for this regex | |
94 | * @return a predicate that evaluates to <code>true</code> if the actual value matches the | |
95 | * regular expression | |
96 | */ | |
97 | public Predicate matches(String regex, String description) { | |
98 | 38295 | return new MatchesPredicate(regex, description); |
99 | } | |
100 | ||
101 | /** | |
102 | * @param prefix prefix string | |
103 | * @return a predicate that evaluates to <code>true</code> if the actual value starts with the | |
104 | * specified prefix. | |
105 | */ | |
106 | public Predicate startsWith(String prefix) { | |
107 | 40 | return matches("^" + prefix + ".*", "starts with " + prefix); |
108 | } | |
109 | ||
110 | /** | |
111 | * @return a predicate that evaluates to <code>true</code> if the actual value can be parsed | |
112 | * into a non-negative integer. | |
113 | */ | |
114 | public Predicate nonNegativeInteger() { | |
115 | 5445 | return matches("\\d*", "a non-negative integer (0,1,2,...)"); |
116 | } | |
117 | ||
118 | /** | |
119 | * @return a predicate that evaluates to <code>true</code> if the actual value can be parsed | |
120 | * into a number with optional decimal digits. | |
121 | */ | |
122 | public Predicate number() { | |
123 | 5450 | return matches("(\\+|\\-)?\\d*\\.?\\d*", "a number with optional decimal digits"); |
124 | } | |
125 | ||
126 | /** | |
127 | * @return a predicate that evaluates to <code>true</code> if the actual value matches a HL7 | |
128 | * date pattern (YYYY[MM[DD]]) | |
129 | */ | |
130 | public Predicate date() { | |
131 | 5450 | return matches("(\\d{4}([01]\\d(\\d{2})?)?)?", "a date string (YYYY[MM[DD]])"); |
132 | } | |
133 | ||
134 | /** | |
135 | * @return a predicate that evaluates to <code>true</code> if the actual value matches a HL7 | |
136 | * time pattern | |
137 | */ | |
138 | public Predicate time() { | |
139 | 5495 | return matches("([012]\\d([0-5]\\d([0-5]\\d(\\.\\d(\\d(\\d(\\d)?)?)?)?)?)?)?([\\+\\-]\\d{4})?", |
140 | "a HL7 time string"); | |
141 | } | |
142 | ||
143 | /** | |
144 | * @return a predicate that evaluates to <code>true</code> if the actual value matches a HL7 | |
145 | * datetime pattern | |
146 | */ | |
147 | public Predicate dateTime() { | |
148 | 5470 | return matches("(\\d{4}([01]\\d(\\d{2}([012]\\d[0-5]\\d([0-5]\\d(\\.\\d(\\d(\\d(\\d)?)?)?)?)?)?)?)?)?([\\+\\-]\\d{4})?", |
149 | "a HL7 datetime string"); | |
150 | } | |
151 | ||
152 | /** | |
153 | * @return a predicate that evaluates to <code>true</code> if the actual value matches a HL7 | |
154 | * datetime pattern | |
155 | */ | |
156 | public Predicate dateTime25() { | |
157 | 5470 | return matches("(\\d{4}([01]\\d(\\d{2}([012]\\d([0-5]\\d([0-5]\\d(\\.\\d(\\d(\\d(\\d)?)?)?)?)?)?)?)?)?)?([\\+\\-]\\d{4})?", |
158 | "a HL7 datetime string"); | |
159 | } | |
160 | ||
161 | /** | |
162 | * @return a predicate that evaluates to <code>true</code> if the actual value matches a US | |
163 | * phone number pattern | |
164 | */ | |
165 | public Predicate usPhoneNumber() { | |
166 | 5435 | return matches("(\\d{1,2} )?(\\(\\d{3}\\))?\\d{3}-\\d{4}(X\\d{1,5})?(B\\d{1,5})?(C.*)?", |
167 | "a US phone number"); | |
168 | } | |
169 | ||
170 | /** | |
171 | * @return a predicate that evaluates to <code>true</code> if the actual value matches an ISO | |
172 | * OID pattern | |
173 | */ | |
174 | public Predicate oid() { | |
175 | 35 | return matches("[0-2](\\.(0|([1-9][0-9]*)))+", |
176 | "an Object Identifier (OID)"); | |
177 | } | |
178 | ||
179 | /** | |
180 | * @return a predicate that evaluates to <code>true</code> if the actual value matches a UUID | |
181 | * pattern | |
182 | */ | |
183 | public Predicate uuid() { | |
184 | 5 | return matches("\\p{XDigit}{8}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{12}", |
185 | "a Unique Universal Identifier (UUID)"); | |
186 | } | |
187 | ||
188 | /** | |
189 | * @param regex regular expression | |
190 | * @param flags regular expression flags | |
191 | * @return a predicate that evaluates to <code>true</code> if the actual value matches the | |
192 | * regular expression | |
193 | */ | |
194 | public Predicate matches(String regex, int flags) { | |
195 | 0 | return new MatchesPredicate(regex, flags); |
196 | } | |
197 | ||
198 | /** | |
199 | * Equivalent with allOf(isEqual(allowed[0]), ..., isEqual(allowed[n-1]) | |
200 | * | |
201 | * @param allowed allowed values | |
202 | * @return a predicate that evaluates to <code>true</code> if the actual value occurs in he | |
203 | * specified array of objects | |
204 | */ | |
205 | public Predicate in(Object... allowed) { | |
206 | 15 | return new InPredicate(Arrays.asList(allowed)); |
207 | } | |
208 | ||
209 | /** | |
210 | * @param allowed allowed values | |
211 | * @return a predicate that evaluates to <code>true</code> if the actual value occurs in he | |
212 | * specified collection of objects | |
213 | */ | |
214 | public Predicate in(Collection<?> allowed) { | |
215 | 15 | return new InPredicate(allowed); |
216 | } | |
217 | ||
218 | /** | |
219 | * @param predicates predicates of which one shall evaluate to true | |
220 | * @return a predicate that evaluates to <code>true</code> if any of the specified predicates | |
221 | * evaluates to <code>true</code> | |
222 | */ | |
223 | public Predicate anyOf(Iterable<Predicate> predicates) { | |
224 | 37960 | return new AnyOfPredicate(predicates); |
225 | } | |
226 | ||
227 | /** | |
228 | * @param predicates predicates of which all shall evaluate to true | |
229 | * @return a predicate that evaluates to <code>true</code> if all of the specified predicates | |
230 | * evaluate to <code>true</code> | |
231 | */ | |
232 | public Predicate allOf(Iterable<Predicate> predicates) { | |
233 | 30 | return new AllOfPredicate(predicates); |
234 | } | |
235 | ||
236 | /** | |
237 | * @param predicates predicates of which one shall evaluate to true | |
238 | * @return a predicate that evaluates to <code>true</code> if any of the specified predicates | |
239 | * evaluates to <code>true</code> | |
240 | */ | |
241 | public Predicate anyOf(Predicate... predicates) { | |
242 | 37945 | return anyOf(Arrays.asList(predicates)); |
243 | } | |
244 | ||
245 | /** | |
246 | * @param predicates predicates of which all shall evaluate to true | |
247 | * @return a predicate that evaluates to <code>true</code> if all of the specified predicates | |
248 | * evaluate to <code>true</code> | |
249 | */ | |
250 | public Predicate allOf(Predicate... predicates) { | |
251 | 15 | return allOf(Arrays.asList(predicates)); |
252 | } | |
253 | ||
254 | /** | |
255 | * @param predicate predicate to be negated | |
256 | * @return a predicate that evaluates to <code>true</code> if the specified predicate evaluate | |
257 | * to <code>false</code> | |
258 | */ | |
259 | public Predicate not(Predicate predicate) { | |
260 | 15 | return new NotPredicate(predicate); |
261 | } | |
262 | ||
263 | /** | |
264 | * @param maxSize maximal length of the value | |
265 | * @return a predicate that evaluates to <code>true</code> if the length of the actual value is | |
266 | * equal or shorter than the specified length | |
267 | */ | |
268 | public Predicate maxLength(int maxSize) { | |
269 | 10875 | return new MaxLengthPredicate(maxSize); |
270 | } | |
271 | ||
272 | /** | |
273 | * @return a predicate that evaluates to <code>false</code> giving the reason that the message | |
274 | * element has been withdrawn and should not be used anymore. | |
275 | */ | |
276 | public Predicate withdrawn() { | |
277 | 5430 | return new WithdrawnPredicate(); |
278 | } | |
279 | ||
280 | /** | |
281 | * @return a predicate that evaluates to the specified boolean value | |
282 | */ | |
283 | public Predicate always(boolean b) { | |
284 | 11445 | return new AlwaysPredicate(b); |
285 | } | |
286 | ||
287 | /** | |
288 | * @return a predicate that evaluates to <code>false</code> | |
289 | */ | |
290 | public Predicate alwaysFails() { | |
291 | 10 | return always(false); |
292 | } | |
293 | ||
294 | static private String join(Iterable<?> list, String conjunction) { | |
295 | 0 | StringBuilder sb = new StringBuilder(); |
296 | 0 | boolean first = true; |
297 | 0 | for (Object item : list) { |
298 | 0 | if (first) |
299 | 0 | first = false; |
300 | else | |
301 | 0 | sb.append(conjunction); |
302 | 0 | sb.append(item); |
303 | 0 | } |
304 | 0 | return sb.toString(); |
305 | } | |
306 | ||
307 | private static class AlwaysPredicate implements Predicate { | |
308 | ||
309 | private boolean b; | |
310 | ||
311 | 11445 | AlwaysPredicate(boolean b) { |
312 | 11445 | this.b = b; |
313 | 11445 | } |
314 | ||
315 | public boolean evaluate(Object data) throws ValidationException { | |
316 | 55588 | return b; |
317 | } | |
318 | ||
319 | public String getDescription() { | |
320 | 11415 | return b ? "anything" : "nothing"; |
321 | } | |
322 | ||
323 | } | |
324 | ||
325 | private static class MaxLengthPredicate implements Predicate { | |
326 | ||
327 | 16305 | private int maxLength = Integer.MAX_VALUE; |
328 | ||
329 | 16305 | public MaxLengthPredicate(int maxSize) { |
330 | 16305 | this.maxLength = maxSize; |
331 | 16305 | } |
332 | ||
333 | public boolean evaluate(Object data) throws ValidationException { | |
334 | 48416 | return (data == null || data.toString().length() <= maxLength); |
335 | } | |
336 | ||
337 | public String getDescription() { | |
338 | 10855 | return "shorter than " + maxLength + " characters"; |
339 | } | |
340 | ||
341 | } | |
342 | ||
343 | private static class InPredicate implements Predicate { | |
344 | ||
345 | private Collection<?> allowed; | |
346 | ||
347 | 30 | InPredicate(Collection<?> allowed) { |
348 | 30 | this.allowed = allowed; |
349 | 30 | } |
350 | ||
351 | public boolean evaluate(Object data) throws ValidationException { | |
352 | 30 | return allowed.contains(data); |
353 | } | |
354 | ||
355 | public String getDescription() { | |
356 | 0 | return "in [" + join(allowed, ",") + "]"; |
357 | } | |
358 | ||
359 | } | |
360 | ||
361 | private static class WithdrawnPredicate extends MaxLengthPredicate { | |
362 | ||
363 | public WithdrawnPredicate() { | |
364 | 5430 | super(0); |
365 | 5430 | } |
366 | ||
367 | @Override | |
368 | public String getDescription() { | |
369 | 5415 | return "empty because it is withdrawn from the current HL7 version and should not be used"; |
370 | } | |
371 | ||
372 | } | |
373 | ||
374 | private static class NotPredicate implements Predicate { | |
375 | ||
376 | private Predicate delegate; | |
377 | ||
378 | 15 | public NotPredicate(Predicate delegate) { |
379 | 15 | this.delegate = delegate; |
380 | 15 | } |
381 | ||
382 | public boolean evaluate(Object data) throws ValidationException { | |
383 | try { | |
384 | 15 | return !delegate.evaluate(data); |
385 | 0 | } catch (ValidationException e) { |
386 | 0 | return true; |
387 | } | |
388 | } | |
389 | ||
390 | public String getDescription() { | |
391 | 0 | return "not " + delegate.getDescription(); |
392 | } | |
393 | ||
394 | } | |
395 | ||
396 | private static class EqualsPredicate implements Predicate { | |
397 | ||
398 | private Object expected; | |
399 | private boolean ignoresCase; | |
400 | ||
401 | public EqualsPredicate(Object expected) { | |
402 | 155 | this(expected, false); |
403 | 155 | } |
404 | ||
405 | EqualsPredicate(Object expected, boolean ignoresCase) { | |
406 | 185 | super(); |
407 | 185 | this.expected = expected; |
408 | 185 | this.ignoresCase = ignoresCase; |
409 | 185 | } |
410 | ||
411 | public boolean evaluate(Object data) throws ValidationException { | |
412 | 155 | if (ignoresCase) |
413 | 60 | return (data == null && expected == null) |
414 | 15 | || (data != null && data.toString().equalsIgnoreCase(expected.toString())); |
415 | 125 | return (data == null && expected == null) || (data != null && data.equals(expected)); |
416 | } | |
417 | ||
418 | public String getDescription() { | |
419 | 5 | return "equal to " + String.valueOf(expected); |
420 | } | |
421 | ||
422 | } | |
423 | ||
424 | private static class EmptyPredicate implements Predicate { | |
425 | ||
426 | 37950 | public EmptyPredicate() { |
427 | 37950 | } |
428 | ||
429 | public boolean evaluate(Object data) throws ValidationException { | |
430 | 15674 | return data == null || "".equals(data) || "\"\"".equals(data); |
431 | } | |
432 | ||
433 | public String getDescription() { | |
434 | 37905 | return "empty"; |
435 | } | |
436 | ||
437 | } | |
438 | ||
439 | private static class MatchesPredicate implements Predicate { | |
440 | ||
441 | private Pattern p; | |
442 | private String description; | |
443 | ||
444 | public MatchesPredicate(String regex) { | |
445 | 20 | this(regex, "matching with " + regex); |
446 | 20 | } |
447 | ||
448 | 38315 | public MatchesPredicate(String regex, String description) { |
449 | 38315 | p = Pattern.compile(regex); |
450 | 38315 | this.description = description; |
451 | 38315 | } |
452 | ||
453 | public MatchesPredicate(String regex, int flags) { | |
454 | 0 | this(regex, flags, "matching with " + regex); |
455 | 0 | } |
456 | ||
457 | 0 | public MatchesPredicate(String regex, int flags, String description) { |
458 | 0 | p = Pattern.compile(regex); |
459 | 0 | this.description = description; |
460 | 0 | } |
461 | ||
462 | public boolean evaluate(Object data) throws ValidationException { | |
463 | 15874 | return (data != null && p.matcher(data.toString()).matches()); |
464 | } | |
465 | ||
466 | public String getDescription() { | |
467 | 37905 | return description; |
468 | } | |
469 | ||
470 | } | |
471 | ||
472 | private static class AnyOfPredicate implements Predicate { | |
473 | ||
474 | private Iterable<Predicate> predicates; | |
475 | ||
476 | public AnyOfPredicate(Iterable<Predicate> predicates) { | |
477 | 37960 | super(); |
478 | 37960 | this.predicates = predicates; |
479 | 37960 | } |
480 | ||
481 | public boolean evaluate(Object data) throws ValidationException { | |
482 | 15684 | for (Predicate p : predicates) { |
483 | 31168 | if (p.evaluate(data)) { |
484 | 15124 | return true; |
485 | } | |
486 | 16044 | } |
487 | 560 | return false; |
488 | } | |
489 | ||
490 | public String getDescription() { | |
491 | 37905 | String or = " or "; |
492 | 37905 | StringBuilder b = new StringBuilder(); |
493 | 37905 | for (Predicate p : predicates) { |
494 | 75810 | b.append(p.getDescription()).append(or); |
495 | 75810 | } |
496 | 37905 | return b.substring(0, b.length() - or.length()); |
497 | } | |
498 | ||
499 | } | |
500 | ||
501 | private static class AllOfPredicate implements Predicate { | |
502 | ||
503 | private Iterable<Predicate> predicates; | |
504 | ||
505 | public AllOfPredicate(Iterable<Predicate> predicates) { | |
506 | 30 | super(); |
507 | 30 | this.predicates = predicates; |
508 | 30 | } |
509 | ||
510 | public boolean evaluate(Object data) throws ValidationException { | |
511 | 30 | for (Predicate p : predicates) { |
512 | 40 | if (!p.evaluate(data)) { |
513 | 20 | return false; |
514 | } | |
515 | 20 | } |
516 | 10 | return true; |
517 | } | |
518 | ||
519 | public String getDescription() { | |
520 | 0 | String and = " and "; |
521 | 0 | StringBuilder b = new StringBuilder(); |
522 | 0 | for (Predicate p : predicates) { |
523 | 0 | b.append(p.getDescription()).append(and); |
524 | 0 | } |
525 | 0 | return b.substring(0, b.length() - and.length()); |
526 | } | |
527 | ||
528 | } | |
529 | ||
530 | } |