001/*
002 * Copyright 2012-2016 UnboundID Corp.
003 *
004 * This program is free software; you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License (GPLv2 only)
006 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
007 * as published by the Free Software Foundation.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program; if not, see <http://www.gnu.org/licenses>.
016 */
017
018package com.unboundid.scim.marshal;
019
020import java.io.FilterInputStream;
021import java.io.IOException;
022import java.io.InputStream;
023import java.util.concurrent.atomic.AtomicLong;
024
025
026
027/**
028 * This class is a wrapper around an input stream that allows us to determine
029 * how many bytes have been read from the stream.
030 */
031public class BulkInputStreamWrapper extends FilterInputStream
032{
033  // The number of bytes read from the stream.
034  private final AtomicLong bytesRead;
035
036
037
038  /**
039   * Creates a new instance of this input stream that wraps the provided
040   * stream.
041   *
042   * @param  s  The input stream to be wrapped.
043   */
044  public BulkInputStreamWrapper(final InputStream s)
045  {
046    super(s);
047    bytesRead  = new AtomicLong(0L);
048  }
049
050
051
052  /**
053   * Reads the next byte of data from the input stream. The value byte is
054   * returned as an <code>int</code> in the range <code>0</code> to
055   * <code>255</code>. If no byte is available because the end of the stream has
056   * been reached, the value <code>-1</code> is returned. This method blocks
057   * until input data is available, the end of the stream is detected, or an
058   * exception is thrown.
059   *
060   * <p> A subclass must provide an implementation of this method.
061   *
062   * @return the next byte of data, or <code>-1</code> if the end of the stream
063   *         is reached.
064   *
065   * @throws java.io.IOException if an I/O error occurs.
066   */
067  @Override
068  public int read() throws IOException
069  {
070    int c = in.read();
071    if (c != -1)
072    {
073      bytesRead.incrementAndGet();
074    }
075
076    return c;
077  }
078
079
080
081  /**
082   * Reads some number of bytes from the input stream and stores them into the
083   * buffer array <code>b</code>. The number of bytes actually read is returned
084   * as an integer.  This method blocks until input data is available, end of
085   * file is detected, or an exception is thrown.
086   *
087   * <p> If the length of <code>b</code> is zero, then no bytes are read and
088   * <code>0</code> is returned; otherwise, there is an attempt to read at least
089   * one byte. If no byte is available because the stream is at the end of the
090   * file, the value <code>-1</code> is returned; otherwise, at least one byte
091   * is read and stored into <code>b</code>.
092   *
093   * <p> The first byte read is stored into element <code>b[0]</code>, the next
094   * one into <code>b[1]</code>, and so on. The number of bytes read is, at
095   * most, equal to the length of <code>b</code>. Let <i>k</i> be the number of
096   * bytes actually read; these bytes will be stored in elements
097   * <code>b[0]</code> through <code>b[</code><i>k</i><code>-1]</code>, leaving
098   * elements <code>b[</code><i>k</i><code>]</code> through
099   * <code>b[b.length-1]</code> unaffected.
100   *
101   * <p> The <code>read(b)</code> method for class <code>InputStream</code>
102   * has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
103   *
104   * @param b the buffer into which the data is read.
105   *
106   * @return the total number of bytes read into the buffer, or <code>-1</code>
107   *         is there is no more data because the end of the stream has been
108   *         reached.
109   *
110   * @throws java.io.IOException  If the first byte cannot be read for any
111   *                              reason other than the end of the file, if the
112   *                              input stream has been closed, or if some other
113   *                              I/O error occurs.
114   * @see java.io.InputStream#read(byte[], int, int)
115   */
116  @Override
117  public int read(final byte[] b) throws IOException
118  {
119    int n = in.read(b);
120    if (n != -1)
121    {
122      bytesRead.addAndGet(n);
123    }
124
125    return n;
126  }
127
128
129
130  /**
131   * Reads up to <code>len</code> bytes of data from the input stream into an
132   * array of bytes.  An attempt is made to read as many as <code>len</code>
133   * bytes, but a smaller number may be read. The number of bytes actually read
134   * is returned as an integer.
135   *
136   * <p> This method blocks until input data is available, end of file is
137   * detected, or an exception is thrown.
138   *=
139   * <p> If <code>len</code> is zero, then no bytes are read and <code>0</code>
140   * is returned; otherwise, there is an attempt to read at least one byte. If
141   * no byte is available because the stream is at end of file, the value
142   * <code>-1</code> is returned; otherwise, at least one byte is read and
143   * stored into <code>b</code>.
144   *
145   * <p> The first byte read is stored into element <code>b[off]</code>, the
146   * next one into <code>b[off+1]</code>, and so on. The number of bytes read
147   * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of bytes
148   * actually read; these bytes will be stored in elements <code>b[off]</code>
149   * through <code>b[off+</code><i>k</i><code>-1]</code>, leaving elements
150   * <code>b[off+</code><i>k</i><code>]</code> through <code>b[off+len-1]</code>
151   * unaffected.
152   *
153   * <p> In every case, elements <code>b[0]</code> through <code>b[off]</code>
154   * and elements <code>b[off+len]</code> through <code>b[b.length-1]</code> are
155   * unaffected.
156   *
157   * <p> The <code>read(b,</code> <code>off,</code> <code>len)</code> method for
158   * class <code>InputStream</code> simply calls the method <code>read()</code>
159   * repeatedly. If the first such call results in an <code>IOException</code>,
160   * that exception is returned from the call to the <code>read(b,</code>
161   * <code>off,</code> <code>len)</code> method.  If any subsequent call to
162   * <code>read()</code> results in a <code>IOException</code>, the exception is
163   * caught and treated as if it were end of file; the bytes read up to that
164   * point are stored into <code>b</code> and the number of bytes read before
165   * the exception occurred is returned. The default implementation of this
166   * method blocks until the requested amount of input data <code>len</code> has
167   * been read, end of file is detected, or an exception is thrown. Subclasses
168   * are encouraged to provide a more efficient implementation of this method.
169   *
170   * @param b   the buffer into which the data is read.
171   * @param off the start offset in array <code>b</code> at which the data is
172   *            written.
173   * @param len the maximum number of bytes to read.
174   *
175   * @return the total number of bytes read into the buffer, or <code>-1</code>
176   *         if there is no more data because the end of the stream has been
177   *         reached.
178   *
179   * @throws java.io.IOException       If the first byte cannot be read for any
180   *                                   reason other than end of file, or if the
181   *                                   input stream has been closed, or if some
182   *                                   other I/O error occurs.
183   * @see java.io.InputStream#read()
184   */
185  @Override
186  public int read(final byte[] b, final int off, final int len)
187      throws IOException
188  {
189    int n = in.read(b, off, len);
190    if (n != -1)
191    {
192      bytesRead.addAndGet(n);
193    }
194
195    return n;
196  }
197
198
199
200  /**
201   * Skips over and discards <code>n</code> bytes of data from this input
202   * stream.
203   * The <code>skip</code> method may, for a variety of reasons, end up skipping
204   * over some smaller number of bytes, possibly <code>0</code>. This may result
205   * from any of a number of conditions; reaching end of file before
206   * <code>n</code> bytes have been skipped is only one possibility. The actual
207   * number of bytes skipped is returned.  If <code>n</code> is negative, no
208   * bytes are skipped.
209   *
210   *
211   * @param n the number of bytes to be skipped.
212   *
213   * @return the actual number of bytes skipped.
214   *
215   * @throws java.io.IOException if the stream does not support seek, or if some
216   *                             other I/O error occurs.
217   */
218  @Override
219  public long skip(final long n) throws IOException
220  {
221    long skipped = in.skip(n);
222    bytesRead.addAndGet(skipped);
223
224    return n;
225  }
226
227
228
229  /**
230   * Retrieves the number of bytes read through this input stream.
231   *
232   * @return  The number of bytes read through this input stream.
233   */
234  public long getBytesRead()
235  {
236    return bytesRead.get();
237  }
238}