KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > client > am > ByteArrayCombinerStream


1 /*
2     Derby - Class org.apache.derby.client.am.ByteArrayCombinerStream
3
4     Licensed to the Apache Software Foundation (ASF) under one
5     or more contributor license agreements. See the NOTICE file
6     distributed with this work for additional information
7     regarding copyright ownership. The ASF licenses this file
8     to you under the Apache License, Version 2.0 (the
9     "License"); you may not use this file except in compliance
10     with 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,
15     software distributed under the License is distributed on an
16     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17     KIND, either express or implied. See the License for the
18     specific language governing permissions and limitations
19     under the License.
20 */

21 package org.apache.derby.client.am;
22
23 import java.io.InputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.util.ArrayList JavaDoc;
26
27 /**
28  * A stream whose source is a list of byte arrays.
29  *
30  * This class was created when first implementing the JDBC 4 length less
31  * overloads in the client driver. The reason was missing support for
32  * streaming data with unknown length from the client to the server.
33  *
34  * The purpose of the stream is to avoid having to repeatedly copy data to grow
35  * the byte buffer, or doing a single big copy to combine the byte arrays in
36  * the end. This is important for the temporary solution, since we must
37  * materialize the stream to find the length anyway.
38  *
39  * If there is less data available than the specified length, an exception is
40  * thrown. Available data is determined by the length of the byte arrays, not
41  * the contents of them. A byte array with all 0's is considered valid data.
42  *
43  * Besides from truncation, this stream does not change the underlying data in
44  * any way.
45  */

46 public class ByteArrayCombinerStream
47     extends InputStream JavaDoc {
48
49     /** A list of the arrays to combine. */
50     private final ArrayList JavaDoc arrays;
51     /** Length of the stream. */
52     private final long specifiedLength;
53     /** Global offset into the whole stream. */
54     private long gOffset = 0;
55     /** Index of the array we are currently reading from. */
56     private int arrayIndex = 0;
57     /** The array we are currently reading from. */
58     private byte[] curArray;
59     /** The local offset into the current array. */
60     private int off = 0;
61
62     /**
63      * Create a stream whose source is a list of byte arrays.
64      *
65      * @param arraysIn an <code>ArrayList</code> with references to the source
66      * byte arrays. The references are copied to a new
67      * <code>ArrayList</code> instance.
68      * @param length the length of the stream. Never published outside
69      * this object. Note that the length specified can be shorter
70      * than the actual number of bytes in the byte arrays.
71      * @throws IllegalArgumentException if there is less data available than
72      * specified by <code>length</code>, or <code>length</code> is
73      * negative.
74      */

75     public ByteArrayCombinerStream(ArrayList JavaDoc arraysIn, long length) {
76         // Don't allow negative length.
77
if (length < 0) {
78             throw new IllegalArgumentException JavaDoc("Length cannot be negative: " +
79                     length);
80         }
81         this.specifiedLength = length;
82         long tmpRemaining = length;
83         if (arraysIn != null && arraysIn.size() > 0) {
84             // Copy references to the byte arrays to a new ArrayList.
85
int arrayCount = arraysIn.size();
86             byte[] tmpArray;
87             arrays = new ArrayList JavaDoc(arrayCount);
88             // Truncate data if there are more bytes then specified.
89
// Done to simplify boundary checking in the read-methods.
90
for (int i=0; i < arrayCount && tmpRemaining > 0; i++) {
91                 tmpArray = (byte[])arraysIn.get(i);
92                 if (tmpRemaining < tmpArray.length) {
93                     // Create a new shrunk array.
94
byte[] shrunkArray =
95                         new byte[(int)(tmpRemaining)];
96                     System.arraycopy(tmpArray, 0,
97                                      shrunkArray, 0, shrunkArray.length);
98                     arrays.add(shrunkArray);
99                     tmpRemaining -= shrunkArray.length;
100                     break;
101                 } else {
102                     // Add the whole array.
103
tmpRemaining -= tmpArray.length;
104                     arrays.add(tmpArray);
105                 }
106             }
107             // Set the first array as the current one.
108
curArray = nextArray();
109         } else {
110             // Specify gOffset so available returns 0;
111
gOffset = length;
112             arrays = null;
113         }
114         // If we don't have enough data, throw exception.
115
if (tmpRemaining > 0) {
116             throw new IllegalArgumentException JavaDoc("Not enough data, " +
117                     tmpRemaining + " bytes short of specified length " +
118                     length);
119         }
120     }
121
122     /**
123      * Read a single byte.
124      *
125      * @return a byte, or <code>-1</code> if the end-of-stream is reached
126      */

127     public int read()
128             throws IOException JavaDoc {
129         if (curArray == null) {
130             return -1;
131         }
132         if (off >= curArray.length) {
133             curArray = nextArray();
134             if (curArray == null) {
135                 return -1;
136             }
137         }
138         gOffset++;
139         return curArray[off++];
140     }
141
142     /**
143      * Reads up to len bytes of data from the input stream into an array of
144      * bytes.
145      * An attempt is made to read as many as <code>len</code> bytes, but
146      * a smaller number may be read. The number of bytes actually read
147      * is returned as an integer.
148      *
149      * @param buf the array to copy bytes into
150      * @param offset offset into the array
151      * @param length the maximum number of bytes to read
152      * @return the number of bytes read, or <code>-1</code> if end-of-stream
153      * is reached
154      */

155     public int read(byte[] buf, int offset, int length)
156             throws IOException JavaDoc {
157         int read = 0;
158         if (curArray == null) {
159             return -1;
160         }
161         if (length <= (curArray.length - off)) {
162             System.arraycopy(curArray, off, buf, offset, length);
163             off += length;
164             gOffset += length;
165             read = length;
166         } else {
167             int toRead = 0;
168             while (curArray != null && read < length) {
169                 toRead = Math.min(curArray.length - off, length - read);
170                 System.arraycopy(curArray, off, buf, offset + read, toRead);
171                 read += toRead;
172                 gOffset += toRead;
173                 off += toRead;
174                 if ( off < curArray.length) {
175                     break;
176                 }
177                 curArray = nextArray();
178             }
179         }
180         return read;
181     }
182
183     /**
184      * Return the number of available bytes.
185      * The method assumes the specified length of the stream is correct.
186      *
187      * @return number of available bytes
188      */

189     public int available() {
190         return (int)(specifiedLength - gOffset);
191     }
192
193     /**
194      * Fetch the next array to read data from.
195      * The reference in the <code>ArrayList</code> is cleared when the array
196      * is "taken out".
197      *
198      * @return a <code>byte[]</code>-object, or <code>null</code> if there are
199      * no more arrays
200      */

201     private byte[] nextArray() {
202         if (arrayIndex >= arrays.size()) {
203             return null;
204         }
205         byte[] tmp = (byte[])arrays.get(arrayIndex);
206         arrays.set(arrayIndex++, null);
207         off = 0;
208         return tmp;
209     }
210 } // End of class ByteArrayCombinerStream
211
Popular Tags