001package ca.uhn.hl7v2.util;
002
003import java.io.InputStream;
004import java.io.Reader;
005import java.util.Iterator;
006
007import ca.uhn.hl7v2.DefaultHapiContext;
008import ca.uhn.hl7v2.HL7Exception;
009import ca.uhn.hl7v2.HapiContext;
010import ca.uhn.hl7v2.HapiContextSupport;
011import ca.uhn.hl7v2.model.Message;
012import ca.uhn.hl7v2.parser.EncodingNotSupportedException;
013import ca.uhn.hl7v2.util.Hl7InputStreamMessageStringIterator.ParseFailureError;
014import ca.uhn.hl7v2.validation.impl.ValidationContextFactory;
015
016/**
017 * <p>
018 * Reads from an {@link InputStream} containing a stream of encoded HL7 messages
019 * and iterates over those messages. This class is geared towards reading from
020 * files, and tries to be very lenient about the format of the stream,
021 * specifically concerning control characters and line endings. It should be
022 * safe to provide a stream containing Windows or Unix line endings (which will
023 * be treated as segment delimiters). It is also safe to provide a stream containing
024 * MLLP control blocks before and after each message (although these will not be
025 * validated! Do not use this class to read MLLP messages from a socket stream!)
026 * </p>
027 * <p>
028 * The input stream could, for example, be a FileInputStream reading from a text
029 * file containing a number of HL7 messages in plain text format.
030 * </p>
031 * <p>
032 * Usage note: If an IOException occurs while reading from the stream or a
033 * message parsing exception occurs, it will be thrown as an unchecked
034 * {@link ParseFailureError}
035 * </p>
036 */
037public class Hl7InputStreamMessageIterator extends HapiContextSupport implements Iterator<Message> {
038
039        private Class<? extends Message> myMessageType;
040        private final Hl7InputStreamMessageStringIterator myWrapped;
041
042        /**
043         * Constructor
044         * 
045         * @param theInputStream
046         *            The input stream to read from
047         */
048        public Hl7InputStreamMessageIterator(InputStream theInputStream) {
049        this(theInputStream, new DefaultHapiContext(ValidationContextFactory.noValidation()));
050        }
051
052        /**
053         * Constructor
054         * 
055         * @param theReader
056         *            The reader to read from
057         */
058        public Hl7InputStreamMessageIterator(Reader theReader) {
059                this(theReader, new DefaultHapiContext(ValidationContextFactory.noValidation()));
060        }
061
062        /**
063         * Constructor
064         * 
065         * @param theInputStream
066         *            The input stream to read from
067         * @param theHapiContext
068         *                       The HapiContext from which to obtain the parser
069         */
070        public Hl7InputStreamMessageIterator(InputStream theInputStream, HapiContext theHapiContext) {
071            super(theHapiContext);
072                myWrapped = new Hl7InputStreamMessageStringIterator(theInputStream);
073        }
074
075        /**
076         * Constructor
077         * 
078         * @param theReader
079         *            The reader to read from
080         * @param theHapiContext
081         *                       The HapiContext from which to obtain the parser
082         */
083        public Hl7InputStreamMessageIterator(Reader theReader, HapiContext theHapiContext) {
084        super(theHapiContext);
085                myWrapped = new Hl7InputStreamMessageStringIterator(theReader);
086        }
087
088        /**
089         * Factory method which returns an instance which reads from a file on the
090         * classpath
091         */
092        public static Hl7InputStreamMessageIterator getForClasspathResource(String theClasspath) {
093                InputStream is = Hl7InputStreamMessageIterator.class.getResourceAsStream(theClasspath);
094                if (is == null) {
095                        throw new IllegalArgumentException("Can't find resource: " + theClasspath);
096                }
097                return new Hl7InputStreamMessageIterator(is);
098        }
099        
100        /**
101         * {@inheritDoc}
102         */
103        public boolean hasNext() {
104                return myWrapped.hasNext();
105        }
106
107        /**
108         * {@inheritDoc}
109         */
110        public Message next() {
111                String nextString = myWrapped.next();
112                Message retVal;
113                try {
114                        if (myMessageType != null) {
115                                retVal = ReflectionUtil.instantiateMessage(myMessageType, getHapiContext().getModelClassFactory());
116                                retVal.parse(nextString);
117                        } else {
118                                retVal = getHapiContext().getPipeParser().parse(nextString);
119                        }
120                } catch (HL7Exception e) {
121                        throw new ParseFailureError("Failed to parse message", e);
122                }
123        return retVal;
124        }
125
126        /**
127         * Unsupported method!
128         * 
129         * @throws UnsupportedOperationException
130         *             If called
131         */
132        public void remove() {
133                throw new UnsupportedOperationException();
134        }
135
136        /**
137         * If set to true, any lines beginning with a hash (#) will be ignored. This allows
138         * you to place comments in a file to be read if needed.
139         */
140        public void setIgnoreComments(boolean theIgnoreComments) {
141                myWrapped.setIgnoreComments(theIgnoreComments);
142        }
143
144        /**
145         * If set (default is <code>null</code>), all messages will be parsed into
146         * instances of this type. 
147         */
148        public void setMessageType(Class<? extends Message> theMessageType) {
149                myMessageType = theMessageType;
150        }
151
152}