View Javadoc

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 "Parser.java".  Description: 
10  "Parses HL7 message Strings into HL7 Message objects and 
11    encodes HL7 Message objects into HL7 message Strings" 
12  
13  The Initial Developer of the Original Code is University Health Network. Copyright (C) 
14  2001.  All Rights Reserved. 
15  
16  Contributor(s): ______________________________________. 
17  
18  Alternatively, the contents of this file may be used under the terms of the 
19  GNU General Public License (the "GPL"), in which case the provisions of the GPL are 
20  applicable instead of those above.  If you wish to allow use of your version of this 
21  file only under the terms of the GPL and not to allow others to use your version 
22  of this file under the MPL, indicate your decision by deleting  the provisions above 
23  and replace  them with the notice and other provisions required by the GPL License.  
24  If you do not delete the provisions above, a recipient may use your version of 
25  this file under either the MPL or the GPL. 
26  */
27  
28  package ca.uhn.hl7v2.parser;
29  
30  import java.lang.reflect.Constructor;
31  
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import ca.uhn.hl7v2.DefaultHapiContext;
36  import ca.uhn.hl7v2.ErrorCode;
37  import ca.uhn.hl7v2.HL7Exception;
38  import ca.uhn.hl7v2.HapiContext;
39  import ca.uhn.hl7v2.HapiContextSupport;
40  import ca.uhn.hl7v2.Version;
41  import ca.uhn.hl7v2.model.AbstractSuperMessage;
42  import ca.uhn.hl7v2.model.GenericMessage;
43  import ca.uhn.hl7v2.model.GenericSegment;
44  import ca.uhn.hl7v2.model.Group;
45  import ca.uhn.hl7v2.model.Message;
46  import ca.uhn.hl7v2.model.Segment;
47  import ca.uhn.hl7v2.model.Type;
48  import ca.uhn.hl7v2.util.ReflectionUtil;
49  import ca.uhn.hl7v2.util.StringUtil;
50  import ca.uhn.hl7v2.util.Terser;
51  import ca.uhn.hl7v2.validation.ValidationContext;
52  import ca.uhn.hl7v2.validation.ValidationExceptionHandler;
53  import ca.uhn.hl7v2.validation.ValidationExceptionHandlerFactory;
54  import ca.uhn.hl7v2.validation.Validator;
55  
56  /**
57   * Parses HL7 message Strings into HL7 Message objects and encodes HL7 Message objects into HL7
58   * message Strings.
59   * 
60   * @author Bryan Tripp (bryan_tripp@sourceforge.net)
61   * @author Christian Ohr
62   */
63  public abstract class Parser extends HapiContextSupport {
64  
65  	private static final Logger log = LoggerFactory.getLogger(Parser.class);
66  	
67  	/**
68  	 * Uses DefaultModelClassFactory for model class lookup.
69  	 */
70  	public Parser() {
71  		this(new DefaultHapiContext());
72  	}
73  
74  	/**
75  	 * Creates a new parser, using the {@link ModelClassFactory}, the {@link ParserConfiguration}
76  	 * and the {@link ValidationContext} as defined in the context.
77  	 * 
78  	 * @param context HapiContext
79  	 */
80  	public Parser(HapiContext context) {
81  		super(context);
82  	}
83  
84  	/**
85  	 * Initialize parser with custom ModelClassFactory and default ValidationContext
86  	 * 
87  	 * @param modelClassFactory custom factory to use for model class lookup
88  	 */
89  	public Parser(ModelClassFactory modelClassFactory) {
90  		this(new DefaultHapiContext(modelClassFactory));
91  	}
92  
93  	/**
94  	 * @return the factory used by this Parser for model class lookup
95  	 */
96  	public ModelClassFactory getFactory() {
97  		return getHapiContext().getModelClassFactory();
98  	}
99  
100 	/**
101 	 * @return the set of validation rules that is applied to messages parsed or encoded by this
102 	 *         parser. Note that this method may return <code>null</code>
103 	 */
104 	public ValidationContext getValidationContext() {
105 		return isValidating() ? getHapiContext().getValidationContext() : null;
106 	}
107 
108 	/**
109 	 * @param context the set of validation rules to be applied to messages parsed or encoded by
110 	 *            this parser (defaults to ValidationContextFactory.DefaultValidation)
111 	 * 
112 	 * @deprecated use a dedicated {@link HapiContext} and set its ValidationContext property
113 	 */
114 	public void setValidationContext(ValidationContext context) {
115 		HapiContext newContext = new DefaultHapiContext(getHapiContext());
116 		newContext.setValidationContext(context);
117 		setHapiContext(newContext);
118 	}
119 
120 	/**
121 	 * <p>
122 	 * Returns the parser configuration. This is a bean which contains configuration
123 	 * instructions relating to how a parser should be parsing or encoding messages it deals
124 	 * with.
125 	 * </p>
126 	 * <p>
127 	 * <b>Note that the parser configuration comes from the {@link #getHapiContext() HAPI Context}.</b>
128 	 * Changes to the configuration for one parser will affect all parsers which share the same
129 	 * context.
130 	 * </p>
131      *
132      * @return the current parser configuration
133 	 */
134 	public ParserConfiguration getParserConfiguration() {
135 		return getHapiContext().getParserConfiguration();
136 	}
137 
138 	/**
139 	 * Sets the parser configuration for this parser (may not be null). This is a bean which
140 	 * contains configuration instructions relating to how a parser should be parsing or encoding
141 	 * messages it deals with.
142 	 * 
143 	 * @param configuration The parser configuration
144 	 * 
145 	 * @deprecated use a dedicated {@link HapiContext} and set its ParserConfiguration property
146 	 */
147 	public void setParserConfiguration(ParserConfiguration configuration) {
148 		HapiContext newContext = new DefaultHapiContext(getHapiContext());
149 		newContext.setParserConfiguration(configuration);
150 		setHapiContext(newContext);
151 	}
152 
153 	/**
154 	 * Returns a String representing the encoding of the given message, if the encoding is
155 	 * recognized. For example if the given message appears to be encoded using HL7 2.x XML rules
156 	 * then "XML" would be returned. If the encoding is not recognized then null is returned. That
157 	 * this method returns a specific encoding does not guarantee that the message is correctly
158 	 * encoded (e.g. well formed XML) - just that it is not encoded using any other encoding than
159 	 * the one returned. Returns null if the encoding is not recognized.
160      *
161      * @param message message string
162      * @return string representing the encoding of the given message, i.e. "XML" or "ER7"
163 	 */
164 	public abstract String getEncoding(String message);
165 
166 	/**
167 	 * Returns true if and only if the given encoding is supported by this Parser.
168      * @param encoding the encoding, "XML" or "ER7"
169      * @return true if this parser supports parsing message encoded this way
170 	 */
171 	public boolean supportsEncoding(String encoding) {
172 		return getDefaultEncoding().equalsIgnoreCase(encoding);
173 	}
174 	
175 	/**
176 	 * @return the preferred encoding of this Parser ("XML" or "ER7")
177 	 */
178 	public abstract String getDefaultEncoding();
179 
180 	/**
181 	 * Parses a message string and returns the corresponding Message object.
182 	 * 
183 	 * @param message a String that contains an HL7 message
184 	 * @return a HAPI Message object parsed from the given String
185 	 * @throws HL7Exception if the message is not correctly formatted.
186 	 * @throws EncodingNotSupportedException if the message encoded is not supported by this parser.
187 	 */
188 	public Message parse(String message) throws HL7Exception {
189 		String encoding = getEncoding(message);
190 		if (!supportsEncoding(encoding)) {
191 			String startOfMessage = null;
192 			if (message.startsWith("MSH")) {
193 				int indexOfCR = message.indexOf('\r');
194 				if (indexOfCR > 0) {
195 					startOfMessage = message.substring(0, indexOfCR);
196 				}
197 			} 
198 			if (startOfMessage == null) {
199 				startOfMessage = message.substring(0, Math.min(message.length(), 50));
200 			}
201 			throw new EncodingNotSupportedException("Determine encoding for message. The following is the first 50 chars of the message for reference, although this may not be where the issue is: "
202 					+ startOfMessage);
203 		}
204 
205 		String version = getVersion(message);
206 		
207 		if (!getParserConfiguration().isAllowUnknownVersions()) {
208 			assertVersionExists(version);
209 		}
210 
211 		assertMessageValidates(message, encoding, version);
212 		Message result = doParse(message, version);
213 		assertMessageValidates(result);
214 
215 		result.setParser(this);
216 
217 		applySuperStructureName(result);
218 		
219 		return result;
220 	}
221 
222 	/**
223 	 * Called by parse() to perform implementation-specific parsing work.
224 	 * 
225 	 * @param message a String that contains an HL7 message
226 	 * @param version the name of the HL7 version to which the message belongs (eg "2.5")
227 	 * @return a HAPI Message object parsed from the given String
228 	 * @throws HL7Exception if the message is not correctly formatted.
229 	 * @throws EncodingNotSupportedException if the message encoded is not supported by this parser.
230 	 */
231 	protected abstract Message doParse(String message, String version) throws HL7Exception;
232 
233 	/**
234 	 * Formats a Message object into an HL7 message string using the given encoding.
235 	 * 
236 	 * @param source a Message object from which to construct an encoded message string
237 	 * @param encoding the name of the HL7 encoding to use (eg "XML"; most implementations support
238 	 *            only one encoding)
239 	 * @return the encoded message
240 	 * @throws HL7Exception if the data fields in the message do not permit encoding (e.g. required
241 	 *             fields are null)
242 	 * @throws EncodingNotSupportedException if the requested encoding is not supported by this
243 	 *             parser.
244 	 */
245 	public String encode(Message source, String encoding) throws HL7Exception {
246 		assertMessageValidates(source);
247 		String result = doEncode(source, encoding);
248 	    assertMessageValidates(result, encoding, source.getVersion());
249 		return result;
250 	}
251 
252 	/**
253 	 * Called by encode(Message, String) to perform implementation-specific encoding work.
254 	 * 
255 	 * @param source a Message object from which to construct an encoded message string
256 	 * @param encoding the name of the HL7 encoding to use (eg "XML"; most implementations support
257 	 *            only one encoding)
258 	 * @return the encoded message
259 	 * @throws HL7Exception if the data fields in the message do not permit encoding (e.g. required
260 	 *             fields are null)
261 	 * @throws EncodingNotSupportedException if the requested encoding is not supported by this
262 	 *             parser.
263 	 */
264 	protected abstract String doEncode(Message source, String encoding) throws HL7Exception;
265 
266 	/**
267 	 * Formats a Message object into an HL7 message string using this parser's default encoding.
268 	 * 
269 	 * @param source a Message object from which to construct an encoded message string
270 	 * @return the encoded message
271 	 * @throws HL7Exception if the data fields in the message do not permit encoding (e.g. required
272 	 *             fields are null)
273 	 */
274 	public String encode(Message source) throws HL7Exception {
275 		assertMessageValidates(source);
276 		String result = doEncode(source);
277 		assertMessageValidates(result, getDefaultEncoding(), source.getVersion());
278 		return result;
279 	}
280 
281 	/**
282 	 * Called by encode(Message) to perform implementation-specific encoding work.
283 	 * 
284 	 * @param source a Message object from which to construct an encoded message string
285 	 * @return the encoded message
286 	 * @throws HL7Exception if the data fields in the message do not permit encoding (e.g. required
287 	 *             fields are null)
288 	 * @throws EncodingNotSupportedException if the requested encoding is not supported by this
289 	 *             parser.
290 	 */
291 	protected abstract String doEncode(Message source) throws HL7Exception;
292 
293 	/**
294 	 * <p>
295 	 * Returns a minimal amount of data from a message string, including only the data needed to
296 	 * send a response to the remote system. This includes the following fields:
297 	 * <ul>
298 	 * <li>field separator</li>
299 	 * <li>encoding characters</li>
300 	 * <li>processing ID</li>
301 	 * <li>message control ID</li>
302 	 * </ul>
303 	 * This method is intended for use when there is an error parsing a message, (so the Message
304 	 * object is unavailable) but an error message must be sent back to the remote system including
305 	 * some of the information in the inbound message. This method parses only that required
306 	 * information, hopefully avoiding the condition that caused the original error.
307 	 * </p>
308 	 *
309      * @param message the message
310 	 * @return an MSH segment
311      * @throws HL7Exception if no MSH segment could be created
312 	 */
313 	public abstract Segment getCriticalResponseData(String message) throws HL7Exception;
314 
315 	/**
316 	 * For response messages, returns the value of MSA-2 (the message ID of the message sent by the
317 	 * sending system). This value may be needed prior to main message parsing, so that
318 	 * (particularly in a multi-threaded scenario) the message can be routed to the thread that sent
319 	 * the request. We need this information first so that any parse exceptions are thrown to the
320 	 * correct thread. Implementers of Parsers should take care to make the implementation of this
321 	 * method very fast and robust. Returns null if MSA-2 can not be found (e.g. if the message is
322 	 * not a response message).
323      *
324      * @param message the message
325      * @return the value of MSA-2
326 	 */
327 	public abstract String getAckID(String message);
328 
329 	/**
330 	 * Returns the version ID (MSH-12) from the given message, without fully parsing the message.
331 	 * The version is needed prior to parsing in order to determine the message class into which the
332 	 * text of the message should be parsed.
333 	 *
334      * @param message the message
335      * @return the value of MSH-12
336 	 * @throws HL7Exception if the version field can not be found.
337 	 */
338 	public abstract String getVersion(String message) throws HL7Exception;
339 
340 	/**
341 	 * Encodes a particular segment and returns the encoded structure
342 	 * 
343 	 * @param structure The structure to encode
344 	 * @param encodingCharacters The encoding characters
345 	 * @return The encoded segment
346 	 * @throws HL7Exception If there is a problem encoding
347 	 * @since 1.0
348 	 */
349 	public abstract String doEncode(Segment structure, EncodingCharacters encodingCharacters)
350 			throws HL7Exception;
351 
352 	/**
353 	 * Encodes a particular type and returns the encoded structure
354 	 * 
355 	 * @param type The type to encode
356 	 * @param encodingCharacters The encoding characters
357 	 * @return The encoded type
358 	 * @throws HL7Exception If there is a problem encoding
359 	 * @since 1.0
360 	 */
361 	public abstract String doEncode(Type type, EncodingCharacters encodingCharacters)
362 			throws HL7Exception;
363 
364 	/**
365 	 * Parses a particular type and returns the encoded structure
366 	 * 
367 	 * @param string The string to parse
368 	 * @param type The type to encode
369 	 * @param encodingCharacters The encoding characters
370 	 * @throws HL7Exception If there is a problem encoding
371 	 * @since 1.0
372 	 */
373 	public abstract void parse(Type type, String string, EncodingCharacters encodingCharacters)
374 			throws HL7Exception;
375 
376 	/**
377 	 * Parse a message using a specific model package instead of the default, using
378 	 * {@link ModelClassFactory#getMessageClassInASpecificPackage(String, String, boolean, String)}
379 	 * .
380 	 * 
381 	 * <b>WARNING: This method is only implemented in some parser implementations</b>. Currently it
382 	 * will only work with the PipeParser parser implementation. Use with caution.
383      *
384      * @param message message string
385      * @param packageName name of the package of the models
386      * @return parsed message
387      * @throws HL7Exception if an error occurred while parsing
388 	 */
389 	public Message parseForSpecificPackage(String message, String packageName) throws HL7Exception {
390 		String encoding = getEncoding(message);
391 		if (!supportsEncoding(encoding)) {
392 			throw new EncodingNotSupportedException("Can't parse message beginning "
393 					+ message.substring(0, Math.min(message.length(), 50)));
394 		}
395 
396 		String version = getVersion(message);
397 		assertVersionExists(version);
398 
399 		assertMessageValidates(message, encoding, version);
400 		Message result = doParseForSpecificPackage(message, version, packageName);
401 		assertMessageValidates(result);
402 
403 		result.setParser(this);
404 		return result;
405 	}
406 
407 	/**
408 	 * Attempt the parse a message using a specific model package
409 	 */
410 	protected abstract Message doParseForSpecificPackage(String message, String version,
411 			String packageName) throws HL7Exception;
412 
413 	/**
414 	 * Instantiate a message type using a specific package name
415 	 * 
416 	 * @see ModelClassFactory#getMessageClassInASpecificPackage(String, String, boolean, String)
417 	 */
418 	protected Message instantiateMessageInASpecificPackage(String theName, String theVersion,
419 		boolean isExplicit, String packageName) throws HL7Exception {
420 		Class<? extends Message> messageClass = getFactory().getMessageClassInASpecificPackage(
421 				theName, theVersion, isExplicit, packageName);
422 		if (messageClass == null) {
423 			throw new HL7Exception("Can't find message class in current package list: " + theName);
424 		}
425 	    return ReflectionUtil.instantiateMessage(messageClass, getFactory());
426 	}
427 
428 	/**
429 	 * Parses a particular segment and returns the encoded structure
430 	 *
431      * @param segment The segment to encode
432      * @param string The string to parse
433 	 * @param encodingCharacters The encoding characters
434 	 * @throws HL7Exception If there is a problem encoding
435 	 */
436 	public abstract void parse(Segment segment, String string, EncodingCharacters encodingCharacters)
437 			throws HL7Exception;
438 
439 	/**
440 	 * Parses a particular message and returns the encoded structure
441 	 *
442      * @param message The message to encode
443      * @param string The string to parse
444 	 * @throws HL7Exception If there is a problem encoding
445 	 * @since 1.0
446 	 */
447 	public abstract void parse(Message message, String string) throws HL7Exception;
448 
449 	/**
450 	 * <p>
451 	 * Creates a version-specific MSH object and returns it as a
452 	 * version-independent MSH interface. 
453 	 * </p>
454 	 * <p>
455 	 * Since HAPI 2.1, if a version specific MSH
456 	 * segment can't be found (for example because the specific
457 	 * structure JAR is not found on the classpath), an instance of
458 	 * {@link GenericSegment} is returned.
459 	 * </p>
460      *
461      * @param version HL7 version
462      * @param factory model class factory to be used
463      * @return MSH segment for this version returned by the model class factory
464      * @throws HL7Exception if no matching segment could be found
465 	 */
466 	public static Segment makeControlMSH(String version, ModelClassFactory factory)
467 			throws HL7Exception {
468 		Segment msh;
469 
470 		try {
471 			Class<? extends Message> genericMessageClass;
472 			genericMessageClass = GenericMessage.getGenericMessageClass(version);
473 			
474 			Message dummy = genericMessageClass
475 					.getConstructor(new Class[] { ModelClassFactory.class })
476 					.newInstance(factory);
477 
478 			Class<? extends Segment> c = null;
479 			
480 			if (Version.supportsVersion(version)) {
481 				c = factory.getSegmentClass("MSH", version);
482 			}
483 			
484 			if (c != null) {
485 				if (GenericSegment.class.isAssignableFrom(c)) {
486 					Class<?>[] constructorParamTypes = { Group.class, String.class };
487 					Object[] constructorParamArgs = { dummy, "MSH" };
488 					Constructor<? extends Segment> constructor = c.getConstructor(constructorParamTypes);
489 					msh = constructor.newInstance(constructorParamArgs);
490 				} else {
491 					Class<?>[] constructorParamTypes = { Group.class, ModelClassFactory.class };
492 					Object[] constructorParamArgs = { dummy, factory };
493 					Constructor<? extends Segment> constructor = c.getConstructor(constructorParamTypes);
494 					msh = constructor.newInstance(constructorParamArgs);
495 				}
496 			} else {
497 				msh = new GenericSegment(dummy, "MSH");    	
498 			}
499 		} catch (Exception e) {
500 			throw new HL7Exception("Couldn't create MSH for version " + version
501 					+ " (does your classpath include this version?) ... ", e);
502 		}
503 		return msh;
504 	}
505 
506 	/**
507 	 * Returns true if the given string represents a valid 2.x version. Valid versions include
508 	 * "2.1", "2.2", "2.3", "2.3.1", "2.4", "2.5", "2.5.1", "2.6"
509 	 *
510 	 * @param version HL7 version string
511 	 * @return <code>true</code> if version is known
512 	 * @deprecated Use {@link Version#supportsVersion(String)}
513 	 */
514 	@Deprecated
515 	public static boolean validVersion(String version) {
516 		return Version.supportsVersion(version);
517 	}
518 	
519 	/**
520 	 * Like {@link #validVersion(String)} but throws an HL7Exception instead
521 	 * 
522 	 * @param version HL7 version
523 	 * @throws HL7Exception if version is unknown
524 	 */
525 	public static void assertVersionExists(String version) throws HL7Exception {
526 		if (!Version.supportsVersion(version))
527             throw new HL7Exception(
528                     "The HL7 version " + version + " is not recognized",
529                     ErrorCode.UNSUPPORTED_VERSION_ID);
530 	}
531 
532 	/**
533 	 * Given a concatenation of message type and event (e.g. ADT_A04), and the version, finds the
534 	 * corresponding message structure (e.g. ADT_A01). This is needed because some events share
535 	 * message structures, although it is not needed when the message structure is explicitly valued
536 	 * in MSH-9-3. If no mapping is found, returns the original name.
537 	 * 
538 	 * @throws HL7Exception if there is an error retrieving the map, or if the given version is
539 	 *             invalid
540 	 *             
541 	 * @deprecated use {@link ModelClassFactory#getMessageStructureForEvent(String, Version)}
542 	 */
543 	public String getMessageStructureForEvent(String name, String version)
544 			throws HL7Exception {
545 		assertVersionExists(version);
546 		return getHapiContext().getModelClassFactory().
547 				getMessageStructureForEvent(name, Version.versionOf(version));
548 	}
549 
550 	/**
551 	 * Note that the validation context of the resulting message is set to this parser's validation
552 	 * context. The validation context is used within Primitive.setValue().
553 	 * 
554 	 * @param theName name of the desired structure in the form XXX_YYY
555 	 * @param theVersion HL7 version (e.g. "2.3")
556 	 * @param isExplicit true if the structure was specified explicitly in MSH-9-3, false if it was
557 	 *            inferred from MSH-9-1 and MSH-9-2. If false, a lookup may be performed to find an
558 	 *            alternate structure corresponding to that message type and event.
559 	 * @return a Message instance
560 	 * @throws HL7Exception if the version is not recognized or no appropriate class can be found or
561 	 *             the Message class throws an exception on instantiation (e.g. if args are not as
562 	 *             expected)
563 	 */
564 	protected Message instantiateMessage(String theName, String theVersion, boolean isExplicit)
565 			throws HL7Exception {
566 		Class<? extends Message> messageClass = getFactory().getMessageClass(theName, theVersion, isExplicit);
567 		if (messageClass == null)
568 			throw new HL7Exception("Can't find message class in current package list: " + theName);
569 		return ReflectionUtil.instantiateMessage(messageClass, getFactory());
570 	}
571 	
572 	protected void applySuperStructureName(Message theMessage) throws HL7Exception {
573         if (theMessage instanceof AbstractSuperMessage) {
574         	if (theMessage.getName() == null) {
575         		Terser t = new Terser(theMessage);
576         		String name = null;
577 				try {
578 					name = t.get("/MSH-9-3");
579 				} catch (HL7Exception e) {
580 					// ignore
581 				}
582 				
583 				if (StringUtil.isBlank(name)) {
584 					name = t.get("/MSH-9-1") + "_" + t.get("/MSH-9-2");
585 				}
586 				
587 				((AbstractSuperMessage)theMessage).setName(name);
588         	}
589         }
590 
591 	}
592 	
593 	private <R> void assertMessageValidates(String message, String encoding, String version) throws HL7Exception {
594 	    if (isValidating()) {
595 	        Validator<R> validator = getHapiContext().getMessageValidator();
596 	        ValidationExceptionHandlerFactory<R> factory  = getHapiContext().getValidationExceptionHandlerFactory();
597 	        ValidationExceptionHandler<R> handler = factory.getNewInstance(getHapiContext());
598 	        R result = validator.validate(message, encoding.equals("XML"), version, handler);
599             handleException(handler, result);
600 	    }
601 	}
602 	
603     private <R> void assertMessageValidates(Message message) throws HL7Exception {
604         if (isValidating()) {
605             Validator<R> validator = getHapiContext().getMessageValidator();
606             ValidationExceptionHandlerFactory<R> factory  = getHapiContext().getValidationExceptionHandlerFactory();
607             if (factory == null) {
608             	throw new NullPointerException("Validation is enabled for this parser, but ValidationExceptionHandlerFactory is null");
609             }
610             ValidationExceptionHandler<R> handler = factory.getNewInstance(getHapiContext());
611             R result = validator.validate(message, handler);
612             handleException(handler, result);
613         }
614     }
615 
616     private <R> void handleException(ValidationExceptionHandler<R> handler, R result)
617             throws HL7Exception {
618         if (handler.hasFailed()) {
619             HL7Exception e = new HL7Exception("Validation has failed");
620             e.setDetail(result);
621             throw e;
622         }
623     }
624     
625     private boolean isValidating() {
626         return getHapiContext().getParserConfiguration().isValidating();
627     }
628     
629 
630 }