KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > iapi > types > RawToBinaryFormatStream


1 /*
2
3    Derby - Class org.apache.derby.iapi.types.RawToBinaryFormatStream
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.iapi.types;
23
24 import java.io.InputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.EOFException JavaDoc;
27
28 import org.apache.derby.iapi.services.io.DerbyIOException;
29 import org.apache.derby.iapi.services.io.LimitInputStream;
30 import org.apache.derby.iapi.services.i18n.MessageService;
31 import org.apache.derby.iapi.reference.SQLState;
32
33 /**
34     Stream that takes a raw input stream and converts it
35     to the on-disk format of the binary types by prepending the
36     length of the value.
37     <P>
38     If the length of the stream is known then it is encoded
39     as the first bytes in the stream in the defined format.
40     <BR>
41     If the length is unknown then the first four bytes will
42     be zero, indicating unknown length.
43     <BR>
44     Note: This stream cannot be re-used. Once end of file is
45     reached, the next read call will throw an EOFException
46     
47     @see SQLBinary
48 */

49 public final class RawToBinaryFormatStream extends LimitInputStream {
50
51     /**
52      * Number of bytes of length encoding.
53      *
54      */

55     private int encodedOffset;
56     
57     /**
58      * Encoding of the length in bytes which will be
59      * seen as the first encodedLength.length bytes of
60      * this stream.
61      */

62     private byte[] encodedLength;
63     
64     // flag to indicate the stream has already been read
65
// and eof reached
66
private boolean eof = false;
67
68     /**
69      * The length of the stream.
70      * Unknown if less than 0.
71      */

72     private final int length;
73     /**
74      * The maximum allowed length for the stream.
75      * No limit if less than 0.
76      */

77     private final int maximumLength;
78     /**
79      * The type of the column the stream is inserted into.
80      * Used for length less streams, <code>null</code> if not in use.
81      */

82     private final String JavaDoc typeName;
83
84     /**
85      * Create a binary on-disk stream from the given <code>InputStream</code>.
86      *
87      * The on-disk stream prepends a length encoding, and validates that the
88      * actual length of the stream matches the specified length (as according
89      * to JDBC 3.0).
90      *
91      * @param in application's raw binary stream passed into JDBC layer
92      * @param length length of the stream
93      * @throws IllegalArgumentException if <code>length</code> is negative.
94      * This exception should never be exposed to the user, and seeing it
95      * means a programming error exists in the code.
96      */

97     public RawToBinaryFormatStream(InputStream JavaDoc in, int length) {
98         super(in);
99         if (length < 0) {
100             throw new IllegalArgumentException JavaDoc(
101                     "Stream length cannot be negative: " + length);
102         }
103         this.length = length;
104         this.maximumLength = -1;
105         this.typeName = null;
106
107         setLimit(length);
108         
109         if (length <= 31)
110         {
111             encodedLength = new byte[1];
112             encodedLength[0] = (byte) (0x80 | (length & 0xff));
113         }
114         else if (length <= 0xFFFF)
115         {
116             encodedLength = new byte[3];
117             encodedLength[0] = (byte) 0xA0;
118             encodedLength[1] = (byte)(length >> 8);
119             encodedLength[2] = (byte)(length);
120         }
121         else
122         {
123             encodedLength = new byte[5];
124             encodedLength[0] = (byte) 0xC0;
125             encodedLength[1] = (byte)(length >> 24);
126             encodedLength[2] = (byte)(length >> 16);
127             encodedLength[3] = (byte)(length >> 8);
128             encodedLength[4] = (byte)(length);
129         }
130     }
131
132     /**
133      * Create a binary on-disk stream from the given <code>InputStream</code>
134      * of unknown length.
135      *
136      * A limit is placed on the maximum length of the stream.
137      *
138      * @param in the application stream
139      * @param maximumLength maximum length of the column data is inserted into
140      * @param typeName type name for the column data is inserted into
141      * @throws IllegalArgumentException if maximum length is negative, or type
142      * name is <code>null<code>. This exception should never be exposed
143      * to the user, and seeing it means a programming error exists in the
144      * code. Although a missing type name is not critical, an exception is
145      * is thrown to signal the intended use of this constructor.
146      */

147     public RawToBinaryFormatStream(InputStream JavaDoc in,
148                                    int maximumLength,
149                                    String JavaDoc typeName) {
150         super(in);
151         if (maximumLength < 0) {
152             throw new IllegalArgumentException JavaDoc("Maximum length for a capped " +
153                     "stream cannot be negative: " + maximumLength);
154         }
155         if (typeName == null) {
156             throw new IllegalArgumentException JavaDoc("Type name cannot be null");
157         }
158         this.length = -1;
159         this.maximumLength = maximumLength;
160         this.typeName = typeName;
161         // Unknown length, four zero bytes.
162
encodedLength = new byte[4];
163         setLimit(maximumLength);
164     }
165
166     /**
167         Read from the wrapped stream prepending the intial bytes if needed.
168         If stream has been read, and eof reached, in that case any subsequent
169         read will throw an EOFException
170     */

171     public int read() throws IOException JavaDoc {
172
173         if ( eof )
174             throw new EOFException JavaDoc(MessageService.getTextMessage
175                         (SQLState.STREAM_EOF));
176         
177         if (encodedOffset < encodedLength.length) {
178             return encodedLength[encodedOffset++] & 0xff;
179         }
180
181         int ret = super.read();
182
183         if (ret == -1)
184             checkSufficientData();
185
186         return ret;
187     }
188
189     /**
190         JDBC 3.0 (from tutorial book) requires that an
191         input stream has the correct number of bytes in
192         the stream.
193     */

194     private void checkSufficientData() throws IOException JavaDoc
195     {
196         // if we reached here, then read call returned -1, and we
197
// have already reached the end of stream, so set eof=true
198
// so that subsequent reads on this stream will return an
199
// EOFException
200
eof = true;
201         if (!limitInPlace)
202         return;
203
204         int remainingBytes = clearLimit();
205
206         if (length > -1 && remainingBytes > 0) {
207             throw new DerbyIOException(
208                         MessageService.getTextMessage(
209                                     SQLState.SET_STREAM_INEXACT_LENGTH_DATA),
210                         SQLState.SET_STREAM_INEXACT_LENGTH_DATA);
211         }
212
213         // if we had a limit try reading one more byte.
214
// JDBC 3.0 states the stream muct have the correct number of characters in it.
215
if (remainingBytes == 0) {
216             int c;
217             try
218             {
219                 c = super.read();
220             }
221             catch (IOException JavaDoc ioe) {
222                 c = -1;
223             }
224             if (c != -1) {
225                 if (length > -1) {
226                     // Stream is not capped, and should have matched the
227
// specified length.
228
throw new DerbyIOException(
229                                 MessageService.getTextMessage(
230                                     SQLState.SET_STREAM_INEXACT_LENGTH_DATA),
231                                 SQLState.SET_STREAM_INEXACT_LENGTH_DATA);
232                 } else {
233                     // Stream is capped, and has exceeded the maximum length.
234
throw new DerbyIOException(
235                             MessageService.getTextMessage(
236                                     SQLState.LANG_STRING_TRUNCATION,
237                                     typeName,
238                                     "XXXX",
239                                     String.valueOf(maximumLength)),
240                             SQLState.LANG_STRING_TRUNCATION);
241                 }
242             }
243         }
244     }
245
246     /**
247         Read from the wrapped stream prepending the intial bytes if needed.
248         If stream has been read, and eof reached, in that case any subsequent
249         read will throw an EOFException
250     */

251     public int read(byte b[], int off, int len) throws IOException JavaDoc {
252   
253         if ( eof )
254             throw new EOFException JavaDoc(MessageService.getTextMessage(SQLState.STREAM_EOF));
255
256         int elen = encodedLength.length - encodedOffset;
257
258         if (elen != 0) {
259             if (len < elen)
260                 elen = len;
261             System.arraycopy(encodedLength, encodedOffset,
262                     b, off, elen);
263
264             encodedOffset += elen;
265
266             off += elen;
267             len -= elen;
268             
269             if (len == 0)
270                 return elen;
271         }
272
273         int realRead = super.read(b, off, len);
274
275         if (realRead < 0)
276         {
277             if (elen != 0)
278                 return elen;
279
280             checkSufficientData();
281             return realRead;
282         }
283
284         return elen + realRead;
285     }
286 }
287
Popular Tags