org.tbull.util
Class CharSequenceReader

Object
  extended by java.io.Reader
      extended by org.tbull.util.CharSequenceReader
All Implemented Interfaces:
Closeable, Readable

public class CharSequenceReader
extends Reader

Like StringReader, but reads arbitrary CharSequences and is unsynchronized.

StringReader has some weird behaviour. Not only are all methods synchronized (which may or may not be desirable), also it throws IOExceptions around without reason. And it is very sloppily documented. You have to read the source code to find out the aforementioned facts.

This class is not synchronized, you can decide yourself if you need sync. It does not throw checked exceptions except for mark/reset. (Only unchecked exceptions, which indicate programming errors at your side, are otherwise thrown.) And it generalizes the source material to any CharSequence, which String is only one example of, so you can read from a StringBuilder or something without constructing an intermediate String.

The last point, however, has some implications. In contrast to a String, a CharSequence is not necessarily immutable. For the mark/reset (rewind) mechanism to work reliably, the reader would have to buffer character data separately from the backing sequence, thus degrading performance, sucking memory and making everything more complicated. Therefore we decided to support rewinding only for character sequences known to be immutable. If you need rewinding with a mutable stream, you can easily wrap a BufferedReader around, which performs the necessary buffering.

The reader knows that Strings are immutable. Any other classes implementing CharSequence are assumed to be mutable unless you tell otherwise. Use setImmutable to tell the reader that the backing sequence is immutable, enabling direct mark/reset support. Make sure you tell the truth, as misuse of this feature may cause unpredictable behaviour!

Note well: Obviously, if the source sequence is sync'd on its own, like a StringBuffer, that sync will not cease to operate only because the sequence is wrapped with this reader. Yet you must not rely on that sync, because it only syncs the internals of the sequence, not the internals of the reader. Thus, in a concurrent environment, you must provide external sync, regardless of the implementation of the underlying sequence.

Ceterum censeo HTML in Javadoc is the dumbest idea ever.

See Also:
StringBuilderWriter

Constructor Summary
CharSequenceReader(CharSequence cs)
          Constructs a reader that reads from cs.
 
Method Summary
 void close()
          Does nothing.
 int length()
          Returns the current length of the underlying char sequence.
 void mark(int readAheadLimit)
          Marks the current position in the stream for later repositioning.
 boolean markSupported()
          Tells whether this stream supports the mark(int) operation.
 int read()
           
 int read(char[] cbuf)
           
 int read(char[] cbuf, int off, int len)
           
 int read(CharBuffer target)
           
 boolean ready()
          Tells whether this stream is ready to be read without blocking, which is always true.
 int remaining()
          Returns the current number of characters remaining to be read from the underlying char sequence.
 void reset()
          Repositions the stream to the previously marked position.
 void setImmutable(boolean immutable)
          Sets the immutable flag for the underlying sequence.
 long skip(long n)
           
 
Methods inherited from class Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

CharSequenceReader

public CharSequenceReader(CharSequence cs)
Constructs a reader that reads from cs. If cs happens to be a String, the sequence is automatically flagged immutable.

Method Detail

setImmutable

public void setImmutable(boolean immutable)
Sets the immutable flag for the underlying sequence.

The mark/reset mechanism is only supported for immutable sequences, such as String. Unless flagged immutable, rewinding is denied by the reader.

Use this, if you know the backing CharSequence is immutable or a mutable sequence will not change over the lifetime of this reader as per your application logic.

The flag is set to false upon the reader's construction, except if the sequence happens to be a String, in which case the flag is set to true, because strings are always immutable.

Use with care! Misuse of this feature may cause unpredictable behaviour!

Parameters:
immutable - Whether the reader should treat the backing sequence as immutable.

length

public int length()
Returns the current length of the underlying char sequence.


remaining

public int remaining()
Returns the current number of characters remaining to be read from the underlying char sequence. In the event that the underlying sequence shrinked in size below the current read position, the returned value is negative.

Returns:
Remaining number of characters.

markSupported

public boolean markSupported()
Tells whether this stream supports the mark(int) operation. This reader supports rewinding the stream if and only if the underlying CharSequence is immutable.

Overrides:
markSupported in class Reader
See Also:
setImmutable(boolean)

mark

public void mark(int readAheadLimit)
          throws IOException
Marks the current position in the stream for later repositioning. Invalidates any previously set marks. The readAheadLimit parameter is ignored.

Due to the specification of this method by Reader we are forced to use IOException to signal that the mark/reset mechanism is not available for this reader (where UnsupportedOperationException would be more appropriate). But we promise that we throw it only if this reader is not backed by an immutable sequence, i.e. markSupported() would return false.

Overrides:
mark in class Reader
Throws:
IOException - If mark/reset is not supported for the underlying sequence.
See Also:
reset(), setImmutable(boolean)

reset

public void reset()
           throws IOException
Repositions the stream to the previously marked position. The mark is not invalidated by this operation, so you can reset to the same position over and over again without calling mark() each time.

Due to the specification of this method by Reader we are forced to use IOException to signal that the mark/reset mechanism is not available for this reader (where UnsupportedOperationException would be more appropriate). But we promise that we throw it only if this reader is not backed by an immutable sequence, i.e. markSupported() would return false.

Overrides:
reset in class Reader
Throws:
IOException - If mark/reset is not supported for the underlying sequence.
See Also:
mark(int), setImmutable(boolean)

read

public int read()
Overrides:
read in class Reader

read

public int read(char[] cbuf,
                int off,
                int len)
         throws IndexOutOfBoundsException
Specified by:
read in class Reader
Throws:
IndexOutOfBoundsException

read

public int read(char[] cbuf)
Overrides:
read in class Reader

read

public int read(CharBuffer target)
Specified by:
read in interface Readable
Overrides:
read in class Reader

ready

public boolean ready()
Tells whether this stream is ready to be read without blocking, which is always true.

Overrides:
ready in class Reader
Returns:
true

skip

public long skip(long n)
          throws IllegalArgumentException
Overrides:
skip in class Reader
Throws:
IllegalArgumentException

close

public void close()
Does nothing.

Specified by:
close in interface Closeable
Specified by:
close in class Reader