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 */
017
018package org.apache.commons.io.output;
019
020import java.io.FilterOutputStream;
021import java.io.IOException;
022import java.io.OutputStream;
023
024import org.apache.commons.io.IOUtils;
025import org.apache.commons.io.build.AbstractStreamBuilder;
026
027/**
028 * A Proxy stream which acts as expected, that is it passes the method calls on to the proxied stream and doesn't change which methods are being called. It is
029 * an alternative base class to FilterOutputStream to increase reusability.
030 * <p>
031 * See the protected methods for ways in which a subclass can easily decorate a stream with custom pre-, post- or error processing functionality.
032 * </p>
033 */
034public class ProxyOutputStream extends FilterOutputStream {
035
036    /**
037     * Builds instances of {@link ProxyOutputStream}.
038     * <p>
039     * This class does not provide a convenience static {@code builder()} method so that subclasses can.
040     * </p>
041     *
042     * @since 2.19.0
043     */
044    public static class Builder extends AbstractStreamBuilder<ProxyOutputStream, Builder> {
045
046        /**
047         * Constructs a new builder of {@link ProxyOutputStream}.
048         */
049        public Builder() {
050            // empty
051        }
052
053        /**
054         * Builds a new {@link ProxyOutputStream}.
055         * <p>
056         * This builder uses the following aspects:
057         * </p>
058         * <ul>
059         * <li>{@link #getOutputStream()} is the target aspect.</li>
060         * </ul>
061         *
062         * @return a new instance.
063         * @throws IllegalStateException         if the {@code origin} is {@code null}.
064         * @throws UnsupportedOperationException if the origin cannot be converted to an {@link OutputStream}.
065         * @throws IOException                   if an I/O error occurs converting to an {@link OutputStream} using {@link #getOutputStream()}.
066         * @see #getOutputStream()
067         * @see #getUnchecked()
068         */
069        @Override
070        public ProxyOutputStream get() throws IOException {
071            return new ProxyOutputStream(this);
072        }
073    }
074
075    @SuppressWarnings("resource") // caller closes
076    ProxyOutputStream(final Builder builder) throws IOException {
077        // the delegate is stored in a protected superclass variable named 'out'
078        super(builder.getOutputStream());
079    }
080
081    /**
082     * Constructs a new ProxyOutputStream.
083     *
084     * @param delegate the OutputStream to delegate to.
085     */
086    public ProxyOutputStream(final OutputStream delegate) {
087        // the delegate is stored in a protected superclass variable named 'out'
088        super(delegate);
089    }
090
091    /**
092     * Invoked by the write methods after the proxied call has returned successfully. The number of bytes written (1 for the {@link #write(int)} method, buffer
093     * length for {@link #write(byte[])}, etc.) is given as an argument.
094     * <p>
095     * Subclasses can override this method to add common post-processing functionality without having to override all the write methods. The default
096     * implementation does nothing.
097     * </p>
098     *
099     * @param n number of bytes written.
100     * @throws IOException if the post-processing fails.
101     * @since 2.0
102     */
103    @SuppressWarnings("unused") // Possibly thrown from subclasses.
104    protected void afterWrite(final int n) throws IOException {
105        // noop
106    }
107
108    /**
109     * Invoked by the write methods before the call is proxied. The number of bytes to be written (1 for the {@link #write(int)} method, buffer length for
110     * {@link #write(byte[])}, etc.) is given as an argument.
111     * <p>
112     * Subclasses can override this method to add common pre-processing functionality without having to override all the write methods. The default
113     * implementation does nothing.
114     * </p>
115     *
116     * @param n number of bytes to be written.
117     * @throws IOException if the pre-processing fails.
118     * @since 2.0
119     */
120    @SuppressWarnings("unused") // Possibly thrown from subclasses.
121    protected void beforeWrite(final int n) throws IOException {
122        // noop
123    }
124
125    /**
126     * Invokes the delegate's {@code close()} method.
127     *
128     * @throws IOException if an I/O error occurs.
129     */
130    @Override
131    public void close() throws IOException {
132        IOUtils.close(out, this::handleIOException);
133    }
134
135    /**
136     * Invokes the delegate's {@code flush()} method.
137     *
138     * @throws IOException if an I/O error occurs.
139     */
140    @Override
141    public void flush() throws IOException {
142        try {
143            out.flush();
144        } catch (final IOException e) {
145            handleIOException(e);
146        }
147    }
148
149    /**
150     * Handle any IOExceptions thrown.
151     * <p>
152     * This method provides a point to implement custom exception. handling. The default behavior is to re-throw the exception.
153     * </p>
154     *
155     * @param e The IOException thrown.
156     * @throws IOException if an I/O error occurs.
157     * @since 2.0
158     */
159    protected void handleIOException(final IOException e) throws IOException {
160        throw e;
161    }
162
163    /**
164     * Sets the underlying output stream.
165     *
166     * @param out the underlying output stream.
167     * @return {@code this} instance.
168     * @since 2.19.0
169     */
170    public ProxyOutputStream setReference(final OutputStream out) {
171        this.out = out;
172        return this;
173    }
174
175    /**
176     * Unwraps this instance by returning the underlying {@link OutputStream}.
177     * <p>
178     * Use with caution; useful to query the underlying {@link OutputStream}.
179     * </p>
180     *
181     * @return the underlying {@link OutputStream}.
182     */
183    OutputStream unwrap() {
184        return out;
185    }
186
187    /**
188     * Invokes the delegate's {@code write(byte[])} method.
189     *
190     * @param b the bytes to write.
191     * @throws IOException if an I/O error occurs.
192     */
193    @Override
194    public void write(final byte[] b) throws IOException {
195        try {
196            final int len = IOUtils.length(b);
197            beforeWrite(len);
198            out.write(b);
199            afterWrite(len);
200        } catch (final IOException e) {
201            handleIOException(e);
202        }
203    }
204
205    /**
206     * Invokes the delegate's {@code write(byte[])} method.
207     *
208     * @param b   the bytes to write.
209     * @param off The start offset.
210     * @param len The number of bytes to write.
211     * @throws IOException if an I/O error occurs.
212     */
213    @Override
214    public void write(final byte[] b, final int off, final int len) throws IOException {
215        try {
216            beforeWrite(len);
217            out.write(b, off, len);
218            afterWrite(len);
219        } catch (final IOException e) {
220            handleIOException(e);
221        }
222    }
223
224    /**
225     * Invokes the delegate's {@code write(int)} method.
226     *
227     * @param b the byte to write.
228     * @throws IOException if an I/O error occurs.
229     */
230    @Override
231    public void write(final int b) throws IOException {
232        try {
233            beforeWrite(1);
234            out.write(b);
235            afterWrite(1);
236        } catch (final IOException e) {
237            handleIOException(e);
238        }
239    }
240
241    /**
242     * Invokes the delegate's {@code write(byte[])} method for the {@code repeat} count.
243     *
244     * @param b      the bytes to write.
245     * @param off    The start offset.
246     * @param len    The number of bytes to write.
247     * @param repeat How many times to write the bytes in {@code b}.
248     * @throws IOException if an I/O error occurs.
249     * @since 2.21.0
250     */
251    public void writeRepeat(final byte[] b, final int off, final int len, final long repeat) throws IOException {
252        long remains = repeat;
253        while (remains-- > 0) {
254            write(b, off, len);
255        }
256    }
257
258    /**
259     * Invokes the delegate's {@code write(byte[])} method for the {@code repeat} count.
260     *
261     * @param b      the bytes to write.
262     * @param repeat How many times to write the bytes in {@code b}.
263     * @throws IOException if an I/O error occurs.
264     * @since 2.21.0
265     */
266    public void writeRepeat(final byte[] b, final long repeat) throws IOException {
267        long remains = repeat;
268        while (remains-- > 0) {
269            write(b);
270        }
271    }
272
273    /**
274     * Invokes the delegate's {@code write(int)} method.
275     *
276     * @param b      the byte to write.
277     * @param repeat How many times to write the byte in {@code b}.
278     * @throws IOException if an I/O error occurs.
279     * @since 2.21.0
280     */
281    public void writeRepeat(final int b, final long repeat) throws IOException {
282        long remains = repeat;
283        while (remains-- > 0) {
284            write(b);
285        }
286    }
287}