001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.io.output;
018
019import java.io.BufferedInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.OutputStream;
023
024import org.apache.commons.io.IOUtils;
025
026/**
027 * Implements a ThreadSafe version of {@link AbstractByteArrayOutputStream} using instance synchronization.
028 */
029//@ThreadSafe
030public class ByteArrayOutputStream extends AbstractByteArrayOutputStream<ByteArrayOutputStream> {
031
032    /**
033     * Fetches entire contents of an {@link InputStream} and represent same data as result InputStream.
034     * <p>
035     * This method is useful where,
036     * </p>
037     * <ul>
038     * <li>Source InputStream is slow.</li>
039     * <li>It has network resources associated, so we cannot keep it open for long time.</li>
040     * <li>It has network timeout associated.</li>
041     * </ul>
042     * <p>
043     * It can be used in favor of {@link #toByteArray()}, since it avoids unnecessary allocation and copy of byte[].
044     * </p>
045     * <p>
046     * This method buffers the input internally, so there is no need to use a {@link BufferedInputStream}.
047     * </p>
048     *
049     * @param input Stream to be fully buffered.
050     * @return A fully buffered stream.
051     * @throws IOException if an I/O error occurs.
052     * @since 2.0
053     */
054    public static InputStream toBufferedInputStream(final InputStream input)
055            throws IOException {
056        return toBufferedInputStream(input, DEFAULT_SIZE);
057    }
058
059    /**
060     * Fetches entire contents of an {@link InputStream} and represent same data as result InputStream.
061     * <p>
062     * This method is useful where,
063     * </p>
064     * <ul>
065     * <li>Source InputStream is slow.</li>
066     * <li>It has network resources associated, so we cannot keep it open for long time.</li>
067     * <li>It has network timeout associated.</li>
068     * </ul>
069     * <p>
070     * It can be used in favor of {@link #toByteArray()}, since it avoids unnecessary allocation and copy of byte[].
071     * </p>
072     * <p>
073     * This method buffers the input internally, so there is no need to use a {@link BufferedInputStream}.
074     * </p>
075     *
076     * @param input Stream to be fully buffered.
077     * @param size  the initial buffer size.
078     * @return A fully buffered stream.
079     * @throws IOException if an I/O error occurs.
080     * @since 2.5
081     */
082    public static InputStream toBufferedInputStream(final InputStream input, final int size)
083        throws IOException {
084        try (ByteArrayOutputStream output = new ByteArrayOutputStream(size)) {
085            output.write(input);
086            return output.toInputStream();
087        }
088    }
089
090    /**
091     * Constructs a new byte array output stream. The buffer capacity is
092     * initially {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes, though its size increases if necessary.
093     */
094    public ByteArrayOutputStream() {
095        this(DEFAULT_SIZE);
096    }
097
098    /**
099     * Constructs a new byte array output stream, with a buffer capacity of
100     * the specified size, in bytes.
101     *
102     * @param size  the initial size.
103     * @throws IllegalArgumentException if size is negative.
104     */
105    public ByteArrayOutputStream(final int size) {
106        if (size < 0) {
107            throw new IllegalArgumentException("Negative initial size: " + size);
108        }
109        synchronized (this) {
110            needNewBuffer(size);
111        }
112    }
113
114    /**
115     * @see java.io.ByteArrayOutputStream#reset()
116     */
117    @Override
118    public synchronized void reset() {
119        resetImpl();
120    }
121
122    @Override
123    public synchronized int size() {
124        return count;
125    }
126
127    @Override
128    public synchronized byte[] toByteArray() {
129        return toByteArrayImpl();
130    }
131
132    @Override
133    public synchronized InputStream toInputStream() {
134        return toInputStream(java.io.ByteArrayInputStream::new);
135    }
136
137    @Override
138    public void write(final byte[] b, final int off, final int len) {
139        IOUtils.checkFromIndexSize(b, off, len);
140        if (len == 0) {
141            return;
142        }
143        synchronized (this) {
144            writeImpl(b, off, len);
145        }
146    }
147
148    @Override
149    public synchronized int write(final InputStream in) throws IOException {
150        return writeImpl(in);
151    }
152
153    @Override
154    public synchronized void write(final int b) {
155        writeImpl(b);
156    }
157
158    @Override
159    public synchronized void writeTo(final OutputStream out) throws IOException {
160        writeToImpl(out);
161    }
162}