View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.csv;
19  
20  import static org.apache.commons.csv.Constants.CR;
21  import static org.apache.commons.csv.Constants.END_OF_STREAM;
22  import static org.apache.commons.csv.Constants.LF;
23  import static org.apache.commons.csv.Constants.UNDEFINED;
24  
25  import java.io.BufferedReader;
26  import java.io.IOException;
27  import java.io.Reader;
28  
29  /**
30   * A special reader decorator which supports more sophisticated access to the underlying reader object.
31   * <p>
32   * In particular the reader supports a look-ahead option, which allows you to see the next char returned by
33   * {@link #read()}.
34   *
35   * @version $Id: ExtendedBufferedReader.java 1461241 2013-03-26 17:49:05Z ggregory $
36   */
37  final class ExtendedBufferedReader extends BufferedReader {
38  
39      /** The last char returned */
40      private int lastChar = UNDEFINED;
41  
42      /** The line counter */
43      private long lineCounter;
44  
45      /**
46       * Created extended buffered reader using default buffer-size
47       */
48      ExtendedBufferedReader(final Reader reader) {
49          super(reader);
50      }
51  
52      @Override
53      public int read() throws IOException {
54          final int current = super.read();
55          if (current == CR || (current == LF && lastChar != CR)) {
56              lineCounter++;
57          }
58          lastChar = current;
59          return lastChar;
60      }
61  
62      /**
63       * Returns the last character that was read as an integer (0 to 65535). This will be the last character returned by
64       * any of the read methods. This will not include a character read using the {@link #peek()} method. If no
65       * character has been read then this will return {@link #UNDEFINED}. If the end of the stream was reached on the
66       * last read then this will return {@link #END_OF_STREAM}.
67       *
68       * @return the last character that was read
69       */
70      int getLastChar() {
71          return lastChar;
72      }
73  
74      @Override
75      public int read(final char[] buf, final int offset, final int length) throws IOException {
76          if (length == 0) {
77              return 0;
78          }
79  
80          final int len = super.read(buf, offset, length);
81  
82          if (len > 0) {
83  
84              for (int i = offset; i < offset + len; i++) {
85                  final char ch = buf[i];
86                  if (ch == LF) {
87                      if (CR != (i > 0 ? buf[i - 1] : lastChar)) {
88                          lineCounter++;
89                      }
90                  } else if (ch == CR) {
91                      lineCounter++;
92                  }
93              }
94  
95              lastChar = buf[offset + len - 1];
96  
97          } else if (len == -1) {
98              lastChar = END_OF_STREAM;
99          }
100 
101         return len;
102     }
103 
104     /**
105      * Calls {@link BufferedReader#readLine()} which drops the line terminator(s). This method should only be called
106      * when processing a comment, otherwise information can be lost.
107      * <p>
108      * Increments {@link #lineCounter}
109      * <p>
110      * Sets {@link #lastChar} to {@link #END_OF_STREAM} at EOF, otherwise to LF
111      *
112      * @return the line that was read, or null if reached EOF.
113      */
114     @Override
115     public String readLine() throws IOException {
116         final String line = super.readLine();
117 
118         if (line != null) {
119             lastChar = LF; // needed for detecting start of line
120             lineCounter++;
121         } else {
122             lastChar = END_OF_STREAM;
123         }
124 
125         return line;
126     }
127 
128     /**
129      * Returns the next character in the current reader without consuming it. So the next call to {@link #read()} will
130      * still return this value.
131      *
132      * @return the next character
133      *
134      * @throws IOException
135      *             if there is an error in reading
136      */
137     int lookAhead() throws IOException {
138         super.mark(1);
139         final int c = super.read();
140         super.reset();
141 
142         return c;
143     }
144 
145     /**
146      * Returns the number of lines read
147      *
148      * @return the current-line-number (or -1)
149      */
150     long getLineNumber() {
151         return lineCounter;
152     }
153 }