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:11:02 UTC

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

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java b/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java
new file mode 100644
index 0000000..d84da6a
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/QueryPostprocessDataContext.java
@@ -0,0 +1,589 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eobjects.metamodel.convert.ConvertedDataSetInterceptor;
+import org.eobjects.metamodel.convert.Converters;
+import org.eobjects.metamodel.convert.HasReadTypeConverters;
+import org.eobjects.metamodel.convert.TypeConverter;
+import org.eobjects.metamodel.data.DataSet;
+import org.eobjects.metamodel.data.DataSetHeader;
+import org.eobjects.metamodel.data.DefaultRow;
+import org.eobjects.metamodel.data.EmptyDataSet;
+import org.eobjects.metamodel.data.FirstRowDataSet;
+import org.eobjects.metamodel.data.InMemoryDataSet;
+import org.eobjects.metamodel.data.MaxRowsDataSet;
+import org.eobjects.metamodel.data.Row;
+import org.eobjects.metamodel.data.SimpleDataSetHeader;
+import org.eobjects.metamodel.query.FilterItem;
+import org.eobjects.metamodel.query.FromItem;
+import org.eobjects.metamodel.query.GroupByItem;
+import org.eobjects.metamodel.query.JoinType;
+import org.eobjects.metamodel.query.OperatorType;
+import org.eobjects.metamodel.query.OrderByItem;
+import org.eobjects.metamodel.query.Query;
+import org.eobjects.metamodel.query.SelectItem;
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.schema.ColumnType;
+import org.eobjects.metamodel.schema.MutableColumn;
+import org.eobjects.metamodel.schema.MutableRelationship;
+import org.eobjects.metamodel.schema.MutableSchema;
+import org.eobjects.metamodel.schema.MutableTable;
+import org.eobjects.metamodel.schema.Relationship;
+import org.eobjects.metamodel.schema.Schema;
+import org.eobjects.metamodel.schema.Table;
+import org.eobjects.metamodel.schema.TableType;
+import org.eobjects.metamodel.util.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract DataContext for data sources that do not support SQL queries
+ * natively.
+ * 
+ * Instead this superclass only requires that a subclass can materialize a
+ * single table at a time. Then the query will be executed by post processing
+ * the datasets client-side.
+ */
+public abstract class QueryPostprocessDataContext extends AbstractDataContext implements HasReadTypeConverters {
+
+    private static final Logger logger = LoggerFactory.getLogger(QueryPostprocessDataContext.class);
+
+    public static final String INFORMATION_SCHEMA_NAME = "information_schema";
+
+    private final Map<Column, TypeConverter<?, ?>> _converters;
+
+    private Schema _mainSchema;
+
+    public QueryPostprocessDataContext() {
+        super();
+        _converters = new HashMap<Column, TypeConverter<?, ?>>();
+    }
+
+    @Override
+    public DataSet executeQuery(final Query query) {
+        final List<SelectItem> selectItems = query.getSelectClause().getItems();
+        final List<FromItem> fromItems = query.getFromClause().getItems();
+        final List<FilterItem> whereItems = query.getWhereClause().getItems();
+        final List<SelectItem> whereSelectItems = query.getWhereClause().getEvaluatedSelectItems();
+        final List<GroupByItem> groupByItems = query.getGroupByClause().getItems();
+        final List<SelectItem> groupBySelectItems = query.getGroupByClause().getEvaluatedSelectItems();
+        final List<SelectItem> havingSelectItems = query.getHavingClause().getEvaluatedSelectItems();
+        final List<SelectItem> orderBySelectItems = query.getOrderByClause().getEvaluatedSelectItems();
+
+        final List<FilterItem> havingItems = query.getHavingClause().getItems();
+        final List<OrderByItem> orderByItems = query.getOrderByClause().getItems();
+
+        // check for approximate SELECT COUNT(*) queries
+        if (fromItems.size() == 1 && selectItems.size() == 1 && groupByItems.isEmpty() && havingItems.isEmpty()) {
+            final SelectItem selectItem = query.getSelectClause().getItem(0);
+            if (SelectItem.isCountAllItem(selectItem)) {
+                final boolean functionApproximationAllowed = selectItem.isFunctionApproximationAllowed();
+                final FromItem fromItem = query.getFromClause().getItem(0);
+                final Table table = fromItem.getTable();
+                if (table != null) {
+                    if (isMainSchemaTable(table)) {
+                        logger.debug("Query is a COUNT query with {} where items. Trying executeCountQuery(...)",
+                                whereItems.size());
+                        final Number count = executeCountQuery(table, whereItems, functionApproximationAllowed);
+                        if (count == null) {
+                            logger.debug("DataContext did not return any count query results. Proceeding with manual counting.");
+                        } else {
+                            List<Row> data = new ArrayList<Row>(1);
+                            final DataSetHeader header = new SimpleDataSetHeader(new SelectItem[] { selectItem });
+                            data.add(new DefaultRow(header, new Object[] { count }));
+                            return new InMemoryDataSet(header, data);
+                        }
+                    }
+                }
+            }
+        }
+
+        final int firstRow = (query.getFirstRow() == null ? 1 : query.getFirstRow());
+        final int maxRows = (query.getMaxRows() == null ? -1 : query.getMaxRows());
+
+        // Check for very simple queries with max rows property set (typically
+        // preview), see Ticket #187
+        previewTable: if (whereItems.isEmpty() && groupByItems.isEmpty() && havingItems.isEmpty()
+                && orderByItems.isEmpty() && fromItems.size() == 1) {
+
+            final Table table = fromItems.get(0).getTable();
+            if (table != null) {
+                for (SelectItem item : selectItems) {
+                    if (item.getFunction() != null || item.getExpression() != null) {
+                        break previewTable;
+                    }
+                }
+
+                DataSet dataSet = materializeTable(table, selectItems, firstRow, maxRows);
+                dataSet = MetaModelHelper.getSelection(selectItems, dataSet);
+                return dataSet;
+            }
+        }
+
+        // Creates a list for all select items that are needed to execute query
+        // (some may only be used as part of a filter, but not shown in result)
+        List<SelectItem> workSelectItems = CollectionUtils.concat(true, selectItems, whereSelectItems,
+                groupBySelectItems, havingSelectItems, orderBySelectItems);
+
+        // Materialize the tables in the from clause
+        final DataSet[] fromDataSets = new DataSet[fromItems.size()];
+        for (int i = 0; i < fromDataSets.length; i++) {
+            FromItem fromItem = fromItems.get(i);
+            fromDataSets[i] = materializeFromItem(fromItem, workSelectItems);
+        }
+
+        // Execute the query using the raw data
+        DataSet dataSet = MetaModelHelper.getCarthesianProduct(fromDataSets, whereItems);
+
+        // we can now exclude the select items imposed by the WHERE clause (and
+        // should, to make the aggregation process faster)
+        workSelectItems = CollectionUtils.concat(true, selectItems, groupBySelectItems, havingSelectItems,
+                orderBySelectItems);
+
+        if (groupByItems.size() > 0) {
+            dataSet = MetaModelHelper.getGrouped(workSelectItems, dataSet, groupByItems);
+        } else {
+            dataSet = MetaModelHelper.getAggregated(workSelectItems, dataSet);
+        }
+        dataSet = MetaModelHelper.getFiltered(dataSet, havingItems);
+
+        if (query.getSelectClause().isDistinct()) {
+            dataSet = MetaModelHelper.getSelection(selectItems, dataSet);
+            dataSet = MetaModelHelper.getDistinct(dataSet);
+            dataSet = MetaModelHelper.getOrdered(dataSet, orderByItems);
+        } else {
+            dataSet = MetaModelHelper.getOrdered(dataSet, orderByItems);
+            dataSet = MetaModelHelper.getSelection(selectItems, dataSet);
+        }
+
+        if (firstRow > 1) {
+            dataSet = new FirstRowDataSet(dataSet, firstRow);
+        }
+        if (maxRows != -1) {
+            dataSet = new MaxRowsDataSet(dataSet, maxRows);
+        }
+        return dataSet;
+    }
+
+    /**
+     * Executes a simple count query, if possible. This method is provided to
+     * allow subclasses to optimize count queries since they are quite common
+     * and often a datastore can retrieve the count using some specialized means
+     * which is much more performant than counting all records manually.
+     * 
+     * @param table
+     *            the table on which the count is requested.
+     * @param whereItems
+     *            a (sometimes empty) list of WHERE items.
+     * @param functionApproximationAllowed
+     *            whether approximation is allowed or not.
+     * @return the count of the particular table, or null if not available.
+     */
+    protected Number executeCountQuery(Table table, List<FilterItem> whereItems, boolean functionApproximationAllowed) {
+        return null;
+    }
+
+    protected DataSet materializeFromItem(final FromItem fromItem, final List<SelectItem> selectItems) {
+        DataSet dataSet;
+        JoinType joinType = fromItem.getJoin();
+        if (fromItem.getTable() != null) {
+            // We need to materialize a single table
+            final Table table = fromItem.getTable();
+            final List<SelectItem> selectItemsToMaterialize = new ArrayList<SelectItem>();
+
+            for (final SelectItem selectItem : selectItems) {
+                final FromItem selectedFromItem = selectItem.getFromItem();
+                if (selectedFromItem != null) {
+                    if (selectedFromItem.equals(fromItem)) {
+                        selectItemsToMaterialize.add(selectItem.replaceFunction(null));
+                    }
+                } else {
+                    // the select item does not specify a specific
+                    // from-item
+                    final Column selectedColumn = selectItem.getColumn();
+                    if (selectedColumn != null) {
+                        // we assume that if the table matches, we will use the
+                        // column
+                        if (selectedColumn.getTable() != null && selectedColumn.getTable().equals(table)) {
+                            selectItemsToMaterialize.add(selectItem.replaceFunction(null));
+                        }
+                    }
+                }
+            }
+
+            if (logger.isDebugEnabled()) {
+                logger.debug("calling materializeTable(" + table.getName() + "," + selectItemsToMaterialize + ",1,-1");
+            }
+
+            // Dispatching to the concrete subclass of
+            // QueryPostprocessDataContextStrategy
+            dataSet = materializeTable(table, selectItemsToMaterialize, 1, -1);
+
+        } else if (joinType != null) {
+            // We need to (recursively) materialize a joined FromItem
+            if (fromItem.getLeftSide() == null || fromItem.getRightSide() == null) {
+                throw new IllegalArgumentException("Joined FromItem requires both left and right side: " + fromItem);
+            }
+            DataSet[] fromItemDataSets = new DataSet[2];
+
+            // materialize left side
+            List<SelectItem> leftOn = Arrays.asList(fromItem.getLeftOn());
+            fromItemDataSets[0] = materializeFromItem(fromItem.getLeftSide(),
+                    CollectionUtils.concat(true, selectItems, leftOn));
+
+            // materialize right side
+            List<SelectItem> rightOn = Arrays.asList(fromItem.getRightOn());
+            fromItemDataSets[1] = materializeFromItem(fromItem.getRightSide(),
+                    CollectionUtils.concat(true, selectItems, rightOn));
+
+            FilterItem[] onConditions = new FilterItem[leftOn.size()];
+            for (int i = 0; i < onConditions.length; i++) {
+                FilterItem whereItem = new FilterItem(leftOn.get(i), OperatorType.EQUALS_TO, rightOn.get(i));
+                onConditions[i] = whereItem;
+            }
+            if (joinType == JoinType.INNER) {
+                dataSet = MetaModelHelper.getCarthesianProduct(fromItemDataSets, onConditions);
+            } else if (joinType == JoinType.LEFT) {
+                dataSet = MetaModelHelper.getLeftJoin(fromItemDataSets[0], fromItemDataSets[1], onConditions);
+            } else if (joinType == JoinType.RIGHT) {
+                dataSet = MetaModelHelper.getRightJoin(fromItemDataSets[0], fromItemDataSets[1], onConditions);
+            } else {
+                throw new IllegalArgumentException("FromItem type not supported: " + fromItem);
+            }
+        } else if (fromItem.getSubQuery() != null) {
+            // We need to (recursively) materialize a subquery
+            dataSet = executeQuery(fromItem.getSubQuery());
+        } else {
+            throw new IllegalArgumentException("FromItem type not supported: " + fromItem);
+        }
+        if (dataSet == null) {
+            throw new IllegalStateException("FromItem was not succesfully materialized: " + fromItem);
+        }
+        return dataSet;
+    }
+
+    protected DataSet materializeTable(final Table table, final List<SelectItem> selectItems, final int firstRow,
+            final int maxRows) {
+        if (table == null) {
+            throw new IllegalArgumentException("Table cannot be null");
+        }
+
+        if (selectItems == null || selectItems.isEmpty()) {
+            // add any column (typically this occurs because of COUNT(*)
+            // queries)
+            Column[] columns = table.getColumns();
+            if (columns.length == 0) {
+                logger.warn("Queried table has no columns: {}", table);
+            } else {
+                selectItems.add(new SelectItem(columns[0]));
+            }
+        }
+
+        if (maxRows == 0) {
+            return new EmptyDataSet(selectItems);
+        }
+
+        final Schema schema = table.getSchema();
+        final String schemaName;
+        if (schema == null) {
+            schemaName = null;
+        } else {
+            schemaName = schema.getName();
+        }
+
+        final DataSet dataSet;
+        if (INFORMATION_SCHEMA_NAME.equals(schemaName)) {
+            final DataSet informationDataSet = materializeInformationSchemaTable(table, selectItems, maxRows);
+            if (firstRow > 1) {
+                dataSet = new FirstRowDataSet(informationDataSet, firstRow);
+            } else {
+                dataSet = informationDataSet;
+            }
+        } else {
+            final DataSet tableDataSet = materializeMainSchemaTable(table, selectItems, firstRow, maxRows);
+
+            // conversion is done at materialization time, since it enables
+            // the refined types to be used also in eg. where clauses.
+            dataSet = new ConvertedDataSetInterceptor(_converters).intercept(tableDataSet);
+        }
+
+        return dataSet;
+    }
+
+    protected boolean isMainSchemaTable(Table table) {
+        Schema schema = table.getSchema();
+        if (INFORMATION_SCHEMA_NAME.equals(schema.getName())) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    @Override
+    protected final String[] getSchemaNamesInternal() throws MetaModelException {
+        final String[] schemaNames = new String[2];
+        schemaNames[0] = INFORMATION_SCHEMA_NAME;
+        schemaNames[1] = getMainSchemaName();
+        return schemaNames;
+    }
+
+    @Override
+    protected String getDefaultSchemaName() throws MetaModelException {
+        return getMainSchemaName();
+    }
+
+    @Override
+    protected final Schema getSchemaByNameInternal(final String name) throws MetaModelException {
+        final String mainSchemaName = getMainSchemaName();
+        if (name == null) {
+            if (mainSchemaName == null) {
+                return getMainSchema();
+            }
+            return null;
+        }
+
+        if (name.equalsIgnoreCase(mainSchemaName)) {
+            return getMainSchemaInternal();
+        } else if (name.equals(INFORMATION_SCHEMA_NAME)) {
+            return getInformationSchema();
+        }
+
+        logger.warn("Could not find matching schema of name '{}'. Main schema name is: '{}'. Returning null.", name,
+                mainSchemaName);
+        return null;
+    }
+
+    private Schema getInformationSchema() {
+        // Create schema
+        MutableSchema informationSchema = new MutableSchema(INFORMATION_SCHEMA_NAME);
+        MutableTable tablesTable = new MutableTable("tables", TableType.TABLE, informationSchema);
+        MutableTable columnsTable = new MutableTable("columns", TableType.TABLE, informationSchema);
+        MutableTable relationshipsTable = new MutableTable("relationships", TableType.TABLE, informationSchema);
+        informationSchema.addTable(tablesTable).addTable(columnsTable).addTable(relationshipsTable);
+
+        // Create "tables" table: name, type, num_columns, remarks
+        tablesTable.addColumn(new MutableColumn("name", ColumnType.VARCHAR, tablesTable, 0, false));
+        tablesTable.addColumn(new MutableColumn("type", ColumnType.VARCHAR, tablesTable, 1, true));
+        tablesTable.addColumn(new MutableColumn("num_columns", ColumnType.INTEGER, tablesTable, 2, true));
+        tablesTable.addColumn(new MutableColumn("remarks", ColumnType.VARCHAR, tablesTable, 3, true));
+
+        // Create "columns" table: name, type, native_type, size, nullable,
+        // indexed, table, remarks
+        columnsTable.addColumn(new MutableColumn("name", ColumnType.VARCHAR, columnsTable, 0, false));
+        columnsTable.addColumn(new MutableColumn("type", ColumnType.VARCHAR, columnsTable, 1, true));
+        columnsTable.addColumn(new MutableColumn("native_type", ColumnType.VARCHAR, columnsTable, 2, true));
+        columnsTable.addColumn(new MutableColumn("size", ColumnType.INTEGER, columnsTable, 3, true));
+        columnsTable.addColumn(new MutableColumn("nullable", ColumnType.BOOLEAN, columnsTable, 4, true));
+        columnsTable.addColumn(new MutableColumn("indexed", ColumnType.BOOLEAN, columnsTable, 5, true));
+        columnsTable.addColumn(new MutableColumn("table", ColumnType.VARCHAR, columnsTable, 6, false));
+        columnsTable.addColumn(new MutableColumn("remarks", ColumnType.VARCHAR, columnsTable, 7, true));
+
+        // Create "relationships" table: primary_table, primary_column,
+        // foreign_table, foreign_column
+        relationshipsTable.addColumn(new MutableColumn("primary_table", ColumnType.VARCHAR, relationshipsTable, 0,
+                false));
+        relationshipsTable.addColumn(new MutableColumn("primary_column", ColumnType.VARCHAR, relationshipsTable, 1,
+                false));
+        relationshipsTable.addColumn(new MutableColumn("foreign_table", ColumnType.VARCHAR, relationshipsTable, 2,
+                false));
+        relationshipsTable.addColumn(new MutableColumn("foreign_column", ColumnType.VARCHAR, relationshipsTable, 3,
+                false));
+
+        MutableRelationship.createRelationship(tablesTable.getColumnByName("name"),
+                columnsTable.getColumnByName("table"));
+        MutableRelationship.createRelationship(tablesTable.getColumnByName("name"),
+                relationshipsTable.getColumnByName("primary_table"));
+        MutableRelationship.createRelationship(tablesTable.getColumnByName("name"),
+                relationshipsTable.getColumnByName("foreign_table"));
+        MutableRelationship.createRelationship(columnsTable.getColumnByName("name"),
+                relationshipsTable.getColumnByName("primary_column"));
+        MutableRelationship.createRelationship(columnsTable.getColumnByName("name"),
+                relationshipsTable.getColumnByName("foreign_column"));
+
+        return informationSchema;
+    }
+
+    private DataSet materializeInformationSchemaTable(final Table table, final List<SelectItem> selectItems,
+            final int maxRows) {
+        final String tableName = table.getName();
+        final SelectItem[] columnSelectItems = MetaModelHelper.createSelectItems(table.getColumns());
+        final SimpleDataSetHeader header = new SimpleDataSetHeader(columnSelectItems);
+        final Table[] tables = getMainSchemaInternal().getTables();
+        final List<Row> data = new ArrayList<Row>();
+        if ("tables".equals(tableName)) {
+            // "tables" columns: name, type, num_columns, remarks
+            for (Table t : tables) {
+                String typeString = null;
+                if (t.getType() != null) {
+                    typeString = t.getType().toString();
+                }
+                data.add(new DefaultRow(header, new Object[] { t.getName(), typeString, t.getColumnCount(),
+                        t.getRemarks() }));
+            }
+        } else if ("columns".equals(tableName)) {
+            // "columns" columns: name, type, native_type, size, nullable,
+            // indexed, table, remarks
+            for (Table t : tables) {
+                for (Column c : t.getColumns()) {
+                    String typeString = null;
+                    if (t.getType() != null) {
+                        typeString = c.getType().toString();
+                    }
+                    data.add(new DefaultRow(header, new Object[] { c.getName(), typeString, c.getNativeType(),
+                            c.getColumnSize(), c.isNullable(), c.isIndexed(), t.getName(), c.getRemarks() }));
+                }
+            }
+        } else if ("relationships".equals(tableName)) {
+            // "relationships" columns: primary_table, primary_column,
+            // foreign_table, foreign_column
+            for (Relationship r : getMainSchemaInternal().getRelationships()) {
+                Column[] primaryColumns = r.getPrimaryColumns();
+                Column[] foreignColumns = r.getForeignColumns();
+                Table pTable = r.getPrimaryTable();
+                Table fTable = r.getForeignTable();
+                for (int i = 0; i < primaryColumns.length; i++) {
+                    Column pColumn = primaryColumns[i];
+                    Column fColumn = foreignColumns[i];
+                    data.add(new DefaultRow(header, new Object[] { pTable.getName(), pColumn.getName(),
+                            fTable.getName(), fColumn.getName() }));
+                }
+            }
+        } else {
+            throw new IllegalArgumentException("Cannot materialize non information_schema table: " + table);
+        }
+
+        DataSet dataSet;
+        if (data.isEmpty()) {
+            dataSet = new EmptyDataSet(selectItems);
+        } else {
+            dataSet = new InMemoryDataSet(header, data);
+        }
+
+        // Handle column subset
+        dataSet = MetaModelHelper.getSelection(selectItems, dataSet);
+
+        // Handle maxRows
+        if (maxRows != -1) {
+            dataSet = new MaxRowsDataSet(dataSet, maxRows);
+        }
+        return dataSet;
+    }
+
+    protected Schema getMainSchemaInternal() {
+        Schema schema = _mainSchema;
+        if (schema == null) {
+            schema = getMainSchema();
+            _mainSchema = schema;
+        }
+        return schema;
+    }
+
+    /**
+     * Adds a {@link TypeConverter} to this DataContext's query engine (Query
+     * Postprocessor) for read operations. Note that this method should NOT be
+     * invoked directly by consuming code. Rather use
+     * {@link Converters#addTypeConverter(DataContext, Column, TypeConverter)}
+     * to ensure conversion on both reads and writes.
+     */
+    @Override
+    public void addConverter(Column column, TypeConverter<?, ?> converter) {
+        _converters.put(column, converter);
+    }
+
+    /**
+     * @return the main schema that subclasses of this class produce
+     */
+    protected abstract Schema getMainSchema() throws MetaModelException;
+
+    /**
+     * @return the name of the main schema that subclasses of this class produce
+     */
+    protected abstract String getMainSchemaName() throws MetaModelException;
+
+    /**
+     * Executes a simple one-table query against a table in the main schema of
+     * the subclasses of this class. This default implementation will delegate
+     * to {@link #materializeMainSchemaTable(Table, Column[], int, int)}.
+     * 
+     * @param table
+     * @param selectItems
+     * @param firstRow
+     * @param maxRows
+     * @return
+     */
+    protected DataSet materializeMainSchemaTable(Table table, List<SelectItem> selectItems, int firstRow, int maxRows) {
+        Column[] columns = new Column[selectItems.size()];
+        for (int i = 0; i < columns.length; i++) {
+            columns[i] = selectItems.get(i).getColumn();
+        }
+        DataSet dataSet = materializeMainSchemaTable(table, columns, firstRow, maxRows);
+
+        dataSet = MetaModelHelper.getSelection(selectItems, dataSet);
+
+        return dataSet;
+    }
+
+    /**
+     * Executes a simple one-table query against a table in the main schema of
+     * the subclasses of this class. This default implementation will delegate
+     * to {@link #materializeMainSchemaTable(Table, Column[], int)} and apply a
+     * {@link FirstRowDataSet} if necessary.
+     * 
+     * @param table
+     * @param columns
+     * @param firstRow
+     * @param maxRows
+     * @return
+     */
+    protected DataSet materializeMainSchemaTable(Table table, Column[] columns, int firstRow, int maxRows) {
+        final int rowsToMaterialize;
+        if (firstRow == 1) {
+            rowsToMaterialize = maxRows;
+        } else {
+            rowsToMaterialize = maxRows + (firstRow - 1);
+        }
+        DataSet dataSet = materializeMainSchemaTable(table, columns, rowsToMaterialize);
+        if (firstRow > 1) {
+            dataSet = new FirstRowDataSet(dataSet, firstRow);
+        }
+        return dataSet;
+    }
+
+    /**
+     * Executes a simple one-table query against a table in the main schema of
+     * the subclasses of this class.
+     * 
+     * @param table
+     *            the table to query
+     * @param columns
+     *            the columns of the table to query
+     * @param maxRows
+     *            the maximum amount of rows needed or -1 if all rows are
+     *            wanted.
+     * @return a dataset with the raw table/column content.
+     */
+    protected abstract DataSet materializeMainSchemaTable(Table table, Column[] columns, int maxRows);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/QueryPostprocessDelegate.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/QueryPostprocessDelegate.java b/core/src/main/java/org/apache/metamodel/QueryPostprocessDelegate.java
new file mode 100644
index 0000000..7d8dde3
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/QueryPostprocessDelegate.java
@@ -0,0 +1,45 @@
+/**
+ * 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;
+
+import org.eobjects.metamodel.schema.Schema;
+
+/**
+ * A simple subclass of {@link QueryPostprocessDataContext} which provides less
+ * implementation fuzz when custom querying features (like composite
+ * datacontexts or type conversion) is needed.
+ * 
+ * @author Kasper Sørensen
+ * @author Ankit Kumar
+ */
+public abstract class QueryPostprocessDelegate extends
+		QueryPostprocessDataContext {
+
+	@Override
+	protected String getMainSchemaName() throws MetaModelException {
+		throw new UnsupportedOperationException(
+				"QueryPostprocessDelegate cannot perform schema exploration");
+	}
+
+	@Override
+	protected Schema getMainSchema() throws MetaModelException {
+		throw new UnsupportedOperationException(
+				"QueryPostprocessDelegate cannot perform schema exploration");
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/SchemaNameComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/SchemaNameComparator.java b/core/src/main/java/org/apache/metamodel/SchemaNameComparator.java
new file mode 100644
index 0000000..14a2f82
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/SchemaNameComparator.java
@@ -0,0 +1,58 @@
+/**
+ * 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;
+
+import java.util.Comparator;
+
+/**
+ * Comparator for comparing schema names.
+ * 
+ * @author Kasper Sørensen
+ */
+class SchemaNameComparator implements Comparator<String> {
+
+    private static Comparator<? super String> _instance = new SchemaNameComparator();
+
+    public static Comparator<? super String> getInstance() {
+        return _instance;
+    }
+
+    private SchemaNameComparator() {
+    }
+
+    public int compare(String o1, String o2) {
+        if (o1 == null && o2 == null) {
+            return 0;
+        }
+        if (o1 == null) {
+            return -1;
+        }
+        if (o2 == null) {
+            return 1;
+        }
+        if (MetaModelHelper.isInformationSchema(o1)) {
+            return -1;
+        }
+        if (MetaModelHelper.isInformationSchema(o2)) {
+            return 1;
+        }
+        return o1.compareTo(o2);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/UpdateCallback.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/UpdateCallback.java b/core/src/main/java/org/apache/metamodel/UpdateCallback.java
new file mode 100644
index 0000000..c19f03f
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/UpdateCallback.java
@@ -0,0 +1,45 @@
+/**
+ * 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;
+
+import org.eobjects.metamodel.create.TableCreatable;
+import org.eobjects.metamodel.delete.RowDeletable;
+import org.eobjects.metamodel.drop.TableDroppable;
+import org.eobjects.metamodel.insert.RowInsertable;
+import org.eobjects.metamodel.update.RowUpdateable;
+
+/**
+ * An {@link UpdateCallback} is used by an {@link UpdateScript} to perform
+ * updates on a {@link DataContext}. Multiple updates (eg. insertion of several
+ * rows or creation of multiple tables) can (and should) be performed with a
+ * single {@link UpdateCallback}. This pattern guarantees that connections
+ * and/or file handles are handled correctly, surrounding the
+ * {@link UpdateScript} that is being executed.
+ * 
+ * @author Kasper Sørensen
+ */
+public interface UpdateCallback extends TableCreatable, TableDroppable, RowInsertable, RowUpdateable, RowDeletable {
+
+    /**
+     * Gets the DataContext on which the update script is being executed.
+     * 
+     * @return the DataContext on which the update script is being executed.
+     */
+    public DataContext getDataContext();
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/UpdateScript.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/UpdateScript.java b/core/src/main/java/org/apache/metamodel/UpdateScript.java
new file mode 100644
index 0000000..b10318e
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/UpdateScript.java
@@ -0,0 +1,41 @@
+/**
+ * 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;
+
+import org.eobjects.metamodel.util.Action;
+
+/**
+ * Represents any updating operation or update script that can be executed on a
+ * {@link UpdateableDataContext}. Users of MetaModel should implement their own
+ * {@link UpdateScript} and submit them to the
+ * {@link UpdateableDataContext#executeUpdate(UpdateScript)} method for
+ * execution.
+ * 
+ * @author Kasper Sørensen
+ */
+public interface UpdateScript extends Action<UpdateCallback> {
+
+	/**
+	 * Invoked by MetaModel when the update script should be run. User should
+	 * implement this method and invoke update operations on the
+	 * {@link UpdateCallback}.
+	 */
+	@Override
+	public void run(UpdateCallback callback);
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/UpdateableDataContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/UpdateableDataContext.java b/core/src/main/java/org/apache/metamodel/UpdateableDataContext.java
new file mode 100644
index 0000000..3153f75
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/UpdateableDataContext.java
@@ -0,0 +1,41 @@
+/**
+ * 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;
+
+/**
+ * Represents a {@link DataContext} that supports updating write-operations.
+ * 
+ * @author Kasper Sørensen
+ */
+public interface UpdateableDataContext extends DataContext {
+
+	/**
+	 * Submits an {@link UpdateScript} for execution on the {@link DataContext}.
+	 * 
+	 * Since implementations of the {@link DataContext} vary quite a lot, there
+	 * is no golden rule as to how an update script will be executed. But the
+	 * implementors should strive towards handling an {@link UpdateScript} as a
+	 * single transactional change to the data store.
+	 * 
+	 * @param update
+	 *            the update script to execute
+	 */
+	public void executeUpdate(UpdateScript update);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/ColumnTypeDetector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/ColumnTypeDetector.java b/core/src/main/java/org/apache/metamodel/convert/ColumnTypeDetector.java
new file mode 100644
index 0000000..1355c95
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/ColumnTypeDetector.java
@@ -0,0 +1,82 @@
+/**
+ * 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.convert;
+
+import org.eobjects.metamodel.util.BooleanComparator;
+import org.eobjects.metamodel.util.TimeComparator;
+
+/**
+ * A class capable of detecting/narrowing a string column type to something more
+ * specific. Either: Boolean, Integer, Double or Date.
+ */
+final class ColumnTypeDetector {
+
+	private boolean _booleanPossible = true;
+	private boolean _integerPossible = true;
+	private boolean _doublePossible = true;
+	private boolean _datePossible = true;
+
+	public void registerValue(String stringValue) {
+		if (stringValue == null || stringValue.length() == 0) {
+			return;
+		}
+		if (_booleanPossible) {
+			try {
+				BooleanComparator.parseBoolean(stringValue);
+			} catch (IllegalArgumentException e) {
+				_booleanPossible = false;
+			}
+		}
+		if (_doublePossible) {
+			try {
+				Double.parseDouble(stringValue);
+			} catch (NumberFormatException e) {
+				_doublePossible = false;
+				_integerPossible = false;
+			}
+			// If integer is possible, double will always also be possible,
+			// but not nescesarily the other way around
+			if (_integerPossible) {
+				try {
+					Integer.parseInt(stringValue);
+				} catch (NumberFormatException e) {
+					_integerPossible = false;
+				}
+			}
+		}
+		if (_datePossible) {
+			if (TimeComparator.toDate(stringValue) == null) {
+				_datePossible = false;
+			}
+		}
+	}
+
+	public TypeConverter<?, ?> createConverter() {
+		if (_booleanPossible) {
+			return new StringToBooleanConverter();
+		} else if (_integerPossible) {
+			return new StringToIntegerConverter();
+		} else if (_doublePossible) {
+			return new StringToDoubleConverter();
+		} else if (_datePossible) {
+			return new StringToDateConverter();
+		}
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSet.java b/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSet.java
new file mode 100644
index 0000000..ff0cf79
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSet.java
@@ -0,0 +1,74 @@
+/**
+ * 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.convert;
+
+import org.eobjects.metamodel.data.AbstractDataSet;
+import org.eobjects.metamodel.data.DataSet;
+import org.eobjects.metamodel.data.DefaultRow;
+import org.eobjects.metamodel.data.Row;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link DataSet} wrapper/decorator which converts values using
+ * {@link TypeConverter}s before returning them to the user.
+ */
+final class ConvertedDataSet extends AbstractDataSet {
+
+    private static final Logger logger = LoggerFactory.getLogger(ConvertedDataSet.class);
+
+    private final DataSet _dataSet;
+    private final TypeConverter<?, ?>[] _converters;
+
+    public ConvertedDataSet(DataSet dataSet, TypeConverter<?, ?>[] converters) {
+        super(dataSet.getSelectItems());
+        _dataSet = dataSet;
+        _converters = converters;
+    }
+
+    @Override
+    public boolean next() {
+        return _dataSet.next();
+    }
+
+    @Override
+    public Row getRow() {
+        Row sourceRow = _dataSet.getRow();
+        Object[] values = new Object[_converters.length];
+        for (int i = 0; i < values.length; i++) {
+            Object value = sourceRow.getValue(i);
+
+            @SuppressWarnings("unchecked")
+            TypeConverter<Object, ?> converter = (TypeConverter<Object, ?>) _converters[i];
+
+            if (converter != null) {
+                Object virtualValue = converter.toVirtualValue(value);
+                logger.debug("Converted physical value {} to {}", value, virtualValue);
+                value = virtualValue;
+            }
+            values[i] = value;
+        }
+        return new DefaultRow(getHeader(), values);
+    }
+
+    @Override
+    public void close() {
+        _dataSet.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSetInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSetInterceptor.java b/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSetInterceptor.java
new file mode 100644
index 0000000..159c469
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/ConvertedDataSetInterceptor.java
@@ -0,0 +1,91 @@
+/**
+ * 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.convert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eobjects.metamodel.data.DataSet;
+import org.eobjects.metamodel.intercept.DataSetInterceptor;
+import org.eobjects.metamodel.query.SelectItem;
+import org.eobjects.metamodel.schema.Column;
+
+/**
+ * A {@link DataSetInterceptor} used for intercepting values in {@link DataSet}s
+ * that need to be converted, according to a set of {@link TypeConverter}s.
+ * 
+ * @see TypeConverter
+ * @see Converters
+ */
+public class ConvertedDataSetInterceptor implements DataSetInterceptor, HasReadTypeConverters {
+
+	private Map<Column, TypeConverter<?, ?>> _converters;
+
+	public ConvertedDataSetInterceptor() {
+		this(new HashMap<Column, TypeConverter<?, ?>>());
+	}
+
+	public ConvertedDataSetInterceptor(
+			Map<Column, TypeConverter<?, ?>> converters) {
+		_converters = converters;
+	}
+
+	@Override
+	public void addConverter(Column column, TypeConverter<?, ?> converter) {
+		if (converter == null) {
+			_converters.remove(column);
+		} else {
+			_converters.put(column, converter);
+		}
+	}
+	
+	protected Map<Column, TypeConverter<?, ?>> getConverters(DataSet dataSet) {
+		return _converters;
+	}
+
+	@Override
+	public final DataSet intercept(DataSet dataSet) {
+		Map<Column, TypeConverter<?, ?>> converters = getConverters(dataSet);
+		if (converters.isEmpty()) {
+			return dataSet;
+		}
+
+		boolean hasConverter = false;
+		SelectItem[] selectItems = dataSet.getSelectItems();
+		TypeConverter<?, ?>[] converterArray = new TypeConverter[selectItems.length];
+		for (int i = 0; i < selectItems.length; i++) {
+			SelectItem selectItem = selectItems[i];
+			Column column = selectItem.getColumn();
+			if (column != null && selectItem.getFunction() == null) {
+				TypeConverter<?, ?> converter = converters.get(column);
+				if (converter != null) {
+					hasConverter = true;
+					converterArray[i] = converter;
+				}
+			}
+		}
+
+		if (!hasConverter) {
+			return dataSet;
+		}
+
+		return new ConvertedDataSet(dataSet, converterArray);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/ConvertedRowInsertionInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/ConvertedRowInsertionInterceptor.java b/core/src/main/java/org/apache/metamodel/convert/ConvertedRowInsertionInterceptor.java
new file mode 100644
index 0000000..b7ea3ad
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/ConvertedRowInsertionInterceptor.java
@@ -0,0 +1,75 @@
+/**
+ * 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.convert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eobjects.metamodel.insert.RowInsertionBuilder;
+import org.eobjects.metamodel.intercept.RowInsertionInterceptor;
+import org.eobjects.metamodel.schema.Column;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link RowInsertionInterceptor} used for intercepting values in
+ * {@link RowInsertionBuilder}s that need to be converted, according to a set of
+ * {@link TypeConverter}s.
+ * 
+ * @see TypeConverter
+ * @see Converters
+ */
+public class ConvertedRowInsertionInterceptor implements RowInsertionInterceptor {
+
+    private static final Logger logger = LoggerFactory.getLogger(ConvertedRowInsertionInterceptor.class);
+
+    private final Map<Column, TypeConverter<?, ?>> _converters;
+
+    public ConvertedRowInsertionInterceptor() {
+        this(new HashMap<Column, TypeConverter<?, ?>>());
+    }
+
+    public ConvertedRowInsertionInterceptor(Map<Column, TypeConverter<?, ?>> converters) {
+        _converters = converters;
+    }
+
+    public void addConverter(Column column, TypeConverter<?, ?> converter) {
+        if (converter == null) {
+            _converters.remove(column);
+        } else {
+            _converters.put(column, converter);
+        }
+    }
+
+    @Override
+    public RowInsertionBuilder intercept(RowInsertionBuilder insert) {
+        if (_converters.isEmpty()) {
+            return insert;
+        }
+
+        logger.debug("Insert statement before conversion: {}", insert);
+
+        insert = Converters.convertRow(insert, _converters);
+
+        logger.debug("Insert statement after conversion:  {}", insert);
+
+        return insert;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/ConvertedRowUpdationInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/ConvertedRowUpdationInterceptor.java b/core/src/main/java/org/apache/metamodel/convert/ConvertedRowUpdationInterceptor.java
new file mode 100644
index 0000000..6a42107
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/ConvertedRowUpdationInterceptor.java
@@ -0,0 +1,67 @@
+/**
+ * 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.convert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eobjects.metamodel.intercept.RowUpdationInterceptor;
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.update.RowUpdationBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConvertedRowUpdationInterceptor implements RowUpdationInterceptor {
+
+    private static final Logger logger = LoggerFactory.getLogger(ConvertedRowUpdationInterceptor.class);
+
+    private final Map<Column, TypeConverter<?, ?>> _converters;
+
+    public ConvertedRowUpdationInterceptor() {
+        this(new HashMap<Column, TypeConverter<?, ?>>());
+    }
+
+    public ConvertedRowUpdationInterceptor(Map<Column, TypeConverter<?, ?>> converters) {
+        _converters = converters;
+    }
+
+    public void addConverter(Column column, TypeConverter<?, ?> converter) {
+        if (converter == null) {
+            _converters.remove(column);
+        } else {
+            _converters.put(column, converter);
+        }
+    }
+
+    @Override
+    public RowUpdationBuilder intercept(RowUpdationBuilder update) {
+        if (_converters.isEmpty()) {
+            return update;
+        }
+
+        logger.debug("Update statement after conversion:  {}", update);
+
+        update = Converters.convertRow(update, _converters);
+        
+        logger.debug("Update statement after conversion:  {}", update);
+
+        return update;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/Converters.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/Converters.java b/core/src/main/java/org/apache/metamodel/convert/Converters.java
new file mode 100644
index 0000000..7b7b43c
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/Converters.java
@@ -0,0 +1,329 @@
+/**
+ * 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.convert;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.eobjects.metamodel.DataContext;
+import org.eobjects.metamodel.MetaModelHelper;
+import org.eobjects.metamodel.UpdateableDataContext;
+import org.eobjects.metamodel.data.DataSet;
+import org.eobjects.metamodel.data.Row;
+import org.eobjects.metamodel.data.RowBuilder;
+import org.eobjects.metamodel.data.Style;
+import org.eobjects.metamodel.intercept.InterceptableDataContext;
+import org.eobjects.metamodel.intercept.Interceptors;
+import org.eobjects.metamodel.query.Query;
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.schema.SuperColumnType;
+import org.eobjects.metamodel.schema.Table;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class consists of static methods for decorating {@link DataContext}s
+ * with {@link TypeConverter}s, which allows for automatic conversion of values
+ * on data read and write operations.
+ */
+public final class Converters {
+
+    private static final Logger logger = LoggerFactory.getLogger(Converters.class);
+
+    private Converters() {
+        // prevent instantiation
+    }
+
+    /**
+     * Adds a type converter to a specific column in the {@link DataContext}.
+     * 
+     * @param dataContext
+     *            the DataContext to decorate
+     * @param column
+     *            the column which holds values to convert
+     * @param converter
+     *            the converter to use on the specified column
+     * @return a decorated DataContext, which should be used for successive
+     *         operations on the data.
+     */
+    public static UpdateableDataContext addTypeConverter(UpdateableDataContext dataContext, Column column,
+            TypeConverter<?, ?> converter) {
+        return addTypeConverterInternally(dataContext, column, converter);
+    }
+
+    /**
+     * Adds a type converter to a specific column in the {@link DataContext}.
+     * 
+     * @param dataContext
+     *            the DataContext to decorate
+     * @param column
+     *            the column which holds values to convert
+     * @param converter
+     *            the converter to use on the specified column
+     * @return a decorated DataContext, which should be used for successive
+     *         operations on the data.
+     */
+    public static DataContext addTypeConverter(DataContext dataContext, Column column, TypeConverter<?, ?> converter) {
+        return addTypeConverterInternally(dataContext, column, converter);
+    }
+
+    /**
+     * Adds a collection of type converters to specific columns in the
+     * {@link DataContext}
+     * 
+     * @param dataContext
+     *            the DataContext to decorate
+     * @param converters
+     *            a map containing columns and mapped type converters.
+     * @return a decorated DataContext, which should be used for successive
+     *         operations on the data.
+     */
+    public static UpdateableDataContext addTypeConverters(UpdateableDataContext dataContext,
+            Map<Column, TypeConverter<?, ?>> converters) {
+        return addTypeConvertersInternally(dataContext, converters);
+    }
+
+    /**
+     * Adds a collection of type converters to specific columns in the
+     * {@link DataContext}
+     * 
+     * @param dataContext
+     *            the DataContext to decorate
+     * @param converters
+     *            a map containing columns and mapped type converters.
+     * @return a decorated DataContext, which should be used for successive
+     *         operations on the data.
+     */
+    public static DataContext addTypeConverters(DataContext dataContext, Map<Column, TypeConverter<?, ?>> converters) {
+        return addTypeConvertersInternally(dataContext, converters);
+    }
+
+    /**
+     * Auto-detects / guesses the type converters to be applied on set of
+     * columns. This method will query the String columns in order to assert
+     * which columns are likely candidates for conversion.
+     * 
+     * As such this method is not guaranteed to pick the correct converters,
+     * since data can change over time or other conversions can be requested.
+     * 
+     * @param dataContext
+     *            the DataContext that holds the data.
+     * @param columns
+     *            the columns to inspect to find type conversion candidates.
+     * @param sampleSize
+     *            the max amount of rows to query for doing auto-detection. Use
+     *            {@link Integer#MAX_VALUE} if no constraint should be put on
+     *            the number of records to sample.
+     * @return a map of {@link Column}s and {@link TypeConverter}s which can be
+     *         used (eg. with the {@link #addTypeConverters(DataContext, Map)}
+     *         method) to decorate the DataContext with type converters.
+     */
+    public static Map<Column, TypeConverter<?, ?>> autoDetectConverters(DataContext dataContext, Column[] columns,
+            int sampleSize) {
+        columns = MetaModelHelper.getColumnsBySuperType(columns, SuperColumnType.LITERAL_TYPE);
+        final Map<Column, TypeConverter<?, ?>> result = new HashMap<Column, TypeConverter<?, ?>>();
+        Table[] tables = MetaModelHelper.getTables(columns);
+        for (Table table : tables) {
+            Column[] tableColumns = MetaModelHelper.getTableColumns(table, columns);
+            autoDetectConvertersInternally(dataContext, table, tableColumns, sampleSize, result);
+        }
+        return result;
+    }
+
+    /**
+     * Auto-detects / guesses the type converters to be applied on a table. This
+     * method will query the String columns of a table in order to assert which
+     * columns are likely candidates for conversion.
+     * 
+     * As such this method is not guaranteed to pick the correct converters,
+     * since data can change over time or other conversions can be requested.
+     * 
+     * @param dataContext
+     *            the DataContext that holds the data.
+     * @param table
+     *            the table to inspect to find type conversion candidates. This
+     *            table will hold all columns of the result.
+     * @param sampleSize
+     *            the max amount of rows to query for doing auto-detection. Use
+     *            {@link Integer#MAX_VALUE} if no constraint should be put on
+     *            the number of records to sample.
+     * @return a map of {@link Column}s and {@link TypeConverter}s which can be
+     *         used (eg. with the {@link #addTypeConverters(DataContext, Map)}
+     *         method) to decorate the DataContext with type converters.
+     */
+    public static Map<Column, TypeConverter<?, ?>> autoDetectConverters(DataContext dataContext, Table table,
+            int sampleSize) {
+        final Map<Column, TypeConverter<?, ?>> result = new HashMap<Column, TypeConverter<?, ?>>();
+        Column[] columns = table.getColumnsOfSuperType(SuperColumnType.LITERAL_TYPE);
+        autoDetectConvertersInternally(dataContext, table, columns, sampleSize, result);
+        return result;
+    }
+
+    private static void autoDetectConvertersInternally(DataContext dataContext, Table table, Column[] columns,
+            int sampleSize, Map<Column, TypeConverter<?, ?>> result) {
+        if (columns == null || columns.length == 0) {
+            return;
+        }
+
+        Map<Column, ColumnTypeDetector> detectors = new HashMap<Column, ColumnTypeDetector>();
+        for (Column column : columns) {
+            detectors.put(column, new ColumnTypeDetector());
+        }
+
+        Query query = dataContext.query().from(table).select(columns).toQuery();
+        if (sampleSize > 0 && sampleSize != Integer.MAX_VALUE) {
+            query.setMaxRows(sampleSize);
+        }
+        DataSet dataSet = dataContext.executeQuery(query);
+        try {
+            while (dataSet.next()) {
+                Row row = dataSet.getRow();
+                for (Column column : columns) {
+                    String stringValue = (String) row.getValue(column);
+                    ColumnTypeDetector detector = detectors.get(column);
+                    detector.registerValue(stringValue);
+                }
+            }
+        } finally {
+            dataSet.close();
+        }
+        for (Column column : columns) {
+            ColumnTypeDetector detector = detectors.get(column);
+            TypeConverter<?, ?> converter = detector.createConverter();
+            if (converter != null) {
+                result.put(column, converter);
+            }
+        }
+    }
+
+    private static InterceptableDataContext addTypeConvertersInternally(final DataContext dc,
+            Map<Column, TypeConverter<?, ?>> converters) {
+        if (converters == null) {
+            throw new IllegalArgumentException("Converters cannot be null");
+        }
+
+        InterceptableDataContext interceptable = Interceptors.intercept(dc);
+
+        Set<Entry<Column, TypeConverter<?, ?>>> entries = converters.entrySet();
+        for (Entry<Column, TypeConverter<?, ?>> entry : entries) {
+            Column column = entry.getKey();
+            TypeConverter<?, ?> converter = entry.getValue();
+            interceptable = addTypeConverterInternally(interceptable, column, converter);
+        }
+
+        return interceptable;
+    }
+
+    private static InterceptableDataContext addTypeConverterInternally(final DataContext dc, Column column,
+            TypeConverter<?, ?> converter) {
+        if (column == null) {
+            throw new IllegalArgumentException("Column cannot be null");
+        }
+
+        InterceptableDataContext interceptable = Interceptors.intercept(dc);
+        DataContext delegate = interceptable.getDelegate();
+
+        boolean interceptDataSets = true;
+
+        if (delegate instanceof HasReadTypeConverters) {
+            // some DataContexts implement the HasTypeConverters interface,
+            // which is preferred when available
+            HasReadTypeConverters hasTypeConverter = (HasReadTypeConverters) delegate;
+            hasTypeConverter.addConverter(column, converter);
+
+            interceptDataSets = false;
+        }
+
+        addTypeConverterInterceptors(interceptable, column, converter, interceptDataSets);
+        return interceptable;
+    }
+
+    private static void addTypeConverterInterceptors(InterceptableDataContext interceptable, Column column,
+            TypeConverter<?, ?> converter, boolean interceptDataSets) {
+        // intercept datasets (reads)
+        if (interceptDataSets) {
+            ConvertedDataSetInterceptor interceptor = interceptable.getDataSetInterceptors().getInterceptorOfType(
+                    ConvertedDataSetInterceptor.class);
+            if (interceptor == null) {
+                interceptor = new ConvertedDataSetInterceptor();
+                interceptable.addDataSetInterceptor(interceptor);
+            }
+            interceptor.addConverter(column, converter);
+        }
+
+        // intercept inserts (writes)
+        {
+            ConvertedRowInsertionInterceptor interceptor = interceptable.getRowInsertionInterceptors()
+                    .getInterceptorOfType(ConvertedRowInsertionInterceptor.class);
+            if (interceptor == null) {
+                interceptor = new ConvertedRowInsertionInterceptor();
+                interceptable.addRowInsertionInterceptor(interceptor);
+            }
+            interceptor.addConverter(column, converter);
+        }
+
+        // convert updates
+        {
+            ConvertedRowUpdationInterceptor interceptor = interceptable.getRowUpdationInterceptors()
+                    .getInterceptorOfType(ConvertedRowUpdationInterceptor.class);
+            if (interceptor == null) {
+                interceptor = new ConvertedRowUpdationInterceptor();
+                interceptable.addRowUpdationInterceptor(interceptor);
+            }
+            interceptor.addConverter(column, converter);
+        }
+
+        // converting deletes (as well as where-items in updates) should not be
+        // applied, because the DataSet interceptor is anyways only working on
+        // the output. In that sense it adds symetry to NOT support conversion
+        // in the where clause of UPDATEs and DELETEs.
+    }
+
+    /**
+     * Converts values in a {@link RowBuilder}.
+     * 
+     * @param rowBuilder
+     * @param converters
+     * @return
+     */
+    protected static <RB extends RowBuilder<?>> RB convertRow(RB rowBuilder, Map<Column, TypeConverter<?, ?>> converters) {
+        Table table = rowBuilder.getTable();
+        Column[] columns = table.getColumns();
+        Row row = rowBuilder.toRow();
+        for (Column column : columns) {
+            @SuppressWarnings("unchecked")
+            TypeConverter<?, Object> converter = (TypeConverter<?, Object>) converters.get(column);
+            if (converter != null) {
+                final int indexInRow = row.indexOf(column);
+                final Object value = row.getValue(indexInRow);
+                final Object physicalValue = converter.toPhysicalValue(value);
+                logger.debug("Converted virtual value {} to {}", value, physicalValue);
+                if (value == null && physicalValue == null && !rowBuilder.isSet(column)) {
+                    logger.debug("Omitting implicit null value for column: {}", column);
+                } else {
+                    final Style style = row.getStyle(indexInRow);
+                    rowBuilder.value(column, physicalValue, style);
+                }
+            }
+        }
+        return rowBuilder;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/HasReadTypeConverters.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/HasReadTypeConverters.java b/core/src/main/java/org/apache/metamodel/convert/HasReadTypeConverters.java
new file mode 100644
index 0000000..5f63c1e
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/HasReadTypeConverters.java
@@ -0,0 +1,33 @@
+/**
+ * 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.convert;
+
+import org.eobjects.metamodel.data.DataSet;
+import org.eobjects.metamodel.schema.Column;
+
+/**
+ * Defines an interface for objects that are aware of {@link TypeConverter}s,
+ * and know how to apply them to read operations (Queries or {@link DataSet}s).
+ * 
+ * @author Kasper Sørensen
+ */
+public interface HasReadTypeConverters {
+
+	public void addConverter(Column column, TypeConverter<?, ?> converter);
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/StringToBooleanConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/StringToBooleanConverter.java b/core/src/main/java/org/apache/metamodel/convert/StringToBooleanConverter.java
new file mode 100644
index 0000000..fb546a4
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/StringToBooleanConverter.java
@@ -0,0 +1,54 @@
+/**
+ * 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.convert;
+
+import org.eobjects.metamodel.util.BooleanComparator;
+
+/**
+ * A {@link TypeConverter} that converts String values (on the physical layer)
+ * to interpreted Booleans.
+ * 
+ * @author Kasper Sørensen
+ * @author Ankit Kumar
+ */
+public class StringToBooleanConverter implements TypeConverter<String, Boolean> {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toPhysicalValue(Boolean virtualValue) {
+		if (virtualValue == null) {
+			return null;
+		}
+		return virtualValue.toString();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Boolean toVirtualValue(String physicalValue) {
+		if (physicalValue == null || physicalValue.length() == 0) {
+			return null;
+		}
+		return BooleanComparator.parseBoolean(physicalValue);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/StringToDateConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/StringToDateConverter.java b/core/src/main/java/org/apache/metamodel/convert/StringToDateConverter.java
new file mode 100644
index 0000000..1684a31
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/StringToDateConverter.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.convert;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.eobjects.metamodel.util.Func;
+import org.eobjects.metamodel.util.TimeComparator;
+
+/**
+ * A {@link TypeConverter} that converts String values (on the physical layer)
+ * to interpreted {@link Date}s.
+ * 
+ * @author Kasper Sørensen
+ * @author Ankit Kumar
+ */
+public class StringToDateConverter implements TypeConverter<String, Date> {
+
+	private final Func<Date, String> _serializeFunc;
+	private final Func<String, Date> _deserializeFunc;
+
+	/**
+	 * Constructs a new {@link StringToDateConverter} which will use the
+	 * {@link TimeComparator#toDate(Object)} method for parsing dates and the
+	 * {@link DateFormat#MEDIUM} date time format for physical representation.
+	 */
+	public StringToDateConverter() {
+		_deserializeFunc = new Func<String, Date>() {
+			@Override
+			public Date eval(String stringValue) {
+				return TimeComparator.toDate(stringValue);
+			}
+		};
+		_serializeFunc = new Func<Date, String>() {
+			@Override
+			public String eval(Date date) {
+				return DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
+						DateFormat.MEDIUM).format(date);
+			}
+		};
+	}
+
+	/**
+	 * Constructs a new {@link StringToDateConverter} using a given date
+	 * pattern.
+	 * 
+	 * @param datePattern
+	 *            a String date pattern, corresponding to the syntax of a
+	 *            {@link SimpleDateFormat}.
+	 */
+	public StringToDateConverter(String datePattern) {
+		this(new SimpleDateFormat(datePattern));
+	}
+
+	/**
+	 * Constructs a new {@link StringToDateConverter} using a given
+	 * {@link DateFormat}.
+	 * 
+	 * @param dateFormat
+	 *            the {@link DateFormat} to use for parsing and formatting
+	 *            dates.
+	 */
+	public StringToDateConverter(final DateFormat dateFormat) {
+		if (dateFormat == null) {
+			throw new IllegalArgumentException("DateFormat cannot be null");
+		}
+		_deserializeFunc = new Func<String, Date>() {
+			@Override
+			public Date eval(String string) {
+				try {
+					return dateFormat.parse(string);
+				} catch (ParseException e) {
+					throw new IllegalArgumentException(
+							"Could not parse date string: " + string);
+				}
+			}
+		};
+		_serializeFunc = new Func<Date, String>() {
+			@Override
+			public String eval(Date date) {
+				return dateFormat.format(date);
+			}
+		};
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toPhysicalValue(Date virtualValue) {
+		if (virtualValue == null) {
+			return null;
+		}
+		return _serializeFunc.eval(virtualValue);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Date toVirtualValue(String physicalValue) {
+		if (physicalValue == null || physicalValue.length() == 0) {
+			return null;
+		}
+		return _deserializeFunc.eval(physicalValue);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/StringToDoubleConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/StringToDoubleConverter.java b/core/src/main/java/org/apache/metamodel/convert/StringToDoubleConverter.java
new file mode 100644
index 0000000..6ce6406
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/StringToDoubleConverter.java
@@ -0,0 +1,52 @@
+/**
+ * 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.convert;
+
+/**
+ * A {@link TypeConverter} that converts String values (on the physical layer)
+ * to interpreted Doubles.
+ * 
+ * @author Kasper Sørensen
+ * @author Ankit Kumar
+ */
+public class StringToDoubleConverter implements TypeConverter<String, Double> {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toPhysicalValue(Double virtualValue) {
+		if (virtualValue == null) {
+			return null;
+		}
+		return virtualValue.toString();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Double toVirtualValue(String physicalValue) {
+		if (physicalValue == null || physicalValue.length() == 0) {
+			return null;
+		}
+		return Double.parseDouble(physicalValue);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/StringToIntegerConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/StringToIntegerConverter.java b/core/src/main/java/org/apache/metamodel/convert/StringToIntegerConverter.java
new file mode 100644
index 0000000..4aafe7d
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/StringToIntegerConverter.java
@@ -0,0 +1,52 @@
+/**
+ * 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.convert;
+
+/**
+ * A {@link TypeConverter} that converts String values (on the physical layer)
+ * to interpreted Integers.
+ * 
+ * @author Kasper Sørensen
+ * @author Ankit Kumar
+ */
+public class StringToIntegerConverter implements TypeConverter<String, Integer> {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toPhysicalValue(Integer virtualValue) {
+		if (virtualValue == null) {
+			return null;
+		}
+		return virtualValue.toString();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Integer toVirtualValue(String physicalValue) {
+		if (physicalValue == null || physicalValue.length() == 0) {
+			return null;
+		}
+		return Integer.parseInt(physicalValue);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/TypeConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/TypeConverter.java b/core/src/main/java/org/apache/metamodel/convert/TypeConverter.java
new file mode 100644
index 0000000..542449e
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/TypeConverter.java
@@ -0,0 +1,54 @@
+/**
+ * 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.convert;
+
+/**
+ * Defines an interface for converting values from and to their physical
+ * materializations and their virtual representations.
+ * 
+ * @see ConvertedDataContext
+ * 
+ * @author Kasper Sørensen
+ * @author Ankit Kumar
+ * 
+ * @param <P>
+ *            the physical type of value
+ * @param <V>
+ *            the virtual type of value
+ */
+public interface TypeConverter<P, V> {
+
+	/**
+	 * Converts a virtual representation of a value into it's physical value.
+	 * 
+	 * @param virtualValue
+	 *            the virtual representation
+	 * @return the physical value
+	 */
+	public P toPhysicalValue(V virtualValue);
+
+	/**
+	 * Converts a physical value into it's virtual representation.
+	 * 
+	 * @param physicalValue
+	 *            the physical value
+	 * @return the virtual representation
+	 */
+	public V toVirtualValue(P physicalValue);
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/convert/package-info.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/convert/package-info.java b/core/src/main/java/org/apache/metamodel/convert/package-info.java
new file mode 100644
index 0000000..0adf2aa
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/convert/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.
+ */
+/**
+ * DataContext decorator for implicit conversion of value types after querying and before insertion.
+ */
+package org.eobjects.metamodel.convert;
+

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/create/AbstractColumnBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/create/AbstractColumnBuilder.java b/core/src/main/java/org/apache/metamodel/create/AbstractColumnBuilder.java
new file mode 100644
index 0000000..26fee26
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/create/AbstractColumnBuilder.java
@@ -0,0 +1,87 @@
+/**
+ * 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.create;
+
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.schema.ColumnType;
+import org.eobjects.metamodel.schema.MutableColumn;
+
+/**
+ * Convenience implementation of all {@link ColumnBuilder} methods
+ * 
+ * @param <T>
+ *            the return type of the builder methods.
+ */
+abstract class AbstractColumnBuilder<T extends ColumnBuilder<?>> implements ColumnBuilder<T> {
+
+    private final MutableColumn _column;
+
+    public AbstractColumnBuilder(MutableColumn column) {
+        _column = column;
+    }
+    
+    protected MutableColumn getColumn() {
+        return _column;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected T getReturnObject() {
+        return (T) this;
+    }
+
+    @Override
+    public final T like(Column column) {
+        _column.setColumnSize(column.getColumnSize());
+        _column.setNativeType(column.getNativeType());
+        _column.setType(column.getType());
+        _column.setNullable(column.isNullable());
+        return getReturnObject();
+    }
+
+    @Override
+    public final T ofType(ColumnType type) {
+        _column.setType(type);
+        return getReturnObject();
+    }
+
+    @Override
+    public final T ofNativeType(String nativeType) {
+        _column.setNativeType(nativeType);
+        return getReturnObject();
+    }
+
+    @Override
+    public final T ofSize(int size) {
+        _column.setColumnSize(size);
+        return getReturnObject();
+    }
+
+    @Override
+    public final T nullable(boolean nullable) {
+        _column.setNullable(nullable);
+        return getReturnObject();
+    }
+
+    @Override
+    public final T asPrimaryKey() {
+        _column.setPrimaryKey(true);
+        return getReturnObject();
+    }
+
+}