Coverage Report - ca.uhn.hl7v2.parser.Parser
 
Classes in this File Line Coverage Branch Coverage Complexity
Parser
66%
86/130
71%
30/42
1.816
 
 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  5
         private static final Logger log = LoggerFactory.getLogger(Parser.class);
 66  
         
 67  
         /**
 68  
          * Uses DefaultModelClassFactory for model class lookup.
 69  
          */
 70  
         public Parser() {
 71  4710
                 this(new DefaultHapiContext());
 72  4710
         }
 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  6220
                 super(context);
 82  6220
         }
 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  25
                 this(new DefaultHapiContext(modelClassFactory));
 91  25
         }
 92  
 
 93  
         /**
 94  
          * @return the factory used by this Parser for model class lookup
 95  
          */
 96  
         public ModelClassFactory getFactory() {
 97  8943
                 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  134743
                 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  0
                 HapiContext newContext = new DefaultHapiContext(getHapiContext());
 116  0
                 newContext.setValidationContext(context);
 117  0
                 setHapiContext(newContext);
 118  0
         }
 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  156138
                 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  0
                 HapiContext newContext = new DefaultHapiContext(getHapiContext());
 149  0
                 newContext.setParserConfiguration(configuration);
 150  0
                 setHapiContext(newContext);
 151  0
         }
 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  3229
                 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  2946
                 String encoding = getEncoding(message);
 190  2946
                 if (!supportsEncoding(encoding)) {
 191  60
                         String startOfMessage = null;
 192  60
                         if (message.startsWith("MSH")) {
 193  25
                                 int indexOfCR = message.indexOf('\r');
 194  25
                                 if (indexOfCR > 0) {
 195  15
                                         startOfMessage = message.substring(0, indexOfCR);
 196  
                                 }
 197  
                         } 
 198  60
                         if (startOfMessage == null) {
 199  45
                                 startOfMessage = message.substring(0, Math.min(message.length(), 50));
 200  
                         }
 201  60
                         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  2886
                 String version = getVersion(message);
 206  
                 
 207  2871
                 if (!getParserConfiguration().isAllowUnknownVersions()) {
 208  2871
                         assertVersionExists(version);
 209  
                 }
 210  
 
 211  2866
                 assertMessageValidates(message, encoding, version);
 212  2861
                 Message result = doParse(message, version);
 213  2824
                 assertMessageValidates(result);
 214  
 
 215  2809
                 result.setParser(this);
 216  
 
 217  2809
                 applySuperStructureName(result);
 218  
                 
 219  2809
                 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  718
                 assertMessageValidates(source);
 247  718
                 String result = doEncode(source, encoding);
 248  718
             assertMessageValidates(result, encoding, source.getVersion());
 249  718
                 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  2066
                 assertMessageValidates(source);
 276  2056
                 String result = doEncode(source);
 277  2056
                 assertMessageValidates(result, getDefaultEncoding(), source.getVersion());
 278  2056
                 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  0
                 String encoding = getEncoding(message);
 391  0
                 if (!supportsEncoding(encoding)) {
 392  0
                         throw new EncodingNotSupportedException("Can't parse message beginning "
 393  0
                                         + message.substring(0, Math.min(message.length(), 50)));
 394  
                 }
 395  
 
 396  0
                 String version = getVersion(message);
 397  0
                 assertVersionExists(version);
 398  
 
 399  0
                 assertMessageValidates(message, encoding, version);
 400  0
                 Message result = doParseForSpecificPackage(message, version, packageName);
 401  0
                 assertMessageValidates(result);
 402  
 
 403  0
                 result.setParser(this);
 404  0
                 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  0
                 Class<? extends Message> messageClass = getFactory().getMessageClassInASpecificPackage(
 421  
                                 theName, theVersion, isExplicit, packageName);
 422  0
                 if (messageClass == null) {
 423  0
                         throw new HL7Exception("Can't find message class in current package list: " + theName);
 424  
                 }
 425  0
             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  90
                         genericMessageClass = GenericMessage.getGenericMessageClass(version);
 473  
 
 474  90
             Constructor<? extends Message> constr = genericMessageClass
 475  90
                     .getConstructor(new Class[] { ModelClassFactory.class });
 476  90
             Message dummy = constr.newInstance(factory);
 477  
 
 478  90
                         Class<? extends Segment> c = null;
 479  
                         
 480  90
                         if (Version.supportsVersion(version)) {
 481  85
                                 c = factory.getSegmentClass("MSH", version);
 482  
                         }
 483  
                         
 484  90
                         if (c != null) {
 485  85
                                 if (GenericSegment.class.isAssignableFrom(c)) {
 486  0
                                         Class<?>[] constructorParamTypes = { Group.class, String.class };
 487  0
                                         Object[] constructorParamArgs = { dummy, "MSH" };
 488  0
                                         Constructor<? extends Segment> constructor = c.getConstructor(constructorParamTypes);
 489  0
                                         msh = constructor.newInstance(constructorParamArgs);
 490  0
                                 } else {
 491  85
                                         Class<?>[] constructorParamTypes = { Group.class, ModelClassFactory.class };
 492  85
                                         Object[] constructorParamArgs = { dummy, factory };
 493  85
                                         Constructor<? extends Segment> constructor = c.getConstructor(constructorParamTypes);
 494  85
                                         msh = constructor.newInstance(constructorParamArgs);
 495  85
                                 }
 496  
                         } else {
 497  5
                                 msh = new GenericSegment(dummy, "MSH");            
 498  
                         }
 499  0
                 } catch (Exception e) {
 500  0
                         throw new HL7Exception("Couldn't create MSH for version " + version
 501  
                                         + " (does your classpath include this version?) ... ", e);
 502  90
                 }
 503  90
                 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  0
                 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  9613
                 if (!Version.supportsVersion(version))
 527  5
             throw new HL7Exception(
 528  
                     "The HL7 version " + version + " is not recognized",
 529  
                     ErrorCode.UNSUPPORTED_VERSION_ID);
 530  9608
         }
 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  0
                 assertVersionExists(version);
 546  0
                 return getHapiContext().getModelClassFactory().
 547  0
                                 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  2861
                 Class<? extends Message> messageClass = getFactory().getMessageClass(theName, theVersion, isExplicit);
 567  2861
                 if (messageClass == null)
 568  0
                         throw new HL7Exception("Can't find message class in current package list: " + theName);
 569  2861
                 return ReflectionUtil.instantiateMessage(messageClass, getFactory());
 570  
         }
 571  
         
 572  
         protected void applySuperStructureName(Message theMessage) throws HL7Exception {
 573  6118
         if (theMessage instanceof AbstractSuperMessage) {
 574  260
                 if (theMessage.getName() == null) {
 575  0
                         Terser t = new Terser(theMessage);
 576  0
                         String name = null;
 577  
                                 try {
 578  0
                                         name = t.get("/MSH-9-3");
 579  0
                                 } catch (HL7Exception e) {
 580  
                                         // ignore
 581  0
                                 }
 582  
                                 
 583  0
                                 if (StringUtil.isBlank(name)) {
 584  0
                                         name = t.get("/MSH-9-1") + "_" + t.get("/MSH-9-2");
 585  
                                 }
 586  
                                 
 587  0
                                 ((AbstractSuperMessage)theMessage).setName(name);
 588  
                 }
 589  
         }
 590  
 
 591  6118
         }
 592  
         
 593  
         private <R> void assertMessageValidates(String message, String encoding, String version) throws HL7Exception {
 594  5640
             if (isValidating()) {
 595  5630
                 Validator<R> validator = getHapiContext().getMessageValidator();
 596  5630
                 ValidationExceptionHandlerFactory<R> factory  = getHapiContext().getValidationExceptionHandlerFactory();
 597  5630
                 ValidationExceptionHandler<R> handler = factory.getNewInstance(getHapiContext());
 598  5630
                 R result = validator.validate(message, encoding.equals("XML"), version, handler);
 599  5625
             handleException(handler, result);
 600  
             }
 601  5635
         }
 602  
         
 603  
     private <R> void assertMessageValidates(Message message) throws HL7Exception {
 604  5608
         if (isValidating()) {
 605  5598
             Validator<R> validator = getHapiContext().getMessageValidator();
 606  5598
             ValidationExceptionHandlerFactory<R> factory  = getHapiContext().getValidationExceptionHandlerFactory();
 607  5598
             if (factory == null) {
 608  0
                     throw new NullPointerException("Validation is enabled for this parser, but ValidationExceptionHandlerFactory is null");
 609  
             }
 610  5598
             ValidationExceptionHandler<R> handler = factory.getNewInstance(getHapiContext());
 611  5598
             R result = validator.validate(message, handler);
 612  5583
             handleException(handler, result);
 613  
         }
 614  5583
     }
 615  
 
 616  
     private <R> void handleException(ValidationExceptionHandler<R> handler, R result)
 617  
             throws HL7Exception {
 618  11208
         if (handler.hasFailed()) {
 619  10
             HL7Exception e = new HL7Exception("Validation has failed");
 620  10
             e.setDetail(result);
 621  10
             if (result instanceof Message) e.setResponseMessage((Message)result);
 622  10
             throw e;
 623  
         }
 624  11198
     }
 625  
     
 626  
     private boolean isValidating() {
 627  145991
         return getHapiContext().getParserConfiguration().isValidating();
 628  
     }
 629  
     
 630  
 
 631  
 }