1 11 package org.eclipse.search.internal.core.text; 12 13 import java.io.IOException ; 14 import java.io.InputStream ; 15 import java.io.InputStreamReader ; 16 import java.io.Reader ; 17 import java.util.Arrays ; 18 19 import org.eclipse.core.runtime.CoreException; 20 import org.eclipse.core.runtime.content.IContentDescription; 21 22 import org.eclipse.core.resources.IFile; 23 24 27 public class FileCharSequenceProvider { 28 29 private static int NUMBER_OF_BUFFERS= 3; 30 public static int BUFFER_SIZE= 2 << 18; 32 private FileCharSequence fReused= null; 33 34 public CharSequence newCharSequence(IFile file) throws CoreException, IOException { 35 if (fReused == null) { 36 return new FileCharSequence(file); 37 } 38 FileCharSequence curr= fReused; 39 fReused= null; 40 curr.reset(file); 41 return curr; 42 } 43 44 public void releaseCharSequence(CharSequence seq) throws CoreException, IOException { 45 if (seq instanceof FileCharSequence) { 46 FileCharSequence curr= (FileCharSequence) seq; 47 try { 48 curr.close(); 49 } finally { 50 if (fReused == null) { 51 fReused= curr; 52 } 53 } 54 } 55 } 56 57 public static class FileCharSequenceException extends RuntimeException { 58 private static final long serialVersionUID= 1L; 59 60 FileCharSequenceException(IOException e) { 61 super(e); 62 } 63 64 FileCharSequenceException(CoreException e) { 65 super(e); 66 } 67 68 public void throwWrappedException() throws CoreException, IOException { 69 Throwable wrapped= getCause(); 70 if (wrapped instanceof CoreException) { 71 throw (CoreException) wrapped; 72 } else if (wrapped instanceof IOException ) { 73 throw (IOException ) wrapped; 74 } 75 } 77 } 78 79 80 private static final class CharSubSequence implements CharSequence { 81 82 private final int fSequenceOffset; 83 private final int fSequenceLength; 84 private final FileCharSequence fParent; 85 86 public CharSubSequence(FileCharSequence parent, int offset, int length) { 87 fParent= parent; 88 fSequenceOffset= offset; 89 fSequenceLength= length; 90 } 91 92 95 public int length() { 96 return fSequenceLength; 97 } 98 99 102 public char charAt(int index) { 103 if (index < 0) { 104 throw new IndexOutOfBoundsException ("index must be larger than 0"); } 106 if (index >= fSequenceLength) { 107 throw new IndexOutOfBoundsException ("index must be smaller than length"); } 109 return fParent.charAt(fSequenceOffset + index); 110 } 111 112 115 public CharSequence subSequence(int start, int end) { 116 if (end < start) { 117 throw new IndexOutOfBoundsException ("end cannot be smaller than start"); } 119 if (start < 0) { 120 throw new IndexOutOfBoundsException ("start must be larger than 0"); } 122 if (end > fSequenceLength) { 123 throw new IndexOutOfBoundsException ("end must be smaller or equal than length"); } 125 return fParent.subSequence(fSequenceOffset + start, fSequenceOffset + end); 126 } 127 128 131 public String toString() { 132 try { 133 return fParent.getSubstring(fSequenceOffset, fSequenceLength); 134 } catch (IOException e) { 135 throw new FileCharSequenceException(e); 136 } catch (CoreException e) { 137 throw new FileCharSequenceException(e); 138 } 139 } 140 } 141 142 143 private static final class Buffer { 144 private final char[] fBuf; 145 private int fOffset; 146 private int fLength; 147 148 private Buffer fNext; 149 private Buffer fPrevious; 150 151 public Buffer() { 152 fBuf= new char[BUFFER_SIZE]; 153 reset(); 154 fNext= this; 155 fPrevious= this; 156 } 157 158 public boolean contains(int pos) { 159 int offset= fOffset; 160 return offset <= pos && pos < offset + fLength; 161 } 162 163 170 public boolean fill(Reader reader, int pos) throws IOException { 171 int res= reader.read(fBuf); 172 if (res == -1) { 173 fOffset= pos; 174 fLength= 0; 175 return true; 176 } 177 178 int charsRead= res; 179 while (charsRead < BUFFER_SIZE) { 180 res= reader.read(fBuf, charsRead, BUFFER_SIZE - charsRead); 181 if (res == -1) { 182 fOffset= pos; 183 fLength= charsRead; 184 return true; 185 } 186 charsRead+= res; 187 } 188 fOffset= pos; 189 fLength= BUFFER_SIZE; 190 return false; 191 } 192 193 public char get(int pos) { 194 return fBuf[pos - fOffset]; 195 } 196 197 public StringBuffer append(StringBuffer buf, int start, int length) { 198 return buf.append(fBuf, start - fOffset, length); 199 } 200 201 public StringBuffer appendAll(StringBuffer buf) { 202 return buf.append(fBuf, 0, fLength); 203 } 204 205 public int getEndOffset() { 206 return fOffset + fLength; 207 } 208 209 public void removeFromChain() { 210 fPrevious.fNext= fNext; 211 fNext.fPrevious= fPrevious; 212 213 fNext= this; 214 fPrevious= this; 215 } 216 217 public void insertBefore(Buffer other) { 218 fNext= other; 219 fPrevious= other.fPrevious; 220 fPrevious.fNext= this; 221 other.fPrevious= this; 222 } 223 224 public Buffer getNext() { 225 return fNext; 226 } 227 228 public Buffer getPrevious() { 229 return fPrevious; 230 } 231 232 public void reset() { 233 fOffset= -1; 234 fLength= 0; 235 } 236 } 237 238 private final class FileCharSequence implements CharSequence { 239 240 private static final String CHARSET_UTF_8= "UTF-8"; 242 private Reader fReader; 243 private int fReaderPos; 244 245 private Integer fLength; 246 247 private Buffer fMostCurrentBuffer; private int fNumberOfBuffers; 249 250 private IFile fFile; 251 252 public FileCharSequence(IFile file) throws CoreException, IOException { 253 fNumberOfBuffers= 0; 254 reset(file); 255 } 256 257 public void reset(IFile file) throws CoreException, IOException { 258 fFile= file; 259 fLength= null; 261 Buffer curr= fMostCurrentBuffer; 262 if (curr != null) { 263 do { 264 curr.reset(); 265 curr= curr.getNext(); 266 } while (curr != fMostCurrentBuffer); 267 } 268 initializeReader(); 269 } 270 271 private void initializeReader() throws CoreException, IOException { 272 if (fReader != null) { 273 fReader.close(); 274 } 275 String charset= fFile.getCharset(); 276 fReader= new InputStreamReader (getInputStream(charset), charset); 277 fReaderPos= 0; 278 } 279 280 private InputStream getInputStream(String charset) throws CoreException, IOException { 281 boolean ok= false; 282 InputStream contents= fFile.getContents(); 283 try { 284 if (CHARSET_UTF_8.equals(charset)) { 285 290 IContentDescription description= fFile.getContentDescription(); 291 if ((description != null) && (description.getProperty(IContentDescription.BYTE_ORDER_MARK) != null)) { 292 int bomLength= IContentDescription.BOM_UTF_8.length; 293 byte[] bomStore= new byte[bomLength]; 294 int bytesRead= 0; 295 do { 296 int bytes= contents.read(bomStore, bytesRead, bomLength - bytesRead); 297 if (bytes == -1) 298 throw new IOException (); 299 bytesRead += bytes; 300 } while (bytesRead < bomLength); 301 302 if (!Arrays.equals(bomStore, IContentDescription.BOM_UTF_8)) { 303 contents.close(); 305 contents= fFile.getContents(); 306 } 307 } 308 } 309 ok= true; 310 } finally { 311 if (!ok && contents != null) 312 try { 313 contents.close(); 314 } catch (IOException ex) { 315 } 317 } 318 return contents; 319 } 320 321 private void clearReader() throws IOException { 322 if (fReader != null) { 323 fReader.close(); 324 } 325 fReader= null; 326 fReaderPos= Integer.MAX_VALUE; 327 } 328 329 332 public int length() { 333 if (fLength == null) { 334 try { 335 getBuffer(Integer.MAX_VALUE); 336 } catch (IOException e) { 337 throw new FileCharSequenceException(e); 338 } catch (CoreException e) { 339 throw new FileCharSequenceException(e); 340 } 341 } 342 return fLength.intValue(); 343 } 344 345 private Buffer getBuffer(int pos) throws IOException , CoreException { 346 Buffer curr= fMostCurrentBuffer; 347 if (curr != null) { 348 do { 349 if (curr.contains(pos)) { 350 return curr; 351 } 352 curr= curr.getNext(); 353 } while (curr != fMostCurrentBuffer); 354 } 355 356 Buffer buf= findBufferToUse(); 357 fillBuffer(buf, pos); 358 if (buf.contains(pos)) { 359 return buf; 360 } 361 return null; 362 } 363 364 private Buffer findBufferToUse() { 365 if (fNumberOfBuffers < NUMBER_OF_BUFFERS) { 366 fNumberOfBuffers++; 367 Buffer newBuffer= new Buffer(); 368 if (fMostCurrentBuffer == null) { 369 fMostCurrentBuffer= newBuffer; 370 return newBuffer; 371 } 372 newBuffer.insertBefore(fMostCurrentBuffer); return newBuffer; 374 } 375 return fMostCurrentBuffer.getPrevious(); 376 } 377 378 private boolean fillBuffer(Buffer buffer, int pos) throws CoreException, IOException { 379 if (fReaderPos > pos) { 380 initializeReader(); 381 } 382 383 do { 384 boolean endReached= buffer.fill(fReader, fReaderPos); 385 fReaderPos= buffer.getEndOffset(); 386 if (endReached) { 387 fLength= new Integer (fReaderPos); fReaderPos= Integer.MAX_VALUE; return true; 390 } 391 } while (fReaderPos <= pos); 392 393 return true; 394 } 395 396 399 public char charAt(final int index) { 400 final Buffer current= fMostCurrentBuffer; 401 if (current != null && current.contains(index)) { 402 return current.get(index); 403 } 404 405 if (index < 0) { 406 throw new IndexOutOfBoundsException ("index must be larger than 0"); } 408 if (fLength != null && index >= fLength.intValue()) { 409 throw new IndexOutOfBoundsException ("index must be smaller than length"); } 411 412 try { 413 final Buffer buffer= getBuffer(index); 414 if (buffer == null) { 415 throw new IndexOutOfBoundsException ("index must be smaller than length"); } 417 if (buffer != fMostCurrentBuffer) { 418 if (buffer.getNext() != fMostCurrentBuffer) { buffer.removeFromChain(); 421 buffer.insertBefore(fMostCurrentBuffer); 422 } 423 fMostCurrentBuffer= buffer; 424 } 425 return buffer.get(index); 426 } catch (IOException e) { 427 throw new FileCharSequenceException(e); 428 } catch (CoreException e) { 429 throw new FileCharSequenceException(e); 430 } 431 } 432 433 public String getSubstring(int start, int length) throws IOException , CoreException { 434 int pos= start; 435 int endPos= start + length; 436 437 if (fLength != null && endPos > fLength.intValue()) { 438 throw new IndexOutOfBoundsException ("end must be smaller than length"); } 440 441 StringBuffer res= new StringBuffer (length); 442 443 Buffer buffer= getBuffer(pos); 444 while (pos < endPos && buffer != null) { 445 int bufEnd= buffer.getEndOffset(); 446 if (bufEnd >= endPos) { 447 return buffer.append(res, pos, endPos - pos).toString(); 448 } 449 buffer.append(res, pos, bufEnd - pos); 450 pos= bufEnd; 451 buffer= getBuffer(pos); 452 } 453 return res.toString(); 454 } 455 456 457 460 public CharSequence subSequence(int start, int end) { 461 if (end < start) { 462 throw new IndexOutOfBoundsException ("end cannot be smaller than start"); } 464 if (start < 0) { 465 throw new IndexOutOfBoundsException ("start must be larger than 0"); } 467 if (fLength != null && end > fLength.intValue()) { 468 throw new IndexOutOfBoundsException ("end must be smaller than length"); } 470 return new CharSubSequence(this, start, end - start); 471 } 472 473 public void close() throws IOException { 474 clearReader(); 475 } 476 477 480 public String toString() { 481 int len= fLength != null ? fLength.intValue() : 4000; 482 StringBuffer res= new StringBuffer (len); 483 try { 484 Buffer buffer= getBuffer(0); 485 while (buffer != null) { 486 buffer.appendAll(res); 487 buffer= getBuffer(res.length()); 488 } 489 return res.toString(); 490 } catch (IOException e) { 491 throw new FileCharSequenceException(e); 492 } catch (CoreException e) { 493 throw new FileCharSequenceException(e); 494 } 495 } 496 } 497 498 } 499 | Popular Tags |