package ca.uhn.hl7v2.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackReader;
import java.io.Reader;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* Reads from an {@link InputStream} containing a stream of encoded HL7 messages
* and iterates over those messages. This class is geared towards reading from
* files, and tries to be very lenient about the format of the stream,
* specifically concerning control characters and line endings. It should be
* safe to provide a stream containing Windows or Unix line endings (which will
* be treated as segment delimiters). It is also safe to provide a stream containing
* MLLP control blocks before and after each message (although these will not be
* validated! Do not use this class to read MLLP messages from a socket stream!)
*
*
* The input stream could, for example, be a FileInputStream reading from a text
* file containing a number of HL7 messages in plain text format.
*
*
* Usage note: If an IOException occurs while reading from the stream or a
* message parsing exception occurs, it will be thrown as an unchecked
* {@link ParseFailureError}
*
*/
public class Hl7InputStreamMessageStringIterator implements Iterator {
private static final Log ourLog = LogFactory.getLog(Hl7InputStreamMessageStringIterator.class);
private Reader myReader;
private Boolean myHasNext;
private String myNext;
private StringBuilder myBuffer = new StringBuilder();
private boolean myFoundMessageInBuffer = false;
/**
* Constructor
*
* @param theInputStream
* The input stream to read from
*/
public Hl7InputStreamMessageStringIterator(InputStream theInputStream) {
this(new InputStreamReader(theInputStream));
}
/**
* Constructor
*
* @param theReader
* The reader to read from
*/
public Hl7InputStreamMessageStringIterator(Reader theReader) {
myReader = new PushbackReader(theReader);
}
/**
* {@inheritDoc}
*/
public boolean hasNext() {
if (myHasNext == null) {
int next;
int endOfBuffer = -1;
while (true) {
try {
next = myReader.read();
} catch (IOException e) {
throw new ParseFailureError("IOException reading from input", e);
}
if (next == -1) {
break;
}
// Convert '\n' or "\r\n" to '\r'
char nextChar = (char) next;
if (nextChar == 10) {
if (myBuffer.length() > 0) {
if (myBuffer.charAt(myBuffer.length() - 1) == 13) {
// don't append
} else {
myBuffer.append((char) 13);
}
}
} else {
myBuffer.append(nextChar);
}
int bLength = myBuffer.length();
if (nextChar == 'H' && bLength >= 3) {
if (myBuffer.charAt(bLength - 2) == 'S') {
if (myBuffer.charAt(bLength - 3) == 'M') {
if (myFoundMessageInBuffer) {
if (myBuffer.charAt(bLength - 4) < 32) {
endOfBuffer = bLength - 3;
break;
}
} else {
// Delete any whitespace or other stuff before
// the first message
myBuffer.delete(0, bLength - 3);
myFoundMessageInBuffer = true;
}
}
}
}
} // while(true)
if (!myFoundMessageInBuffer) {
myHasNext = false;
return myHasNext;
}
String msgString;
if (endOfBuffer > -1) {
msgString = myBuffer.substring(0, endOfBuffer);
myBuffer.delete(0, endOfBuffer);
} else {
msgString = myBuffer.toString();
myBuffer.setLength(0);
}
if (!msgString.startsWith("MSH")) {
myHasNext = Boolean.FALSE;
return myHasNext;
}
myNext = msgString;
myHasNext = Boolean.TRUE;
}
return myHasNext;
}
/**
* {@inheritDoc}
*/
public String next() {
if (!hasNext()) {
throw new IllegalStateException();
}
String retVal = myNext;
myNext = null;
myHasNext = null;
return retVal;
}
/**
* Unsupported method!
*
* @throws UnsupportedOperationException
* If called
*/
public void remove() {
throw new UnsupportedOperationException();
}
public static class ParseFailureError extends RuntimeException {
private static final long serialVersionUID = 1L;
public ParseFailureError(String theMessage, Exception theCause) {
super(theMessage, theCause);
}
}
}