Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
I initially set out to design a data-reading character-stream filter class. Analogous to the byte-stream filter
DataInputStream, this character-stream filter was intended to provide the capability to read textual data from a character stream (namely
the output of a human or the
PrintWriter println() method).
Let me now, in retrospect, describe what I actually implemented.
First, I created an
UndoReader class. This character-stream filter supports three special methods:
As you read characters through the stream, you have the option to checkpoint the stream -- that is, save the stream's current state and put it into a mode such that it stores all data subsequently read
through it. From that point on, the
UndoReader stores all the data you read. After any amount of reading, choosing to commit the stream will cause the stored data to be discarded, after which reading proceeds undisturbed. Alternatively, choosing
to rollback the stream will cause it to rewind and revert to reading from the position at which you asserted the checkpoint -- just as
if you hadn't yet read anything. This stream also supports a couple of related methods.
Next, I implemented the
DataReader class, the character-stream filter. This class makes use of the
UndoReader class and provides methods to read all the primitive Java types (
readBoolean(), and so on). What is special about this class is that if you attempt to read a primitive from the stream and it turns out
the stream data is incorrect -- if, for example, you attempt to read a
Boolean and the next token in the stream is truthfulness -- it rolls back, un-reading any characters read during the erroneous operation, and throws an exception. The stream also supports a feature whereby it
can read data one line at a time, signaling each time the end of a line is reached (among other wonders).
The classes I developed will only work in JDK 1.1-plus. Adapting them to work as
InputStreams, usable under JDK 1.0.2, should be quite easy, however.
In the interest of brevity, I'll spare you an introduction to character streams. Todd Sundsted's November 1997 How-To Java column should serve that purpose quite adequately; if you want an introduction to byte streams, check out Todd's October 1997 column. For further details of the Java stream classes, I refer you to Java Network Programming, Second Edition, which I coauthored with Michael Shoffner and Derek Hamner, and which is due out any day now. (See Resources.)
What I should perhaps explain is my justification for the
UndoReader class. At the most abstract level, I want the ability to undo a series of read operations, because otherwise my
DataReader class will violate the basic law of propriety: It would be improper for an erroneous attempt at reading an
int to end up consuming a
Boolean. Furthermore, the behavior of my class wouldn't necessarily be clearly defined in the presence of such an error: the amount
of erroneous input consumed would be implementation-dependent, and exposing implementation-dependent details of this nature
simply invites abuse.
Those readers familiar with the stream classes may then ask about the
reset() methods, or indeed the
PushbackReader class: Do these basic features of the stream API not already address my needs? Indeed, use of the
reset() methods does allow a sequence of read operations to be undone, and the
unread() methods can be used to the same effect. However, both of these options are bounded. Therefore you must, in each case, declare ahead of time the maximum volume of data you will un-read. In our situation, no
such limit exists for textual data: "00...01" is a valid integer, just as "00...0z" is not. I cannot, simply to avoid writing
an extra class, presume to impose arbitrary limitations on the data I will process.
Thus rationalized, we can now proceed with the code.
UndoReader class is a character-stream filter that provides unbounded checkpoint, commit, and rollback operations and an additional undo facility.
Figure 1. Using methods checkpoint(), commit(), rollback(), and undo()
checkpoint()is used, it proceeds to store all data read through it in an internal, expanding buffer
commit()is used, the stored contents of the checkpoint buffer are discarded and further reads are no longer stored
rollback()is used, reading reverts to data stored in the internal buffer; when this is used up, reading proceeds as normal
We'll start by looking at our class definition: