Coverage Report - ca.uhn.hl7v2.conf.check.DefaultValidator
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultValidator
70%
194/275
74%
129/174
4.031
 
 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 "DefaultValidator.java".  Description: 
 10  
 "A default conformance validator." 
 11  
 
 12  
 The Initial Developer of the Original Code is University Health Network. Copyright (C) 
 13  
 2003.  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  
  */
 27  
 
 28  
 package ca.uhn.hl7v2.conf.check;
 29  
 
 30  
 import java.io.BufferedReader;
 31  
 import java.io.File;
 32  
 import java.io.FileReader;
 33  
 import java.io.IOException;
 34  
 import java.util.ArrayList;
 35  
 import java.util.List;
 36  
 
 37  
 import ca.uhn.hl7v2.model.*;
 38  
 import org.slf4j.Logger;
 39  
 import org.slf4j.LoggerFactory;
 40  
 
 41  
 import ca.uhn.hl7v2.DefaultHapiContext;
 42  
 import ca.uhn.hl7v2.HL7Exception;
 43  
 import ca.uhn.hl7v2.HapiContext;
 44  
 import ca.uhn.hl7v2.HapiContextSupport;
 45  
 import ca.uhn.hl7v2.conf.ProfileException;
 46  
 import ca.uhn.hl7v2.conf.parser.ProfileParser;
 47  
 import ca.uhn.hl7v2.conf.spec.RuntimeProfile;
 48  
 import ca.uhn.hl7v2.conf.spec.message.AbstractComponent;
 49  
 import ca.uhn.hl7v2.conf.spec.message.AbstractSegmentContainer;
 50  
 import ca.uhn.hl7v2.conf.spec.message.Component;
 51  
 import ca.uhn.hl7v2.conf.spec.message.Field;
 52  
 import ca.uhn.hl7v2.conf.spec.message.ProfileStructure;
 53  
 import ca.uhn.hl7v2.conf.spec.message.Seg;
 54  
 import ca.uhn.hl7v2.conf.spec.message.SegGroup;
 55  
 import ca.uhn.hl7v2.conf.spec.message.StaticDef;
 56  
 import ca.uhn.hl7v2.conf.spec.message.SubComponent;
 57  
 import ca.uhn.hl7v2.conf.store.CodeStore;
 58  
 import ca.uhn.hl7v2.conf.store.ProfileStoreFactory;
 59  
 import ca.uhn.hl7v2.parser.EncodingCharacters;
 60  
 import ca.uhn.hl7v2.parser.GenericParser;
 61  
 import ca.uhn.hl7v2.parser.Parser;
 62  
 import ca.uhn.hl7v2.parser.PipeParser;
 63  
 import ca.uhn.hl7v2.util.Terser;
 64  
 
 65  
 /**
 66  
  * A default conformance profile validator.
 67  
  * 
 68  
  * Note: this class is currently NOT thread-safe!
 69  
  * 
 70  
  * @author Bryan Tripp
 71  
  */
 72  
 public class DefaultValidator extends HapiContextSupport implements Validator {
 73  
 
 74  
         private EncodingCharacters enc; // used to check for content in parts of a message
 75  5
         private static final Logger log = LoggerFactory.getLogger(DefaultValidator.class);
 76  60
         private boolean validateChildren = true;
 77  
         private CodeStore codeStore;
 78  
 
 79  
         /** Creates a new instance of DefaultValidator */
 80  
         public DefaultValidator() {
 81  30
             this(new DefaultHapiContext());
 82  30
         }
 83  
         
 84  
     public DefaultValidator(HapiContext context) {
 85  60
         super(context);
 86  60
         enc = new EncodingCharacters('|', null); // the | is assumed later -- don't change
 87  60
     }        
 88  
 
 89  
         /**
 90  
          * If set to false (default is true), each testXX and validateXX method will only test the
 91  
          * direct object it is responsible for, not its children.
 92  
          */
 93  
         public void setValidateChildren(boolean validateChildren) {
 94  0
                 this.validateChildren = validateChildren;
 95  0
         }
 96  
 
 97  
         /**
 98  
          * <p>
 99  
          * Provides a code store to use to provide the code tables which will be used to validate coded
 100  
          * value types. If a code store has not been set (which is the default),
 101  
          * {@link ProfileStoreFactory} will be checked for an appropriate code store, and if none is
 102  
          * found then coded values will not be validated.
 103  
          * </p>
 104  
          */
 105  
         public void setCodeStore(CodeStore theCodeStore) {
 106  0
                 codeStore = theCodeStore;
 107  0
         }
 108  
 
 109  
         /**
 110  
          * @see Validator#validate
 111  
          */
 112  
         public HL7Exception[] validate(Message message, StaticDef profile) throws ProfileException,
 113  
                         HL7Exception {
 114  105
                 List<HL7Exception> exList = new ArrayList<HL7Exception>();
 115  105
                 Terser t = new Terser(message);
 116  
 
 117  105
         checkMessageType(t.get("/MSH-9-1"), profile, exList);
 118  105
         checkEventType(t.get("/MSH-9-2"), profile, exList);
 119  105
         checkMessageStructure(t.get("/MSH-9-3"), profile, exList);
 120  
 
 121  105
         exList.addAll(doTestGroup(message, profile, profile.getIdentifier(),
 122  
                                 validateChildren));
 123  105
                 return exList.toArray(new HL7Exception[exList.size()]);
 124  
         }
 125  
 
 126  
 
 127  
     protected void checkEventType(String evType, StaticDef profile, List<HL7Exception> exList) throws HL7Exception {
 128  105
         if (!evType.equals(profile.getEventType())
 129  30
                 && !profile.getEventType().equalsIgnoreCase("ALL")) {
 130  0
             HL7Exception e = new ProfileNotFollowedException("Event type " + evType
 131  0
                     + " doesn't match profile type of " + profile.getEventType());
 132  0
             exList.add(e);
 133  
         }
 134  105
     }
 135  
 
 136  
     protected void checkMessageType(String msgType, StaticDef profile, List<HL7Exception> exList) throws HL7Exception {
 137  105
         if (!msgType.equals(profile.getMsgType())) {
 138  0
             HL7Exception e = new ProfileNotFollowedException("Message type " + msgType
 139  0
                     + " doesn't match profile type of " + profile.getMsgType());
 140  0
             exList.add(e);
 141  
         }
 142  105
     }
 143  
 
 144  
     protected void checkMessageStructure(String msgStruct, StaticDef profile, List<HL7Exception> exList) {
 145  105
         if (msgStruct == null || !msgStruct.equals(profile.getMsgStructID())) {
 146  0
             HL7Exception e = new ProfileNotFollowedException("Message structure " + msgStruct
 147  0
                     + " doesn't match profile type of " + profile.getMsgStructID());
 148  0
             exList.add(e);
 149  
         }
 150  105
     }
 151  
 
 152  
         /**
 153  
          * Tests a group against a group section of a profile.
 154  
          */
 155  
         public List<HL7Exception> testGroup(Group group, SegGroup profile, String profileID)
 156  
                         throws ProfileException {
 157  0
                 return doTestGroup(group, profile, profileID, true);
 158  
         }
 159  
 
 160  
         protected List<HL7Exception> doTestGroup(Group group, AbstractSegmentContainer profile,
 161  
                         String profileID, boolean theValidateChildren) throws ProfileException {
 162  105
                 List<HL7Exception> exList = new ArrayList<HL7Exception>();
 163  105
                 List<String> allowedStructures = new ArrayList<String>();
 164  
 
 165  105
                 for (ProfileStructure struct : profile) {
 166  
 
 167  
                         // only test a structure in detail if it isn't X
 168  340
                         if (!struct.getUsage().equalsIgnoreCase("X")) {
 169  330
                                 allowedStructures.add(struct.getName());
 170  
 
 171  
                                 // see which instances have content
 172  
                                 try {
 173  330
                                         List<Structure> instancesWithContent = new ArrayList<Structure>();
 174  510
                                         for (Structure instance : group.getAll(struct.getName())) {
 175  180
                                                 if (!instance.isEmpty())
 176  180
                                                         instancesWithContent.add(instance);
 177  
                                         }
 178  
 
 179  640
                                         testCardinality(instancesWithContent.size(), struct.getMin(),
 180  320
                                                         struct.getMax(), struct.getUsage(), struct.getName(), exList);
 181  
 
 182  
                                         // test children on instances with content
 183  320
                                         if (theValidateChildren) {
 184  320
                                                 for (Structure s : instancesWithContent) {
 185  180
                             exList.addAll(testStructure(s, struct, profileID));
 186  180
                                                 }
 187  
                                         }
 188  
 
 189  10
                                 } catch (HL7Exception he) {
 190  10
                                         exList.add(new ProfileNotHL7CompliantException(struct.getName()
 191  
                                                         + " not found in message"));
 192  320
                                 }
 193  
                         }
 194  340
                 }
 195  
 
 196  
                 // complain about X structures that have content
 197  105
        checkForExtraStructures(group, allowedStructures, exList);
 198  
 
 199  105
                 return exList;
 200  
         }
 201  
 
 202  
         /**
 203  
          * Checks a group's children against a list of allowed structures for the group (ie those
 204  
          * mentioned in the profile with usage other than X). Returns a list of exceptions representing
 205  
          * structures that appear in the message but are not supposed to.
 206  
          */
 207  
         protected void checkForExtraStructures(Group group, List<String> allowedStructures, List<HL7Exception> exList)
 208  
                         throws ProfileException {
 209  1845
                 for (String childName : group.getNames()) {
 210  1740
                         if (!allowedStructures.contains(childName)) {
 211  
                                 try {
 212  1430
                                         for (Structure rep : group.getAll(childName)) {
 213  5
                                                 if (!rep.isEmpty()) {
 214  5
                                                         HL7Exception e = new XElementPresentException("The structure "
 215  
                                                                         + childName + " appears in the message but not in the profile");
 216  5
                                                         exList.add(e);
 217  
                                                 }
 218  
                                         }
 219  0
                                 } catch (HL7Exception he) {
 220  0
                                         throw new ProfileException("Problem checking profile", he);
 221  1425
                                 }
 222  
                         }
 223  
                 }
 224  105
         }
 225  
 
 226  
         /**
 227  
          * Checks cardinality and creates an appropriate exception if out of bounds. The usage code is
 228  
          * needed because if min cardinality is > 0, the min # of reps is only required if the usage
 229  
          * code is 'R' (see HL7 v2.5 section 2.12.6.4).
 230  
          * 
 231  
          * @param reps the number of reps
 232  
          * @param min the minimum number of reps
 233  
          * @param max the maximum number of reps (-1 means *)
 234  
          * @param usage the usage code
 235  
          * @param name the name of the repeating structure (used in exception msg)
 236  
          * @return null if cardinality OK, exception otherwise
 237  
          */
 238  
         protected HL7Exception testCardinality(int reps, int min, int max, String usage, String name, List<HL7Exception> exList) {
 239  2965
                 HL7Exception e = null;
 240  2965
                 if (reps < min && usage.equalsIgnoreCase("R")) {
 241  50
             e = new ProfileNotFollowedException(name + " must have at least " + min
 242  
                                         + " repetitions (has " + reps + ")");
 243  2915
                 } else if (max > 0 && reps > max) {
 244  0
             e = new ProfileNotFollowedException(name + " must have no more than " + max
 245  
                                         + " repetitions (has " + reps + ")");
 246  
                 }
 247  2965
         if (e != null) exList.add(e);
 248  2965
         return e;
 249  
         }
 250  
 
 251  
         /**
 252  
          * Tests a structure (segment or group) against the corresponding part of a profile.
 253  
          */
 254  
         public List<HL7Exception> testStructure(Structure s, ProfileStructure profile, String profileID)
 255  
                         throws ProfileException {
 256  180
                 List<HL7Exception> exList = new ArrayList<HL7Exception>();
 257  180
                 if (profile instanceof Seg) {
 258  180
                         if (Segment.class.isAssignableFrom(s.getClass())) {
 259  180
                                 exList.addAll(doTestSegment((Segment) s, (Seg) profile, profileID, validateChildren));
 260  
                         } else {
 261  0
                                 exList.add(new ProfileNotHL7CompliantException(
 262  
                                                 "Mismatch between a segment in the profile and the structure "
 263  0
                                                                 + s.getClass().getName() + " in the message"));
 264  
                         }
 265  0
                 } else if (profile instanceof SegGroup) {
 266  0
                         if (Group.class.isAssignableFrom(s.getClass())) {
 267  0
                 exList.addAll(testGroup((Group) s, (SegGroup) profile, profileID));
 268  
                         } else {
 269  0
                                 exList.add(new ProfileNotHL7CompliantException(
 270  
                                                 "Mismatch between a group in the profile and the structure "
 271  0
                                                                 + s.getClass().getName() + " in the message"));
 272  
                         }
 273  
                 }
 274  180
                 return exList;
 275  
         }
 276  
 
 277  
         /**
 278  
          * Tests a segment against a segment section of a profile.
 279  
          */
 280  
         public List<HL7Exception> testSegment(ca.uhn.hl7v2.model.Segment segment, Seg profile,
 281  
                         String profileID) throws ProfileException {
 282  0
                 return doTestSegment(segment, profile, profileID, true);
 283  
         }
 284  
 
 285  
         protected List<HL7Exception> doTestSegment(ca.uhn.hl7v2.model.Segment segment, Seg profile,
 286  
                         String profileID, boolean theValidateChildren) throws ProfileException {
 287  180
                 List<HL7Exception> exList = new ArrayList<HL7Exception>();
 288  180
                 List<Integer> allowedFields = new ArrayList<Integer>();
 289  
 
 290  2835
                 for (int i = 1; i <= profile.getFields(); i++) {
 291  2655
                         Field field = profile.getField(i);
 292  
 
 293  
                         // only test a field in detail if it isn't X
 294  2655
                         if (!field.getUsage().equalsIgnoreCase("X")) {
 295  2645
                                 allowedFields.add(i);
 296  
 
 297  
                                 // see which instances have content
 298  
                                 try {
 299  2645
                                         Type[] instances = segment.getField(i);
 300  2645
                                         List<Type> instancesWithContent = new ArrayList<Type>();
 301  3700
                                         for (Type instance : instances) {
 302  1055
                                                 if (!instance.isEmpty())
 303  1040
                                                         instancesWithContent.add(instance);
 304  
                                         }
 305  
 
 306  5290
                                         HL7Exception ce = testCardinality(instancesWithContent.size(), field.getMin(),
 307  2645
                                                         field.getMax(), field.getUsage(), field.getName(), exList);
 308  2645
                                         if (ce != null) {
 309  30
                                                 ce.setFieldPosition(i);
 310  
                                         }
 311  
 
 312  
                                         // test field instances with content
 313  2645
                                         if (theValidateChildren) {
 314  2645
                                                 for (Type s : instancesWithContent) {
 315  
                                                         // escape field value when checking length
 316  1040
                             boolean escape = !(profile.getName().equalsIgnoreCase("MSH") && i < 3);
 317  1040
                             List<HL7Exception> childExceptions = doTestField(s, field, escape,
 318  
                                                                         profileID, validateChildren);
 319  1040
                                                         for (HL7Exception ex : childExceptions) {
 320  215
                                 ex.setFieldPosition(i);
 321  215
                                                         }
 322  1040
                             exList.addAll(childExceptions);
 323  1040
                                                 }
 324  
                                         }
 325  
 
 326  0
                                 } catch (HL7Exception he) {
 327  0
                                         exList.add(new ProfileNotHL7CompliantException("Field " + i
 328  
                                                         + " not found in message"));
 329  2645
                                 }
 330  
                         }
 331  
 
 332  
                 }
 333  
 
 334  
                 // complain about X fields with content
 335  180
                 checkForExtraFields(segment, allowedFields, exList);
 336  
 
 337  180
                 for (HL7Exception ex : exList) {
 338  250
             ex.setSegmentName(profile.getName());
 339  250
                 }
 340  180
                 return exList;
 341  
         }
 342  
 
 343  
         /**
 344  
          * Checks a segment against a list of allowed fields (ie those mentioned in the profile with
 345  
          * usage other than X). Returns a list of exceptions representing field that appear but are not
 346  
          * supposed to.
 347  
          * 
 348  
          * @param allowedFields an array of Integers containing field #s of allowed fields
 349  
          */
 350  
         protected void checkForExtraFields(Segment segment, List<Integer> allowedFields, List<HL7Exception> exList)
 351  
                         throws ProfileException {
 352  2835
                 for (int i = 1; i <= segment.numFields(); i++) {
 353  2655
                         if (!allowedFields.contains(i)) {
 354  
                                 try {
 355  10
                                         Type[] reps = segment.getField(i);
 356  15
                                         for (Type rep : reps) {
 357  5
                                                 if (!rep.isEmpty()) {
 358  5
                                                         HL7Exception e = new XElementPresentException("Field " + i + " in "
 359  5
                                                                         + segment.getName()
 360  
                                                                         + " appears in the message but not in the profile");
 361  5
                                                         exList.add(e);
 362  
                                                 }
 363  
                                         }
 364  0
                                 } catch (HL7Exception he) {
 365  0
                                         throw new ProfileException("Problem testing against profile", he);
 366  10
                                 }
 367  
                         }
 368  
                 }
 369  180
         }
 370  
 
 371  
         /**
 372  
          * Tests a Type against the corresponding section of a profile.
 373  
          * 
 374  
          * @param encoded optional encoded form of type (if you want to specify this -- if null, default
 375  
          *            pipe-encoded form is used to check length and constant val)
 376  
          */
 377  
         public List<HL7Exception> testType(Type type, AbstractComponent<?> profile, String encoded,
 378  
                         String profileID) {
 379  3065
                 List<HL7Exception> exList = new ArrayList<HL7Exception>();
 380  3065
                 if (encoded == null)
 381  2845
                         encoded = PipeParser.encode(type, this.enc);
 382  
 
 383  3065
                 testUsage(encoded, profile.getUsage(), profile.getName(), exList);
 384  
 
 385  3065
                 if (!profile.getUsage().equals("X")) {
 386  2970
             checkDataType(profile.getDatatype(), type, exList);
 387  2970
             checkLength(profile.getLength(), profile.getName(), encoded, exList);
 388  2970
             checkConstantValue(profile.getConstantValue(), encoded, exList);
 389  
 
 390  2970
             testTypeAgainstTable(type, profile, profileID, exList);
 391  
                 }
 392  
 
 393  3065
                 return exList;
 394  
         }
 395  
 
 396  
     protected void checkConstantValue(String value, String encoded, List<HL7Exception> exList) {
 397  
         // check constant value
 398  2970
         if (value != null && value.length() > 0) {
 399  0
             if (!encoded.equals(value))
 400  0
                 exList.add(new ProfileNotFollowedException("'" + encoded
 401  
                         + "' doesn't equal constant value of '" + value + "'"));
 402  
         }
 403  2970
     }
 404  
 
 405  
     protected void checkLength(long length, String name, String encoded, List<HL7Exception> exList) {
 406  
         // check length
 407  2970
         if (encoded.length() > length)
 408  200
             exList.add(new ProfileNotFollowedException("The type " + name
 409  100
                     + " has length " + encoded.length() + " which exceeds max of "
 410  
                     + length));
 411  2970
     }
 412  
 
 413  
     protected void checkDataType(String dataType, Type type, List<HL7Exception> exList) {
 414  
         // check datatype
 415  2970
         String typeName = type.getName();
 416  2970
         if (!(type instanceof Varies || typeName.equals(dataType))) {
 417  45
             exList.add(new ProfileNotHL7CompliantException("HL7 datatype " + typeName
 418  
                     + " doesn't match profile datatype " + dataType));
 419  
         }
 420  2970
     }
 421  
 
 422  
     /**
 423  
          * Tests whether the given type falls within a maximum length.
 424  
          * 
 425  
          * @return null of OK, an HL7Exception otherwise
 426  
          */
 427  
         public HL7Exception testLength(Type type, int maxLength) {
 428  0
                 HL7Exception e = null;
 429  0
                 String encoded = PipeParser.encode(type, this.enc);
 430  0
                 if (encoded.length() > maxLength) {
 431  0
                         e = new ProfileNotFollowedException("Length of " + encoded.length()
 432  
                                         + " exceeds maximum of " + maxLength);
 433  
                 }
 434  0
                 return e;
 435  
         }
 436  
 
 437  
         /**
 438  
          * Tests an element against the corresponding usage code. The element is required in its encoded
 439  
          * form.
 440  
          * 
 441  
          * @param encoded the pipe-encoded message element
 442  
          * @param usage the usage code (e.g. "CE")
 443  
          * @param name the name of the element (for use in exception messages)
 444  
          * @return null if there is no problem, an HL7Exception otherwise
 445  
          */
 446  
         protected void testUsage(String encoded, String usage, String name, List<HL7Exception> exList) {
 447  3065
                 if (usage.equalsIgnoreCase("R")) {
 448  1135
                         if (encoded.length() == 0)
 449  60
                                 exList.add(new ProfileNotFollowedException("Required element " + name + " is missing"));
 450  1930
                 } else if (usage.equalsIgnoreCase("RE")) {
 451  
                         // can't test anything
 452  1930
                 } else if (usage.equalsIgnoreCase("O")) {
 453  
                         // can't test anything
 454  295
                 } else if (usage.equalsIgnoreCase("C")) {
 455  
                         // can't test anything yet -- wait for condition syntax in v2.6
 456  140
                 } else if (usage.equalsIgnoreCase("CE")) {
 457  
                         // can't test anything
 458  140
                 } else if (usage.equalsIgnoreCase("X")) {
 459  95
                         if (encoded.length() > 0)
 460  10
                                 exList.add(new XElementPresentException("Element \"" + name
 461  
                                                 + "\" is present but specified as not used (X)"));
 462  45
                 } else if (usage.equalsIgnoreCase("B")) {
 463  
                         // can't test anything
 464  
                 }
 465  3065
         }
 466  
 
 467  
         /**
 468  
          * Tests table values for ID, IS, and CE types. An empty list is returned for all other types or
 469  
          * if the table name or number is missing.
 470  
          */
 471  
     protected void testTypeAgainstTable(Type type, AbstractComponent<?> profile,
 472  
                         String profileID, List<HL7Exception> exList) {
 473  2970
                 if (profile.getTable() != null
 474  1570
                                 && (type.getName().equals("IS") || type.getName().equals("ID"))) {
 475  1225
                         String codeSystem = String.format("HL7%1$4s", profile.getTable()).replace(" ", "0");
 476  1225
                         String value = ((Primitive) type).getValue();
 477  1225
                         addTableTestResult(profileID, codeSystem, value, exList);
 478  1225
                 } else if (type.getName().equals("CE")) {
 479  215
                         String value = Terser.getPrimitive(type, 1, 1).getValue();
 480  215
                         String codeSystem = Terser.getPrimitive(type, 3, 1).getValue();
 481  215
                         addTableTestResult(profileID, codeSystem, value, exList);
 482  
 
 483  215
                         value = Terser.getPrimitive(type, 4, 1).getValue();
 484  215
                         codeSystem = Terser.getPrimitive(type, 6, 1).getValue();
 485  215
                         addTableTestResult(profileID, codeSystem, value, exList);
 486  
                 }
 487  2970
         }
 488  
 
 489  
         protected void addTableTestResult(String profileID, String codeSystem, String value, List<HL7Exception> exList) {
 490  1655
                 if (codeSystem != null && value != null && validateChildren) {
 491  735
                         testValueAgainstTable(profileID, codeSystem, value, exList);
 492  
                 }
 493  1655
         }
 494  
 
 495  
         protected void testValueAgainstTable(String profileID, String codeSystem, String value, List<HL7Exception> exList) {
 496  735
                 CodeStore store = codeStore;
 497  735
                 if (codeStore == null) {
 498  735
                         store = getHapiContext().getCodeStoreRegistry().getCodeStore(profileID, codeSystem);
 499  
                 }
 500  
 
 501  735
                 if (store == null) {
 502  710
                         log.info(
 503  
                                         "Not checking value {}: no code store was found for profile {} code system {}",
 504  
                                         new Object[] { value, profileID, codeSystem });
 505  
                 } else {
 506  25
                         if (!store.knowsCodes(codeSystem)) {
 507  0
                                 log.warn("Not checking value {}: Don't have a table for code system {}", value,
 508  
                                                 codeSystem);
 509  25
                         } else if (!store.isValidCode(codeSystem, value)) {
 510  5
                                 exList.add(new ProfileNotFollowedException("Code '" + value + "' not found in table "
 511  
                                                 + codeSystem + ", profile " + profileID));
 512  
                         }
 513  
                 }
 514  
 
 515  735
         }
 516  
 
 517  
         public List<HL7Exception> testField(Type type, Field profile, boolean escape, String profileID)
 518  
                         throws ProfileException, HL7Exception {
 519  15
                 return doTestField(type, profile, escape, profileID, true);
 520  
         }
 521  
 
 522  
         protected List<HL7Exception> doTestField(Type type, Field profile, boolean escape, String profileID,
 523  
                         boolean theValidateChildren) throws ProfileException, HL7Exception {
 524  1055
                 List<HL7Exception> exList = new ArrayList<HL7Exception>();
 525  
 
 526  
                 // account for MSH 1 & 2 which aren't escaped
 527  1055
                 String encoded = null;
 528  1055
                 if (!escape && Primitive.class.isAssignableFrom(type.getClass()))
 529  220
                         encoded = ((Primitive) type).getValue();
 530  
 
 531  1055
                 exList.addAll(testType(type, profile, encoded, profileID));
 532  
 
 533  
                 // test children
 534  1055
                 if (theValidateChildren) {
 535  1055
                         if (profile.getComponents() > 0 && !profile.getUsage().equals("X")) {
 536  620
                                 if (Composite.class.isAssignableFrom(type.getClass())) {
 537  620
                                         Composite comp = (Composite) type;
 538  2615
                                         for (int i = 1; i <= profile.getComponents(); i++) {
 539  1995
                                                 Component childProfile = profile.getComponent(i);
 540  
                                                 try {
 541  1995
                                                         Type child = comp.getComponent(i - 1);
 542  1995
                                                         exList.addAll(doTestComponent(child, childProfile, profileID, validateChildren));
 543  0
                                                 } catch (DataTypeException de) {
 544  0
                                                         exList.add(new ProfileNotHL7CompliantException(
 545  
                                                                         "More components in profile than allowed in message: "
 546  0
                                                                                         + de.getMessage()));
 547  1995
                                                 }
 548  
                                         }
 549  620
                                         checkExtraComponents(comp, profile.getComponents(), exList);
 550  620
                                 } else {
 551  0
                                         exList.add(new ProfileNotHL7CompliantException("A field has type primitive "
 552  0
                                                         + type.getClass().getName() + " but the profile defines components"));
 553  
                                 }
 554  
                         }
 555  
                 }
 556  
 
 557  1055
                 return exList;
 558  
         }
 559  
 
 560  
         public List<HL7Exception> testComponent(Type type, Component profile, String profileID)
 561  
                         throws ProfileException, HL7Exception {
 562  0
                 return doTestComponent(type, profile, profileID, true);
 563  
         }
 564  
 
 565  
         protected List<HL7Exception> doTestComponent(Type type, Component profile, String profileID,
 566  
                         boolean theValidateChildren) throws ProfileException, HL7Exception {
 567  1995
                 List<HL7Exception> exList = new ArrayList<HL7Exception>();
 568  1995
                 exList.addAll(testType(type, profile, null, profileID));
 569  
 
 570  
                 // test children
 571  1995
                 if (profile.getSubComponents() > 0 && !profile.getUsage().equals("X") && (!type.isEmpty())) {
 572  5
                         if (Composite.class.isAssignableFrom(type.getClass())) {
 573  5
                                 Composite comp = (Composite) type;
 574  
 
 575  5
                                 if (theValidateChildren) {
 576  20
                                         for (int i = 1; i <= profile.getSubComponents(); i++) {
 577  15
                                                 SubComponent childProfile = profile.getSubComponent(i);
 578  
                                                 try {
 579  15
                                                         Type child = comp.getComponent(i - 1);
 580  15
                                                         exList.addAll(testType(child, childProfile, null, profileID));
 581  0
                                                 } catch (DataTypeException de) {
 582  0
                                                         exList.add(new ProfileNotHL7CompliantException(
 583  
                                                                         "More subcomponents in profile than allowed in message: "
 584  0
                                                                                         + de.getMessage()));
 585  15
                                                 }
 586  
                                         }
 587  
                                 }
 588  
 
 589  5
                 checkExtraComponents(comp, profile.getSubComponents(), exList);
 590  5
                         } else {
 591  0
                                 exList.add(new ProfileNotFollowedException("A component has primitive type "
 592  0
                                                 + type.getClass().getName() + " but the profile defines subcomponents"));
 593  
                         }
 594  
                 }
 595  
 
 596  1995
                 return exList;
 597  
         }
 598  
 
 599  
         /** Tests for extra components (ie any not defined in the profile) */
 600  
         protected void checkExtraComponents(Composite comp, int numInProfile, List<HL7Exception> exList)
 601  
                         throws ProfileException {
 602  625
                 StringBuilder extra = new StringBuilder();
 603  625
                 for (int i = numInProfile; i < comp.getComponents().length; i++) {
 604  
                         try {
 605  0
                                 String s = PipeParser.encode(comp.getComponent(i), enc);
 606  0
                                 if (s.length() > 0) {
 607  0
                                         extra.append(s).append(enc.getComponentSeparator());
 608  
                                 }
 609  0
                         } catch (DataTypeException de) {
 610  0
                                 throw new ProfileException("Problem testing against profile", de);
 611  0
                         }
 612  
                 }
 613  
 
 614  625
                 if (extra.length() > 0) {
 615  0
                         exList.add(new XElementPresentException(
 616  0
                                         "The following components are not defined in the profile: " + extra.toString()));
 617  
                 }
 618  
 
 619  625
         }
 620  
 
 621  
         public static void main(String args[]) {
 622  
 
 623  0
                 if (args.length != 2) {
 624  0
                         System.out.println("Usage: DefaultValidator message_file profile_file");
 625  0
                         System.exit(1);
 626  
                 }
 627  
 
 628  0
                 DefaultValidator val = new DefaultValidator();
 629  
                 try {
 630  0
                         String msgString = loadFile(args[0]);
 631  0
                         Parser parser = new GenericParser();
 632  0
                         Message message = parser.parse(msgString);
 633  
 
 634  0
                         String profileString = loadFile(args[1]);
 635  0
                         ProfileParser profParser = new ProfileParser(true);
 636  0
                         RuntimeProfile profile = profParser.parse(profileString);
 637  
 
 638  0
                         HL7Exception[] exceptions = val.validate(message, profile.getMessage());
 639  
 
 640  0
                         System.out.println("Exceptions: ");
 641  0
                         for (int i = 0; i < exceptions.length; i++) {
 642  0
                                 System.out.println((i + 1) + ". " + exceptions[i].getMessage());
 643  
                         }
 644  0
                 } catch (Exception e) {
 645  0
                         e.printStackTrace();
 646  0
                 }
 647  0
         }
 648  
 
 649  
         /** loads file at the given path */
 650  
         private static String loadFile(String path) throws IOException {
 651  0
                 File file = new File(path);
 652  
                 // char[] cbuf = new char[(int) file.length()];
 653  0
                 BufferedReader in = new BufferedReader(new FileReader(file));
 654  0
                 StringBuffer buf = new StringBuffer(5000);
 655  
                 int c;
 656  0
                 while ((c = in.read()) != -1) {
 657  0
                         buf.append((char) c);
 658  
                 }
 659  
                 // in.read(cbuf, 0, (int) file.length());
 660  0
                 in.close();
 661  
                 // return String.valueOf(cbuf);
 662  0
                 return buf.toString();
 663  
         }
 664  
 
 665  
 }