blob: 4c4ce065f2a8fe178d07b95f1fd68f793abe4d78 [file] [log] [blame]
/*
* 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 java.io;
import java.nio.CharBuffer;
import java.nio.ReadOnlyBufferException;
/**
* The base class for all readers. A reader is a means of reading data from a
* source in a character-wise manner. Some readers also support marking a
* position in the input and returning to this position later.
* <p>
* This abstract class does not provide a fully working implementation, so it
* needs to be subclassed, and at least the {@link #read(char[], int, int)} and
* {@link #close()} methods needs to be overridden. Overriding some of the
* non-abstract methods is also often advised, since it might result in higher
* efficiency.
* <p>
* Many specialized readers for purposes like reading from a file already exist
* in this package.
*
* @see Writer
*/
public abstract class Reader implements Readable, Closeable {
/**
* The object used to synchronize access to the reader.
*/
protected Object lock;
/**
* Constructs a new {@code Reader} with {@code this} as the object used to
* synchronize critical sections.
*/
protected Reader() {
super();
lock = this;
}
/**
* Constructs a new {@code Reader} with {@code lock} used to synchronize
* critical sections.
*
* @param lock
* the {@code Object} used to synchronize critical sections.
* @throws NullPointerException
* if {@code lock} is {@code null}.
*/
protected Reader(Object lock) {
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}
/**
* Closes this reader. Implementations of this method should free any
* resources associated with the reader.
*
* @throws IOException
* if an error occurs while closing this reader.
*/
public abstract void close() throws IOException;
/**
* Sets a mark position in this reader. The parameter {@code readLimit}
* indicates how many characters can be read before the mark is invalidated.
* Calling {@code reset()} will reposition the reader back to the marked
* position if {@code readLimit} has not been surpassed.
* <p>
* This default implementation simply throws an {@code IOException};
* subclasses must provide their own implementation.
*
* @param readLimit
* the number of characters that can be read before the mark is
* invalidated.
* @throws IllegalArgumentException
* if {@code readLimit < 0}.
* @throws IOException
* if an error occurs while setting a mark in this reader.
* @see #markSupported()
* @see #reset()
*/
public void mark(int readLimit) throws IOException {
throw new IOException();
}
/**
* Indicates whether this reader supports the {@code mark()} and
* {@code reset()} methods. This default implementation returns
* {@code false}.
*
* @return always {@code false}.
*/
public boolean markSupported() {
return false;
}
/**
* Reads a single character from this reader and returns it as an integer
* with the two higher-order bytes set to 0. Returns -1 if the end of the
* reader has been reached.
*
* @return the character read or -1 if the end of the reader has been
* reached.
* @throws IOException
* if this reader is closed or some other I/O error occurs.
*/
public int read() throws IOException {
synchronized (lock) {
char[] charArray = new char[1];
if (read(charArray, 0, 1) != -1) {
return charArray[0];
}
return -1;
}
}
/**
* Reads characters from this reader and stores them in the character array
* {@code buf} starting at offset 0. Returns the number of characters
* actually read or -1 if the end of the reader has been reached.
*
* @param buf
* character array to store the characters read.
* @return the number of characters read or -1 if the end of the reader has
* been reached.
* @throws IOException
* if this reader is closed or some other I/O error occurs.
*/
public int read(char[] buf) throws IOException {
// BEGIN android-note
// changed array notation to be consistent with the rest of harmony
// END android-note
return read(buf, 0, buf.length);
}
/**
* Reads at most {@code count} characters from this reader and stores them
* at {@code offset} in the character array {@code buf}. Returns the number
* of characters actually read or -1 if the end of the reader has been
* reached.
*
* @param buf
* the character array to store the characters read.
* @param offset
* the initial position in {@code buffer} to store the characters
* read from this reader.
* @param count
* the maximum number of characters to read.
* @return the number of characters read or -1 if the end of the reader has
* been reached.
* @throws IOException
* if this reader is closed or some other I/O error occurs.
*/
public abstract int read(char[] buf, int offset, int count)
throws IOException;
// BEGIN android-note
// changed array notation to be consistent with the rest of harmony
// END android-note
/**
* Indicates whether this reader is ready to be read without blocking.
* Returns {@code true} if this reader will not block when {@code read} is
* called, {@code false} if unknown or blocking will occur. This default
* implementation always returns {@code false}.
*
* @return always {@code false}.
* @throws IOException
* if this reader is closed or some other I/O error occurs.
* @see #read()
* @see #read(char[])
* @see #read(char[], int, int)
*/
public boolean ready() throws IOException {
return false;
}
/**
* Resets this reader's position to the last {@code mark()} location.
* Invocations of {@code read()} and {@code skip()} will occur from this new
* location. If this reader has not been marked, the behavior of
* {@code reset()} is implementation specific. This default
* implementation throws an {@code IOException}.
*
* @throws IOException
* always thrown in this default implementation.
* @see #mark(int)
* @see #markSupported()
*/
public void reset() throws IOException {
throw new IOException();
}
/**
* Skips {@code amount} characters in this reader. Subsequent calls of
* {@code read} methods will not return these characters unless {@code
* reset()} is used. This method may perform multiple reads to read {@code
* count} characters.
*
* @param count
* the maximum number of characters to skip.
* @return the number of characters actually skipped.
* @throws IllegalArgumentException
* if {@code amount < 0}.
* @throws IOException
* if this reader is closed or some other I/O error occurs.
* @see #mark(int)
* @see #markSupported()
* @see #reset()
*/
public long skip(long count) throws IOException {
if (count < 0) {
throw new IllegalArgumentException();
}
synchronized (lock) {
long skipped = 0;
int toRead = count < 512 ? (int) count : 512;
char[] charsSkipped = new char[toRead];
while (skipped < count) {
int read = read(charsSkipped, 0, toRead);
if (read == -1) {
return skipped;
}
skipped += read;
if (read < toRead) {
return skipped;
}
if (count - skipped < toRead) {
toRead = (int) (count - skipped);
}
}
return skipped;
}
}
/**
* Reads characters and puts them into the {@code target} character buffer.
*
* @param target
* the destination character buffer.
* @return the number of characters put into {@code target} or -1 if the end
* of this reader has been reached before a character has been read.
* @throws IOException
* if any I/O error occurs while reading from this reader.
* @throws NullPointerException
* if {@code target} is {@code null}.
* @throws ReadOnlyBufferException
* if {@code target} is read-only.
*/
public int read(CharBuffer target) throws IOException {
if (null == target) {
throw new NullPointerException();
}
int length = target.length();
char[] buf = new char[length];
length = Math.min(length, read(buf));
if (length > 0) {
target.put(buf, 0, length);
}
return length;
}
}