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 * To build an instance, use {@link Builder}. 035 * </p> 036 * <p> 037 * Provenance: Apache Harmony and modified. 038 * </p> 039 * 040 * @see Builder 041 * @see FilterInputStream 042 * @since 2.12.0 043 */ 044//@NotThreadSafe 045public class UnsynchronizedFilterInputStream extends InputStream { 046 047 // @formatter:off 048 /** 049 * Builds a new {@link UnsynchronizedFilterInputStream}. 050 * 051 * <p> 052 * Using File IO: 053 * </p> 054 * <pre>{@code 055 * UnsynchronizedFilterInputStream s = UnsynchronizedFilterInputStream.builder() 056 * .setFile(file) 057 * .get();} 058 * </pre> 059 * <p> 060 * Using NIO Path: 061 * </p> 062 * <pre>{@code 063 * UnsynchronizedFilterInputStream s = UnsynchronizedFilterInputStream.builder() 064 * .setPath(path) 065 * .get();} 066 * </pre> 067 * 068 * @see #get() 069 */ 070 // @formatter:on 071 public static class Builder extends AbstractStreamBuilder<UnsynchronizedFilterInputStream, Builder> { 072 073 /** 074 * Constructs a new builder of {@link UnsynchronizedFilterInputStream}. 075 */ 076 public Builder() { 077 // empty 078 } 079 080 /** 081 * Builds a new {@link UnsynchronizedFilterInputStream}. 082 * <p> 083 * You must set an aspect that supports {@link #getInputStream()}, otherwise, this method throws an exception. 084 * </p> 085 * <p> 086 * This builder uses the following aspects: 087 * </p> 088 * <ul> 089 * <li>{@link #getInputStream()}</li> 090 * </ul> 091 * 092 * @return a new instance. 093 * @throws IllegalStateException if the {@code origin} is {@code null}. 094 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link InputStream}. 095 * @throws IOException if an I/O error occurs converting to an {@link InputStream} using {@link #getInputStream()}. 096 * @see #getInputStream() 097 * @see #getUnchecked() 098 */ 099 @Override 100 public UnsynchronizedFilterInputStream get() throws IOException { 101 return new UnsynchronizedFilterInputStream(getInputStream()); 102 } 103 104 } 105 106 /** 107 * Constructs a new {@link Builder}. 108 * 109 * @return a new {@link Builder}. 110 */ 111 public static Builder builder() { 112 return new Builder(); 113 } 114 115 /** 116 * The source input stream that is filtered. 117 */ 118 protected volatile InputStream inputStream; 119 120 /** 121 * Constructs a new {@code FilterInputStream} with the specified input stream as source. 122 * 123 * @param inputStream the non-null InputStream to filter reads on. 124 */ 125 UnsynchronizedFilterInputStream(final InputStream inputStream) { 126 this.inputStream = inputStream; 127 } 128 129 /** 130 * Returns the number of bytes that are available before this stream will block. 131 * 132 * @return the number of bytes available before blocking. 133 * @throws IOException if an error occurs in this stream. 134 */ 135 @Override 136 public int available() throws IOException { 137 return inputStream.available(); 138 } 139 140 /** 141 * Closes this stream. This implementation closes the filtered stream. 142 * 143 * @throws IOException if an error occurs while closing this stream. 144 */ 145 @Override 146 public void close() throws IOException { 147 inputStream.close(); 148 } 149 150 /** 151 * Sets a mark position in this stream. The parameter {@code readLimit} indicates how many bytes can be read before the mark is invalidated. Sending 152 * {@code reset()} will reposition this stream back to the marked position, provided that {@code readLimit} has not been surpassed. 153 * <p> 154 * This implementation sets a mark in the filtered stream. 155 * 156 * @param readLimit the number of bytes that can be read from this stream before the mark is invalidated. 157 * @see #markSupported() 158 * @see #reset() 159 */ 160 @SuppressWarnings("sync-override") // by design. 161 @Override 162 public void mark(final int readLimit) { 163 inputStream.mark(readLimit); 164 } 165 166 /** 167 * Indicates whether this stream supports {@code mark()} and {@code reset()}. This implementation returns whether or not the filtered stream supports 168 * marking. 169 * 170 * @return {@code true} if {@code mark()} and {@code reset()} are supported, {@code false} otherwise. 171 * @see #mark(int) 172 * @see #reset() 173 * @see #skip(long) 174 */ 175 @Override 176 public boolean markSupported() { 177 return inputStream.markSupported(); 178 } 179 180 /** 181 * 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 182 * reached. 183 * 184 * @return the byte read or -1 if the end of the filtered stream has been reached. 185 * @throws IOException if the stream is closed or another IOException occurs. 186 */ 187 @Override 188 public int read() throws IOException { 189 return inputStream.read(); 190 } 191 192 /** 193 * 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 194 * the end of this stream was encountered. This implementation reads bytes from the filtered stream. 195 * 196 * @param buffer the byte array in which to store the read bytes. 197 * @return the number of bytes actually read or -1 if the end of the filtered stream has been reached while reading. 198 * @throws IOException if this stream is closed or another IOException occurs. 199 */ 200 @Override 201 public int read(final byte[] buffer) throws IOException { 202 return read(buffer, 0, buffer.length); 203 } 204 205 /** 206 * 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 207 * 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 208 * stream. 209 * 210 * @param buffer the byte array in which to store the bytes read. 211 * @param offset the initial position in {@code buffer} to store the bytes read from this stream. 212 * @param count the maximum number of bytes to store in {@code buffer}. 213 * @return the number of bytes actually read or -1 if the end of the filtered stream has been reached while reading. 214 * @throws IOException if this stream is closed or another I/O error occurs. 215 */ 216 @Override 217 public int read(final byte[] buffer, final int offset, final int count) throws IOException { 218 return inputStream.read(buffer, offset, count); 219 } 220 221 /** 222 * Resets this stream to the last marked location. This implementation resets the target stream. 223 * 224 * @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 225 * been read since setting the mark. 226 * @see #mark(int) 227 * @see #markSupported() 228 */ 229 @SuppressWarnings("sync-override") // by design. 230 @Override 231 public void reset() throws IOException { 232 inputStream.reset(); 233 } 234 235 /** 236 * Skips {@code count} number of bytes in this stream. Subsequent {@code read()}'s will not return these bytes unless {@code reset()} is used. This 237 * implementation skips {@code count} number of bytes in the filtered stream. 238 * 239 * @param count the number of bytes to skip. 240 * @return the number of bytes actually skipped. 241 * @throws IOException if this stream is closed or another IOException occurs. 242 * @see #mark(int) 243 * @see #reset() 244 */ 245 @Override 246 public long skip(final long count) throws IOException { 247 return inputStream.skip(count); 248 } 249}