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