001    package ca.uhn.hl7v2.util;
002    
003    import java.io.IOException;
004    import java.io.InputStream;
005    import java.io.InputStreamReader;
006    import java.io.PushbackReader;
007    import java.io.Reader;
008    import java.util.Iterator;
009    
010    import org.slf4j.Logger;
011    import org.slf4j.LoggerFactory;
012    
013    /**
014     * <p>
015     * Reads from an {@link InputStream} containing a stream of encoded HL7 messages
016     * and iterates over those messages. This class is geared towards reading from
017     * files, and tries to be very lenient about the format of the stream,
018     * specifically concerning control characters and line endings. It should be
019     * safe to provide a stream containing Windows or Unix line endings (which will
020     * be treated as segment delimiters). It is also safe to provide a stream
021     * containing MLLP control blocks before and after each message (although these
022     * will not be validated! Do not use this class to read MLLP messages from a
023     * socket stream!)
024     * </p>
025     * <p>
026     * The input stream could, for example, be a FileInputStream reading from a text
027     * file containing a number of HL7 messages in plain text format.
028     * </p>
029     * <p>
030     * Usage note: If an IOException occurs while reading from the stream or a
031     * message parsing exception occurs, it will be thrown as an unchecked
032     * {@link ParseFailureError}
033     * </p>
034     */
035    public class Hl7InputStreamMessageStringIterator implements Iterator<String> {
036    
037            @SuppressWarnings("unused")
038            private static final Logger ourLog = LoggerFactory.getLogger(Hl7InputStreamMessageStringIterator.class);
039            
040            private StringBuilder myBuffer = new StringBuilder();
041            private boolean myFoundMessageInBuffer = false;
042            private Boolean myHasNext;
043            private boolean myIgnoreComments;
044            private String myNext;
045            private Reader myReader;
046    
047            /**
048             * Constructor
049             * 
050             * @param theInputStream
051             *            The input stream to read from
052             */
053            public Hl7InputStreamMessageStringIterator(InputStream theInputStream) {
054                    this(new InputStreamReader(theInputStream));
055            }
056    
057            /**
058             * Constructor
059             * 
060             * @param theReader
061             *            The reader to read from
062             */
063            public Hl7InputStreamMessageStringIterator(Reader theReader) {
064                    myReader = new PushbackReader(theReader);
065            }
066    
067            /**
068             * {@inheritDoc}
069             */
070            public boolean hasNext() {
071                    if (myHasNext == null) {
072    
073                            int next;
074                            int prev = -1;
075                            int endOfBuffer = -1;
076                            boolean inComment = false;
077                            
078                            while (true) {
079                                    try {
080                                            next = myReader.read();
081                                    } catch (IOException e) {
082                                            throw new ParseFailureError("IOException reading from input", e);
083                                    }
084    
085                                    if (next == -1) {
086                                            break;
087                                    }
088    
089                                    char nextChar = (char) next;
090                                    if (nextChar == '#' && myIgnoreComments && (prev == -1 || prev == '\n' || prev == '\r')) {
091                                            inComment = true;
092                                            continue;
093                                    }
094                                    
095                                    // Convert '\n' or "\r\n" to '\r'
096                                    if (nextChar == 10) {
097                                            if (myBuffer.length() > 0) {
098                                                    if (myBuffer.charAt(myBuffer.length() - 1) == 13) {
099                                                            // don't append
100                                                    } else {
101                                                            myBuffer.append((char) 13);
102                                                    }
103                                            }
104                                    } else if (inComment) {
105                                            if (nextChar == 10 || nextChar == 13) {
106                                                    inComment = false;
107                                            }
108                                    } else {
109                                            myBuffer.append(nextChar);
110                                    }
111                                    
112                                    prev = next;
113                                    
114                                    int bLength = myBuffer.length();
115                                    if (nextChar == 'H' && bLength >= 3) {
116                                            if (myBuffer.charAt(bLength - 2) == 'S') {
117                                                    if (myBuffer.charAt(bLength - 3) == 'M') {
118                                                            if (myFoundMessageInBuffer) {
119                                                                    if (myBuffer.charAt(bLength - 4) < 32) {
120                                                                            endOfBuffer = bLength - 3;
121                                                                            break;
122                                                                    }
123                                                            } else {
124                                                                    // Delete any whitespace or other stuff before
125                                                                    // the first message
126                                                                    myBuffer.delete(0, bLength - 3);
127                                                                    myFoundMessageInBuffer = true;
128                                                            }
129                                                    }
130                                            }
131                                    }
132    
133                            } // while(true)
134    
135                            if (!myFoundMessageInBuffer) {
136                                    myHasNext = false;
137                                    return myHasNext;
138                            }
139    
140                            String msgString;
141                            if (endOfBuffer > -1) {
142                                    msgString = myBuffer.substring(0, endOfBuffer);
143                                    myBuffer.delete(0, endOfBuffer);
144                            } else {
145                                    msgString = myBuffer.toString();
146                                    myBuffer.setLength(0);
147                            }
148    
149                            if (!msgString.startsWith("MSH")) {
150                                    myHasNext = Boolean.FALSE;
151                                    return myHasNext;
152                            }
153    
154                            myNext = msgString;
155                            myHasNext = Boolean.TRUE;
156    
157                    }
158                    return myHasNext;
159            }
160    
161            /**
162             * {@inheritDoc}
163             */
164            public String next() {
165                    if (!hasNext()) {
166                            throw new IllegalStateException();
167                    }
168                    String retVal = myNext;
169                    myNext = null;
170                    myHasNext = null;
171                    return retVal;
172            }
173    
174            /**
175             * Unsupported method!
176             * 
177             * @throws UnsupportedOperationException
178             *             If called
179             */
180            public void remove() {
181                    throw new UnsupportedOperationException();
182            }
183    
184            /**
185             * If set to true, any lines beginning with a hash (#) will be ignored. This
186             * allows you to place comments in a file to be read if needed.
187             */
188            public void setIgnoreComments(boolean theIgnoreComments) {
189                    myIgnoreComments = theIgnoreComments;
190            }
191    
192            public static class ParseFailureError extends RuntimeException {
193    
194                    private static final long serialVersionUID = 1L;
195    
196                    public ParseFailureError(String theMessage, Exception theCause) {
197                            super(theMessage, theCause);
198                    }
199    
200            }
201    
202    }