Coverage Report - org.melati.util.DelimitedBufferedInputStream
 
Classes in this File Line Coverage Branch Coverage Complexity
DelimitedBufferedInputStream
77%
93/120
63%
63/100
7.1
 
 1  
 /*
 2  
  * $Source$
 3  
  * $Revision$
 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  24
     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  2
        super(in);
 74  2
     }
 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  22
       super(in, size);
 89  22
     }
 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  65
       if (markpos < 0)
 102  65
            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  65
       count = pos;
 124  65
       int n = in.read(buf, pos, buf.length - pos);
 125  65
       if (n > 0)
 126  65
         count = n + pos;
 127  65
     }
 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  493
       if (in == null)
 136  0
         throw new IOException("Stream closed");
 137  493
     }
 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  539
       int avail = count - pos;
 146  539
       if (avail <= 0) {
 147  65
           fill();
 148  65
           avail = count - pos;
 149  65
           if (avail <= 0) return -1;
 150  
       }
 151  539
       int cnt = (avail < len) ? avail : len;
 152  
 
 153  
       // indexOf sets potentialMatch
 154  539
       int found = indexOf(buf, delim, pos, avail);
 155  539
       int max = cnt;
 156  539
       if (found != -1)
 157  473
         max = found - pos;
 158  66
       else if (potentialMatch != -1)
 159  3
         max = potentialMatch - pos;
 160  
 
 161  539
       cnt = (max < cnt) ? max : cnt;
 162  539
       System.arraycopy(buf, pos, b, off, cnt);
 163  539
       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  539
       if (found == -1 && potentialMatch != -1 && pos == potentialMatch) {
 170  
 
 171  
         /* the number of bytes to shift the buffer down by */
 172  3
         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  3
         int extras = potentialMatch + delim.length - count;
 181  
 
 182  
         /* the number of bytes we want to end up in buf */
 183  3
         int nsz = potentialMatch + delim.length - halfmark;
 184  3
         if (nsz > buf.length) {
 185  
           // we need to grow the buffer
 186  3
           byte nbuf[] = new byte[nsz];
 187  3
           System.arraycopy(buf, halfmark, nbuf, 0, count - halfmark);
 188  3
           buf = nbuf;
 189  3
         } else {
 190  0
           System.arraycopy(buf, halfmark, buf, 0, count - halfmark);
 191  
         }
 192  
 
 193  3
         pos = pos - halfmark;
 194  3
         count = count - halfmark;
 195  3
         markpos = (markpos == -1) ? -1 : 0;
 196  3
         int n = in.read(buf, count, buf.length - count);
 197  3
         if (n > 0)
 198  3
           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  3
         if (n < extras) {
 203  3
           int unfilled = len - cnt;
 204  3
           if (unfilled > 0) {
 205  3
             avail = count - pos;
 206  3
             int cnt2 = (unfilled < avail) ? unfilled : avail;
 207  3
             System.arraycopy(buf, pos, b, off+cnt, cnt2);
 208  3
             cnt += cnt2;
 209  3
             pos += cnt2;
 210  
           }
 211  
         }
 212  
       }
 213  539
       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  493
     ensureOpen();
 259  493
     if ((off < 0) || (off > b.length) || (len < 0) ||
 260  
             ((off + len) > b.length) || ((off + len) < 0)) {
 261  0
         throw new IndexOutOfBoundsException();
 262  493
     } else if (len == 0) {
 263  0
         return 0;
 264  
     }
 265  
 
 266  493
     int n = readToDelimiter1(b, off, len, delim);
 267  493
     if (n <= 0) return n;
 268  355
     while ((n < len) && (in.available() > 0)) {
 269  46
       int n1 = readToDelimiter1(b, off + n, len - n, delim);
 270  46
       if (n1 <= 0) break;
 271  11
       n += n1;
 272  11
     }
 273  344
     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  3
       if (fromIndex < 0)
 291  0
         fromIndex = 0;
 292  3
       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  542
       byte[] v1 = data1;
 309  542
       byte[] v2 = data2;
 310  542
       if (len <= 0)
 311  0
         return -1;
 312  542
       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  542
       if (fromIndex < 0)
 318  0
         fromIndex = 0;
 319  542
       if (data2.length == 0)
 320  0
         return fromIndex;
 321  542
       int end = (fromIndex + len > data1.length) ? 
 322  
                            data1.length : fromIndex + len;
 323  542
       int maxStart = end - data2.length;
 324  542
       byte first = v2[0];
 325  542
       int i = fromIndex;
 326  
 
 327  
       startSearchForFirstByte:
 328  
       while (true) {
 329  547
         potentialMatch = -1;
 330  102718
         while (i < end && v1[i] != first)
 331  102171
           i++;
 332  547
         if (i >= end)
 333  64
           return -1;
 334  483
         if (i > maxStart) {  // we might find an initial segment of delim
 335  5
           potentialMatch = i;
 336  
         }
 337  483
         int j = i + 1;
 338  483
         int last = (potentialMatch == -1) ? i + data2.length : data1.length;
 339  483
         int k = 1;
 340  8440
         while (j < last) {
 341  7962
           if (v1[j++] != v2[k++]) {
 342  5
             i++;
 343  5
             continue startSearchForFirstByte;
 344  
           }
 345  
         }
 346  478
         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  1
       return buf.length;
 357  
     }
 358  
     /**
 359  
      * Used in tests.
 360  
      * @return the potentialMatch
 361  
      */
 362  
     public int getPotentialMatch() {
 363  3
       return potentialMatch;
 364  
     }
 365  
 
 366  
 }