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
57%
8/14
66%
4/6
1.397
BuilderSupport$AlwaysPredicate
100%
5/5
50%
1/2
1.397
BuilderSupport$AnyOfPredicate
100%
14/14
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%
3/3
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  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  
 }