001 package com.thaiopensource.validate.auto;
002
003 import java.io.IOException;
004 import java.io.Reader;
005
006 /**
007 * Rewindable implementation over a reader.
008 * Modified from RewindableInputStream by replacing the input stream with a reader.
009 * @author george
010 *
011 */
012 public class RewindableReader extends Reader implements Rewindable {
013 static class Block {
014 Block next;
015 final char[] buf;
016 int used = 0;
017 static final int MIN_SIZE = 1024;
018 Block(int minSize) {
019 buf = new char[Math.max(MIN_SIZE, minSize)];
020 }
021
022 Block() {
023 this(0);
024 }
025
026 void append(char b) {
027 buf[used++] = b;
028 }
029
030 void append(char[] b, int off, int len) {
031 System.arraycopy(b, off, buf, used, len);
032 used += len;
033 }
034 }
035
036 private Block head;
037 /**
038 * If curBlockAvail > 0, then there are curBlockAvail chars available to be
039 * returned starting at curBlockPos in curBlock.buf.
040 */
041 private int curBlockAvail;
042 private Block curBlock;
043 private int curBlockPos;
044 private Block lastBlock;
045 /**
046 * true unless willNotRewind has been called
047 */
048 private boolean saving = true;
049 private final Reader in;
050 private boolean pretendClosed = false;
051 /**
052 * true if we have got an EOF from the underlying Reader
053 */
054 private boolean eof;
055
056 public RewindableReader(Reader in) {
057 if (in == null)
058 throw new NullPointerException();
059 this.in = in;
060 }
061
062 public void close() throws IOException {
063 if (saving) {
064 curBlockAvail = 0;
065 curBlock = null;
066 pretendClosed = true;
067 }
068 else {
069 head = null;
070 curBlock = null;
071 lastBlock = null;
072 saving = false;
073 curBlockAvail = 0;
074 in.close();
075 }
076 }
077
078 public void rewind() {
079 if (!saving)
080 throw new IllegalStateException("rewind() after willNotRewind()");
081 pretendClosed = false;
082 if (head == null)
083 return;
084 curBlock = head;
085 curBlockPos = 0;
086 curBlockAvail = curBlock.used;
087 }
088
089 public boolean canRewind() {
090 return saving;
091 }
092
093 public void willNotRewind() {
094 saving = false;
095 head = null;
096 lastBlock = null;
097 if (pretendClosed) {
098 pretendClosed = false;
099 try {
100 in.close();
101 }
102 catch (IOException e) { }
103 }
104 }
105
106 public int read() throws IOException {
107 if (curBlockAvail > 0) {
108 int c = curBlock.buf[curBlockPos++] & 0xFF;
109 --curBlockAvail;
110 if (curBlockAvail == 0) {
111 curBlock = curBlock.next;
112 if (curBlock != null) {
113 curBlockPos = 0;
114 curBlockAvail = curBlock.used;
115 }
116 }
117 return c;
118 }
119 int c = in.read();
120 if (saving && c != -1) {
121 if (lastBlock == null)
122 lastBlock = head = new Block();
123 else if (lastBlock.used == lastBlock.buf.length)
124 lastBlock = lastBlock.next = new Block();
125 lastBlock.append((char)c);
126 }
127 return c;
128 }
129
130 public int read(char b[], int off, int len) throws IOException {
131 if (curBlockAvail == 0 && !saving)
132 return in.read(b, off, len);
133 if (b == null)
134 throw new NullPointerException();
135 if (len < 0)
136 throw new IndexOutOfBoundsException();
137 int nRead = 0;
138 if (curBlockAvail != 0) {
139 for (;;) {
140 if (len == 0)
141 return nRead;
142 b[off++] = curBlock.buf[curBlockPos++];
143 --len;
144 nRead++;
145 --curBlockAvail;
146 if (curBlockAvail == 0) {
147 curBlock = curBlock.next;
148 if (curBlock == null)
149 break;
150 curBlockAvail = curBlock.used;
151 curBlockPos = 0;
152 }
153 }
154 }
155 if (len == 0)
156 return nRead;
157 if (eof)
158 return nRead > 0 ? nRead : -1;
159 try {
160 int n = in.read(b, off, len);
161 if (n < 0) {
162 eof = true;
163 return nRead > 0 ? nRead : -1;
164 }
165 nRead += n;
166 if (saving) {
167 if (lastBlock == null)
168 lastBlock = head = new Block(n);
169 else if (lastBlock.buf.length - lastBlock.used < n) {
170 if (lastBlock.used != lastBlock.buf.length) {
171 int free = lastBlock.buf.length - lastBlock.used;
172 lastBlock.append(b, off, free);
173 off += free;
174 n -= free;
175 }
176 lastBlock = lastBlock.next = new Block(n);
177 }
178 lastBlock.append(b, off, n);
179 }
180 }
181 catch (IOException e) {
182 eof = true;
183 if (nRead == 0)
184 throw e;
185 }
186 return nRead;
187 }
188 }