View Javadoc

1   /*
2    * Hl7InputStreamReader.java
3    */
4   
5   package ca.uhn.hl7v2.util;
6   
7   import java.io.BufferedReader;
8   import java.io.FileNotFoundException;
9   import java.io.IOException;
10  import java.io.InputStream;
11  import java.io.InputStreamReader;
12  import java.io.PushbackReader;
13  import java.io.Reader;
14  import java.util.ArrayList;
15  import java.util.List;
16  import java.util.regex.Matcher;
17  import java.util.regex.Pattern;
18  
19  import org.slf4j.Logger;
20  import org.slf4j.LoggerFactory;
21  
22  /**
23   * Reads HL7 messages from an InputStream
24   * 
25   * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:27 $ by $Author: jamesagnew $
26   * @deprecated see {@link Hl7InputStreamMessageIterator} or
27   *             {@link Hl7InputStreamMessageStringIterator}
28   */
29  public class Hl7InputStreamReader {
30  
31  	private static final Logger ourLog = LoggerFactory.getLogger(Hl7InputStreamReader.class);
32  
33  	/**
34  	 * Reads HL7 messages from an InputStream and outputs an array of HL7 message strings
35  	 * 
36  	 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:27 $ by $Author: jamesagnew $
37  	 */
38  	public static String[] read(InputStream theMsgInputStream) throws FileNotFoundException,
39  			IOException {
40  		BufferedReader in = null;
41  		try {
42  			in = new BufferedReader(new CommentFilterReader(
43  					new InputStreamReader(theMsgInputStream)));
44  
45  			StringBuffer rawMsgBuffer = new StringBuffer();
46  			int c = 0;
47  			while ((c = in.read()) >= 0) {
48  				rawMsgBuffer.append((char) c);
49  			}
50  
51  			String[] messages = getHL7Messages(rawMsgBuffer.toString());
52  			ourLog.info(messages.length + " messages sent.");
53  			return messages;
54  		} finally {
55  			if (in != null)
56  				in.close();
57  		}
58  
59  	}
60  
61  	/**
62  	 * Given a string that contains HL7 messages, and possibly other junk, returns an array of the
63  	 * HL7 messages. An attempt is made to recognize segments even if there is other content between
64  	 * segments, for example if a log file logs segments individually with timestamps between them.
65  	 * 
66  	 * @param theSource a string containing HL7 messages
67  	 * @return the HL7 messages contained in theSource
68  	 */
69  	private static String[] getHL7Messages(String theSource) {
70  		List<String> messages = new ArrayList<String>(20);
71  		Pattern startPattern = Pattern.compile("^MSH", Pattern.MULTILINE);
72  		Matcher startMatcher = startPattern.matcher(theSource);
73  
74  		while (startMatcher.find()) {
75  			String messageExtent = getMessageExtent(theSource.substring(startMatcher.start()),
76  					startPattern);
77  
78  			char fieldDelim = messageExtent.charAt(3);
79  			Pattern segmentPattern = Pattern.compile("^[A-Z]{3}\\" + fieldDelim + ".*$",
80  					Pattern.MULTILINE);
81  			Matcher segmentMatcher = segmentPattern.matcher(messageExtent);
82  			StringBuffer msg = new StringBuffer();
83  			while (segmentMatcher.find()) {
84  				msg.append(segmentMatcher.group().trim());
85  				msg.append('\r');
86  			}
87  			messages.add(msg.toString());
88  		}
89  		return messages.toArray(new String[0]);
90  	}
91  
92  	/**
93  	 * Given a string that contains at least one HL7 message, returns the smallest string that
94  	 * contains the first of these messages.
95  	 */
96  	private static String getMessageExtent(String theSource, Pattern theStartPattern) {
97  		Matcher startMatcher = theStartPattern.matcher(theSource);
98  		if (!startMatcher.find()) {
99  			throw new IllegalArgumentException(theSource + "does not contain message start pattern"
100 					+ theStartPattern.toString());
101 		}
102 
103 		int start = startMatcher.start();
104 		int end = theSource.length();
105 		if (startMatcher.find()) {
106 			end = startMatcher.start();
107 		}
108 
109 		return theSource.substring(start, end).trim();
110 	}
111 
112 	/**
113 	 * TODO: this code is copied from HAPI ... should make it part of HAPI public API instead
114 	 * Removes C and C++ style comments from a reader stream. C style comments are distinguished
115 	 * from URL protocol delimiters by the preceding colon in the latter.
116 	 */
117 	private static class CommentFilterReader extends PushbackReader {
118 
119 		private final char[] startCPPComment = { '/', '*' };
120 		private final char[] endCPPComment = { '*', '/' };
121 		private final char[] startCComment = { '/', '/' };
122 		private final char[] endCComment = { '\n' };
123 		private final char[] protocolDelim = { ':', '/', '/' };
124 
125 		public CommentFilterReader(Reader in) {
126 			super(in, 5);
127 		}
128 
129 		/**
130 		 * Returns the next character, not including comments.
131 		 */
132 		public int read() throws IOException {
133 			if (atSequence(protocolDelim)) {
134 				// proceed normally
135 			} else if (atSequence(startCPPComment)) {
136 				// skip() doesn't seem to work for some reason
137 				while (!atSequence(endCPPComment))
138 					super.read();
139 				for (int i = 0; i < endCPPComment.length; i++)
140 					super.read();
141 			} else if (atSequence(startCComment)) {
142 				while (!atSequence(endCComment))
143 					super.read();
144 				for (int i = 0; i < endCComment.length; i++)
145 					super.read();
146 			}
147 			int ret = super.read();
148 			if (ret == 65535)
149 				ret = -1;
150 			return ret;
151 		}
152 
153 		public int read(char[] cbuf, int off, int len) throws IOException {
154 			int i = -1;
155 			boolean done = false;
156 			while (++i < len) {
157 				int next = read();
158 				if (next == 65535 || next == -1) { // Pushback causes -1 to convert to 65535
159 					done = true;
160 					break;
161 				}
162 				cbuf[off + i] = (char) next;
163 			}
164 			if (i == 0 && done)
165 				i = -1;
166 			return i;
167 		}
168 
169 		/**
170 		 * Tests incoming data for match with char sequence, resets reader when done.
171 		 */
172 		private boolean atSequence(char[] sequence) throws IOException {
173 			boolean result = true;
174 			int i = -1;
175 			int[] data = new int[sequence.length];
176 			while (++i < sequence.length && result == true) {
177 				data[i] = super.read();
178 				if ((char) data[i] != sequence[i])
179 					result = false; // includes case where end of stream reached
180 			}
181 			for (int j = i - 1; j >= 0; j--) {
182 				this.unread(data[j]);
183 			}
184 			return result;
185 		}
186 	}
187 
188 }