001/** 002The contents of this file are subject to the Mozilla Public License Version 1.1 003(the "License"); you may not use this file except in compliance with the License. 004You may obtain a copy of the License at http://www.mozilla.org/MPL/ 005Software distributed under the License is distributed on an "AS IS" basis, 006WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 007specific language governing rights and limitations under the License. 008 009The Original Code is "DefaultValidator.java". Description: 010"A default conformance validator." 011 012The Initial Developer of the Original Code is University Health Network. Copyright (C) 0132003. All Rights Reserved. 014 015Contributor(s): ______________________________________. 016 017Alternatively, the contents of this file may be used under the terms of the 018GNU General Public License (the �GPL�), in which case the provisions of the GPL are 019applicable instead of those above. If you wish to allow use of your version of this 020file only under the terms of the GPL and not to allow others to use your version 021of this file under the MPL, indicate your decision by deleting the provisions above 022and replace them with the notice and other provisions required by the GPL License. 023If you do not delete the provisions above, a recipient may use your version of 024this file under either the MPL or the GPL. 025 026*/ 027 028package ca.uhn.hl7v2.conf.check; 029 030import java.io.BufferedReader; 031import java.io.File; 032import java.io.FileReader; 033import java.io.IOException; 034import java.util.ArrayList; 035 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039import ca.uhn.hl7v2.HL7Exception; 040import ca.uhn.hl7v2.conf.ProfileException; 041import ca.uhn.hl7v2.conf.parser.ProfileParser; 042import ca.uhn.hl7v2.conf.spec.RuntimeProfile; 043import ca.uhn.hl7v2.conf.spec.message.AbstractComponent; 044import ca.uhn.hl7v2.conf.spec.message.AbstractSegmentContainer; 045import ca.uhn.hl7v2.conf.spec.message.Component; 046import ca.uhn.hl7v2.conf.spec.message.Field; 047import ca.uhn.hl7v2.conf.spec.message.ProfileStructure; 048import ca.uhn.hl7v2.conf.spec.message.Seg; 049import ca.uhn.hl7v2.conf.spec.message.SegGroup; 050import ca.uhn.hl7v2.conf.spec.message.StaticDef; 051import ca.uhn.hl7v2.conf.spec.message.SubComponent; 052import ca.uhn.hl7v2.conf.store.CodeStore; 053import ca.uhn.hl7v2.conf.store.ProfileStoreFactory; 054import ca.uhn.hl7v2.model.Composite; 055import ca.uhn.hl7v2.model.DataTypeException; 056import ca.uhn.hl7v2.model.Group; 057import ca.uhn.hl7v2.model.Message; 058import ca.uhn.hl7v2.model.Primitive; 059import ca.uhn.hl7v2.model.Segment; 060import ca.uhn.hl7v2.model.Structure; 061import ca.uhn.hl7v2.model.Type; 062import ca.uhn.hl7v2.parser.EncodingCharacters; 063import ca.uhn.hl7v2.parser.GenericParser; 064import ca.uhn.hl7v2.parser.Parser; 065import ca.uhn.hl7v2.parser.PipeParser; 066import ca.uhn.hl7v2.util.Terser; 067 068/** 069 * A default conformance validator. 070 * @author Bryan Tripp 071 */ 072public class DefaultValidator implements Validator { 073 074 private EncodingCharacters enc; //used to check for content in parts of a message 075 private static final Logger log = LoggerFactory.getLogger(DefaultValidator.class); 076 private boolean validateChildren = true; 077 private CodeStore codeStore; 078 079 080 /** Creates a new instance of DefaultValidator */ 081 public DefaultValidator() { 082 enc = new EncodingCharacters('|', null); //the | is assumed later -- don't change 083 } 084 085 /** 086 * If set to false (default is true), each testXX and validateXX method will only 087 * test the direct object it is responsible for, not its children. 088 */ 089 public void setValidateChildren(boolean validateChildren) { 090 this.validateChildren = validateChildren; 091 } 092 093 /** 094 * <p> 095 * Provides a code store to use to provide the code tables which will be 096 * used to validate coded value types. If a code store has not been set (which 097 * is the default), {@link ProfileStoreFactory} will be checked for an 098 * appropriate code store, and if none is found then coded values will not 099 * be validated. 100 * </p> 101 */ 102 public void setCodeStore(CodeStore theCodeStore) { 103 codeStore = theCodeStore; 104 } 105 106 /** 107 * @see Validator#validate 108 */ 109 public HL7Exception[] validate(Message message, StaticDef profile) throws ProfileException, HL7Exception { 110 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(20); 111 Terser t = new Terser(message); 112 113 //check msg type, event type, msg struct ID 114 String msgType = t.get("/MSH-9-1"); 115 if (!msgType.equals(profile.getMsgType())) { 116 HL7Exception e = 117 new ProfileNotFollowedException("Message type " + msgType + " doesn't match profile type of " + profile.getMsgType()); 118 exList.add(e); 119 } 120 121 String evType = t.get("/MSH-9-2"); 122 if (!evType.equals(profile.getEventType()) && !profile.getEventType().equalsIgnoreCase("ALL")) { 123 HL7Exception e = 124 new ProfileNotFollowedException("Event type " + evType + " doesn't match profile type of " + profile.getEventType()); 125 exList.add(e); 126 } 127 128 String msgStruct = t.get("/MSH-9-3"); 129 if (msgStruct == null || !msgStruct.equals(profile.getMsgStructID())) { 130 HL7Exception e = 131 new ProfileNotFollowedException("Message structure " + msgStruct + " doesn't match profile type of " + profile.getMsgStructID()); 132 exList.add(e); 133 } 134 135// if (validateChildren) { 136 HL7Exception[] childExceptions; 137 childExceptions = doTestGroup(message, profile, profile.getIdentifier(), validateChildren); 138 for (int i = 0; i < childExceptions.length; i++) { 139 exList.add(childExceptions[i]); 140// } 141 } 142 143 return toArray(exList); 144 } 145 146 /** 147 * Tests a group against a group section of a profile. 148 */ 149 public HL7Exception[] testGroup(Group group, AbstractSegmentContainer profile, String profileID) throws ProfileException { 150 return doTestGroup(group, profile, profileID, true); 151 } 152 153 private HL7Exception[] doTestGroup(Group group, AbstractSegmentContainer profile, String profileID, boolean theValidateChildren) throws ProfileException { 154 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(20); 155 ArrayList<String> allowedStructures = new ArrayList<String>(20); 156 157 for (int i = 1; i <= profile.getChildren(); i++) { 158 ProfileStructure struct = profile.getChild(i); 159 160 //only test a structure in detail if it isn't X 161 if (!struct.getUsage().equalsIgnoreCase("X")) { 162 allowedStructures.add(struct.getName()); 163 164 //see which instances have content 165 try { 166 Structure[] instances = group.getAll(struct.getName()); 167 ArrayList<Structure> instancesWithContent = new ArrayList<Structure>(10); 168 for (int j = 0; j < instances.length; j++) { 169 if (hasContent(instances[j])) instancesWithContent.add(instances[j]); 170 } 171 172 HL7Exception ce = testCardinality(instancesWithContent.size(), 173 struct.getMin(), struct.getMax(), struct.getUsage(), struct.getName()); 174 if (ce != null) exList.add(ce); 175 176 //test children on instances with content 177 if (theValidateChildren) { 178 for (int j = 0; j < instancesWithContent.size(); j++) { 179 Structure s = (Structure) instancesWithContent.get(j); 180 HL7Exception[] childExceptions = testStructure(s, struct, profileID); 181 addToList(childExceptions, exList); 182 } 183 } 184 185 } catch (HL7Exception he) { 186 exList.add(new ProfileNotHL7CompliantException(struct.getName() + " not found in message")); 187 } 188 } 189 } 190 191 //complain about X structures that have content 192 addToList(checkForExtraStructures(group, allowedStructures), exList); 193 194 return toArray(exList); 195 } 196 197 /** 198 * Checks a group's children against a list of allowed structures for the group 199 * (ie those mentioned in the profile with usage other than X). Returns 200 * a list of exceptions representing structures that appear in the message 201 * but are not supposed to. 202 */ 203 private HL7Exception[] checkForExtraStructures(Group group, ArrayList<String> allowedStructures) throws ProfileException { 204 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(); 205 String[] childNames = group.getNames(); 206 for (int i = 0; i < childNames.length; i++) { 207 if (!allowedStructures.contains(childNames[i])) { 208 try { 209 Structure[] reps = group.getAll(childNames[i]); 210 for (int j = 0; j < reps.length; j++) { 211 if (hasContent(reps[j])) { 212 HL7Exception e = 213 new XElementPresentException("The structure " 214 + childNames[i] 215 + " appears in the message but not in the profile"); 216 exList.add(e); 217 } 218 } 219 } catch (HL7Exception he) { 220 throw new ProfileException("Problem checking profile", he); 221 } 222 } 223 } 224 return toArray(exList); 225 } 226 227 /** 228 * Checks cardinality and creates an appropriate exception if out 229 * of bounds. The usage code is needed because if min cardinality 230 * is > 0, the min # of reps is only required if the usage code 231 * is 'R' (see HL7 v2.5 section 2.12.6.4). 232 * @param reps the number of reps 233 * @param min the minimum number of reps 234 * @param max the maximum number of reps (-1 means *) 235 * @param usage the usage code 236 * @param name the name of the repeating structure (used in exception msg) 237 * @return null if cardinality OK, exception otherwise 238 */ 239 protected HL7Exception testCardinality(int reps, int min, int max, String usage, String name) { 240 HL7Exception e = null; 241 if (reps < min && usage.equalsIgnoreCase("R")) { 242 e = new ProfileNotFollowedException(name + " must have at least " + min + " repetitions (has " + reps + ")"); 243 } else if (max > 0 && reps > max) { 244 e = new ProfileNotFollowedException(name + " must have no more than " + max + " repetitions (has " + reps + ")"); 245 } 246 return e; 247 } 248 249 /** 250 * Tests a structure (segment or group) against the corresponding part of a profile. 251 */ 252 public HL7Exception[] testStructure(Structure s, ProfileStructure profile, String profileID) throws ProfileException { 253 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(20); 254 if (profile instanceof Seg) { 255 if (Segment.class.isAssignableFrom(s.getClass())) { 256 addToList(doTestSegment((Segment) s, (Seg) profile, profileID, validateChildren), exList); 257 } else { 258 exList.add(new ProfileNotHL7CompliantException("Mismatch between a segment in the profile and the structure " 259 + s.getClass().getName() + " in the message")); 260 } 261 } else if (profile instanceof SegGroup) { 262 if (Group.class.isAssignableFrom(s.getClass())) { 263 addToList(testGroup((Group) s, (SegGroup) profile, profileID), exList); 264 } else { 265 exList.add(new ProfileNotHL7CompliantException("Mismatch between a group in the profile and the structure " 266 + s.getClass().getName() + " in the message")); 267 } 268 } 269 return toArray(exList); 270 } 271 272 /** 273 * Tests a segment against a segment section of a profile. 274 */ 275 public HL7Exception[] testSegment(ca.uhn.hl7v2.model.Segment segment, Seg profile, String profileID) throws ProfileException { 276 return doTestSegment(segment, profile, profileID, true); 277 } 278 279 private HL7Exception[] doTestSegment(ca.uhn.hl7v2.model.Segment segment, Seg profile, String profileID, boolean theValidateChildren) throws ProfileException { 280 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(20); 281 ArrayList<Integer> allowedFields = new ArrayList<Integer>(20); 282 283 for (int i = 1; i <= profile.getFields(); i++) { 284 Field field = profile.getField(i); 285 286 //only test a field in detail if it isn't X 287 if (!field.getUsage().equalsIgnoreCase("X")) { 288 allowedFields.add(new Integer(i)); 289 290 //see which instances have content 291 try { 292 Type[] instances = segment.getField(i); 293 ArrayList<Type> instancesWithContent = new ArrayList<Type>(10); 294 for (int j = 0; j < instances.length; j++) { 295 if (hasContent(instances[j])) instancesWithContent.add(instances[j]); 296 } 297 298 HL7Exception ce = testCardinality(instancesWithContent.size(), 299 field.getMin(), field.getMax(), field.getUsage(), field.getName()); 300 if (ce != null) { 301 ce.setFieldPosition(i); 302 exList.add(ce); 303 } 304 305 //test field instances with content 306 if (theValidateChildren) { 307 for (int j = 0; j < instancesWithContent.size(); j++) { 308 Type s = (Type) instancesWithContent.get(j); 309 310 boolean escape = true; //escape field value when checking length 311 if (profile.getName().equalsIgnoreCase("MSH") && i < 3) { 312 escape = false; 313 } 314 HL7Exception[] childExceptions = doTestField(s, field, escape, profileID, validateChildren); 315 for (int k = 0; k < childExceptions.length; k++) { 316 childExceptions[k].setFieldPosition(i); 317 } 318 addToList(childExceptions, exList); 319 } 320 } 321 322 } catch (HL7Exception he) { 323 exList.add(new ProfileNotHL7CompliantException("Field " + i + " not found in message")); 324 } 325 } 326 327 } 328 329 //complain about X fields with content 330 this.addToList(checkForExtraFields(segment, allowedFields), exList); 331 332 HL7Exception[] ret = toArray(exList); 333 for (int i = 0; i < ret.length; i++) { 334 ret[i].setSegmentName(profile.getName()); 335 } 336 return ret; 337 } 338 339 /** 340 * Checks a segment against a list of allowed fields 341 * (ie those mentioned in the profile with usage other than X). Returns 342 * a list of exceptions representing field that appear 343 * but are not supposed to. 344 * @param allowedFields an array of Integers containing field #s of allowed fields 345 */ 346 private HL7Exception[] checkForExtraFields(Segment segment, ArrayList<Integer> allowedFields) throws ProfileException { 347 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(); 348 for (int i = 1; i <= segment.numFields(); i++) { 349 if (!allowedFields.contains(new Integer(i))) { 350 try { 351 Type[] reps = segment.getField(i); 352 for (int j = 0; j < reps.length; j++) { 353 if (hasContent(reps[j])) { 354 HL7Exception e = new XElementPresentException( 355 "Field " + i + " in " + segment.getName() + " appears in the message but not in the profile"); 356 exList.add(e); 357 } 358 } 359 } catch (HL7Exception he) { 360 throw new ProfileException("Problem testing against profile", he); 361 } 362 } 363 } 364 return this.toArray(exList); 365 } 366 367 /** 368 * Tests a Type against the corresponding section of a profile. 369 * @param encoded optional encoded form of type (if you want to specify this -- if null, 370 * default pipe-encoded form is used to check length and constant val) 371 */ 372 public HL7Exception[] testType(Type type, AbstractComponent profile, String encoded, String profileID) { 373 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(); 374 if (encoded == null) encoded = PipeParser.encode(type, this.enc); 375 376 HL7Exception ue = testUsage(encoded, profile.getUsage(), profile.getName()); 377 if (ue != null) exList.add(ue); 378 379 if ( !profile.getUsage().equals("X") ) { 380 //check datatype 381 String typeName = type.getName(); 382 if (!typeName.equals(profile.getDatatype())) { 383 exList.add(new ProfileNotHL7CompliantException("HL7 datatype " + typeName + " doesn't match profile datatype " + profile.getDatatype())); 384 } 385 386 //check length 387 if (encoded.length() > profile.getLength()) 388 exList.add(new ProfileNotFollowedException("The type " + profile.getName() + " has length " + encoded.length() + " which exceeds max of " + profile.getLength())); 389 390 //check constant value 391 if (profile.getConstantValue() != null && profile.getConstantValue().length() > 0) { 392 if (!encoded.equals(profile.getConstantValue())) 393 exList.add(new ProfileNotFollowedException("'" + encoded + "' doesn't equal constant value of '" + profile.getConstantValue() + "'")); 394 } 395 396 HL7Exception[] te = testTypeAgainstTable(type, profile, profileID); 397 for (int i = 0; i < te.length; i++) { 398 exList.add(te[i]); 399 } 400 } 401 402 return this.toArray(exList); 403 } 404 405 /** 406 * Tests whether the given type falls within a maximum length. 407 * @return null of OK, an HL7Exception otherwise 408 */ 409 public HL7Exception testLength(Type type, int maxLength) { 410 HL7Exception e = null; 411 String encoded = PipeParser.encode(type, this.enc); 412 if (encoded.length() > maxLength) { 413 e = new ProfileNotFollowedException("Length of " + encoded.length() + " exceeds maximum of " + maxLength); 414 } 415 return e; 416 } 417 418 /** 419 * Tests an element against the corresponding usage code. The element 420 * is required in its encoded form. 421 * @param encoded the pipe-encoded message element 422 * @param usage the usage code (e.g. "CE") 423 * @param name the name of the element (for use in exception messages) 424 * @returns null if there is no problem, an HL7Exception otherwise 425 */ 426 private HL7Exception testUsage(String encoded, String usage, String name) { 427 HL7Exception e = null; 428 if (usage.equalsIgnoreCase("R")) { 429 if (encoded.length() == 0) 430 e = new ProfileNotFollowedException("Required element " + name + " is missing"); 431 } else if (usage.equalsIgnoreCase("RE")) { 432 //can't test anything 433 } else if (usage.equalsIgnoreCase("O")) { 434 //can't test anything 435 } else if (usage.equalsIgnoreCase("C")) { 436 //can't test anything yet -- wait for condition syntax in v2.6 437 } else if (usage.equalsIgnoreCase("CE")) { 438 //can't test anything 439 } else if (usage.equalsIgnoreCase("X")) { 440 if (encoded.length() > 0) 441 e = new XElementPresentException("Element \"" + name + "\" is present but specified as not used (X)"); 442 } else if (usage.equalsIgnoreCase("B")) { 443 //can't test anything 444 } 445 return e; 446 } 447 448 /** 449 * Tests table values for ID, IS, and CE types. An empty list is returned for 450 * all other types or if the table name or number is missing. 451 */ 452 private HL7Exception[] testTypeAgainstTable(Type type, AbstractComponent profile, String profileID) { 453 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(); 454 if (profile.getTable() != null && (type.getName().equals("IS") || type.getName().equals("ID"))) { 455 String codeSystem = makeTableName(profile.getTable()); 456 String value = ((Primitive) type).getValue(); 457 addTableTestResult(exList, profileID, codeSystem, value); 458 } else if (type.getName().equals("CE")) { 459 String value = Terser.getPrimitive(type, 1, 1).getValue(); 460 String codeSystem = Terser.getPrimitive(type, 3, 1).getValue(); 461 addTableTestResult(exList, profileID, codeSystem, value); 462 463 value = Terser.getPrimitive(type, 4, 1).getValue(); 464 codeSystem = Terser.getPrimitive(type, 6, 1).getValue(); 465 addTableTestResult(exList, profileID, codeSystem, value); 466 } 467 return this.toArray(exList); 468 } 469 470 private void addTableTestResult(ArrayList<HL7Exception> exList, String profileID, String codeSystem, String value) { 471 if (codeSystem != null && value != null) { 472 HL7Exception e = testValueAgainstTable(profileID, codeSystem, value); 473 if (e != null) exList.add(e); 474 } 475 } 476 477 private HL7Exception testValueAgainstTable(String profileID, String codeSystem, String value) { 478 HL7Exception ret = null; 479 if (validateChildren == false) { 480 return ret; 481 } 482 483 CodeStore store = codeStore; 484 if (codeStore == null) { 485 store = ProfileStoreFactory.getCodeStore(profileID, codeSystem); 486 } 487 488 if (store == null) { 489 log.warn("Not checking value {}: no code store was found for profile {} code system {}" 490 , new Object[] {value, profileID, codeSystem}); 491 } else { 492 if (!store.knowsCodes(codeSystem)) { 493 log.warn("Not checking value {}: Don't have a table for code system {}", value, codeSystem); 494 } else if (!store.isValidCode(codeSystem, value)) { 495 ret = new ProfileNotFollowedException("Code '" + value + "' not found in table " + codeSystem + ", profile " + profileID); 496 } 497 } 498 return ret; 499 } 500 501 private String makeTableName(String hl7Table) { 502 StringBuffer buf = new StringBuffer("HL7"); 503 if (hl7Table.length() < 4) buf.append("0"); 504 if (hl7Table.length() < 3) buf.append("0"); 505 if (hl7Table.length() < 2) buf.append("0"); 506 buf.append(hl7Table); 507 return buf.toString(); 508 } 509 510 public HL7Exception[] testField(Type type, Field profile, boolean escape, String profileID) throws ProfileException { 511 return doTestField(type, profile, escape, profileID, true); 512 } 513 514 private HL7Exception[] doTestField(Type type, Field profile, boolean escape, String profileID, boolean theValidateChildren) throws ProfileException { 515 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(20); 516 517 //account for MSH 1 & 2 which aren't escaped 518 String encoded = null; 519 if (!escape && Primitive.class.isAssignableFrom(type.getClass())) encoded = ((Primitive) type).getValue(); 520 521 addToList(testType(type, profile, encoded, profileID), exList); 522 523 //test children 524 if (theValidateChildren) { 525 if (profile.getComponents() > 0 && !profile.getUsage().equals("X")) { 526 if (Composite.class.isAssignableFrom(type.getClass())) { 527 Composite comp = (Composite) type; 528 for (int i = 1; i <= profile.getComponents(); i++) { 529 Component childProfile = profile.getComponent(i); 530 try { 531 Type child = comp.getComponent(i-1); 532 addToList(doTestComponent(child, childProfile, profileID, validateChildren), exList); 533 } catch (DataTypeException de) { 534 exList.add(new ProfileNotHL7CompliantException("More components in profile than allowed in message: " + de.getMessage())); 535 } 536 } 537 addToList(checkExtraComponents(comp, profile.getComponents()), exList); 538 } else { 539 exList.add(new ProfileNotHL7CompliantException( 540 "A field has type primitive " + type.getClass().getName() + " but the profile defines components")); 541 } 542 } 543 } 544 545 return toArray(exList); 546 } 547 548 public HL7Exception[] testComponent(Type type, Component profile, String profileID) throws ProfileException { 549 return doTestComponent(type, profile, profileID, true); 550 } 551 552 private HL7Exception[] doTestComponent(Type type, Component profile, String profileID, boolean theValidateChildren) throws ProfileException { 553 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(20); 554 555 addToList(testType(type, profile, null, profileID), exList); 556 557 //test children 558 if (profile.getSubComponents() > 0 && !profile.getUsage().equals("X") && hasContent(type)) { 559 if (Composite.class.isAssignableFrom(type.getClass())) { 560 Composite comp = (Composite) type; 561 562 if (theValidateChildren) { 563 for (int i = 1; i <= profile.getSubComponents(); i++) { 564 SubComponent childProfile = profile.getSubComponent(i); 565 try { 566 Type child = comp.getComponent(i-1); 567 addToList(testType(child, childProfile, null, profileID), exList); 568 } catch (DataTypeException de) { 569 exList.add(new ProfileNotHL7CompliantException("More subcomponents in profile than allowed in message: " + de.getMessage())); 570 } 571 } 572 } 573 574 addToList(checkExtraComponents(comp, profile.getSubComponents()), exList); 575 } else { 576 exList.add(new ProfileNotFollowedException( 577 "A component has primitive type " + type.getClass().getName() + " but the profile defines subcomponents")); 578 } 579 } 580 581 return toArray(exList); 582 } 583 584 /** Tests for extra components (ie any not defined in the profile) */ 585 private HL7Exception[] checkExtraComponents(Composite comp, int numInProfile) throws ProfileException { 586 ArrayList<HL7Exception> exList = new ArrayList<HL7Exception>(20); 587 588 StringBuffer extra = new StringBuffer(); 589 for (int i = numInProfile; i < comp.getComponents().length; i++) { 590 try { 591 String s = PipeParser.encode(comp.getComponent(i), enc); 592 if (s.length() > 0) { 593 extra.append(s); 594 extra.append(enc.getComponentSeparator()); 595 } 596 } catch (DataTypeException de) { 597 throw new ProfileException("Problem testing against profile", de); 598 } 599 } 600 601 if (extra.toString().length() > 0) { 602 exList.add(new XElementPresentException("The following components are not defined in the profile: " + extra.toString())); 603 } 604 605 return toArray(exList); 606 } 607 608 /** 609 * Tests a composite against the corresponding section of a profile. 610 */ 611 /*public HL7Exception[] testComposite(Composite comp, AbstractComposite profile) { 612 }*/ 613 614 /** 615 * Tests a primitive datatype against a profile. Tests include 616 * length, datatype, whether the profile defines any children 617 * (this would indicate an error), constant value if defined. 618 * Table values are not verified. 619 */ 620 /*public Hl7Exception[] testPrimitive(Primitive, AbstractComponent profile) { 621 622 }*/ 623 624 /** Returns true is there is content in the given structure */ 625 private boolean hasContent(Structure struct) throws HL7Exception { 626 if (Group.class.isAssignableFrom(struct.getClass())) { 627 return hasContent( (Group) struct ); 628 } else if (Segment.class.isAssignableFrom(struct.getClass())) { 629 return hasContent( (Segment) struct ); 630 } else { 631 throw new HL7Exception("Structure " + struct.getClass().getName() + " not recognized", HL7Exception.APPLICATION_INTERNAL_ERROR); 632 } 633 } 634 635 /** Returns true is there is content in the given group */ 636 private boolean hasContent(Group group) throws HL7Exception { 637 boolean has = false; 638 String encoded = PipeParser.encode(group, enc); 639 if (encoded.indexOf('|') >= 0) has = true; 640 return has; 641 } 642 643 /** Returns true is there is content in the given segment */ 644 private boolean hasContent(Segment segment) { 645 boolean has = false; 646 String encoded = PipeParser.encode(segment, enc); 647 if (encoded != null && encoded.length() > 3) has = true; 648 return has; 649 } 650 651 /** Returns true is there is content in the given type */ 652 private boolean hasContent(Type type) { 653 boolean has = false; 654 String encoded = PipeParser.encode(type, enc); 655 if (encoded != null && encoded.length() > 0) has = true; 656 return has; 657 } 658 659 /** Appends an array of HL7 exceptions to a list */ 660 private void addToList(HL7Exception[] exceptions, ArrayList<HL7Exception> list) { 661 for (int i = 0; i < exceptions.length; i++) { 662 list.add(exceptions[i]); 663 } 664 } 665 666 /** Returns the HL7 exceptions in the given ArrayList<HL7Exception> in an array */ 667 private HL7Exception[] toArray(ArrayList<HL7Exception> list) { 668 return (HL7Exception[]) list.toArray(new HL7Exception[0]); 669 } 670 671 public static void main(String args[]) { 672 673 if (args.length != 2) { 674 System.out.println("Usage: DefaultValidator message_file profile_file"); 675 System.exit(1); 676 } 677 678 DefaultValidator val = new DefaultValidator(); 679 try { 680 String msgString = loadFile(args[0]); 681 Parser parser = new GenericParser(); 682 Message message = parser.parse(msgString); 683 684 String profileString = loadFile(args[1]); 685 ProfileParser profParser = new ProfileParser(true); 686 RuntimeProfile profile = profParser.parse(profileString); 687 688 HL7Exception[] exceptions = val.validate(message, profile.getMessage()); 689 690 System.out.println("Exceptions: "); 691 for (int i = 0; i < exceptions.length; i++) { 692 System.out.println((i + 1) + ". " + exceptions[i].getMessage()); 693 } 694 } catch (Exception e) { 695 e.printStackTrace(); 696 } 697 } 698 699 /** loads file at the given path */ 700 private static String loadFile(String path) throws IOException { 701 File file = new File(path); 702 //char[] cbuf = new char[(int) file.length()]; 703 BufferedReader in = new BufferedReader(new FileReader(file)); 704 StringBuffer buf = new StringBuffer(5000); 705 int c = -1; 706 while ( (c = in.read()) != -1) { 707 buf.append( (char) c ); 708 } 709 //in.read(cbuf, 0, (int) file.length()); 710 in.close(); 711 //return String.valueOf(cbuf); 712 return buf.toString(); 713 } 714 715}