UnsynchronizedReader.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.io.input;
import static org.apache.commons.io.IOUtils.EOF;
import java.io.IOException;
import java.io.Reader;
import org.apache.commons.io.IOUtils;
/**
* A {@link Reader} without any of the superclass' synchronization.
*
* @since 2.17.0
*/
public abstract class UnsynchronizedReader extends Reader {
/**
* The maximum skip-buffer size.
*/
private static final int MAX_SKIP_BUFFER_SIZE = IOUtils.DEFAULT_BUFFER_SIZE;
/**
* Whether {@link #close()} completed successfully.
*/
private boolean closed;
/**
* The skip buffer, defaults to null until allocated in {@link UnsynchronizedReader#skip(long)}.
*/
private char skipBuffer[];
/**
* Checks if this instance is closed and throws an IOException if so.
*
* @throws IOException if this instance is closed.
*/
void checkOpen() throws IOException {
Input.checkOpen(!isClosed());
}
@Override
public void close() throws IOException {
closed = true;
}
/**
* Tests whether this instance is closed; if {@link #close()} completed successfully.
*
* @return whether this instance is closed.
*/
public boolean isClosed() {
return closed;
}
/**
* Sets whether this instance is closed.
*
* @param closed whether this instance is closed.
*/
public void setClosed(final boolean closed) {
this.closed = closed;
}
/**
* Skips characters by reading from this instance.
*
* This method will <em>block</em> until:
* <ul>
* <li>some characters are available,</li>
* <li>an I/O error occurs, or</li>
* <li>the end of the stream is reached.</li>
* </ul>
*
* @param n The number of characters to skip.
* @return The number of characters actually skipped.
* @throws IllegalArgumentException If {@code n} is negative.
* @throws IOException If an I/O error occurs.
*/
@Override
public long skip(final long n) throws IOException {
if (n < 0L) {
throw new IllegalArgumentException("skip value < 0");
}
final int bufSize = (int) Math.min(n, MAX_SKIP_BUFFER_SIZE);
if (skipBuffer == null || skipBuffer.length < bufSize) {
skipBuffer = new char[bufSize];
}
long remaining = n;
while (remaining > 0) {
final int countOrEof = read(skipBuffer, 0, (int) Math.min(remaining, bufSize));
if (countOrEof == EOF) {
break;
}
remaining -= countOrEof;
}
return n - remaining;
}
}