KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > search > internal > core > text > FileCharSequenceProvider


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.search.internal.core.text;
12
13 import java.io.IOException JavaDoc;
14 import java.io.InputStream JavaDoc;
15 import java.io.InputStreamReader JavaDoc;
16 import java.io.Reader JavaDoc;
17 import java.util.Arrays JavaDoc;
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 /**
25  *
26  */

27 public class FileCharSequenceProvider {
28     
29     private static int NUMBER_OF_BUFFERS= 3;
30     public static int BUFFER_SIZE= 2 << 18; // public for testing
31

32     private FileCharSequence fReused= null;
33     
34     public CharSequence JavaDoc newCharSequence(IFile file) throws CoreException, IOException JavaDoc {
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 JavaDoc seq) throws CoreException, IOException JavaDoc {
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 JavaDoc {
58         private static final long serialVersionUID= 1L;
59
60         /* package */ FileCharSequenceException(IOException JavaDoc e) {
61             super(e);
62         }
63         
64         /* package */ FileCharSequenceException(CoreException e) {
65             super(e);
66         }
67         
68         public void throwWrappedException() throws CoreException, IOException JavaDoc {
69             Throwable JavaDoc wrapped= getCause();
70             if (wrapped instanceof CoreException) {
71                 throw (CoreException) wrapped;
72             } else if (wrapped instanceof IOException JavaDoc) {
73                 throw (IOException JavaDoc) wrapped;
74             }
75             // not possible
76
}
77     }
78     
79     
80     private static final class CharSubSequence implements CharSequence JavaDoc {
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         /* (non-Javadoc)
93          * @see java.lang.CharSequence#length()
94          */

95         public int length() {
96             return fSequenceLength;
97         }
98
99         /* (non-Javadoc)
100          * @see java.lang.CharSequence#charAt(int)
101          */

102         public char charAt(int index) {
103             if (index < 0) {
104                 throw new IndexOutOfBoundsException JavaDoc("index must be larger than 0"); //$NON-NLS-1$
105
}
106             if (index >= fSequenceLength) {
107                 throw new IndexOutOfBoundsException JavaDoc("index must be smaller than length"); //$NON-NLS-1$
108
}
109             return fParent.charAt(fSequenceOffset + index);
110         }
111
112         /* (non-Javadoc)
113          * @see java.lang.CharSequence#subSequence(int, int)
114          */

115         public CharSequence JavaDoc subSequence(int start, int end) {
116             if (end < start) {
117                 throw new IndexOutOfBoundsException JavaDoc("end cannot be smaller than start"); //$NON-NLS-1$
118
}
119             if (start < 0) {
120                 throw new IndexOutOfBoundsException JavaDoc("start must be larger than 0"); //$NON-NLS-1$
121
}
122             if (end > fSequenceLength) {
123                 throw new IndexOutOfBoundsException JavaDoc("end must be smaller or equal than length"); //$NON-NLS-1$
124
}
125             return fParent.subSequence(fSequenceOffset + start, fSequenceOffset + end);
126         }
127         
128         /* (non-Javadoc)
129          * @see java.lang.Object#toString()
130          */

131         public String JavaDoc toString() {
132             try {
133                 return fParent.getSubstring(fSequenceOffset, fSequenceLength);
134             } catch (IOException JavaDoc 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         /**
164          * Fills the buffer by reading from the given reader.
165          * @param reader the reader to read from
166          * @param pos the offset of the reader in the file
167          * @return returns true if the end of the file has been reached
168          * @throws IOException
169          */

170         public boolean fill(Reader JavaDoc reader, int pos) throws IOException JavaDoc {
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 JavaDoc append(StringBuffer JavaDoc buf, int start, int length) {
198             return buf.append(fBuf, start - fOffset, length);
199         }
200         
201         public StringBuffer JavaDoc appendAll(StringBuffer JavaDoc 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 JavaDoc {
239         
240         private static final String JavaDoc CHARSET_UTF_8= "UTF-8"; //$NON-NLS-1$
241

242         private Reader JavaDoc fReader;
243         private int fReaderPos;
244         
245         private Integer JavaDoc fLength;
246     
247         private Buffer fMostCurrentBuffer; // access to the buffer chain
248
private int fNumberOfBuffers;
249         
250         private IFile fFile;
251             
252         public FileCharSequence(IFile file) throws CoreException, IOException JavaDoc {
253             fNumberOfBuffers= 0;
254             reset(file);
255         }
256         
257         public void reset(IFile file) throws CoreException, IOException JavaDoc {
258             fFile= file;
259             fLength= null; // only calculated on demand
260

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 JavaDoc {
272             if (fReader != null) {
273                 fReader.close();
274             }
275             String JavaDoc charset= fFile.getCharset();
276             fReader= new InputStreamReader JavaDoc(getInputStream(charset), charset);
277             fReaderPos= 0;
278         }
279         
280         private InputStream JavaDoc getInputStream(String JavaDoc charset) throws CoreException, IOException JavaDoc {
281             boolean ok= false;
282             InputStream JavaDoc contents= fFile.getContents();
283             try {
284                 if (CHARSET_UTF_8.equals(charset)) {
285                     /*
286                      * This is a workaround for a corresponding bug in Java readers and writer,
287                      * see: http://developer.java.sun.com/developer/bugParade/bugs/4508058.html
288                      * we remove the BOM before passing the stream to the reader
289                      */

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 JavaDoc();
299                             bytesRead += bytes;
300                         } while (bytesRead < bomLength);
301                         
302                         if (!Arrays.equals(bomStore, IContentDescription.BOM_UTF_8)) {
303                             // discard file reader, we were wrong, no BOM -> new stream
304
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 JavaDoc ex) {
315                         // ignore
316
}
317             }
318             return contents;
319         }
320         
321         private void clearReader() throws IOException JavaDoc {
322             if (fReader != null) {
323                 fReader.close();
324             }
325             fReader= null;
326             fReaderPos= Integer.MAX_VALUE;
327         }
328         
329         /* (non-Javadoc)
330          * @see java.lang.CharSequence#length()
331          */

332         public int length() {
333             if (fLength == null) {
334                 try {
335                     getBuffer(Integer.MAX_VALUE);
336                 } catch (IOException JavaDoc 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 JavaDoc, 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); // insert before first
373
return newBuffer;
374             }
375             return fMostCurrentBuffer.getPrevious();
376         }
377     
378         private boolean fillBuffer(Buffer buffer, int pos) throws CoreException, IOException JavaDoc {
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 JavaDoc(fReaderPos); // at least we know the size of the file now
388
fReaderPos= Integer.MAX_VALUE; // will have to reset next time
389
return true;
390                 }
391             } while (fReaderPos <= pos);
392     
393             return true;
394         }
395     
396         /* (non-Javadoc)
397          * @see java.lang.CharSequence#charAt(int)
398          */

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 JavaDoc("index must be larger than 0"); //$NON-NLS-1$
407
}
408             if (fLength != null && index >= fLength.intValue()) {
409                 throw new IndexOutOfBoundsException JavaDoc("index must be smaller than length"); //$NON-NLS-1$
410
}
411             
412             try {
413                 final Buffer buffer= getBuffer(index);
414                 if (buffer == null) {
415                     throw new IndexOutOfBoundsException JavaDoc("index must be smaller than length"); //$NON-NLS-1$
416
}
417                 if (buffer != fMostCurrentBuffer) {
418                     // move to first
419
if (buffer.getNext() != fMostCurrentBuffer) { // already before the current?
420
buffer.removeFromChain();
421                         buffer.insertBefore(fMostCurrentBuffer);
422                     }
423                     fMostCurrentBuffer= buffer;
424                 }
425                 return buffer.get(index);
426             } catch (IOException JavaDoc e) {
427                 throw new FileCharSequenceException(e);
428             } catch (CoreException e) {
429                 throw new FileCharSequenceException(e);
430             }
431         }
432         
433         public String JavaDoc getSubstring(int start, int length) throws IOException JavaDoc, CoreException {
434             int pos= start;
435             int endPos= start + length;
436             
437             if (fLength != null && endPos > fLength.intValue()) {
438                 throw new IndexOutOfBoundsException JavaDoc("end must be smaller than length"); //$NON-NLS-1$
439
}
440             
441             StringBuffer JavaDoc res= new StringBuffer JavaDoc(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         /* (non-Javadoc)
458          * @see java.lang.CharSequence#subSequence(int, int)
459          */

460         public CharSequence JavaDoc subSequence(int start, int end) {
461             if (end < start) {
462                 throw new IndexOutOfBoundsException JavaDoc("end cannot be smaller than start"); //$NON-NLS-1$
463
}
464             if (start < 0) {
465                 throw new IndexOutOfBoundsException JavaDoc("start must be larger than 0"); //$NON-NLS-1$
466
}
467             if (fLength != null && end > fLength.intValue()) {
468                 throw new IndexOutOfBoundsException JavaDoc("end must be smaller than length"); //$NON-NLS-1$
469
}
470             return new CharSubSequence(this, start, end - start);
471         }
472         
473         public void close() throws IOException JavaDoc {
474             clearReader();
475         }
476         
477         /* (non-Javadoc)
478          * @see java.lang.Object#toString()
479          */

480         public String JavaDoc toString() {
481             int len= fLength != null ? fLength.intValue() : 4000;
482             StringBuffer JavaDoc res= new StringBuffer JavaDoc(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 JavaDoc e) {
491                 throw new FileCharSequenceException(e);
492             } catch (CoreException e) {
493                 throw new FileCharSequenceException(e);
494             }
495         }
496     }
497
498 }
499
Popular Tags