KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > percederberg > grammatica > parser > LookAheadReader


1 /*
2  * LookAheadReader.java
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 2.1
7  * of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17  * MA 02111-1307, USA.
18  *
19  * Copyright (c) 2004-2005 Per Cederberg. All rights reserved.
20  */

21
22 package net.percederberg.grammatica.parser;
23
24 import java.io.IOException JavaDoc;
25 import java.io.Reader JavaDoc;
26
27 /**
28  * A look-ahead character stream reader. This class provides the
29  * functionalities of a buffered line-number reader, but with the
30  * additional possibility of peeking an unlimited number of characters
31  * ahead. When looking further and further ahead in the character
32  * stream, the buffer is continously enlarged to contain all the
33  * required characters from the current position an onwards. This
34  * means that looking more characters ahead requires more memory, and
35  * thus becomes unviable in the end.
36  *
37  * @author Per Cederberg, <per at percederberg dot net>
38  * @version 1.5
39  * @since 1.5
40  */

41 public class LookAheadReader extends Reader JavaDoc {
42
43     /**
44      * The character stream block size. All reads from the underlying
45      * character stream will be made in multiples of this block size.
46      */

47     private static final int STREAM_BLOCK_SIZE = 4096;
48
49     /**
50      * The buffer block size. The size of the internal buffer will
51      * always be a multiple of this block size.
52      */

53     private static final int BUFFER_BLOCK_SIZE = 1024;
54
55     /**
56      * The character buffer.
57      */

58     private char[] buffer = new char[STREAM_BLOCK_SIZE];
59
60     /**
61      * The current character buffer position.
62      */

63     private int pos = 0;
64
65     /**
66      * The number of characters in the buffer.
67      */

68     private int length = 0;
69
70     /**
71      * The underlying character stream reader.
72      */

73     private Reader JavaDoc input = null;
74
75     /**
76      * The line number of the next character to read. This value will
77      * be incremented when reading past line breaks.
78      */

79     private int line = 1;
80
81     /**
82      * The column number of the next character to read. This value
83      * will be updated for every character read.
84      */

85     private int column = 1;
86
87     /**
88      * Creates a new look-ahead character stream reader.
89      *
90      * @param input the character stream reader to wrap
91      */

92     public LookAheadReader(Reader JavaDoc input) {
93         super();
94         this.input = input;
95     }
96
97     /**
98      * Returns the current line number. This number is the line number
99      * of the next character to read.
100      *
101      * @return the current line number
102      */

103     public int getLineNumber() {
104         return line;
105     }
106
107     /**
108      * Returns the current column number. This number is the column
109      * number of the next character to read.
110      *
111      * @return the current column number
112      */

113     public int getColumnNumber() {
114         return column;
115     }
116
117     /**
118      * Reads a single character.
119      *
120      * @return the character read as an integer in the range 0 to
121      * 65535 (0x00-0xffff), or -1 if the end of the stream was reached
122      *
123      * @throws IOException if an I/O error occurred
124      */

125     public int read() throws IOException JavaDoc {
126         readAhead(1);
127         if (pos >= length) {
128             return -1;
129         } else {
130             updateLineColumnNumbers(1);
131             return buffer[pos++];
132         }
133     }
134
135     /**
136      * Reads characters into an array. This method will always return
137      * any remaining characters to read before returning -1.
138      *
139      * @param cbuf the destination buffer
140      *
141      * @return the number of characters read, or -1 if the end of the
142      * stream was reached
143      *
144      * @throws IOException if an I/O error occurred
145      */

146     public int read(char[] cbuf) throws IOException JavaDoc {
147         return read(cbuf, 0, cbuf.length);
148     }
149
150     /**
151      * Reads characters into an array. This method will always return
152      * any remaining characters to read before returning -1.
153      *
154      * @param cbuf the destination buffer
155      * @param off the offset at which to start storing chars
156      * @param len the maximum number of characters to read
157      *
158      * @return the number of characters read, or -1 if the end of the
159      * stream was reached
160      *
161      * @throws IOException if an I/O error occurred
162      */

163     public int read(char[] cbuf, int off, int len) throws IOException JavaDoc {
164         int count;
165
166         readAhead(len);
167         if (pos >= length) {
168             return -1;
169         } else {
170             count = length - pos;
171             if (count > len) {
172                 count = len;
173             }
174             updateLineColumnNumbers(count);
175             System.arraycopy(buffer, pos, cbuf, off, count);
176             pos += count;
177             return count;
178         }
179     }
180
181     /**
182      * Reads characters into a string. This method will always return
183      * any remaining characters to read before returning null.
184      *
185      * @param len the maximum number of characters to read
186      *
187      * @return the string containing the characters read, or null if
188      * the end of the stream was reached
189      *
190      * @throws IOException if an I/O error occurred
191      */

192     public String JavaDoc readString(int len) throws IOException JavaDoc {
193         int count;
194         String JavaDoc result;
195
196         readAhead(len);
197         if (pos >= length) {
198             return null;
199         } else {
200             count = length - pos;
201             if (count > len) {
202                 count = len;
203             }
204             updateLineColumnNumbers(count);
205             result = new String JavaDoc(buffer, pos, count);
206             pos += count;
207             return result;
208         }
209     }
210
211     /**
212      * Returns a character not yet read. This method will read
213      * characters up until the specified offset and store them for
214      * future retrieval in an internal buffer. The character offset
215      * must be positive, but is allowed to span the entire size of the
216      * input character stream. Note that the internal buffer must hold
217      * all the intermediate characters, which may be wasteful of
218      * memory if offset is too large.
219      *
220      * @param off the character offset, from 0 and up
221      *
222      * @return the character found as an integer in the range 0 to
223      * 65535 (0x00-0xffff), or -1 if the end of the stream was reached
224      *
225      * @throws IOException if an I/O error occurred
226      */

227     public int peek(int off) throws IOException JavaDoc {
228         readAhead(off + 1);
229         if (pos + off >= length) {
230             return -1;
231         } else {
232             return buffer[pos + off];
233         }
234     }
235
236     /**
237      * Returns a string of characters not yet read. This method will
238      * read characters up until the specified offset (plus length) and
239      * store them for future retrieval in an internal buffer. The
240      * character offset must be positive, but is allowed to span the
241      * entire size of the input character stream. Note that the
242      * internal buffer must hold all the intermediate characters,
243      * which may be wasteful of memory if offset is too large.
244      *
245      * @param off the character offset, from 0 and up
246      * @param len the maximum number of characters to read
247      *
248      * @return the string containing the characters read, or null if
249      * the end of the stream was reached
250      *
251      * @throws IOException if an I/O error occurred
252      */

253     public String JavaDoc peekString(int off, int len) throws IOException JavaDoc {
254         int count;
255
256         readAhead(off + len + 1);
257         if (pos + off >= length) {
258             return null;
259         } else {
260             count = length - (pos + off);
261             if (count > len) {
262                 count = len;
263             }
264             return new String JavaDoc(buffer, pos + off, count);
265         }
266     }
267
268     /**
269      * Close the stream. Once a stream has been closed, further reads
270      * will throw an IOException. Closing a previously-closed stream,
271      * however, has no effect.
272      *
273      * @throws IOException if an I/O error occurs
274      */

275     public void close() throws IOException JavaDoc {
276         buffer = null;
277         pos = 0;
278         length = 0;
279         if (input != null) {
280             try {
281                 input.close();
282             } finally {
283                 input = null;
284             }
285         }
286     }
287
288     /**
289      * Reads characters from the input stream and appends them to the
290      * input buffer. This method is safe to call even though the end
291      * of file has been reached. As a side effect, this method may
292      * also remove characters at the beginning of the buffer. It will
293      * enlarge the buffer as needed.
294      *
295      * @param offset the read offset, from 0 and up
296      *
297      * @throws IOException if an error was encountered while reading
298      * the input stream
299      */

300     private void readAhead(int offset) throws IOException JavaDoc {
301         int size;
302         int readSize;
303
304         // Check for end of stream or already read characters
305
if (input == null || pos + offset < length) {
306             return;
307         }
308
309         // Remove old characters from buffer
310
if (pos > BUFFER_BLOCK_SIZE) {
311             System.arraycopy(buffer, pos, buffer, 0, length - pos);
312             length -= pos;
313             pos = 0;
314         }
315
316         // Calculate number of characters to read
317
size = pos + offset - length + 1;
318         if (size % STREAM_BLOCK_SIZE != 0) {
319             size = (size / STREAM_BLOCK_SIZE) * STREAM_BLOCK_SIZE;
320             size += STREAM_BLOCK_SIZE;
321         }
322         ensureBufferCapacity(length + size);
323
324         // Read characters
325
try {
326             readSize = input.read(buffer, length, size);
327         } catch (IOException JavaDoc e) {
328             input = null;
329             throw e;
330         }
331
332         // Append characters to buffer
333
if (readSize > 0) {
334             length += readSize;
335         }
336         if (readSize < size) {
337             try {
338                 input.close();
339             } finally {
340                 input = null;
341             }
342         }
343     }
344
345     /**
346      * Ensures that the buffer has at least the specified capacity.
347      *
348      * @param size the minimum buffer size
349      */

350     private void ensureBufferCapacity(int size) {
351         char[] newbuf;
352
353         if (buffer.length >= size) {
354             return;
355         }
356         if (size % BUFFER_BLOCK_SIZE != 0) {
357             size = (size / BUFFER_BLOCK_SIZE) * BUFFER_BLOCK_SIZE;
358             size += BUFFER_BLOCK_SIZE;
359         }
360         newbuf = new char[size];
361         System.arraycopy(buffer, 0, newbuf, 0, length);
362         buffer = newbuf;
363     }
364
365     /**
366      * Updates the line and column numbers counters. This method
367      * requires all the characters to be processed (i.e. returned as
368      * read) to be present in the buffer, starting at the current
369      * buffer position.
370      *
371      * @param offset the number of characters to process
372      */

373     private void updateLineColumnNumbers(int offset) {
374         for (int i = 0; i < offset; i++) {
375             if (buffer[pos + i] == '\n') {
376                 line++;
377                 column = 1;
378             } else {
379                 column++;
380             }
381         }
382     }
383 }
384
Popular Tags