import java.io.*;
import java.util.*;

/**
 * A class to encapsulate reading of CSV files.
 * @author Matthew W. Coan
 * 5/2/2001
 */
public class CSVFileReader {
   private String fileName;
   private boolean firstLineIsNames;
   private BufferedReader fin = null;
   private Vector line = null;
   private Hashtable names = null;
   
   public Enumeration colNames() { 
      if(names == null)
         return null;
      return names.keys();
   }

   private void parseLine(String rec) throws IOException {
      line = new Vector();
      StringBuffer field;
      boolean gotQ, gotC = false;
      int i = 0, j = 0;
      while(i < rec.length()) {
         gotC = gotQ = false;
         field = new StringBuffer();
         if(rec.charAt(i) == '\"') {
            gotQ = true;
            i++;
         }
         while(i < rec.length()) {
            if(rec.charAt(i) == '\"' && gotQ) {
               if(i+1 < rec.length()) {
                  if(rec.charAt(i+1) == '\"') {
                     field.append('\"'); 
                     i++;
                  }
                  else {
                     gotQ = false;
                     break;
                  }
               }
               else
                  break;
            }
            else {
               if(!gotQ && rec.charAt(i) == ',')
                  break;
               field.append(rec.charAt(i));
            }
            while((i+1) == rec.length() && gotQ) {
               String temp = fin.readLine();
               if(temp != null) {
                  rec += "\n";
                  rec += temp;
               }
               else
                  break;
            }
            i++;
         }
         if(i < rec.length())
            if(rec.charAt(i) == '\"')
               i++;
         if(i < rec.length())
            if(rec.charAt(i) == ',') {
               i++; 
               gotC = true;
            }
         line.addElement(field.toString());
      }
      if(gotC)
         line.addElement("");         
   }

   /**
    * Create a CSV file from a file name.
    * @param fileName the file name.
    * @param firstLineIsNames is the first line of 
    *                         the CSV file the names 
    *                         of the colums.
    */
   public CSVFileReader(String fileName, boolean firstLineIsNames) {
      this.fileName = fileName;
      this.firstLineIsNames = firstLineIsNames;
   }

   /**
    * Open the CSV file for reading.
    */
   public void open() throws IOException {
      //fin = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), "UTF-8"));
      fin = new BufferedReader(new FileReader(fileName));
      if(firstLineIsNames) {
         readLine();
         names = new Hashtable();
         for(int i = 0; i < line.size(); i++)
            names.put(line.elementAt(i).toString().toLowerCase(), new Integer(i));
         line = null;
      }
   }

   /**
    * Close the CSV file.
    */
   public void close() throws IOException {
      if(fin == null)
         return;
      fin.close();
      fin = null;
      line = null;
      names = null;
   }

   /**
    * Read the next record.
    * @return true if the line 
    *         was read; else false
    *         is returned.
    */
   public boolean readLine() throws IOException {
      String rec = fin.readLine();
      if(rec == null) 
         return false;
      else 
         parseLine(rec);
      return true;
   }

   /**
    * Get a value from the current record.
    * @param index the zero based index of the field.
    * @return the value or null if the index is out of bounds.
    */
   public String getValue(int index) {
      if(index < 0 || index >= line.size())
         return null;
      return line.elementAt(index).toString();
   }
 
   /**
    * Get a value from the current record.
    * @param name the name of a field.
    * @return the value or null if the index is out of bounds.
    */
   public String getValue(String name) {
      if(names == null)
         return null;
      Integer index = (Integer)names.get(name.toLowerCase());
      if(index == null)
         return null;
      return getValue(index.intValue());
   }

   /** 
    * Test main entry point.
    */
   public static void main(String args[]) throws IOException {
      CSVFileReader csvFile = new CSVFileReader("test.csv", true);
      csvFile.open();
      while(csvFile.readLine()) {
         String name = csvFile.getValue("Name");
         String age = csvFile.getValue(1);
         String test = csvFile.getValue("test");
         System.out.println("Name: " + name);
         System.out.println("Age: " + age);
         System.out.println("test: " + test);
      }
      csvFile.close();
   }
}
