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 }