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}