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 "GenericParser.java".  Description:
10  "A Parser that delegates parsing tasks to an underlying PipeParser and DefaultXMLParser"
11  
12  The Initial Developer of the Original Code is University Health Network. Copyright (C)
13  2001.  All Rights Reserved.
14  
15  Contributor(s): ______________________________________.
16  
17  Alternatively, the contents of this file may be used under the terms of the
18  GNU General Public License (the  �GPL�), in which case the provisions of the GPL are
19  applicable instead of those above.  If you wish to allow use of your version of this
20  file only under the terms of the GPL and not to allow others to use your version
21  of this file under the MPL, indicate your decision by deleting  the provisions above
22  and replace  them with the notice and other provisions required by the GPL License.
23  If you do not delete the provisions above, a recipient may use your version of
24  this file under either the MPL or the GPL.
25  
26   */
27  package ca.uhn.hl7v2.parser;
28  
29  import ca.uhn.hl7v2.DefaultHapiContext;
30  import ca.uhn.hl7v2.HL7Exception;
31  import ca.uhn.hl7v2.HapiContext;
32  import ca.uhn.hl7v2.model.Message;
33  import ca.uhn.hl7v2.model.Segment;
34  import ca.uhn.hl7v2.model.Type;
35  import ca.uhn.hl7v2.validation.ValidationContext;
36  import ca.uhn.hl7v2.validation.impl.NoValidation;
37  import ca.uhn.hl7v2.validation.impl.ValidationContextFactory;
38  
39  /**
40   * A Parser that delegates parsing tasks to an underlying PipeParser and DefaultXMLParser as needed.
41   * Messages to be parsed are inspected in order to determine which Parser to use. If a message is to
42   * be encoded, the "primary" Parser will be used. The primary parser defaults to PipeParser, and can
43   * be configured using the set...AsPrimary() methods.
44   * 
45   * @author Bryan Tripp
46   */
47  public class GenericParser extends Parser {
48  
49  	private Parser primaryParser;
50  	private Parser secondaryParser;
51  	private final PipeParser pipeParser;
52  	private final XMLParser xmlParser;
53  
54  	/** Creates a new instance of GenericParser */
55  	public GenericParser() {
56  		this(new DefaultHapiContext());
57  	}
58  
59      /**
60       * @param context the HapiContext to be used
61       */
62  	public GenericParser(HapiContext context) {
63  		super(context);
64  		pipeParser = new PipeParser(context);
65  		xmlParser = new DefaultXMLParser(context);
66  		setPipeParserAsPrimary();
67  	}
68  
69  	/**
70  	 * Creates a new instance of GenericParser
71  	 * 
72  	 * @param theFactory custom factory to use for model class lookup
73  	 */
74  	public GenericParser(ModelClassFactory theFactory) {
75  		super(theFactory);
76  
77  		pipeParser = new PipeParser(theFactory);
78  		xmlParser = new DefaultXMLParser(theFactory);
79  		setPipeParserAsPrimary();
80  	}
81  
82  	/**
83  	 * Sets the underlying XMLParser as the primary parser, so that subsequent calls to encode()
84  	 * will return XML encoded messages, and an attempt will be made to use the XMLParser first for
85  	 * parsing.
86  	 */
87  	public void setXMLParserAsPrimary() {
88  		primaryParser = xmlParser;
89  		secondaryParser = pipeParser;
90  	}
91  
92  	/**
93  	 * Sets the underlying PipeParser as the primary parser, so that subsequent calls to encode()
94  	 * will return traditionally encoded messages, and an attempt will be made to use the PipeParser
95  	 * first for parsing. This is the default setting.
96  	 */
97  	public void setPipeParserAsPrimary() {
98  		primaryParser = pipeParser;
99  		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;
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 {
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 }