]> source.dussan.org Git - jackcess.git/commitdiff
Add ExportUtil and associated utilities for exporting tables to flat files (thanks...
authorJames Ahlborn <jtahlborn@yahoo.com>
Mon, 31 May 2010 19:52:30 +0000 (19:52 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Mon, 31 May 2010 19:52:30 +0000 (19:52 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@471 f203690c-595d-4dc9-a70b-905162fa7fd2

CREDITS.txt
src/changes/changes.xml
src/java/com/healthmarketscience/jackcess/ExportFilter.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/ExportUtil.java [new file with mode: 0644]
src/java/com/healthmarketscience/jackcess/SimpleExportFilter.java [new file with mode: 0644]
test/src/java/com/healthmarketscience/jackcess/ExportTest.java [new file with mode: 0644]

index f52f6e396061b0d2d7024fd3b68ea440f2242743..3d772169c72d0a0cb1f8c029a598a0713e418839 100644 (file)
@@ -5,4 +5,5 @@ James Ahlborn - Added support for NUMERIC data type
 Jon Iles - Added support for reading table definitions that span multiple pages
 James Schopp - added support for reading currency columns
 Patricia Donaldson - contributed RowFilter class
-Dan Rollo - added support for new DB file formats
\ No newline at end of file
+Dan Rollo - added support for new DB file formats
+F. Gerbig - added ExportUtil
index 34142ea03a1fc22fb068dfa2c36b8a8b10f56517..a7b55398f5dd5d6866d732d996016b2214b39b18 100644 (file)
@@ -4,6 +4,12 @@
     <author email="javajedi@users.sf.net">Tim McCune</author>
   </properties>
   <body>
+    <release version="1.2.1" date="TBD">
+      <action dev="jahlborn" type="add" issue="3005272">
+        Add ExportUtil and associated utilities for exporting tables to flat
+        files (thanks to F. Gerbig).
+      </action>
+    </release>
     <release version="1.2.0" date="2010-04-18">
       <action dev="bhamail" type="update" issue="1451628">
         Add support for access 2002/2003/2007 databases.
diff --git a/src/java/com/healthmarketscience/jackcess/ExportFilter.java b/src/java/com/healthmarketscience/jackcess/ExportFilter.java
new file mode 100644 (file)
index 0000000..b9c6124
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+Copyright (c) 2007 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+ */
+
+package com.healthmarketscience.jackcess;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Interface which allows customization of the behavior of the
+ * <code>Database</code> export methods.
+ * 
+ * @author James Ahlborn
+ */
+public interface ExportFilter {
+
+  /**
+   * The columns that should be used to create the exported file.
+   * 
+   * @param columns
+   *          the columns as determined by the export code, may be directly
+   *          modified and returned
+   * @return the columns to use when creating the export file
+   */
+  public List<Column> filterColumns(List<Column> columns) throws IOException;
+
+  /**
+   * The desired values for the row.
+   * 
+   * @param row
+   *          the row data as determined by the import code, may be directly
+   *          modified
+   * @return the row data as it should be written to the import table
+   */
+  public Object[] filterRow(Object[] row) throws IOException;
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/ExportUtil.java b/src/java/com/healthmarketscience/jackcess/ExportUtil.java
new file mode 100644 (file)
index 0000000..6c903fb
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+Copyright (c) 2007 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * 
+ * @author Frank Gerbig
+ */
+public class ExportUtil {
+
+  private static final Log LOG = LogFactory.getLog(ExportUtil.class);
+
+  public static final String DEFAULT_DELIMITER = ",";
+  public static final char DEFAULT_QUOTE_CHAR = '"';
+  public static final String DEFAULT_FILE_EXT = "csv";
+
+
+  private ExportUtil() {
+  }
+
+  /**
+   * Copy all tables into new delimited text files <br>
+   * Equivalent to: {@code exportAll(db, dir, "csv");}
+   * 
+   * @param db
+   *          Database the table to export belongs to
+   * @param dir
+   *          The directory where the new files will be created
+   * 
+   * @see #exportAll(Database,File,String)
+   */
+  public static void exportAll(Database db, File dir)
+      throws IOException {
+    exportAll(db, dir, DEFAULT_FILE_EXT);
+  }
+
+  /**
+   * Copy all tables into new delimited text files <br>
+   * Equivalent to: {@code exportFile(db, name, f, false, null, '"',
+   * SimpleExportFilter.INSTANCE);}
+   * 
+   * @param db
+   *          Database the table to export belongs to
+   * @param dir
+   *          The directory where the new files will be created
+   * @param ext
+   *          The file extension of the new files
+   * 
+   * @see #exportFile(Database,String,File,boolean,String,char,ExportFilter)
+   */
+  public static void exportAll(Database db, File dir,
+      String ext) throws IOException {
+    for (String tableName : db.getTableNames()) {
+      exportFile(db, tableName, new File(dir, tableName + "." + ext), false,
+          DEFAULT_DELIMITER, DEFAULT_QUOTE_CHAR, SimpleExportFilter.INSTANCE);
+    }
+  }
+
+  /**
+   * Copy all tables into new delimited text files <br>
+   * Equivalent to: {@code exportFile(db, name, f, false, null, '"',
+   * SimpleExportFilter.INSTANCE);}
+   * 
+   * @param db
+   *          Database the table to export belongs to
+   * @param dir
+   *          The directory where the new files will be created
+   * @param ext
+   *          The file extension of the new files
+   * @param header
+   *          If <code>true</code> the first line contains the column names
+   * 
+   * @see #exportFile(Database,String,File,boolean,String,char,ExportFilter)
+   */
+  public static void exportAll(Database db, File dir,
+      String ext, boolean header)
+      throws IOException {
+    for (String tableName : db.getTableNames()) {
+      exportFile(db, tableName, new File(dir, tableName + "." + ext), header,
+          DEFAULT_DELIMITER, DEFAULT_QUOTE_CHAR, SimpleExportFilter.INSTANCE);
+    }
+  }
+
+  /**
+   * Copy all tables into new delimited text files <br>
+   * Equivalent to: {@code exportFile(db, name, f, false, null, '"',
+   * SimpleExportFilter.INSTANCE);}
+   * 
+   * @param db
+   *          Database the table to export belongs to
+   * @param dir
+   *          The directory where the new files will be created
+   * @param ext
+   *          The file extension of the new files
+   * @param header
+   *          If <code>true</code> the first line contains the column names
+   * @param delim
+   *          The column delimiter, <code>null</code> for default (comma)
+   * @param quote
+   *          The quote character
+   * @param filter
+   *          valid export filter
+   * 
+   * @see #exportFile(Database,String,File,boolean,String,char,ExportFilter)
+   */
+  public static void exportAll(Database db, File dir,
+      String ext, boolean header, String delim,
+      char quote, ExportFilter filter)
+      throws IOException {
+    for (String tableName : db.getTableNames()) {
+      exportFile(db, tableName, new File(dir, tableName + "." + ext), header,
+          delim, quote, filter);
+    }
+  }
+
+  /**
+   * Copy a table into a new delimited text file <br>
+   * Equivalent to: {@code exportFile(db, name, f, false, null, '"',
+   * SimpleExportFilter.INSTANCE);}
+   * 
+   * @param db
+   *          Database the table to export belongs to
+   * @param tableName
+   *          Name of the table to export
+   * @param f
+   *          New file to create
+   * 
+   * @see #exportFile(Database,String,File,boolean,String,char,ExportFilter)
+   */
+  public static void exportFile(Database db, String tableName,
+      File f) throws IOException {
+    exportFile(db, tableName, f, false, DEFAULT_DELIMITER, DEFAULT_QUOTE_CHAR, 
+        SimpleExportFilter.INSTANCE);
+  }
+
+  /**
+   * Copy a table into a new delimited text file <br>
+   * Nearly equivalent to: {@code exportWriter(db, name, new BufferedWriter(f),
+   * header, delim, quote, filter);}
+   * 
+   * @param db
+   *          Database the table to export belongs to
+   * @param tableName
+   *          Name of the table to export
+   * @param f
+   *          New file to create
+   * @param header
+   *          If <code>true</code> the first line contains the column names
+   * @param delim
+   *          The column delimiter, <code>null</code> for default (comma)
+   * @param quote
+   *          The quote character
+   * @param filter
+   *          valid export filter
+   * 
+   * @see #exportWriter(Database,String,BufferedWriter,boolean,String,char,ExportFilter)
+   */
+  public static void exportFile(Database db, String tableName,
+      File f, boolean header, String delim, char quote,
+      ExportFilter filter) throws IOException {
+    BufferedWriter out = null;
+    try {
+      out = new BufferedWriter(new FileWriter(f));
+      exportWriter(db, tableName, out, header, delim, quote, filter);
+      out.close();
+    } finally {
+      if (out != null) {
+        try {
+          out.close();
+        } catch (Exception ex) {
+          LOG.warn("Could not close file " + f.getAbsolutePath(), ex);
+        }
+      }
+    }
+  }
+
+  /**
+   * Copy a table in this database into a new delimited text file <br>
+   * Equivalent to: {@code exportWriter(db, name, out, false, null, '"',
+   * SimpleExportFilter.INSTANCE);}
+   * 
+   * @param db
+   *          Database the table to export belongs to
+   * @param tableName
+   *          Name of the table to export
+   * @param out
+   *          Writer to export to
+   * 
+   * @see #exportWriter(Database,String,BufferedWriter,boolean,String,char,ExportFilter)
+   */
+  public static void exportWriter(Database db, String tableName,
+      BufferedWriter out) throws IOException {
+    exportWriter(db, tableName, out, false, DEFAULT_DELIMITER, 
+                 DEFAULT_QUOTE_CHAR, SimpleExportFilter.INSTANCE);
+  }
+
+  /**
+   * Copy a table in this database into a new delimited text file. <br>
+   * Equivalent to: {@code exportWriter(Cursor.createCursor(db.getTable(tableName)), out, header, delim, quote, filter);}
+   * 
+   * @param db
+   *          Database the table to export belongs to
+   * @param tableName
+   *          Name of the table to export
+   * @param out
+   *          Writer to export to
+   * @param header
+   *          If <code>true</code> the first line contains the column names
+   * @param delim
+   *          The column delimiter, <code>null</code> for default (comma)
+   * @param quote
+   *          The quote character
+   * @param filter
+   *          valid export filter
+   * 
+   * @see #exportWriter(Cursor,BufferedWriter,boolean,String,char,ExportFilter)
+   */
+  public static void exportWriter(Database db, String tableName,
+      BufferedWriter out, boolean header, String delim,
+      char quote, ExportFilter filter)
+      throws IOException 
+  {
+    exportWriter(Cursor.createCursor(db.getTable(tableName)), out, header,
+                 delim, quote, filter);
+  }
+
+  /**
+   * Copy a table in this database into a new delimited text file.
+   * 
+   * @param cursor
+   *          Cursor to export
+   * @param out
+   *          Writer to export to
+   * @param header
+   *          If <code>true</code> the first line contains the column names
+   * @param delim
+   *          The column delimiter, <code>null</code> for default (comma)
+   * @param quote
+   *          The quote character
+   * @param filter
+   *          valid export filter
+   */
+  public static void exportWriter(Cursor cursor,
+      BufferedWriter out, boolean header, String delim,
+      char quote, ExportFilter filter)
+      throws IOException 
+  {
+    String delimiter = (delim == null) ? DEFAULT_DELIMITER : delim;
+
+    // create pattern which will indicate whether or not a value needs to be
+    // quoted or not (contains delimiter, separator, or newline)
+    Pattern needsQuotePattern = Pattern.compile(
+        "(?:" + Pattern.quote(delimiter) + ")|(?:" + 
+        Pattern.quote("" + quote) + ")|(?:[\n\r])");
+
+    List<Column> origCols = cursor.getTable().getColumns();
+    List<Column> columns = new ArrayList<Column>(origCols);
+    columns = filter.filterColumns(columns);
+
+    Collection<String> columnNames = null;
+    if(!origCols.equals(columns)) {
+
+      // columns have been filtered
+      columnNames = new HashSet<String>();
+      for (Column c : columns) {
+        columnNames.add(c.getName());
+      }
+    }
+
+    // print the header row (if desired)
+    if (header) {
+      for (Iterator<Column> iter = columns.iterator(); iter.hasNext();) {
+
+        writeValue(out, iter.next().getName(), quote, needsQuotePattern);
+
+        if (iter.hasNext()) {
+          out.write(delimiter);
+        }
+      }
+      out.newLine();
+    }
+
+    // print the data rows
+    Map<String, Object> row;
+    Object[] unfilteredRowData = new Object[columns.size()];
+    while ((row = cursor.getNextRow(columnNames)) != null) {
+
+      // fill raw row data in array
+      for (int i = 0; i < columns.size(); i++) {
+        unfilteredRowData[i] = row.get(columns.get(i).getName());
+      }
+
+      // apply filter
+      Object[] rowData = filter.filterRow(unfilteredRowData);
+
+      // print row
+      for (int i = 0; i < columns.size(); i++) {
+
+        Object obj = rowData[i];
+        if(obj != null) {
+
+          String value = null;
+          if(obj instanceof byte[]) {
+
+            value = ByteUtil.toHexString((byte[])obj);
+
+          } else {
+
+            value = String.valueOf(rowData[i]);
+          }
+
+          writeValue(out, value, quote, needsQuotePattern);
+        }
+
+        if (i < columns.size() - 1) {
+          out.write(delimiter);
+        }
+      }
+
+      out.newLine();
+    }
+
+    out.flush();
+  }
+
+  private static void writeValue(BufferedWriter out, String value, char quote,
+                                 Pattern needsQuotePattern) 
+    throws IOException
+  {
+    if(!needsQuotePattern.matcher(value).find()) {
+
+      // no quotes necessary
+      out.write(value);
+      return;
+    }
+
+    // wrap the value in quotes and handle internal quotes
+    out.write(quote);
+    for (int i = 0; i < value.length(); ++i) {
+      char c = value.charAt(i);
+
+      if (c == quote) {
+        out.write(quote);
+      }
+      out.write(c);
+    }
+    out.write(quote);
+  }
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/SimpleExportFilter.java b/src/java/com/healthmarketscience/jackcess/SimpleExportFilter.java
new file mode 100644 (file)
index 0000000..3669a94
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+Copyright (c) 2007 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+ */
+
+package com.healthmarketscience.jackcess;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Simple concrete implementation of ImportFilter which just returns the given
+ * values.
+ * 
+ * @author James Ahlborn
+ */
+public class SimpleExportFilter implements ExportFilter {
+
+  public static final SimpleExportFilter INSTANCE = new SimpleExportFilter();
+
+  public SimpleExportFilter() {
+  }
+
+  public List<Column> filterColumns(List<Column> columns) throws IOException {
+    return columns;
+  }
+
+  public Object[] filterRow(Object[] row) throws IOException {
+    return row;
+  }
+
+}
diff --git a/test/src/java/com/healthmarketscience/jackcess/ExportTest.java b/test/src/java/com/healthmarketscience/jackcess/ExportTest.java
new file mode 100644 (file)
index 0000000..a0528f4
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+Copyright (c) 2007 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess;
+
+import java.io.BufferedWriter;
+import java.io.StringWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import junit.framework.TestCase;
+import org.apache.commons.lang.SystemUtils;
+
+import static com.healthmarketscience.jackcess.Database.*;
+import static com.healthmarketscience.jackcess.DatabaseTest.*;
+
+/**
+ *
+ * @author James Ahlborn
+ */
+public class ExportTest extends TestCase
+{
+  private static final String NL = SystemUtils.LINE_SEPARATOR;
+
+
+  public ExportTest(String name) {
+    super(name);
+  }
+
+  public void testExportToFile() throws Exception
+  {
+    DateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
+
+    for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
+      Database db = create(fileFormat);
+
+      Table t = new TableBuilder("test")
+        .addColumn(new ColumnBuilder("col1", DataType.TEXT).toColumn())
+        .addColumn(new ColumnBuilder("col2", DataType.LONG).toColumn())
+        .addColumn(new ColumnBuilder("col3", DataType.DOUBLE).toColumn())
+        .addColumn(new ColumnBuilder("col4", DataType.OLE).toColumn())
+        .addColumn(new ColumnBuilder("col5", DataType.BOOLEAN).toColumn())
+        .addColumn(new ColumnBuilder("col6", DataType.SHORT_DATE_TIME).toColumn())
+        .toTable(db);
+
+      t.addRow("some text||some more", 13, 13.25, createString(30).getBytes(), true, df.parse("19801231 00:00:00"));
+
+      t.addRow("crazy'data\"here", -345, -0.000345, createString(7).getBytes(),
+               true, null);
+
+      StringWriter out = new StringWriter();
+
+      ExportUtil.exportWriter(db, "test", new BufferedWriter(out));
+
+      String expected = 
+        "some text||some more,13,13.25,\"61 62 63 64  65 66 67 68  69 6A 6B 6C  6D 6E 6F 70  71 72 73 74  75 76 77 78" + NL +
+        "79 7A 61 62  63 64\",true,Wed Dec 31 00:00:00 EST 1980" + NL +
+        "\"crazy'data\"\"here\",-345,-3.45E-4,61 62 63 64  65 66 67,true," + NL;
+
+      assertEquals(expected, out.toString());
+
+      out = new StringWriter();
+      
+      ExportUtil.exportWriter(db, "test", new BufferedWriter(out),
+                              true, "||", '\'', SimpleExportFilter.INSTANCE);
+
+      expected = 
+        "col1||col2||col3||col4||col5||col6" + NL +
+        "'some text||some more'||13||13.25||'61 62 63 64  65 66 67 68  69 6A 6B 6C  6D 6E 6F 70  71 72 73 74  75 76 77 78" + NL +
+        "79 7A 61 62  63 64'||true||Wed Dec 31 00:00:00 EST 1980" + NL +
+        "'crazy''data\"here'||-345||-3.45E-4||61 62 63 64  65 66 67||true||" + NL;
+      assertEquals(expected, out.toString());
+    }
+  }
+
+}