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    }