Coverage Report - ca.uhn.hl7v2.validation.builder.BuilderSupport
 
Classes in this File Line Coverage Branch Coverage Complexity
BuilderSupport
72%
29/40
0%
0/4
1.397
BuilderSupport$AllOfPredicate
60%
9/15
66%
4/6
1.397
BuilderSupport$AlwaysPredicate
100%
5/5
50%
1/2
1.397
BuilderSupport$AnyOfPredicate
100%
15/15
100%
6/6
1.397
BuilderSupport$EmptyPredicate
100%
4/4
100%
6/6
1.397
BuilderSupport$EqualsPredicate
100%
11/11
100%
18/18
1.397
BuilderSupport$InPredicate
80%
4/5
N/A
1.397
BuilderSupport$MatchesPredicate
57%
8/14
100%
4/4
1.397
BuilderSupport$MaxLengthPredicate
100%
6/6
100%
4/4
1.397
BuilderSupport$NotPredicate
57%
4/7
100%
2/2
1.397
BuilderSupport$WithdrawnPredicate
100%
4/4
N/A
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  8980
         protected BuilderSupport() {        
 45  8980
         }
 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  31
                 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  6
                 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  3215
                 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  3211
                 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  4
                 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  3284
         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  8
                 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  464
                 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  465
                 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  465
                 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  474
                 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  469
                 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  469
                 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  462
                 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  7
                 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  1
                 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  3
                 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  3
                 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  3217
                 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  6
                 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  3214
                 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  3
                 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  3
                 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  925
                 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  461
                 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  1011
                 return new AlwaysPredicate(b);
 285  
         }
 286  
 
 287  
         /**
 288  
          * @return a predicate that evaluates to <code>false</code>
 289  
          */
 290  
         public Predicate alwaysFails() {
 291  2
                 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 class AlwaysPredicate implements Predicate {
 308  
 
 309  
                 private boolean b;
 310  
 
 311  1011
                 AlwaysPredicate(boolean b) {
 312  1011
                         this.b = b;
 313  1011
                 }
 314  
 
 315  
                 public boolean evaluate(Object data) throws ValidationException {
 316  7415
                         return b;
 317  
                 }
 318  
 
 319  
                 public String getDescription() {
 320  1005
                         return b ? "anything" : "nothing";
 321  
                 }
 322  
 
 323  
         }
 324  
 
 325  
         private class MaxLengthPredicate implements Predicate {
 326  
 
 327  1386
                 private int maxLength = Integer.MAX_VALUE;
 328  
 
 329  1386
                 public MaxLengthPredicate(int maxSize) {
 330  1386
                         this.maxLength = maxSize;
 331  1386
                 }
 332  
 
 333  
                 public boolean evaluate(Object data) throws ValidationException {
 334  5799
                         return (data == null || data.toString().length() <= maxLength);
 335  
                 }
 336  
 
 337  
                 public String getDescription() {
 338  921
                         return "shorter than " + maxLength + " characters";
 339  
                 }
 340  
 
 341  
         }
 342  
 
 343  
         private class InPredicate implements Predicate {
 344  
 
 345  
                 private Collection<?> allowed;
 346  
 
 347  6
                 InPredicate(Collection<?> allowed) {
 348  6
                         this.allowed = allowed;
 349  6
                 }
 350  
 
 351  
                 public boolean evaluate(Object data) throws ValidationException {
 352  6
                         return allowed.contains(data);
 353  
                 }
 354  
 
 355  
                 public String getDescription() {
 356  0
                         return "in [" + join(allowed, ",") + "]";
 357  
                 }
 358  
 
 359  
         }
 360  
 
 361  
         private class WithdrawnPredicate extends MaxLengthPredicate {
 362  
 
 363  461
                 public WithdrawnPredicate() {
 364  461
                         super(0);
 365  461
                 }
 366  
 
 367  
                 @Override
 368  
                 public String getDescription() {
 369  458
                         return "empty because it is withdrawn from the current HL7 version and should not be used";
 370  
                 }
 371  
 
 372  
         }
 373  
 
 374  
         private class NotPredicate implements Predicate {
 375  
 
 376  
                 private Predicate delegate;
 377  
 
 378  3
                 public NotPredicate(Predicate delegate) {
 379  3
                         this.delegate = delegate;
 380  3
                 }
 381  
 
 382  
                 public boolean evaluate(Object data) throws ValidationException {
 383  
                         try {
 384  3
                                 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 class EqualsPredicate implements Predicate {
 397  
 
 398  
                 private Object expected;
 399  
                 private boolean ignoresCase;
 400  
 
 401  
                 public EqualsPredicate(Object expected) {
 402  31
                         this(expected, false);
 403  31
                 }
 404  
 
 405  37
                 EqualsPredicate(Object expected, boolean ignoresCase) {
 406  37
                         super();
 407  37
                         this.expected = expected;
 408  37
                         this.ignoresCase = ignoresCase;
 409  37
                 }
 410  
 
 411  
                 public boolean evaluate(Object data) throws ValidationException {
 412  31
                         if (ignoresCase)
 413  6
                                 return (data == null && expected == null)
 414  
                                                 || (data != null && data.toString().equalsIgnoreCase(expected.toString()));
 415  25
                         return (data == null && expected == null) || (data != null && data.equals(expected));
 416  
                 }
 417  
 
 418  
                 public String getDescription() {
 419  1
                         return "equal to " + String.valueOf(expected);
 420  
                 }
 421  
 
 422  
         }
 423  
 
 424  
         private class EmptyPredicate implements Predicate {
 425  
 
 426  3215
                 public EmptyPredicate() {
 427  3215
                 }
 428  
 
 429  
                 public boolean evaluate(Object data) throws ValidationException {
 430  2327
                         return data == null || "".equals(data) || "\"\"".equals(data);
 431  
                 }
 432  
 
 433  
                 public String getDescription() {
 434  3206
                         return "empty";
 435  
                 }
 436  
 
 437  
         }
 438  
 
 439  
         private class MatchesPredicate implements Predicate {
 440  
 
 441  
                 private Pattern p;
 442  
                 private String description;
 443  
 
 444  
                 public MatchesPredicate(String regex) {
 445  4
                         this(regex, "matching with " + regex);
 446  4
                 }
 447  
                 
 448  3288
         public MatchesPredicate(String regex, String description) {
 449  3288
             p = Pattern.compile(regex);
 450  3288
             this.description = description;
 451  3288
         }                
 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  2374
                         return (data != null && p.matcher(data.toString()).matches());
 464  
                 }
 465  
 
 466  
                 public String getDescription() {
 467  3206
                         return description;
 468  
                 }
 469  
 
 470  
         }
 471  
 
 472  
         private class AnyOfPredicate implements Predicate {
 473  
 
 474  
                 private Iterable<Predicate> predicates;
 475  
 
 476  3217
                 public AnyOfPredicate(Iterable<Predicate> predicates) {
 477  3217
                         super();
 478  3217
                         this.predicates = predicates;
 479  3217
                 }
 480  
 
 481  
                 public boolean evaluate(Object data) throws ValidationException {
 482  2329
                         for (Predicate p : predicates) {
 483  4625
                                 if (p.evaluate(data)) {
 484  2220
                                         return true;
 485  
                                 }
 486  2405
                         }
 487  109
                         return false;
 488  
                 }
 489  
 
 490  
                 public String getDescription() {
 491  3206
                         String or = " or ";
 492  3206
                         StringBuilder b = new StringBuilder();
 493  3206
                         for (Predicate p : predicates) {
 494  6412
                                 b.append(p.getDescription()).append(or);
 495  6412
                         }
 496  3206
                         return b.substring(0, b.length() - or.length());
 497  
                 }
 498  
 
 499  
         }
 500  
 
 501  
         private class AllOfPredicate implements Predicate {
 502  
 
 503  
                 private Iterable<Predicate> predicates;
 504  
 
 505  6
                 public AllOfPredicate(Iterable<Predicate> predicates) {
 506  6
                         super();
 507  6
                         this.predicates = predicates;
 508  6
                 }
 509  
 
 510  
                 public boolean evaluate(Object data) throws ValidationException {
 511  6
                         for (Predicate p : predicates) {
 512  8
                                 if (!p.evaluate(data)) {
 513  4
                                         return false;
 514  
                                 }
 515  4
                         }
 516  2
                         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  
 }