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 "GenericParser.java". Description: 010"A Parser that delegates parsing tasks to an underlying PipeParser and DefaultXMLParser" 011 012The Initial Developer of the Original Code is University Health Network. Copyright (C) 0132001. 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 */ 027package ca.uhn.hl7v2.parser; 028 029import ca.uhn.hl7v2.DefaultHapiContext; 030import ca.uhn.hl7v2.HL7Exception; 031import ca.uhn.hl7v2.HapiContext; 032import ca.uhn.hl7v2.model.Message; 033import ca.uhn.hl7v2.model.Segment; 034import ca.uhn.hl7v2.model.Type; 035import ca.uhn.hl7v2.validation.ValidationContext; 036import ca.uhn.hl7v2.validation.impl.NoValidation; 037import ca.uhn.hl7v2.validation.impl.ValidationContextFactory; 038 039/** 040 * A Parser that delegates parsing tasks to an underlying PipeParser and DefaultXMLParser as needed. 041 * Messages to be parsed are inspected in order to determine which Parser to use. If a message is to 042 * be encoded, the "primary" Parser will be used. The primary parser defaults to PipeParser, and can 043 * be configured using the set...AsPrimary() methods. 044 * 045 * @author Bryan Tripp 046 */ 047public class GenericParser extends Parser { 048 049 private Parser primaryParser; 050 private Parser secondaryParser; 051 private final PipeParser pipeParser; 052 private final XMLParser xmlParser; 053 054 /** Creates a new instance of GenericParser */ 055 public GenericParser() { 056 this(new DefaultHapiContext()); 057 } 058 059 /** 060 * @param context the HapiContext to be used 061 */ 062 public GenericParser(HapiContext context) { 063 super(context); 064 pipeParser = new PipeParser(context); 065 xmlParser = new DefaultXMLParser(context); 066 setPipeParserAsPrimary(); 067 } 068 069 /** 070 * Creates a new instance of GenericParser 071 * 072 * @param theFactory custom factory to use for model class lookup 073 */ 074 public GenericParser(ModelClassFactory theFactory) { 075 super(theFactory); 076 077 pipeParser = new PipeParser(theFactory); 078 xmlParser = new DefaultXMLParser(theFactory); 079 setPipeParserAsPrimary(); 080 } 081 082 /** 083 * Sets the underlying XMLParser as the primary parser, so that subsequent calls to encode() 084 * will return XML encoded messages, and an attempt will be made to use the XMLParser first for 085 * parsing. 086 */ 087 public void setXMLParserAsPrimary() { 088 primaryParser = xmlParser; 089 secondaryParser = pipeParser; 090 } 091 092 /** 093 * Sets the underlying PipeParser as the primary parser, so that subsequent calls to encode() 094 * will return traditionally encoded messages, and an attempt will be made to use the PipeParser 095 * first for parsing. This is the default setting. 096 */ 097 public void setPipeParserAsPrimary() { 098 primaryParser = pipeParser; 099 secondaryParser = xmlParser; 100 } 101 102 /** 103 * Returns true if the pipe parser is primary 104 * 105 * @return true if the pipe parser is primary 106 */ 107 public boolean isPipeParserPrimary() { 108 return primaryParser == pipeParser; 109 } 110 111 /** 112 * @param theContext the set of validation rules to be applied to messages parsed or encoded by 113 * this parser (defaults to ValidationContextFactory.DefaultValidation) 114 * 115 * @deprecated use a dedicated {@link HapiContext} and set its ValidationContext property 116 */ 117 public void setValidationContext(ValidationContext theContext) { 118 super.setValidationContext(theContext); 119 120 // These parsers are not yet initialized when this is called during the Parser constructor 121 if (xmlParser != null) { 122 pipeParser.setValidationContext(theContext); 123 xmlParser.setValidationContext(theContext); 124 } 125 } 126 127 /** 128 * Checks the encoding of the message using getEncoding(), and returns the corresponding parser 129 * (either pipeParser or xmlParser). If the encoding is not recognized an HL7Exception is 130 * thrown. 131 */ 132 private Parser getAppropriateParser(String message) throws HL7Exception { 133 String encoding = getEncoding(message); 134 if ("VB".equalsIgnoreCase(encoding)) return pipeParser; 135 if ("XML".equalsIgnoreCase(encoding)) return xmlParser; 136 throw new HL7Exception("Can't find appropriate parser - encoding not recognized"); 137 } 138 139 /** 140 * Formats a Message object into an HL7 message string using the given encoding. 141 * 142 * @throws HL7Exception if the data fields in the message do not permit encoding (e.g. required 143 * fields are null) 144 * @throws EncodingNotSupportedException if the requested encoding is not supported by this 145 * parser. 146 */ 147 protected String doEncode(Message source, String encoding) throws HL7Exception, 148 EncodingNotSupportedException { 149 String ret = null; 150 if (encoding == null) 151 encoding = ""; // prevent null pointer exception 152 if (encoding.equalsIgnoreCase("VB")) { 153 ret = pipeParser.doEncode(source); 154 } else if (encoding.equalsIgnoreCase("XML")) { 155 ret = xmlParser.doEncode(source); 156 } else { 157 throw new EncodingNotSupportedException("The encoding " + encoding 158 + " is not supported by " + this.getClass().getName()); 159 } 160 return ret; 161 } 162 163 /** 164 * <p> 165 * Returns a minimal amount of data from a message string, including only the data needed to 166 * send a response to the remote system. This includes the following fields: 167 * <ul> 168 * <li>field separator</li> 169 * <li>encoding characters</li> 170 * <li>processing ID</li> 171 * <li>message control ID</li> 172 * </ul> 173 * This method is intended for use when there is an error parsing a message, (so the Message 174 * object is unavailable) but an error message must be sent back to the remote system including 175 * some of the information in the inbound message. This method parses only that required 176 * information, hopefully avoiding the condition that caused the original error. 177 * </p> 178 */ 179 public Segment getCriticalResponseData(String message) throws HL7Exception { 180 return getAppropriateParser(message).getCriticalResponseData(message); 181 } 182 183 /** 184 * Returns the version ID (MSH-12) from the given message, without fully parsing the message. 185 * The version is needed prior to parsing in order to determine the message class into which the 186 * text of the message should be parsed. 187 * 188 * @throws HL7Exception if the version field can not be found. 189 */ 190 public String getVersion(String message) throws HL7Exception { 191 return getAppropriateParser(message).getVersion(message); 192 } 193 194 /** 195 * Returns a String representing the encoding of the given message, if the encoding is 196 * recognized. For example if the given message appears to be encoded using HL7 2.x XML rules 197 * then "XML" would be returned. If the encoding is not recognized then null is returned. That 198 * this method returns a specific encoding does not guarantee that the message is correctly 199 * encoded (e.g. well formed XML) - just that it is not encoded using any other encoding than 200 * the one returned. Returns null if the encoding is not recognized. 201 */ 202 public String getEncoding(String message) { 203 String encoding = primaryParser.getEncoding(message); 204 return (encoding == null ? secondaryParser.getEncoding(message) : encoding); 205 } 206 207 /** 208 * For response messages, returns the value of MSA-2 (the message ID of the message sent by the 209 * sending system). This value may be needed prior to main message parsing, so that 210 * (particularly in a multi-threaded scenario) the message can be routed to the thread that sent 211 * the request. We need this information first so that any parse exceptions are thrown to the 212 * correct thread. Implementers of Parsers should take care to make the implementation of this 213 * method very fast and robust. Returns null if MSA-2 can not be found (e.g. if the message is 214 * not a response message). 215 */ 216 public String getAckID(String message) { 217 try { 218 return getAppropriateParser(message).getAckID(message); 219 } catch (HL7Exception e) { 220 return null; 221 } 222 } 223 224 /** 225 * Returns true if and only if the given encoding is supported by this Parser. 226 */ 227 public boolean supportsEncoding(String encoding) { 228 return (primaryParser.supportsEncoding(encoding) || secondaryParser.supportsEncoding(encoding)); 229 } 230 231 /** 232 * @return the preferred encoding of the current primary Parser 233 */ 234 public String getDefaultEncoding() { 235 return primaryParser.getDefaultEncoding(); 236 } 237 238 /** 239 * Parses a message string and returns the corresponding Message object. 240 * 241 * @throws HL7Exception if the message is not correctly formatted. 242 * @throws EncodingNotSupportedException if the message encoded is not supported by this parser. 243 */ 244 protected Message doParse(String message, String version) throws HL7Exception { 245 return getAppropriateParser(message).doParse(message, version); 246 } 247 248 /** 249 * {@inheritDoc} 250 */ 251 @Override 252 public Message parse(String theMessage) throws HL7Exception { 253 Message retVal = super.parse(theMessage); 254 Parser parser = getAppropriateParser(theMessage); 255 retVal.setParser(parser); 256 return retVal; 257 } 258 259 /** 260 * Formats a Message object into an HL7 message string using this parser's default encoding. 261 * 262 * @throws HL7Exception if the data fields in the message do not permit encoding (e.g. required 263 * fields are null) 264 */ 265 protected String doEncode(Message source) throws HL7Exception { 266 return primaryParser.doEncode(source); 267 } 268 269 /** 270 * {@inheritDoc } 271 */ 272 @Override 273 public String doEncode(Segment structure, EncodingCharacters encodingCharacters) 274 throws HL7Exception { 275 return primaryParser.doEncode(structure, encodingCharacters); 276 } 277 278 /** 279 * {@inheritDoc } 280 */ 281 @Override 282 public String doEncode(Type type, EncodingCharacters encodingCharacters) throws HL7Exception { 283 return primaryParser.doEncode(type, encodingCharacters); 284 } 285 286 /** 287 * {@inheritDoc } 288 */ 289 @Override 290 public void parse(Type type, String string, EncodingCharacters encodingCharacters) 291 throws HL7Exception { 292 primaryParser.parse(type, string, encodingCharacters); 293 } 294 295 /** 296 * {@inheritDoc } 297 */ 298 @Override 299 public void parse(Segment segment, String string, EncodingCharacters encodingCharacters) 300 throws HL7Exception { 301 primaryParser.parse(segment, string, encodingCharacters); 302 } 303 304 @Override 305 public void parse(Message message, String string) throws HL7Exception { 306 primaryParser.parse(message, string); 307 } 308 309 /** 310 * Convenience factory method which returns an instance that has a {@link NoValidation 311 * NoValidation validation context}. 312 * 313 * @return instance of GenericParser without validation 314 */ 315 public static GenericParser getInstanceWithNoValidation() { 316 return new GenericParser( 317 new DefaultHapiContext(ValidationContextFactory.noValidation())); 318 } 319 320 @Override 321 protected Message doParseForSpecificPackage(String theMessage, String theVersion, 322 String thePackageName) throws HL7Exception, EncodingNotSupportedException { 323 return primaryParser.doParseForSpecificPackage(theMessage, theVersion, thePackageName); 324 } 325 326 public static void main(String[] args) throws HL7Exception { 327 328 String msgString = "MSH|^~\\&|RAMSOFT|SENDING FACILITY|RAMSOFT|RECEIVING FACILITY|20101223202939-0400||ADT^A08|101|P|2.3.1||||||||\r" 329 + "EVN|A08|20101223202939-0400||||\r" 330 + "PID||P12345^^^ISSUER|P12345^^^ISSUER||PATIENT^TEST^M^^^^||19741018|M|||10808 FOOTHILL BLVD^^RANCHO CUCAMONGA^CA^91730^US||(909)481-5872^^^sales@ramsoft.com|(909)481-5800x1||M||12345|286-50-9510|||\r" 331 + "PV1||O||||||||||||||||||||||||||||||||||||||||||||||||||\r" 332 + "AL1|1||^PORK^|\r" 333 + "AL1|2||^PENICILLIN^|"; 334 335 GenericParser parser = new GenericParser(); 336 parser.setValidationContext(ValidationContextFactory.noValidation()); 337 Message msg = parser.parse(msgString); 338 System.out.println(msg.getClass().getName()); 339 340 } 341 342 343 344}