All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uno.informatics.common.io.text.TextFileRowReader Maven / Gradle / Ivy

There is a newer version: 1.5.0
Show newest version
/*******************************************************************************
 * Copyright 2016 Guy Davenport
 *
 * Licensed 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 uno.informatics.common.io.text;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.regex.Pattern;

import uno.informatics.common.ConversionException;
import uno.informatics.common.ConversionUtilities;
import uno.informatics.common.io.RowReader;
import uno.informatics.data.DataTypeConstants;

public class TextFileRowReader extends AbstractTextFileHandler implements RowReader {
    
    
    /**
     * Sets no options, all options are set to false
     */
    public static final int NO_OPTIONS = 0 ;
    
    /**
     * Sets if two more more delimiters are encountered together if these should
     * be treated as one delimiters
     */
    
    /**
     * Sets if the reader should parse empty strings.
     */
    public static final int PARSE_EMPTY_STRINGS = 1 ;
    
    /**
     * Sets if the reader should attempt to convert values.
     */
    public static final int CONVERT_VALUES = 2 ;
  
    /**
     * Sets if two more more delimiters are encountered together if these should
     * be treated as one delimiters
     */
    public static final int IGNORE_MULTIPLE_DELIMITERS = 4 ;
    
    /**
     * Sets if rows are adjusted to be all the same size as the first row
     */
    public static final int ROWS_SAME_SIZE = 8 ;

    /**
     * Sets if the reader remove any prefix or suffix white space for Strings
     */
    public static final int REMOVE_WHITE_SPACE = 16 ;
    
    private int options = NO_OPTIONS ;

    private Map conversionTypesMap;

    private int conversionTypesCount;

    private int[] conversionTypesArray;

    private int defaultConversionTypes;

    private Pattern pattern;

    private BufferedReader bufferedReader;

    private String[] line;
    private String[] nextLine;

    private int defaultInt;

    private double defaultDouble;

    private boolean defaultBoolean;

    private static final String BUFFERREADER_NULL = "Buffer reader is undefined";

    private TextFileRowReader() {
        conversionTypesArray = new int[0];
    }

    /**
     * Constructs an initialised reader using a string reference to a text file.
     * 
     * @param reference
     *            a text file name or URL
     * @throws FileNotFoundException
     *             if the file to read is not found
     * @throws IOException
     *             if an I/O error occurs
     */
    public TextFileRowReader(String reference) throws IOException, FileNotFoundException {
        this();

        if (reference == null)
            throw new FileNotFoundException("File undefined");

        setFileReference(reference);

        initialise();
    }

    /**
     * Constructs an initialised reader using a file.
     * 
     * @param path the Path to file to be read.
     * @throws FileNotFoundException
     *             if the file to read is not found
     * @throws IOException
     *             if an I/O error occurs
     */
    public TextFileRowReader(Path path) throws IOException, FileNotFoundException {
        this();

        if (path == null)
            throw new FileNotFoundException("Path undefined");

        setPath(path);

        initialise();
    }

    public TextFileRowReader(BufferedReader bufferedReader) throws IOException {
        this();

        if (bufferedReader != null)
            this.bufferedReader = bufferedReader;
        else
            throw new IOException("Buffered reader undefined");

        initialise();
    }

    /**
     * Check to see if the reader is ready to be used and if additional cells
     * can still be read
     *
     * @return true if the reader is ready to be used and if
     *         additional cells can still be read, false otherwise
     */
    public final boolean ready() {
        try {
            if (bufferedReader != null) {
                return bufferedReader.ready();
            } else {
                return false;
            }
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * Close the reader, disposing of any internal resources
     */
    public final void close() {
        try {
            if (bufferedReader != null)
                bufferedReader.close();
        } catch (IOException e) {

        }

        bufferedReader = null;
    }

    /**
     * Gets an int representing a bit array of options
     * 
     * @return an int preresenting a bit array of options
     */
    public final int getOptions() {
        return options;
    }

    /**
     * Sets an int representing a bit array of options
     * 
     * @param options an int preresenting a bit array of options
     */
    public final void setOptions(int options) throws IOException {
        if (options != this.options) {
            if (isInUse())
                throw new IOException("Options can not be changed while reader is in use");

            this.options = options;
            
            updatePattern(); 
        }
    }

    /**
     * Sets the delimiter string.
     * 
     * @param delimiter
     *            the delimiter string
     * @exception IOException
     *                if the reader is already is use
     */
    public final synchronized void setDelimiterString(String delimiter) throws IOException {
        super.setDelimiterString(delimiter);

        updatePattern();
    }

    @Override
    public final Object[][] readCellsAsArray() throws IOException {
        List rows = new LinkedList();

        while (nextRow()) {
            rows.add(getRowCellsAsArray());
        }

        Object[][] cells = new Object[rows.size()][];

        Iterator iterator = rows.iterator();

        int i = 0;

        while (iterator.hasNext()) {
            cells[i] = iterator.next();
            ++i;
        }

        return cells;
    }

    @Override
    public final List> readCells() throws IOException {
        List> cells = new LinkedList>();

        while (nextRow()) {
            cells.add(getRowCells());
        }

        return cells;
    }

    @Override
    public final boolean hasNextRow() {
        if (nextLine != null) {
            return true;
        } else {
            if (ready() && this.getRowPosition() < 0) {
                try {
                    readNextLine();

                    return true;
                } catch (IOException e) {
                    return false;
                }

            } else {
                return false;
            }
        }
    }

    @Override
    public final boolean nextRow() throws IOException {
        if (hasNextRow()) {
            readNextLine();
            incrementRowIndex();
            updateRowSize(line != null ? line.length : 0);

            return true;
        } else {
            return false;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see uno.informatics.common.io.TableReader#hasNextColumn()
     */
    @Override
    public boolean hasNextColumn() {
        return getColumnIndex() + 1 < getRowSize();
    }

    /*
     * (non-Javadoc)
     * 
     * @see uno.informatics.common.io.TableReader#nextColumn()
     */
    @Override
    public boolean nextColumn() throws IOException {
        if (hasNextColumn()) {
            incrementColumnIndex();

            return true;
        } else {
            return false;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see uno.informatics.common.io.TableReader#getCell()
     */
    @Override
    public Object getCell() throws IOException {
        return parseCell(getColumnIndex());
    }

    @Override
    public String getCellAsString() throws IOException {
        return parseCellAsString(getColumnIndex());
    }

    @Override
    public double getCellAsDouble() throws IOException {
        return parseCellAsDouble(getColumnIndex());
    }

    @Override
    public int getCellAsInt() throws IOException {
        return parseCellAsInt(getColumnIndex());
    }

    @Override
    public boolean getCellAsBoolean() throws IOException {
        return parseCellAsBoolean(getColumnIndex());
    }

    @Override
    public final List getRowCells() throws IOException {
        if (this.getRowIndex() < 0)
            throw new IOException("Reader before first row!");

        return parseRowCells(getColumnIndex(), getRowSize());
    }

    @Override
    public final List getRowCellsAsString() throws IOException {
        if (this.getRowIndex() < 0)
            throw new IOException("Reader before first row!");

        return parseRowCellsAsString(getColumnIndex(), getRowSize());
    }

    @Override
    public final List getRowCellsAsInt() throws IOException {
        if (this.getRowIndex() < 0)
            throw new IOException("Reader before first row!");

        return parseRowCellsAsInteger(getColumnIndex(), getRowSize());
    }

    @Override
    public final List getRowCellsAsDouble() throws IOException {
        if (this.getRowIndex() < 0)
            throw new IOException("Reader before first row!");

        return parseRowCellsAsDouble(getColumnIndex(), getRowSize());
    }

    @Override
    public final List getRowCellsAsBoolean() throws IOException {
        if (this.getRowIndex() < 0)
            throw new IOException("Reader before first row!");

        return parseRowCellsAsBoolean(getColumnIndex(), getRowSize());
    }

    @Override
    public final Object[] getRowCellsAsArray() throws IOException {
        return parseRowCellsAsArray(getColumnIndex(), getRowSize());
    }

    @Override
    public final String[] getRowCellsAsStringArray() throws IOException {
        return parseRowCellsAsStringArray(getColumnIndex(), getRowSize());
    }

    @Override
    public final int[] getRowCellsAsIntArray() throws IOException {
        return parseRowCellsAsIntArray(getColumnIndex(), getRowSize());
    }

    @Override
    public final double[] getRowCellsAsDoubleArray() throws IOException {
        return parseRowCellsAsDoubleArray(getColumnIndex(), getRowSize());
    }

    @Override
    public final boolean[] getRowCellsAsBooleanArray() throws IOException {
        return parseRowCellsAsBooleanArray(getColumnIndex(), getRowSize());
    }

    protected Object parseValue(String text, int rowIndex, int columnIndex) throws IOException {
        try {
            return convertValue(text);
        } catch (ClassCastException e) {
            throw new IOException("Can not parse cell position " + rowIndex + "," + columnIndex + " due to "
                    + e.getLocalizedMessage(), e);
        }
    }

    protected Object parseValue(String text, int rowIndex, int columnIndex, int conversionTypes) throws IOException {
        try {
            return convertValue(text, conversionTypes);
        } catch (Exception e) {
            throw new IOException("Can not parse cell position " + rowIndex + "," + columnIndex + " due to "
                    + e.getLocalizedMessage(), e);
        }
    }

    protected Object convertValue(String text) {
        return ConversionUtilities.convertToObject(text);
    }

    protected Object convertValue(String text, int dataTypes) throws ConversionException {
        return ConversionUtilities.convertToObject(text, dataTypes);
    }

    /**
     * Initialises the reader.
     * 
     * @throws FileNotFoundException
     *             if the file to read is not found
     * @throws IOException
     *             if an I/O error occurs
     */
    protected final void initialise() throws FileNotFoundException, IOException {
        super.initialise();

        defaultConversionTypes = DataTypeConstants.DEFAULT_TYPE_IDS;

        conversionTypesMap = new TreeMap();

        conversionTypesCount = 0;

        conversionTypesArray = null;

        updatePattern();

        if (getPathReference() != null)
            initialiseBufferedReader(getBufferReader(getPathReference()));
        else if (getPath() != null)
            initialiseBufferedReader(getBufferReader(getPath()));
        else if (bufferedReader != null)
            initialiseBufferedReader(bufferedReader);
        else
            throw new IOException("Unable to initialise reader");
    }

    /**
     * Reads the next valid line if possible into memory
     * 
     * @throws IOException
     */
    private void readNextLine() throws IOException {
        line = nextLine;
        nextLine = null;
        setColumnIndex(-1);

        while (bufferedReader.ready() && nextLine == null) {
            nextLine = readLine();

            incrementRowPosition();
        }
    }

    /**
     * @return a tokenised version of the next line, or null if the next
     *         line is a comment or an empty line when not in strict mode
     * 
     * @throws IOException
     */
    private String[] readLine() throws IOException {
        String line = bufferedReader.readLine();

        // ignore any commented record or empty lines if not in strict mode
        if (line != null && ((line.trim().length() == 0 && isInStrictMode())
                || (getCommentString() != null && line.trim().startsWith(getCommentString())))) {
            return null;
        } else {
            if (hasOption(IGNORE_MULTIPLE_DELIMITERS))
                return pattern.split(line);
            else
                return pattern.split(line, -1);
        }
    }

    /**
     * Initialises the reader using a bufferedReader directly.
     * 
     * @param bufferedReader
     *            a buffered reader
     */
    private final void initialiseBufferedReader(BufferedReader bufferedReader) {
        if (bufferedReader == null)
            throw new NullPointerException(BUFFERREADER_NULL);

        this.bufferedReader = bufferedReader;
    }

    private final void updatePattern() {
        // TODO need to check for special characters
        pattern = Pattern.compile(getDelimiterString(), Pattern.DOTALL);
    }

    protected String convertToken(String string) {
        if (hasOption(REMOVE_WHITE_SPACE))
            return convertTokenWithTrim(string) ;
        else
            return convertTokenWithoutTrim(string) ;
    }
    
    protected String convertTokenWithTrim(String string) {
        
        String token = convertTokenWithoutTrim(string) ;
        
        if (token != null)
            return token.trim() ;
        else 
            return null;
    }
    
    protected String convertTokenWithoutTrim(String string) {
        
        if (hasOption(PARSE_EMPTY_STRINGS))
            return string;
        else if (string != null)
            if ("".equals(string.trim()))
                return null;
            else
                return string;
        else
            return null;
    }


    protected int getDefaultConversionTypes() {
        return defaultConversionTypes;
    }

    public final void setDefaultConversionTypes(int defaultConversionTypes) {
        this.defaultConversionTypes = defaultConversionTypes;
    }

    public final int getConversionTypes(int index) {
        if (index >= 0 && conversionTypesMap.containsKey(index))
            return conversionTypesMap.get(index);
        else
            return defaultConversionTypes;
    }

    public final int[] getAllConversionTypes() {
        if (conversionTypesArray == null) {
            conversionTypesArray = new int[conversionTypesCount];

            Iterator> iterator = conversionTypesMap.entrySet().iterator();

            Entry entry = null;

            while (iterator.hasNext()) {
                entry = iterator.next();

                conversionTypesArray[entry.getKey()] = entry.getValue();
            }
        }

        return conversionTypesArray;
    }

    public final void setAllConversionTypes(int[] conversionTypes) {
        conversionTypesArray = null;
        conversionTypesCount = conversionTypes.length;
        for (int i = 0; i < conversionTypes.length; ++i)
            conversionTypesMap.put(i, conversionTypes[i]);
    }

    public final void setConversionTypes(int conversionTypes, int index) {
        if (index >= 0) {
            conversionTypesMap.put(index, conversionTypes);

            if (index >= conversionTypesCount) {
                conversionTypesCount = index + 1;
                conversionTypesArray = null;
            } else {
                conversionTypesArray[index] = conversionTypes;
            }
        }
    }

    private Object parseCell(int index) throws IOException {
        if (line != null && index < line.length) {

            if (conversionTypesCount > 0) {
                return parseValue(convertToken(line[index]), getRowIndex(), index, getConversionTypes(index));
            } else {
                return parseValue(convertToken(line[index]), getRowIndex(), index);
            }
        } else {
            return null;
        }
    }

    private String parseCellAsString(int index) throws IOException {
        if (line != null) {
            try {
                return convertToken(line[index]);
            } catch (Exception e) {
                throw new IOException("Can not parse cell position " + getRowIndex() + "," + index + " due to "
                        + e.getLocalizedMessage(), e);
            }
        } else {
            throw new IOException("Can not convert to string");
        }
    }

    private int parseCellAsInt(int index) throws IOException {
        if (line != null) {
            try {
                Integer value = ConversionUtilities.convertToInteger(convertTokenWithoutTrim(line[index]));
                if (value != null)
                    return (int) value;
                else
                    return defaultInt ;
            } catch (Exception e) {
                throw new IOException("Can not parse cell position " + getRowIndex() + "," + index + " due to "
                        + e.getLocalizedMessage(), e);
            }
        } else {
            throw new IOException("Can not convert to int");
        }
    }

    private Integer parseCellAsIntegerObject(int index) throws IOException {
        if (line != null) {
            try {
                return ConversionUtilities.convertToInteger(convertTokenWithoutTrim(line[index]));
            } catch (Exception e) {
                throw new IOException("Can not parse cell position " + getRowIndex() + "," + index + " due to "
                        + e.getLocalizedMessage(), e);
            }
        } else {
            throw new IOException("Can not convert to int");
        }
    }

    private double parseCellAsDouble(int index) throws IOException {
        if (line != null) {
            try {
                Double value = ConversionUtilities.convertToDouble(convertTokenWithoutTrim(line[index]));
                if (value != null)
                    return (double) value;
                else
                    return defaultDouble ;
            } catch (Exception e) {
                throw new IOException("Can not parse cell position " + getRowIndex() + "," + index + " due to "
                        + e.getLocalizedMessage(), e);
            }
        } else {
            throw new IOException("Can not convert to int");
        }
    }
    
    private Double parseCellAsDoubleObject(int index) throws IOException {
        if (line != null) {
            try {
                return ConversionUtilities.convertToDouble(convertTokenWithoutTrim(line[index]));
            } catch (Exception e) {
                throw new IOException("Can not parse cell position " + getRowIndex() + "," + index + " due to "
                        + e.getLocalizedMessage(), e);
            }
        } else {
            throw new IOException("Can not convert to int");
        }
    }

    private boolean parseCellAsBoolean(int index) throws IOException {
        if (line != null) {
            try {
                Boolean value = ConversionUtilities.convertToBoolean(convertTokenWithoutTrim(line[index]));
                if (value != null)
                    return (boolean) value;
                else
                    return defaultBoolean ;
            } catch (Exception e) {
                throw new IOException("Can not parse cell position " + getRowIndex() + "," + index + " due to "
                        + e.getLocalizedMessage(), e);
            }
        } else {
            throw new IOException("Can not convert to int");
        }
    }

    private Boolean parseCellAsBooleanObject(int index) throws IOException {
        if (line != null) {
            try {
                return ConversionUtilities.convertToBoolean(convertTokenWithoutTrim(line[index]));
            } catch (Exception e) {
                throw new IOException("Can not parse cell position " + getRowIndex() + "," + index + " due to "
                        + e.getLocalizedMessage(), e);
            }
        } else {
            throw new IOException("Can not convert to int");
        }
    }

    private List parseRowCells(int firstIndex, int requestedSize) throws IOException {
        ArrayList row;

        if (line != null) {
            int size = requestedSize < line.length ? requestedSize : line.length;
            int startIndex = firstIndex < 0 ? 0 : firstIndex >= size ? size : firstIndex;

            if (hasOption(ROWS_SAME_SIZE))
            {
                row = new ArrayList(requestedSize - startIndex);
                
                if (conversionTypesCount > 0) {
                    if (hasOption(REMOVE_WHITE_SPACE)) {
                        for (int i = startIndex; i < size; ++i)
                            row.add(parseValue(convertTokenWithTrim(line[i]), getRowIndex(), i, getConversionTypes(i)));
                    } else {
                        for (int i = startIndex; i < size; ++i)
                            row.add(parseValue(convertTokenWithoutTrim(line[i]), getRowIndex(), i));
                    }
                } else {
                    if (hasOption(REMOVE_WHITE_SPACE)) {
                        for (int i = startIndex; i < size; ++i)
                            row.add(parseValue(convertTokenWithTrim(line[i]), getRowIndex(), i, getConversionTypes(i)));
                    } else {
                        for (int i = startIndex; i < size; ++i)
                            row.add(parseValue(convertTokenWithoutTrim(line[i]), getRowIndex(), i));
                    }  
                }
          
                for (int i = size; i < requestedSize; ++i)
                    row.add(null);
            }
            else
            {
                row = new ArrayList(size - startIndex);     
                
                if (conversionTypesCount > 0) {
                    if (hasOption(REMOVE_WHITE_SPACE)) {
                        for (int i = startIndex; i < size; ++i)
                            row.add(parseValue(convertTokenWithTrim(line[i]), getRowIndex(), i, getConversionTypes(i)));
                    } else {
                        for (int i = startIndex; i < size; ++i)
                            row.add(parseValue(convertTokenWithoutTrim(line[i]), getRowIndex(), i));
                    }
                } else {
                    if (hasOption(REMOVE_WHITE_SPACE)) {
                        for (int i = startIndex; i < size; ++i)
                            row.add(parseValue(convertTokenWithTrim(line[i]), getRowIndex(), i, getConversionTypes(i)));
                    } else {
                        for (int i = startIndex; i < size; ++i)
                            row.add(parseValue(convertTokenWithoutTrim(line[i]), getRowIndex(), i));
                    }  
                }
            }
        } else {
            row = new ArrayList();
        }

        return row;
    }

    private List parseRowCellsAsString(int firstIndex, int requestedSize) throws IOException {
        ArrayList row;

        if (line != null) {
            int size = requestedSize < line.length ? requestedSize : line.length;
            int startIndex = firstIndex < 0 ? 0 : firstIndex >= size ? size : firstIndex;

            if (hasOption(ROWS_SAME_SIZE))
            {
                row = new ArrayList(requestedSize - startIndex);
                
                if (hasOption(REMOVE_WHITE_SPACE))
                    for (int i = startIndex; i < size; ++i)
                        row.add(convertTokenWithTrim(line[i]));
                else
                    for (int i = startIndex; i < size; ++i)
                        row.add(convertTokenWithoutTrim(line[i]));    
                
                for (int i = size; i < requestedSize; ++i)
                    row.add(null);
            }
            else
            {
                row = new ArrayList(size - startIndex);    
                
                if (hasOption(REMOVE_WHITE_SPACE))
                    for (int i = startIndex; i < size; ++i)
                        row.add(convertTokenWithTrim(line[i]));
                else
                    for (int i = startIndex; i < size; ++i)
                        row.add(convertTokenWithoutTrim(line[i]));    
            }
            

        } else {
            row = new ArrayList();
        }

        return row;
    }

    private List parseRowCellsAsInteger(int firstIndex, int requestedSize) throws IOException {
        ArrayList row;

        if (line != null) {
            int size = requestedSize < line.length ? requestedSize : line.length;
            int startIndex = firstIndex < 0 ? 0 : firstIndex >= size ? size : firstIndex;

            if (hasOption(ROWS_SAME_SIZE))
            {
                row = new ArrayList(requestedSize - startIndex);
                
                for (int i = startIndex; i < size; ++i)
                    row.add(parseCellAsIntegerObject(i));
                
                for (int i = size; i < requestedSize; ++i)
                    row.add(null);
            }
            else
            {
                row = new ArrayList(size - startIndex);         
                
                for (int i = startIndex; i < size; ++i)
                    row.add(parseCellAsIntegerObject(i));
            }

        } else {
            row = new ArrayList();
        }

        return row;
    }

    private List parseRowCellsAsDouble(int firstIndex, int requestedSize) throws IOException {
        ArrayList row;

        if (line != null) {
            int size = requestedSize < line.length ? requestedSize : line.length;
            int startIndex = firstIndex < 0 ? 0 : firstIndex >= size ? size : firstIndex;

            if (hasOption(ROWS_SAME_SIZE))
            {
                row = new ArrayList(requestedSize - startIndex);
                
                for (int i = startIndex; i < size; ++i)
                    row.add(parseCellAsDoubleObject(i));

                for (int i = size; i < requestedSize; ++i)
                    row.add(null);
            }
            else
            {
                row = new ArrayList(size - startIndex);   
                
                for (int i = startIndex; i < size; ++i)
                    row.add(parseCellAsDoubleObject(i));
            }
        } else {
            row = new ArrayList();
        }

        return row;
    }

    private List parseRowCellsAsBoolean(int firstIndex, int requestedSize) throws IOException {
        ArrayList row;

        if (line != null) {
            int size = requestedSize < line.length ? requestedSize : line.length;
            int startIndex = firstIndex < 0 ? 0 : firstIndex >= size ? size : firstIndex;

            if (hasOption(ROWS_SAME_SIZE))
            {
                row = new ArrayList(requestedSize - startIndex);
                
                for (int i = startIndex; i < size; ++i)
                    row.add(parseCellAsBooleanObject(i));
                
                for (int i = size; i < requestedSize; ++i)
                    row.add(null);
            }
            else
            {
                row = new ArrayList(size - startIndex);        
                
                for (int i = startIndex; i < size; ++i)
                    row.add(parseCellAsBooleanObject(i));
            }


        } else {
            row = new ArrayList();
        }

        return row;
    }

    private Object[] parseRowCellsAsArray(int firstIndex, int requestedSize) throws IOException {
        Object[] row;

        if (line != null) {
            int size = requestedSize < line.length ? requestedSize : line.length;
            int startIndex = firstIndex < 0 ? 0 : firstIndex >= size ? size : firstIndex;

            if (hasOption(ROWS_SAME_SIZE))
            {
                row = new Object[requestedSize - startIndex];
            }
            else
            {
                row = new Object[size - startIndex];               
            }

            if (conversionTypesCount > 0) {
                if (hasOption(REMOVE_WHITE_SPACE)) {
                    for (int i = startIndex; i < size; ++i)
                        row[i - startIndex] = parseValue(convertTokenWithTrim(line[i]), getRowIndex(), i, getConversionTypes(i));
                } else {
                    for (int i = startIndex; i < size; ++i)
                        row[i - startIndex] = parseValue(convertTokenWithoutTrim(line[i]), getRowIndex(), i);
                }
            } else {
                if (hasOption(REMOVE_WHITE_SPACE)) {
                    for (int i = startIndex; i < size; ++i)
                        row[i - startIndex] = parseValue(convertTokenWithTrim(line[i]), getRowIndex(), i, getConversionTypes(i));
                } else {
                    for (int i = startIndex; i < size; ++i)
                        row[i - startIndex] = parseValue(convertTokenWithoutTrim(line[i]), getRowIndex(), i);
                }
            }
        } else {
            row = new Object[0];
        }

        return row;
    }

    private String[] parseRowCellsAsStringArray(int firstIndex, int requestedSize) throws IOException {
        String[] row;

        if (line != null) {
            int size = requestedSize < line.length ? requestedSize : line.length;
            int startIndex = firstIndex < 0 ? 0 : firstIndex >= size ? size : firstIndex;

            if (hasOption(ROWS_SAME_SIZE))
            {
                row = new String[requestedSize - startIndex];
            }
            else
            {
                row = new String[size - startIndex];               
            }
            
            if (this.hasOption(REMOVE_WHITE_SPACE))
                for (int i = startIndex; i < size; ++i)
                    row[i - startIndex] = convertTokenWithTrim(line[i]);
            else
                for (int i = startIndex; i < size; ++i)
                    row[i - startIndex] = convertTokenWithoutTrim(line[i]);                
        } else {
            row = new String[0];
        }

        return row;
    }

    private int[] parseRowCellsAsIntArray(int firstIndex, int requestedSize) throws IOException {
        int[] row;

        if (line != null) {
            int size = requestedSize < line.length ? requestedSize : line.length;
            int startIndex = firstIndex < 0 ? 0 : firstIndex >= size ? size : firstIndex;

            if (hasOption(ROWS_SAME_SIZE))
            {
                row = new int[requestedSize - startIndex];
            }
            else
            {
                row = new int[size - startIndex];               
            }
            
            for (int i = startIndex; i < size; ++i)
                row[i - startIndex] = parseCellAsInt(i);
        } else {
            row = new int[0];
        }

        return row;
    }

    private double[] parseRowCellsAsDoubleArray(int firstIndex, int requestedSize) throws IOException {
        double[] row;

        if (line != null) {
            int size = requestedSize < line.length ? requestedSize : line.length;
            int startIndex = firstIndex < 0 ? 0 : firstIndex >= size ? size : firstIndex;

            if (hasOption(ROWS_SAME_SIZE))
            {
                row = new double[requestedSize - startIndex];
            }
            else
            {
                row = new double[size - startIndex];               
            }

            for (int i = startIndex; i < size; ++i)
                row[i - startIndex] = parseCellAsDouble(i);
        } else {
            row = new double[0];
        }

        return row;
    }

    private boolean[] parseRowCellsAsBooleanArray(int firstIndex, int requestedSize) throws IOException {
        boolean[] row;

        if (line != null) {
            int size = requestedSize < line.length ? requestedSize : line.length;
            int startIndex = firstIndex < 0 ? 0 : firstIndex >= size ? size : firstIndex;

            if (hasOption(ROWS_SAME_SIZE))
            {
                row = new boolean[requestedSize - startIndex];
            }
            else
            {
                row = new boolean[size - startIndex];               
            }

            for (int i = startIndex; i < size; ++i)
                row[i - startIndex] = parseCellAsBoolean(i);
        } else {
            row = new boolean[0];
        }

        return row;
    }
    
    private boolean hasOption(int option) {
        return (options & option) > 0;
    }

    /**
     * Creates a BufferedReader using the string reference to a text file.
     * 
     * @param textFileReference
     *            a text file name or URL
     * @return a bufferedReader
     * 
     * @exception FileNotFoundException
     *                if the file referenced can not be found
     * @exception IOException
     *                if the reader can not open an input stream to the file
     */
    private static final BufferedReader getBufferReader(Path path) throws FileNotFoundException, IOException {
        // TODO support for other Charsets
        if (path != null)
            return Files.newBufferedReader(path);
        else
            throw new FileNotFoundException("File object is null");
    }

    /**
     * Creates a BufferedReader using the string reference to a text file.
     * 
     * @param textFileReference
     *            a text file name or URL
     * @return a bufferedReader
     * 
     * @exception FileNotFoundException
     *                if the file referenced can not be found
     * @exception IOException
     *                if the reader can not open an input stream to the file
     */
    private static final BufferedReader getBufferReader(String fileReference)
            throws FileNotFoundException, IOException {
        BufferedReader bufferedReader = null;

        try {
            URL refURL = new java.net.URL(fileReference);
            bufferedReader = new BufferedReader(new InputStreamReader(refURL.openStream()));
        } catch (MalformedURLException malformedURLException) {
            bufferedReader = new BufferedReader(new FileReader(fileReference));
        }

        return bufferedReader;
    }
}