/[classpath]/classpath/java/io/LineNumberReader.java
ViewVC logotype

Diff of /classpath/java/io/LineNumberReader.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.7 by arenn, Thu Mar 6 03:00:10 2003 UTC revision 1.8 by arenn, Mon Mar 10 00:03:46 2003 UTC
# Line 1  Line 1 
1  /* LineNumberReader.java -- A character input stream which counts line numbers  /* LineNumberReader.java -- A character input stream which counts line numbers
2     Copyright (C) 1998,2003 Free Software Foundation, Inc.     Copyright (C) 1998, 2003 Free Software Foundation, Inc.
3    
4  This file is part of GNU Classpath.  This file is part of GNU Classpath.
5    
# Line 7  GNU Classpath is free software; you can Line 7  GNU Classpath is free software; you can
7  it under the terms of the GNU General Public License as published by  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2, or (at your option)  the Free Software Foundation; either version 2, or (at your option)
9  any later version.  any later version.
10    
11  GNU Classpath is distributed in the hope that it will be useful, but  GNU Classpath is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Line 35  this exception to your version of the li Line 35  this exception to your version of the li
35  obligated to do so.  If you do not wish to do so, delete this  obligated to do so.  If you do not wish to do so, delete this
36  exception statement from your version. */  exception statement from your version. */
37    
   
38  package java.io;  package java.io;
39    
40  /**  /**
41    * This class functions like a standard <code>Reader</code> except that it   * This class functions like a standard <code>Reader</code> except that it
42    * counts line numbers, and canonicalizes newline characters.  As data   * counts line numbers, and canonicalizes newline characters.  As data
43    * is read, whenever the char sequences "\r", "\n", or "\r\n" are encountered,   * is read, whenever the char sequences "\r", "\n", or "\r\n" are encountered,
44    * the running line count is incremeted by one.  Additionally, the whatever   * the running line count is incremeted by one.  Additionally, the whatever
45    * line termination sequence was encountered will be converted to a "\n"   * line termination sequence was encountered will be converted to a "\n"
46    * char.  Note that this class numbers lines from 0.  When the first   * char.  Note that this class numbers lines from 0.  When the first
47    * line terminator is encountered, the line number is incremented to 1, and   * line terminator is encountered, the line number is incremented to 1, and
48    * so on.  Also note that actual "\r" and "\n" characters are looked for.   * so on.  Also note that actual "\r" and "\n" characters are looked for.
49    * The system dependent line separator sequence is ignored.   * The system dependent line separator sequence is ignored.
50    * <p>   * <p>
51    * This class counts only line termination characters.  If the last line   * This class counts only line termination characters.  If the last line
52    * read from the stream does not end in a line termination sequence, it   * read from the stream does not end in a line termination sequence, it
53    * will not be counted as a line.   * will not be counted as a line.
54    *   *
55    * @author Aaron M. Renn (arenn@urbanophile.com)   * @author Per Bothner <bothner@cygnus.com>
56    */   * @author Aaron M. Renn (arenn@urbanophile.com)
57     * @date April 22, 1998.  
58     */
59    /* Written using "Java Class Libraries", 2nd edition, plus online
60     * API docs for JDK 1.2 beta from http://www.javasoft.com.
61     * Status:  Believed complete and correct.
62     *
63     * This implementation has the feature that if '\r' is read, it
64     * does not look for a '\n', but immediately returns '\n'.
65     * On the next read(), if a '\n' is read, it is skipped.
66     * This has the advantage that we do not read (and hang) unnecessarily.
67     *
68     * This implementation is also minimal in the number of fields it uses.
69     */
70  public class LineNumberReader extends BufferedReader  public class LineNumberReader extends BufferedReader
71  {  {
72      /** The current line number. */
73    /*************************************************************************/    private int lineNumber;
   
   /*  
    * Instance Variables  
    */  
   
   /**  
     * This variable is used to keep track of the current line number  
     */  
   private int line_number;  
74    
75    /**    /**
76      * This variable is used to keep track of the line number that was      * Create a new <code>LineNumberReader</code> that reads from the
77      * current when the <code>mark()</code> method was called.      * specified subordinate <code>Reader</code>.  A default 8K char sized
     */  
   private int marked_line_number;  
   
   /**  
     * Determines whether or not a '\n' as the first character of a read  
     * is the continuation of a '\r' as the last character of the previous  
     * read or not  
     */  
   private boolean continued_newline;  
   
   /*************************************************************************/  
   
   /*  
    * Constructors  
    */  
   
   /**  
     * Create a new <code>LineNumberReader</code> that reads from the  
     * specified subordinate <code>Reader</code>.  A default 4096 char sized  
78      * buffer will be used for reads.      * buffer will be used for reads.
79      *      *
80      * @param in The subordinate <code>Reader</code> to read from      * @param in The subordinate <code>Reader</code> to read from
81      */      */
82    public LineNumberReader(Reader in)    public LineNumberReader(Reader in)
83    {    {
84      this(in, DEFAULT_BUFFER_SIZE);      super(in, DEFAULT_BUFFER_SIZE);
85    }    }
86    
   /*************************************************************************/  
   
87    /**    /**
88      * This method initializes a new <code>LineNumberReader</code> to read      * This method initializes a new <code>LineNumberReader</code> to read
89      * from the specified subordinate <code>Reader</code> using the specified      * from the specified subordinate <code>Reader</code> using the specified
# Line 115  public class LineNumberReader extends Bu Line 97  public class LineNumberReader extends Bu
97      super(in, size);      super(in, size);
98    }    }
99    
   /*************************************************************************/  
   
   /*  
    * Instance Methods  
    */  
   
100    /**    /**
101      * This method returns the current line number      * This method returns the current line number
102      *      *
# Line 128  public class LineNumberReader extends Bu Line 104  public class LineNumberReader extends Bu
104      */      */
105    public int getLineNumber()    public int getLineNumber()
106    {    {
107      return(line_number);      return lineNumber;
108    }    }
109    
   /*************************************************************************/  
   
110    /**    /**
111      * This method sets the current line number to the specified value.      * This method sets the current line number to the specified value.
112      *      *
113      * @param line_number The new line number      * @param line_number The new line number
114      */      */
115    public void setLineNumber(int line_number)    public void setLineNumber(int lineNumber)
116    {    {
117      this.line_number = line_number;      this.lineNumber = lineNumber;
118    }    }
119    
120    /*************************************************************************/    private static int countLines (char[] buffer, int off, int len)
121      {
122        int count = 0;
123        char prev = '\0';
124        for (int i = 0;  i < len;  i++)
125          {
126            char ch = buffer[i+off];
127            if ((ch == '\n' && prev != '\r') || ch == '\r')
128              count++;
129            prev = ch;
130          }
131        return count;
132      }
133    
134    /**    /**
135      * This method marks a position in the input to which the stream can be      * This method marks a position in the input to which the stream can be
136      * "reset" char calling the <code>reset()</code> method.  The parameter      * "reset" char calling the <code>reset()</code> method.  The parameter
137      * <code>readlimit</code> is the number of chars that can be read from the      * <code>readlimit</code> is the number of chars that can be read from the
138      * stream after setting the mark before the mark becomes invalid.   For      * stream after setting the mark before the mark becomes invalid.   For
139      * example, if <code>mark()</code> is called with a read limit of 10,      * example, if <code>mark()</code> is called with a read limit of 10,
140      * then when      * then when
141      * 11 chars of data are read from the stream before the <code>reset()</code>      * 11 chars of data are read from the stream before the <code>reset()</code>
142      * method is called, then the mark is invalid and the stream object      * method is called, then the mark is invalid and the stream object
143      * instance is not required to remember the mark.      * instance is not required to remember the mark.
144      * <p>      * <p>
145      * In this class, this method will remember the current line number as well      * In this class, this method will remember the current line number as well
146      * as the current position in the stream.  When the <code>reset()</code>      * as the current position in the stream.  When the <code>reset()</code>
147      * method      * method
148      * is called, the line number will be restored to the saved line number in      * is called, the line number will be restored to the saved line number in
149      * addition to the stream position.      * addition to the stream position.
150      *      *
151      * @param readlimit The number of chars that can be read before the      * @param readlimit The number of chars that can be read before the
152      * mark becomes invalid      * mark becomes invalid
153      *      *
154      * @exception IOException If an error occurs      * @exception IOException If an error occurs
155      */      */
156    public void mark(int readlimit) throws IOException    public void mark(int readLimit) throws IOException
157    {    {
158      synchronized (lock) {      synchronized (lock)
159          {
160      super.mark(readlimit);          // This is basically the same as BufferedReader.mark.
161            // However, if the previous character was a '\r', we need to
162      marked_line_number = line_number;          // save that 'r', in case the next character is a '\n'.
163            if (pos + readLimit > limit)
164      } // synchronized            {
165                int saveCR = (pos > 0 && buffer[pos-1] == '\r') ? 1 : 0;
166                char[] old_buffer = buffer;
167                if (readLimit > limit)
168                  buffer = new char[saveCR + readLimit];
169                int copy_start = pos - saveCR;
170                limit -= copy_start;
171                System.arraycopy(old_buffer, copy_start, buffer, 0, limit);
172                pos = saveCR;
173              }
174            markPos = pos;
175          }
176    }    }
177    
   /*************************************************************************/  
   
178    /**    /**
179      * This method resets a stream to the point where the <code>mark()</code>      * This method resets a stream to the point where the <code>mark()</code>
180      * method      * method
181      * was called.  Any chars that were read after the mark point was set will      * was called.  Any chars that were read after the mark point was set will
182      * be re-read during subsequent reads.      * be re-read during subsequent reads.
183      * <p>      * <p>
184      * In this class, this method will also restore the line number that was      * In this class, this method will also restore the line number that was
185      * current when the <code>mark()</code> method was called.      * current when the <code>mark()</code> method was called.
186      *      *
187      * @exception IOException If an error occurs      * @exception IOException If an error occurs
188      */      */
189    public void reset() throws IOException    public void reset() throws IOException
190    {    {
191      synchronized (lock) {      synchronized (lock)
192          {
193      super.reset();          if (markPos < 0)
194              throw new IOException("mark never set or invalidated");
195      line_number = marked_line_number;          if (markPos > 0 && pos > markPos && buffer[markPos-1] == '\r'
196                && buffer[markPos] == '\n')
197      } // synchronized            lineNumber--;
198            lineNumber -= countLines(buffer, markPos, pos - markPos);
199            pos = markPos;
200          }
201    }    }
202    
   /*************************************************************************/  
   
203    /**    /**
204      * This method reads an unsigned char from the input stream and returns it      * This method reads an unsigned char from the input stream and returns it
205      * as an int in the range of 0-255.  This method will return -1 if the      * as an int in the range of 0-65535.  This method will return -1 if the
206      * end of the stream has been reached.      * end of the stream has been reached.
207      * <p>      * <p>
208      * Note that if a line termination sequence is encountered (ie, "\r",      * Note that if a line termination sequence is encountered (ie, "\r",
# Line 219  public class LineNumberReader extends Bu Line 215  public class LineNumberReader extends Bu
215      * to be read.      * to be read.
216      *      *
217      * @return The char read or -1 if end of stream      * @return The char read or -1 if end of stream
218      *      *
219      * @exception IOException If an error occurs      * @exception IOException If an error occurs
220      */      */
221    public int read() throws IOException    public int read() throws IOException
222    {    {
223      synchronized (lock)      synchronized (lock)
224        {        {
225          int char_read = super.read();          skipRedundantLF();
226            if (pos >= limit)
227          if (char_read == '\n')            {
228            ++line_number;              if (markPos >= 0 && limit == buffer.length)
229                  markPos = -1;
230          if (char_read == '\r')              if (markPos <= 0)
231            {                pos = limit = 0;
232              int extra_char_read = super.read();              int count = in.read(buffer, limit, buffer.length - limit);
233                if (count <= 0)
234              if ((extra_char_read != '\n') && (extra_char_read != -1))                return -1;
235                pos--;              limit += count;
236              }
237              char_read = '\n';          char ch = buffer[pos++];
238              ++line_number;          if (ch == '\r' || ch == '\n')
239            }            {
240                lineNumber++;
241          return(char_read);              return '\n';
242        } // synchronized            }
243            return (int) ch;
244          }
245    }    }
246    
   /*************************************************************************/  
   
247    /**    /**
248      * This method reads chars from a stream and stores them into a caller      * This method reads chars from a stream and stores them into a caller
249      * supplied buffer.  It starts storing data at index <code>offset</code> into      * supplied buffer.  It starts storing data at index <code>offset</code> into    * the buffer and attemps to read <code>len</code> chars.  This method can
     * the buffer and attemps to read <code>len</code> chars.  This method can  
250      * return before reading the number of chars requested.  The actual number      * return before reading the number of chars requested.  The actual number
251      * of chars read is returned as an int.  A -1 is returned to indicated the      * of chars read is returned as an int.  A -1 is returned to indicated the
252      * end of the stream.      * end of the stream.
# Line 271  public class LineNumberReader extends Bu Line 266  public class LineNumberReader extends Bu
266      *      *
267      * @exception IOException If an error occurs.      * @exception IOException If an error occurs.
268      */      */
269    public int read(char[] buf, int offset, int len) throws IOException    public int read(char[] buf, int offset, int count) throws IOException
270    {    {
271      if (len == 0)      if (count <= 0)
       return(0);  
   
     synchronized (lock)  
272        {        {
273            if (count < 0)
274          // Read the first char here in order to allow IOException's to            throw new IndexOutOfBoundsException();
275          // propagate up          return 0;
276          int char_read = read();        }
277          if (char_read == -1)      synchronized (lock)
278            return(-1);        {
279          buf[offset] = (char)char_read;          int first = read();
280            if (first < 0)
281          int total_read = 1;            return -1;
282            int start_offset = offset;
283          // Read the rest of the chars.  We do this in a single char read() loop,          buf[offset++] = (char) first;
284          // which is bad and should be fixed in the future. *****RECODE THIS****          if (buffer[pos-1] == '\r' && pos < limit && buffer[pos] == '\n')
285          try            pos++;
286            {          count--;
287              for (int i = 1; i < len; i++)          while (count-- > 0 && pos < limit)
288                {            {
289                  char_read = read();              char ch = buffer[pos++];
290                  if (char_read == -1)              if (ch == '\r')
291                    return(total_read);                {
292                    lineNumber++;
293                  buf[offset + i] = (char)char_read;                  ch = '\n';
294                                    if (pos < limit && buffer[pos] == '\n')
295                  ++total_read;                      pos++;
296                }                }
297            }              else if (ch == '\n')
298          catch (IOException e)                lineNumber++;
299            {              buf[offset++] = ch;
300              return(total_read);            }
301            }          return offset - start_offset;
302          }
         return(total_read);  
       } // synchronized  
303    }    }
304    
305    /*************************************************************************/    private void skipRedundantLF() throws IOException
306      {
307        if (pos > 0 && buffer[pos-1] == '\r')
308          {
309            if (pos < limit)
310              { // fast case
311                if (buffer[pos] == '\n')
312                  pos++;
313              }
314            else
315              { // use read() to deal with the general case.
316                // Set pos and limit to zero to avoid infinite recursion in read.
317                // May need to invalidate markPos if we've exceeded the buffer.  
318                if (pos >= buffer.length)
319                  markPos = -1;
320                pos = limit = 0;
321                int ch = read();
322                if (ch >= 0 && ch != '\n')
323                  pos--;
324              }
325          }
326      }
327    
328    /**    /**
329      * This method reads a line of text from the input stream and returns      * This method reads a line of text from the input stream and returns
# Line 320  public class LineNumberReader extends Bu Line 331  public class LineNumberReader extends Bu
331      * by a "\r", "\n", or "\r\n" sequence, not by the system dependent line      * by a "\r", "\n", or "\r\n" sequence, not by the system dependent line
332      * separator.      * separator.
333      *      *
334      * @return The line read as a <code>String</code> or <code>null</code>      * @return The line read as a <code>String</code> or <code>null</code>
335      * if end of stream.      * if end of stream.
336      *      *
337      * @exception IOException If an error occurs      * @exception IOException If an error occurs
338      */      */
339    public String readLine() throws IOException    public String readLine() throws IOException
340    {    {
341      synchronized (lock)      // BufferedReader.readLine already does this.  Shouldn't need to keep
342        {      // track of newlines (since the read method deals with this for us).
343          String line = super.readLine();      // But if the buffer is large, we may not call the read method at all
344          if (line != null)      // and super.readLine can't increment lineNumber itself.
345            ++line_number;      // Though it may seem kludgy, the safest thing to do is to save off
346        // lineNumber and increment it explicitly when we're done (iff we
347        // ended with a '\n' or '\r' as opposed to EOF).
348        //
349        // Also, we need to undo the special casing done by BufferedReader.readLine
350        // when a '\r' is the last char in the buffer.  That situation is marked
351        // by 'pos > limit'.
352        int tmpLineNumber = lineNumber;
353        skipRedundantLF();
354        String str = super.readLine();
355        if (pos > limit)
356          --pos;
357    
358        int ch;
359        if (pos > 0 && ((ch = buffer[pos - 1]) == '\n' || ch == '\r'))
360          lineNumber = tmpLineNumber + 1;
361    
362          return(line);      return str;
       } // synchronized  
363    }    }
364    
   /*************************************************************************/  
   
365    /**    /**
366      * This method skips over characters in the stream.  This method will      * This method skips over characters in the stream.  This method will
367      * skip the specified number of characters if possible, but is not required      * skip the specified number of characters if possible, but is not required
368      * to skip them all.  The actual number of characters skipped is returned.      * to skip them all.  The actual number of characters skipped is returned.
369      * This method returns 0 if the specified number of chars is less than 1.      * This method returns 0 if the specified number of chars is less than 1.
370      *      *
371      * @param num_chars The specified number of chars to skip.      * @param count The specified number of chars to skip.
372      *      *
373      * @return The actual number of chars skipped.      * @return The actual number of chars skipped.
374      *      *
375      * @exception IOException If an error occurs      * @exception IOException If an error occurs
376      */      */
377    public long skip(long num_chars) throws IOException    public long skip(long count) throws IOException
378    {    {
379      // The spec says to override this method so we do even though we      return(super.skip(count));
380      // don't need to.  Assume we aren't counting lines in skipped chars.      // FIXME: This method not merged from gcj. The code below is the original
381      return(super.skip(num_chars));      // gcj code that doesn't work.  I'm not sure if the method above (from
382        // Classpath) is broken or not, but it seems to work correctly in all
383        // methods I've tested.  If somebody wants to investigate why the code
384        // below fails, feel free.
385    /*
386        if (count <= 0)
387          return 0;
388        long to_do = count;
389        do
390          {
391            int ch = read();
392            if (ch < 0)
393              break;
394            to_do--;
395            if (ch == '\n' || ch == '\r')
396              lineNumber++;
397            else
398              {
399                long fence = pos + to_do;
400                if (limit < fence)
401                  fence = limit;
402                int end = pos;
403                for (; end < fence; end++)
404                  {
405                    char endch = buffer[end];
406                    if (endch == '\n' || endch == '\r')
407                      break;
408                  }
409                to_do -= end - pos;
410                pos = end;
411              }
412          }
413        while (to_do > 0);
414        return count - to_do;
415    */
416    }    }
417    }
 } // class LineNumberReader  
418    

Legend:
Removed from v.1.7  
changed lines
  Added in v.1.8

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26