//
// Project "Portal"
//
// Michael Meyling
// Stoltenstrasse 38
// 22119 Hamburg
// Germany

package com.meyling.portal;

import java.io.*;

/**
 * Extended Wrapper for the Class <code>FileReader</code>.
 * This class deals with character seperated files:
 * each line is a sequence of values that are separated by
 * the character ";". If the beginning of a value
 * is a quote character """" each pair of quote characters
 * is repleaced by a single quote character till a single
 * quote character marks the end of that value.
 * A line that starts with an "#" is ignored.
 * These files have the ending ".csv".
 *
 * @version $Revision: 1.3 $
 * @author  Michael Meyling
 */
class CsvFileReader {

    /**
     * file to read from
     */
    private BufferedReader in = null;

    /**
     * name of the file
     */
    private String fileName = null;

    /**
     * line counter
     */
    private int linecounter = 0;

    /**
     * last read line
     */
    private String line = null;

    /**
     * is there still a line in the reading buffer? Is used to undo
     * a {@link #readLine}.
     */
    private boolean alreadyRead = false;


    /**
     * Constructs a <code>FileReader</code> object.
     * @param   inputName   name of the imput file without the
     *          extension ".csv"
     * @throws  IOException if the file couldn't be opened
     * @throws  FileNotFoundException   if the file could't be found
     */
    CsvFileReader(String inputName)
            throws IOException, FileNotFoundException {
        fileName = inputName + ".csv";
        in = new BufferedReader(new FileReader(fileName));
        linecounter = 0;
        alreadyRead = false;
    }


    /**
     * Closes the file without any exception.
     */
    void close() {
        if (in != null) {
            try {
                in.close();
            } catch (Exception e) {
            }
            in = null;
        }
    }


    /**
     * For reading a whole line of the file. The current line is
     * also saved in the instance attribut <code>line</code>.
     * A comment line is automatically skipped.
     * @return  read line
     * @throws  IOException if a reading exception occured
     */
    String readLine() throws IOException {

        if (in == null) {
            throw new IOException("It was tried to read form a closed  "
                + "file");
        }
        if (!alreadyRead) {
            do {
                line = in.readLine();
                linecounter++;
            } while (line != null && (line.length() == 0
                || line.startsWith("#")));
            if (line == null) {
                in.close();
                in = null;
            }
        }
        alreadyRead = false;
        return line;
    }


    /**
     * Cancels the last call of {@link #readLine}. Another
     * {@link #readLine} gives than the same result. It is not allowed
     * to call this method a second time without a intermediate
     * call of {@link #readLine}.
     * @throws IllgegalStateException if this method was called a second
     *                                time without a intermediate call
     *                                of {@link #readLine}
     */
    void unReadLine() throws IllegalStateException {
        if (line != null && !alreadyRead) {
            alreadyRead = true;
        } else {
            throw new IllegalStateException(
                "Es wurde versucht eine Zeile zuviel zurückzulesen,");
        }

    }


     /**
      * Returns the name of the input file (with extension ".csv").
      * @return file name
      */
     String getFileName() {
        return fileName;
     }



    /**
     * Get the current line number of file.
     * @return  current line number
     */
     int getLineNumber() {
        return linecounter;
     }


    /**
     * Quotes a <code>String</code> for a csv file: replaces a quote by
     * double quote and appends a quote at the beginning and the end.
     * @param   unquoted    <code>String</code> that will be extended
     * @return  with quotes extended <code>String<code>
     * <code>unquoted</code>
     */
    static String quote(String unquoted) {

        String result = new String("\"");

        for (int i = 0; i < unquoted.length(); i++) {
            if (unquoted.charAt(i) == '\"') {
                result += "\\\"";
            } else {
                result += unquoted.charAt(i);
            }
        }
        result += '\"';
        return result;
    }


    /**
     * Gets a certain piece of a <code>String</code> that has ";"
     * seperated values. A quoted value is "dequoted".
     * @param    str     the source
     * @param    num     piece number
     * @return   the <code>num</code>-th piece of <code>str</code>
     * @throws   IllegalArgumentException
     *                     if a quoted value doesn't end with a quote
     *                     or isn't followed by ";" or the end of the
     *                     <code>source</code>
     */
    static String getPiece(String str, int num) throws
            IllegalArgumentException {

        return getPiece(str, num, "\"");
    }


    /**
     * Gets a certain piece of a <code>String</code> that has ";"
     * seperated values. A double quote value is replaced by the
     * parameter <code>qoute</code>.
     * @param    str     source
     * @param    num     piece number
     * @param    quote   the replacement for double quotes
     * @return   the <code>num</code>-th piece of <code>str</code>
     * @throws   IllegalArgumentException
     *                     if a quoted value doesn't end with a quote
     *                     or isn't followed by ";" or the end of the
     *                     <code>source</code>
     */
    static String getPiece(String str, int num, String quote) throws
            IllegalArgumentException {

        String part = new String();     // result

        int i = 0;                      // pointer to position
        for (int j = 0; j < num; j++) { // count till piece
            part = "";
            if (i >= str.length()) {
                break;
            }
            if (str.charAt(i) != '\"') {
                int k = i;              // remember position
                while (i < str.length() && str.charAt(i) != ';') {
                    i++;
                }
                part = str.substring(k, i);
                if (i < str.length()) {
                    i++;
                }
            }
            else {
                i++;
                if (i >= str.length()) {
                    throw new IllegalArgumentException(
                        "getPiece, closing \" is missing:\n" + str);
                }
                int k;                  // to remember the position
                do {
                    k = i;
                    while (i < str.length() && str.charAt(i) != '\"') {
                        i++;
                    }
                    if (i >= str.length()) {
                        throw new IllegalArgumentException(
                            "getPiece, closing \" is missing:\n" + str);
                    }
                    i++;
                    if (i >= str.length()) {
                        break;
                    }
                    if (str.charAt(i) == '\"') {
                        part = part + str.substring(k, i - 1) + quote;
                        i++;
                        continue;
                    }
                    break;
                } while (true);
                part = part + str.substring(k, i - 1);
                if (i < str.length()) {
                    if (str.charAt(i) != ';') {
                        throw new IllegalArgumentException(
                            "getPiece, closing \" must be followed"
                            + " by a colon:\n" + str);
                    }
                    i++;
                }
            }
        }
        return part;
    }

}
