001    /*
002     * Copyright 2010-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2010-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.util;
022    
023    
024    
025    import java.io.IOException;
026    import java.io.OutputStream;
027    import java.io.Serializable;
028    
029    import static com.unboundid.util.UtilityMessages.*;
030    
031    
032    
033    /**
034     * This class provides an {@code OutputStream} implementation that writes data
035     * to a provided byte array.  It is similar to the
036     * {@code java.io.ByteArrayOutputStream} class, except that it allows you to
037     * pass in the array that it uses, and the array will not grow over time.
038     */
039    @Mutable()
040    @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
041    public final class FixedArrayOutputStream
042           extends OutputStream
043           implements Serializable
044    {
045      /**
046       * The serial version UID for this serializable class.
047       */
048      private static final long serialVersionUID = 4678108653480347534L;
049    
050    
051    
052      // The byte array used by this class.
053      private final byte[] array;
054    
055      // The initial position for this array.
056      private final int initialPosition;
057    
058      // The maximum number of bytes that may be written.
059      private final int length;
060    
061      // The maximum position at which data may be written.
062      private final int maxPosition;
063    
064      // The current position at which data may be written.
065      private int pos;
066    
067    
068    
069      /**
070       * Creates a new output stream that will write data to the provided array.
071       * It will use the entire array.
072       *
073       * @param  array  The array to which data will be written.  It must not be
074       *                {@code null}.
075       */
076      public FixedArrayOutputStream(final byte[] array)
077      {
078        this(array, 0, array.length);
079      }
080    
081    
082    
083      /**
084       * Creates a new output stream that will write data to the provided array.
085       * It will use the specified portion of the array.
086       *
087       * @param  array  The array to which data will be written.  It must not be
088       *                {@code null}.
089       * @param  pos    The position at which to start writing data.  It must be
090       *                greater than or equal to zero and less than or equal to the
091       *                length of the array.
092       * @param  len    The maximum number of bytes that may be written.  It must
093       *                be greater than or equal to zero and less than or equal to
094       *                the difference between the length of the array and the
095       *                provided {@code pos} value.
096       */
097      public FixedArrayOutputStream(final byte[] array, final int pos,
098                                       final int len)
099      {
100        this.array = array;
101        this.pos   = pos;
102    
103        initialPosition = pos;
104        maxPosition     = pos + len;
105        length          = len;
106    
107        Validator.ensureTrue((pos >= 0),
108             "The position must be greater than or equal to zero.");
109        Validator.ensureTrue((len >= 0),
110             "The length must be greater than or equal to zero.");
111        Validator.ensureTrue((maxPosition <= array.length),
112             "The sum of pos and len must not exceed the array length.");
113      }
114    
115    
116    
117      /**
118       * Retrieves the backing array used by this output stream.
119       *
120       * @return  The backing array used by this output stream.
121       */
122      public byte[] getBackingArray()
123      {
124        return array;
125      }
126    
127    
128    
129      /**
130       * Retrieves the initial position provided when this output stream was
131       * created.
132       *
133       * @return  The initial position provided when this output stream was created.
134       */
135      public int getInitialPosition()
136      {
137        return initialPosition;
138      }
139    
140    
141    
142      /**
143       * Retrieves the maximum number of bytes that may be written to this output
144       * stream.
145       *
146       * @return  The maximum number of bytes that may be written to this output
147       *          stream.
148       */
149      public int getLength()
150      {
151        return length;
152      }
153    
154    
155    
156      /**
157       * Retrieves the number of bytes that have been written so far to this output
158       * stream.
159       *
160       * @return  The number of bytes that have been written so far to this output
161       *          stream.
162       */
163      public int getBytesWritten()
164      {
165        return (pos - initialPosition);
166      }
167    
168    
169    
170      /**
171       * Closes this output stream.  This has no effect.
172       */
173      @Override()
174      public void close()
175      {
176        // No implementation required.
177      }
178    
179    
180    
181      /**
182       * Flushes this output stream.  This has no effect.
183       */
184      @Override()
185      public void flush()
186      {
187        // No implementation required.
188      }
189    
190    
191    
192      /**
193       * Writes the provided byte to this output stream.
194       *
195       * @param  b  The byte to be written.
196       *
197       * @throws  IOException  If an attempt was made to write beyond the end of the
198       *                       array.
199       */
200      @Override()
201      public void write(final int b)
202             throws IOException
203      {
204        if (pos >= maxPosition)
205        {
206          throw new IOException(ERR_FIXED_ARRAY_OS_WRITE_BEYOND_END.get());
207        }
208    
209        array[pos++] = (byte) b;
210      }
211    
212    
213    
214      /**
215       * Writes the contents of the provided array to this output stream.
216       *
217       * @param  b  The byte array containing the data to be written.  It must not
218       *            be {@code null}.
219       *
220       * @throws  IOException  If an attempt was made to write beyond the end of the
221       *                       array.
222       */
223      @Override()
224      public void write(final byte[] b)
225             throws IOException
226      {
227        write(b, 0, b.length);
228      }
229    
230    
231    
232      /**
233       * Writes the contents of the provided array to this output stream.
234       *
235       * @param  b    The byte array containing the data to be written.  It must not
236       *              be {@code null}.
237       * @param  off  The offset within the provided array of the beginning of the
238       *              data to be written.  It must be greater than or equal to zero
239       *              and less than or equal to the length of the provided array.
240       * @param  len  The number of bytes to be written.  It must be greater than or
241       *              equal to zero, and the sum of {@code off} and {@code len} must
242       *              be less than the length of the provided array.
243       *
244       * @throws  IOException  If an attempt was made to write beyond the end of the
245       *                       array.
246       */
247      @Override()
248      public void write(final byte[] b, final int off, final int len)
249             throws IOException
250      {
251        Validator.ensureTrue((off >= 0),
252             "The provided offset must be greater than or equal to zero.");
253        Validator.ensureTrue((len >= 0),
254             "The provided length must be greater than or equal to zero.");
255        Validator.ensureTrue(((off + len) <= b.length),
256             "The sum of off and len must not exceed the array length.");
257    
258        if ((pos + len) > maxPosition)
259        {
260          throw new IOException(ERR_FIXED_ARRAY_OS_WRITE_BEYOND_END.get());
261        }
262    
263        System.arraycopy(b, off, array, pos, len);
264        pos += len;
265      }
266    }