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 018package org.apache.commons.io.input; 019 020import java.io.FilterInputStream; 021import java.io.IOException; 022import java.io.InputStream; 023 024import org.apache.commons.io.build.AbstractStreamBuilder; 025 026/** 027 * An unsynchronized version of {@link FilterInputStream}, not thread-safe. 028 * <p> 029 * Wraps an existing {@link InputStream} and performs some transformation on the input data while it is being read. Transformations can be anything from a 030 * simple byte-wise filtering input data to an on-the-fly compression or decompression of the underlying stream. Input streams that wrap another input stream 031 * and provide some additional functionality on top of it usually inherit from this class. 032 * </p> 033 * <p> 034 * Provenance: Apache Harmony and modified. 035 * </p> 036 * 037 * @see FilterInputStream 038 * @since 2.12.0 039 */ 040//@NotThreadSafe 041public class UnsynchronizedFilterInputStream extends InputStream { 042 043 /** 044 * Builds a new {@link UnsynchronizedFilterInputStream} instance. 045 * <p> 046 * Using File IO: 047 * </p> 048 * <pre>{@code 049 * UnsynchronizedFilterInputStream s = UnsynchronizedFilterInputStream.builder() 050 * .setFile(file) 051 * .get()} 052 * </pre> 053 * <p> 054 * Using NIO Path: 055 * </p> 056 * <pre>{@code 057 * UnsynchronizedFilterInputStream s = UnsynchronizedFilterInputStream.builder() 058 * .setPath(path) 059 * .get()} 060 * </pre> 061 */ 062 public static class Builder extends AbstractStreamBuilder<UnsynchronizedFilterInputStream, Builder> { 063 064 /** 065 * Constructs a new instance. 066 * 067 * @throws UnsupportedOperationException if the origin cannot be converted to an InputStream. 068 */ 069 @SuppressWarnings("resource") // Caller closes. 070 @Override 071 public UnsynchronizedFilterInputStream get() throws IOException { 072 return new UnsynchronizedFilterInputStream(getOrigin().getInputStream()); 073 } 074 075 } 076 077 /** 078 * Constructs a new {@link Builder}. 079 * 080 * @return a new {@link Builder}. 081 */ 082 public static Builder builder() { 083 return new Builder(); 084 } 085 086 /** 087 * The source input stream that is filtered. 088 */ 089 protected volatile InputStream inputStream; 090 091 /** 092 * Constructs a new {@code FilterInputStream} with the specified input stream as source. 093 * 094 * @param inputStream the non-null InputStream to filter reads on. 095 */ 096 UnsynchronizedFilterInputStream(final InputStream inputStream) { 097 this.inputStream = inputStream; 098 } 099 100 /** 101 * Returns the number of bytes that are available before this stream will block. 102 * 103 * @return the number of bytes available before blocking. 104 * @throws IOException if an error occurs in this stream. 105 */ 106 @Override 107 public int available() throws IOException { 108 return inputStream.available(); 109 } 110 111 /** 112 * Closes this stream. This implementation closes the filtered stream. 113 * 114 * @throws IOException if an error occurs while closing this stream. 115 */ 116 @Override 117 public void close() throws IOException { 118 inputStream.close(); 119 } 120 121 /** 122 * Sets a mark position in this stream. The parameter {@code readlimit} indicates how many bytes can be read before the mark is invalidated. Sending 123 * {@code reset()} will reposition this stream back to the marked position, provided that {@code readlimit} has not been surpassed. 124 * <p> 125 * This implementation sets a mark in the filtered stream. 126 * 127 * @param readlimit the number of bytes that can be read from this stream before the mark is invalidated. 128 * @see #markSupported() 129 * @see #reset() 130 */ 131 @SuppressWarnings("sync-override") // by design. 132 @Override 133 public void mark(final int readlimit) { 134 inputStream.mark(readlimit); 135 } 136 137 /** 138 * Indicates whether this stream supports {@code mark()} and {@code reset()}. This implementation returns whether or not the filtered stream supports 139 * marking. 140 * 141 * @return {@code true} if {@code mark()} and {@code reset()} are supported, {@code false} otherwise. 142 * @see #mark(int) 143 * @see #reset() 144 * @see #skip(long) 145 */ 146 @Override 147 public boolean markSupported() { 148 return inputStream.markSupported(); 149 } 150 151 /** 152 * Reads a single byte from the filtered stream and returns it as an integer in the range from 0 to 255. Returns -1 if the end of this stream has been 153 * reached. 154 * 155 * @return the byte read or -1 if the end of the filtered stream has been reached. 156 * @throws IOException if the stream is closed or another IOException occurs. 157 */ 158 @Override 159 public int read() throws IOException { 160 return inputStream.read(); 161 } 162 163 /** 164 * Reads bytes from this stream and stores them in the byte array {@code buffer}. Returns the number of bytes actually read or -1 if no bytes were read and 165 * the end of this stream was encountered. This implementation reads bytes from the filtered stream. 166 * 167 * @param buffer the byte array in which to store the read bytes. 168 * @return the number of bytes actually read or -1 if the end of the filtered stream has been reached while reading. 169 * @throws IOException if this stream is closed or another IOException occurs. 170 */ 171 @Override 172 public int read(final byte[] buffer) throws IOException { 173 return read(buffer, 0, buffer.length); 174 } 175 176 /** 177 * Reads at most {@code count} bytes from this stream and stores them in the byte array {@code buffer} starting at {@code offset}. Returns the number of 178 * bytes actually read or -1 if no bytes have been read and the end of this stream has been reached. This implementation reads bytes from the filtered 179 * stream. 180 * 181 * @param buffer the byte array in which to store the bytes read. 182 * @param offset the initial position in {@code buffer} to store the bytes read from this stream. 183 * @param count the maximum number of bytes to store in {@code buffer}. 184 * @return the number of bytes actually read or -1 if the end of the filtered stream has been reached while reading. 185 * @throws IOException if this stream is closed or another I/O error occurs. 186 */ 187 @Override 188 public int read(final byte[] buffer, final int offset, final int count) throws IOException { 189 return inputStream.read(buffer, offset, count); 190 } 191 192 /** 193 * Resets this stream to the last marked location. This implementation resets the target stream. 194 * 195 * @throws IOException if this stream is already closed, no mark has been set or the mark is no longer valid because more than {@code readlimit} bytes have 196 * been read since setting the mark. 197 * @see #mark(int) 198 * @see #markSupported() 199 */ 200 @SuppressWarnings("sync-override") // by design. 201 @Override 202 public void reset() throws IOException { 203 inputStream.reset(); 204 } 205 206 /** 207 * Skips {@code count} number of bytes in this stream. Subsequent {@code read()}'s will not return these bytes unless {@code reset()} is used. This 208 * implementation skips {@code count} number of bytes in the filtered stream. 209 * 210 * @param count the number of bytes to skip. 211 * @return the number of bytes actually skipped. 212 * @throws IOException if this stream is closed or another IOException occurs. 213 * @see #mark(int) 214 * @see #reset() 215 */ 216 @Override 217 public long skip(final long count) throws IOException { 218 return inputStream.skip(count); 219 } 220}