/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.csv;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
/**
* ExtendedBufferedReader
*
* A special reader decorator which supports more
* sophisticated access to the underlying reader object.
*
* In particular the reader supports a look-ahead option,
* which allows you to see the next char returned by
* next().
*/
class ExtendedBufferedReader extends BufferedReader {
/** The end of stream symbol */
static final int END_OF_STREAM = -1;
/** Undefined state for the lookahead char */
static final int UNDEFINED = -2;
/** The last char returned */
private int lastChar = UNDEFINED;
/** The line counter */
private int lineCounter = 0;
/**
* Created extended buffered reader using default buffer-size
*/
ExtendedBufferedReader(Reader r) {
super(r);
}
@Override
public int read() throws IOException {
int current = super.read();
if (current == '\r' || (current == '\n' && lastChar != '\r')) {
lineCounter++;
}
lastChar = current;
return lastChar;
}
/**
* Returns the last character that was read as an integer (0 to 65535). This
* will be the last character returned by any of the read methods. This will
* not include a character read using the {@link #peek()} method. If no
* character has been read then this will return {@link #UNDEFINED}. If the
* end of the stream was reached on the last read then this will return
* {@link #END_OF_STREAM}.
*
* @return the last character that was read
*/
int readAgain() {
return lastChar;
}
@Override
public int read(char[] buf, int offset, int length) throws IOException {
if (length == 0) {
return 0;
}
int len = super.read(buf, offset, length);
if (len > 0) {
for (int i = offset; i < offset + len; i++) {
char ch = buf[i];
if (ch == '\n') {
if ('\r' != (i > 0 ? buf[i-1]: lastChar)) {
lineCounter++;
}
} else if (ch == '\r') {
lineCounter++;
}
}
lastChar = buf[offset + len - 1];
} else if (len == -1) {
lastChar = END_OF_STREAM;
}
return len;
}
/**
* Calls {@link BufferedReader#readLine()} which drops the line terminator(s).
* This method should only be called when processing a comment, otherwise
* information can be lost.
* <p>
* Increments {@link #lineCounter}
* <p>
* Sets {@link #lastChar} to {@link #END_OF_STREAM} at EOF,
* otherwise to LF
*
* @return the line that was read, or null if reached EOF.
*/
@Override
public String readLine() throws IOException {
String line = super.readLine();
if (line != null) {
lastChar = '\n'; // needed for detecting start of line
lineCounter++;
} else {
lastChar = END_OF_STREAM;
}
return line;
}
/**
* Returns the next character in the current reader without consuming it. So
* the next call to {@link #read()} will still return this value.
*
* @return the next character
*
* @throws IOException if there is an error in reading
*/
int lookAhead() throws IOException {
super.mark(1);
int c = super.read();
super.reset();
return c;
}
/**
* Returns the number of lines read
*
* @return the current-line-number (or -1)
*/
int getLineNumber() {
return lineCounter;
}
}