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 }