KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > source > save > SourceBuffer


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.java.source.save;
20
21 import java.io.*;
22 import java.util.BitSet JavaDoc;
23 import javax.tools.JavaFileObject;
24
25 /**
26  * A buffer for a source file with various navigation tools. The characters in the buffer are
27  * generally accessed using 0-based index into the array, but helper methods exist to go to and from
28  * row/column addresses.
29  */

30 public class SourceBuffer implements CharSequence JavaDoc {
31     public char[] src;
32     LineMap lineMap;
33     int maxLines;
34
35     /**
36      * Construct a new SourceBuffer for a given char array and logical length
37      * @param src Source char array
38      * @param length logical length of buffer which may be less than to equal to src.length
39      */

40     public SourceBuffer(char[] src, int length) {
41         this.src = src;
42     lineMap = new LineMap(src, src.length);
43         maxLines = lineMap.getLineNumber(src.length - 1);
44     }
45
46     /**
47      * Construct a new SourceBuffer from a file path.
48      * @param path the path of the file containing the source code.
49      */

50     public SourceBuffer (String JavaDoc path) throws IOException {
51     this(new FileReader(path));
52     }
53
54     /**
55      * Construct a new SourceBuffer from an input stream.
56      * @param input the InputStream to read.
57      */

58     public SourceBuffer (InputStream input) throws IOException {
59         this(new InputStreamReader(input));
60     }
61     
62     /**
63      * Construct a new SourceBuffer from an input stream and an encoding.
64      * @param input the InputStream to read.
65      * @param encoding the encoding to use when reading the InputStream.
66      */

67     public SourceBuffer (InputStream input, String JavaDoc encoding) throws IOException {
68         this(new InputStreamReader(input, encoding));
69     }
70     
71     /**
72      * Construct a new SourceBuffer from a Reader.
73      * @param input the Reader to use.
74      */

75     public SourceBuffer(Reader input) throws IOException {
76         src = readInFile(input);
77     lineMap = new LineMap(src, src.length);
78         maxLines = lineMap.getLineNumber(src.length - 1);
79     }
80     
81     /**
82      * Construct a new SourceBuffer from a JavaFileObject.
83      * @param sourceFile the file object to use.
84      */

85     public SourceBuffer(JavaFileObject sourceFile) throws IOException {
86         String JavaDoc s = sourceFile.getCharContent(true).toString();
87         src = s.toCharArray();
88     lineMap = new LineMap(src, src.length);
89         maxLines = lineMap.getLineNumber(src.length - 1);
90     }
91
92     private static char[] readInFile(Reader in) throws IOException {
93     CharArrayWriter out = new CharArrayWriter(4096);
94     char[] buf = new char[4096];
95     int n;
96     while ((n = in.read(buf)) > 0)
97         out.write(buf, 0, n);
98     in.close();
99         
100         // The javac Scanner requires an extra char in the array.
101
out.write(0);
102
103     return out.toCharArray();
104     }
105  
106     /**
107      * Returns a new character array that contains this buffer's characters.
108      *
109      * @return the requested char array
110      */

111     public char[] getChars() {
112     char[] buf = new char[src.length];
113     System.arraycopy(src, 0, buf, 0, src.length);
114     return buf;
115     }
116
117     /**
118      * Returns a new character array that is a subset of this buffer.
119      * The array starts with the character at the specified index and
120      * ends with the character at index <tt>end - 1</tt>. The length
121      * of the returned sequence is <tt>end - start</tt>, so if
122      * <tt>start == end</tt> then an empty sequence is returned.
123      *
124      * @param start the start index, inclusive
125      * @param end the end index, exclusive
126      * @return the requested char array
127      * @throws IndexOutOfBoundsException
128      * if <tt>start</tt> or <tt>end</tt> are negative,
129      * if <tt>end</tt> is greater than <tt>length()</tt>,
130      * or if <tt>start</tt> is greater than <tt>end</tt>
131      */

132     public char[] getChars(int start, int end) {
133     int len = end - start;
134     char[] buf = new char[len];
135     System.arraycopy(src, start, buf, 0, len);
136     return buf;
137     }
138     
139     /**
140      * Returns a new String that is a subset of this buffer.
141      * The String starts with the character at the specified index and
142      * ends with the character at index <tt>end - 1</tt>. The length
143      * of the returned String is <tt>end - start</tt>, so if
144      * <tt>start == end</tt> then an empty String is returned.
145      *
146      * @param start the start index, inclusive
147      * @param end the end index, exclusive
148      * @return the requested String
149      * @throws IndexOutOfBoundsException
150      * if <tt>start</tt> or <tt>end</tt> are negative,
151      * if <tt>end</tt> is greater than <tt>length()</tt>,
152      * or if <tt>start</tt> is greater than <tt>end</tt>
153      */

154     public String JavaDoc getString(int start, int end) {
155         return new String JavaDoc(src, start, end - start);
156     }
157
158     /**
159      * Returns a specified line as an array of chars, minus the EOL
160      * char(s).
161      *
162      * @param line the number of the line to be returned (1 is the first
163      * line number)
164      * @return the characters in that line, without the line ending
165      * @throws IndexOutOfBoundsException if line is less than one
166      * or greater than the number of lines in the buffer.
167      */

168     public char[] getLine(int line) {
169     if (line <= 0 || line > maxLines)
170         throw new IndexOutOfBoundsException JavaDoc("line out of range: " + line);
171         int start = lineMap.getStartPosition(line);
172         int end = ((line + 1 >= maxLines) ? src.length : lineMap.getStartPosition(line+1)) - 1;
173
174     // These two tests (in this order) strip all three supported
175
// line-endings: \r, \n, and \r\n.
176
if (src[end] == '\n')
177         --end;
178     if (src[end] == '\r')
179         --end;
180
181     return getChars(start, end + 1);
182     }
183     
184     /**
185      * Returns the number of the line in which a character position occurs; a line termination
186      * characater is on the line it terminates.
187      *
188      * @param pos character offset of the position
189      * @return the 0-based line number on which pos occurs
190      */

191     public int getLineNumber(int pos) {
192         return lineMap.getLineNumber(pos);
193     }
194
195     /**
196      * Return the character column for a given buffer index
197      *
198      * @param pos The 0-based index to find the column of
199      * @return the character column
200      */

201     public int getColumnNumber(int pos) {
202         return lineMap.getColumnNumber(pos);
203     }
204
205     /**
206      * Returns the offset for a given line, column pair
207      */

208     public int getPosition(int line, int column) {
209         return lineMap.getPosition(line, column);
210     }
211
212     /**
213      * Returns the offset of the first character in a line.
214      */

215     public int getStartPosition(int line) {
216         return lineMap.getStartPosition(line);
217     }
218     
219     /**
220      * Returns the number of chars in this array.
221      */

222     public int length() {
223         return src.length - 1; // subtract Scanner extra char
224
}
225
226     /**
227      * Returns the <code>char</code> value at the specified index. An index ranges from zero
228      * to <tt>length() - 1</tt>. The first <code>char</code> value of the sequence is at
229      * index zero, the next at index one, and so on, as for array
230      * indexing. </p>
231      *
232      * @param index the index of the <code>char</code> value to be returned
233      * @return the specified <code>char</code> value
234      * @throws IndexOutOfBoundsException
235      * if the <tt>index</tt> argument is negative or not less than
236      * <tt>length()</tt>
237      */

238     public char charAt(int index) {
239         return src[index];
240     }
241
242     /**
243      * Returns a new <code>CharSequence</code> that is a subsequence of this sequence.
244      * The subsequence starts with the <code>char</code> value at the specified index and
245      * ends with the <code>char</code> value at index <tt>end - 1</tt>.
246      *
247      * @param start the start index, inclusive
248      * @param end the end index, exclusive
249      * @return the specified subsequence
250      * @throws IndexOutOfBoundsException
251      * if <tt>start</tt> or <tt>end</tt> are negative,
252      * if <tt>end</tt> is greater than <tt>length()</tt>,
253      * or if <tt>start</tt> is greater than <tt>end</tt>
254      */

255     public CharSequence JavaDoc subSequence(int start, int end) {
256         return getString(start, end);
257     }
258
259     public String JavaDoc toString() {
260         return new String JavaDoc(src, 0, length());
261     }
262
263     /** A two-way map between line/column numbers and positions,
264      * derived from a scan done at creation time. Tab expansion is
265      * optionally supported via a character map. Text content
266      * is not retained.
267      *<p>
268      * Notes: The first character position FIRSTPOS is at
269      * (FIRSTLINE,FIRSTCOLUMN). No account is taken of Unicode escapes.
270      */

271     static class LineMap {
272     private int[] startPosition; // start position of each line
273
private BitSet JavaDoc tabMap; // bits set for tab positions.
274

275         public static final int FIRSTLINE = 1;
276         public static final int FIRSTCOLUMN = 1;
277         public final static int TabInc = 8;
278         private static final char SCANNER_SENTINEL = '\u0000'; // zero char added by javac scanner
279

280     protected LineMap(char[] src, int max) {
281             if (src[max - 1] == SCANNER_SENTINEL)
282                 max--;
283             tabMap = new BitSet JavaDoc(max);
284         int c = 0;
285         int i = 0;
286         int[] linebuf = new int[max];
287         while (i < max) {
288         linebuf[c++] = i;
289         do {
290             char ch = src[i];
291             if (ch == '\r' || ch == '\n') {
292             if (ch == '\r' && (i+1) < max && src[i+1] == '\n')
293                 i += 2;
294             else
295                 ++i;
296             break;
297             }
298             else if (ch == '\t')
299             setTabPosition(i);
300         } while (++i < max);
301         }
302         this.startPosition = new int[c];
303         System.arraycopy(linebuf, 0, startPosition, 0, c);
304     }
305         
306     public int getStartPosition(int line) {
307         return startPosition[line - FIRSTLINE];
308     }
309
310     public int getPosition(int line, int column) {
311         int pos = startPosition[line - FIRSTLINE];
312         column -= FIRSTCOLUMN;
313         int col = 0;
314         while (col < column) {
315         pos++;
316         if (tabMap.get(pos))
317             col = (col / TabInc * TabInc) + TabInc;
318         else
319             col++;
320         }
321         return pos;
322     }
323
324     // Cache of last line number lookup
325
private int lastPosition = 0;
326     private int lastLine = FIRSTLINE;
327
328     public int getLineNumber(int pos) {
329         if (pos == lastPosition) {
330         return lastLine;
331         }
332         lastPosition = pos;
333
334         int low = 0;
335         int high = startPosition.length-1;
336         while (low <= high) {
337         int mid = (low + high) >> 1;
338         int midVal = startPosition[mid];
339
340         if (midVal < pos)
341             low = mid + 1;
342         else if (midVal > pos)
343             high = mid - 1;
344         else {
345             lastLine = mid + 1; // pos is at beginning of this line
346
return lastLine;
347         }
348         }
349         lastLine = low;
350         return lastLine; // pos is on this line
351
}
352
353     public int getColumnNumber(int pos) {
354         int lineStart = startPosition[getLineNumber(pos) - FIRSTLINE];
355         int column = 0;
356         for (int bp = lineStart; bp < pos; bp++) {
357         if (tabMap.get(bp))
358             column = (column / TabInc * TabInc) + TabInc;
359         else
360             column++;
361         }
362         return column + FIRSTCOLUMN;
363     }
364
365     protected void setTabPosition(int offset) {
366         tabMap.set(offset);
367     }
368     }
369 }
370
Popular Tags