##### Coverage Report - org.melati.util.DelimitedBufferedInputStream

Classes in this File | Line Coverage | Branch Coverage | Complexity
DelimitedBufferedInputStream |
78% | 94/120
66% | 66/100
| 7.1;7.1

1 |  |
     /*
2 |  |
      * $Source: /usr/cvsroot/melati/melati/src/site/resources/withWebmacro/org.melati.util.DelimitedBufferedInputStream.html,v $
3 |  |
      * $Revision: 1.1 $
4 |  |
      *
5 |  |
      * Copyright (C) 2000 Myles Chippendale
6 |  |
      *
7 |  |
      * Part of Melati (http://melati.org), a framework for the rapid
8 |  |
      * development of clean, maintainable web applications.
9 |  |
      *
10 |  |
      * Melati is free software; Permission is granted to copy, distribute
11 |  |
      * and/or modify this software under the terms either:
12 |  |
      *
13 |  |
      * a) the GNU General Public License as published by the Free Software
14 |  |
      *    Foundation; either version 2 of the License, or (at your option)
15 |  |
      *    any later version,
16 |  |
      *
17 |  |
      *    or
18 |  |
      *
19 |  |
      * b) any version of the Melati Software License, as published
20 |  |
      *    at http://melati.org
21 |  |
      *
22 |  |
      * You should have received a copy of the GNU General Public License and
23 |  |
      * the Melati Software License along with this program;
24 |  |
      * if not, write to the Free Software Foundation, Inc.,
25 |  |
      * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26 |  |
      * GNU General Public License and visit http://melati.org to obtain the
27 |  |
      * Melati Software License.
28 |  |
      *
29 |  |
      * Feel free to contact the Developers of Melati (http://melati.org),
30 |  |
      * if you would like to work out a different arrangement than the options
31 |  |
      * outlined here.  It is our intention to allow Melati to be used by as
32 |  |
      * wide an audience as possible.
33 |  |
      *
34 |  |
      * This program is distributed in the hope that it will be useful,
35 |  |
      * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 |  |
      * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37 |  |
      * GNU General Public License for more details.
38 |  |
      *
39 |  |
      * Contact details for copyright holder:
40 |  |
      *
41 |  |
      *     Myles Chippendale <mylesc At paneris.org>
42 |  |
      *     http://paneris.org/
43 |  |
      *     29 Stanley Road, Oxford, OX4 1QY, UK
44 |  |
      */
45 |  |
46 |  |
     package org.melati.util;
47 |  |
48 |  |
     import java.io.BufferedInputStream;
49 |  |
     import java.io.IOException;
50 |  |
     import java.io.InputStream;
51 |  |
52 |  |
     /**
53 |  |
      * Like a <code>BufferedInputStream</code> except it has a new function
54 |  |
      * {@link #readToDelimiter} which will only read bytes upto the start
55 |  |
      * of any occurrence of the delimiter in the InputStream.
56 |  |
      *
57 |  |
      * @see java.io.BufferedInputStream
58 |  |
      */
59 |  |
60 |  |
     public class DelimitedBufferedInputStream extends BufferedInputStream {
61 |  |
62 | 48 |
         private int potentialMatch = -1;
63 |  |
64 |  |
         /**
65 |  |
          * Creates a <code>DelimitedBufferedInputStream</code>
66 |  |
          * and saves its  argument, the input stream
67 |  |
          * <code>in</code>, for later use. An internal
68 |  |
          * buffer array is created and  stored in <code>buf</code>.
69 |  |
          *
70 |  |
          * @param   in   the underlying input stream.
71 |  |
          **/
72 |  |
         public DelimitedBufferedInputStream(InputStream in) {
73 | 4 |
            super(in);
74 | 4 |
         }
75 |  |
76 |  |
         /**
77 |  |
          * Creates a <code>DelimitedBufferedInputStream</code>
78 |  |
          * with the specified buffer size,
79 |  |
          * and saves its  argument, the input stream
80 |  |
          * <code>in</code>, for later use.  An internal
81 |  |
          * buffer array of length  <code>size</code>
82 |  |
          * is created and stored in <code>buf</code>.
83 |  |
          *
84 |  |
          * @param   in     the underlying input stream.
85 |  |
          * @param   size   the buffer size.
86 |  |
          **/
87 |  |
         public DelimitedBufferedInputStream(InputStream in, int size) {
88 | 44 |
           super(in, size);
89 | 44 |
         }
90 |  |
91 |  |
         /**
92 |  |
          * Fills the buffer with more data, taking into account
93 |  |
          * shuffling and other tricks for dealing with marks.
94 |  |
          * Assumes that it is being called by a synchronized method.
95 |  |
          * This method also assumes that all data has already been read in,
96 |  |
          * hence pos > count.
97 |  |
          * <p>
98 |  |
          * This code is copy'n'pasted from BufferedInputString (it's private)
99 |  |
          **/
100 |  |
         private void fill() throws IOException {
101 | 166 |
           if (markpos < 0)
102 | 166 |
                pos = 0;                 /* no mark: throw away the buffer */
103 | 0 |
           else if (pos >= buf.length)   /* no room left in buffer */
104 | 0 |
                if (markpos > 0) {    /* can throw away early part of the buffer */
105 | 0 |
                    int sz = pos - markpos;
106 | 0 |
                    System.arraycopy(buf, markpos, buf, 0, sz);
107 | 0 |
                    pos = sz;
108 | 0 |
                    markpos = 0;
109 | 0 |
                 }
110 | 0 |
                 else if (buf.length >= marklimit) {
111 | 0 |
                    markpos = -1;    /* buffer got too big, invalidate mark */
112 | 0 |
                    pos = 0;         /* drop buffer contents */
113 |  |
                 }
114 |  |
                 else {              /* grow buffer */
115 | 0 |
                    int nsz = pos * 2;
116 | 0 |
                    if (nsz > marklimit)
117 | 0 |
                        nsz = marklimit;
118 | 0 |
                    byte nbuf[] = new byte[nsz];
119 | 0 |
                    System.arraycopy(buf, 0, nbuf, 0, pos);
120 | 0 |
                    buf = nbuf;
121 |  |
                 }
122 |  |
123 | 166 |
           count = pos;
124 | 166 |
           int n = in.read(buf, pos, buf.length - pos);
125 | 166 |
           if (n > 0)
126 | 166 |
             count = n + pos;
127 | 166 |
         }
128 |  |
129 |  |
         /**
130 |  |
          * Check to make sure that this stream has not been closed.
131 |  |
          * <p>
132 |  |
          * This code is copy'n'pasted from BufferedInputString (it's private)
133 |  |
          **/
134 |  |
         private void ensureOpen() throws IOException {
135 | 1038 |
           if (in == null)
136 | 0 |
             throw new IOException("Stream closed");
137 | 1038 |
         }
138 |  |
139 |  |
         /**
140 |  |
          * Read characters into a portion of an array, reading from the underlying
141 |  |
          * stream at most twice if necessary.
142 |  |
          **/
143 |  |
         private int readToDelimiter1(byte[] b, int off, int len, byte delim[])
144 |  |
               throws IOException {
145 | 1098 |
           int avail = count - pos;
146 | 1098 |
           if (avail <= 0) {
147 | 166 |
               fill();
148 | 166 |
               avail = count - pos;
149 | 166 |
               if (avail <= 0) return -1;
150 |  |
           }
151 | 1098 |
           int cnt = (avail < len) ? avail : len;
152 |  |
153 |  |
           // indexOf sets potentialMatch
154 | 1098 |
           int found = indexOf(buf, delim, pos, avail);
155 | 1098 |
           int max = cnt;
156 | 1098 |
           if (found != -1)
157 | 912 |
             max = found - pos;
158 | 186 |
           else if (potentialMatch != -1)
159 | 18 |
             max = potentialMatch - pos;
160 |  |
161 | 1098 |
           cnt = (max < cnt) ? max : cnt;
162 | 1098 |
           System.arraycopy(buf, pos, b, off, cnt);
163 | 1098 |
           pos += cnt;
164 |  |
165 |  |
           /* We want to shuffle the buffer so that it contains
166 |  |
            * at least delim.length bytes after potentialMatch,
167 |  |
            * if we have read that far into the buffer
168 |  |
            */
169 | 1098 |
           if (found == -1 && potentialMatch != -1 && pos == potentialMatch) {
170 |  |
171 |  |
             /* the number of bytes to shift the buffer down by */
172 | 12 |
             int halfmark = (markpos == -1)
173 |  |
                            ? potentialMatch
174 |  |
                            : markpos;
175 |  |
176 |  |
             /*
177 |  |
                the number of bytes we need to read to get all our potential
178 |  |
                delim into the buffer
179 |  |
              */
180 | 12 |
             int extras = potentialMatch + delim.length - count;
181 |  |
182 |  |
             /* the number of bytes we want to end up in buf */
183 | 12 |
             int nsz = potentialMatch + delim.length - halfmark;
184 | 12 |
             if (nsz > buf.length) {
185 |  |
               // we need to grow the buffer
186 | 6 |
               byte nbuf[] = new byte[nsz];
187 | 6 |
               System.arraycopy(buf, halfmark, nbuf, 0, count - halfmark);
188 | 6 |
               buf = nbuf;
189 | 6 |
             } else {
190 | 6 |
               System.arraycopy(buf, halfmark, buf, 0, count - halfmark);
191 |  |
             }
192 |  |
193 | 12 |
             pos = pos - halfmark;
194 | 12 |
             count = count - halfmark;
195 | 12 |
             markpos = (markpos == -1) ? -1 : 0;
196 | 12 |
             int n = in.read(buf, count, buf.length - count);
197 | 12 |
             if (n > 0)
198 | 12 |
               count += n;
199 |  |
200 |  |
             /* If, after reading, we don't have enough bytes to match delim,
201 |  |
                add the new bytes to our output */
202 | 12 |
             if (n < extras) {
203 | 6 |
               int unfilled = len - cnt;
204 | 6 |
               if (unfilled > 0) {
205 | 6 |
                 avail = count - pos;
206 | 6 |
                 int cnt2 = (unfilled < avail) ? unfilled : avail;
207 | 6 |
                 System.arraycopy(buf, pos, b, off+cnt, cnt2);
208 | 6 |
                 cnt += cnt2;
209 | 6 |
                 pos += cnt2;
210 |  |
               }
211 |  |
             }
212 |  |
           }
213 | 1098 |
           return cnt;
214 |  |
         }
215 |  |
216 |  |
         /**
217 |  |
          * Reads bytes from this byte-input stream into the specified byte array,
218 |  |
          * starting at the given offset, but we stop at the first occurrence of
219 |  |
          * delim.
220 |  |
          *
221 |  |
          * <p> This method implements the general contract of the corresponding
222 |  |
          * <code>{@link InputStream#read(byte[], int, int) read}</code> method of
223 |  |
          * the <code>{@link InputStream}</code> class.  As an additional
224 |  |
          * convenience, it attempts to read as many bytes as possible by repeatedly
225 |  |
          * invoking the <code>read</code> method of the underlying stream.  This
226 |  |
          * iterated <code>read</code> continues until one of the following
227 |  |
          * conditions becomes true: <ul>
228 |  |
          *
229 |  |
          *   <li> The specified number of bytes have been read,
230 |  |
          *
231 |  |
          *   <li> The <code>read</code> method of the underlying stream returns
232 |  |
          *   <code>-1</code>, indicating end-of-file, or
233 |  |
          *
234 |  |
          *   <li> The <code>available</code> method of the underlying stream
235 |  |
          *   returns zero, indicating that further input requests would block.
236 |  |
          *
237 |  |
          *   <li> We encounter an occurrence of delim in the underlying stream
238 |  |
          *
239 |  |
          * </ul> If the first <code>read</code> on the underlying stream returns
240 |  |
          * <code>-1</code> to indicate end-of-file then this method returns
241 |  |
          * <code>-1</code>.  Otherwise this method returns the number of bytes
242 |  |
          * actually read.
243 |  |
          *
244 |  |
          * <p> Subclasses of this class are encouraged, but not required, to
245 |  |
          * attempt to read as many bytes as possible in the same fashion.
246 |  |
          *
247 |  |
          * @param      b     destination buffer.
248 |  |
          * @param      off   offset at which to start storing bytes.
249 |  |
          * @param      len   maximum number of bytes to read.
250 |  |
          * @param      delim the delimiter which we should stop reading at.
251 |  |
          * @return     the number of bytes read, or <code>-1</code> if the end of
252 |  |
          *             the stream has been reached.
253 |  |
          * @exception  IOException  if an I/O error occurs.
254 |  |
          **/
255 |  |
       public synchronized int readToDelimiter(byte b[], int off,
256 |  |
                                               int len, byte delim[])
257 |  |
             throws IOException  {
258 | 1038 |
         ensureOpen();
259 | 1038 |
         if ((off < 0) || (off > b.length) || (len < 0) ||
260 |  |
                 ((off + len) > b.length) || ((off + len) < 0)) {
261 | 0 |
             throw new IndexOutOfBoundsException();
262 | 1038 |
         } else if (len == 0) {
263 | 0 |
             return 0;
264 |  |
         }
265 |  |
266 | 1038 |
         int n = readToDelimiter1(b, off, len, delim);
267 | 1038 |
         if (n <= 0) return n;
268 | 758 |
         while ((n < len) && (in.available() > 0)) {
269 | 60 |
           int n1 = readToDelimiter1(b, off + n, len - n, delim);
270 | 60 |
           if (n1 <= 0) break;
271 | 18 |
           n += n1;
272 | 18 |
         }
273 | 740 |
         return n;
274 |  |
       }
275 |  |
276 |  |
         /**
277 |  |
          * Calculates length.
278 |  |
          * <p>
279 |  |
          * Returns the index within data1 of the
280 |  |
          * byte array data2. It starts looking in data1 at fromIndex and only
281 |  |
          * considers len bytes of data1
282 |  |
          *
283 |  |
          * @param      data1 array to look in.
284 |  |
          * @param      data2 array to look for.
285 |  |
          * @param      fromIndex where to start looking from in data1.
286 |  |
          * @return the index of data2 within data1 or
287 |  |
          *         -1 if data2 does not occur within data1
288 |  |
          */
289 |  |
         public int indexOf(byte[] data1, byte[] data2, int fromIndex) {
290 | 6 |
           if (fromIndex < 0)
291 | 0 |
             fromIndex = 0;
292 | 6 |
           return indexOf(data1, data2, fromIndex, data1.length - fromIndex);
293 |  |
         }
294 |  |
295 |  |
         /**
296 |  |
          * Returns the index within data1 of the
297 |  |
          * byte array data2. It starts looking in data1 at fromIndex and only
298 |  |
          * considers len bytes of data1
299 |  |
          *
300 |  |
          * @param      data1 array to look in.
301 |  |
          * @param      data2 array to look for.
302 |  |
          * @param      fromIndex where to start looking from in data1.
303 |  |
          * @param      len   maximum number of bytes of data1 to look in.
304 |  |
          * @return the index of data2 within data1 or
305 |  |
          *         -1 if data2 does not occur within data1
306 |  |
          **/
307 |  |
         public int indexOf(byte[] data1, byte[] data2, int fromIndex, int len) {
308 | 1104 |
           byte[] v1 = data1;
309 | 1104 |
           byte[] v2 = data2;
310 | 1104 |
           if (len <= 0)
311 | 0 |
             return -1;
312 | 1104 |
           if (fromIndex >= data1.length) {
313 | 0 |
             if (data1.length == 0 && fromIndex == 0 && data2.length == 0)
314 | 0 |
               return 0;
315 | 0 |
             return -1;
316 |  |
           }
317 | 1104 |
           if (fromIndex < 0)
318 | 0 |
             fromIndex = 0;
319 | 1104 |
           if (data2.length == 0)
320 | 0 |
             return fromIndex;
321 | 1104 |
           int end = (fromIndex + len > data1.length) ?
322 |  |
                                data1.length : fromIndex + len;
323 | 1104 |
           int maxStart = end - data2.length;
324 | 1104 |
           byte first = v2[0];
325 | 1104 |
           int i = fromIndex;
326 |  |
327 |  |
           startSearchForFirstByte:
328 |  |
           while (true) {
329 | 1114 |
             potentialMatch = -1;
330 | 300287 |
             while (i < end && v1[i] != first)
331 | 299173 |
               i++;
332 | 1114 |
             if (i >= end)
333 | 170 |
               return -1;
334 | 944 |
             if (i > maxStart) {  // we might find an initial segment of delim
335 | 22 |
               potentialMatch = i;
336 |  |
             }
337 | 944 |
             int j = i + 1;
338 | 944 |
             int last = (potentialMatch == -1) ? i + data2.length : data1.length;
339 | 944 |
             int k = 1;
340 | 16665 |
             while (j < last) {
341 | 15731 |
               if (v1[j++] != v2[k++]) {
342 | 10 |
                 i++;
343 | 10 |
                 continue startSearchForFirstByte;
344 |  |
               }
345 |  |
             }
346 | 934 |
             return (potentialMatch == -1) ? i : -1;
347 |  |
           }
348 |  |
         }
349 |  |
350 |  |
351 |  |
         /**
352 |  |
          * Used in tests.
353 |  |
          * @return the buffer length
354 |  |
          */
355 |  |
         public int getBufferLength() {
356 | 2 |
           return buf.length;
357 |  |
         }
358 |  |
         /**
359 |  |
          * Used in tests.
360 |  |
          * @return the potentialMatch
361 |  |
          */
362 |  |
         public int getPotentialMatch() {
363 | 6 |
           return potentialMatch;
364 |  |
         }
365 |  |
366 |  |
     }