1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.jdbc.UTF8Reader |
4 | |
5 | Copyright 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.jdbc; |
22 | |
23 | import java.io.InputStream; |
24 | import java.io.Reader; |
25 | import java.io.IOException; |
26 | import java.io.UTFDataFormatException; |
27 | import java.io.EOFException; |
28 | import java.sql.SQLException; |
29 | |
30 | /** |
31 | */ |
32 | public final class UTF8Reader extends Reader |
33 | { |
34 | |
35 | private InputStream in; |
36 | private final long utfLen; // bytes |
37 | private long utfCount; // bytes |
38 | private long readerCharCount; // characters |
39 | private long maxFieldSize; // characeters |
40 | |
41 | private char[] buffer = new char[8 * 1024]; |
42 | private int charactersInBuffer; // within buffer |
43 | private int readPositionInBuffer; |
44 | |
45 | private boolean noMoreReads; |
46 | |
47 | // maintain a reference to the parent object so that it can't get |
48 | // garbage collected until we are done with the stream. |
49 | private ConnectionChild parent; |
50 | |
51 | public UTF8Reader( |
52 | InputStream in, |
53 | long maxFieldSize, |
54 | ConnectionChild parent, |
55 | Object synchronization) |
56 | throws IOException |
57 | { |
58 | super(synchronization); |
59 | |
60 | this.in = in; |
61 | this.maxFieldSize = maxFieldSize; |
62 | this.parent = parent; |
63 | |
64 | synchronized (lock) { |
65 | this.utfLen = readUnsignedShort(); |
66 | } |
67 | } |
68 | |
69 | /* |
70 | ** Reader implemention. |
71 | */ |
72 | public int read() throws IOException |
73 | { |
74 | synchronized (lock) { |
75 | |
76 | // check if closed.. |
77 | if (noMoreReads) |
78 | throw new IOException(); |
79 | |
80 | if (readPositionInBuffer >= charactersInBuffer) { |
81 | if (fillBuffer()) { |
82 | return -1; |
83 | } |
84 | readPositionInBuffer = 0; |
85 | } |
86 | |
87 | return buffer[readPositionInBuffer++]; |
88 | } |
89 | } |
90 | |
91 | public int read(char[] cbuf, int off, int len) throws IOException |
92 | { |
93 | synchronized (lock) { |
94 | // check if closed.. |
95 | if (noMoreReads) |
96 | throw new IOException(); |
97 | |
98 | if (readPositionInBuffer >= charactersInBuffer) { |
99 | if (fillBuffer()) { |
100 | return -1; |
101 | } |
102 | readPositionInBuffer = 0; |
103 | } |
104 | |
105 | int remainingInBuffer = charactersInBuffer - readPositionInBuffer; |
106 | |
107 | if (len > remainingInBuffer) |
108 | len = remainingInBuffer; |
109 | |
110 | System.arraycopy(buffer, readPositionInBuffer, cbuf, off, len); |
111 | readPositionInBuffer += len; |
112 | |
113 | return len; |
114 | } |
115 | } |
116 | |
117 | public long skip(long len) throws IOException { |
118 | synchronized (lock) { |
119 | // check if closed.. |
120 | if (noMoreReads) |
121 | throw new IOException(); |
122 | |
123 | if (readPositionInBuffer >= charactersInBuffer) { |
124 | // do somthing |
125 | if (fillBuffer()) { |
126 | return -1; |
127 | } |
128 | readPositionInBuffer = 0; |
129 | } |
130 | |
131 | int remainingInBuffer = charactersInBuffer - readPositionInBuffer; |
132 | |
133 | if (len > remainingInBuffer) |
134 | len = remainingInBuffer; |
135 | |
136 | readPositionInBuffer += len; |
137 | |
138 | return len; |
139 | } |
140 | |
141 | } |
142 | |
143 | public void close() |
144 | { |
145 | synchronized (lock) { |
146 | closeIn(); |
147 | parent = null; |
148 | noMoreReads = true; |
149 | } |
150 | } |
151 | |
152 | /* |
153 | ** Methods just for Cloudscape's JDBC driver |
154 | */ |
155 | |
156 | public int readInto(StringBuffer sb, int len) throws IOException { |
157 | |
158 | synchronized (lock) { |
159 | if (readPositionInBuffer >= charactersInBuffer) { |
160 | if (fillBuffer()) { |
161 | return -1; |
162 | } |
163 | readPositionInBuffer = 0; |
164 | } |
165 | |
166 | int remainingInBuffer = charactersInBuffer - readPositionInBuffer; |
167 | |
168 | if (len > remainingInBuffer) |
169 | len = remainingInBuffer; |
170 | sb.append(buffer, readPositionInBuffer, len); |
171 | |
172 | readPositionInBuffer += len; |
173 | |
174 | return len; |
175 | } |
176 | } |
177 | int readAsciiInto(byte[] abuf, int off, int len) throws IOException { |
178 | |
179 | synchronized (lock) { |
180 | if (readPositionInBuffer >= charactersInBuffer) { |
181 | if (fillBuffer()) { |
182 | return -1; |
183 | } |
184 | readPositionInBuffer = 0; |
185 | } |
186 | |
187 | int remainingInBuffer = charactersInBuffer - readPositionInBuffer; |
188 | |
189 | if (len > remainingInBuffer) |
190 | len = remainingInBuffer; |
191 | |
192 | char[] lbuffer = buffer; |
193 | for (int i = 0; i < len; i++) { |
194 | char c = lbuffer[readPositionInBuffer + i]; |
195 | byte cb; |
196 | if (c <= 255) |
197 | cb = (byte) c; |
198 | else |
199 | cb = (byte) '?'; // Question mark - out of range character. |
200 | |
201 | abuf[off + i] = cb; |
202 | } |
203 | |
204 | readPositionInBuffer += len; |
205 | |
206 | return len; |
207 | } |
208 | } |
209 | |
210 | /* |
211 | ** internal implementation |
212 | */ |
213 | |
214 | |
215 | private void closeIn() { |
216 | if (in != null) { |
217 | try { |
218 | in.close(); |
219 | } catch (IOException ioe) { |
220 | } finally { |
221 | in = null; |
222 | } |
223 | } |
224 | } |
225 | private IOException utfFormatException(String s) { |
226 | noMoreReads = true; |
227 | closeIn(); |
228 | return new UTFDataFormatException(s); |
229 | } |
230 | |
231 | private IOException utfFormatException() { |
232 | noMoreReads = true; |
233 | closeIn(); |
234 | return new UTFDataFormatException(); |
235 | } |
236 | |
237 | /** |
238 | Fill the buffer, return true if eof has been reached. |
239 | */ |
240 | private boolean fillBuffer() throws IOException |
241 | { |
242 | if (in == null) |
243 | return true; |
244 | |
245 | charactersInBuffer = 0; |
246 | |
247 | try { |
248 | try { |
249 | |
250 | parent.setupContextStack(); |
251 | |
252 | readChars: |
253 | while ( |
254 | (charactersInBuffer < buffer.length) && |
255 | ((utfCount < utfLen) || (utfLen == 0)) && |
256 | ((maxFieldSize == 0) || (readerCharCount < maxFieldSize)) |
257 | ) |
258 | { |
259 | int c = in.read(); |
260 | if (c == -1) { |
261 | if (utfLen == 0) { |
262 | closeIn(); |
263 | break readChars; |
264 | } |
265 | throw utfFormatException(); |
266 | } |
267 | |
268 | int finalChar; |
269 | switch (c >> 4) { |
270 | case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: |
271 | // 0xxxxxxx |
272 | utfCount++; |
273 | finalChar = c; |
274 | break; |
275 | |
276 | case 12: case 13: |
277 | { |
278 | // 110x xxxx 10xx xxxx |
279 | utfCount += 2; |
280 | int char2 = in.read(); |
281 | if (char2 == -1) |
282 | throw utfFormatException(); |
283 | |
284 | if ((char2 & 0xC0) != 0x80) |
285 | throw utfFormatException(); |
286 | finalChar = (((c & 0x1F) << 6) | (char2 & 0x3F)); |
287 | break; |
288 | } |
289 | |
290 | case 14: |
291 | { |
292 | // 1110 xxxx 10xx xxxx 10xx xxxx |
293 | utfCount += 3; |
294 | int char2 = in.read(); |
295 | int char3 = in.read(); |
296 | if (char2 == -1 || char3 == -1) |
297 | throw utfFormatException(); |
298 | |
299 | if ((c == 0xE0) && (char2 == 0) && (char3 == 0)) |
300 | { |
301 | if (utfLen == 0) { |
302 | // we reached the end of a long string, |
303 | // that was terminated with |
304 | // (11100000, 00000000, 00000000) |
305 | closeIn(); |
306 | break readChars; |
307 | } |
308 | throw utfFormatException(); |
309 | } |
310 | |
311 | if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) |
312 | throw utfFormatException(); |
313 | |
314 | finalChar = (((c & 0x0F) << 12) | |
315 | ((char2 & 0x3F) << 6) | |
316 | ((char3 & 0x3F) << 0)); |
317 | } |
318 | break; |
319 | |
320 | default: |
321 | // 10xx xxxx, 1111 xxxx |
322 | throw utfFormatException(); |
323 | } |
324 | |
325 | buffer[charactersInBuffer++] = (char) finalChar; |
326 | readerCharCount++; |
327 | } |
328 | if (utfLen != 0 && utfCount > utfLen) |
329 | throw utfFormatException("utfCount " + utfCount + " utfLen " + utfLen); |
330 | |
331 | if (charactersInBuffer != 0) |
332 | return false; |
333 | |
334 | closeIn(); |
335 | return true; |
336 | } finally { |
337 | parent.restoreContextStack(); |
338 | } |
339 | } catch (SQLException sqle) { |
340 | throw new IOException(sqle.getSQLState() + ":" + sqle.getMessage()); |
341 | } |
342 | } |
343 | |
344 | |
345 | // this method came from java.io.DataInputStream |
346 | private final int readUnsignedShort() throws IOException { |
347 | int ch1 = in.read(); |
348 | int ch2 = in.read(); |
349 | if ((ch1 | ch2) < 0) |
350 | throw new EOFException(); |
351 | |
352 | return (ch1 << 8) + (ch2 << 0); |
353 | } |
354 | } |