/*
 * ARX: Powerful Data Anonymization
 * Copyright 2012 - 2015 Florian Kohlmayer, Fabian Prasser
 * 
 * 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 org.deidentifier.arx.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * This class implements a reader for CSV encoded information.
 *
 * @author Fabian Prasser
 * @author Florian Kohlmayer
 */
public class CSVDataInput extends CSVAbstractInput {

    /** The number of columns in the dataset. */
    private int                  columns     = -1;

    /** A reader. */
    private final BufferedReader reader;

    /** Size of the buffer. */
    private static final int     BUFFER_SIZE = 1024 * 1024;

    /**
     * Instantiate.
     *
     * @param file
     * @param separator
     * @throws IOException
     */
    public CSVDataInput(final File file, final char separator) throws IOException {
        super(separator);
        reader = new BufferedReader(new FileReader(file), BUFFER_SIZE);
    }

    /**
     * Instantiate.
     *
     * @param stream
     * @param separator
     * @throws IOException
     */
    public CSVDataInput(final InputStream stream, final char separator) throws IOException {
        super(separator);
        reader = new BufferedReader(new InputStreamReader(stream), BUFFER_SIZE);
    }

    /**
     * Instantiate.
     *
     * @param file
     * @param separator
     * @throws IOException
     */
    public CSVDataInput(final String file, final char separator) throws IOException {
        super(separator);
        reader = new BufferedReader(new FileReader(file), BUFFER_SIZE);
    }

    /* (non-Javadoc)
     * @see org.deidentifier.arx.io.CSVAbstractInput#close()
     */
    @Override
    public void close() throws IOException {
        reader.close();
    }

    /**
     * Counts the number of columns.
     *
     * @param line
     * @return
     */
    private int countColumns(final String line) {
        int result = 0;
        int offset = 0;
        int index = 0;
        while (index != -1) {
            index = line.indexOf(separator, offset);
            offset = index + 1;
            result++;
        }
        return result;
    }

    /**
     * Reads a line from the input
     * @return
     * @throws IOException
     */
    private String readLine() throws IOException {
        
        // Read a line, ignoring empty lines
        String line = "";
        while (line != null && line.length()==0) {
            line = reader.readLine();
        }

        // Extract number of columns
        if (columns == -1) {
            columns = countColumns(line);
        }

        // Return
        return line;
    }

    /* (non-Javadoc)
     * @see org.deidentifier.arx.io.CSVAbstractInput#readRow()
     */
    @Override
    protected String[] readRow() throws IOException {

        // Read a line
        String line = readLine();
        
        // Check for EOF
        if (line == null) {
            reader.close();
            return null;
        }

        // Extract tuple
        final String[] tuple = new String[columns];
        int column = 0;
        int offset = 0;
        int index = 0;
        while (column < (columns - 1)) {
            
            // Read the next line in case of line breaks
            index = line.indexOf(separator, offset);
            while (index < 0) {
                offset = 0;
                line = readLine();
                if (line == null) {
                    throw new IOException("Schema mismatch: too few columns");
                }
                index = line.indexOf(separator, offset);
            }
            
            // Store
            tuple[column++] = line.substring(offset, index);
            offset = index + 1;
        }
        
        // Store remainder
        tuple[column] = line.substring(offset);
        
        // Check if end of line equals end of tuple
        if (line.indexOf(separator, offset) >= 0) {
            throw new IOException("Schema mismatch: too many columns");
        }

        // Return
        return tuple;
    }
}
@@ -1081,16 +1081,17 @@
 the data
+ 
 set. */%0A
@@ -4273,44 +4273,920 @@
  // 
-Store%0A            tuple%5Bcolumn++%5D = 
+Extract %0A            String value = line.substring(offset, index);%0A            offset = index + 1;%0A            %0A            // If starts with an escape character, read on%0A            if (value.length() %3E 0 && value.charAt(0) == '%5C%22' && value.charAt(value.length() - 1) != '%5C%22') %7B%0A                StringBuilder builder = new StringBuilder();%0A                builder.append(value);%0A                while (builder.charAt(builder.length() - 1) != '%5C%22') %7B%0A                    index = line.indexOf(separator, offset);%0A                    while (index %3C 0) %7B%0A                        offset = 0;%0A                        line = readLine();%0A                        if (line == null) %7B %0A                            throw new IOException(%22Schema mismatch: too few columns%22); %0A                        %7D%0A                        index = line.indexOf(separator, offset);%0A                    %7D%0A                    builder.append(
 line
@@ -5202,32 +5202,33 @@
 g(offset, index)
+)
 ;%0A            of
@@ -5217,32 +5217,40 @@
 ));%0A            
+   
+     
 offset = index +
@@ -5245,32 +5245,135 @@
 et = index + 1;%0A
+                %7D%0A            %7D%0A            %0A            // Store%0A            tuple%5Bcolumn++%5D = value;%0A
         %7D%0A      

Support escaping characters in csv input