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.input; 018 019import static org.apache.commons.io.IOUtils.EOF; 020 021import java.io.FilterInputStream; 022import java.io.IOException; 023import java.io.InputStream; 024 025import org.apache.commons.io.IOUtils; 026 027/** 028 * A Proxy stream which acts as expected, that is it passes the method 029 * calls on to the proxied stream and doesn't change which methods are 030 * being called. 031 * <p> 032 * It is an alternative base class to FilterInputStream 033 * to increase reusability, because FilterInputStream changes the 034 * methods being called, such as read(byte[]) to read(byte[], int, int). 035 * </p> 036 * <p> 037 * See the protected methods for ways in which a subclass can easily decorate 038 * a stream with custom pre-, post- or error processing functionality. 039 * </p> 040 */ 041public abstract class ProxyInputStream extends FilterInputStream { 042 043 /** 044 * Constructs a new ProxyInputStream. 045 * 046 * @param proxy the InputStream to delegate to 047 */ 048 public ProxyInputStream(final InputStream proxy) { 049 super(proxy); 050 // the proxy is stored in a protected superclass variable named 'in' 051 } 052 053 /** 054 * Invokes the delegate's {@code read()} method. 055 * @return the byte read or -1 if the end of stream 056 * @throws IOException if an I/O error occurs. 057 */ 058 @Override 059 public int read() throws IOException { 060 try { 061 beforeRead(1); 062 final int b = in.read(); 063 afterRead(b != EOF ? 1 : EOF); 064 return b; 065 } catch (final IOException e) { 066 handleIOException(e); 067 return EOF; 068 } 069 } 070 071 /** 072 * Invokes the delegate's {@code read(byte[])} method. 073 * @param bts the buffer to read the bytes into 074 * @return the number of bytes read or EOF if the end of stream 075 * @throws IOException if an I/O error occurs. 076 */ 077 @Override 078 public int read(final byte[] bts) throws IOException { 079 try { 080 beforeRead(IOUtils.length(bts)); 081 final int n = in.read(bts); 082 afterRead(n); 083 return n; 084 } catch (final IOException e) { 085 handleIOException(e); 086 return EOF; 087 } 088 } 089 090 /** 091 * Invokes the delegate's {@code read(byte[], int, int)} method. 092 * @param bts the buffer to read the bytes into 093 * @param off The start offset 094 * @param len The number of bytes to read 095 * @return the number of bytes read or -1 if the end of stream 096 * @throws IOException if an I/O error occurs. 097 */ 098 @Override 099 public int read(final byte[] bts, final int off, final int len) throws IOException { 100 try { 101 beforeRead(len); 102 final int n = in.read(bts, off, len); 103 afterRead(n); 104 return n; 105 } catch (final IOException e) { 106 handleIOException(e); 107 return EOF; 108 } 109 } 110 111 /** 112 * Invokes the delegate's {@code skip(long)} method. 113 * @param ln the number of bytes to skip 114 * @return the actual number of bytes skipped 115 * @throws IOException if an I/O error occurs. 116 */ 117 @Override 118 public long skip(final long ln) throws IOException { 119 try { 120 return in.skip(ln); 121 } catch (final IOException e) { 122 handleIOException(e); 123 return 0; 124 } 125 } 126 127 /** 128 * Invokes the delegate's {@code available()} method. 129 * @return the number of available bytes 130 * @throws IOException if an I/O error occurs. 131 */ 132 @Override 133 public int available() throws IOException { 134 try { 135 return super.available(); 136 } catch (final IOException e) { 137 handleIOException(e); 138 return 0; 139 } 140 } 141 142 /** 143 * Invokes the delegate's {@code close()} method. 144 * @throws IOException if an I/O error occurs. 145 */ 146 @Override 147 public void close() throws IOException { 148 IOUtils.close(in, this::handleIOException); 149 } 150 151 /** 152 * Invokes the delegate's {@code mark(int)} 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()} 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()} 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 @SuppressWarnings("unused") // Possibly thrown from subclasses. 201 protected void beforeRead(final int n) throws IOException { 202 // no-op 203 } 204 205 /** 206 * Invoked by the read methods after the proxied call has returned 207 * successfully. The number of bytes returned to the caller (or -1 if 208 * the end of stream was reached) is given as an argument. 209 * <p> 210 * Subclasses can override this method to add common post-processing 211 * functionality without having to override all the read methods. 212 * The default implementation does nothing. 213 * <p> 214 * Note this method is <em>not</em> called from {@link #skip(long)} or 215 * {@link #reset()}. You need to explicitly override those methods if 216 * you want to add post-processing steps also to them. 217 * 218 * @since 2.0 219 * @param n number of bytes read, or -1 if the end of stream was reached 220 * @throws IOException if the post-processing fails 221 */ 222 @SuppressWarnings("unused") // Possibly thrown from subclasses. 223 protected void afterRead(final int n) throws IOException { 224 // no-op 225 } 226 227 /** 228 * Handle any IOExceptions thrown. 229 * <p> 230 * This method provides a point to implement custom exception 231 * handling. The default behavior is to re-throw the exception. 232 * @param e The IOException thrown 233 * @throws IOException if an I/O error occurs. 234 * @since 2.0 235 */ 236 protected void handleIOException(final IOException e) throws IOException { 237 throw e; 238 } 239 240}