You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2013/07/22 10:10:30 UTC

[17/64] [partial] Hard rename of all 'org/eobjects' folders to 'org/apache'.

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/couchdb/src/test/java/org/eobjects/metamodel/couchdb/CouchDbDataContextTest.java
----------------------------------------------------------------------
diff --git a/couchdb/src/test/java/org/eobjects/metamodel/couchdb/CouchDbDataContextTest.java b/couchdb/src/test/java/org/eobjects/metamodel/couchdb/CouchDbDataContextTest.java
deleted file mode 100644
index 403a64c..0000000
--- a/couchdb/src/test/java/org/eobjects/metamodel/couchdb/CouchDbDataContextTest.java
+++ /dev/null
@@ -1,354 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.eobjects.metamodel.couchdb;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import junit.framework.TestCase;
-
-import org.ektorp.CouchDbConnector;
-import org.ektorp.DbAccessException;
-import org.ektorp.http.HttpClient;
-import org.ektorp.http.StdHttpClient;
-import org.ektorp.impl.StdCouchDbInstance;
-import org.eobjects.metamodel.UpdateCallback;
-import org.eobjects.metamodel.UpdateScript;
-import org.eobjects.metamodel.data.DataSet;
-import org.eobjects.metamodel.data.Row;
-import org.eobjects.metamodel.drop.DropTable;
-import org.eobjects.metamodel.insert.InsertInto;
-import org.eobjects.metamodel.schema.Column;
-import org.eobjects.metamodel.schema.ColumnType;
-import org.eobjects.metamodel.schema.Schema;
-import org.eobjects.metamodel.schema.Table;
-import org.eobjects.metamodel.util.SimpleTableDef;
-
-public class CouchDbDataContextTest extends TestCase {
-
-    private static final String TEST_DATABASE_NAME = "eobjects_metamodel_test";
-
-    private boolean serverAvailable;
-
-    private HttpClient httpClient;
-    private StdCouchDbInstance couchDbInstance;
-    private CouchDbConnector connector;
-    private SimpleTableDef predefinedTableDef;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        httpClient = new StdHttpClient.Builder().host("localhost").build();
-
-        // set up a simple database
-        couchDbInstance = new StdCouchDbInstance(httpClient);
-
-        try {
-            if (couchDbInstance.getAllDatabases().contains(TEST_DATABASE_NAME)) {
-                throw new IllegalStateException("Couch DB instance already has a database called " + TEST_DATABASE_NAME);
-            }
-            connector = couchDbInstance.createConnector(TEST_DATABASE_NAME, true);
-            System.out.println("Running CouchDB integration tests");
-            serverAvailable = true;
-        } catch (DbAccessException e) {
-            System.out.println("!!! WARNING: Skipping CouchDB tests because local server is not available");
-            e.printStackTrace();
-            serverAvailable = false;
-        }
-
-        final String[] columnNames = new String[] { "name", "gender", "age" };
-        final ColumnType[] columnTypes = new ColumnType[] { ColumnType.VARCHAR, ColumnType.CHAR, ColumnType.INTEGER };
-        predefinedTableDef = new SimpleTableDef(TEST_DATABASE_NAME, columnNames, columnTypes);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
-        connector = null;
-
-        if (serverAvailable) {
-            couchDbInstance.deleteDatabase(TEST_DATABASE_NAME);
-        }
-
-        httpClient.shutdown();
-    }
-
-    public void testWorkingWithMapsAndLists() throws Exception {
-        if (!serverAvailable) {
-            return;
-        }
-
-        connector = couchDbInstance.createConnector("test_table_map_and_list", true);
-
-        final CouchDbDataContext dc = new CouchDbDataContext(couchDbInstance, new SimpleTableDef("test_table_map_and_list",
-                new String[] { "id", "foo", "bar" }, new ColumnType[] { ColumnType.INTEGER, ColumnType.MAP, ColumnType.LIST }));
-        Table table = null;
-        try {
-            table = dc.getTableByQualifiedLabel("test_table_map_and_list");
-            Map<String, Object> exampleMap = new LinkedHashMap<String, Object>();
-            exampleMap.put("hello", Arrays.asList("world", "welt", "verden"));
-            exampleMap.put("foo", "bar");
-
-            List<Map<String, Object>> exampleList = new ArrayList<Map<String, Object>>();
-            exampleList.add(new LinkedHashMap<String, Object>());
-            Map<String, Object> exampleMap2 = new LinkedHashMap<String, Object>();
-            exampleMap2.put("meta", "model");
-            exampleMap2.put("couch", "db");
-            exampleList.add(exampleMap2);
-
-            dc.executeUpdate(new InsertInto(table).value("id", 1).value("foo", exampleMap).value("bar", exampleList));
-
-            DataSet ds = dc.query().from(table).select("id","foo","bar").execute();
-            assertTrue(ds.next());
-            Row row = ds.getRow();
-            assertFalse(ds.next());
-            ds.close();
-
-            assertEquals(
-                    "Row[values=[1, {hello=[world, welt, verden], foo=bar}, [{}, {meta=model, couch=db}]]]",
-                    row.toString());
-            assertTrue(row.getValue(0) instanceof Integer);
-            assertTrue(row.getValue(1) instanceof Map);
-            assertTrue(row.getValue(2) instanceof List);
-
-        } finally {
-            dc.executeUpdate(new DropTable(table));
-        }
-
-    }
-
-    public void testCreateUpdateDeleteScenario() throws Exception {
-        if (!serverAvailable) {
-            return;
-        }
-
-        final CouchDbDataContext dc = new CouchDbDataContext(couchDbInstance);
-
-        // first delete the manually created database!
-        dc.executeUpdate(new UpdateScript() {
-            @Override
-            public void run(UpdateCallback callback) {
-                callback.dropTable(TEST_DATABASE_NAME).execute();
-            }
-        });
-
-        assertNull(dc.getDefaultSchema().getTableByName(TEST_DATABASE_NAME));
-
-        dc.executeUpdate(new UpdateScript() {
-            @Override
-            public void run(UpdateCallback callback) {
-                Table table = callback.createTable(dc.getDefaultSchema(), TEST_DATABASE_NAME).withColumn("foo")
-                        .ofType(ColumnType.VARCHAR).withColumn("greeting").ofType(ColumnType.VARCHAR).execute();
-                assertEquals("[_id, _rev, foo, greeting]", Arrays.toString(table.getColumnNames()));
-            }
-        });
-
-        dc.executeUpdate(new UpdateScript() {
-            @Override
-            public void run(UpdateCallback callback) {
-                callback.insertInto(TEST_DATABASE_NAME).value("foo", "bar").value("greeting", "hello").execute();
-                callback.insertInto(TEST_DATABASE_NAME).value("foo", "baz").value("greeting", "hi").execute();
-            }
-        });
-
-        DataSet ds = dc.query().from(TEST_DATABASE_NAME).select("_id", "foo", "greeting").execute();
-        assertTrue(ds.next());
-        assertNotNull(ds.getRow().getValue(0));
-        assertEquals("bar", ds.getRow().getValue(1));
-        assertEquals("hello", ds.getRow().getValue(2));
-        assertTrue(ds.next());
-        assertNotNull(ds.getRow().getValue(0));
-        assertEquals("baz", ds.getRow().getValue(1));
-        assertEquals("hi", ds.getRow().getValue(2));
-        assertFalse(ds.next());
-        ds.close();
-
-        dc.executeUpdate(new UpdateScript() {
-            @Override
-            public void run(UpdateCallback callback) {
-                callback.update(TEST_DATABASE_NAME).value("greeting", "howdy").where("foo").isEquals("baz").execute();
-
-                callback.update(TEST_DATABASE_NAME).value("foo", "foo").where("foo").isEquals("bar").execute();
-            }
-        });
-
-        ds = dc.query().from(TEST_DATABASE_NAME).select("_id", "foo", "greeting").execute();
-        assertTrue(ds.next());
-        assertNotNull(ds.getRow().getValue(0));
-        assertEquals("foo", ds.getRow().getValue(1));
-        assertEquals("hello", ds.getRow().getValue(2));
-        assertTrue(ds.next());
-        assertNotNull(ds.getRow().getValue(0));
-        assertEquals("baz", ds.getRow().getValue(1));
-        assertEquals("howdy", ds.getRow().getValue(2));
-        assertFalse(ds.next());
-        ds.close();
-    }
-
-    public void testBasicQuery() throws Exception {
-        if (!serverAvailable) {
-            return;
-        }
-
-        // insert a few records
-        {
-            HashMap<String, Object> map;
-
-            map = new HashMap<String, Object>();
-            map.put("name", "John Doe");
-            map.put("age", 30);
-            connector.create(map);
-
-            map = new HashMap<String, Object>();
-            map.put("name", "Jane Doe");
-            map.put("gender", 'F');
-            connector.create(map);
-        }
-
-        // create datacontext using detected schema
-        SimpleTableDef tableDef = CouchDbDataContext.detectTable(connector);
-        CouchDbDataContext dc = new CouchDbDataContext(couchDbInstance, tableDef);
-
-        // verify schema and execute query
-        Schema schema = dc.getMainSchema();
-        assertEquals("[eobjects_metamodel_test]", Arrays.toString(schema.getTableNames()));
-
-        assertEquals("[_id, _rev, age, gender, name]",
-                Arrays.toString(schema.getTableByName(TEST_DATABASE_NAME).getColumnNames()));
-        Column idColumn = schema.getTableByName(TEST_DATABASE_NAME).getColumnByName("_id");
-        assertEquals("Column[name=_id,columnNumber=0,type=VARCHAR,nullable=true,nativeType=null,columnSize=null]",
-                idColumn.toString());
-        assertTrue(idColumn.isPrimaryKey());
-
-        assertEquals("Column[name=_rev,columnNumber=1,type=VARCHAR,nullable=true,nativeType=null,columnSize=null]", schema
-                .getTableByName(TEST_DATABASE_NAME).getColumnByName("_rev").toString());
-
-        DataSet ds;
-
-        ds = dc.query().from(TEST_DATABASE_NAME).select("name").and("age").execute();
-        assertTrue(ds.next());
-        assertEquals("Row[values=[John Doe, 30]]", ds.getRow().toString());
-        assertTrue(ds.next());
-        assertEquals("Row[values=[Jane Doe, null]]", ds.getRow().toString());
-        assertFalse(ds.next());
-        ds.close();
-
-        ds = dc.query().from(TEST_DATABASE_NAME).select("name").and("gender").where("age").isNull().execute();
-        assertTrue(ds.next());
-        assertEquals("Row[values=[Jane Doe, F]]", ds.getRow().toString());
-        assertFalse(ds.next());
-        ds.close();
-    }
-
-    public void testFirstRowAndLastRow() throws Exception {
-        if (!serverAvailable) {
-            return;
-        }
-
-        // insert a few records
-        {
-            HashMap<String, Object> map;
-
-            map = new HashMap<String, Object>();
-            map.put("name", "John Doe");
-            map.put("age", 30);
-            connector.create(map);
-
-            map = new HashMap<String, Object>();
-            map.put("name", "Jane Doe");
-            map.put("gender", 'F');
-            connector.create(map);
-        }
-
-        // create datacontext using detected schema
-        SimpleTableDef tableDef = CouchDbDataContext.detectTable(connector);
-        CouchDbDataContext dc = new CouchDbDataContext(couchDbInstance, tableDef);
-
-        DataSet ds1 = dc.query().from(TEST_DATABASE_NAME).select("name").and("age").firstRow(2).execute();
-        DataSet ds2 = dc.query().from(TEST_DATABASE_NAME).select("name").and("age").maxRows(1).execute();
-
-        assertTrue("Class: " + ds1.getClass().getName(), ds1 instanceof CouchDbDataSet);
-        assertTrue("Class: " + ds2.getClass().getName(), ds2 instanceof CouchDbDataSet);
-
-        assertTrue(ds1.next());
-        assertTrue(ds2.next());
-
-        final Row row1 = ds1.getRow();
-        final Row row2 = ds2.getRow();
-
-        assertFalse(ds1.next());
-        assertFalse(ds2.next());
-
-        assertEquals("Row[values=[Jane Doe, null]]", row1.toString());
-        assertEquals("Row[values=[John Doe, 30]]", row2.toString());
-
-        ds1.close();
-        ds2.close();
-    }
-
-    public void testInsert() throws Exception {
-        if (!serverAvailable) {
-            return;
-        }
-
-        // create datacontext using predefined table def
-        CouchDbDataContext dc = new CouchDbDataContext(httpClient, predefinedTableDef);
-        Table table = dc.getTableByQualifiedLabel(TEST_DATABASE_NAME);
-        assertNotNull(table);
-
-        assertEquals("[_id, _rev, name, gender, age]", Arrays.toString(table.getColumnNames()));
-
-        DataSet ds;
-
-        // assert not rows in DB
-        ds = dc.query().from(TEST_DATABASE_NAME).selectCount().execute();
-        assertTrue(ds.next());
-        assertEquals(0, ((Number) ds.getRow().getValue(0)).intValue());
-        assertFalse(ds.next());
-        ds.close();
-
-        dc.executeUpdate(new UpdateScript() {
-            @Override
-            public void run(UpdateCallback callback) {
-                callback.insertInto(TEST_DATABASE_NAME).value("name", "foo").value("gender", 'M').execute();
-                callback.insertInto(TEST_DATABASE_NAME).value("name", "bar").value("age", 32).execute();
-            }
-        });
-
-        // now count should be 2
-        ds = dc.query().from(TEST_DATABASE_NAME).selectCount().execute();
-        assertTrue(ds.next());
-        assertEquals(2, ((Number) ds.getRow().getValue(0)).intValue());
-        assertFalse(ds.next());
-        ds.close();
-
-        ds = dc.query().from(TEST_DATABASE_NAME).select("name", "gender", "age").execute();
-        assertTrue(ds.next());
-        assertEquals("Row[values=[foo, M, null]]", ds.getRow().toString());
-        assertTrue(ds.next());
-        assertEquals("Row[values=[bar, null, 32]]", ds.getRow().toString());
-        assertFalse(ds.next());
-        ds.close();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvConfiguration.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvConfiguration.java b/csv/src/main/java/org/apache/metamodel/csv/CsvConfiguration.java
new file mode 100644
index 0000000..8e34913
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvConfiguration.java
@@ -0,0 +1,160 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.eobjects.metamodel.util.BaseObject;
+import org.eobjects.metamodel.util.FileHelper;
+
+/**
+ * Represents the configuration for reading/parsing CSV files.
+ * 
+ * @author Kasper Sørensen
+ */
+public final class CsvConfiguration extends BaseObject implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * The value is '\\uFFFF', the "not a character" value which should not
+	 * occur in any valid Unicode string. This special char can be used to
+	 * disable either quote chars or escape chars.
+	 */
+	public static final char NOT_A_CHAR = '\uFFFF';
+	public static final int NO_COLUMN_NAME_LINE = 0;
+	public static final int DEFAULT_COLUMN_NAME_LINE = 1;
+	public static final char DEFAULT_SEPARATOR_CHAR = ',';
+	public static final char DEFAULT_QUOTE_CHAR = '"';
+	public static final char DEFAULT_ESCAPE_CHAR = '\\';
+
+	private final int columnNameLineNumber;
+	private final String encoding;
+	private final char separatorChar;
+	private final char quoteChar;
+	private final char escapeChar;
+	private final boolean failOnInconsistentRowLength;
+
+	public CsvConfiguration() {
+		this(DEFAULT_COLUMN_NAME_LINE);
+	}
+
+	public CsvConfiguration(int columnNameLineNumber) {
+		this(columnNameLineNumber, FileHelper.DEFAULT_ENCODING,
+				DEFAULT_SEPARATOR_CHAR, DEFAULT_QUOTE_CHAR, DEFAULT_ESCAPE_CHAR);
+	}
+
+	public CsvConfiguration(int columnNameLineNumber, String encoding,
+			char separatorChar, char quoteChar, char escapeChar) {
+		this(columnNameLineNumber, encoding, separatorChar, quoteChar,
+				escapeChar, false);
+	}
+
+	public CsvConfiguration(int columnNameLineNumber, String encoding,
+			char separatorChar, char quoteChar, char escapeChar,
+			boolean failOnInconsistentRowLength) {
+		this.columnNameLineNumber = columnNameLineNumber;
+		this.encoding = encoding;
+		this.separatorChar = separatorChar;
+		this.quoteChar = quoteChar;
+		this.escapeChar = escapeChar;
+		this.failOnInconsistentRowLength = failOnInconsistentRowLength;
+	}
+
+	/**
+	 * Determines whether to fail (by throwing an
+	 * {@link InconsistentRowLengthException}) if a line in the CSV file has
+	 * inconsistent amounts of columns.
+	 * 
+	 * If set to false (default) MetaModel will gracefully fill in missing null
+	 * values in or ignore additional values in a line.
+	 * 
+	 * @return a boolean indicating whether to fail or gracefully compensate for
+	 *         inconsistent lines in the CSV files.
+	 */
+	public boolean isFailOnInconsistentRowLength() {
+		return failOnInconsistentRowLength;
+	}
+
+	/**
+	 * The line number (1 based) from which to get the names of the columns.
+	 * 
+	 * @return the line number (1 based)
+	 */
+	public int getColumnNameLineNumber() {
+		return columnNameLineNumber;
+	}
+
+	/**
+	 * Gets the file encoding to use for reading the file.
+	 * 
+	 * @return the text encoding of the file.
+	 */
+	public String getEncoding() {
+		return encoding;
+	}
+
+	/**
+	 * Gets the separator char (typically comma or semicolon) for separating
+	 * values.
+	 * 
+	 * @return the separator char
+	 */
+	public char getSeparatorChar() {
+		return separatorChar;
+	}
+
+	/**
+	 * Gets the quote char, used for encapsulating values.
+	 * 
+	 * @return the quote char
+	 */
+	public char getQuoteChar() {
+		return quoteChar;
+	}
+
+	/**
+	 * Gets the escape char, used for escaping eg. quote chars inside values.
+	 * 
+	 * @return the escape char
+	 */
+	public char getEscapeChar() {
+		return escapeChar;
+	}
+
+	@Override
+	protected void decorateIdentity(List<Object> identifiers) {
+		identifiers.add(columnNameLineNumber);
+		identifiers.add(encoding);
+		identifiers.add(separatorChar);
+		identifiers.add(quoteChar);
+		identifiers.add(escapeChar);
+		identifiers.add(failOnInconsistentRowLength);
+	}
+
+	@Override
+	public String toString() {
+		return "CsvConfiguration[columnNameLineNumber=" + columnNameLineNumber
+				+ ", encoding=" + encoding + ", separatorChar=" + separatorChar
+				+ ", quoteChar=" + quoteChar + ", escapeChar=" + escapeChar
+				+ ", failOnInconsistentRowLength="
+				+ failOnInconsistentRowLength + "]";
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvCreateTableBuilder.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvCreateTableBuilder.java b/csv/src/main/java/org/apache/metamodel/csv/CsvCreateTableBuilder.java
new file mode 100644
index 0000000..906d2e1
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvCreateTableBuilder.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import org.eobjects.metamodel.create.AbstractTableCreationBuilder;
+import org.eobjects.metamodel.schema.MutableTable;
+import org.eobjects.metamodel.schema.Schema;
+import org.eobjects.metamodel.schema.Table;
+
+final class CsvCreateTableBuilder extends
+		AbstractTableCreationBuilder<CsvUpdateCallback> {
+
+	public CsvCreateTableBuilder(CsvUpdateCallback updateCallback,
+			Schema schema, String name) {
+		super(updateCallback, schema, name);
+		if (!(schema instanceof CsvSchema)) {
+			throw new IllegalArgumentException("Not a valid CSV schema: "
+					+ schema);
+		}
+	}
+
+	@Override
+	public Table execute() {
+		CsvUpdateCallback csvUpdateCallback = getUpdateCallback();
+
+		MutableTable table = getTable();
+		String[] columnNames = table.getColumnNames();
+		csvUpdateCallback.writeRow(columnNames, false);
+
+		CsvSchema schema = (CsvSchema) table.getSchema();
+		CsvTable csvTable = new CsvTable(schema, table.getColumnNames());
+		schema.setTable(csvTable);
+		return csvTable;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvDataContext.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvDataContext.java b/csv/src/main/java/org/apache/metamodel/csv/CsvDataContext.java
new file mode 100644
index 0000000..6409596
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvDataContext.java
@@ -0,0 +1,393 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.util.List;
+
+import org.eobjects.metamodel.MetaModelException;
+import org.eobjects.metamodel.QueryPostprocessDataContext;
+import org.eobjects.metamodel.UpdateScript;
+import org.eobjects.metamodel.UpdateableDataContext;
+import org.eobjects.metamodel.data.DataSet;
+import org.eobjects.metamodel.query.FilterItem;
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.schema.Table;
+import org.eobjects.metamodel.util.FileHelper;
+import org.eobjects.metamodel.util.FileResource;
+import org.eobjects.metamodel.util.Func;
+import org.eobjects.metamodel.util.Resource;
+import org.eobjects.metamodel.util.UrlResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import au.com.bytecode.opencsv.CSVReader;
+
+/**
+ * DataContext implementation for reading CSV files.
+ */
+public final class CsvDataContext extends QueryPostprocessDataContext implements UpdateableDataContext {
+
+    private static final Logger logger = LoggerFactory.getLogger(CsvDataContext.class);
+
+    private final Object WRITE_LOCK = new Object();
+
+    private final Resource _resource;
+    private final CsvConfiguration _configuration;
+    private final boolean _writable;
+
+    /**
+     * Constructs a CSV DataContext based on a file
+     * 
+     * @param file
+     * @param configuration
+     */
+    public CsvDataContext(File file, CsvConfiguration configuration) {
+        if (file == null) {
+            throw new IllegalArgumentException("File cannot be null");
+        }
+        if (configuration == null) {
+            throw new IllegalArgumentException("CsvConfiguration cannot be null");
+        }
+        _resource = new FileResource(file);
+        _configuration = configuration;
+        _writable = true;
+    }
+
+    public CsvDataContext(Resource resource, CsvConfiguration configuration) {
+        if (resource == null) {
+            throw new IllegalArgumentException("File cannot be null");
+        }
+        if (configuration == null) {
+            throw new IllegalArgumentException("CsvConfiguration cannot be null");
+        }
+        _resource = resource;
+        _configuration = configuration;
+        _writable = !resource.isReadOnly();
+    }
+
+    /**
+     * Constructs a CSV DataContext based on a {@link URL}
+     * 
+     * @param url
+     * @param configuration
+     */
+    public CsvDataContext(URL url, CsvConfiguration configuration) {
+        _resource = new UrlResource(url);
+        _configuration = configuration;
+        _writable = false;
+    }
+
+    /**
+     * Constructs a CSV DataContext based on a file
+     * 
+     * @param file
+     */
+    public CsvDataContext(File file) {
+        this(file, new CsvConfiguration());
+    }
+
+    /**
+     * Constructs a CSV DataContext based on an {@link InputStream}
+     * 
+     * @param inputStream
+     * @param configuration
+     */
+    public CsvDataContext(InputStream inputStream, CsvConfiguration configuration) {
+        File file = createFileFromInputStream(inputStream, configuration.getEncoding());
+        _configuration = configuration;
+        _writable = false;
+        _resource = new FileResource(file);
+    }
+
+    /**
+     * @deprecated use {@link #CsvDataContext(File, CsvConfiguration)} instead.
+     */
+    @Deprecated
+    public CsvDataContext(File file, char separatorChar) {
+        this(file, separatorChar, CsvConfiguration.DEFAULT_QUOTE_CHAR);
+    }
+
+    /**
+     * @deprecated use {@link #CsvDataContext(File, CsvConfiguration)} instead.
+     */
+    @Deprecated
+    public CsvDataContext(File file, char separatorChar, char quoteChar) {
+        this(file, new CsvConfiguration(CsvConfiguration.DEFAULT_COLUMN_NAME_LINE, FileHelper.DEFAULT_ENCODING,
+                separatorChar, quoteChar, CsvConfiguration.DEFAULT_ESCAPE_CHAR));
+    }
+
+    /**
+     * @deprecated use {@link #CsvDataContext(File, CsvConfiguration)} instead.
+     */
+    @Deprecated
+    public CsvDataContext(File file, char separatorChar, char quoteChar, String encoding) {
+        this(file, new CsvConfiguration(CsvConfiguration.DEFAULT_COLUMN_NAME_LINE, encoding, separatorChar, quoteChar,
+                CsvConfiguration.DEFAULT_ESCAPE_CHAR));
+    }
+
+    /**
+     * @deprecated use {@link #CsvDataContext(URL, CsvConfiguration)} instead.
+     */
+    @Deprecated
+    public CsvDataContext(URL url, char separatorChar, char quoteChar) {
+        this(url, separatorChar, quoteChar, FileHelper.DEFAULT_ENCODING);
+    }
+
+    /**
+     * @deprecated use {@link #CsvDataContext(URL, CsvConfiguration)} instead.
+     */
+    @Deprecated
+    public CsvDataContext(URL url, char separatorChar, char quoteChar, String encoding) {
+        this(url, new CsvConfiguration(CsvConfiguration.DEFAULT_COLUMN_NAME_LINE, encoding, separatorChar, quoteChar,
+                CsvConfiguration.DEFAULT_ESCAPE_CHAR));
+    }
+
+    /**
+     * @deprecated use {@link #CsvDataContext(InputStream, CsvConfiguration)}
+     *             instead.
+     */
+    @Deprecated
+    public CsvDataContext(InputStream inputStream, char separatorChar, char quoteChar) {
+        this(inputStream, new CsvConfiguration(CsvConfiguration.DEFAULT_COLUMN_NAME_LINE, FileHelper.DEFAULT_ENCODING,
+                separatorChar, quoteChar, CsvConfiguration.DEFAULT_ESCAPE_CHAR));
+    }
+
+    /**
+     * @deprecated use {@link #CsvDataContext(InputStream, CsvConfiguration)}
+     *             instead.
+     */
+    @Deprecated
+    public CsvDataContext(InputStream inputStream, char separatorChar, char quoteChar, String encoding) {
+        this(inputStream, new CsvConfiguration(CsvConfiguration.DEFAULT_COLUMN_NAME_LINE, encoding, separatorChar,
+                quoteChar, CsvConfiguration.DEFAULT_ESCAPE_CHAR));
+    }
+
+    /**
+     * Gets the CSV configuration used
+     * 
+     * @return a CSV configuration
+     */
+    public CsvConfiguration getConfiguration() {
+        return _configuration;
+    }
+
+    /**
+     * Gets the CSV file being read
+     * 
+     * @return a file
+     * 
+     * @deprecated use {@link #getResource()} instead.
+     */
+    @Deprecated
+    public File getFile() {
+        if (_resource instanceof FileResource) {
+            return ((FileResource) _resource).getFile();
+        }
+        return null;
+    }
+
+    /**
+     * Gets the resource that is being read from.
+     * 
+     * @return
+     */
+    public Resource getResource() {
+        return _resource;
+    }
+
+    private static File createFileFromInputStream(InputStream inputStream, String encoding) {
+        final File file;
+        final File tempDir = FileHelper.getTempDir();
+
+        File fileCandidate = null;
+        boolean usableName = false;
+        int index = 0;
+
+        while (!usableName) {
+            index++;
+            fileCandidate = new File(tempDir, "metamodel" + index + ".csv");
+            usableName = !fileCandidate.exists();
+        }
+        file = fileCandidate;
+
+        final BufferedWriter writer = FileHelper.getBufferedWriter(file, encoding);
+        final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+
+        try {
+            file.createNewFile();
+            file.deleteOnExit();
+
+            boolean firstLine = true;
+
+            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+                if (firstLine) {
+                    firstLine = false;
+                } else {
+                    writer.write('\n');
+                }
+                writer.write(line);
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        } finally {
+            FileHelper.safeClose(writer, reader);
+        }
+
+        return file;
+    }
+
+    @Override
+    protected Number executeCountQuery(Table table, List<FilterItem> whereItems, boolean functionApproximationAllowed) {
+        if (!functionApproximationAllowed) {
+            return null;
+        }
+
+        return _resource.read(new Func<InputStream, Number>() {
+            @Override
+            public Number eval(InputStream inputStream) {
+                try {
+                    final long length = _resource.getSize();
+                    // read up to 5 megs of the file and approximate number of
+                    // lines
+                    // based on that.
+
+                    final int sampleSize = (int) Math.min(length, 1024 * 1024 * 5);
+                    final int chunkSize = Math.min(sampleSize, 1024 * 1024);
+
+                    int readSize = 0;
+                    int newlines = 0;
+                    int carriageReturns = 0;
+                    byte[] byteBuffer = new byte[chunkSize];
+                    char[] charBuffer = new char[chunkSize];
+
+                    while (readSize < sampleSize) {
+                        final int read = inputStream.read(byteBuffer);
+                        if (read == -1) {
+                            break;
+                        } else {
+                            readSize += read;
+                        }
+
+                        Reader reader = getReader(byteBuffer, _configuration.getEncoding());
+                        reader.read(charBuffer);
+                        for (char c : charBuffer) {
+                            if ('\n' == c) {
+                                newlines++;
+                            } else if ('\r' == c) {
+                                carriageReturns++;
+                            }
+                        }
+                    }
+
+                    int lines = Math.max(newlines, carriageReturns);
+
+                    logger.info("Found {} lines breaks in {} bytes", lines, sampleSize);
+
+                    long approxCount = (long) (lines * length / sampleSize);
+                    return approxCount;
+                } catch (IOException e) {
+                    logger.error("Unexpected error during COUNT(*) approximation", e);
+                    throw new IllegalStateException(e);
+                }
+            }
+        });
+    }
+
+    private Reader getReader(byte[] byteBuffer, String encoding) throws UnsupportedEncodingException {
+        try {
+            return new InputStreamReader(new ByteArrayInputStream(byteBuffer), encoding);
+        } catch (UnsupportedEncodingException e1) {
+            // this may happen on more exotic encodings, but since this reader
+            // is only meant for finding newlines, we'll try again with UTF8
+            try {
+                return new InputStreamReader(new ByteArrayInputStream(byteBuffer), "UTF8");
+            } catch (UnsupportedEncodingException e2) {
+                throw e1;
+            }
+        }
+    }
+
+    @Override
+    public DataSet materializeMainSchemaTable(Table table, Column[] columns, int maxRows) {
+        final int lineNumber = _configuration.getColumnNameLineNumber();
+        final CSVReader reader = createCsvReader(lineNumber);
+        final int columnCount = table.getColumnCount();
+        final boolean failOnInconsistentRowLength = _configuration.isFailOnInconsistentRowLength();
+        if (maxRows < 0) {
+            return new CsvDataSet(reader, columns, null, columnCount, failOnInconsistentRowLength);
+        } else {
+            return new CsvDataSet(reader, columns, maxRows, columnCount, failOnInconsistentRowLength);
+        }
+    }
+
+    protected CSVReader createCsvReader(int skipLines) {
+        final Reader fileReader = FileHelper.getReader(_resource.read(), _configuration.getEncoding());
+        final CSVReader csvReader = new CSVReader(fileReader, _configuration.getSeparatorChar(),
+                _configuration.getQuoteChar(), _configuration.getEscapeChar(), skipLines);
+        return csvReader;
+    }
+
+    @Override
+    protected CsvSchema getMainSchema() throws MetaModelException {
+        CsvSchema schema = new CsvSchema(getMainSchemaName(), this);
+        if (_resource.isExists()) {
+            schema.setTable(new CsvTable(schema));
+        }
+        return schema;
+    }
+
+    @Override
+    protected String getMainSchemaName() {
+        return _resource.getName();
+    }
+
+    protected boolean isWritable() {
+        return _writable;
+    }
+
+    private void checkWritable() {
+        if (!isWritable()) {
+            throw new IllegalStateException(
+                    "This CSV DataContext is not writable, as it based on a read-only resource.");
+        }
+    }
+
+    @Override
+    public void executeUpdate(UpdateScript update) {
+        checkWritable();
+        CsvUpdateCallback callback = new CsvUpdateCallback(this);
+        synchronized (WRITE_LOCK) {
+            try {
+                update.run(callback);
+            } finally {
+                callback.close();
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvDataSet.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvDataSet.java b/csv/src/main/java/org/apache/metamodel/csv/CsvDataSet.java
new file mode 100644
index 0000000..ae31865
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvDataSet.java
@@ -0,0 +1,127 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import java.io.IOException;
+
+import org.eobjects.metamodel.MetaModelException;
+import org.eobjects.metamodel.data.AbstractDataSet;
+import org.eobjects.metamodel.data.DefaultRow;
+import org.eobjects.metamodel.data.Row;
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.util.FileHelper;
+
+import au.com.bytecode.opencsv.CSVReader;
+
+/**
+ * Streaming DataSet implementation for CSV support
+ * 
+ * @author Kasper Sørensen
+ */
+final class CsvDataSet extends AbstractDataSet {
+
+	private final CSVReader _reader;
+	private final boolean _failOnInconsistentRowLength;
+	private final int _columnsInTable;
+	private volatile int _rowNumber;
+	private volatile Integer _rowsRemaining;
+	private volatile Row _row;
+
+	public CsvDataSet(CSVReader reader, Column[] columns, Integer maxRows,
+			int columnsInTable, boolean failOnInconsistentRowLength) {
+	    super(columns);
+		_reader = reader;
+		_columnsInTable = columnsInTable;
+		_failOnInconsistentRowLength = failOnInconsistentRowLength;
+		_rowNumber = 0;
+		_rowsRemaining = maxRows;
+	}
+
+	@Override
+	public void close() {
+		FileHelper.safeClose(_reader);
+		_row = null;
+		_rowsRemaining = null;
+	}
+	
+	@Override
+	protected void finalize() throws Throwable {
+		super.finalize();
+		// close is always safe to invoke
+		close();
+	}
+
+	@Override
+	public Row getRow() throws MetaModelException {
+		return _row;
+	}
+
+	@Override
+	public boolean next() {
+		if (_rowsRemaining != null && _rowsRemaining > 0) {
+			_rowsRemaining--;
+			return nextInternal();
+		} else if (_rowsRemaining == null) {
+			return nextInternal();
+		} else {
+			return false;
+		}
+	}
+
+	private boolean nextInternal() {
+		if (_reader == null) {
+			return false;
+		}
+		final String[] csvValues;
+		try {
+			csvValues = _reader.readNext();
+		} catch (IOException e) {
+			throw new IllegalStateException("Exception reading from file", e);
+		}
+		if (csvValues == null) {
+			close();
+			return false;
+		}
+		
+		final int size = getHeader().size();
+		final Object[] rowValues = new Object[size];
+		for (int i = 0; i < size; i++) {
+			Column column = getHeader().getSelectItem(i).getColumn();
+			int columnNumber = column.getColumnNumber();
+			if (columnNumber < csvValues.length) {
+				rowValues[i] = csvValues[columnNumber];
+			} else {
+				// Ticket #125: Missing values should be enterpreted as
+				// null.
+				rowValues[i] = null;
+			}
+		}
+		_row = new DefaultRow(getHeader(), rowValues);
+
+		if (_failOnInconsistentRowLength) {
+			_rowNumber++;
+			if (_columnsInTable != csvValues.length) {
+				throw new InconsistentRowLengthException(_columnsInTable, _row,
+						csvValues, _rowNumber);
+			}
+		}
+
+		return true;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvDeleteBuilder.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvDeleteBuilder.java b/csv/src/main/java/org/apache/metamodel/csv/CsvDeleteBuilder.java
new file mode 100644
index 0000000..52c18e0
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvDeleteBuilder.java
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eobjects.metamodel.MetaModelException;
+import org.eobjects.metamodel.UpdateCallback;
+import org.eobjects.metamodel.UpdateScript;
+import org.eobjects.metamodel.data.DataSet;
+import org.eobjects.metamodel.data.Row;
+import org.eobjects.metamodel.delete.AbstractRowDeletionBuilder;
+import org.eobjects.metamodel.schema.Table;
+import org.eobjects.metamodel.util.Action;
+import org.eobjects.metamodel.util.FileHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class CsvDeleteBuilder extends AbstractRowDeletionBuilder {
+
+    private static final Logger logger = LoggerFactory.getLogger(CsvDeleteBuilder.class);
+
+    private final CsvUpdateCallback _updateCallback;
+
+    public CsvDeleteBuilder(CsvUpdateCallback updateCallback, Table table) {
+        super(table);
+        _updateCallback = updateCallback;
+    }
+
+    @Override
+    public void execute() throws MetaModelException {
+        final File tempFile = FileHelper.createTempFile("metamodel_deletion", "csv");
+
+        final CsvConfiguration configuration = _updateCallback.getConfiguration();
+
+        final CsvDataContext copyDataContext = new CsvDataContext(tempFile, configuration);
+        copyDataContext.executeUpdate(new UpdateScript() {
+
+            @Override
+            public void run(UpdateCallback callback) {
+                final Table originalTable = getTable();
+                final Table copyTable = callback.createTable(copyDataContext.getDefaultSchema(), originalTable.getName())
+                        .like(originalTable).execute();
+
+                if (isTruncateTableOperation()) {
+                    // no need to iterate old records, they should all be
+                    // removed
+                    return;
+                }
+
+                final DataSet dataSet = _updateCallback.getDataContext().query().from(originalTable)
+                        .select(originalTable.getColumns()).execute();
+                try {
+                    while (dataSet.next()) {
+                        final Row row = dataSet.getRow();
+                        if (!deleteRow(row)) {
+                            callback.insertInto(copyTable).like(row).execute();
+                        }
+                    }
+                } finally {
+                    dataSet.close();
+                }
+            }
+        });
+
+        // copy the copy (which does not have deleted records) to overwrite the
+        // original
+        final InputStream in = FileHelper.getInputStream(tempFile);
+        try {
+            _updateCallback.getResource().write(new Action<OutputStream>() {
+                @Override
+                public void run(OutputStream out) throws Exception {
+                    FileHelper.copy(in, out);
+                }
+            });
+        } finally {
+            FileHelper.safeClose(in);
+        }
+
+        final boolean deleted = tempFile.delete();
+        if (!deleted) {
+            logger.warn("Could not delete temporary copy-file: {}", tempFile);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvInsertBuilder.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvInsertBuilder.java b/csv/src/main/java/org/apache/metamodel/csv/CsvInsertBuilder.java
new file mode 100644
index 0000000..718de8e
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvInsertBuilder.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import org.eobjects.metamodel.insert.AbstractRowInsertionBuilder;
+import org.eobjects.metamodel.schema.Table;
+
+final class CsvInsertBuilder extends AbstractRowInsertionBuilder<CsvUpdateCallback> {
+
+	public CsvInsertBuilder(CsvUpdateCallback updateCallback, Table table) {
+		super(updateCallback, table);
+	}
+
+	@Override
+	public void execute() {
+		Object[] values = getValues();
+		String[] stringValues = new String[values.length];
+		for (int i = 0; i < stringValues.length; i++) {
+			stringValues[i] = values[i] == null ? "" : values[i].toString();
+		}
+		getUpdateCallback().writeRow(stringValues, true);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvSchema.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvSchema.java b/csv/src/main/java/org/apache/metamodel/csv/CsvSchema.java
new file mode 100644
index 0000000..c6d2ba9
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvSchema.java
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import org.eobjects.metamodel.schema.AbstractSchema;
+import org.eobjects.metamodel.schema.Table;
+
+final class CsvSchema extends AbstractSchema {
+
+    private static final long serialVersionUID = 1L;
+
+    private final String _name;
+	private final transient CsvDataContext _dataContext;
+	private CsvTable _table;
+
+	public CsvSchema(String name, CsvDataContext dataContext) {
+		super();
+		_name = name;
+		_dataContext = dataContext;
+	}
+
+	protected void setTable(CsvTable table) {
+		_table = table;
+	}
+
+	@Override
+	public String getName() {
+		return _name;
+	}
+
+	protected CsvDataContext getDataContext() {
+		return _dataContext;
+	}
+
+	@Override
+	public String getQuote() {
+		return null;
+	}
+
+	@Override
+	public Table[] getTables() {
+		if (_table == null) {
+			return new Table[0];
+		}
+		return new Table[] { _table };
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvTable.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvTable.java b/csv/src/main/java/org/apache/metamodel/csv/CsvTable.java
new file mode 100644
index 0000000..eb9bdc0
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvTable.java
@@ -0,0 +1,149 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import java.io.IOException;
+
+import org.eobjects.metamodel.schema.AbstractTable;
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.schema.ColumnType;
+import org.eobjects.metamodel.schema.MutableColumn;
+import org.eobjects.metamodel.schema.Relationship;
+import org.eobjects.metamodel.schema.Schema;
+import org.eobjects.metamodel.schema.TableType;
+import org.eobjects.metamodel.util.AlphabeticSequence;
+import org.eobjects.metamodel.util.FileHelper;
+
+import au.com.bytecode.opencsv.CSVReader;
+
+final class CsvTable extends AbstractTable {
+
+    private static final long serialVersionUID = 1L;
+
+    private final CsvSchema _schema;
+    private Column[] _columns;
+
+    /**
+     * Constructor for creating a new CSV table which has not yet been written.
+     * 
+     * @param schema
+     * @param columnNames
+     */
+    public CsvTable(CsvSchema schema, String[] columnNames) {
+        this(schema);
+        _columns = buildColumns(columnNames);
+    }
+
+    /**
+     * Constructor for reading an existing CSV table.
+     * 
+     * @param schema
+     */
+    public CsvTable(CsvSchema schema) {
+        _schema = schema;
+    }
+
+    @Override
+    public String getName() {
+        String schemaName = _schema.getName();
+        return schemaName.substring(0, schemaName.length() - 4);
+    }
+
+    @Override
+    public Column[] getColumns() {
+        if (_columns == null) {
+            synchronized (this) {
+                if (_columns == null) {
+                    _columns = buildColumns();
+                }
+            }
+        }
+        return _columns;
+    }
+
+    private Column[] buildColumns() {
+        CSVReader reader = null;
+        try {
+            reader = _schema.getDataContext().createCsvReader(0);
+
+            final int columnNameLineNumber = _schema.getDataContext().getConfiguration().getColumnNameLineNumber();
+            for (int i = 1; i < columnNameLineNumber; i++) {
+                reader.readNext();
+            }
+            final String[] columnHeaders = reader.readNext();
+
+            reader.close();
+            return buildColumns(columnHeaders);
+        } catch (IOException e) {
+            throw new IllegalStateException("Exception reading from resource: " + _schema.getDataContext().getResource().getName(), e);
+        } finally {
+            FileHelper.safeClose(reader);
+        }
+    }
+
+    private Column[] buildColumns(final String[] columnNames) {
+        if (columnNames == null) {
+            return new Column[0];
+        }
+
+        final CsvConfiguration configuration = _schema.getDataContext().getConfiguration();
+        final int columnNameLineNumber = configuration.getColumnNameLineNumber();
+        final boolean nullable = !configuration.isFailOnInconsistentRowLength();
+
+        final Column[] columns = new Column[columnNames.length];
+        final AlphabeticSequence sequence = new AlphabeticSequence();
+        for (int i = 0; i < columnNames.length; i++) {
+            final String columnName;
+            if (columnNameLineNumber == CsvConfiguration.NO_COLUMN_NAME_LINE) {
+                columnName = sequence.next();
+            } else {
+                columnName = columnNames[i];
+            }
+            Column column = new MutableColumn(columnName, ColumnType.VARCHAR, this, i, null, null, nullable, null,
+                    false, null);
+            columns[i] = column;
+        }
+        return columns;
+    }
+
+    @Override
+    public Schema getSchema() {
+        return _schema;
+    }
+
+    @Override
+    public TableType getType() {
+        return TableType.TABLE;
+    }
+
+    @Override
+    public Relationship[] getRelationships() {
+        return new Relationship[0];
+    }
+
+    @Override
+    public String getRemarks() {
+        return null;
+    }
+
+    @Override
+    public String getQuote() {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvTableDropBuilder.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvTableDropBuilder.java b/csv/src/main/java/org/apache/metamodel/csv/CsvTableDropBuilder.java
new file mode 100644
index 0000000..0685fb4
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvTableDropBuilder.java
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import org.eobjects.metamodel.drop.AbstractTableDropBuilder;
+import org.eobjects.metamodel.schema.Table;
+
+final class CsvTableDropBuilder extends AbstractTableDropBuilder {
+
+    private final CsvUpdateCallback _updateCallback;
+
+    public CsvTableDropBuilder(CsvUpdateCallback updateCallback, Table table) {
+        super(table);
+        _updateCallback = updateCallback;
+    }
+
+    @Override
+    public void execute() {
+        _updateCallback.dropTable();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvUpdateCallback.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvUpdateCallback.java b/csv/src/main/java/org/apache/metamodel/csv/CsvUpdateCallback.java
new file mode 100644
index 0000000..72e276c
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvUpdateCallback.java
@@ -0,0 +1,249 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.RandomAccessFile;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
+
+import org.eobjects.metamodel.AbstractUpdateCallback;
+import org.eobjects.metamodel.MetaModelException;
+import org.eobjects.metamodel.UpdateCallback;
+import org.eobjects.metamodel.create.TableCreationBuilder;
+import org.eobjects.metamodel.delete.RowDeletionBuilder;
+import org.eobjects.metamodel.drop.TableDropBuilder;
+import org.eobjects.metamodel.insert.RowInsertionBuilder;
+import org.eobjects.metamodel.schema.Schema;
+import org.eobjects.metamodel.schema.Table;
+import org.eobjects.metamodel.update.RowUpdationBuilder;
+import org.eobjects.metamodel.util.Action;
+import org.eobjects.metamodel.util.EqualsBuilder;
+import org.eobjects.metamodel.util.FileHelper;
+import org.eobjects.metamodel.util.FileResource;
+import org.eobjects.metamodel.util.Resource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class CsvUpdateCallback extends AbstractUpdateCallback implements UpdateCallback {
+
+    private static final Logger logger = LoggerFactory.getLogger(CsvUpdateCallback.class);
+
+    private final CsvConfiguration _configuration;
+    private final Resource _resource;
+    private Writer _writer;
+
+    public CsvUpdateCallback(CsvDataContext dataContext) {
+        super(dataContext);
+        _resource = dataContext.getResource();
+        _configuration = dataContext.getConfiguration();
+    }
+
+    @Override
+    public TableCreationBuilder createTable(Schema schema, String name) throws IllegalArgumentException, IllegalStateException {
+        return new CsvCreateTableBuilder(this, schema, name);
+    }
+
+    @Override
+    public RowInsertionBuilder insertInto(Table table) throws IllegalArgumentException, IllegalStateException {
+        validateTable(table);
+        return new CsvInsertBuilder(this, table);
+    }
+
+    public CsvConfiguration getConfiguration() {
+        return _configuration;
+    }
+
+    public Resource getResource() {
+        return _resource;
+    }
+
+    private void validateTable(Table table) {
+        if (!(table instanceof CsvTable)) {
+            throw new IllegalArgumentException("Not a valid CSV table: " + table);
+        }
+    }
+
+    protected synchronized void writeRow(final String[] stringValues, final boolean append) {
+        final CsvWriter _csvWriter = new CsvWriter(_configuration);
+        final String line = _csvWriter.buildLine(stringValues);
+        if (_resource instanceof FileResource) {
+            // optimized handling for file-based resources
+            final File file = ((FileResource) _resource).getFile();
+            final Writer writer = getFileWriter(file, append);
+            try {
+                writer.write(line);
+            } catch (IOException e) {
+                throw new MetaModelException("Failed to write line to file: " + line, e);
+            }
+        } else {
+            // generic handling for any kind of resource
+            final Action<OutputStream> action = new Action<OutputStream>() {
+                @Override
+                public void run(OutputStream out) throws Exception {
+                    final String encoding = _configuration.getEncoding();
+                    final OutputStreamWriter writer = new OutputStreamWriter(out, encoding);
+                    writer.write(line);
+                    writer.flush();
+                }
+            };
+            if (append) {
+                _resource.append(action);
+            } else {
+                _resource.write(action);
+            }
+        }
+    }
+
+    private Writer getFileWriter(File file, boolean append) {
+        if (_writer == null || !append) {
+            final boolean needsLineBreak = needsLineBreak(file, _configuration);
+
+            final Writer writer = FileHelper.getWriter(file, _configuration.getEncoding(), append);
+            if (needsLineBreak) {
+                try {
+                    writer.write('\n');
+                } catch (IOException e) {
+                    logger.debug("Failed to insert newline", e);
+                }
+            }
+            _writer = writer;
+        }
+        return _writer;
+    }
+
+    protected static boolean needsLineBreak(File file, CsvConfiguration configuration) {
+        if (!file.exists() || file.length() == 0) {
+            return false;
+        }
+
+        try {
+            // find the bytes a newline would match under the encoding
+            final byte[] bytesInLineBreak;
+            {
+                ByteBuffer encodedLineBreak = Charset.forName(configuration.getEncoding()).encode("\n");
+                bytesInLineBreak = new byte[encodedLineBreak.capacity()];
+                encodedLineBreak.get(bytesInLineBreak);
+            }
+
+            // find the last bytes of the file
+            final byte[] bytesFromFile = new byte[bytesInLineBreak.length];
+            {
+                final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
+                try {
+                    FileChannel channel = randomAccessFile.getChannel();
+                    try {
+                        long length = randomAccessFile.length();
+
+                        channel = channel.position(length - bytesInLineBreak.length);
+                        channel.read(ByteBuffer.wrap(bytesFromFile));
+                    } finally {
+                        channel.close();
+                    }
+                } finally {
+                    randomAccessFile.close();
+                }
+            }
+
+            // if the two byte arrays match, then the newline is not needed.
+            if (EqualsBuilder.equals(bytesInLineBreak, bytesFromFile)) {
+                return false;
+            }
+            return true;
+        } catch (Exception e) {
+            logger.error("Error occurred while checking if file needs linebreak, omitting check", e);
+        }
+        return false;
+    }
+
+    /**
+     * Closes all open handles
+     */
+    protected void close() {
+        if (_writer != null) {
+            try {
+                _writer.flush();
+            } catch (IOException e) {
+                logger.warn("Failed to flush CSV writer", e);
+            }
+            try {
+                _writer.close();
+            } catch (IOException e) {
+                logger.error("Failed to close CSV writer", e);
+            } finally {
+                _writer = null;
+            }
+        }
+    }
+
+    @Override
+    public RowUpdationBuilder update(Table table) throws IllegalArgumentException, IllegalStateException {
+        close();
+        return super.update(table);
+    }
+
+    @Override
+    public boolean isDropTableSupported() {
+        return true;
+    }
+
+    @Override
+    public TableDropBuilder dropTable(Table table) {
+        validateTable(table);
+        return new CsvTableDropBuilder(this, table);
+    }
+
+    /**
+     * Callback method used by {@link CsvTableDropBuilder} when execute is
+     * called
+     */
+    protected void dropTable() {
+        close();
+        if (_resource instanceof FileResource) {
+            final File file = ((FileResource) _resource).getFile();
+            final boolean success = file.delete();
+            if (!success) {
+                throw new MetaModelException("Could not delete (drop) file: " + file);
+            }
+        } else {
+            _resource.write(new Action<OutputStream>() {
+                @Override
+                public void run(OutputStream arg) throws Exception {
+                    // do nothing, just write an empty file
+                }
+            });
+        }
+    }
+
+    @Override
+    public boolean isDeleteSupported() {
+        return true;
+    }
+
+    @Override
+    public RowDeletionBuilder deleteFrom(Table table) {
+        validateTable(table);
+        return new CsvDeleteBuilder(this, table);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/CsvWriter.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/CsvWriter.java b/csv/src/main/java/org/apache/metamodel/csv/CsvWriter.java
new file mode 100644
index 0000000..7b35847
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/CsvWriter.java
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import org.eobjects.metamodel.util.Resource;
+
+/**
+ * This class is an adaptation of the CSVWriter class of OpenCSV. We've made the
+ * writer work without having the output stream as state (suiting for
+ * {@link Resource} usage).
+ */
+public final class CsvWriter {
+
+    public static final int INITIAL_STRING_SIZE = 128;
+
+    private final CsvConfiguration _configuration;
+
+    public CsvWriter(CsvConfiguration configuration) {
+        _configuration = configuration;
+    }
+
+    /**
+     * Builds a line for the CSV file output
+     * 
+     * @param nextLine
+     *            a string array with each comma-separated element as a separate
+     *            entry.
+     */
+    public String buildLine(String[] nextLine) {
+        final StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE);
+        for (int i = 0; i < nextLine.length; i++) {
+
+            if (i != 0) {
+                sb.append(_configuration.getSeparatorChar());
+            }
+
+            final String nextElement = nextLine[i];
+            if (nextElement == null) {
+                continue;
+            }
+            final char quoteChar = _configuration.getQuoteChar();
+            if (quoteChar != CsvConfiguration.NOT_A_CHAR) {
+                sb.append(quoteChar);
+            }
+
+            sb.append(stringContainsSpecialCharacters(nextElement) ? processLine(nextElement) : nextElement);
+
+            if (quoteChar != CsvConfiguration.NOT_A_CHAR) {
+                sb.append(quoteChar);
+            }
+        }
+
+        sb.append('\n');
+        return sb.toString();
+
+    }
+
+    private boolean stringContainsSpecialCharacters(String line) {
+        return line.indexOf(_configuration.getQuoteChar()) != -1 || line.indexOf(_configuration.getEscapeChar()) != -1;
+    }
+
+    private StringBuilder processLine(String nextElement) {
+        final StringBuilder sb = new StringBuilder(INITIAL_STRING_SIZE);
+        for (int j = 0; j < nextElement.length(); j++) {
+            final char nextChar = nextElement.charAt(j);
+            final char escapeChar = _configuration.getEscapeChar();
+            if (escapeChar != CsvConfiguration.NOT_A_CHAR && nextChar == _configuration.getQuoteChar()) {
+                sb.append(escapeChar).append(nextChar);
+            } else if (escapeChar != CsvConfiguration.NOT_A_CHAR && nextChar == escapeChar) {
+                sb.append(escapeChar).append(nextChar);
+            } else {
+                sb.append(nextChar);
+            }
+        }
+
+        return sb;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/InconsistentRowLengthException.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/InconsistentRowLengthException.java b/csv/src/main/java/org/apache/metamodel/csv/InconsistentRowLengthException.java
new file mode 100644
index 0000000..60c093f
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/InconsistentRowLengthException.java
@@ -0,0 +1,101 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.csv;
+
+import org.eobjects.metamodel.InconsistentRowFormatException;
+import org.eobjects.metamodel.data.DataSet;
+import org.eobjects.metamodel.data.Row;
+
+/**
+ * Exception thrown when a line in a CSV file has an inconsistent amount of
+ * columns compared to the previous lines (and headers). The exception will be
+ * thrown when {@link DataSet#next()} is called.
+ * 
+ * Note that this exception is only thrown if the
+ * {@link CsvConfiguration#isFailOnInconsistentRowLength()} property is true.
+ * Enabling it allows a somewhat different approach to iterating through a
+ * resulting DataSet. For example something like:
+ * 
+ * <pre>
+ * while (true) {
+ * 	try {
+ * 		if (!dataSet.next) {
+ * 			break;
+ * 		}
+ * 		Row row = dataSet.getRow();
+ * 		handleRegularRow(row);
+ * 	} catch (InconsistentRowLengthException e) {
+ * 		handleIrregularRow(e.getSourceLine());
+ * 	}
+ * }
+ * </pre>
+ * 
+ * @author Kasper Sørensen
+ */
+public final class InconsistentRowLengthException extends
+		InconsistentRowFormatException {
+
+	private static final long serialVersionUID = 1L;
+
+	private final int _columnsInTable;
+	private final String[] _line;
+
+	public InconsistentRowLengthException(int columnsInTable, Row proposedRow,
+			String[] line, int rowNumber) {
+		super(proposedRow, rowNumber);
+		_columnsInTable = columnsInTable;
+		_line = line;
+	}
+
+	@Override
+	public String getMessage() {
+		return "Inconsistent length of row no. " + getRowNumber()
+				+ ". Expected " + getColumnsInTable() + " columns but found "
+				+ getColumnsInLine() + ".";
+	}
+
+	/**
+	 * Gets the source line, as parsed by the CSV parser (regardless of table
+	 * metadata).
+	 * 
+	 * @return an array of string values.
+	 */
+	public String[] getSourceLine() {
+		return _line;
+	}
+
+	/**
+	 * Gets the amount of columns in the parsed line.
+	 * 
+	 * @return an int representing the amount of values in the inconsistent
+	 *         line.
+	 */
+	public int getColumnsInLine() {
+		return _line.length;
+	}
+
+	/**
+	 * Gets the expected amounts of columns, as defined by the table metadata.
+	 * 
+	 * @return an int representing the amount of columns defined in the table.
+	 */
+	public int getColumnsInTable() {
+		return _columnsInTable;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/apache/metamodel/csv/package-info.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/apache/metamodel/csv/package-info.java b/csv/src/main/java/org/apache/metamodel/csv/package-info.java
new file mode 100644
index 0000000..241c74e
--- /dev/null
+++ b/csv/src/main/java/org/apache/metamodel/csv/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * Module package for Comma Separated Values (CSV) files
+ */
+package org.eobjects.metamodel.csv;
+

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/eobjects/metamodel/csv/CsvConfiguration.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/eobjects/metamodel/csv/CsvConfiguration.java b/csv/src/main/java/org/eobjects/metamodel/csv/CsvConfiguration.java
deleted file mode 100644
index c67615b..0000000
--- a/csv/src/main/java/org/eobjects/metamodel/csv/CsvConfiguration.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.eobjects.metamodel.csv;
-
-import java.io.Serializable;
-import java.util.List;
-
-import org.eobjects.metamodel.util.BaseObject;
-import org.eobjects.metamodel.util.FileHelper;
-
-/**
- * Represents the configuration for reading/parsing CSV files.
- * 
- * @author Kasper Sørensen
- */
-public final class CsvConfiguration extends BaseObject implements Serializable {
-
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * The value is '\\uFFFF', the "not a character" value which should not
-	 * occur in any valid Unicode string. This special char can be used to
-	 * disable either quote chars or escape chars.
-	 */
-	public static final char NOT_A_CHAR = '\uFFFF';
-	public static final int NO_COLUMN_NAME_LINE = 0;
-	public static final int DEFAULT_COLUMN_NAME_LINE = 1;
-	public static final char DEFAULT_SEPARATOR_CHAR = ',';
-	public static final char DEFAULT_QUOTE_CHAR = '"';
-	public static final char DEFAULT_ESCAPE_CHAR = '\\';
-
-	private final int columnNameLineNumber;
-	private final String encoding;
-	private final char separatorChar;
-	private final char quoteChar;
-	private final char escapeChar;
-	private final boolean failOnInconsistentRowLength;
-
-	public CsvConfiguration() {
-		this(DEFAULT_COLUMN_NAME_LINE);
-	}
-
-	public CsvConfiguration(int columnNameLineNumber) {
-		this(columnNameLineNumber, FileHelper.DEFAULT_ENCODING,
-				DEFAULT_SEPARATOR_CHAR, DEFAULT_QUOTE_CHAR, DEFAULT_ESCAPE_CHAR);
-	}
-
-	public CsvConfiguration(int columnNameLineNumber, String encoding,
-			char separatorChar, char quoteChar, char escapeChar) {
-		this(columnNameLineNumber, encoding, separatorChar, quoteChar,
-				escapeChar, false);
-	}
-
-	public CsvConfiguration(int columnNameLineNumber, String encoding,
-			char separatorChar, char quoteChar, char escapeChar,
-			boolean failOnInconsistentRowLength) {
-		this.columnNameLineNumber = columnNameLineNumber;
-		this.encoding = encoding;
-		this.separatorChar = separatorChar;
-		this.quoteChar = quoteChar;
-		this.escapeChar = escapeChar;
-		this.failOnInconsistentRowLength = failOnInconsistentRowLength;
-	}
-
-	/**
-	 * Determines whether to fail (by throwing an
-	 * {@link InconsistentRowLengthException}) if a line in the CSV file has
-	 * inconsistent amounts of columns.
-	 * 
-	 * If set to false (default) MetaModel will gracefully fill in missing null
-	 * values in or ignore additional values in a line.
-	 * 
-	 * @return a boolean indicating whether to fail or gracefully compensate for
-	 *         inconsistent lines in the CSV files.
-	 */
-	public boolean isFailOnInconsistentRowLength() {
-		return failOnInconsistentRowLength;
-	}
-
-	/**
-	 * The line number (1 based) from which to get the names of the columns.
-	 * 
-	 * @return the line number (1 based)
-	 */
-	public int getColumnNameLineNumber() {
-		return columnNameLineNumber;
-	}
-
-	/**
-	 * Gets the file encoding to use for reading the file.
-	 * 
-	 * @return the text encoding of the file.
-	 */
-	public String getEncoding() {
-		return encoding;
-	}
-
-	/**
-	 * Gets the separator char (typically comma or semicolon) for separating
-	 * values.
-	 * 
-	 * @return the separator char
-	 */
-	public char getSeparatorChar() {
-		return separatorChar;
-	}
-
-	/**
-	 * Gets the quote char, used for encapsulating values.
-	 * 
-	 * @return the quote char
-	 */
-	public char getQuoteChar() {
-		return quoteChar;
-	}
-
-	/**
-	 * Gets the escape char, used for escaping eg. quote chars inside values.
-	 * 
-	 * @return the escape char
-	 */
-	public char getEscapeChar() {
-		return escapeChar;
-	}
-
-	@Override
-	protected void decorateIdentity(List<Object> identifiers) {
-		identifiers.add(columnNameLineNumber);
-		identifiers.add(encoding);
-		identifiers.add(separatorChar);
-		identifiers.add(quoteChar);
-		identifiers.add(escapeChar);
-		identifiers.add(failOnInconsistentRowLength);
-	}
-
-	@Override
-	public String toString() {
-		return "CsvConfiguration[columnNameLineNumber=" + columnNameLineNumber
-				+ ", encoding=" + encoding + ", separatorChar=" + separatorChar
-				+ ", quoteChar=" + quoteChar + ", escapeChar=" + escapeChar
-				+ ", failOnInconsistentRowLength="
-				+ failOnInconsistentRowLength + "]";
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/csv/src/main/java/org/eobjects/metamodel/csv/CsvCreateTableBuilder.java
----------------------------------------------------------------------
diff --git a/csv/src/main/java/org/eobjects/metamodel/csv/CsvCreateTableBuilder.java b/csv/src/main/java/org/eobjects/metamodel/csv/CsvCreateTableBuilder.java
deleted file mode 100644
index 30e0a54..0000000
--- a/csv/src/main/java/org/eobjects/metamodel/csv/CsvCreateTableBuilder.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.eobjects.metamodel.csv;
-
-import org.eobjects.metamodel.create.AbstractTableCreationBuilder;
-import org.eobjects.metamodel.schema.MutableTable;
-import org.eobjects.metamodel.schema.Schema;
-import org.eobjects.metamodel.schema.Table;
-
-final class CsvCreateTableBuilder extends
-		AbstractTableCreationBuilder<CsvUpdateCallback> {
-
-	public CsvCreateTableBuilder(CsvUpdateCallback updateCallback,
-			Schema schema, String name) {
-		super(updateCallback, schema, name);
-		if (!(schema instanceof CsvSchema)) {
-			throw new IllegalArgumentException("Not a valid CSV schema: "
-					+ schema);
-		}
-	}
-
-	@Override
-	public Table execute() {
-		CsvUpdateCallback csvUpdateCallback = getUpdateCallback();
-
-		MutableTable table = getTable();
-		String[] columnNames = table.getColumnNames();
-		csvUpdateCallback.writeRow(columnNames, false);
-
-		CsvSchema schema = (CsvSchema) table.getSchema();
-		CsvTable csvTable = new CsvTable(schema, table.getColumnNames());
-		schema.setTable(csvTable);
-		return csvTable;
-	}
-}