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:55 UTC

[42/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/query/parser/QueryPartProcessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/parser/QueryPartProcessor.java b/core/src/main/java/org/apache/metamodel/query/parser/QueryPartProcessor.java
new file mode 100644
index 0000000..27a5d88
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/parser/QueryPartProcessor.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.query.parser;
+
+/**
+ * Callback of the {@link QueryPartParser}, which recieves notifications
+ * whenever a token is identified/parsed. A {@link QueryPartProcessor} is used
+ * to perform the actual processing of identified tokens.
+ */
+public interface QueryPartProcessor {
+
+    /**
+     * Method invoked whenever the {@link QueryPartParser} identifies a token.
+     * 
+     * @param delim
+     *            the (previous) delimitor identified before the token. This
+     *            will always be null in case of the first token.
+     * @param token
+     *            the token identified.
+     */
+    public void parse(String delim, String token);
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/parser/SelectItemParser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/parser/SelectItemParser.java b/core/src/main/java/org/apache/metamodel/query/parser/SelectItemParser.java
new file mode 100644
index 0000000..2810a4a
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/parser/SelectItemParser.java
@@ -0,0 +1,170 @@
+/**
+ * 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.query.parser;
+
+import org.eobjects.metamodel.MetaModelException;
+import org.eobjects.metamodel.query.FromItem;
+import org.eobjects.metamodel.query.FunctionType;
+import org.eobjects.metamodel.query.Query;
+import org.eobjects.metamodel.query.SelectItem;
+import org.eobjects.metamodel.schema.Column;
+
+public final class SelectItemParser implements QueryPartProcessor {
+
+    public static class MultipleSelectItemsParsedException extends IllegalArgumentException {
+
+        private static final long serialVersionUID = 1L;
+
+        private final FromItem _fromItem;
+
+        public MultipleSelectItemsParsedException(FromItem fromItem) {
+            _fromItem = fromItem;
+        }
+
+        public FromItem getFromItem() {
+            return _fromItem;
+        }
+    }
+
+    private final Query _query;
+    private final boolean _allowExpressionBasedSelectItems;
+
+    public SelectItemParser(Query query, boolean allowExpressionBasedSelectItems) {
+        _query = query;
+        _allowExpressionBasedSelectItems = allowExpressionBasedSelectItems;
+    }
+
+    @Override
+    public void parse(String delim, String itemToken) throws MetaModelException {
+        if ("*".equals(itemToken)) {
+            _query.selectAll();
+            return;
+        }
+
+        String alias = null;
+        final int indexOfAlias = itemToken.toUpperCase().lastIndexOf(" AS ");
+        if (indexOfAlias != -1) {
+            alias = itemToken.substring(indexOfAlias + " AS ".length());
+            itemToken = itemToken.substring(0, indexOfAlias);
+        }
+
+        try {
+            final SelectItem selectItem = findSelectItem(itemToken);
+            if (selectItem == null) {
+                throw new QueryParserException("Not capable of parsing SELECT token: " + itemToken);
+            }
+
+            if (alias != null) {
+                selectItem.setAlias(alias);
+            }
+
+            _query.select(selectItem);
+        } catch (MultipleSelectItemsParsedException e) {
+            FromItem fromItem = e.getFromItem();
+            if (fromItem != null) {
+                _query.selectAll(fromItem);
+            } else {
+                throw e;
+            }
+        }
+    }
+    
+    /**
+     * Finds/creates a SelectItem based on the given expression. Unlike the
+     * {@link #parse(String, String)} method, this method will not actually add
+     * the selectitem to the query.
+     * 
+     * @param expression
+     * @return
+     * 
+     * @throws MultipleSelectItemsParsedException
+     *             if an expression yielding multiple select-items (such as "*")
+     *             was passed in the expression
+     */
+    public SelectItem findSelectItem(String expression) throws MultipleSelectItemsParsedException {
+        if ("*".equals(expression)) {
+            throw new MultipleSelectItemsParsedException(null);
+        }
+
+        if ("COUNT(*)".equalsIgnoreCase(expression)) {
+            return SelectItem.getCountAllItem();
+        }
+
+        final FunctionType function;
+        final int startParenthesis = expression.indexOf('(');
+        if (startParenthesis > 0 && expression.endsWith(")")) {
+            String functionName = expression.substring(0, startParenthesis);
+            function = FunctionType.get(functionName);
+            if (function != null) {
+                expression = expression.substring(startParenthesis + 1, expression.length() - 1).trim();
+                if (function == FunctionType.COUNT && "*".equals(expression)) {
+                    return SelectItem.getCountAllItem();
+                }
+            }
+        } else {
+            function = null;
+        }
+
+        int lastIndexOfDot = expression.lastIndexOf(".");
+
+        String columnName = null;
+        FromItem fromItem = null;
+
+        if (lastIndexOfDot != -1) {
+            String prefix = expression.substring(0, lastIndexOfDot);
+            columnName = expression.substring(lastIndexOfDot + 1);
+            fromItem = _query.getFromClause().getItemByReference(prefix);
+        }
+
+        if (fromItem == null) {
+            if (_query.getFromClause().getItemCount() == 1) {
+                fromItem = _query.getFromClause().getItem(0);
+                columnName = expression;
+            } else {
+                fromItem = null;
+                columnName = null;
+            }
+        }
+
+        if (fromItem != null) {
+            if ("*".equals(columnName)) {
+                throw new MultipleSelectItemsParsedException(fromItem);
+            } else if (fromItem.getTable() != null) {
+                Column column = fromItem.getTable().getColumnByName(columnName);
+                if (column != null) {
+                    SelectItem selectItem = new SelectItem(function, column, fromItem);
+                    return selectItem;
+                }
+            } else if (fromItem.getSubQuery() != null) {
+                final Query subQuery = fromItem.getSubQuery();
+                final SelectItem subQuerySelectItem = new SelectItemParser(subQuery, _allowExpressionBasedSelectItems).findSelectItem(columnName);
+                if (subQuerySelectItem == null) {
+                    return null;
+                }
+                return new SelectItem(subQuerySelectItem, fromItem);
+            }
+        }
+
+        if (_allowExpressionBasedSelectItems) {
+            return new SelectItem(function, expression, null);
+        }
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/parser/WhereItemParser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/parser/WhereItemParser.java b/core/src/main/java/org/apache/metamodel/query/parser/WhereItemParser.java
new file mode 100644
index 0000000..764fbdc
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/parser/WhereItemParser.java
@@ -0,0 +1,35 @@
+/**
+ * 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.query.parser;
+
+import org.eobjects.metamodel.query.Query;
+
+final class WhereItemParser implements QueryPartProcessor {
+
+    private final Query _query;
+
+    public WhereItemParser(Query query) {
+        _query = query;
+    }
+
+    @Override
+    public void parse(String delim, String itemToken) {
+        _query.where(itemToken);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/schema/AbstractColumn.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/AbstractColumn.java b/core/src/main/java/org/apache/metamodel/schema/AbstractColumn.java
new file mode 100644
index 0000000..85cc2e4
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/AbstractColumn.java
@@ -0,0 +1,104 @@
+/**
+ * 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.schema;
+
+/**
+ * Abstract {@link Column} implementation. Implements most common and trivial
+ * methods.
+ * 
+ * @author Kasper Sørensen
+ */
+public abstract class AbstractColumn implements Column {
+
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public final String getQuotedName() {
+        String quote = getQuote();
+        if (quote == null) {
+            return getName();
+        }
+        return quote + getName() + quote;
+    }
+
+    @Override
+    public final String getQualifiedLabel() {
+        StringBuilder sb = new StringBuilder();
+        Table table = getTable();
+        if (table != null) {
+            sb.append(table.getQualifiedLabel());
+            sb.append('.');
+        }
+        sb.append(getName());
+        return sb.toString();
+    }
+
+    @Override
+    public final int compareTo(Column that) {
+        int diff = getQualifiedLabel().compareTo(that.getQualifiedLabel());
+        if (diff == 0) {
+            diff = toString().compareTo(that.toString());
+        }
+        return diff;
+    }
+
+    @Override
+    public final String toString() {
+        return "Column[name=" + getName() + ",columnNumber=" + getColumnNumber() + ",type=" + getType() + ",nullable="
+                + isNullable() + ",nativeType=" + getNativeType() + ",columnSize=" + getColumnSize() + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return getName().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof Column) {
+            Column other = (Column) obj;
+            if (!getName().equals(other.getName())) {
+                return false;
+            }
+            if (getType() != other.getType()) {
+                return false;
+            }
+
+            final Table table1 = getTable();
+            final Table table2 = other.getTable();
+            if (table1 == null) {
+                if (table2 != null) {
+                    return false;
+                }
+            } else {
+                if (!table1.equals(table2)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+}
\ 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/schema/AbstractRelationship.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/AbstractRelationship.java b/core/src/main/java/org/apache/metamodel/schema/AbstractRelationship.java
new file mode 100644
index 0000000..c1d5646
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/AbstractRelationship.java
@@ -0,0 +1,119 @@
+/**
+ * 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.schema;
+
+import java.util.List;
+
+import org.eobjects.metamodel.util.BaseObject;
+
+public abstract class AbstractRelationship extends BaseObject implements
+		Relationship {
+    
+    private static final long serialVersionUID = 1L;
+
+	protected static Table checkSameTable(Column[] columns) {
+		if (columns == null || columns.length == 0) {
+			throw new IllegalArgumentException(
+					"At least one key-column must exist on both "
+							+ "primary and foreign side for "
+							+ "a relation to exist.");
+		}
+		Table table = null;
+		for (int i = 0; i < columns.length; i++) {
+			Column column = columns[i];
+			if (i == 0) {
+				table = column.getTable();
+			} else {
+				if (table != column.getTable()) {
+					throw new IllegalArgumentException(
+							"Key-columns did not have same table");
+				}
+			}
+		}
+		return table;
+	}
+
+	@Override
+	public Table getForeignTable() {
+		return getForeignColumns()[0].getTable();
+	}
+
+	@Override
+	public Table getPrimaryTable() {
+		return getPrimaryColumns()[0].getTable();
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("Relationship[");
+		sb.append("primaryTable=" + getPrimaryTable().getName());
+		Column[] columns = getPrimaryColumns();
+		sb.append(",primaryColumns=[");
+		for (int i = 0; i < columns.length; i++) {
+			if (i != 0) {
+				sb.append(", ");
+			}
+			sb.append(columns[i].getName());
+		}
+		sb.append("]");
+		sb.append(",foreignTable=" + getForeignTable().getName());
+		columns = getForeignColumns();
+		sb.append(",foreignColumns=[");
+		for (int i = 0; i < columns.length; i++) {
+			if (i != 0) {
+				sb.append(", ");
+			}
+			sb.append(columns[i].getName());
+		}
+		sb.append("]");
+		sb.append("]");
+		return sb.toString();
+	}
+
+	public int compareTo(Relationship that) {
+		return toString().compareTo(that.toString());
+	}
+
+	@Override
+	protected final void decorateIdentity(List<Object> identifiers) {
+		identifiers.add(getPrimaryColumns());
+		identifiers.add(getForeignColumns());
+	}
+
+	@Override
+	protected final boolean classEquals(BaseObject obj) {
+		return obj instanceof Relationship;
+	}
+
+	@Override
+	public boolean containsColumnPair(Column pkColumn, Column fkColumn) {
+		if (pkColumn != null && fkColumn != null) {
+			Column[] primaryColumns = getPrimaryColumns();
+			Column[] foreignColumns = getForeignColumns();
+			for (int i = 0; i < primaryColumns.length; i++) {
+				if (pkColumn.equals(primaryColumns[i])
+						&& fkColumn.equals(foreignColumns[i])) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+}
\ 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/schema/AbstractSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/AbstractSchema.java b/core/src/main/java/org/apache/metamodel/schema/AbstractSchema.java
new file mode 100644
index 0000000..79ccb09
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/AbstractSchema.java
@@ -0,0 +1,198 @@
+/**
+ * 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.schema;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eobjects.metamodel.util.Action;
+import org.eobjects.metamodel.util.CollectionUtils;
+import org.eobjects.metamodel.util.EqualsBuilder;
+import org.eobjects.metamodel.util.HasNameMapper;
+import org.eobjects.metamodel.util.Predicate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract implementation of the {@link Schema} interface. Implements most
+ * common and trivial methods.
+ * 
+ * @author Kasper Sørensen
+ */
+public abstract class AbstractSchema implements Schema {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final Logger logger = LoggerFactory.getLogger(AbstractSchema.class);
+
+    @Override
+    public final String getQuotedName() {
+        String quote = getQuote();
+        if (quote == null) {
+            return getName();
+        }
+        return quote + getName() + quote;
+    }
+
+    @Override
+    public Relationship[] getRelationships() {
+        final Set<Relationship> result = new LinkedHashSet<Relationship>();
+        CollectionUtils.forEach(getTables(), new Action<Table>() {
+            @Override
+            public void run(Table table) {
+                Relationship[] relations = table.getRelationships();
+                for (int i = 0; i < relations.length; i++) {
+                    Relationship relation = relations[i];
+                    result.add(relation);
+                }
+            }
+        });
+        return result.toArray(new Relationship[result.size()]);
+    }
+
+    @Override
+    public Table getTable(int index) throws IndexOutOfBoundsException {
+        Table[] tables = getTables();
+        return tables[index];
+    }
+
+    @Override
+    public final String getQualifiedLabel() {
+        return getName();
+    }
+
+    @Override
+    public final int getTableCount(TableType type) {
+        return getTables(type).length;
+    }
+
+    @Override
+    public final int getRelationshipCount() {
+        return getRelationships().length;
+    }
+
+    @Override
+    public final int getTableCount() {
+        return getTables().length;
+    }
+
+    @Override
+    public final Table[] getTables(final TableType type) {
+        return CollectionUtils.filter(getTables(), new Predicate<Table>() {
+            @Override
+            public Boolean eval(Table table) {
+                return table.getType() == type;
+            }
+        }).toArray(new Table[0]);
+    }
+
+    @Override
+    public final Table getTableByName(String tableName) {
+        if (tableName == null) {
+            return null;
+        }
+
+        final List<Table> foundTables = new ArrayList<Table>(1);
+        // Search for table matches, case insensitive.
+        for (Table table : getTables()) {
+            if (tableName.equalsIgnoreCase(table.getName())) {
+                foundTables.add(table);
+            }
+        }
+
+        final int numTables = foundTables.size();
+        if (logger.isDebugEnabled()) {
+            logger.debug("Found {} tables(s) matching '{}': {}", new Object[] { numTables, tableName, foundTables });
+        }
+
+        if (numTables == 0) {
+            return null;
+        } else if (numTables == 1) {
+            return foundTables.get(0);
+        }
+
+        // If more matches are found, search case sensitive
+        for (Table table : foundTables) {
+            if (tableName.equals(table.getName())) {
+                return table;
+            }
+        }
+
+        // if none matches case sensitive, pick the first one.
+        return foundTables.get(0);
+    }
+
+    @Override
+    public final String[] getTableNames() {
+        Table[] tables = getTables();
+        return CollectionUtils.map(tables, new HasNameMapper()).toArray(new String[tables.length]);
+    }
+
+    @Override
+    public final String toString() {
+        return "Schema[name=" + getName() + "]";
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof Schema) {
+            Schema other = (Schema) obj;
+            EqualsBuilder eb = new EqualsBuilder();
+            eb.append(getName(), other.getName());
+            eb.append(getQuote(), other.getQuote());
+            if (eb.isEquals()) {
+                try {
+                    int tableCount1 = getTableCount();
+                    int tableCount2 = other.getTableCount();
+                    eb.append(tableCount1, tableCount2);
+                } catch (Exception e) {
+                    // might occur when schemas are disconnected. Omit this check then.
+                }
+            }
+            return eb.isEquals();
+        }
+        return false;
+    }
+    
+    @Override
+    public int hashCode() {
+        String name = getName();
+        if (name == null) {
+            return -1;
+        }
+        return name.hashCode();
+    }
+
+    @Override
+    public final int compareTo(Schema that) {
+        int diff = getQualifiedLabel().compareTo(that.getQualifiedLabel());
+        if (diff == 0) {
+            diff = toString().compareTo(that.toString());
+        }
+        return diff;
+    }
+}
\ 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/schema/AbstractTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/AbstractTable.java b/core/src/main/java/org/apache/metamodel/schema/AbstractTable.java
new file mode 100644
index 0000000..3728751
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/AbstractTable.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.schema;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eobjects.metamodel.MetaModelHelper;
+import org.eobjects.metamodel.util.Action;
+import org.eobjects.metamodel.util.CollectionUtils;
+import org.eobjects.metamodel.util.HasNameMapper;
+import org.eobjects.metamodel.util.Predicate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract {@link Table} implementation. Includes most common/trivial methods.
+ * 
+ * @author Kasper Sørensen
+ */
+public abstract class AbstractTable implements Table {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final Logger logger = LoggerFactory.getLogger(AbstractTable.class);
+
+    @Override
+    public final int getColumnCount() {
+        return getColumns().length;
+    }
+
+    @Override
+    public Column getColumn(int index) throws IndexOutOfBoundsException {
+        Column[] columns = getColumns();
+        return columns[index];
+    }
+
+    @Override
+    public final Column getColumnByName(final String columnName) {
+        if (columnName == null) {
+            return null;
+        }
+
+        final List<Column> foundColumns = new ArrayList<Column>(1);
+
+        // Search for column matches, case insensitive.
+        for (Column column : getColumns()) {
+            final String candidateName = column.getName();
+            if (columnName.equalsIgnoreCase(candidateName)) {
+                foundColumns.add(column);
+            }
+        }
+
+        final int numColumns = foundColumns.size();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Found {} column(s) matching '{}': {}", new Object[] { numColumns, columnName, foundColumns });
+        }
+
+        if (numColumns == 0) {
+            return null;
+        } else if (numColumns == 1) {
+            // if there's only one, return it.
+            return foundColumns.get(0);
+        }
+
+        // If more matches are found, search case sensitive
+        for (Column column : foundColumns) {
+            if (columnName.equals(column.getName())) {
+                return column;
+            }
+        }
+
+        // if none matches case sensitive, pick the first one.
+        return foundColumns.get(0);
+    }
+
+    @Override
+    public final int getRelationshipCount() {
+        return getRelationships().length;
+    }
+
+    @Override
+    public final Column[] getNumberColumns() {
+        return CollectionUtils.filter(getColumns(), new Predicate<Column>() {
+            @Override
+            public Boolean eval(Column col) {
+                ColumnType type = col.getType();
+                return type != null && type.isNumber();
+            }
+        }).toArray(new Column[0]);
+    }
+
+    @Override
+    public final Column[] getLiteralColumns() {
+        return CollectionUtils.filter(getColumns(), new Predicate<Column>() {
+            @Override
+            public Boolean eval(Column col) {
+                ColumnType type = col.getType();
+                return type != null && type.isLiteral();
+            }
+        }).toArray(new Column[0]);
+    }
+
+    @Override
+    public final Column[] getTimeBasedColumns() {
+        return CollectionUtils.filter(getColumns(), new Predicate<Column>() {
+            @Override
+            public Boolean eval(Column col) {
+                ColumnType type = col.getType();
+                return type != null && type.isTimeBased();
+            }
+        }).toArray(new Column[0]);
+    }
+
+    @Override
+    public final Column[] getBooleanColumns() {
+        return CollectionUtils.filter(getColumns(), new Predicate<Column>() {
+            @Override
+            public Boolean eval(Column col) {
+                ColumnType type = col.getType();
+                return type != null && type.isBoolean();
+            }
+        }).toArray(new Column[0]);
+    }
+
+    @Override
+    public final Column[] getIndexedColumns() {
+        return CollectionUtils.filter(getColumns(), new Predicate<Column>() {
+            @Override
+            public Boolean eval(Column col) {
+                return col.isIndexed();
+            }
+        }).toArray(new Column[0]);
+    }
+
+    @Override
+    public final Relationship[] getForeignKeyRelationships() {
+        return CollectionUtils.filter(getRelationships(), new Predicate<Relationship>() {
+            @Override
+            public Boolean eval(Relationship arg) {
+                return AbstractTable.this.equals(arg.getForeignTable());
+            }
+        }).toArray(new Relationship[0]);
+    }
+
+    @Override
+    public final Relationship[] getPrimaryKeyRelationships() {
+        return CollectionUtils.filter(getRelationships(), new Predicate<Relationship>() {
+            @Override
+            public Boolean eval(Relationship arg) {
+                return AbstractTable.this.equals(arg.getPrimaryTable());
+            }
+        }).toArray(new Relationship[0]);
+    }
+
+    @Override
+    public final Column[] getForeignKeys() {
+        final Set<Column> columns = new HashSet<Column>();
+        final Relationship[] relationships = getForeignKeyRelationships();
+        CollectionUtils.forEach(relationships, new Action<Relationship>() {
+            @Override
+            public void run(Relationship arg) {
+                Column[] foreignColumns = arg.getForeignColumns();
+                for (Column column : foreignColumns) {
+                    columns.add(column);
+                }
+            }
+        });
+        return columns.toArray(new Column[columns.size()]);
+    }
+
+    @Override
+    public final Column[] getPrimaryKeys() {
+        final List<Column> primaryKeyColumns = new ArrayList<Column>();
+        final Column[] columnsInTable = getColumns();
+        for (Column column : columnsInTable) {
+            if (column.isPrimaryKey()) {
+                primaryKeyColumns.add(column);
+            }
+        }
+        return primaryKeyColumns.toArray(new Column[primaryKeyColumns.size()]);
+    }
+
+    @Override
+    public final String[] getColumnNames() {
+        Column[] columns = getColumns();
+        return CollectionUtils.map(columns, new HasNameMapper()).toArray(new String[columns.length]);
+    }
+
+    @Override
+    public final Column[] getColumnsOfType(ColumnType columnType) {
+        Column[] columns = getColumns();
+        return MetaModelHelper.getColumnsByType(columns, columnType);
+    }
+
+    @Override
+    public final Column[] getColumnsOfSuperType(final SuperColumnType superColumnType) {
+        Column[] columns = getColumns();
+        return MetaModelHelper.getColumnsBySuperType(columns, superColumnType);
+    }
+
+    @Override
+    public final Relationship[] getRelationships(final Table otherTable) {
+        Relationship[] relationships = getRelationships();
+
+        return CollectionUtils.filter(relationships, new Predicate<Relationship>() {
+            @Override
+            public Boolean eval(Relationship relation) {
+                if (relation.getForeignTable() == otherTable && relation.getPrimaryTable() == AbstractTable.this) {
+                    return true;
+                } else if (relation.getForeignTable() == AbstractTable.this && relation.getPrimaryTable() == otherTable) {
+                    return true;
+                }
+                return false;
+            }
+        }).toArray(new Relationship[0]);
+    }
+
+    @Override
+    public final String getQuotedName() {
+        String quote = getQuote();
+        if (quote == null) {
+            return getName();
+        }
+        return quote + getName() + quote;
+    }
+
+    @Override
+    public final String getQualifiedLabel() {
+        StringBuilder sb = new StringBuilder();
+        Schema schema = getSchema();
+        if (schema != null && schema.getName() != null) {
+            sb.append(schema.getQualifiedLabel());
+            sb.append('.');
+        }
+        sb.append(getName());
+        return sb.toString();
+    }
+
+    @Override
+    public final String toString() {
+        return "Table[name=" + getName() + ",type=" + getType() + ",remarks=" + getRemarks() + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return getName().hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof Table) {
+            final Table other = (Table) obj;
+            if (!getQualifiedLabel().equals(other.getQualifiedLabel())) {
+                return false;
+            }
+            if (getType() != other.getType()) {
+                return false;
+            }
+            final Schema sch1 = getSchema();
+            final Schema sch2 = other.getSchema();
+            if (sch1 != null) {
+                if (!sch1.equals(sch2)) {
+                    return false;
+                }
+            } else {
+                if (sch2 != null) {
+                    return false;
+                }
+            }
+
+            try {
+                final String[] columnNames1 = getColumnNames();
+                final String[] columnNames2 = other.getColumnNames();
+
+                if (columnNames1 != null && columnNames1.length != 0) {
+                    if (columnNames2 != null && columnNames2.length != 0) {
+                        if (!Arrays.equals(columnNames1, columnNames2)) {
+                            return false;
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                // going "down stream" may throw exceptions, e.g. due to
+                // de-serialization issues. We will be tolerant to such
+                // exceptions
+                logger.debug("Caught (and ignoring) exception while comparing column names of tables", e);
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public final int compareTo(Table that) {
+        int diff = getQualifiedLabel().compareTo(that.getQualifiedLabel());
+        if (diff == 0) {
+            diff = toString().compareTo(that.toString());
+        }
+        return diff;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/schema/Column.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/Column.java b/core/src/main/java/org/apache/metamodel/schema/Column.java
new file mode 100644
index 0000000..1208368
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/Column.java
@@ -0,0 +1,108 @@
+/**
+ * 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.schema;
+
+import java.io.Serializable;
+
+/**
+ * Represents a column and it's metadata description. Columns reside within a
+ * Table and can be used as keys for relationships between tables.
+ * 
+ * @see Table
+ * 
+ * @author Kasper Sørensen
+ */
+public interface Column extends Comparable<Column>, Serializable, NamedStructure {
+
+    /**
+     * Gets the name of this Column
+     * 
+     * @return the name of this Column
+     */
+    @Override
+    public String getName();
+
+    /**
+     * Returns the column number or index. Note: This column number is 0-based
+     * whereas the JDBC is 1-based.
+     * 
+     * @return the number of this column.
+     */
+    public int getColumnNumber();
+
+    /**
+     * Gets the type of the column
+     * 
+     * @return this column's type.
+     */
+    public ColumnType getType();
+
+    /**
+     * Gets the table for which this column belong
+     * 
+     * @return this column's table.
+     */
+    public Table getTable();
+
+    /**
+     * Determines whether or not this column accepts null values.
+     * 
+     * @return true if this column accepts null values, false if not and null if
+     *         not known.
+     */
+    public Boolean isNullable();
+
+    /**
+     * Gets any remarks/comments to this column.
+     * 
+     * @return any remarks/comments to this column.
+     */
+    public String getRemarks();
+
+    /**
+     * Gets the data type size of this column.
+     * 
+     * @return the data type size of this column or null if the size is not
+     *         determined or known.
+     */
+    public Integer getColumnSize();
+
+    /**
+     * Gets the native type of this column. A native type is the name of the
+     * data type as defined in the datastore.
+     * 
+     * @return the name of the native type.
+     */
+    public String getNativeType();
+
+    /**
+     * Determines if this column is indexed.
+     * 
+     * @return true if this column is indexed or false if not (or not known)
+     */
+    public boolean isIndexed();
+
+    /**
+     * Determines if this column is (one of) the primary key(s) of its table.
+     * 
+     * @return true if this column is a primary key, or false if not (or if this
+     *         is not determinable).
+     */
+    public boolean isPrimaryKey();
+}
\ 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/schema/ColumnType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/ColumnType.java b/core/src/main/java/org/apache/metamodel/schema/ColumnType.java
new file mode 100644
index 0000000..ea938e6
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/ColumnType.java
@@ -0,0 +1,292 @@
+/**
+ * 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.schema;
+
+import static org.eobjects.metamodel.schema.SuperColumnType.BINARY_TYPE;
+import static org.eobjects.metamodel.schema.SuperColumnType.BOOLEAN_TYPE;
+import static org.eobjects.metamodel.schema.SuperColumnType.LITERAL_TYPE;
+import static org.eobjects.metamodel.schema.SuperColumnType.NUMBER_TYPE;
+import static org.eobjects.metamodel.schema.SuperColumnType.OTHER_TYPE;
+import static org.eobjects.metamodel.schema.SuperColumnType.TIME_TYPE;
+
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.eobjects.metamodel.util.NumberComparator;
+import org.eobjects.metamodel.util.ObjectComparator;
+import org.eobjects.metamodel.util.TimeComparator;
+import org.eobjects.metamodel.util.ToStringComparator;
+
+/**
+ * Represents the data-type of columns. Most of the elements in this enum are
+ * based on the JDBC {@link Types} class, but with a few additions.
+ */
+public enum ColumnType {
+
+    /**
+     * Literal
+     */
+    CHAR(LITERAL_TYPE), VARCHAR(LITERAL_TYPE), LONGVARCHAR(LITERAL_TYPE), CLOB(LITERAL_TYPE), NCHAR(LITERAL_TYPE), NVARCHAR(
+            LITERAL_TYPE), LONGNVARCHAR(LITERAL_TYPE), NCLOB(LITERAL_TYPE),
+
+    /**
+     * Numbers
+     */
+    TINYINT(NUMBER_TYPE), SMALLINT(NUMBER_TYPE), INTEGER(NUMBER_TYPE), BIGINT(NUMBER_TYPE), FLOAT(NUMBER_TYPE), REAL(
+            NUMBER_TYPE), DOUBLE(NUMBER_TYPE), NUMERIC(NUMBER_TYPE), DECIMAL(NUMBER_TYPE),
+
+    /**
+     * Time based
+     */
+    DATE(TIME_TYPE), TIME(TIME_TYPE), TIMESTAMP(TIME_TYPE),
+
+    /**
+     * Booleans
+     */
+    BIT(BOOLEAN_TYPE), BOOLEAN(BOOLEAN_TYPE),
+
+    /**
+     * Binary types
+     */
+    BINARY(BINARY_TYPE), VARBINARY(BINARY_TYPE), LONGVARBINARY(BINARY_TYPE), BLOB(BINARY_TYPE),
+
+    /**
+     * Other types (as defined in {@link Types}).
+     */
+    NULL(OTHER_TYPE), OTHER(OTHER_TYPE), JAVA_OBJECT(OTHER_TYPE), DISTINCT(OTHER_TYPE), STRUCT(OTHER_TYPE), ARRAY(
+            OTHER_TYPE), REF(OTHER_TYPE), DATALINK(OTHER_TYPE), ROWID(OTHER_TYPE), SQLXML(OTHER_TYPE),
+
+    /**
+     * Additional types (added by MetaModel for non-JDBC datastores)
+     */
+    LIST(OTHER_TYPE), MAP(OTHER_TYPE);
+
+    private SuperColumnType _superType;
+
+    private ColumnType(SuperColumnType superType) {
+        if (superType == null) {
+            throw new IllegalArgumentException("SuperColumnType cannot be null");
+        }
+        _superType = superType;
+    }
+
+    public Comparator<Object> getComparator() {
+        if (isTimeBased()) {
+            return TimeComparator.getComparator();
+        }
+        if (isNumber()) {
+            return NumberComparator.getComparator();
+        }
+        if (isLiteral()) {
+            return ToStringComparator.getComparator();
+        }
+        return ObjectComparator.getComparator();
+    }
+
+    public boolean isBoolean() {
+        return _superType == BOOLEAN_TYPE;
+    }
+
+    public boolean isBinary() {
+        return _superType == BINARY_TYPE;
+    }
+
+    public boolean isNumber() {
+        return _superType == NUMBER_TYPE;
+    }
+
+    public boolean isTimeBased() {
+        return _superType == TIME_TYPE;
+    }
+
+    public boolean isLiteral() {
+        return _superType == LITERAL_TYPE;
+    }
+
+    public boolean isLargeObject() {
+        switch (this) {
+        case BLOB:
+        case CLOB:
+        case NCLOB:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    /**
+     * @return a java class that is appropriate for handling column values of
+     *         this column type
+     */
+    public Class<?> getJavaEquivalentClass() {
+        switch (this) {
+        case TINYINT:
+        case SMALLINT:
+            return Short.class;
+        case INTEGER:
+            return Integer.class;
+        case BIGINT:
+            return BigInteger.class;
+        case DECIMAL:
+        case NUMERIC:
+        case FLOAT:
+        case REAL:
+        case DOUBLE:
+            return Double.class;
+        case DATE:
+        case TIME:
+        case TIMESTAMP:
+            return Date.class;
+        case BLOB:
+            return Blob.class;
+        case CLOB:
+        case NCLOB:
+            return Clob.class;
+        case MAP:
+            return Map.class;
+        case LIST:
+            return List.class;
+        default:
+            // All other types have fitting java equivalent classes in the super
+            // type
+            return _superType.getJavaEquivalentClass();
+        }
+    }
+
+    public SuperColumnType getSuperType() {
+        return _superType;
+    }
+
+    /**
+     * Finds the ColumnType enum corresponding to the incoming JDBC
+     * type-constant
+     */
+    public static ColumnType convertColumnType(int jdbcType) {
+        try {
+            Field[] fields = JdbcTypes.class.getFields();
+            // We assume that the JdbcTypes class only consists of constant
+            // integer types, so we make no assertions here
+            for (int i = 0; i < fields.length; i++) {
+                Field field = fields[i];
+                int value = (Integer) field.getInt(null);
+                if (value == jdbcType) {
+                    String fieldName = field.getName();
+                    ColumnType[] enumConstants = ColumnType.class.getEnumConstants();
+                    for (int j = 0; j < enumConstants.length; j++) {
+                        ColumnType columnType = enumConstants[j];
+                        if (fieldName.equals(columnType.toString())) {
+                            return columnType;
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            throw new IllegalStateException("Could not access fields in JdbcTypes", e);
+        }
+        return OTHER;
+    }
+
+    /**
+     * Gets the JDBC type as per the {@link Types} class.
+     * 
+     * @return an int representing one of the constants in the {@link Types}
+     *         class.
+     * @throws IllegalStateException
+     *             in case getting the JDBC type was unsuccesful.
+     */
+    public int getJdbcType() throws IllegalStateException {
+        final String name = this.toString();
+        try {
+            // We assume that the JdbcTypes class only consists of constant
+            // integer types, so we make no assertions here
+            final Field[] fields = JdbcTypes.class.getFields();
+            for (int i = 0; i < fields.length; i++) {
+                Field field = fields[i];
+                String fieldName = field.getName();
+                if (fieldName.equals(name)) {
+                    int value = (Integer) field.getInt(null);
+                    return value;
+                }
+            }
+            throw new IllegalStateException("No JdbcType found with field name: " + name);
+        } catch (Exception e) {
+            throw new IllegalStateException("Could not access fields in JdbcTypes", e);
+        }
+    }
+
+    /**
+     * Finds the ColumnType enum corresponding to the incoming Java class.
+     * 
+     * @param cls
+     * @return
+     */
+    public static ColumnType convertColumnType(Class<?> cls) {
+        if (cls == null) {
+            throw new IllegalArgumentException("Class cannot be null");
+        }
+
+        final ColumnType type;
+        if (cls == String.class) {
+            type = ColumnType.VARCHAR;
+        } else if (cls == Boolean.class || cls == boolean.class) {
+            type = ColumnType.BOOLEAN;
+        } else if (cls == Character.class || cls == char.class || cls == Character[].class || cls == char[].class) {
+            type = ColumnType.CHAR;
+        } else if (cls == Byte.class || cls == byte.class) {
+            type = ColumnType.TINYINT;
+        } else if (cls == Short.class || cls == short.class) {
+            type = ColumnType.SMALLINT;
+        } else if (cls == Integer.class || cls == int.class) {
+            type = ColumnType.INTEGER;
+        } else if (cls == Long.class || cls == long.class || cls == BigInteger.class) {
+            type = ColumnType.BIGINT;
+        } else if (cls == Float.class || cls == float.class) {
+            type = ColumnType.FLOAT;
+        } else if (cls == Double.class || cls == double.class) {
+            type = ColumnType.DOUBLE;
+        } else if (cls == BigDecimal.class) {
+            type = ColumnType.DECIMAL;
+        } else if (Map.class.isAssignableFrom(cls)) {
+            type = ColumnType.MAP;
+        } else if (List.class.isAssignableFrom(cls)) {
+            type = ColumnType.LIST;
+        } else if (cls == java.sql.Date.class) {
+            type = ColumnType.DATE;
+        } else if (cls == Timestamp.class) {
+            type = ColumnType.TIMESTAMP;
+        } else if (cls == Time.class) {
+            type = ColumnType.TIME;
+        } else if (Date.class.isAssignableFrom(cls)) {
+            type = ColumnType.TIMESTAMP;
+        } else {
+            type = ColumnType.OTHER;
+        }
+        return type;
+    }
+}
\ 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/schema/CompositeSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/CompositeSchema.java b/core/src/main/java/org/apache/metamodel/schema/CompositeSchema.java
new file mode 100644
index 0000000..f4f75e1
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/CompositeSchema.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.schema;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eobjects.metamodel.DataContext;
+import org.eobjects.metamodel.util.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A composite schema, comprising tables from several {@link DataContext}s.
+ * 
+ * @author Kasper Sørensen
+ */
+public class CompositeSchema extends AbstractSchema {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final Logger logger = LoggerFactory.getLogger(CompositeSchema.class);
+
+    private final String name;
+    private final Collection<? extends Schema> delegates;
+
+    public CompositeSchema(String name, Collection<? extends Schema> delegates) {
+        super();
+        this.name = name;
+        this.delegates = delegates;
+        if (logger.isWarnEnabled()) {
+            Set<String> names = new HashSet<String>();
+            for (Table table : getTables()) {
+                if (names.contains(table.getName())) {
+                    logger.warn("Name-clash detected for Table {}.", table.getName());
+                    logger.warn("getTableByName(\"{}\") will return just the first table.", table.getName());
+                } else {
+                    names.add(table.getName());
+                }
+            }
+            if (!names.isEmpty()) {
+                logger.warn("The following table names clashes in composite schema: " + names);
+            }
+        }
+    }
+
+    @Override
+    public Relationship[] getRelationships() {
+        Relationship[] result = new Relationship[0];
+        for (Schema delegate : delegates) {
+            result = CollectionUtils.array(result, delegate.getRelationships());
+        }
+        return result;
+    }
+
+    @Override
+    public Table[] getTables() {
+        Table[] result = new Table[0];
+        for (Schema delegate : delegates) {
+            result = CollectionUtils.array(result, delegate.getTables());
+        }
+        return result;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getQuote() {
+        return null;
+    }
+}
\ 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/schema/ImmutableColumn.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/ImmutableColumn.java b/core/src/main/java/org/apache/metamodel/schema/ImmutableColumn.java
new file mode 100644
index 0000000..3236c00
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/ImmutableColumn.java
@@ -0,0 +1,173 @@
+/**
+ * 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.schema;
+
+import java.io.Serializable;
+
+/**
+ * Immutable implementation of the Column interface.
+ * 
+ * It is not intended to be instantiated on it's own. Rather, use the
+ * constructor in ImmutableSchema.
+ * 
+ * @see ImmutableSchema
+ * 
+ * @author Kasper Sørensen
+ */
+public final class ImmutableColumn extends AbstractColumn implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private final int columnNumber;
+    private final ColumnType type;
+    private final Table table;
+    private final Boolean nullable;
+    private final String remarks;
+    private final Integer columnSize;
+    private final String nativeType;
+    private final boolean indexed;
+    private final boolean primaryKey;
+    private final String name;
+    private final String quote;
+
+    /**
+     * Constructs a new {@link ImmutableColumn}.
+     * 
+     * @param name
+     *            the name of the column
+     * @param type
+     *            the type of the column
+     * @param table
+     *            the table which the constructed column will pertain to
+     * @param columnNumber
+     *            the column number of the column
+     * @param columnSize
+     *            the size of the column
+     * @param nativeType
+     *            the native type of the column
+     * @param nullable
+     *            whether the column's values are nullable
+     * @param remarks
+     *            the remarks of the column
+     * @param indexed
+     *            whether the column is indexed or not
+     * @param quote
+     *            the quote character(s) of the column
+     * @param primaryKey
+     *            whether the column is a primary key or not
+     */
+    public ImmutableColumn(String name, ColumnType type, Table table, int columnNumber, Integer columnSize,
+            String nativeType, Boolean nullable, String remarks, boolean indexed, String quote, boolean primaryKey) {
+        this.name = name;
+        this.type = type;
+        this.table = table;
+        this.columnNumber = columnNumber;
+        this.columnSize = columnSize;
+        this.nativeType = nativeType;
+        this.nullable = nullable;
+        this.remarks = remarks;
+        this.indexed = indexed;
+        this.quote = quote;
+        this.primaryKey = primaryKey;
+    }
+
+    /**
+     * Constructs an {@link ImmutableColumn} based on an existing column and a
+     * table.
+     * 
+     * @param column
+     *            the column to immitate
+     * @param table
+     *            the table that the constructed column will pertain to
+     */
+    public ImmutableColumn(Column column, Table table) {
+        this.name = column.getName();
+        this.type = column.getType();
+        this.table = table;
+        this.columnNumber = column.getColumnNumber();
+        this.columnSize = column.getColumnSize();
+        this.nativeType = column.getNativeType();
+        this.nullable = column.isNullable();
+        this.remarks = column.getRemarks();
+        this.indexed = column.isIndexed();
+        this.quote = column.getQuote();
+        this.primaryKey = column.isPrimaryKey();
+    }
+
+    protected ImmutableColumn(Column column, ImmutableTable table) {
+        this(column.getName(), column.getType(), table, column.getColumnNumber(), column.getColumnSize(), column
+                .getNativeType(), column.isNullable(), column.getRemarks(), column.isIndexed(), column.getQuote(),
+                column.isPrimaryKey());
+    }
+
+    @Override
+    public int getColumnNumber() {
+        return columnNumber;
+    }
+
+    @Override
+    public ColumnType getType() {
+        return type;
+    }
+
+    @Override
+    public Table getTable() {
+        return table;
+    }
+
+    @Override
+    public Boolean isNullable() {
+        return nullable;
+    }
+
+    @Override
+    public String getRemarks() {
+        return remarks;
+    }
+
+    @Override
+    public Integer getColumnSize() {
+        return columnSize;
+    }
+
+    @Override
+    public String getNativeType() {
+        return nativeType;
+    }
+
+    @Override
+    public boolean isIndexed() {
+        return indexed;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean isPrimaryKey() {
+        return primaryKey;
+    }
+
+    @Override
+    public String getQuote() {
+        return quote;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/schema/ImmutableRelationship.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/ImmutableRelationship.java b/core/src/main/java/org/apache/metamodel/schema/ImmutableRelationship.java
new file mode 100644
index 0000000..43f2cf0
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/ImmutableRelationship.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.schema;
+
+import java.io.Serializable;
+
+public final class ImmutableRelationship extends AbstractRelationship implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private final Column[] primaryColumns;
+	private final Column[] foreignColumns;
+
+	public static void create(Relationship origRelationship,
+			ImmutableSchema schema) {
+		ImmutableTable primaryTable = getSimilarTable(
+				origRelationship.getPrimaryTable(), schema);
+		assert primaryTable != null;
+		Column[] primaryColumns = getSimilarColumns(
+				origRelationship.getPrimaryColumns(), primaryTable);
+		checkSameTable(primaryColumns);
+
+		ImmutableTable foreignTable = getSimilarTable(
+				origRelationship.getForeignTable(), schema);
+		assert foreignTable != null;
+		Column[] foreignColumns = getSimilarColumns(
+				origRelationship.getForeignColumns(), foreignTable);
+		checkSameTable(foreignColumns);
+
+		ImmutableRelationship relationship = new ImmutableRelationship(
+				primaryColumns, foreignColumns);
+		primaryTable.addRelationship(relationship);
+		foreignTable.addRelationship(relationship);
+	}
+
+	private static Column[] getSimilarColumns(Column[] columns, Table table) {
+		Column[] result = new Column[columns.length];
+		for (int i = 0; i < columns.length; i++) {
+			String name = columns[i].getName();
+			result[i] = table.getColumnByName(name);
+		}
+		return result;
+	}
+
+	private static ImmutableTable getSimilarTable(Table table,
+			ImmutableSchema schema) {
+		String name = table.getName();
+		return (ImmutableTable) schema.getTableByName(name);
+	}
+
+	private ImmutableRelationship(Column[] primaryColumns,
+			Column[] foreignColumns) {
+		this.primaryColumns = primaryColumns;
+		this.foreignColumns = foreignColumns;
+	}
+
+	@Override
+	public Column[] getPrimaryColumns() {
+		return primaryColumns;
+	}
+
+	@Override
+	public Column[] getForeignColumns() {
+		return foreignColumns;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/schema/ImmutableSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/ImmutableSchema.java b/core/src/main/java/org/apache/metamodel/schema/ImmutableSchema.java
new file mode 100644
index 0000000..8ac8a79
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/ImmutableSchema.java
@@ -0,0 +1,72 @@
+/**
+ * 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.schema;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An immutable implementation of the {@link Schema} interface.
+ * 
+ * @author Kasper Sørensen
+ */
+public final class ImmutableSchema extends AbstractSchema implements
+		Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private final List<ImmutableTable> tables = new ArrayList<ImmutableTable>();
+	private String name;
+	private String quote;
+
+	private ImmutableSchema(String name, String quote) {
+		super();
+		this.name = name;
+		this.quote = quote;
+	}
+
+	public ImmutableSchema(Schema schema) {
+		this(schema.getName(), schema.getQuote());
+		Table[] origTables = schema.getTables();
+		for (Table table : origTables) {
+			tables.add(new ImmutableTable(table, this));
+		}
+
+		Relationship[] origRelationships = schema.getRelationships();
+		for (Relationship relationship : origRelationships) {
+			ImmutableRelationship.create(relationship, this);
+		}
+	}
+
+	@Override
+	public Table[] getTables() {
+		return tables.toArray(new Table[tables.size()]);
+	}
+
+	@Override
+	public String getName() {
+		return name;
+	}
+
+	@Override
+	public String getQuote() {
+		return quote;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/schema/ImmutableTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/ImmutableTable.java b/core/src/main/java/org/apache/metamodel/schema/ImmutableTable.java
new file mode 100644
index 0000000..a4d6d81
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/ImmutableTable.java
@@ -0,0 +1,106 @@
+/**
+ * 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.schema;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An immutable implementation of the Table interface.
+ * 
+ * It is not intended to be instantiated on it's own. Rather, use the
+ * constructor in ImmutableSchema.
+ * 
+ * @see ImmutableSchema
+ * 
+ * @author Kasper Sørensen
+ */
+final class ImmutableTable extends AbstractTable implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private final List<ImmutableColumn> columns = new ArrayList<ImmutableColumn>();
+	private final List<ImmutableRelationship> relationships = new ArrayList<ImmutableRelationship>();
+	private final ImmutableSchema schema;
+	private final TableType type;
+	private final String remarks;
+	private final String name;
+	private final String quote;
+
+	protected ImmutableTable(String name, TableType type, ImmutableSchema schema,
+			String remarks, String quote) {
+		this.name = name;
+		this.type = type;
+		this.schema = schema;
+		this.remarks = remarks;
+		this.quote = quote;
+	}
+
+	protected ImmutableTable(Table table, ImmutableSchema schema) {
+		this(table.getName(), table.getType(), schema, table.getRemarks(),
+				table.getQuote());
+		Column[] origColumns = table.getColumns();
+		for (Column column : origColumns) {
+			columns.add(new ImmutableColumn(column, this));
+		}
+	}
+
+	protected void addRelationship(ImmutableRelationship relationship) {
+		if (!relationships.contains(relationship)) {
+			relationships.add(relationship);
+		}
+	}
+
+	@Override
+	public Column[] getColumns() {
+		return columns.toArray(new Column[columns.size()]);
+	}
+
+	@Override
+	public Schema getSchema() {
+		return schema;
+	}
+
+	@Override
+	public TableType getType() {
+		return type;
+	}
+
+	@Override
+	public Relationship[] getRelationships() {
+		return relationships.toArray(new Relationship[relationships.size()]);
+	}
+
+	@Override
+	public String getRemarks() {
+		return remarks;
+	}
+
+	@Override
+	public String getName() {
+		return name;
+	}
+
+	@Override
+	public String getQuote() {
+		return quote;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/schema/JdbcTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/JdbcTypes.java b/core/src/main/java/org/apache/metamodel/schema/JdbcTypes.java
new file mode 100644
index 0000000..4531921
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/JdbcTypes.java
@@ -0,0 +1,69 @@
+/**
+ * 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.schema;
+
+/**
+ * This is a copy of the content (comments removed) of Java 6.0's
+ * java.sql.Types. It is backwards compatible with older versions, but have
+ * additional types (confirmed by JavaTypesTest). It is being used to convert
+ * JDBC types to ColumnType enumerations.
+ */
+final class JdbcTypes {
+
+	// Prevent instantiation
+	private JdbcTypes() {
+	}
+
+	public final static int BIT = -7;
+	public final static int TINYINT = -6;
+	public final static int SMALLINT = 5;
+	public final static int INTEGER = 4;
+	public final static int BIGINT = -5;
+	public final static int FLOAT = 6;
+	public final static int REAL = 7;
+	public final static int DOUBLE = 8;
+	public final static int NUMERIC = 2;
+	public final static int DECIMAL = 3;
+	public final static int CHAR = 1;
+	public final static int VARCHAR = 12;
+	public final static int LONGVARCHAR = -1;
+	public final static int DATE = 91;
+	public final static int TIME = 92;
+	public final static int TIMESTAMP = 93;
+	public final static int BINARY = -2;
+	public final static int VARBINARY = -3;
+	public final static int LONGVARBINARY = -4;
+	public final static int NULL = 0;
+	public final static int OTHER = 1111;
+	public final static int JAVA_OBJECT = 2000;
+	public final static int DISTINCT = 2001;
+	public final static int STRUCT = 2002;
+	public final static int ARRAY = 2003;
+	public final static int BLOB = 2004;
+	public final static int CLOB = 2005;
+	public final static int REF = 2006;
+	public final static int DATALINK = 70;
+	public final static int BOOLEAN = 16;
+	public final static int ROWID = -8;
+	public static final int NCHAR = -15;
+	public static final int NVARCHAR = -9;
+	public static final int LONGNVARCHAR = -16;
+	public static final int NCLOB = 2011;
+	public static final int SQLXML = 2009;
+}
\ 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/schema/MutableColumn.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/MutableColumn.java b/core/src/main/java/org/apache/metamodel/schema/MutableColumn.java
new file mode 100644
index 0000000..d4c464d
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/MutableColumn.java
@@ -0,0 +1,185 @@
+/**
+ * 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.schema;
+
+import java.io.Serializable;
+
+/**
+ * Represents a column and it's metadata description. Columns reside within a
+ * Table and can be used as keys for relationships between tables.
+ * 
+ * @see MutableTable
+ * @see Relationship
+ */
+public class MutableColumn extends AbstractColumn implements Serializable {
+
+    private static final long serialVersionUID = -353183696233890927L;
+    private int _columnNumber;
+    private String _name;
+    private ColumnType _type;
+    private Table _table;
+    private Boolean _nullable = null;
+    private String _remarks;
+    private boolean _indexed = false;
+    private boolean _primaryKey = false;
+    private Integer _columnSize = null;
+    private String _nativeType = null;
+    private String _quoteString = null;
+
+    public MutableColumn() {
+        super();
+    }
+
+    public MutableColumn(String name) {
+        this();
+        setName(name);
+    }
+
+    public MutableColumn(String name, ColumnType type) {
+        this(name);
+        setType(type);
+    }
+
+    public MutableColumn(String name, ColumnType type, Table table, int columnNumber, Boolean nullable) {
+        this(name, type);
+        setColumnNumber(columnNumber);
+        setTable(table);
+        setNullable(nullable);
+    }
+
+    public MutableColumn(String name, ColumnType type, Table table, int columnNumber, Integer columnSize,
+            String nativeType, Boolean nullable, String remarks, boolean indexed, String quote) {
+        this(name, type, table, columnNumber, nullable);
+        setColumnSize(columnSize);
+        setNativeType(nativeType);
+        setRemarks(remarks);
+        setIndexed(indexed);
+        setQuote(quote);
+    }
+
+    @Override
+    public int getColumnNumber() {
+        return _columnNumber;
+    }
+
+    public MutableColumn setColumnNumber(int columnNumber) {
+        _columnNumber = columnNumber;
+        return this;
+    }
+
+    @Override
+    public String getName() {
+        return _name;
+    }
+
+    public MutableColumn setName(String name) {
+        _name = name;
+        return this;
+    }
+
+    @Override
+    public ColumnType getType() {
+        return _type;
+    }
+
+    public MutableColumn setType(ColumnType type) {
+        _type = type;
+        return this;
+    }
+
+    @Override
+    public Table getTable() {
+        return _table;
+    }
+
+    public MutableColumn setTable(Table table) {
+        _table = table;
+        return this;
+    }
+
+    @Override
+    public Boolean isNullable() {
+        return _nullable;
+    }
+
+    public MutableColumn setNullable(Boolean nullable) {
+        _nullable = nullable;
+        return this;
+    }
+
+    @Override
+    public String getRemarks() {
+        return _remarks;
+    }
+
+    public MutableColumn setRemarks(String remarks) {
+        _remarks = remarks;
+        return this;
+    }
+
+    @Override
+    public Integer getColumnSize() {
+        return _columnSize;
+    }
+
+    public MutableColumn setColumnSize(Integer columnSize) {
+        _columnSize = columnSize;
+        return this;
+    }
+
+    @Override
+    public String getNativeType() {
+        return _nativeType;
+    }
+
+    public MutableColumn setNativeType(String nativeType) {
+        _nativeType = nativeType;
+        return this;
+    }
+
+    @Override
+    public boolean isIndexed() {
+        return _indexed;
+    }
+
+    public MutableColumn setIndexed(boolean indexed) {
+        _indexed = indexed;
+        return this;
+    }
+
+    @Override
+    public String getQuote() {
+        return _quoteString;
+    }
+
+    public MutableColumn setQuote(String quoteString) {
+        _quoteString = quoteString;
+        return this;
+    }
+
+    @Override
+    public boolean isPrimaryKey() {
+        return _primaryKey;
+    }
+
+    public MutableColumn setPrimaryKey(boolean primaryKey) {
+        _primaryKey = primaryKey;
+        return this;
+    }
+}
\ 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/schema/MutableRelationship.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/MutableRelationship.java b/core/src/main/java/org/apache/metamodel/schema/MutableRelationship.java
new file mode 100644
index 0000000..8762222
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/MutableRelationship.java
@@ -0,0 +1,132 @@
+/**
+ * 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.schema;
+
+import java.io.Serializable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Immutable implementation of the Relationship interface.
+ * 
+ * The immutability help ensure integrity of object-relationships. To create
+ * relationsips use the <code>createRelationship</code> method.
+ * 
+ * @author Kasper Sørensen
+ */
+public class MutableRelationship extends AbstractRelationship implements
+		Serializable, Relationship {
+
+	private static final long serialVersionUID = 238786848828528822L;
+	private static final Logger logger = LoggerFactory
+			.getLogger(MutableRelationship.class);
+
+	private final Column[] _primaryColumns;
+	private final Column[] _foreignColumns;
+
+	/**
+	 * Factory method to create relations between two tables by specifying which
+	 * columns from the tables that enforce the relationship.
+	 * 
+	 * @param primaryColumns
+	 *            the columns from the primary key table
+	 * @param foreignColumns
+	 *            the columns from the foreign key table
+	 * @return the relation created
+	 */
+	public static Relationship createRelationship(Column[] primaryColumns,
+			Column[] foreignColumns) {
+		Table primaryTable = checkSameTable(primaryColumns);
+		Table foreignTable = checkSameTable(foreignColumns);
+		MutableRelationship relation = new MutableRelationship(primaryColumns,
+				foreignColumns);
+
+		if (primaryTable instanceof MutableTable) {
+			try {
+				((MutableTable) primaryTable).addRelationship(relation);
+			} catch (UnsupportedOperationException e) {
+				// this is an allowed behaviour - not all tables need to support
+				// this method.
+				logger.debug(
+						"primary table ({}) threw exception when adding relationship",
+						primaryTable);
+			}
+
+			// Ticket #144: Some tables have relations with them selves and then
+			// the
+			// relationship should only be added once.
+			if (foreignTable != primaryTable
+					&& foreignTable instanceof MutableTable) {
+				try {
+					((MutableTable) foreignTable).addRelationship(relation);
+				} catch (UnsupportedOperationException e) {
+					// this is an allowed behaviour - not all tables need to
+					// support this method.
+					logger.debug(
+							"foreign table ({}) threw exception when adding relationship",
+							foreignTable);
+				}
+			}
+		}
+		return relation;
+	}
+
+	public void remove() {
+		Table primaryTable = getPrimaryTable();
+		if (primaryTable instanceof MutableTable) {
+			((MutableTable) primaryTable).removeRelationship(this);
+		}
+		Table foreignTable = getForeignTable();
+		if (foreignTable instanceof MutableTable) {
+			((MutableTable) foreignTable).removeRelationship(this);
+		}
+	}
+
+	@Override
+	protected void finalize() throws Throwable {
+		super.finalize();
+		remove();
+	}
+
+	public static Relationship createRelationship(Column primaryColumn,
+			Column foreignColumn) {
+		return createRelationship(new Column[] { primaryColumn },
+				new Column[] { foreignColumn });
+	}
+
+	/**
+	 * Prevent external instantiation
+	 */
+	private MutableRelationship(Column[] primaryColumns, Column[] foreignColumns) {
+		_primaryColumns = primaryColumns;
+		_foreignColumns = foreignColumns;
+	}
+
+	@Override
+	public Column[] getPrimaryColumns() {
+		return _primaryColumns;
+	}
+
+	@Override
+	public Column[] getForeignColumns() {
+		return _foreignColumns;
+	}
+
+}
\ 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/schema/MutableSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/MutableSchema.java b/core/src/main/java/org/apache/metamodel/schema/MutableSchema.java
new file mode 100644
index 0000000..71d05d2
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/schema/MutableSchema.java
@@ -0,0 +1,106 @@
+/**
+ * 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.schema;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Represents a schema and it's metadata. Schemas represent a collection of
+ * tables.
+ * 
+ * @see Table
+ */
+public class MutableSchema extends AbstractSchema implements Serializable,
+		Schema {
+
+	private static final long serialVersionUID = 4465197783868238863L;
+
+	private String _name;
+	private final List<MutableTable> _tables;
+
+	public MutableSchema() {
+		super();
+		_tables = new ArrayList<MutableTable>();
+	}
+
+	public MutableSchema(String name) {
+		this();
+		_name = name;
+	}
+
+	public MutableSchema(String name, MutableTable... tables) {
+		this(name);
+		setTables(tables);
+	}
+
+	@Override
+	public String getName() {
+		return _name;
+	}
+
+	public MutableSchema setName(String name) {
+		_name = name;
+		return this;
+	}
+
+	@Override
+	public MutableTable[] getTables() {
+		MutableTable[] array = new MutableTable[_tables.size()];
+		return _tables.toArray(array);
+	}
+
+	public MutableSchema setTables(Collection<? extends MutableTable> tables) {
+	    clearTables();
+		for (MutableTable table : tables) {
+			_tables.add(table);
+		}
+		return this;
+	}
+
+	public MutableSchema setTables(MutableTable... tables) {
+	    clearTables();
+		for (MutableTable table : tables) {
+			_tables.add(table);
+		}
+		return this;
+	}
+	
+	public MutableSchema clearTables() {
+	    _tables.clear();
+	    return this;
+	}
+
+	public MutableSchema addTable(MutableTable table) {
+		_tables.add(table);
+		return this;
+	}
+
+	public MutableSchema removeTable(Table table) {
+		_tables.remove(table);
+		return this;
+	}
+
+	@Override
+	public String getQuote() {
+		return null;
+	}
+}
\ No newline at end of file