diff --git a/.classpath b/.classpath
index 05a4eb25f..89118c7af 100644
--- a/.classpath
+++ b/.classpath
@@ -10,7 +10,7 @@
-
+
@@ -65,5 +65,10 @@
+
+
+
+
+
diff --git a/main/src/com/google/refine/importers/OpenOfficeImporter.java b/main/src/com/google/refine/importers/OpenOfficeImporter.java
new file mode 100644
index 000000000..af9da416a
--- /dev/null
+++ b/main/src/com/google/refine/importers/OpenOfficeImporter.java
@@ -0,0 +1,266 @@
+/*
+
+Copyright 2011, Thomas F. Morris
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+package com.google.refine.importers;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.odftoolkit.odfdom.doc.OdfDocument;
+import org.odftoolkit.odfdom.doc.table.OdfTable;
+import org.odftoolkit.odfdom.doc.table.OdfTableCell;
+import org.odftoolkit.odfdom.doc.table.OdfTableRow;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.refine.ProjectMetadata;
+import com.google.refine.importing.ImportingJob;
+import com.google.refine.importing.ImportingUtilities;
+import com.google.refine.model.Cell;
+import com.google.refine.model.Project;
+import com.google.refine.model.Recon;
+import com.google.refine.model.ReconCandidate;
+import com.google.refine.model.Recon.Judgment;
+import com.google.refine.util.JSONUtilities;
+
+
+public class OpenOfficeImporter extends TabularImportingParserBase {
+ final static Logger logger = LoggerFactory.getLogger("open office");
+
+ public OpenOfficeImporter() {
+ super(true);
+ }
+
+
+ @Override
+ public JSONObject createParserUIInitializationData(
+ ImportingJob job, List fileRecords, String format) {
+ JSONObject options = super.createParserUIInitializationData(job, fileRecords, format);
+
+ JSONArray sheetRecords = new JSONArray();
+ JSONUtilities.safePut(options, "sheetRecords", sheetRecords);
+ OdfDocument odfDoc = null;
+ try {
+ JSONObject firstFileRecord = fileRecords.get(0);
+ File file = ImportingUtilities.getFile(job, firstFileRecord);
+ InputStream is = new FileInputStream(file);
+ odfDoc = OdfDocument.loadDocument(is);
+ List tables = odfDoc.getTableList();
+ int sheetCount = tables.size();
+
+ boolean hasData = false;
+ for (int i = 0; i < sheetCount; i++) {
+ OdfTable sheet = tables.get(i);
+ int rows = sheet.getRowCount();
+
+ JSONObject sheetRecord = new JSONObject();
+ JSONUtilities.safePut(sheetRecord, "name", sheet.getTableName());
+ JSONUtilities.safePut(sheetRecord, "rows", rows);
+ if (hasData) {
+ JSONUtilities.safePut(sheetRecord, "selected", false);
+ } else if (rows > 0) {
+ JSONUtilities.safePut(sheetRecord, "selected", true);
+ hasData = true;
+ }
+ JSONUtilities.append(sheetRecords, sheetRecord);
+ }
+ } catch (FileNotFoundException e) {
+ logger.info("File not found",e);
+ } catch (Exception e) {
+ // ODF throws *VERY* wide exceptions
+ logger.info("Error reading ODF spreadsheet",e);
+ } finally {
+ if (odfDoc != null) {
+ odfDoc.close();
+ }
+ }
+ return options;
+ }
+
+
+ @Override
+ public void parseOneFile(
+ Project project,
+ ProjectMetadata metadata,
+ ImportingJob job,
+ String fileSource,
+ InputStream inputStream,
+ int limit,
+ JSONObject options,
+ List exceptions
+ ) {
+ OdfDocument odfDoc;
+ try {
+ odfDoc = OdfDocument.loadDocument(inputStream);
+ } catch (Exception e) { // Ugh! could they throw any wider exception?
+ exceptions.add(e);
+ return;
+ }
+
+ List tables = odfDoc.getTableList();
+
+ int[] sheets = JSONUtilities.getIntArray(options, "sheets");
+ for (int sheetIndex : sheets) {
+ final OdfTable table = tables.get(sheetIndex);
+ final int lastRow = table.getRowCount();
+
+ TableDataReader dataReader = new TableDataReader() {
+ int nextRow = 0;
+ Map reconMap = new HashMap();
+
+ @Override
+ public List