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 *      http://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.FilterOutputStream;
020import java.io.IOException;
021import java.io.OutputStream;
022
023import org.apache.commons.io.IOUtils;
024
025/**
026 * A Proxy stream which acts as expected, that is it passes the method
027 * calls on to the proxied stream and doesn't change which methods are
028 * being called. It is an alternative base class to FilterOutputStream
029 * to increase reusability.
030 * <p>
031 * See the protected methods for ways in which a subclass can easily decorate
032 * a stream with custom pre-, post- or error processing functionality.
033 *
034 */
035public class ProxyOutputStream extends FilterOutputStream {
036
037    /**
038     * Constructs a new ProxyOutputStream.
039     *
040     * @param proxy  the OutputStream to delegate to
041     */
042    public ProxyOutputStream(final OutputStream proxy) {
043        super(proxy);
044        // the proxy is stored in a protected superclass variable named 'out'
045    }
046
047    /**
048     * Invokes the delegate's <code>write(int)</code> method.
049     * @param idx the byte to write
050     * @throws IOException if an I/O error occurs
051     */
052    @Override
053    public void write(final int idx) throws IOException {
054        try {
055            beforeWrite(1);
056            out.write(idx);
057            afterWrite(1);
058        } catch (final IOException e) {
059            handleIOException(e);
060        }
061    }
062
063    /**
064     * Invokes the delegate's <code>write(byte[])</code> method.
065     * @param bts the bytes to write
066     * @throws IOException if an I/O error occurs
067     */
068    @Override
069    public void write(final byte[] bts) throws IOException {
070        try {
071            final int len = IOUtils.length(bts);
072            beforeWrite(len);
073            out.write(bts);
074            afterWrite(len);
075        } catch (final IOException e) {
076            handleIOException(e);
077        }
078    }
079
080    /**
081     * Invokes the delegate's <code>write(byte[])</code> method.
082     * @param bts the bytes to write
083     * @param st The start offset
084     * @param end The number of bytes to write
085     * @throws IOException if an I/O error occurs
086     */
087    @Override
088    public void write(final byte[] bts, final int st, final int end) throws IOException {
089        try {
090            beforeWrite(end);
091            out.write(bts, st, end);
092            afterWrite(end);
093        } catch (final IOException e) {
094            handleIOException(e);
095        }
096    }
097
098    /**
099     * Invokes the delegate's <code>flush()</code> method.
100     * @throws IOException if an I/O error occurs
101     */
102    @Override
103    public void flush() throws IOException {
104        try {
105            out.flush();
106        } catch (final IOException e) {
107            handleIOException(e);
108        }
109    }
110
111    /**
112     * Invokes the delegate's <code>close()</code> method.
113     * @throws IOException if an I/O error occurs
114     */
115    @Override
116    public void close() throws IOException {
117        IOUtils.close(out, e -> handleIOException(e));
118    }
119
120    /**
121     * Invoked by the write methods before the call is proxied. The number
122     * of bytes to be written (1 for the {@link #write(int)} method, buffer
123     * length for {@link #write(byte[])}, etc.) is given as an argument.
124     * <p>
125     * Subclasses can override this method to add common pre-processing
126     * functionality without having to override all the write methods.
127     * The default implementation does nothing.
128     *
129     * @since 2.0
130     * @param n number of bytes to be written
131     * @throws IOException if the pre-processing fails
132     */
133    protected void beforeWrite(final int n) throws IOException {
134        // noop
135    }
136
137    /**
138     * Invoked by the write methods after the proxied call has returned
139     * successfully. The number of bytes written (1 for the
140     * {@link #write(int)} method, buffer length for {@link #write(byte[])},
141     * etc.) is given as an argument.
142     * <p>
143     * Subclasses can override this method to add common post-processing
144     * functionality without having to override all the write methods.
145     * The default implementation does nothing.
146     *
147     * @since 2.0
148     * @param n number of bytes written
149     * @throws IOException if the post-processing fails
150     */
151    protected void afterWrite(final int n) throws IOException {
152        // noop
153    }
154
155    /**
156     * Handle any IOExceptions thrown.
157     * <p>
158     * This method provides a point to implement custom exception
159     * handling. The default behavior is to re-throw the exception.
160     * @param e The IOException thrown
161     * @throws IOException if an I/O error occurs
162     * @since 2.0
163     */
164    protected void handleIOException(final IOException e) throws IOException {
165        throw e;
166    }
167
168}