1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.io.input; 19 20 import java.io.FilterInputStream; 21 import java.io.IOException; 22 import java.io.InputStream; 23 24 import org.apache.commons.io.build.AbstractStreamBuilder; 25 26 /** 27 * An unsynchronized version of {@link FilterInputStream}, not thread-safe. 28 * <p> 29 * Wraps an existing {@link InputStream} and performs some transformation on the input data while it is being read. Transformations can be anything from a 30 * 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 31 * and provide some additional functionality on top of it usually inherit from this class. 32 * </p> 33 * <p> 34 * To build an instance, use {@link Builder}. 35 * </p> 36 * <p> 37 * Provenance: Apache Harmony and modified. 38 * </p> 39 * 40 * @see Builder 41 * @see FilterInputStream 42 * @since 2.12.0 43 */ 44 //@NotThreadSafe 45 public class UnsynchronizedFilterInputStream extends InputStream { 46 47 // @formatter:off 48 /** 49 * Builds a new {@link UnsynchronizedFilterInputStream}. 50 * 51 * <p> 52 * Using File IO: 53 * </p> 54 * <pre>{@code 55 * UnsynchronizedFilterInputStream s = UnsynchronizedFilterInputStream.builder() 56 * .setFile(file) 57 * .get();} 58 * </pre> 59 * <p> 60 * Using NIO Path: 61 * </p> 62 * <pre>{@code 63 * UnsynchronizedFilterInputStream s = UnsynchronizedFilterInputStream.builder() 64 * .setPath(path) 65 * .get();} 66 * </pre> 67 * 68 * @see #get() 69 */ 70 // @formatter:on 71 public static class Builder extends AbstractStreamBuilder<UnsynchronizedFilterInputStream, Builder> { 72 73 /** 74 * Constructs a new builder of {@link UnsynchronizedFilterInputStream}. 75 */ 76 public Builder() { 77 // empty 78 } 79 80 /** 81 * Builds a new {@link UnsynchronizedFilterInputStream}. 82 * <p> 83 * You must set an aspect that supports {@link #getInputStream()}, otherwise, this method throws an exception. 84 * </p> 85 * <p> 86 * This builder uses the following aspects: 87 * </p> 88 * <ul> 89 * <li>{@link #getInputStream()}</li> 90 * </ul> 91 * 92 * @return a new instance. 93 * @throws IllegalStateException if the {@code origin} is {@code null}. 94 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link InputStream}. 95 * @throws IOException if an I/O error occurs converting to an {@link InputStream} using {@link #getInputStream()}. 96 * @see #getInputStream() 97 * @see #getUnchecked() 98 */ 99 @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 }