CsvScanner.java
package de.siegmar.fastcsv.reader;
import static de.siegmar.fastcsv.util.Util.CR;
import static de.siegmar.fastcsv.util.Util.LF;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
final class CsvScanner {
private final byte fieldSeparator;
private final byte quoteCharacter;
private final byte commentCharacter;
private final CsvListener csvListener;
private final ByteChannelStream stream;
private final boolean readComments;
CsvScanner(final ReadableByteChannel channel, final int bomHeaderLength, final byte fieldSeparator,
final byte quoteCharacter, final CommentStrategy commentStrategy, final byte commentCharacter,
final CsvListener csvListener) throws IOException {
this.fieldSeparator = fieldSeparator;
this.quoteCharacter = quoteCharacter;
this.commentCharacter = commentCharacter;
this.csvListener = csvListener;
readComments = commentStrategy != CommentStrategy.NONE;
stream = new ByteChannelStream(channel, csvListener);
if (bomHeaderLength > 0) {
for (int i = 0; i < bomHeaderLength; i++) {
stream.get();
}
}
}
@SuppressWarnings({"PMD.AssignmentInOperand", "checkstyle:CyclomaticComplexity",
"checkstyle:NestedIfDepth"})
void scan() throws IOException {
int d;
while ((d = stream.get()) != -1) {
csvListener.startOffset(stream.getOffset());
// parse a record
if (d == commentCharacter && readComments) {
consumeCommentedLine();
} else {
consumeRecord(d);
}
csvListener.onReadRecord();
}
}
@SuppressWarnings({"PMD.AvoidReassigningParameters", "checkstyle:FinalParameters",
"checkstyle:ParameterAssignment"})
private void consumeRecord(int d) throws IOException {
do {
// parse fields
if (d == quoteCharacter) {
if (consumeQuotedField()) {
// reached the end of record
break;
}
} else if (consumeUnquotedField(d)) {
// reached the end of record
break;
}
} while ((d = stream.get()) != -1);
}
@SuppressWarnings("PMD.AssignmentInOperand")
private boolean consumeQuotedField() throws IOException {
int d;
while ((d = stream.get()) != -1) {
if (d == quoteCharacter) {
if (!stream.consumeIfNextEq(quoteCharacter)) {
break;
}
} else if (d == CR) {
stream.consumeIfNextEq(LF);
csvListener.additionalLine();
} else if (d == LF) {
csvListener.additionalLine();
}
}
// handle all kinds of characters after closing quote
return stream.hasData() && consumeUnquotedField(stream.get());
}
@SuppressWarnings({"PMD.AvoidReassigningParameters", "checkstyle:FinalParameters",
"checkstyle:ParameterAssignment"})
private boolean consumeUnquotedField(int d) throws IOException {
do {
if (d == fieldSeparator) {
return false;
} else if (d == CR) {
stream.consumeIfNextEq(LF);
break;
} else if (d == LF) {
break;
}
} while ((d = stream.get()) != -1);
return true;
}
@SuppressWarnings("PMD.AssignmentInOperand")
private void consumeCommentedLine() throws IOException {
int d;
while ((d = stream.get()) != -1) {
if (d == CR) {
stream.consumeIfNextEq(LF);
break;
} else if (d == LF) {
break;
}
}
}
public interface CsvListener {
void onReadBytes(int readCnt);
void startOffset(long offset);
void onReadRecord();
void additionalLine();
}
}