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; 035import java.util.List; 036 037import ca.uhn.hl7v2.model.*; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import ca.uhn.hl7v2.DefaultHapiContext; 042import ca.uhn.hl7v2.HL7Exception; 043import ca.uhn.hl7v2.HapiContext; 044import ca.uhn.hl7v2.HapiContextSupport; 045import ca.uhn.hl7v2.conf.ProfileException; 046import ca.uhn.hl7v2.conf.parser.ProfileParser; 047import ca.uhn.hl7v2.conf.spec.RuntimeProfile; 048import ca.uhn.hl7v2.conf.spec.message.AbstractComponent; 049import ca.uhn.hl7v2.conf.spec.message.AbstractSegmentContainer; 050import ca.uhn.hl7v2.conf.spec.message.Component; 051import ca.uhn.hl7v2.conf.spec.message.Field; 052import ca.uhn.hl7v2.conf.spec.message.ProfileStructure; 053import ca.uhn.hl7v2.conf.spec.message.Seg; 054import ca.uhn.hl7v2.conf.spec.message.SegGroup; 055import ca.uhn.hl7v2.conf.spec.message.StaticDef; 056import ca.uhn.hl7v2.conf.spec.message.SubComponent; 057import ca.uhn.hl7v2.conf.store.CodeStore; 058import ca.uhn.hl7v2.conf.store.ProfileStoreFactory; 059import ca.uhn.hl7v2.parser.EncodingCharacters; 060import ca.uhn.hl7v2.parser.GenericParser; 061import ca.uhn.hl7v2.parser.Parser; 062import ca.uhn.hl7v2.parser.PipeParser; 063import ca.uhn.hl7v2.util.Terser; 064 065/** 066 * A default conformance profile validator. 067 * 068 * Note: this class is currently NOT thread-safe! 069 * 070 * @author Bryan Tripp 071 */ 072public class DefaultValidator extends HapiContextSupport 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 /** Creates a new instance of DefaultValidator */ 080 public DefaultValidator() { 081 this(new DefaultHapiContext()); 082 } 083 084 public DefaultValidator(HapiContext context) { 085 super(context); 086 enc = new EncodingCharacters('|', null); // the | is assumed later -- don't change 087 } 088 089 /** 090 * If set to false (default is true), each testXX and validateXX method will only test the 091 * direct object it is responsible for, not its children. 092 */ 093 public void setValidateChildren(boolean validateChildren) { 094 this.validateChildren = validateChildren; 095 } 096 097 /** 098 * <p> 099 * 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 codeStore = theCodeStore; 107 } 108 109 /** 110 * @see Validator#validate 111 */ 112 public HL7Exception[] validate(Message message, StaticDef profile) throws ProfileException, 113 HL7Exception { 114 List<HL7Exception> exList = new ArrayList<HL7Exception>(); 115 Terser t = new Terser(message); 116 117 checkMessageType(t.get("/MSH-9-1"), profile, exList); 118 checkEventType(t.get("/MSH-9-2"), profile, exList); 119 checkMessageStructure(t.get("/MSH-9-3"), profile, exList); 120 121 exList.addAll(doTestGroup(message, profile, profile.getIdentifier(), 122 validateChildren)); 123 return exList.toArray(new HL7Exception[exList.size()]); 124 } 125 126 127 protected void checkEventType(String evType, StaticDef profile, List<HL7Exception> exList) throws HL7Exception { 128 if (!evType.equals(profile.getEventType()) 129 && !profile.getEventType().equalsIgnoreCase("ALL")) { 130 HL7Exception e = new ProfileNotFollowedException("Event type " + evType 131 + " doesn't match profile type of " + profile.getEventType()); 132 exList.add(e); 133 } 134 } 135 136 protected void checkMessageType(String msgType, StaticDef profile, List<HL7Exception> exList) throws HL7Exception { 137 if (!msgType.equals(profile.getMsgType())) { 138 HL7Exception e = new ProfileNotFollowedException("Message type " + msgType 139 + " doesn't match profile type of " + profile.getMsgType()); 140 exList.add(e); 141 } 142 } 143 144 protected void checkMessageStructure(String msgStruct, StaticDef profile, List<HL7Exception> exList) { 145 if (msgStruct == null || !msgStruct.equals(profile.getMsgStructID())) { 146 HL7Exception e = new ProfileNotFollowedException("Message structure " + msgStruct 147 + " doesn't match profile type of " + profile.getMsgStructID()); 148 exList.add(e); 149 } 150 } 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 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 List<HL7Exception> exList = new ArrayList<HL7Exception>(); 163 List<String> allowedStructures = new ArrayList<String>(); 164 165 for (ProfileStructure struct : profile) { 166 167 // only test a structure in detail if it isn't X 168 if (!struct.getUsage().equalsIgnoreCase("X")) { 169 allowedStructures.add(struct.getName()); 170 171 // see which instances have content 172 try { 173 List<Structure> instancesWithContent = new ArrayList<Structure>(); 174 for (Structure instance : group.getAll(struct.getName())) { 175 if (!instance.isEmpty()) 176 instancesWithContent.add(instance); 177 } 178 179 testCardinality(instancesWithContent.size(), struct.getMin(), 180 struct.getMax(), struct.getUsage(), struct.getName(), exList); 181 182 // test children on instances with content 183 if (theValidateChildren) { 184 for (Structure s : instancesWithContent) { 185 exList.addAll(testStructure(s, struct, profileID)); 186 } 187 } 188 189 } catch (HL7Exception he) { 190 exList.add(new ProfileNotHL7CompliantException(struct.getName() 191 + " not found in message")); 192 } 193 } 194 } 195 196 // complain about X structures that have content 197 checkForExtraStructures(group, allowedStructures, exList); 198 199 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 for (String childName : group.getNames()) { 210 if (!allowedStructures.contains(childName)) { 211 try { 212 for (Structure rep : group.getAll(childName)) { 213 if (!rep.isEmpty()) { 214 HL7Exception e = new XElementPresentException("The structure " 215 + childName + " 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 } 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 HL7Exception e = null; 240 if (reps < min && usage.equalsIgnoreCase("R")) { 241 e = new ProfileNotFollowedException(name + " must have at least " + min 242 + " repetitions (has " + reps + ")"); 243 } else if (max > 0 && reps > max) { 244 e = new ProfileNotFollowedException(name + " must have no more than " + max 245 + " repetitions (has " + reps + ")"); 246 } 247 if (e != null) exList.add(e); 248 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 List<HL7Exception> exList = new ArrayList<HL7Exception>(); 257 if (profile instanceof Seg) { 258 if (Segment.class.isAssignableFrom(s.getClass())) { 259 exList.addAll(doTestSegment((Segment) s, (Seg) profile, profileID, validateChildren)); 260 } else { 261 exList.add(new ProfileNotHL7CompliantException( 262 "Mismatch between a segment in the profile and the structure " 263 + s.getClass().getName() + " in the message")); 264 } 265 } else if (profile instanceof SegGroup) { 266 if (Group.class.isAssignableFrom(s.getClass())) { 267 exList.addAll(testGroup((Group) s, (SegGroup) profile, profileID)); 268 } else { 269 exList.add(new ProfileNotHL7CompliantException( 270 "Mismatch between a group in the profile and the structure " 271 + s.getClass().getName() + " in the message")); 272 } 273 } 274 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 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 List<HL7Exception> exList = new ArrayList<HL7Exception>(); 288 List<Integer> allowedFields = new ArrayList<Integer>(); 289 290 for (int i = 1; i <= profile.getFields(); i++) { 291 Field field = profile.getField(i); 292 293 // only test a field in detail if it isn't X 294 if (!field.getUsage().equalsIgnoreCase("X")) { 295 allowedFields.add(i); 296 297 // see which instances have content 298 try { 299 Type[] instances = segment.getField(i); 300 List<Type> instancesWithContent = new ArrayList<Type>(); 301 for (Type instance : instances) { 302 if (!instance.isEmpty()) 303 instancesWithContent.add(instance); 304 } 305 306 HL7Exception ce = testCardinality(instancesWithContent.size(), field.getMin(), 307 field.getMax(), field.getUsage(), field.getName(), exList); 308 if (ce != null) { 309 ce.setFieldPosition(i); 310 } 311 312 // test field instances with content 313 if (theValidateChildren) { 314 for (Type s : instancesWithContent) { 315 // escape field value when checking length 316 boolean escape = !(profile.getName().equalsIgnoreCase("MSH") && i < 3); 317 List<HL7Exception> childExceptions = doTestField(s, field, escape, 318 profileID, validateChildren); 319 for (HL7Exception ex : childExceptions) { 320 ex.setFieldPosition(i); 321 } 322 exList.addAll(childExceptions); 323 } 324 } 325 326 } catch (HL7Exception he) { 327 exList.add(new ProfileNotHL7CompliantException("Field " + i 328 + " not found in message")); 329 } 330 } 331 332 } 333 334 // complain about X fields with content 335 checkForExtraFields(segment, allowedFields, exList); 336 337 for (HL7Exception ex : exList) { 338 ex.setSegmentName(profile.getName()); 339 } 340 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 for (int i = 1; i <= segment.numFields(); i++) { 353 if (!allowedFields.contains(i)) { 354 try { 355 Type[] reps = segment.getField(i); 356 for (Type rep : reps) { 357 if (!rep.isEmpty()) { 358 HL7Exception e = new XElementPresentException("Field " + i + " in " 359 + segment.getName() 360 + " appears in the message but not in the profile"); 361 exList.add(e); 362 } 363 } 364 } catch (HL7Exception he) { 365 throw new ProfileException("Problem testing against profile", he); 366 } 367 } 368 } 369 } 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 List<HL7Exception> exList = new ArrayList<HL7Exception>(); 380 if (encoded == null) 381 encoded = PipeParser.encode(type, this.enc); 382 383 testUsage(encoded, profile.getUsage(), profile.getName(), exList); 384 385 if (!profile.getUsage().equals("X")) { 386 checkDataType(profile.getDatatype(), type, exList); 387 checkLength(profile.getLength(), profile.getName(), encoded, exList); 388 checkConstantValue(profile.getConstantValue(), encoded, exList); 389 390 testTypeAgainstTable(type, profile, profileID, exList); 391 } 392 393 return exList; 394 } 395 396 protected void checkConstantValue(String value, String encoded, List<HL7Exception> exList) { 397 // check constant value 398 if (value != null && value.length() > 0) { 399 if (!encoded.equals(value)) 400 exList.add(new ProfileNotFollowedException("'" + encoded 401 + "' doesn't equal constant value of '" + value + "'")); 402 } 403 } 404 405 protected void checkLength(long length, String name, String encoded, List<HL7Exception> exList) { 406 // check length 407 if (encoded.length() > length) 408 exList.add(new ProfileNotFollowedException("The type " + name 409 + " has length " + encoded.length() + " which exceeds max of " 410 + length)); 411 } 412 413 protected void checkDataType(String dataType, Type type, List<HL7Exception> exList) { 414 // check datatype 415 String typeName = type.getName(); 416 if (!(type instanceof Varies || typeName.equals(dataType))) { 417 exList.add(new ProfileNotHL7CompliantException("HL7 datatype " + typeName 418 + " doesn't match profile datatype " + dataType)); 419 } 420 } 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 HL7Exception e = null; 429 String encoded = PipeParser.encode(type, this.enc); 430 if (encoded.length() > maxLength) { 431 e = new ProfileNotFollowedException("Length of " + encoded.length() 432 + " exceeds maximum of " + maxLength); 433 } 434 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 if (usage.equalsIgnoreCase("R")) { 448 if (encoded.length() == 0) 449 exList.add(new ProfileNotFollowedException("Required element " + name + " is missing")); 450 } else if (usage.equalsIgnoreCase("RE")) { 451 // can't test anything 452 } else if (usage.equalsIgnoreCase("O")) { 453 // can't test anything 454 } else if (usage.equalsIgnoreCase("C")) { 455 // can't test anything yet -- wait for condition syntax in v2.6 456 } else if (usage.equalsIgnoreCase("CE")) { 457 // can't test anything 458 } else if (usage.equalsIgnoreCase("X")) { 459 if (encoded.length() > 0) 460 exList.add(new XElementPresentException("Element \"" + name 461 + "\" is present but specified as not used (X)")); 462 } else if (usage.equalsIgnoreCase("B")) { 463 // can't test anything 464 } 465 } 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 if (profile.getTable() != null 474 && (type.getName().equals("IS") || type.getName().equals("ID"))) { 475 String codeSystem = String.format("HL7%1$4s", profile.getTable()).replace(" ", "0"); 476 String value = ((Primitive) type).getValue(); 477 addTableTestResult(profileID, codeSystem, value, exList); 478 } else if (type.getName().equals("CE")) { 479 String value = Terser.getPrimitive(type, 1, 1).getValue(); 480 String codeSystem = Terser.getPrimitive(type, 3, 1).getValue(); 481 addTableTestResult(profileID, codeSystem, value, exList); 482 483 value = Terser.getPrimitive(type, 4, 1).getValue(); 484 codeSystem = Terser.getPrimitive(type, 6, 1).getValue(); 485 addTableTestResult(profileID, codeSystem, value, exList); 486 } 487 } 488 489 protected void addTableTestResult(String profileID, String codeSystem, String value, List<HL7Exception> exList) { 490 if (codeSystem != null && value != null && validateChildren) { 491 testValueAgainstTable(profileID, codeSystem, value, exList); 492 } 493 } 494 495 protected void testValueAgainstTable(String profileID, String codeSystem, String value, List<HL7Exception> exList) { 496 CodeStore store = codeStore; 497 if (codeStore == null) { 498 store = getHapiContext().getCodeStoreRegistry().getCodeStore(profileID, codeSystem); 499 } 500 501 if (store == null) { 502 log.info( 503 "Not checking value {}: no code store was found for profile {} code system {}", 504 new Object[] { value, profileID, codeSystem }); 505 } else { 506 if (!store.knowsCodes(codeSystem)) { 507 log.warn("Not checking value {}: Don't have a table for code system {}", value, 508 codeSystem); 509 } else if (!store.isValidCode(codeSystem, value)) { 510 exList.add(new ProfileNotFollowedException("Code '" + value + "' not found in table " 511 + codeSystem + ", profile " + profileID)); 512 } 513 } 514 515 } 516 517 public List<HL7Exception> testField(Type type, Field profile, boolean escape, String profileID) 518 throws ProfileException, HL7Exception { 519 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 List<HL7Exception> exList = new ArrayList<HL7Exception>(); 525 526 // account for MSH 1 & 2 which aren't escaped 527 String encoded = null; 528 if (!escape && Primitive.class.isAssignableFrom(type.getClass())) 529 encoded = ((Primitive) type).getValue(); 530 531 exList.addAll(testType(type, profile, encoded, profileID)); 532 533 // test children 534 if (theValidateChildren) { 535 if (profile.getComponents() > 0 && !profile.getUsage().equals("X")) { 536 if (Composite.class.isAssignableFrom(type.getClass())) { 537 Composite comp = (Composite) type; 538 for (int i = 1; i <= profile.getComponents(); i++) { 539 Component childProfile = profile.getComponent(i); 540 try { 541 Type child = comp.getComponent(i - 1); 542 exList.addAll(doTestComponent(child, childProfile, profileID, validateChildren)); 543 } catch (DataTypeException de) { 544 exList.add(new ProfileNotHL7CompliantException( 545 "More components in profile than allowed in message: " 546 + de.getMessage())); 547 } 548 } 549 checkExtraComponents(comp, profile.getComponents(), exList); 550 } else { 551 exList.add(new ProfileNotHL7CompliantException("A field has type primitive " 552 + type.getClass().getName() + " but the profile defines components")); 553 } 554 } 555 } 556 557 return exList; 558 } 559 560 public List<HL7Exception> testComponent(Type type, Component profile, String profileID) 561 throws ProfileException, HL7Exception { 562 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 List<HL7Exception> exList = new ArrayList<HL7Exception>(); 568 exList.addAll(testType(type, profile, null, profileID)); 569 570 // test children 571 if (profile.getSubComponents() > 0 && !profile.getUsage().equals("X") && (!type.isEmpty())) { 572 if (Composite.class.isAssignableFrom(type.getClass())) { 573 Composite comp = (Composite) type; 574 575 if (theValidateChildren) { 576 for (int i = 1; i <= profile.getSubComponents(); i++) { 577 SubComponent childProfile = profile.getSubComponent(i); 578 try { 579 Type child = comp.getComponent(i - 1); 580 exList.addAll(testType(child, childProfile, null, profileID)); 581 } catch (DataTypeException de) { 582 exList.add(new ProfileNotHL7CompliantException( 583 "More subcomponents in profile than allowed in message: " 584 + de.getMessage())); 585 } 586 } 587 } 588 589 checkExtraComponents(comp, profile.getSubComponents(), exList); 590 } else { 591 exList.add(new ProfileNotFollowedException("A component has primitive type " 592 + type.getClass().getName() + " but the profile defines subcomponents")); 593 } 594 } 595 596 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 StringBuilder extra = new StringBuilder(); 603 for (int i = numInProfile; i < comp.getComponents().length; i++) { 604 try { 605 String s = PipeParser.encode(comp.getComponent(i), enc); 606 if (s.length() > 0) { 607 extra.append(s).append(enc.getComponentSeparator()); 608 } 609 } catch (DataTypeException de) { 610 throw new ProfileException("Problem testing against profile", de); 611 } 612 } 613 614 if (extra.length() > 0) { 615 exList.add(new XElementPresentException( 616 "The following components are not defined in the profile: " + extra.toString())); 617 } 618 619 } 620 621 public static void main(String args[]) { 622 623 if (args.length != 2) { 624 System.out.println("Usage: DefaultValidator message_file profile_file"); 625 System.exit(1); 626 } 627 628 DefaultValidator val = new DefaultValidator(); 629 try { 630 String msgString = loadFile(args[0]); 631 Parser parser = new GenericParser(); 632 Message message = parser.parse(msgString); 633 634 String profileString = loadFile(args[1]); 635 ProfileParser profParser = new ProfileParser(true); 636 RuntimeProfile profile = profParser.parse(profileString); 637 638 HL7Exception[] exceptions = val.validate(message, profile.getMessage()); 639 640 System.out.println("Exceptions: "); 641 for (int i = 0; i < exceptions.length; i++) { 642 System.out.println((i + 1) + ". " + exceptions[i].getMessage()); 643 } 644 } catch (Exception e) { 645 e.printStackTrace(); 646 } 647 } 648 649 /** loads file at the given path */ 650 private static String loadFile(String path) throws IOException { 651 File file = new File(path); 652 // char[] cbuf = new char[(int) file.length()]; 653 BufferedReader in = new BufferedReader(new FileReader(file)); 654 StringBuffer buf = new StringBuffer(5000); 655 int c; 656 while ((c = in.read()) != -1) { 657 buf.append((char) c); 658 } 659 // in.read(cbuf, 0, (int) file.length()); 660 in.close(); 661 // return String.valueOf(cbuf); 662 return buf.toString(); 663 } 664 665}