001 /**
002 The 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.
004 You may obtain a copy of the License at http://www.mozilla.org/MPL/
005 Software distributed under the License is distributed on an "AS IS" basis,
006 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
007 specific language governing rights and limitations under the License.
008
009 The Original Code is "GroupPointer.java". Description:
010 "A GroupPointer is used when parsing traditionally encoded HL7 messages"
011
012 The Initial Developer of the Original Code is University Health Network. Copyright (C)
013 2001. All Rights Reserved.
014
015 Contributor(s): ______________________________________.
016
017 Alternatively, the contents of this file may be used under the terms of the
018 GNU General Public License (the �GPL�), in which case the provisions of the GPL are
019 applicable instead of those above. If you wish to allow use of your version of this
020 file only under the terms of the GPL and not to allow others to use your version
021 of this file under the MPL, indicate your decision by deleting the provisions above
022 and replace them with the notice and other provisions required by the GPL License.
023 If you do not delete the provisions above, a recipient may use your version of
024 this file under either the MPL or the GPL.
025
026 */
027 package ca.uhn.hl7v2.parser;
028
029 import ca.uhn.hl7v2.HL7Exception;
030 import ca.uhn.hl7v2.model.Message;
031 import ca.uhn.hl7v2.model.Segment;
032 import ca.uhn.hl7v2.model.Type;
033 import ca.uhn.hl7v2.validation.ValidationContext;
034 import 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 */
044 public 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 }