Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
Hl7InputStreamMessageStringIterator |
|
| 5.285714285714286;5.286 | ||||
Hl7InputStreamMessageStringIterator$ParseFailureError |
|
| 5.285714285714286;5.286 |
1 | package ca.uhn.hl7v2.util; | |
2 | ||
3 | import java.io.IOException; | |
4 | import java.io.InputStream; | |
5 | import java.io.InputStreamReader; | |
6 | import java.io.PushbackReader; | |
7 | import java.io.Reader; | |
8 | import java.util.Iterator; | |
9 | ||
10 | import org.slf4j.Logger; | |
11 | import org.slf4j.LoggerFactory; | |
12 | ||
13 | /** | |
14 | * <p> | |
15 | * Reads from an {@link InputStream} containing a stream of encoded HL7 messages | |
16 | * and iterates over those messages. This class is geared towards reading from | |
17 | * files, and tries to be very lenient about the format of the stream, | |
18 | * specifically concerning control characters and line endings. It should be | |
19 | * safe to provide a stream containing Windows or Unix line endings (which will | |
20 | * be treated as segment delimiters). It is also safe to provide a stream | |
21 | * containing MLLP control blocks before and after each message (although these | |
22 | * will not be validated! Do not use this class to read MLLP messages from a | |
23 | * socket stream!) | |
24 | * </p> | |
25 | * <p> | |
26 | * The input stream could, for example, be a FileInputStream reading from a text | |
27 | * file containing a number of HL7 messages in plain text format. | |
28 | * </p> | |
29 | * <p> | |
30 | * Usage note: If an IOException occurs while reading from the stream or a | |
31 | * message parsing exception occurs, it will be thrown as an unchecked | |
32 | * {@link ParseFailureError} | |
33 | * </p> | |
34 | */ | |
35 | 0 | public class Hl7InputStreamMessageStringIterator implements Iterator<String> { |
36 | ||
37 | @SuppressWarnings("unused") | |
38 | 5 | private static final Logger ourLog = LoggerFactory.getLogger(Hl7InputStreamMessageStringIterator.class); |
39 | ||
40 | 50 | private StringBuilder myBuffer = new StringBuilder(); |
41 | 50 | private boolean myFoundMessageInBuffer = false; |
42 | private Boolean myHasNext; | |
43 | private boolean myIgnoreComments; | |
44 | private String myNext; | |
45 | private Reader myReader; | |
46 | ||
47 | /** | |
48 | * Constructor | |
49 | * | |
50 | * @param theInputStream | |
51 | * The input stream to read from | |
52 | */ | |
53 | public Hl7InputStreamMessageStringIterator(InputStream theInputStream) { | |
54 | 20 | this(new InputStreamReader(theInputStream)); |
55 | 20 | } |
56 | ||
57 | /** | |
58 | * Constructor | |
59 | * | |
60 | * @param theReader | |
61 | * The reader to read from | |
62 | */ | |
63 | 50 | public Hl7InputStreamMessageStringIterator(Reader theReader) { |
64 | 50 | myReader = new PushbackReader(theReader); |
65 | 50 | } |
66 | ||
67 | /** | |
68 | * {@inheritDoc} | |
69 | */ | |
70 | public boolean hasNext() { | |
71 | 370 | if (myHasNext == null) { |
72 | ||
73 | int next; | |
74 | 210 | int prev = -1; |
75 | 210 | int endOfBuffer = -1; |
76 | 210 | boolean inComment = false; |
77 | ||
78 | while (true) { | |
79 | try { | |
80 | 72700 | next = myReader.read(); |
81 | 0 | } catch (IOException e) { |
82 | 0 | throw new ParseFailureError("IOException reading from input", e); |
83 | 72700 | } |
84 | ||
85 | 72700 | if (next == -1) { |
86 | 90 | break; |
87 | } | |
88 | ||
89 | 72610 | char nextChar = (char) next; |
90 | 72610 | if (nextChar == '#' && myIgnoreComments && (prev == -1 || prev == '\n' || prev == '\r')) { |
91 | 20 | inComment = true; |
92 | 20 | continue; |
93 | } | |
94 | ||
95 | // Convert '\n' or "\r\n" to '\r' | |
96 | 72590 | if (nextChar == 10) { |
97 | 290 | if (myBuffer.length() > 0) { |
98 | 290 | if (myBuffer.charAt(myBuffer.length() - 1) == 13) { |
99 | // don't append | |
100 | } else { | |
101 | 110 | myBuffer.append((char) 13); |
102 | } | |
103 | } | |
104 | 72300 | } else if (inComment) { |
105 | 300 | if (nextChar == 10 || nextChar == 13) { |
106 | 20 | inComment = false; |
107 | } | |
108 | } else { | |
109 | 72000 | myBuffer.append(nextChar); |
110 | } | |
111 | ||
112 | 72590 | prev = next; |
113 | ||
114 | 72590 | int bLength = myBuffer.length(); |
115 | 72590 | if (nextChar == 'H' && bLength >= 3) { |
116 | 575 | if (myBuffer.charAt(bLength - 2) == 'S') { |
117 | 170 | if (myBuffer.charAt(bLength - 3) == 'M') { |
118 | 170 | if (myFoundMessageInBuffer) { |
119 | 120 | if (myBuffer.charAt(bLength - 4) < 32) { |
120 | 120 | endOfBuffer = bLength - 3; |
121 | 120 | break; |
122 | } | |
123 | } else { | |
124 | // Delete any whitespace or other stuff before | |
125 | // the first message | |
126 | 50 | myBuffer.delete(0, bLength - 3); |
127 | 50 | myFoundMessageInBuffer = true; |
128 | } | |
129 | } | |
130 | } | |
131 | } | |
132 | ||
133 | 72470 | } // while(true) |
134 | ||
135 | 210 | if (!myFoundMessageInBuffer) { |
136 | 0 | myHasNext = false; |
137 | 0 | return myHasNext; |
138 | } | |
139 | ||
140 | String msgString; | |
141 | 210 | if (endOfBuffer > -1) { |
142 | 120 | msgString = myBuffer.substring(0, endOfBuffer); |
143 | 120 | myBuffer.delete(0, endOfBuffer); |
144 | } else { | |
145 | 90 | msgString = myBuffer.toString(); |
146 | 90 | myBuffer.setLength(0); |
147 | } | |
148 | ||
149 | 210 | if (!msgString.startsWith("MSH")) { |
150 | 40 | myHasNext = Boolean.FALSE; |
151 | 40 | return myHasNext; |
152 | } | |
153 | ||
154 | 170 | myNext = msgString; |
155 | 170 | myHasNext = Boolean.TRUE; |
156 | ||
157 | } | |
158 | 330 | return myHasNext; |
159 | } | |
160 | ||
161 | /** | |
162 | * {@inheritDoc} | |
163 | */ | |
164 | public String next() { | |
165 | 170 | if (!hasNext()) { |
166 | 0 | throw new IllegalStateException(); |
167 | } | |
168 | 170 | String retVal = myNext; |
169 | 170 | myNext = null; |
170 | 170 | myHasNext = null; |
171 | 170 | return retVal; |
172 | } | |
173 | ||
174 | /** | |
175 | * Unsupported method! | |
176 | * | |
177 | * @throws UnsupportedOperationException | |
178 | * If called | |
179 | */ | |
180 | public void remove() { | |
181 | 0 | 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 | 5 | myIgnoreComments = theIgnoreComments; |
190 | 5 | } |
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 | 0 | super(theMessage, theCause); |
198 | 0 | } |
199 | ||
200 | } | |
201 | ||
202 | } |