/*
* Java CSV is a stream based library for reading and writing
* CSV and other delimited data.
*
* Copyright (C) Bruce Dunwiddie [email protected]
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* A stream based parser for parsing delimited text data from a file or a
* stream.
*/ public class CsvReader { private Reader inputStream = null;
private String fileName = null;
// this holds all the values for switches that the user is allowed to set private UserSettings userSettings = new UserSettings();
private Charset charset = null;
private boolean useCustomRecordDelimiter = false;
// this will be our working buffer to hold data chunks
// read in from the data file
private DataBuffer dataBuffer = new DataBuffer();
private ColumnBuffer columnBuffer = new ColumnBuffer();
private RawRecordBuffer rawBuffer = new RawRecordBuffer();
private boolean[] isQualified = null;
private String rawRecord = "";
private HeadersHolder headersHolder = new HeadersHolder();
// these are all more or less global loop variables
// to keep from needing to pass them all into various
// methods during parsing
private boolean startedColumn = false;
private boolean startedWithQualifier = false;
private boolean hasMoreData = true;
private char lastLetter = '\0';
private boolean hasReadNextLine = false;
private int columnsCount = 0;
private long currentRecord = 0;
private String[] values = new String[StaticSettings.INITIAL_COLUMN_COUNT];
private boolean initialized = false;
private boolean closed = false;
/**
* Double up the text qualifier to represent an occurance of the text
* qualifier.
*/ public static final int ESCAPE_MODE_DOUBLED = 1;
/**
* Use a backslash character before the text qualifier to represent an
* occurance of the text qualifier.
*/ public static final int ESCAPE_MODE_BACKSLASH = 2;
/**
* Creates a {@link com.csvreader.CsvReader CsvReader} object using a file
* as the data source.
*
* @param fileName
* The path to the file to use as the data source.
* @param delimiter
* The character to use as the column delimiter.
* @param charset
* The {@link java.nio.charset.Charset Charset} to use while
* parsing the data.
*/ public CsvReader(String fileName, char delimiter, Charset charset) throws FileNotFoundException { if (fileName == null) { throw new IllegalArgumentException(
"Parameter fileName can not be null.");
}
if (charset == null) { throw new IllegalArgumentException(
"Parameter charset can not be null.");
}
if (!new File(fileName).exists()) { throw new FileNotFoundException("File " + fileName
+ " does not exist.");
}
/**
* Creates a {@link com.csvreader.CsvReader CsvReader} object using a file
* as the data source. Uses ISO-8859-1 as the
* {@link java.nio.charset.Charset Charset}.
*
* @param fileName
* The path to the file to use as the data source.
* @param delimiter
* The character to use as the column delimiter.
*/ public CsvReader(String fileName, char delimiter) throws FileNotFoundException { this(fileName, delimiter, Charset.forName("ISO-8859-1"));
}
/**
* Creates a {@link com.csvreader.CsvReader CsvReader} object using a file
* as the data source. Uses a comma as the column delimiter and
* ISO-8859-1 as the {@link java.nio.charset.Charset Charset}.
*
* @param fileName
* The path to the file to use as the data source.
*/ public CsvReader(String fileName) throws FileNotFoundException { this(fileName, Letters.COMMA);
}
/**
* Constructs a {@link com.csvreader.CsvReader CsvReader} object using a
* {@link java.io.Reader Reader} object as the data source.
*
* @param inputStream
* The stream to use as the data source.
* @param delimiter
* The character to use as the column delimiter.
*/ public CsvReader(Reader inputStream, char delimiter) { if (inputStream == null) { throw new IllegalArgumentException(
"Parameter inputStream can not be null.");
}
/**
* Constructs a {@link com.csvreader.CsvReader CsvReader} object using a
* {@link java.io.Reader Reader} object as the data source. Uses a
* comma as the column delimiter.
*
* @param inputStream
* The stream to use as the data source.
*/ public CsvReader(Reader inputStream) { this(inputStream, Letters.COMMA);
}
/**
* Constructs a {@link com.csvreader.CsvReader CsvReader} object using an
* {@link java.io.InputStream InputStream} object as the data source.
*
* @param inputStream
* The stream to use as the data source.
* @param delimiter
* The character to use as the column delimiter.
* @param charset
* The {@link java.nio.charset.Charset Charset} to use while
* parsing the data.
*/ public CsvReader(InputStream inputStream, char delimiter, Charset charset) { this(new InputStreamReader(inputStream, charset), delimiter);
}
/**
* Constructs a {@link com.csvreader.CsvReader CsvReader} object using an
* {@link java.io.InputStream InputStream} object as the data
* source. Uses a comma as the column delimiter.
*
* @param inputStream
* The stream to use as the data source.
* @param charset
* The {@link java.nio.charset.Charset Charset} to use while
* parsing the data.
*/ public CsvReader(InputStream inputStream, Charset charset) { this(new InputStreamReader(inputStream, charset));
}
public boolean getCaptureRawRecord() { return userSettings.CaptureRawRecord;
}
public void setCaptureRawRecord(boolean captureRawRecord) {
userSettings.CaptureRawRecord = captureRawRecord;
}
public String getRawRecord() { return rawRecord;
}
/**
* Gets whether leading and trailing whitespace characters are being trimmed
* from non-textqualified column data. Default is true.
*
* @return Whether leading and trailing whitespace characters are being
* trimmed from non-textqualified column data.
*/ public boolean getTrimWhitespace() { return userSettings.TrimWhitespace;
}
/**
* Sets whether leading and trailing whitespace characters should be trimmed
* from non-textqualified column data or not. Default is true.
*
* @param trimWhitespace
* Whether leading and trailing whitespace characters should be
* trimmed from non-textqualified column data or not.
*/ public void setTrimWhitespace(boolean trimWhitespace) {
userSettings.TrimWhitespace = trimWhitespace;
}
/**
* Gets the character being used as the column delimiter. Default is comma,
* ','.
*
* @return The character being used as the column delimiter.
*/ public char getDelimiter() { return userSettings.Delimiter;
}
/**
* Sets the character to use as the column delimiter. Default is comma, ','.
*
* @param delimiter
* The character to use as the column delimiter.
*/ public void setDelimiter(char delimiter) {
userSettings.Delimiter = delimiter;
}
public char getRecordDelimiter() { return userSettings.RecordDelimiter;
}
/**
* Sets the character to use as the record delimiter.
*
* @param recordDelimiter
* The character to use as the record delimiter. Default is
* combination of standard end of line characters for Windows,
* Unix, or Mac.
*/ public void setRecordDelimiter(char recordDelimiter) {
useCustomRecordDelimiter = true;
userSettings.RecordDelimiter = recordDelimiter;
}
/**
* Gets the character to use as a text qualifier in the data.
*
* @return The character to use as a text qualifier in the data.
*/ public char getTextQualifier() { return userSettings.TextQualifier;
}
/**
* Sets the character to use as a text qualifier in the data.
*
* @param textQualifier
* The character to use as a text qualifier in the data.
*/ public void setTextQualifier(char textQualifier) {
userSettings.TextQualifier = textQualifier;
}
/**
* Whether text qualifiers will be used while parsing or not.
*
* @return Whether text qualifiers will be used while parsing or not.
*/ public boolean getUseTextQualifier() { return userSettings.UseTextQualifier;
}
/**
* Sets whether text qualifiers will be used while parsing or not.
*
* @param useTextQualifier
* Whether to use a text qualifier while parsing or not.
*/ public void setUseTextQualifier(boolean useTextQualifier) {
userSettings.UseTextQualifier = useTextQualifier;
}
/**
* Gets the character being used as a comment signal.
*
* @return The character being used as a comment signal.
*/ public char getComment() { return userSettings.Comment;
}
/**
* Sets the character to use as a comment signal.
*
* @param comment
* The character to use as a comment signal.
*/ public void setComment(char comment) {
userSettings.Comment = comment;
}
/**
* Gets whether comments are being looked for while parsing or not.
*
* @return Whether comments are being looked for while parsing or not.
*/ public boolean getUseComments() { return userSettings.UseComments;
}
/**
* Sets whether comments are being looked for while parsing or not.
*
* @param useComments
* Whether comments are being looked for while parsing or not.
*/ public void setUseComments(boolean useComments) {
userSettings.UseComments = useComments;
}
/**
* Gets the current way to escape an occurance of the text qualifier inside
* qualified data.
*
* @return The current way to escape an occurance of the text qualifier
* inside qualified data.
*/ public int getEscapeMode() { return userSettings.EscapeMode;
}
/**
* Sets the current way to escape an occurance of the text qualifier inside
* qualified data.
*
* @param escapeMode
* The way to escape an occurance of the text qualifier inside
* qualified data.
* @exception IllegalArgumentException
* When an illegal value is specified for escapeMode.
*/ public void setEscapeMode(int escapeMode) throws IllegalArgumentException { if (escapeMode != ESCAPE_MODE_DOUBLED
&& escapeMode != ESCAPE_MODE_BACKSLASH) { throw new IllegalArgumentException(
"Parameter escapeMode must be a valid value.");
}
userSettings.EscapeMode = escapeMode;
}
public boolean getSkipEmptyRecords() { return userSettings.SkipEmptyRecords;
}
public void setSkipEmptyRecords(boolean skipEmptyRecords) {
userSettings.SkipEmptyRecords = skipEmptyRecords;
}
/**
* Safety caution to prevent the parser from using large amounts of memory
* in the case where parsing settings like file encodings don't end up
* matching the actual format of a file. This switch can be turned off if
* the file format is known and tested. With the switch off, the max column
* lengths and max column count per record supported by the parser will
* greatly increase. Default is true.
*
* @return The current setting of the safety switch.
*/ public boolean getSafetySwitch() { return userSettings.SafetySwitch;
}
/**
* Safety caution to prevent the parser from using large amounts of memory
* in the case where parsing settings like file encodings don't end up
* matching the actual format of a file. This switch can be turned off if
* the file format is known and tested. With the switch off, the max column
* lengths and max column count per record supported by the parser will
* greatly increase. Default is true.
*
* @param safetySwitch
*/ public void setSafetySwitch(boolean safetySwitch) {
userSettings.SafetySwitch = safetySwitch;
}
/**
* Gets the count of columns found in this record.
*
* @return The count of columns found in this record.
*/ public int getColumnCount() { return columnsCount;
}
/**
* Gets the index of the current record.
*
* @return The index of the current record.
*/ public long getCurrentRecord() { return currentRecord - 1;
}
/**
* Gets the count of headers read in by a previous call to
* {@link com.csvreader.CsvReader#readHeaders readHeaders()}.
*
* @return The count of headers read in by a previous call to
* {@link com.csvreader.CsvReader#readHeaders readHeaders()}.
*/ public int getHeaderCount() { return headersHolder.Length;
}
/**
* Returns the header values as a string array.
*
* @return The header values as a String array.
* @exception IOException
* Thrown if this object has already been closed.
*/ public String[] getHeaders() throws IOException {
checkClosed();
if (headersHolder.Headers == null) { return null;
} else {
// use clone here to prevent the outside code from
// setting values on the array directly, which would
// throw off the index lookup based on header name
String[] clone = new String[headersHolder.Length];
System.arraycopy(headersHolder.Headers, 0, clone, 0,
headersHolder.Length); return clone;
}
}
public void setHeaders(String[] headers) {
headersHolder.Headers = headers;
// use headersHolder.Length here in case headers is null for (int i = 0; i < headersHolder.Length; i++) {
headersHolder.IndexByName.put(headers[i], Integer.valueOf(i));
}
}
public String[] getValues() throws IOException {
checkClosed();
// need to return a clone, and can't use clone because values.Length
// might be greater than columnsCount
String[] clone = new String[columnsCount];
System.arraycopy(values, 0, clone, 0, columnsCount); return clone;
}
/**
* Returns the current column value for a given column index.
*
* @param columnIndex
* The index of the column.
* @return The current column value.
* @exception IOException
* Thrown if this object has already been closed.
*/ public String get(int columnIndex) throws IOException {
checkClosed();
/**
* Returns the current column value for a given column header name.
*
* @param headerName
* The header name of the column.
* @return The current column value.
* @exception IOException
* Thrown if this object has already been closed.
*/ public String get(String headerName) throws IOException {
checkClosed();
return get(getIndex(headerName));
}
/**
* Creates a {@link com.csvreader.CsvReader CsvReader} object using a string
* of data as the source. Uses ISO-8859-1 as the
* {@link java.nio.charset.Charset Charset}.
*
* @param data
* The String of data to use as the source.
* @return A {@link com.csvreader.CsvReader CsvReader} object using the
* String of data as the source.
*/ public static CsvReader parse(String data) { if (data == null) { throw new IllegalArgumentException(
"Parameter data can not be null.");
}
return new CsvReader(new StringReader(data));
}
/**
* Reads another record.
*
* @return Whether another record was successfully read or not.
* @exception IOException
* Thrown if an error occurs while reading data from the
* source stream.
*/ public boolean readRecord() throws IOException {
checkClosed();
columnsCount = 0;
rawBuffer.Position = 0;
dataBuffer.LineStart = dataBuffer.Position;
hasReadNextLine = false;
// check to see if we've already found the end of data
if (hasMoreData) {
// loop over the data stream until the end of data is found
// or the end of the record is found
do { if (dataBuffer.Position == dataBuffer.Count) {
checkDataLength();
} else {
startedWithQualifier = false;
if (userSettings.UseTextQualifier
&& currentLetter == userSettings.TextQualifier) {
// this will be a text qualified column, so
// we need to set startedWithQualifier to make it
// enter the seperate branch to handle text
// qualified columns
if (userSettings.EscapeMode == ESCAPE_MODE_DOUBLED) {
lastLetterWasEscape = true;
}
lastLetterWasQualifier = true;
}
} else if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH
&& lastLetterWasEscape) { switch (currentLetter) { case 'n':
appendLetter(Letters.LF); break; case 'r':
appendLetter(Letters.CR); break; case 't':
appendLetter(Letters.TAB); break; case 'b':
appendLetter(Letters.BACKSPACE); break; case 'f':
appendLetter(Letters.FORM_FEED); break; case 'e':
appendLetter(Letters.ESCAPE); break; case 'v':
appendLetter(Letters.VERTICAL_TAB); break; case 'a':
appendLetter(Letters.ALERT); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
escape = ComplexEscape.OCTAL;
readingComplexEscape = true;
escapeLength = 1;
escapeValue = (char) (currentLetter - '0');
dataBuffer.ColumnStart = dataBuffer.Position + 1; break; case 'u': case 'x': case 'o': case 'd': case 'U': case 'X': case 'O': case 'D': switch (currentLetter) { case 'u': case 'U':
escape = ComplexEscape.UNICODE; break; case 'x': case 'X':
escape = ComplexEscape.HEX; break; case 'o': case 'O':
escape = ComplexEscape.OCTAL; break; case 'd': case 'D':
escape = ComplexEscape.DECIMAL; break;
}
throw new IOException(
"Maximum column length of 100,000 exceeded in column "
+ NumberFormat
.getIntegerInstance()
.format(
columnsCount)
+ " in record "
+ NumberFormat
.getIntegerInstance()
.format(
currentRecord)
+ ". Set the SafetySwitch property to false"
+ " if you're expecting column lengths greater than 100,000 characters to"
+ " avoid this error.");
}
}
} // end else
} while (hasMoreData && startedColumn);
} else if (currentLetter == userSettings.Delimiter) {
// we encountered a column with no data, so
// just send the end column
lastLetter = currentLetter;
endColumn();
} else if (useCustomRecordDelimiter
&& currentLetter == userSettings.RecordDelimiter) {
// this will skip blank lines if (startedColumn || columnsCount > 0
|| !userSettings.SkipEmptyRecords) {
endColumn();
lastLetter = currentLetter;
} else if (userSettings.UseComments && columnsCount == 0
&& currentLetter == userSettings.Comment) {
// encountered a comment character at the beginning of
// the line so just ignore the rest of the line
lastLetter = currentLetter;
skipLine();
} else if (userSettings.TrimWhitespace
&& (currentLetter == Letters.SPACE || currentLetter == Letters.TAB)) {
// do nothing, this will trim leading whitespace
// for both text qualified columns and non
startedColumn = true;
dataBuffer.ColumnStart = dataBuffer.Position + 1;
} else {
// since the letter wasn't a special letter, this
// will be the first letter of our current column
do { if (!firstLoop
&& dataBuffer.Position == dataBuffer.Count) {
checkDataLength();
} else { if (!firstLoop) {
// grab the current letter as a char
currentLetter = dataBuffer.Buffer[dataBuffer.Position];
}
if (escapeLength == 3) {
readingComplexEscape = false;
}
break; case ComplexEscape.HEX:
escapeValue *= (char) 16;
escapeValue += hexToDec(currentLetter);
if (escapeLength == 2) {
readingComplexEscape = false;
}
break;
}
if (!readingComplexEscape) {
appendLetter(escapeValue);
} else {
dataBuffer.ColumnStart = dataBuffer.Position + 1;
}
} else if (userSettings.EscapeMode == ESCAPE_MODE_BACKSLASH
&& lastLetterWasBackslash) { switch (currentLetter) { case 'n':
appendLetter(Letters.LF); break; case 'r':
appendLetter(Letters.CR); break; case 't':
appendLetter(Letters.TAB); break; case 'b':
appendLetter(Letters.BACKSPACE); break; case 'f':
appendLetter(Letters.FORM_FEED); break; case 'e':
appendLetter(Letters.ESCAPE); break; case 'v':
appendLetter(Letters.VERTICAL_TAB); break; case 'a':
appendLetter(Letters.ALERT); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
escape = ComplexEscape.OCTAL;
readingComplexEscape = true;
escapeLength = 1;
escapeValue = (char) (currentLetter - '0');
dataBuffer.ColumnStart = dataBuffer.Position + 1; break; case 'u': case 'x': case 'o': case 'd': case 'U': case 'X': case 'O': case 'D': switch (currentLetter) { case 'u': case 'U':
escape = ComplexEscape.UNICODE; break; case 'x': case 'X':
escape = ComplexEscape.HEX; break; case 'o': case 'O':
escape = ComplexEscape.OCTAL; break; case 'd': case 'D':
escape = ComplexEscape.DECIMAL; break;
}
throw new IOException(
"Maximum column length of 100,000 exceeded in column "
+ NumberFormat
.getIntegerInstance()
.format(
columnsCount)
+ " in record "
+ NumberFormat
.getIntegerInstance()
.format(
currentRecord)
+ ". Set the SafetySwitch property to false"
+ " if you're expecting column lengths greater than 100,000 characters to"
+ " avoid this error.");
}
}
} // end else
} while (hasMoreData && startedColumn);
}
if (hasMoreData) {
dataBuffer.Position++;
}
} // end else
} while (hasMoreData && !hasReadNextLine);
// check to see if we hit the end of the file
// without processing the current record
if (startedColumn || lastLetter == userSettings.Delimiter) {
endColumn();
endRecord();
}
}
if (userSettings.CaptureRawRecord) { if (hasMoreData) { if (rawBuffer.Position == 0) {
rawRecord = new String(dataBuffer.Buffer,
dataBuffer.LineStart, dataBuffer.Position
- dataBuffer.LineStart - 1);
} else {
rawRecord = new String(rawBuffer.Buffer, 0,
rawBuffer.Position)
+ new String(dataBuffer.Buffer,
dataBuffer.LineStart, dataBuffer.Position
- dataBuffer.LineStart - 1);
}
} else {
// for hasMoreData to ever be false, all data would have had to
// have been
// copied to the raw buffer
rawRecord = new String(rawBuffer.Buffer, 0, rawBuffer.Position);
}
} else {
rawRecord = "";
}
return hasReadNextLine;
}
/**
* @exception IOException
* Thrown if an error occurs while reading data from the
* source stream.
*/ private void checkDataLength() throws IOException { if (!initialized) { if (fileName != null) {
inputStream = new BufferedReader(new InputStreamReader( new FileInputStream(fileName), charset),
StaticSettings.MAX_FILE_BUFFER_SIZE);
}
charset = null;
initialized = true;
}
updateCurrentValue();
if (userSettings.CaptureRawRecord && dataBuffer.Count > 0) { if (rawBuffer.Buffer.length - rawBuffer.Position < dataBuffer.Count
- dataBuffer.LineStart) { int newLength = rawBuffer.Buffer.length
+ Math.max(dataBuffer.Count - dataBuffer.LineStart,
rawBuffer.Buffer.length);
/**
* Read the first record of data as column headers.
*
* @return Whether the header record was successfully read or not.
* @exception IOException
* Thrown if an error occurs while reading data from the
* source stream.
*/ public boolean readHeaders() throws IOException { boolean result = readRecord();
// copy the header data from the column array
// to the header string array
headersHolder.Length = columnsCount;
headersHolder.Headers = new String[columnsCount];
for (int i = 0; i < headersHolder.Length; i++) {
String columnValue = get(i);
headersHolder.Headers[i] = columnValue;
// if there are duplicate header names, we will save the last one
headersHolder.IndexByName.put(columnValue, Integer.valueOf(i));
}
if (result) {
currentRecord--;
}
columnsCount = 0;
return result;
}
/**
* Returns the column header value for a given column index.
*
* @param columnIndex
* The index of the header column being requested.
* @return The value of the column header at the given column index.
* @exception IOException
* Thrown if this object has already been closed.
*/ public String getHeader(int columnIndex) throws IOException {
checkClosed();
// check to see if we have read the header record yet
// check to see if the column index is within the bounds
// of our header array
if (columnIndex > -1 && columnIndex < headersHolder.Length) {
// return the processed header data for this column
/**
* @exception IOException
* Thrown if a very rare extreme exception occurs during
* parsing, normally resulting from improper data format.
*/ private void endColumn() throws IOException {
String currentValue = "";
// must be called before setting startedColumn = false if (startedColumn) { if (columnBuffer.Position == 0) { if (dataBuffer.ColumnStart < dataBuffer.Position) { int lastLetter = dataBuffer.Position - 1;
currentValue = new String(columnBuffer.Buffer, 0,
lastLetter + 1);
}
}
columnBuffer.Position = 0;
startedColumn = false;
if (columnsCount >= 100000 && userSettings.SafetySwitch) {
close();
throw new IOException(
"Maximum column count of 100,000 exceeded in record "
+ NumberFormat.getIntegerInstance().format(
currentRecord)
+ ". Set the SafetySwitch property to false"
+ " if you're expecting more than 100,000 columns per record to"
+ " avoid this error.");
}
// check to see if our current holder array for
// column chunks is still big enough to handle another
// column chunk
if (columnsCount == values.length) {
// holder array needs to grow to be able to hold another column int newLength = values.length * 2;
/**
* @exception IOException
* Thrown if an error occurs while reading data from the
* source stream.
*/ private void endRecord() throws IOException {
// this flag is used as a loop exit condition
// during parsing
hasReadNextLine = true;
currentRecord++;
}
/**
* Gets the corresponding column index for a given column header name.
*
* @param headerName
* The header name of the column.
* @return The column index for the given column header name. Returns
* -1 if not found.
* @exception IOException
* Thrown if this object has already been closed.
*/ public int getIndex(String headerName) throws IOException {
checkClosed();
/**
* Skips the next record of data by parsing each column. Does not
* increment
* {@link com.csvreader.CsvReader#getCurrentRecord getCurrentRecord()}.
*
* @return Whether another record was successfully skipped or not.
* @exception IOException
* Thrown if an error occurs while reading data from the
* source stream.
*/ public boolean skipRecord() throws IOException {
checkClosed();
boolean recordRead = false;
if (hasMoreData) {
recordRead = readRecord();
if (recordRead) {
currentRecord--;
}
}
return recordRead;
}
/**
* Skips the next line of data using the standard end of line characters and
* does not do any column delimited parsing.
*
* @return Whether a line was successfully skipped or not.
* @exception IOException
* Thrown if an error occurs while reading data from the
* source stream.
*/ public boolean skipLine() throws IOException {
checkClosed();
// clear public column values for current line
columnsCount = 0;
boolean skippedLine = false;
if (hasMoreData) { boolean foundEol = false;
do { if (dataBuffer.Position == dataBuffer.Count) {
checkDataLength();
} else {
skippedLine = true;
try { if (initialized) {
inputStream.close();
}
} catch (Exception e) {
// just eat the exception
}
inputStream = null;
closed = true;
}
}
/**
* @exception IOException
* Thrown if this object has already been closed.
*/ private void checkClosed() throws IOException { if (closed) { throw new IOException(
"This instance of the CsvReader class has already been closed.");
}
}
if (hex >= 'a') {
result = (char) (hex - 'a' + 10);
} else if (hex >= 'A') {
result = (char) (hex - 'A' + 10);
} else {
result = (char) (hex - '0');
}
return result;
}
private class DataBuffer { public char[] Buffer;
public int Position;
// / <summary>
// / How much usable data has been read into the stream,
// / which will not always be as long as Buffer.Length.
// / </summary> public int Count;
// / <summary>
// / The position of the cursor in the buffer when the
// / current column was started or the last time data
// / was moved out to the column buffer.
// / </summary> public int ColumnStart;
public int LineStart;
public DataBuffer() {
Buffer = new char[StaticSettings.MAX_BUFFER_SIZE];
Position = 0;
Count = 0;
ColumnStart = 0;
LineStart = 0;
}
}
private class ColumnBuffer { public char[] Buffer;
public int Position;
public ColumnBuffer() {
Buffer = new char[StaticSettings.INITIAL_COLUMN_BUFFER_SIZE];
Position = 0;
}
}
private class RawRecordBuffer { public char[] Buffer;
public int Position;
public RawRecordBuffer() {
Buffer = new char[StaticSettings.INITIAL_COLUMN_BUFFER_SIZE
* StaticSettings.INITIAL_COLUMN_COUNT];
Position = 0;
}
}
private class Letters { public static final char LF = '\n';
public static final char CR = '\r';
public static final char QUOTE = '"';
public static final char COMMA = ',';
public static final char SPACE = ' ';
public static final char TAB = '\t';
public static final char POUND = '#';
public static final char BACKSLASH = '\\';
public static final char NULL = '\0';
public static final char BACKSPACE = '\b';
public static final char FORM_FEED = '\f';
public static final char ESCAPE = '\u001B'; // ASCII/ANSI escape
public static final char VERTICAL_TAB = '\u000B';
public static final char ALERT = '\u0007';
}
private class UserSettings {
// having these as publicly accessible members will prevent
// the overhead of the method call that exists on properties public boolean CaseSensitive;
private class HeadersHolder { public String[] Headers;
public int Length;
public HashMap<String, Integer> IndexByName;
public HeadersHolder() {
Headers = null;
Length = 0;
IndexByName = new HashMap<String, Integer>();
}
}
private class StaticSettings {
// these are static instead of final so they can be changed in unit test
// isn't visible outside this class and is only accessed once during
// CsvReader construction public static final int MAX_BUFFER_SIZE = 1024;
public static final int MAX_FILE_BUFFER_SIZE = 4 * 1024;
public static final int INITIAL_COLUMN_COUNT = 10;
public static final int INITIAL_COLUMN_BUFFER_SIZE = 50;
}
}