KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > js > rhino > LineBuffer


1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * The contents of this file are subject to the Netscape Public
4  * License Version 1.1 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of
6  * the License at http://www.mozilla.org/NPL/
7  *
8  * Software distributed under the License is distributed on an "AS
9  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10  * implied. See the License for the specific language governing
11  * rights and limitations under the License.
12  *
13  * The Original Code is Rhino code, released
14  * May 6, 1999.
15  *
16  * The Initial Developer of the Original Code is Netscape
17  * Communications Corporation. Portions created by Netscape are
18  * Copyright (C) 1997-1999 Netscape Communications Corporation. All
19  * Rights Reserved.
20  *
21  * Contributor(s):
22  * Mike McCabe
23  *
24  * Alternatively, the contents of this file may be used under the
25  * terms of the GNU Public License (the "GPL"), in which case the
26  * provisions of the GPL are applicable instead of those above.
27  * If you wish to allow use of your version of this file only
28  * under the terms of the GPL and not to allow others to use your
29  * version of this file under the NPL, indicate your decision by
30  * deleting the provisions above and replace them with the notice
31  * and other provisions required by the GPL. If you do not delete
32  * the provisions above, a recipient may use your version of this
33  * file under either the NPL or the GPL.
34  */

35 // Modified by Google
36

37 package com.google.gwt.dev.js.rhino;
38
39 import java.io.Reader JavaDoc;
40 import java.io.IOException JavaDoc;
41
42 /**
43  * An input buffer that combines fast character-based access with
44  * (slower) support for retrieving the text of the current line. It
45  * also supports building strings directly out of the internal buffer
46  * to support fast scanning with minimal object creation.
47  *
48  * Note that it is customized in several ways to support the
49  * TokenStream class, and should not be considered general.
50  *
51  * Credits to Kipp Hickman and John Bandhauer.
52  *
53  * @author Mike McCabe
54  */

55 final class LineBuffer {
56     /*
57      * for smooth operation of getLine(), this should be greater than
58      * the length of any expected line. Currently, 256 is 3% slower
59      * than 4096 for large compiles, but seems safer given evaluateString.
60      * Strings for the scanner are are built with StringBuffers
61      * instead of directly out of the buffer whenever a string crosses
62      * a buffer boundary, so small buffer sizes will mean that more
63      * objects are created.
64      */

65     static final int BUFLEN = 256;
66
67     LineBuffer(Reader JavaDoc in, int lineno) {
68         this.in = in;
69         this.lineno = lineno;
70     }
71
72     int read() throws IOException JavaDoc {
73         for(;;) {
74             if (end == offset && !fill())
75                 return -1;
76
77             int c = buffer[offset];
78             ++offset;
79
80             if ((c & EOL_HINT_MASK) == 0) {
81                 switch (c) {
82                     case '\r':
83                         // if the next character is a newline, skip past it.
84
if (offset != end) {
85                             if (buffer[offset] == '\n')
86                                 ++offset;
87                         } else {
88                             // set a flag for fill(), in case the first char
89
// of the next fill is a newline.
90
lastWasCR = true;
91                         }
92                         // NO break here!
93
case '\n': case '\u2028': case '\u2029':
94                         prevStart = lineStart;
95                         lineStart = offset;
96                         lineno++;
97                         return '\n';
98                 }
99             }
100
101             if (c < 128 || !formatChar(c)) {
102                 return c;
103             }
104         }
105     }
106
107     void unread() {
108         // offset can only be 0 when we're asked to unread() an implicit
109
// EOF_CHAR.
110

111         // This would be wrong behavior in the general case,
112
// because a peek() could map a buffer.length offset to 0
113
// in the process of a fill(), and leave it there. But
114
// the scanner never calls peek() or a failed match()
115
// followed by unread()... this would violate 1-character
116
// lookahead.
117
if (offset == 0 && !hitEOF) Context.codeBug();
118
119         if (offset == 0) // Same as if (hitEOF)
120
return;
121         offset--;
122         int c = buffer[offset];
123         if ((c & EOL_HINT_MASK) == 0 && eolChar(c)) {
124             lineStart = prevStart;
125             lineno--;
126         }
127     }
128
129     private void skipFormatChar() {
130         if (checkSelf && !formatChar(buffer[offset])) Context.codeBug();
131
132         // swap prev character with format one so possible call to
133
// startString can assume that previous non-format char is at
134
// offset - 1. Note it causes getLine to return not exactly the
135
// source LineBuffer read, but it is used only in error reporting
136
// and should not be a problem.
137
if (offset != 0) {
138             char tmp = buffer[offset];
139             buffer[offset] = buffer[offset - 1];
140             buffer[offset - 1] = tmp;
141         }
142         else if (otherEnd != 0) {
143             char tmp = buffer[offset];
144             buffer[offset] = otherBuffer[otherEnd - 1];
145             otherBuffer[otherEnd - 1] = tmp;
146         }
147
148         ++offset;
149     }
150
151     int peek() throws IOException JavaDoc {
152         for (;;) {
153             if (end == offset && !fill()) {
154                 return -1;
155             }
156
157             int c = buffer[offset];
158             if ((c & EOL_HINT_MASK) == 0 && eolChar(c)) {
159                 return '\n';
160             }
161             if (c < 128 || !formatChar(c)) {
162                 return c;
163             }
164
165             skipFormatChar();
166         }
167     }
168
169     boolean match(int test) throws IOException JavaDoc {
170         // TokenStream never looks ahead for '\n', which allows simple code
171
if ((test & EOL_HINT_MASK) == 0 && eolChar(test))
172             Context.codeBug();
173         // Format chars are not allowed either
174
if (test >= 128 && formatChar(test))
175             Context.codeBug();
176
177         for (;;) {
178             if (end == offset && !fill())
179                 return false;
180
181             int c = buffer[offset];
182             if (test == c) {
183                 ++offset;
184                 return true;
185             }
186             if (c < 128 || !formatChar(c)) {
187                 return false;
188             }
189             skipFormatChar();
190         }
191     }
192
193     // Reconstruct a source line from the buffers. This can be slow...
194
String JavaDoc getLine() {
195         // Look for line end in the unprocessed buffer
196
int i = offset;
197         while(true) {
198             if (i == end) {
199                 // if we're out of buffer, let's just expand it. We do
200
// this instead of reading into a StringBuffer to
201
// preserve the stream for later reads.
202
if (end == buffer.length) {
203                     char[] tmp = new char[buffer.length * 2];
204                     System.arraycopy(buffer, 0, tmp, 0, end);
205                     buffer = tmp;
206                 }
207                 int charsRead = 0;
208                 try {
209                     charsRead = in.read(buffer, end, buffer.length - end);
210                 } catch (IOException JavaDoc ioe) {
211                     // ignore it, we're already displaying an error...
212
break;
213                 }
214                 if (charsRead < 0)
215                     break;
216                 end += charsRead;
217             }
218             int c = buffer[i];
219             if ((c & EOL_HINT_MASK) == 0 && eolChar(c))
220                 break;
221             i++;
222         }
223
224         int start = lineStart;
225         if (lineStart < 0) {
226             // the line begins somewhere in the other buffer; get that first.
227
StringBuffer JavaDoc sb = new StringBuffer JavaDoc(otherEnd - otherStart + i);
228             sb.append(otherBuffer, otherStart, otherEnd - otherStart);
229             sb.append(buffer, 0, i);
230             return sb.toString();
231         } else {
232             return new String JavaDoc(buffer, lineStart, i - lineStart);
233         }
234     }
235
236     // Get the offset of the current character, relative to
237
// the line that getLine() returns.
238
int getOffset() {
239         if (lineStart < 0)
240             // The line begins somewhere in the other buffer.
241
return offset + (otherEnd - otherStart);
242         else
243             return offset - lineStart;
244     }
245
246     private boolean fill() throws IOException JavaDoc {
247         // fill should be caled only for emty buffer
248
if (checkSelf && !(end == offset)) Context.codeBug();
249
250         // swap buffers
251
char[] tempBuffer = buffer;
252         buffer = otherBuffer;
253         otherBuffer = tempBuffer;
254
255         // allocate the buffers lazily, in case we're handed a short string.
256
if (buffer == null) {
257             buffer = new char[BUFLEN];
258         }
259
260         // buffers have switched, so move the newline marker.
261
if (lineStart >= 0) {
262             otherStart = lineStart;
263         } else {
264             // discard beging of the old line
265
otherStart = 0;
266         }
267
268         otherEnd = end;
269
270         // set lineStart to a sentinel value, unless this is the first
271
// time around.
272
prevStart = lineStart = (otherBuffer == null) ? 0 : -1;
273
274         offset = 0;
275         end = in.read(buffer, 0, buffer.length);
276         if (end < 0) {
277             end = 0;
278
279             // can't null buffers here, because a string might be retrieved
280
// out of the other buffer, and a 0-length string might be
281
// retrieved out of this one.
282

283             hitEOF = true;
284             return false;
285         }
286
287         // If the last character of the previous fill was a carriage return,
288
// then ignore a newline.
289

290         // There's another bizzare special case here. If lastWasCR is
291
// true, and we see a newline, and the buffer length is
292
// 1... then we probably just read the last character of the
293
// file, and returning after advancing offset is not the right
294
// thing to do. Instead, we try to ignore the newline (and
295
// likely get to EOF for real) by doing yet another fill().
296
if (lastWasCR) {
297             if (buffer[0] == '\n') {
298               offset++;
299               if (end == 1)
300                   return fill();
301             }
302             lineStart = offset;
303             lastWasCR = false;
304         }
305         return true;
306     }
307
308     int getLineno() { return lineno; }
309     boolean eof() { return hitEOF; }
310
311     private static boolean formatChar(int c) {
312         return Character.getType((char)c) == Character.FORMAT;
313     }
314
315     private static boolean eolChar(int c) {
316         return c == '\r' || c == '\n' || c == '\u2028' || c == '\u2029';
317     }
318
319     // Optimization for faster check for eol character: eolChar(c) returns
320
// true only when (c & EOL_HINT_MASK) == 0
321
private static final int EOL_HINT_MASK = 0xdfd0;
322
323     private Reader JavaDoc in;
324     private char[] otherBuffer = null;
325     private char[] buffer = null;
326
327     // Yes, there are too too many of these.
328
private int offset = 0;
329     private int end = 0;
330     private int otherEnd;
331     private int lineno;
332
333     private int lineStart = 0;
334     private int otherStart = 0;
335     private int prevStart = 0;
336
337     private boolean lastWasCR = false;
338     private boolean hitEOF = false;
339
340 // Rudimentary support for Design-by-Contract
341
private static final boolean checkSelf = true;
342 }
343
Popular Tags