1 package com.thaiopensource.validate.auto; 2 3 import java.io.InputStream ; 4 import java.io.IOException ; 5 6 public class RewindableInputStream extends InputStream implements Rewindable { 7 static class Block { 8 Block next; 9 final byte[] buf; 10 int used = 0; 11 static final int MIN_SIZE = 1024; 12 Block(int minSize) { 13 buf = new byte[Math.max(MIN_SIZE, minSize)]; 14 } 15 16 Block() { 17 this(0); 18 } 19 20 void append(byte b) { 21 buf[used++] = b; 22 } 23 24 void append(byte[] b, int off, int len) { 25 System.arraycopy(b, off, buf, used, len); 26 used += len; 27 } 28 } 29 30 private Block head; 31 35 private int curBlockAvail; 36 private Block curBlock; 37 private int curBlockPos; 38 private Block lastBlock; 39 42 private boolean saving = true; 43 private final InputStream in; 44 private boolean pretendClosed = false; 45 48 private boolean eof; 49 50 public RewindableInputStream(InputStream in) { 51 if (in == null) 52 throw new NullPointerException (); 53 this.in = in; 54 } 55 56 public void close() throws IOException { 57 if (saving) { 58 curBlockAvail = 0; 59 curBlock = null; 60 pretendClosed = true; 61 } 62 else { 63 head = null; 64 curBlock = null; 65 lastBlock = null; 66 saving = false; 67 curBlockAvail = 0; 68 in.close(); 69 } 70 } 71 72 public void rewind() { 73 if (!saving) 74 throw new IllegalStateException ("rewind() after willNotRewind()"); 75 pretendClosed = false; 76 if (head == null) 77 return; 78 curBlock = head; 79 curBlockPos = 0; 80 curBlockAvail = curBlock.used; 81 } 82 83 public boolean canRewind() { 84 return saving; 85 } 86 87 public void willNotRewind() { 88 saving = false; 89 head = null; 90 lastBlock = null; 91 if (pretendClosed) { 92 pretendClosed = false; 93 try { 94 in.close(); 95 } 96 catch (IOException e) { } 97 } 98 } 99 100 public int read() throws IOException { 101 if (curBlockAvail > 0) { 102 int c = curBlock.buf[curBlockPos++] & 0xFF; 103 --curBlockAvail; 104 if (curBlockAvail == 0) { 105 curBlock = curBlock.next; 106 if (curBlock != null) { 107 curBlockPos = 0; 108 curBlockAvail = curBlock.used; 109 } 110 } 111 return c; 112 } 113 int c = in.read(); 114 if (saving && c != -1) { 115 if (lastBlock == null) 116 lastBlock = head = new Block(); 117 else if (lastBlock.used == lastBlock.buf.length) 118 lastBlock = lastBlock.next = new Block(); 119 lastBlock.append((byte)c); 120 } 121 return c; 122 } 123 124 public int read(byte b[], int off, int len) throws IOException { 125 if (curBlockAvail == 0 && !saving) 126 return in.read(b, off, len); 127 if (b == null) 128 throw new NullPointerException (); 129 if (len < 0) 130 throw new IndexOutOfBoundsException (); 131 int nRead = 0; 132 if (curBlockAvail != 0) { 133 for (;;) { 134 if (len == 0) 135 return nRead; 136 b[off++] = curBlock.buf[curBlockPos++]; 137 --len; 138 nRead++; 139 --curBlockAvail; 140 if (curBlockAvail == 0) { 141 curBlock = curBlock.next; 142 if (curBlock == null) 143 break; 144 curBlockAvail = curBlock.used; 145 curBlockPos = 0; 146 } 147 } 148 } 149 if (len == 0) 150 return nRead; 151 if (eof) 152 return nRead > 0 ? nRead : -1; 153 try { 154 int n = in.read(b, off, len); 155 if (n < 0) { 156 eof = true; 157 return nRead > 0 ? nRead : -1; 158 } 159 nRead += n; 160 if (saving) { 161 if (lastBlock == null) 162 lastBlock = head = new Block(n); 163 else if (lastBlock.buf.length - lastBlock.used < n) { 164 if (lastBlock.used != lastBlock.buf.length) { 165 int free = lastBlock.buf.length - lastBlock.used; 166 lastBlock.append(b, off, free); 167 off += free; 168 n -= free; 169 } 170 lastBlock = lastBlock.next = new Block(n); 171 } 172 lastBlock.append(b, off, n); 173 } 174 } 175 catch (IOException e) { 176 eof = true; 177 if (nRead == 0) 178 throw e; 179 } 180 return nRead; 181 } 182 183 public int available() throws IOException { 184 if (curBlockAvail == 0) 185 return in.available(); 186 int n = curBlockAvail; 187 for (Block b = curBlock.next; b != null; b = b.next) 188 n += b.used; 189 return n + in.available(); 190 } 191 192 } 193 | Popular Tags |