1 23 package org.archive.io; 24 25 import java.io.IOException ; 26 import java.io.RandomAccessFile ; 27 import java.io.UnsupportedEncodingException ; 28 import java.util.logging.Level ; 29 import java.util.logging.Logger ; 30 31 import org.archive.util.DevUtils; 32 33 68 class ByteReplayCharSequence implements ReplayCharSequence { 69 70 protected static Logger logger = 71 Logger.getLogger(ByteReplayCharSequence.class.getName()); 72 73 78 private byte[] prefixBuffer; 79 80 86 protected int length; 87 88 94 private int absoluteLength = -1; 95 96 99 private byte[] wraparoundBuffer; 100 101 104 private int wrapOrigin; 105 106 109 private int wrapOffset; 110 111 115 private String backingFilename; 116 117 120 private RandomAccessFile raFile; 121 122 125 private int contentOffset; 126 127 131 private static final String DEFAULT_SINGLE_BYTE_ENCODING = 132 "ISO-8859-1"; 133 134 135 151 public ByteReplayCharSequence(byte[] buffer, long size, 152 long responseBodyStart, String backingFilename) 153 throws IOException { 154 155 this.length = (int)(size - responseBodyStart); 156 this.absoluteLength = (int)size; 157 this.prefixBuffer = buffer; 158 this.contentOffset = (int)responseBodyStart; 159 160 if (size > buffer.length) { 163 this.backingFilename = backingFilename; 164 this.raFile = new RandomAccessFile (backingFilename, "r"); 165 this.wraparoundBuffer = new byte[this.prefixBuffer.length]; 166 this.wrapOrigin = this.prefixBuffer.length; 167 this.wrapOffset = 0; 168 loadBuffer(); 169 } 170 } 171 172 176 public int length() { 177 return this.length; 178 } 179 180 192 public char charAt(int index) { 193 int c = -1; 194 index += this.contentOffset; 197 if (index < this.prefixBuffer.length) { 198 c = this.prefixBuffer[index]; 200 } else if (index >= this.wrapOrigin && 201 (index - this.wrapOrigin) < this.wraparoundBuffer.length) { 202 c = this.wraparoundBuffer[ 204 ((index - this.wrapOrigin) + this.wrapOffset) % 205 this.wraparoundBuffer.length]; 206 } else { 207 c = faultCharAt(index); 211 } 212 return (char)(c & 0xff); 215 } 216 217 234 private int faultCharAt(int index) { 235 if(Thread.interrupted()) { 236 throw new RuntimeException ("thread interrupted"); 237 } 238 if(index >= this.wrapOrigin + this.wraparoundBuffer.length) { 239 while (index >= this.wrapOrigin + this.wraparoundBuffer.length) 241 { 242 advanceBuffer(); 244 } 245 return charAt(index - this.contentOffset); 246 } 247 recenterBuffer(index); 249 return charAt(index - this.contentOffset); 250 } 251 252 258 private void recenterBuffer(int index) { 259 if (logger.isLoggable(Level.FINE)) { 260 logger.fine("Recentering around " + index + " in " + 261 this.backingFilename); 262 } 263 this.wrapOrigin = index - (this.wraparoundBuffer.length / 2); 264 if(this.wrapOrigin < this.prefixBuffer.length) { 265 this.wrapOrigin = this.prefixBuffer.length; 266 } 267 this.wrapOffset = 0; 268 loadBuffer(); 269 } 270 271 274 private void loadBuffer() 275 { 276 long len = -1; 277 try { 278 len = this.raFile.length(); 279 this.raFile.seek(this.wrapOrigin - this.prefixBuffer.length); 280 this.raFile.readFully(this.wraparoundBuffer, 0, 281 Math.min(this.wraparoundBuffer.length, 282 this.absoluteLength - this.wrapOrigin)); 283 } 284 285 catch (IOException e) { 286 DevUtils.logger.log ( 288 Level.SEVERE, 289 "raFile.seek(" + 290 (this.wrapOrigin - this.prefixBuffer.length) + 291 ")\n" + 292 "raFile.readFully(wraparoundBuffer,0," + 293 (Math.min(this.wraparoundBuffer.length, 294 this.length - this.wrapOrigin )) + 295 ")\n"+ 296 "raFile.length()" + len + "\n" + 297 DevUtils.extraInfo(), 298 e); 299 throw new RuntimeException (e); 300 } 301 } 302 303 306 private void advanceBuffer() { 307 try { 308 this.wraparoundBuffer[this.wrapOffset] = 309 (byte)this.raFile.read(); 310 this.wrapOffset++; 311 this.wrapOffset %= this.wraparoundBuffer.length; 312 this.wrapOrigin++; 313 } catch (IOException e) { 314 DevUtils.logger.log(Level.SEVERE, "advanceBuffer()" + 315 DevUtils.extraInfo(), e); 316 throw new RuntimeException (e); 317 } 318 } 319 320 public CharSequence subSequence(int start, int end) { 321 return new CharSubSequence(this, start, end); 322 } 323 324 329 public void close() throws IOException 330 { 331 this.prefixBuffer = null; 332 if (this.raFile != null) { 333 this.raFile.close(); 334 this.raFile = null; 335 } 336 } 337 338 341 protected void finalize() throws Throwable 342 { 343 super.finalize(); 344 close(); 345 } 346 347 350 public String substring(int offset, int len) { 351 StringBuffer ret = new StringBuffer (len); 352 offset += this.contentOffset; 355 if (offset < this.prefixBuffer.length) { 356 int from = offset; 358 int count = this.prefixBuffer.length - from; 360 if (offset + len < this.prefixBuffer.length) { 361 count = len; } else { 363 offset = this.prefixBuffer.length + 1; 365 len = len - count; 366 } 367 try { 375 ret.append(new String (this.prefixBuffer,from,count, 376 DEFAULT_SINGLE_BYTE_ENCODING)); 377 } 378 catch (UnsupportedEncodingException e) { 379 logger.severe("Failed encoding string: " + e.getMessage()); 380 } 381 } 382 if (offset >= this.prefixBuffer.length) { 383 int to = offset + len; 386 for(int i = offset ; i < to ; i++) { 387 ret.append(charAt(i - this.contentOffset)); 388 } 389 } 390 391 return ret.toString(); 392 } 393 394 public String toString() { 395 return substring(0, length()); 396 } 397 } | Popular Tags |