Coverage Report - org.melati.poem.csv.CSVTable
 
Classes in this File Line Coverage Branch Coverage Complexity
CSVTable
0%
0/117
0%
0/44
4.167
 
 1  
 /*
 2  
  * $Source$
 3  
  * $Revision$
 4  
  *
 5  
  * Part of Melati (http://melati.org), a framework for the rapid
 6  
  * development of clean, maintainable web applications.
 7  
  *
 8  
  *  Copyright (C) 2001 Myles Chippendale
 9  
  *
 10  
  * Melati is free software; Permission is granted to copy, distribute
 11  
  * and/or modify this software under the terms either:
 12  
  *
 13  
  * a) the GNU General Public License as published by the Free Software
 14  
  *    Foundation; either version 2 of the License, or (at your option)
 15  
  *    any later version,
 16  
  *
 17  
  *    or
 18  
  *
 19  
  * b) any version of the Melati Software License, as published
 20  
  *    at http://melati.org
 21  
  *
 22  
  * You should have received a copy of the GNU General Public License and
 23  
  * the Melati Software License along with this program;
 24  
  * if not, write to the Free Software Foundation, Inc.,
 25  
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
 26  
  * GNU General Public License and visit http://melati.org to obtain the
 27  
  * Melati Software License.
 28  
  *
 29  
  * Feel free to contact the Developers of Melati (http://melati.org),
 30  
  * if you would like to work out a different arrangement than the options
 31  
  * outlined here.  It is our intention to allow Melati to be used by as
 32  
  * wide an audience as possible.
 33  
  *
 34  
  * This program is distributed in the hope that it will be useful,
 35  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 36  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 37  
  * GNU General Public License for more details.
 38  
  *
 39  
  * Contact details for copyright holder:
 40  
  *
 41  
  *     Myles Chippendale <mylesc At paneris.org>
 42  
  *
 43  
  *
 44  
  * ------
 45  
  *  Note
 46  
  * ------
 47  
  *
 48  
  * I will assign copyright to PanEris (http://paneris.org) as soon as
 49  
  * we have sorted out what sort of legal existence we need to have for
 50  
  * that to make sense. 
 51  
  * In the meantime, if you want to use Melati on non-GPL terms,
 52  
  * contact me!
 53  
  */
 54  
 
 55  
 package org.melati.poem.csv;
 56  
 
 57  
 import java.io.BufferedReader;
 58  
 import java.io.File;
 59  
 import java.io.FileReader;
 60  
 import java.io.IOException;
 61  
 import java.io.Writer;
 62  
 import java.util.Enumeration;
 63  
 import java.util.Hashtable;
 64  
 import java.util.NoSuchElementException;
 65  
 import java.util.Vector;
 66  
 
 67  
 import org.melati.poem.Persistent;
 68  
 import org.melati.poem.Table;
 69  
 
 70  
 /**
 71  
  * A representation of a CSV file as a POEM Table.
 72  
  */
 73  
 public class CSVTable {
 74  
 
 75  0
   protected Table<?> table = null;
 76  0
   protected File data = null;
 77  0
   protected Hashtable<String,CSVColumn> columns = new Hashtable<String,CSVColumn>();
 78  0
   protected Vector<CSVColumn> columnsInUploadOrder = new Vector<CSVColumn>();
 79  0
   protected CSVColumn primaryKey = null;
 80  0
   protected Vector<CSVRecord> records = new Vector<CSVRecord>();
 81  0
   protected BufferedReader reader = null;
 82  0
   protected CSVFileParser parser = null;
 83  
   
 84  
   /** The line number of the CSV file. */
 85  
   private int lineNo;
 86  
   
 87  
   /** The record number of the CSV file. */
 88  
   private int recordNo;
 89  
 
 90  
 
 91  
  /**
 92  
   * Constructor.
 93  
   * 
 94  
   * @param table POEM table to load data into
 95  
   * @param data CSV file to read from
 96  
   */
 97  0
   public CSVTable(Table<?> table, File data) {
 98  0
     this.table = table;
 99  0
     this.data = data;
 100  
     try {
 101  0
       reader = new BufferedReader(new FileReader(this.data));
 102  0
       parser = new CSVFileParser(this.reader);
 103  0
     } catch (Exception e) {
 104  0
       throw new RuntimeException(e);
 105  0
     }
 106  0
   }
 107  
 
 108  
   /**
 109  
    * Process the first line to define columns. 
 110  
    * The first line contains the field names - this needs to be
 111  
    * validated against expected values, and the order of
 112  
    * the fields established.
 113  
    * 
 114  
    * @throws IOException
 115  
    */
 116  
   public void define() throws IOException  {
 117  0
       parser.nextRecord();
 118  0
       lineNo = 1;
 119  0
       recordNo = 0;
 120  
 
 121  0
       while (parser.recordHasMoreFields()) {
 122  0
         String key = parser.nextField();
 123  0
         CSVColumn col = columns.get(key);
 124  0
         if (col == null)
 125  0
           throw new CSVParseException(
 126  0
             "I don't know what to do with the column in " + data.getPath() +
 127  
             " called " + key); 
 128  0
         columnsInUploadOrder.addElement(col);
 129  0
       }
 130  0
   }  
 131  
 
 132  
  /**
 133  
   * Add column definitions to this table.
 134  
   */
 135  
   public void addColumn(String csvName, String poemName) {
 136  0
     columns.put(csvName, new CSVColumn(poemName));
 137  0
   }
 138  
 
 139  
  /**
 140  
   * Add column definitions, perhaps Primary Keys, to this table.
 141  
   */
 142  
   public void addColumn(String csvName, String poemName, boolean isPrimaryKey)
 143  
       throws CSVPrimaryKeyColumnAlreadySetException {
 144  0
     if (isPrimaryKey && primaryKey != null)
 145  0
       throw new CSVPrimaryKeyColumnAlreadySetException(table.getName());
 146  0
     CSVColumn col = new CSVColumn(poemName, isPrimaryKey);
 147  0
     columns.put(csvName, col);
 148  0
     if (isPrimaryKey)
 149  0
       primaryKey = col;
 150  0
   }
 151  
 
 152  
    /**
 153  
     * Add column definitions for foreign keys to this table.
 154  
     */
 155  
     public void addColumn(String csvName,  
 156  
                           String foreignPoemName, CSVTable foreignTable) {
 157  0
       columns.put(csvName, new CSVColumn(foreignPoemName, foreignTable));
 158  0
     }
 159  
 
 160  
 
 161  
  /**
 162  
   * Parse the CSV data file and store the data for saving later.
 163  
   * 
 164  
   * @param writeOnFly whether to commit each line to db as we go
 165  
   * @throws IOException if there is a file system problem
 166  
   * @throws CSVParseException if the is a malformed field in the CSV
 167  
   * @throws CSVWriteDownException
 168  
   * @throws NoPrimaryKeyInCSVTableException
 169  
   */
 170  
   public void load(boolean writeOnFly) 
 171  
       throws IOException, CSVParseException, NoPrimaryKeyInCSVTableException, 
 172  
              CSVWriteDownException {
 173  
 
 174  
     try {
 175  0
       define();
 176  
       // Suck in all the data
 177  
       CSVRecord record;
 178  0
       while(null != (record = parseRecord())) {
 179  0
         record.setLineNo(lineNo++);
 180  0
         record.setRecordNo(recordNo++);
 181  0
         if (writeOnFly)
 182  0
           record.makePersistent();
 183  
         else 
 184  0
           records.addElement(record);
 185  
       }
 186  
 
 187  
     }
 188  0
     catch (IllegalArgumentException e) {
 189  0
        throw new CSVParseException("Failed to load field in " +
 190  0
                                     data.getPath() + 
 191  
                                     " line " + lineNo +
 192  0
                                     ": " + e.toString());
 193  
     }
 194  0
     catch (NoSuchElementException f) {
 195  0
        throw new CSVParseException("Failed to read column header in " +
 196  0
                                     data.getPath() + 
 197  
                                     " line " + lineNo +
 198  0
                                     ": " + f.toString());
 199  
     }
 200  
     finally {
 201  0
       reader.close();
 202  0
     }
 203  0
   }
 204  
 
 205  
 
 206  
  /**
 207  
   * Reads the file until is has seen an object's-worth of
 208  
   * field values (ie until it sees an EOF or a line starting
 209  
   * with '$') which it returns in a hashtable (null if there are
 210  
   * no field values).
 211  
    * @return a new CSVRecord
 212  
    * @throws IOException if there is a problem with the file system
 213  
    * @throws CSVParseException if there is a problem parsing the input
 214  
    */
 215  
   public CSVRecord parseRecord() throws IOException, CSVParseException {
 216  0
     if (!parser.nextRecord())
 217  0
       return null;
 218  
 
 219  0
     int i = 0;
 220  0
     String value = null;
 221  
     try {
 222  0
       CSVRecord record = new CSVRecord(table);
 223  0
       for (; i < columnsInUploadOrder.size(); i++) {
 224  0
         value = parser.nextField();
 225  0
         CSVColumn col = (CSVColumn)columnsInUploadOrder.elementAt(i);
 226  0
         record.addField(new CSVField(col, value));
 227  
       }
 228  0
       record.setLineNo(parser.getLineNo());
 229  0
       return record;
 230  
     }
 231  0
     catch (IllegalArgumentException e) {
 232  0
        throw new CSVParseException("Failed to read data field no. " + 
 233  
                                     (i+1) + 
 234  
                                     " in " +
 235  
                                     data + 
 236  
                                     " line " + lineNo +
 237  0
                                     ": " + e.toString());
 238  
     }
 239  0
     catch (NoSuchElementException f) {
 240  0
       String message = "Problem with data field no. " + (i+1) +
 241  0
       " of " + columnsInUploadOrder.size() +
 242  
       " in " + data +
 243  
       " line " + lineNo;
 244  
       
 245  0
       if (value == null) {
 246  0
         message += " (Check last line of file) : " + 
 247  0
                     f.toString();
 248  
       } else {
 249  0
         message += ", Value:" + value + ": " + f.toString();
 250  
       }
 251  0
       throw new CSVParseException(message);
 252  
     }
 253  
   }
 254  
 
 255  
 
 256  
  /**
 257  
   * Delete all Persistents from the Poem table.
 258  
   */
 259  
   public void emptyTable() {
 260  0
     Enumeration<?> rows = table.selection();
 261  0
     while(rows.hasMoreElements()) {
 262  0
       Persistent p = (Persistent)rows.nextElement();
 263  0
       p.delete();
 264  0
     }
 265  0
   }
 266  
 
 267  
  /**
 268  
   * Write the records to the database, 
 269  
   * called if we are not writing each record to db as we go.
 270  
   */
 271  
   public void writeRecords() 
 272  
       throws NoPrimaryKeyInCSVTableException,  CSVWriteDownException {
 273  0
     for (int i = 0; i < records.size(); i++) {
 274  0
       ((CSVRecord)records.elementAt(i)).makePersistent();
 275  
     }
 276  0
   }
 277  
   
 278  
 
 279  
   /**
 280  
    * Lookup the Persistent corresponding to the CSV record
 281  
    * with the given value for the CSV table's primary key.
 282  
    * @throws NoPrimaryKeyInCSVTableException
 283  
    * @throws CSVWriteDownException
 284  
    */
 285  
   protected Persistent getRecordWithID(String csvValue)
 286  
       throws NoPrimaryKeyInCSVTableException, CSVWriteDownException {
 287  0
     if (primaryKey == null)
 288  0
       throw new NoPrimaryKeyInCSVTableException(table.getName(), csvValue);
 289  
 
 290  0
     for (int i = 0; i < records.size(); i++) {
 291  0
       CSVRecord record = (CSVRecord)records.elementAt(i);
 292  0
       if (record.primaryKeyValue != null &&
 293  0
           record.primaryKeyValue.equals(csvValue))
 294  0
         return record.getPersistent();
 295  
     }
 296  0
     return null;
 297  
   }
 298  
 
 299  
  /**
 300  
   * Return a string reporting on the data added to this table.
 301  
   */
 302  
   public void report(boolean recordDetails, boolean fieldDetails,
 303  
                        Writer output) throws IOException {
 304  
 
 305  0
     output.write("*** TABLE: " + table.getName().toUpperCase() + " **\n\n");
 306  0
     output.write("** I have read " + records.size() + " records of " +
 307  0
                    columnsInUploadOrder.size() + " fields\n");
 308  
 
 309  0
     if (recordDetails) {
 310  0
       for (int i = 0; i < records.size(); i++) {
 311  0
         CSVRecord record = (CSVRecord)records.elementAt(i);
 312  0
         output.write("   Record: CSV primary key = " +
 313  
                         record.primaryKeyValue);
 314  
 
 315  0
         if (record.poemRecord == null)
 316  0
           output.write(", No Poem Persistent written\n");
 317  
         else
 318  0
           output.write(", Poem Troid = " + record.poemRecord.getTroid() + "\n");
 319  
 
 320  0
         if (fieldDetails) {
 321  0
           for (int j = 0; j < record.getFields().size(); j++) {
 322  0
             CSVField field = (CSVField)record.getFields().elementAt(j);
 323  0
             output.write(field.column + "=\"" + field.value);
 324  0
             if (j < record.getFields().size()-1)
 325  0
               output.write("\",");
 326  
             else
 327  0
               output.write("\"\n");
 328  
           }
 329  
         }
 330  
       }
 331  
     }
 332  0
     output.write("** Currently " + table.count(null) +
 333  
                   " Persistents in this table\n\n");
 334  0
   }
 335  
 
 336  
   /**
 337  
    * Used in debugging to display name of table being emptied.
 338  
    * 
 339  
    * @return the POEM Table's name
 340  
    */
 341  
   public String getName() {
 342  0
     return table.getName();
 343  
   }
 344  
 }
 345