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 * https://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(this); 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 UnsynchronizedFilterInputStream(final Builder builder) throws IOException { 121 this.inputStream = builder.getInputStream(); 122 } 123 124 /** 125 * Constructs a new {@code FilterInputStream} with the specified input stream as source. 126 * 127 * @param inputStream the non-null InputStream to filter reads on. 128 */ 129 UnsynchronizedFilterInputStream(final InputStream inputStream) { 130 this.inputStream = inputStream; 131 } 132 133 /** 134 * Returns the number of bytes that are available before this stream will block. 135 * 136 * @return the number of bytes available before blocking. 137 * @throws IOException if an error occurs in this stream. 138 */ 139 @Override 140 public int available() throws IOException { 141 return inputStream.available(); 142 } 143 144 /** 145 * Closes this stream. This implementation closes the filtered stream. 146 * 147 * @throws IOException if an error occurs while closing this stream. 148 */ 149 @Override 150 public void close() throws IOException { 151 inputStream.close(); 152 } 153 154 /** 155 * Sets a mark position in this stream. The parameter {@code readLimit} indicates how many bytes can be read before the mark is invalidated. Sending 156 * {@code reset()} will reposition this stream back to the marked position, provided that {@code readLimit} has not been surpassed. 157 * <p> 158 * This implementation sets a mark in the filtered stream. 159 * 160 * @param readLimit the number of bytes that can be read from this stream before the mark is invalidated. 161 * @see #markSupported() 162 * @see #reset() 163 */ 164 @SuppressWarnings("sync-override") // by design. 165 @Override 166 public void mark(final int readLimit) { 167 inputStream.mark(readLimit); 168 } 169 170 /** 171 * Indicates whether this stream supports {@code mark()} and {@code reset()}. This implementation returns whether or not the filtered stream supports 172 * marking. 173 * 174 * @return {@code true} if {@code mark()} and {@code reset()} are supported, {@code false} otherwise. 175 * @see #mark(int) 176 * @see #reset() 177 * @see #skip(long) 178 */ 179 @Override 180 public boolean markSupported() { 181 return inputStream.markSupported(); 182 } 183 184 /** 185 * 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 186 * reached. 187 * 188 * @return the byte read or -1 if the end of the filtered stream has been reached. 189 * @throws IOException if the stream is closed or another IOException occurs. 190 */ 191 @Override 192 public int read() throws IOException { 193 return inputStream.read(); 194 } 195 196 /** 197 * 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 198 * the end of this stream was encountered. This implementation reads bytes from the filtered stream. 199 * 200 * @param buffer the byte array in which to store the read bytes. 201 * @return the number of bytes actually read or -1 if the end of the filtered stream has been reached while reading. 202 * @throws IOException if this stream is closed or another IOException occurs. 203 */ 204 @Override 205 public int read(final byte[] buffer) throws IOException { 206 return read(buffer, 0, buffer.length); 207 } 208 209 /** 210 * 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 211 * 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 212 * stream. 213 * 214 * @param buffer the byte array in which to store the bytes read. 215 * @param offset the initial position in {@code buffer} to store the bytes read from this stream. 216 * @param count the maximum number of bytes to store in {@code buffer}. 217 * @return the number of bytes actually read or -1 if the end of the filtered stream has been reached while reading. 218 * @throws IOException if this stream is closed or another I/O error occurs. 219 */ 220 @Override 221 public int read(final byte[] buffer, final int offset, final int count) throws IOException { 222 return inputStream.read(buffer, offset, count); 223 } 224 225 /** 226 * Resets this stream to the last marked location. This implementation resets the target stream. 227 * 228 * @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 229 * been read since setting the mark. 230 * @see #mark(int) 231 * @see #markSupported() 232 */ 233 @SuppressWarnings("sync-override") // by design. 234 @Override 235 public void reset() throws IOException { 236 inputStream.reset(); 237 } 238 239 /** 240 * Skips {@code count} number of bytes in this stream. Subsequent {@code read()}'s will not return these bytes unless {@code reset()} is used. This 241 * implementation skips {@code count} number of bytes in the filtered stream. 242 * 243 * @param count the number of bytes to skip. 244 * @return the number of bytes actually skipped. 245 * @throws IOException if this stream is closed or another IOException occurs. 246 * @see #mark(int) 247 * @see #reset() 248 */ 249 @Override 250 public long skip(final long count) throws IOException { 251 return inputStream.skip(count); 252 } 253}