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