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