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 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 (EncodingNotSupportedException e) {
121                        throw new Hl7InputStreamMessageStringIterator.ParseFailureError("Failed to parse message", e);
122                } catch (HL7Exception e) {
123                        throw new Hl7InputStreamMessageStringIterator.ParseFailureError("Failed to parse message", e);
124                }
125                return retVal;
126        }
127
128        /**
129         * Unsupported method!
130         * 
131         * @throws UnsupportedOperationException
132         *             If called
133         */
134        public void remove() {
135                throw new UnsupportedOperationException();
136        }
137
138        /**
139         * If set to true, any lines beginning with a hash (#) will be ignored. This allows
140         * you to place comments in a file to be read if needed.
141         */
142        public void setIgnoreComments(boolean theIgnoreComments) {
143                myWrapped.setIgnoreComments(theIgnoreComments);
144        }
145
146        /**
147         * If set (default is <code>null</code>), all messages will be parsed into
148         * instances of this type. 
149         */
150        public void setMessageType(Class<? extends Message> theMessageType) {
151                myMessageType = theMessageType;
152        }
153
154}