001: /*
002:
003: Derby - Class org.apache.derby.impl.jdbc.UTF8Reader
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.jdbc;
023:
024: import java.io.InputStream;
025: import java.io.Reader;
026: import java.io.IOException;
027: import java.io.UTFDataFormatException;
028: import java.io.EOFException;
029: import java.sql.SQLException;
030:
031: /**
032: */
033: public final class UTF8Reader extends Reader {
034:
035: private InputStream in;
036: private final long utfLen; // bytes
037: private long utfCount; // bytes
038: private long readerCharCount; // characters
039: private long maxFieldSize; // characeters
040:
041: private char[] buffer = new char[8 * 1024];
042: private int charactersInBuffer; // within buffer
043: private int readPositionInBuffer;
044:
045: private boolean noMoreReads;
046:
047: // maintain a reference to the parent object so that it can't get
048: // garbage collected until we are done with the stream.
049: private ConnectionChild parent;
050:
051: public UTF8Reader(InputStream in, long maxFieldSize,
052: ConnectionChild parent, Object synchronization)
053: throws IOException {
054: super (synchronization);
055:
056: this .in = in;
057: this .maxFieldSize = maxFieldSize;
058: this .parent = parent;
059:
060: synchronized (lock) {
061: this .utfLen = readUnsignedShort();
062: }
063: }
064:
065: /*
066: ** Reader implemention.
067: */
068: public int read() throws IOException {
069: synchronized (lock) {
070:
071: // check if closed..
072: if (noMoreReads)
073: throw new IOException();
074:
075: if (readPositionInBuffer >= charactersInBuffer) {
076: if (fillBuffer()) {
077: return -1;
078: }
079: readPositionInBuffer = 0;
080: }
081:
082: return buffer[readPositionInBuffer++];
083: }
084: }
085:
086: public int read(char[] cbuf, int off, int len) throws IOException {
087: synchronized (lock) {
088: // check if closed..
089: if (noMoreReads)
090: throw new IOException();
091:
092: if (readPositionInBuffer >= charactersInBuffer) {
093: if (fillBuffer()) {
094: return -1;
095: }
096: readPositionInBuffer = 0;
097: }
098:
099: int remainingInBuffer = charactersInBuffer
100: - readPositionInBuffer;
101:
102: if (len > remainingInBuffer)
103: len = remainingInBuffer;
104:
105: System.arraycopy(buffer, readPositionInBuffer, cbuf, off,
106: len);
107: readPositionInBuffer += len;
108:
109: return len;
110: }
111: }
112:
113: public long skip(long len) throws IOException {
114: synchronized (lock) {
115: // check if closed..
116: if (noMoreReads)
117: throw new IOException();
118:
119: if (readPositionInBuffer >= charactersInBuffer) {
120: // do somthing
121: if (fillBuffer()) {
122: return -1;
123: }
124: readPositionInBuffer = 0;
125: }
126:
127: int remainingInBuffer = charactersInBuffer
128: - readPositionInBuffer;
129:
130: if (len > remainingInBuffer)
131: len = remainingInBuffer;
132:
133: readPositionInBuffer += len;
134:
135: return len;
136: }
137:
138: }
139:
140: public void close() {
141: synchronized (lock) {
142: closeIn();
143: parent = null;
144: noMoreReads = true;
145: }
146: }
147:
148: /*
149: ** Methods just for Cloudscape's JDBC driver
150: */
151:
152: public int readInto(StringBuffer sb, int len) throws IOException {
153:
154: synchronized (lock) {
155: if (readPositionInBuffer >= charactersInBuffer) {
156: if (fillBuffer()) {
157: return -1;
158: }
159: readPositionInBuffer = 0;
160: }
161:
162: int remainingInBuffer = charactersInBuffer
163: - readPositionInBuffer;
164:
165: if (len > remainingInBuffer)
166: len = remainingInBuffer;
167: sb.append(buffer, readPositionInBuffer, len);
168:
169: readPositionInBuffer += len;
170:
171: return len;
172: }
173: }
174:
175: int readAsciiInto(byte[] abuf, int off, int len) throws IOException {
176:
177: synchronized (lock) {
178: if (readPositionInBuffer >= charactersInBuffer) {
179: if (fillBuffer()) {
180: return -1;
181: }
182: readPositionInBuffer = 0;
183: }
184:
185: int remainingInBuffer = charactersInBuffer
186: - readPositionInBuffer;
187:
188: if (len > remainingInBuffer)
189: len = remainingInBuffer;
190:
191: char[] lbuffer = buffer;
192: for (int i = 0; i < len; i++) {
193: char c = lbuffer[readPositionInBuffer + i];
194: byte cb;
195: if (c <= 255)
196: cb = (byte) c;
197: else
198: cb = (byte) '?'; // Question mark - out of range character.
199:
200: abuf[off + i] = cb;
201: }
202:
203: readPositionInBuffer += len;
204:
205: return len;
206: }
207: }
208:
209: /*
210: ** internal implementation
211: */
212:
213: private void closeIn() {
214: if (in != null) {
215: try {
216: in.close();
217: } catch (IOException ioe) {
218: } finally {
219: in = null;
220: }
221: }
222: }
223:
224: private IOException utfFormatException(String s) {
225: noMoreReads = true;
226: closeIn();
227: return new UTFDataFormatException(s);
228: }
229:
230: private IOException utfFormatException() {
231: noMoreReads = true;
232: closeIn();
233: return new UTFDataFormatException();
234: }
235:
236: /**
237: Fill the buffer, return true if eof has been reached.
238: */
239: private boolean fillBuffer() throws IOException {
240: if (in == null)
241: return true;
242:
243: charactersInBuffer = 0;
244:
245: try {
246: try {
247:
248: parent.setupContextStack();
249:
250: readChars: while ((charactersInBuffer < buffer.length)
251: && ((utfCount < utfLen) || (utfLen == 0))
252: && ((maxFieldSize == 0) || (readerCharCount < maxFieldSize))) {
253: int c = in.read();
254: if (c == -1) {
255: if (utfLen == 0) {
256: closeIn();
257: break readChars;
258: }
259: throw utfFormatException();
260: }
261:
262: int finalChar;
263: switch (c >> 4) {
264: case 0:
265: case 1:
266: case 2:
267: case 3:
268: case 4:
269: case 5:
270: case 6:
271: case 7:
272: // 0xxxxxxx
273: utfCount++;
274: finalChar = c;
275: break;
276:
277: case 12:
278: case 13: {
279: // 110x xxxx 10xx xxxx
280: utfCount += 2;
281: int char2 = in.read();
282: if (char2 == -1)
283: throw utfFormatException();
284:
285: if ((char2 & 0xC0) != 0x80)
286: throw utfFormatException();
287: finalChar = (((c & 0x1F) << 6) | (char2 & 0x3F));
288: break;
289: }
290:
291: case 14: {
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: if (utfLen == 0) {
301: // we reached the end of a long string,
302: // that was terminated with
303: // (11100000, 00000000, 00000000)
304: closeIn();
305: break readChars;
306: }
307: throw utfFormatException();
308: }
309:
310: if (((char2 & 0xC0) != 0x80)
311: || ((char3 & 0xC0) != 0x80))
312: throw utfFormatException();
313:
314: finalChar = (((c & 0x0F) << 12)
315: | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
316: }
317: break;
318:
319: default:
320: // 10xx xxxx, 1111 xxxx
321: throw utfFormatException();
322: }
323:
324: buffer[charactersInBuffer++] = (char) finalChar;
325: readerCharCount++;
326: }
327: if (utfLen != 0 && utfCount > utfLen)
328: throw utfFormatException("utfCount " + utfCount
329: + " 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() + ":"
341: + sqle.getMessage());
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: }
|