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.wicket.util.io;
018
019import java.io.FilterInputStream;
020import java.io.IOException;
021import java.io.InputStream;
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.
027 * <p>
028 * It is an alternative base class to FilterInputStream
029 * to increase reusability, because FilterInputStream changes the
030 * methods being called, such as read(byte[]) to read(byte[], int, int).
031 * <p>
032 * See the protected methods for ways in which a subclass can easily decorate
033 * a stream with custom pre-, post- or error processing functionality.
034 *
035 * @version $Id$
036 */
037public abstract class ProxyInputStream extends FilterInputStream {
038
039    /**
040     * Constructs a new ProxyInputStream.
041     *
042     * @param proxy  the InputStream to delegate to
043     */
044    public ProxyInputStream(final InputStream proxy) {
045        super(proxy);
046        // the proxy is stored in a protected superclass variable named 'in'
047    }
048
049    /**
050     * Invokes the delegate's <code>read()</code> method.
051     * @return the byte read or -1 if the end of stream
052     * @throws IOException if an I/O error occurs
053     */
054    @Override
055    public int read() throws IOException {
056        try {
057            beforeRead(1);
058            final int b = in.read();
059            afterRead(b != -1 ? 1 : -1);
060            return b;
061        } catch (final IOException e) {
062            handleIOException(e);
063            return -1;
064        }
065    }
066
067    /**
068     * Invokes the delegate's <code>read(byte[])</code> method.
069     * @param bts the buffer to read the bytes into
070     * @return the number of bytes read or -1 if the end of stream
071     * @throws IOException if an I/O error occurs
072     */
073    @Override
074    public int read(final byte[] bts) throws IOException {
075        try {
076            beforeRead(bts != null ? bts.length : 0);
077            final int n = in.read(bts);
078            afterRead(n);
079            return n;
080        } catch (final IOException e) {
081            handleIOException(e);
082            return -1;
083        }
084    }
085
086    /**
087     * Invokes the delegate's <code>read(byte[], int, int)</code> method.
088     * @param bts the buffer to read the bytes into
089     * @param off The start offset
090     * @param len The number of bytes to read
091     * @return the number of bytes read or -1 if the end of stream
092     * @throws IOException if an I/O error occurs
093     */
094    @Override
095    public int read(final byte[] bts, final int off, final int len) throws IOException {
096        try {
097            beforeRead(len);
098            final int n = in.read(bts, off, len);
099            afterRead(n);
100            return n;
101        } catch (final IOException e) {
102            handleIOException(e);
103            return -1;
104        }
105    }
106
107    /**
108     * Invokes the delegate's <code>skip(long)</code> method.
109     * @param ln the number of bytes to skip
110     * @return the actual number of bytes skipped
111     * @throws IOException if an I/O error occurs
112     */
113    @Override
114    public long skip(final long ln) throws IOException {
115        try {
116            return in.skip(ln);
117        } catch (final IOException e) {
118            handleIOException(e);
119            return 0;
120        }
121    }
122
123    /**
124     * Invokes the delegate's <code>available()</code> method.
125     * @return the number of available bytes
126     * @throws IOException if an I/O error occurs
127     */
128    @Override
129    public int available() throws IOException {
130        try {
131            return super.available();
132        } catch (final IOException e) {
133            handleIOException(e);
134            return 0;
135        }
136    }
137
138    /**
139     * Invokes the delegate's <code>close()</code> method.
140     * @throws IOException if an I/O error occurs
141     */
142    @Override
143    public void close() throws IOException {
144        try {
145            in.close();
146        } catch (final IOException e) {
147            handleIOException(e);
148        }
149    }
150
151    /**
152     * Invokes the delegate's <code>mark(int)</code> method.
153     * @param readlimit read ahead limit
154     */
155    @Override
156    public synchronized void mark(final int readlimit) {
157        in.mark(readlimit);
158    }
159
160    /**
161     * Invokes the delegate's <code>reset()</code> method.
162     * @throws IOException if an I/O error occurs
163     */
164    @Override
165    public synchronized void reset() throws IOException {
166        try {
167            in.reset();
168        } catch (final IOException e) {
169            handleIOException(e);
170        }
171    }
172
173    /**
174     * Invokes the delegate's <code>markSupported()</code> method.
175     * @return true if mark is supported, otherwise false
176     */
177    @Override
178    public boolean markSupported() {
179        return in.markSupported();
180    }
181
182    /**
183     * Invoked by the read methods before the call is proxied. The number
184     * of bytes that the caller wanted to read (1 for the {@link #read()}
185     * method, buffer length for {@link #read(byte[])}, etc.) is given as
186     * an argument.
187     * <p>
188     * Subclasses can override this method to add common pre-processing
189     * functionality without having to override all the read methods.
190     * The default implementation does nothing.
191     * <p>
192     * Note this method is <em>not</em> called from {@link #skip(long)} or
193     * {@link #reset()}. You need to explicitly override those methods if
194     * you want to add pre-processing steps also to them.
195     *
196     * @since 2.0
197     * @param n number of bytes that the caller asked to be read
198     * @throws IOException if the pre-processing fails
199     */
200    protected void beforeRead(final int n) throws IOException {
201    }
202
203    /**
204     * Invoked by the read methods after the proxied call has returned
205     * successfully. The number of bytes returned to the caller (or -1 if
206     * the end of stream was reached) is given as an argument.
207     * <p>
208     * Subclasses can override this method to add common post-processing
209     * functionality without having to override all the read methods.
210     * The default implementation does nothing.
211     * <p>
212     * Note this method is <em>not</em> called from {@link #skip(long)} or
213     * {@link #reset()}. You need to explicitly override those methods if
214     * you want to add post-processing steps also to them.
215     *
216     * @since 2.0
217     * @param n number of bytes read, or -1 if the end of stream was reached
218     * @throws IOException if the post-processing fails
219     */
220    protected void afterRead(final int n) throws IOException {
221    }
222
223    /**
224     * Handle any IOExceptions thrown.
225     * <p>
226     * This method provides a point to implement custom exception
227     * handling. The default behaviour is to re-throw the exception.
228     * @param e The IOException thrown
229     * @throws IOException if an I/O error occurs
230     * @since 2.0
231     */
232    protected void handleIOException(final IOException e) throws IOException {
233        throw e;
234    }
235
236}