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 java.io.BufferedReader;
21 import java.io.IOException;
22 import java.io.Reader;
23
24 /**
25 * ExtendedBufferedReader
26 *
27 * A special reader decorator which supports more
28 * sophisticated access to the underlying reader object.
29 *
30 * In particular the reader supports a look-ahead option,
31 * which allows you to see the next char returned by
32 * next().
33 */
34 class ExtendedBufferedReader extends BufferedReader {
35
36 /** The end of stream symbol */
37 static final int END_OF_STREAM = -1;
38
39 /** Undefined state for the lookahead char */
40 static final int UNDEFINED = -2;
41
42 /** The lookahead chars */
43 private int lookaheadChar = UNDEFINED;
44
45 /** The last char returned */
46 private int lastChar = UNDEFINED;
47
48 /** The line counter */
49 private int lineCounter = 0;
50
51 private CharBuffer line = new CharBuffer();
52
53 /**
54 * Created extended buffered reader using default buffer-size
55 */
56 ExtendedBufferedReader(Reader r) {
57 super(r);
58 /* note uh: do not fetch the first char here,
59 * because this might block the method!
60 */
61 }
62
63 /**
64 * Reads the next char from the input stream.
65 *
66 * @return the next char or END_OF_STREAM if end of stream has been reached.
67 */
68 @Override
69 public int read() throws IOException {
70 // initialize the lookahead
71 if (lookaheadChar == UNDEFINED) {
72 lookaheadChar = super.read();
73 }
74 lastChar = lookaheadChar;
75 if (super.ready()) {
76 lookaheadChar = super.read();
77 } else {
78 lookaheadChar = UNDEFINED;
79 }
80 if (lastChar == '\n') {
81 lineCounter++;
82 }
83 return lastChar;
84 }
85
86 /**
87 * Returns the last read character again.
88 *
89 * @return the last read char or UNDEFINED
90 */
91 int readAgain() {
92 return lastChar;
93 }
94
95 /**
96 * Non-blocking reading of len chars into buffer buf starting
97 * at bufferposition off.
98 * <p/>
99 * performs an iterative read on the underlying stream
100 * as long as the following conditions hold:
101 * - less than len chars have been read
102 * - end of stream has not been reached
103 * - next read is not blocking
104 *
105 * @return nof chars actually read or END_OF_STREAM
106 */
107 @Override
108 public int read(char[] buf, int off, int len) throws IOException {
109 // do not claim if len == 0
110 if (len == 0) {
111 return 0;
112 }
113
114 // init lookahead, but do not block !!
115 if (lookaheadChar == UNDEFINED) {
116 if (ready()) {
117 lookaheadChar = super.read();
118 } else {
119 return -1;
120 }
121 }
122 // 'first read of underlying stream'
123 if (lookaheadChar == -1) {
124 return -1;
125 }
126 // continue until the lookaheadChar would block
127 int cOff = off;
128 while (len > 0 && ready()) {
129 if (lookaheadChar == -1) {
130 // eof stream reached, do not continue
131 return cOff - off;
132 } else {
133 buf[cOff++] = (char) lookaheadChar;
134 if (lookaheadChar == '\n') {
135 lineCounter++;
136 }
137 lastChar = lookaheadChar;
138 lookaheadChar = super.read();
139 len--;
140 }
141 }
142 return cOff - off;
143 }
144
145 /**
146 * @return A String containing the contents of the line, not
147 * including any line-termination characters, or null
148 * if the end of the stream has been reached
149 */
150 @Override
151 public String readLine() throws IOException {
152
153 if (lookaheadChar == UNDEFINED) {
154 lookaheadChar = super.read();
155 }
156
157 line.clear(); //reuse
158
159 // return null if end of stream has been reached
160 if (lookaheadChar == END_OF_STREAM) {
161 return null;
162 }
163 // do we have a line termination already
164 char laChar = (char) lookaheadChar;
165 if (laChar == '\n' || laChar == '\r') {
166 lastChar = lookaheadChar;
167 lookaheadChar = super.read();
168 // ignore '\r\n' as well
169 if ((char) lookaheadChar == '\n') {
170 lastChar = lookaheadChar;
171 lookaheadChar = super.read();
172 }
173 lineCounter++;
174 return line.toString();
175 }
176
177 // create the rest-of-line return and update the lookahead
178 line.append(laChar);
179 String restOfLine = super.readLine(); // TODO involves copying
180 lastChar = lookaheadChar;
181 lookaheadChar = super.read();
182 if (restOfLine != null) {
183 line.append(restOfLine);
184 }
185 lineCounter++;
186 return line.toString();
187 }
188
189 /**
190 * Unsupported
191 */
192 @Override
193 public long skip(long n) throws IllegalArgumentException, IOException {
194 throw new UnsupportedOperationException("CSV has no reason to implement this");
195 }
196
197 /**
198 * Returns the next char in the stream without consuming it.
199 *
200 * Remember the next char read by read(..) will always be
201 * identical to lookAhead().
202 *
203 * @return the next char (without consuming it) or END_OF_STREAM
204 */
205 int lookAhead() throws IOException {
206 if (lookaheadChar == UNDEFINED) {
207 lookaheadChar = super.read();
208 }
209 return lookaheadChar;
210 }
211
212
213 /**
214 * Returns the nof line read
215 *
216 * @return the current-line-number (or -1)
217 */
218 int getLineNumber() {
219 return lineCounter > -1 ? lineCounter : -1;
220 }
221
222 /**
223 * Unsupported.
224 * @throws UnsupportedOperationException if invoked
225 */
226 @Override
227 public boolean markSupported() {
228 throw new UnsupportedOperationException("CSV has no reason to implement this");
229 }
230
231 }