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