< prev index next >

src/java.base/share/classes/java/io/LineNumberReader.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -23,22 +23,22 @@
  * questions.
  */
 
 package java.io;
 
-
 /**
  * A buffered character-input stream that keeps track of line numbers.  This
  * class defines methods {@link #setLineNumber(int)} and {@link
  * #getLineNumber()} for setting and getting the current line number
  * respectively.
  *
  * <p> By default, line numbering begins at 0. This number increments at every
- * <a href="#lt">line terminator</a> as the data is read, and can be changed
- * with a call to {@code setLineNumber(int)}.  Note however, that
- * {@code setLineNumber(int)} does not actually change the current position in
- * the stream; it only changes the value that will be returned by
+ * <a href="#lt">line terminator</a> as the data is read, and at the end of the
+ * stream if the last character in the stream is not a line terminator.  This
+ * number can be changed with a call to {@code setLineNumber(int)}.  Note
+ * however, that {@code setLineNumber(int)} does not actually change the current
+ * position in the stream; it only changes the value that will be returned by
  * {@code getLineNumber()}.
  *
  * <p> A line is considered to be <a id="lt">terminated</a> by any one of a
  * line feed ('\n'), a carriage return ('\r'), or a carriage return followed
  * immediately by a linefeed.

@@ -47,10 +47,19 @@
  * @since       1.1
  */
 
 public class LineNumberReader extends BufferedReader {
 
+    /** Previous character types */
+    private static final int NONE = 0; // no previous character
+    private static final int CHAR = 1; // non-line terminator
+    private static final int EOL = 2; // line terminator
+    private static final int EOF  = 3; // end-of-file
+
+    /** The previous character type */
+    private int prevChar = NONE;
+
     /** The current line number */
     private int lineNumber = 0;
 
     /** The line number of the mark, if any */
     private int markedLineNumber; // Defaults to 0

@@ -109,12 +118,14 @@
         return lineNumber;
     }
 
     /**
      * Read a single character.  <a href="#lt">Line terminators</a> are
-     * compressed into single newline ('\n') characters.  Whenever a line
-     * terminator is read the current line number is incremented.
+     * compressed into single newline ('\n') characters.  The current line
+     * number is incremented whenever a line terminator is read, or when the
+     * end of the stream is reached and the last character in the stream is
+     * not a line terminator.
      *
      * @return  The character read, or -1 if the end of the stream has been
      *          reached
      *
      * @throws  IOException

@@ -132,20 +143,30 @@
             switch (c) {
             case '\r':
                 skipLF = true;
             case '\n':          /* Fall through */
                 lineNumber++;
+                prevChar = EOL;
                 return '\n';
+            case -1:
+                if (prevChar == CHAR)
+                    lineNumber++;
+                prevChar = EOF;
+                break;
+            default:
+                prevChar = CHAR;
+                break;
             }
             return c;
         }
     }
 
     /**
-     * Read characters into a portion of an array.  Whenever a <a
-     * href="#lt">line terminator</a> is read the current line number is
-     * incremented.
+     * Read characters into a portion of an array.  The current line
+     * number is incremented whenever a line terminator is read, or when the
+     * end of the stream is reached and the last character in the stream is
+     * not a line terminator.
      *
      * @param  cbuf
      *         Destination buffer
      *
      * @param  off

@@ -165,10 +186,17 @@
     @SuppressWarnings("fallthrough")
     public int read(char cbuf[], int off, int len) throws IOException {
         synchronized (lock) {
             int n = super.read(cbuf, off, len);
 
+            if (n == -1) {
+                if (prevChar == CHAR)
+                    lineNumber++;
+                prevChar = EOF;
+                return -1;
+            }
+
             for (int i = off; i < off + n; i++) {
                 int c = cbuf[i];
                 if (skipLF) {
                     skipLF = false;
                     if (c == '\n')

@@ -181,31 +209,51 @@
                     lineNumber++;
                     break;
                 }
             }
 
+            if (n > 0) {
+                switch ((int)cbuf[off + n - 1]) {
+                case '\r':
+                case '\n':      /* Fall through */
+                    prevChar = EOL;
+                    break;
+                default:
+                    prevChar = CHAR;
+                    break;
+                }
+            }
+
             return n;
         }
     }
 
     /**
-     * Read a line of text.  Whenever a <a href="#lt">line terminator</a> is
-     * read the current line number is incremented.
+     * Read a line of text.  The current line number is incremented whenever
+     * a line terminator is read, or when the end of the stream is reached and
+     * the last character in the stream is not a line terminator.
      *
      * @return  A String containing the contents of the line, not including
      *          any <a href="#lt">line termination characters</a>, or
      *          {@code null} if the end of the stream has been reached
      *
      * @throws  IOException
      *          If an I/O error occurs
      */
     public String readLine() throws IOException {
         synchronized (lock) {
-            String l = super.readLine(skipLF);
+            boolean[] term = new boolean[1];
+            String l = super.readLine(skipLF, term);
             skipLF = false;
-            if (l != null)
+            if (l != null) {
+                lineNumber++;
+                prevChar = term[0] ? EOL : EOF;
+            } else { // l == null
+                if (prevChar == CHAR)
                 lineNumber++;
+                prevChar = EOF;
+            }
             return l;
         }
     }
 
     /** Maximum skip-buffer size */

@@ -240,10 +288,13 @@
                 int nc = read(skipBuffer, 0, (int) Math.min(r, nn));
                 if (nc == -1)
                     break;
                 r -= nc;
             }
+            if (n - r > 0) {
+                prevChar = NONE;
+            }
             return n - r;
         }
     }
 
     /**
< prev index next >