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     */
017    package org.apache.commons.io.input;
018    
019    import java.io.FilterInputStream;
020    import java.io.IOException;
021    import 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: ProxyInputStream.java 1304052 2012-03-22 20:55:29Z ggregory $
036     */
037    public 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(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                int b = in.read();
059                afterRead(b != -1 ? 1 : -1);
060                return b;
061            } catch (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(byte[] bts) throws IOException {
075            try {
076                beforeRead(bts != null ? bts.length : 0);
077                int n = in.read(bts);
078                afterRead(n);
079                return n;
080            } catch (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(byte[] bts, int off, int len) throws IOException {
096            try {
097                beforeRead(len);
098                int n = in.read(bts, off, len);
099                afterRead(n);
100                return n;
101            } catch (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(long ln) throws IOException {
115            try {
116                return in.skip(ln);
117            } catch (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 (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 (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(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 (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(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(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(IOException e) throws IOException {
233            throw e;
234        }
235    
236    }