blob: 41369d2c0f646d13a30c0372bad473d8eef36f33 [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;
/**
* A specialized {@link Reader} for reading the contents of a char array.
*
* @see CharArrayWriter
*/
public class CharArrayReader extends Reader {
/**
* The buffer for characters.
*/
protected char[] buf;
/**
* The current buffer position.
*/
protected int pos;
/**
* The current mark position.
*/
protected int markedPos = -1;
/**
* The ending index of the buffer.
*/
protected int count;
/**
* Constructs a CharArrayReader on the char array {@code buf}. The size of
* the reader is set to the length of the buffer and the object to to read
* from is set to {@code buf}.
*
* @param buf
* the char array from which to read.
*/
public CharArrayReader(char[] buf) {
this.buf = buf;
this.count = buf.length;
}
/**
* Constructs a CharArrayReader on the char array {@code buf}. The size of
* the reader is set to {@code length} and the start position from which to
* read the buffer is set to {@code offset}.
*
* @param buf
* the char array from which to read.
* @param offset
* the index of the first character in {@code buf} to read.
* @param length
* the number of characters that can be read from {@code buf}.
* @throws IllegalArgumentException
* if {@code offset < 0} or {@code length < 0}, or if
* {@code offset} is greater than the size of {@code buf} .
*/
public CharArrayReader(char[] buf, int offset, int length) {
/*
* The spec of this constructor is broken. In defining the legal values
* of offset and length, it doesn't consider buffer's length. And to be
* compatible with the broken spec, we must also test whether
* (offset + length) overflows.
*/
if (offset < 0 || offset > buf.length || length < 0 || offset + length < 0) {
throw new IllegalArgumentException();
}
this.buf = buf;
this.pos = offset;
this.markedPos = offset;
/* This is according to spec */
int bufferLength = buf.length;
this.count = offset + length < bufferLength ? length : bufferLength;
}
/**
* This method closes this CharArrayReader. Once it is closed, you can no
* longer read from it. Only the first invocation of this method has any
* effect.
*/
@Override
public void close() {
synchronized (lock) {
if (isOpen()) {
buf = null;
}
}
}
/**
* Indicates whether this reader is open.
*
* @return {@code true} if the reader is open, {@code false} otherwise.
*/
private boolean isOpen() {
return buf != null;
}
/**
* Indicates whether this reader is closed.
*
* @return {@code true} if the reader is closed, {@code false} otherwise.
*/
private boolean isClosed() {
return buf == null;
}
/**
* Sets a mark position in this reader. The parameter {@code readLimit} is
* ignored for CharArrayReaders. Calling {@code reset()} will reposition the
* reader back to the marked position provided the mark has not been
* invalidated.
*
* @param readLimit
* ignored for CharArrayReaders.
* @throws IOException
* if this reader is closed.
*/
@Override
public void mark(int readLimit) throws IOException {
synchronized (lock) {
checkNotClosed();
markedPos = pos;
}
}
private void checkNotClosed() throws IOException {
if (isClosed()) {
throw new IOException("CharArrayReader is closed");
}
}
/**
* Indicates whether this reader supports the {@code mark()} and
* {@code reset()} methods.
*
* @return {@code true} for CharArrayReader.
* @see #mark(int)
* @see #reset()
*/
@Override
public boolean markSupported() {
return true;
}
/**
* 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 no more
* characters are available from this reader.
*
* @return the character read as an int or -1 if the end of the reader has
* been reached.
* @throws IOException
* if this reader is closed.
*/
@Override
public int read() throws IOException {
synchronized (lock) {
checkNotClosed();
if (pos == count) {
return -1;
}
return buf[pos++];
}
}
/**
* Reads at most {@code count} characters from this CharArrayReader 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 reader
* was encountered.
*
* @param buffer
* 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 len
* the maximum number of characters to read.
* @return number of characters read or -1 if the end of the reader has been
* reached.
* @throws IndexOutOfBoundsException
* if {@code offset < 0} or {@code len < 0}, or if
* {@code offset + len} is bigger than the size of
* {@code buffer}.
* @throws IOException
* if this reader is closed.
*/
@Override
public int read(char[] buffer, int offset, int len) throws IOException {
// BEGIN android-note
// changed array notation to be consistent with the rest of harmony
// END android-note
if (offset < 0 || offset > buffer.length) {
throw new ArrayIndexOutOfBoundsException("Offset out of bounds: " + offset);
}
if (len < 0 || len > buffer.length - offset) {
throw new ArrayIndexOutOfBoundsException("Length out of bounds: " + len);
}
synchronized (lock) {
checkNotClosed();
if (pos < this.count) {
int bytesRead = pos + len > this.count ? this.count - pos : len;
System.arraycopy(this.buf, pos, buffer, offset, bytesRead);
pos += bytesRead;
return bytesRead;
}
return -1;
}
}
/**
* Indicates whether this reader is ready to be read without blocking.
* Returns {@code true} if the next {@code read} will not block. Returns
* {@code false} if this reader may or may not block when {@code read} is
* called. The implementation in CharArrayReader always returns {@code true}
* even when it has been closed.
*
* @return {@code true} if this reader will not block when {@code read} is
* called, {@code false} if unknown or blocking will occur.
* @throws IOException
* if this reader is closed.
*/
@Override
public boolean ready() throws IOException {
synchronized (lock) {
checkNotClosed();
return pos != count;
}
}
/**
* 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, it is reset to the
* beginning of the string.
*
* @throws IOException
* if this reader is closed.
*/
@Override
public void reset() throws IOException {
synchronized (lock) {
checkNotClosed();
pos = markedPos != -1 ? markedPos : 0;
}
}
/**
* Skips {@code count} number of characters in this reader. Subsequent
* {@code read()}s will not return these characters unless {@code reset()}
* is used. This method does nothing and returns 0 if {@code n} is negative.
*
* @param n
* the number of characters to skip.
* @return the number of characters actually skipped.
* @throws IOException
* if this reader is closed.
*/
@Override
public long skip(long n) throws IOException {
synchronized (lock) {
checkNotClosed();
if (n <= 0) {
return 0;
}
long skipped = 0;
if (n < this.count - pos) {
pos = pos + (int) n;
skipped = n;
} else {
skipped = this.count - pos;
pos = this.count;
}
return skipped;
}
}
}