The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at http://www.eclipse.org/legal/epl-v10.html.
+For purposes of the EPL, "Program" will mean the Content.
+
+
If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at http://www.eclipse.org.
+
+
+
diff --git a/org.eclipse.emf.cdo.server.db/about.ini b/org.eclipse.emf.cdo.server.db/about.ini
new file mode 100644
index 0000000000..32006ae5d6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/about.ini
@@ -0,0 +1,15 @@
+# about.ini
+# contains information about a feature
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# "%key" are externalized strings defined in about.properties
+# This file does not need to be translated.
+
+# Property "aboutText" contains blurb for "About" dialog (translated)
+aboutText=%featureText
+
+# Property "featureImage" contains path to feature image (32x32)
+featureImage=modeling32.png
+
+# Property "appName" contains name of the application (translated)
+appName=%featureName
+
diff --git a/org.eclipse.emf.cdo.server.db/about.mappings b/org.eclipse.emf.cdo.server.db/about.mappings
new file mode 100644
index 0000000000..bddaab4310
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/about.mappings
@@ -0,0 +1,6 @@
+# about.mappings
+# contains fill-ins for about.properties
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# This file does not need to be translated.
+
+0=@build@
\ No newline at end of file
diff --git a/org.eclipse.emf.cdo.server.db/about.properties b/org.eclipse.emf.cdo.server.db/about.properties
new file mode 100644
index 0000000000..949ccadf4e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/about.properties
@@ -0,0 +1,31 @@
+# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Eike Stepper - initial API and implementation
+
+# NLS_MESSAGEFORMAT_VAR
+
+# ==============================================================================
+# Do not change the properties between this line and the last line containing:
+# %%% END OF TRANSLATED PROPERTIES %%%
+# Instead, either redefine an existing property, or create a new property,
+# append it to the end of the file, and change the code to use the new name.
+# ==============================================================================
+
+featureName = CDO Model Repository Server DB
+featureText = CDO Model Repository Server DB\n\
+Version: {featureVersion}\n\
+Build id: {0}\n\
+\n\
+Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. All rights reserved.\n\
+\n\
+Visit http://www.eclipse.org/cdo
+
+# ==============================================================================
+# %%% END OF TRANSLATED PROPERTIES %%%
+# The above properties have been shipped for translation.
+# ==============================================================================
diff --git a/org.eclipse.emf.cdo.server.db/build.properties b/org.eclipse.emf.cdo.server.db/build.properties
new file mode 100644
index 0000000000..7962001d93
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/build.properties
@@ -0,0 +1,29 @@
+# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Eike Stepper - initial API and implementation
+
+# NLS_MESSAGEFORMAT_VAR
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ .options,\
+ about.html,\
+ copyright.txt,\
+ plugin.xml,\
+ schema/,\
+ plugin.properties,\
+ about.ini,\
+ about.mappings,\
+ about.properties,\
+ modeling32.png
+src.includes = about.html,\
+ copyright.txt
+
+org.eclipse.emf.cdo.releng.javadoc.project = org.eclipse.emf.cdo.doc
diff --git a/org.eclipse.emf.cdo.server.db/copyright.txt b/org.eclipse.emf.cdo.server.db/copyright.txt
new file mode 100644
index 0000000000..e921242cf0
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/copyright.txt
@@ -0,0 +1,8 @@
+Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/epl-v10.html
+
+Contributors:
+ Eike Stepper - initial API and implementation
\ No newline at end of file
diff --git a/org.eclipse.emf.cdo.server.db/modeling32.png b/org.eclipse.emf.cdo.server.db/modeling32.png
new file mode 100644
index 0000000000..6b08de2ada
Binary files /dev/null and b/org.eclipse.emf.cdo.server.db/modeling32.png differ
diff --git a/org.eclipse.emf.cdo.server.db/plugin.properties b/org.eclipse.emf.cdo.server.db/plugin.properties
new file mode 100644
index 0000000000..ade0c88bdc
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/plugin.properties
@@ -0,0 +1,13 @@
+# Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Eike Stepper - initial API and implementation
+
+pluginName = CDO Model Repository Server DB
+providerName = Eclipse Modeling Project
+
+extension-point.name = CDO Mapping Strategies
\ No newline at end of file
diff --git a/org.eclipse.emf.cdo.server.db/plugin.xml b/org.eclipse.emf.cdo.server.db/plugin.xml
new file mode 100644
index 0000000000..6abb1a84a7
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/plugin.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.emf.cdo.server.db/schema/mappingStrategies.exsd b/org.eclipse.emf.cdo.server.db/schema/mappingStrategies.exsd
new file mode 100644
index 0000000000..c708d5537b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/schema/mappingStrategies.exsd
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+ [Enter description of this extension point.]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Enter the first release in which this extension point appears.]
+
+
+
+
+
+
+
+
+ [Enter extension point usage example here.]
+
+
+
+
+
+
+
+
+ [Enter API information here.]
+
+
+
+
+
+
+
+
+ [Enter information about supplied implementation of this extension point.]
+
+
+
+
+
+
+
+
+ Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.<br>
+All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
+
+
+
+
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java
new file mode 100644
index 0000000000..b5cb29a567
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/CDODBUtil.java
@@ -0,0 +1,202 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings
+ * Stefan Winkler - 249610: [DB] Support external references (Implementation)
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.DBBrowserPage;
+import org.eclipse.emf.cdo.server.internal.db.DBStore;
+import org.eclipse.emf.cdo.server.internal.db.SmartPreparedStatementCache;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalAuditMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalAuditMappingStrategyWithRanges;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalBranchingMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalBranchingMappingStrategyWithRanges;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.HorizontalNonAuditMappingStrategy;
+
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.db.IDBConnectionProvider;
+import org.eclipse.net4j.util.ObjectUtil;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.container.IManagedContainer;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * @author Eike Stepper
+ */
+public final class CDODBUtil
+{
+ /**
+ * @since 2.0
+ */
+ public static final int DEFAULT_STATEMENT_CACHE_CAPACITY = 200;
+
+ /**
+ * @since 2.0
+ */
+ public static final String EXT_POINT_MAPPING_STRATEGIES = "mappingStrategies"; //$NON-NLS-1$
+
+ /**
+ * @since 4.1
+ */
+ public static final String PROP_WITH_RANGES = "withRanges";
+
+ /**
+ * @since 4.1
+ */
+ public static final String PROP_COPY_ON_BRANCH = "copyOnBranch";
+
+ private CDODBUtil()
+ {
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static void prepareContainer(IManagedContainer container)
+ {
+ container.registerFactory(new DBBrowserPage.Factory());
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static IDBStore createStore(IMappingStrategy mappingStrategy, IDBAdapter dbAdapter,
+ IDBConnectionProvider dbConnectionProvider)
+ {
+ DBStore store = new DBStore();
+ store.setMappingStrategy(mappingStrategy);
+ store.setDBAdapter(dbAdapter);
+ store.setDbConnectionProvider(dbConnectionProvider);
+ return store;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static IMappingStrategy createHorizontalMappingStrategy(boolean auditing)
+ {
+ return createHorizontalMappingStrategy(auditing, false, false);
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static IMappingStrategy createHorizontalMappingStrategy(boolean auditing, boolean branching)
+ {
+ return createHorizontalMappingStrategy(auditing, branching, false);
+ }
+
+ /**
+ * @since 4.1
+ */
+ public static IMappingStrategy createHorizontalMappingStrategy(boolean auditing, boolean branching, boolean withRanges)
+ {
+ if (branching)
+ {
+ if (auditing)
+ {
+ if (withRanges)
+ {
+ return new HorizontalBranchingMappingStrategyWithRanges();
+ }
+
+ return new HorizontalBranchingMappingStrategy();
+ }
+
+ throw new IllegalArgumentException("Misconfiguration: Branching requires Auditing!");
+ }
+
+ if (auditing)
+ {
+ if (withRanges)
+ {
+ return new HorizontalAuditMappingStrategyWithRanges();
+ }
+
+ return new HorizontalAuditMappingStrategy();
+ }
+
+ return new HorizontalNonAuditMappingStrategy();
+ }
+
+ /**
+ * Creates a horizontal {@link IMappingStrategy mapping strategy} that supports all valid combinations of auditing and
+ * branching.
+ *
+ * @since 4.1
+ */
+ public static IMappingStrategy createHorizontalMappingStrategy()
+ {
+ return new HorizontalMappingStrategy();
+ }
+
+ /**
+ * Can only be used when Eclipse is running. In standalone scenarios create the mapping strategy instance by directly
+ * calling the constructor of the mapping strategy class.
+ *
+ * @see #createHorizontalMappingStrategy(boolean)
+ * @see #createHorizontalMappingStrategy(boolean, boolean)
+ * @since 2.0
+ */
+ public static IMappingStrategy createMappingStrategy(String type)
+ {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IConfigurationElement[] elements = registry.getConfigurationElementsFor(OM.BUNDLE_ID, EXT_POINT_MAPPING_STRATEGIES);
+ for (final IConfigurationElement element : elements)
+ {
+ if ("mappingStrategy".equals(element.getName())) //$NON-NLS-1$
+ {
+ String typeAttr = element.getAttribute("type"); //$NON-NLS-1$
+ if (ObjectUtil.equals(typeAttr, type))
+ {
+ try
+ {
+ return (IMappingStrategy)element.createExecutableExtension("class"); //$NON-NLS-1$
+ }
+ catch (CoreException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a prepared statement cache with the {@link CDODBUtil#DEFAULT_STATEMENT_CACHE_CAPACITY default capacity}.
+ *
+ * @since 2.0
+ * @see CDODBUtil#createStatementCache(int)
+ */
+ public static IPreparedStatementCache createStatementCache()
+ {
+ return createStatementCache(DEFAULT_STATEMENT_CACHE_CAPACITY);
+ }
+
+ /**
+ * Creates a prepared statement cache with the given capacity.
+ *
+ * @since 2.0
+ */
+ public static IPreparedStatementCache createStatementCache(int capacity)
+ {
+ return new SmartPreparedStatementCache(capacity);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java
new file mode 100644
index 0000000000..c1c6d23498
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStore.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings
+ * Stefan Winkler - 249610: [DB] Support external references (Implementation)
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.server.ISession;
+import org.eclipse.emf.cdo.server.IStore;
+import org.eclipse.emf.cdo.server.IStore.CanHandleClientAssignedIDs;
+import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.db.IDBConnectionProvider;
+import org.eclipse.net4j.db.ddl.IDBSchema;
+
+/**
+ * @author Eike Stepper
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IDBStore extends IStore, IDBConnectionProvider, CanHandleClientAssignedIDs
+{
+ /**
+ * @since 2.0
+ */
+ public IMappingStrategy getMappingStrategy();
+
+ /**
+ * @since 4.0
+ */
+ public IIDHandler getIDHandler();
+
+ public IDBAdapter getDBAdapter();
+
+ public IDBSchema getDBSchema();
+
+ /**
+ * Get the meta data manager associated with this DBStore.
+ *
+ * @since 2.0
+ */
+ public IMetaDataManager getMetaDataManager();
+
+ /**
+ * @since 2.0
+ */
+ public IDBStoreAccessor getReader(ISession session);
+
+ /**
+ * @since 2.0
+ */
+ public IDBStoreAccessor getWriter(ITransaction transaction);
+
+ /**
+ * @since 4.0
+ */
+ public interface Props
+ {
+ public static final String CONNECTION_KEEPALIVE_PERIOD = "connectionKeepAlivePeriod"; //$NON-NLS-1$
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java
new file mode 100644
index 0000000000..2d507942be
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+
+import java.sql.Connection;
+
+/**
+ * @author Eike Stepper
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IDBStoreAccessor extends IStoreAccessor.Raw
+{
+ public IDBStore getStore();
+
+ public Connection getConnection();
+
+ /**
+ * @since 2.0
+ */
+ public IPreparedStatementCache getStatementCache();
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java
new file mode 100644
index 0000000000..67f9bea078
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreChunkReader.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.server.IStoreChunkReader;
+
+/**
+ * @author Eike Stepper
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IDBStoreChunkReader extends IStoreChunkReader
+{
+ /**
+ * @since 2.0
+ */
+ public IDBStoreAccessor getAccessor();
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java
new file mode 100644
index 0000000000..0bbee5d23a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IIDHandler.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Comparator;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IIDHandler extends Comparator
+{
+ public IDBStore getStore();
+
+ public DBType getDBType();
+
+ public Set getObjectIDTypes();
+
+ public ITypeMapping getObjectTypeMapping();
+
+ public CDOID createCDOID(String val);
+
+ public boolean isLocalCDOID(CDOID id);
+
+ public CDOID getLastObjectID();
+
+ public void setLastObjectID(CDOID lastObjectID);
+
+ /**
+ * @since 4.1
+ */
+ public void adjustLastObjectID(CDOID maxID);
+
+ public CDOID getNextLocalObjectID();
+
+ public void setNextLocalObjectID(CDOID nextLocalObjectID);
+
+ public CDOID getNextCDOID(CDORevision revision);
+
+ public void appendCDOID(StringBuilder builder, CDOID id);
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException;
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException;
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException;
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException;
+
+ public CDOID getMinCDOID();
+
+ public CDOID getMaxCDOID();
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime);
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id);
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException;
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime, OMMonitor fork)
+ throws IOException;
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java
new file mode 100644
index 0000000000..972d735afd
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IMetaDataManager.java
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - bug 271444: [DB] Multiple refactorings
+ * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EModelElement;
+import org.eclipse.emf.ecore.EPackage;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.util.Collection;
+
+/**
+ * @author Eike Stepper
+ * @since 2.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IMetaDataManager
+{
+ /**
+ * Returns the meta ID of the given {@link EModelElement}. getMetaID(getMetaInstance(x)) yields
+ * x
+ *
+ * @param modelElement
+ * the element
+ * @return the corresponding ID
+ * @since 4.0
+ */
+ public CDOID getMetaID(EModelElement modelElement, long commitTime);
+
+ /**
+ * Returns the {@link EModelElement} referred to by the given ID. getMetaInstance(getMetaID(m)) yields
+ * m
+ *
+ * @since 4.0
+ */
+ public EModelElement getMetaInstance(CDOID id);
+
+ /**
+ * Loads a package unit from the database.
+ *
+ * @param connection
+ * the DB connection to read from.
+ * @param packageUnit
+ * the package unit to load.
+ * @return the loaded package unit.
+ * @since 2.0
+ */
+ public EPackage[] loadPackageUnit(Connection connection, InternalCDOPackageUnit packageUnit);
+
+ /**
+ * @since 4.0
+ */
+ public void clearMetaIDMappings();
+
+ /**
+ * Reads information about package units present in the database.
+ *
+ * @param connection
+ * the DB connection to read from.
+ * @return a collection of package unit information records which can be passed to
+ * {@link IMetaDataManager#loadPackageUnit(Connection, InternalCDOPackageUnit)} in order to read the EPackage.
+ * @since 2.0
+ */
+ public Collection readPackageUnits(Connection connection);
+
+ /**
+ * Write package units to the database.
+ *
+ * @param connection
+ * the DB connection to write to.
+ * @param packageUnits
+ * the package units to write.
+ * @param monitor
+ * the monitor to indicate progress.
+ * @since 2.0
+ */
+ public void writePackageUnits(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor);
+
+ /**
+ * @since 3.0
+ */
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException;
+
+ /**
+ * @since 4.0
+ */
+ public Collection rawImport(Connection connection, CDODataInput in, long fromCommitTime,
+ long toCommitTime, OMMonitor monitor) throws IOException;
+
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java
new file mode 100644
index 0000000000..4c81edc459
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IPreparedStatementCache.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.db;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+
+/**
+ * @author Stefan Winkler
+ * @since 2.0
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IPreparedStatementCache
+{
+ public void setConnection(Connection connection);
+
+ public PreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability);
+
+ public void releasePreparedStatement(PreparedStatement ps);
+
+ /**
+ * An enum for the degree of probability to which a prepared statement is reused later on. This is used for managing
+ * the cache of prepared statements so that statements which are more likely reused are kept in the cache longer. Rule
+ * of thumb:
+ *
+ *
For global statements which are used regularly (such as lookup object in cdo_objects) use
+ * {@link ReuseProbability#MAX MAX}.
+ *
For constant object-specific statements which are used regularly use {@link ReuseProbability#HIGH HIGH}.
+ *
For object-specific statements which are assembled from constants which are used regularly use
+ * {@link ReuseProbability#MEDIUM MEDIUM}.
+ *
For all other dynamic statements, like queries, use {@link ReuseProbability#LOW LOW}
+ *
+ *
+ * @author Stefan Winkler
+ * @since 2.0
+ * @noextend This interface is not intended to be extended by clients.
+ */
+ public static enum ReuseProbability
+ {
+ MAX, HIGH, MEDIUM, LOW;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java
new file mode 100644
index 0000000000..745e96eab9
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMapping.java
@@ -0,0 +1,287 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - bug 271444: [DB] Multiple refactorings
+ * Stefan Winkler - bug 275303: [DB] DBStore does not handle BIG_INTEGER and BIG_DECIMAL
+ * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 285270: [DB] Support XSD based models
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.common.revision.CDORevisionData;
+import org.eclipse.emf.cdo.server.internal.db.DBAnnotation;
+import org.eclipse.emf.cdo.server.internal.db.MetaDataManager;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingRegistry;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * This is a default implementation for the {@link ITypeMapping} interface which provides default behavor for all common
+ * types. Implementors should provide a constructor which the factory (see below) can use and implement
+ * {@link #getResultSetValue(ResultSet)}. If needed, {@link #doSetValue(PreparedStatement, int, Object)} can also be
+ * overridden as a counterpart to {@link #getResultSetValue(ResultSet)}. Finally, an implementor should also implement a
+ * suitable factory for the {@link TypeMappingRegistry} and register it either manually using
+ * {@link IManagedContainer#registerFactory(org.eclipse.net4j.util.factory.IFactory)} or using the Net4j Extension Point
+ * factories.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+public abstract class AbstractTypeMapping implements ITypeMapping
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractTypeMapping.class);
+
+ private IMappingStrategy mappingStrategy;
+
+ private EStructuralFeature feature;
+
+ private IDBField field;
+
+ private DBType dbType;
+
+ /**
+ * Create a new type mapping
+ */
+ public AbstractTypeMapping()
+ {
+ super();
+ }
+
+ public final void setMappingStrategy(IMappingStrategy mappingStrategy)
+ {
+ this.mappingStrategy = mappingStrategy;
+ }
+
+ public final IMappingStrategy getMappingStrategy()
+ {
+ return mappingStrategy;
+ }
+
+ public final void setFeature(EStructuralFeature feature)
+ {
+ this.feature = feature;
+ }
+
+ public final EStructuralFeature getFeature()
+ {
+ return feature;
+ }
+
+ public final void setValueFromRevision(PreparedStatement stmt, int index, InternalCDORevision revision)
+ throws SQLException
+ {
+ setValue(stmt, index, getRevisionValue(revision));
+ }
+
+ public final void setDefaultValue(PreparedStatement stmt, int index) throws SQLException
+ {
+ setValue(stmt, index, getDefaultValue());
+ }
+
+ public final void setValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ if (value == CDORevisionData.NIL)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("TypeMapping for {0}: converting Revision.NIL to DB-null", feature.getName()); //$NON-NLS-1$
+ }
+
+ stmt.setNull(index, getSqlType());
+ }
+ else if (value == null)
+ {
+ if (feature.isMany() || getDefaultValue() == null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("TypeMapping for {0}: writing Revision.null as DB.null", feature.getName()); //$NON-NLS-1$
+ }
+
+ stmt.setNull(index, getSqlType());
+ }
+ else
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("TypeMapping for {0}: converting Revision.null to default value", feature.getName()); //$NON-NLS-1$
+ }
+
+ setDefaultValue(stmt, index);
+ }
+ }
+ else
+ {
+ doSetValue(stmt, index, value);
+ }
+ }
+
+ public final void createDBField(IDBTable table)
+ {
+ createDBField(table, mappingStrategy.getFieldName(feature));
+ }
+
+ public final void createDBField(IDBTable table, String fieldName)
+ {
+ DBType fieldType = getDBType();
+ int fieldLength = getDBLength(fieldType);
+ field = table.addField(fieldName, fieldType, fieldLength);
+ }
+
+ public final void setDBField(IDBTable table, String fieldName)
+ {
+ field = table.getField(fieldName);
+ }
+
+ public final IDBField getField()
+ {
+ return field;
+ }
+
+ public final void readValueToRevision(ResultSet resultSet, InternalCDORevision revision) throws SQLException
+ {
+ Object value = readValue(resultSet);
+ revision.setValue(getFeature(), value);
+ }
+
+ public final Object readValue(ResultSet resultSet) throws SQLException
+ {
+ Object value = getResultSetValue(resultSet);
+ if (resultSet.wasNull())
+ {
+ if (feature.isMany())
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("TypeMapping for {0}: read db.null - setting Revision.null", feature.getName()); //$NON-NLS-1$
+ }
+
+ value = null;
+ }
+ else
+ {
+ if (getDefaultValue() == null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(
+ "TypeMapping for {0}: read db.null - setting Revision.null, because of default", feature.getName()); //$NON-NLS-1$
+ }
+
+ value = null;
+ }
+ else
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("TypeMapping for {0}: read db.null - setting Revision.NIL", feature.getName()); //$NON-NLS-1$
+ }
+
+ value = CDORevisionData.NIL;
+ }
+ }
+ }
+
+ return value;
+ }
+
+ protected Object getDefaultValue()
+ {
+ return feature.getDefaultValue();
+ }
+
+ protected final Object getRevisionValue(InternalCDORevision revision)
+ {
+ return revision.getValue(getFeature());
+ }
+
+ /**
+ * Implementors could override this method to convert a given value to the database representation and set it to the
+ * prepared statement.
+ *
+ * @param stmt
+ * the {@link PreparedStatement} which is used for DB access
+ * @param index
+ * the parameter index in the statement which should be set
+ * @param value
+ * the value of the feature which should be written into the DB
+ */
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ stmt.setObject(index, value, getSqlType());
+ }
+
+ /**
+ * Returns the SQL type of this TypeMapping. The default implementation considers the type map held by the
+ * {@link MetaDataManager meta-data manager}. Subclasses may override.
+ *
+ * @return The sql type of this TypeMapping.
+ */
+ protected int getSqlType()
+ {
+ return getDBType().getCode();
+ }
+
+ public final void setDBType(DBType dbType)
+ {
+ this.dbType = dbType;
+ }
+
+ public DBType getDBType()
+ {
+ return dbType;
+ }
+
+ protected int getDBLength(DBType type)
+ {
+ String value = DBAnnotation.COLUMN_LENGTH.getValue(feature);
+ if (value != null)
+ {
+ try
+ {
+ return Integer.parseInt(value);
+ }
+ catch (NumberFormatException e)
+ {
+ OM.LOG.error("Illegal columnLength annotation of feature " + feature.getName());
+ }
+ }
+
+ // TODO: implement DBAdapter.getDBLength
+ // mappingStrategy.getStore().getDBAdapter().getDBLength(type);
+ // which should then return the correct default field length for the db type
+ return type == DBType.VARCHAR ? 32672 : IDBField.DEFAULT;
+ }
+
+ /**
+ * Subclasses should implement this method to read the value from the result set. Typical implementations should look
+ * similar to this one: resultSet.getString(getField().getName())
+ *
+ * @param resultSet
+ * the result set to read from
+ * @return the result value read (this has to be compatible with the {@link #feature}.
+ */
+ protected abstract Object getResultSetValue(ResultSet resultSet) throws SQLException;
+
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java
new file mode 100644
index 0000000000..4170dbfae6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/AbstractTypeMappingFactory.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping.Descriptor;
+
+import org.eclipse.net4j.util.factory.Factory;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+
+/**
+ * Abstract implementation for {@link ITypeMapping.Factory}. Implementors should implement their custom
+ * {@link #create(String)} method and construct the factory using their custom descriptor. Subclasses must have a
+ * default constructor!
+ *
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+public abstract class AbstractTypeMappingFactory extends Factory implements
+ org.eclipse.emf.cdo.server.db.mapping.ITypeMapping.Factory
+{
+ private ITypeMapping.Descriptor descriptor;
+
+ public AbstractTypeMappingFactory(Descriptor descriptor)
+ {
+ super(PRODUCT_GROUP, descriptor.getFactoryType());
+ this.descriptor = descriptor;
+ }
+
+ public abstract ITypeMapping create(String description) throws ProductCreationException;
+
+ public final Descriptor getDescriptor()
+ {
+ return descriptor;
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java
new file mode 100644
index 0000000000..9775d42fd5
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMapping.java
@@ -0,0 +1,201 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Basic interface for class mappings.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IClassMapping
+{
+ /**
+ * @since 3.0
+ */
+ public EClass getEClass();
+
+ /**
+ * Returns all DB tables which are used by this class and all its contained features.
+ *
+ * @return a collection of all tables of this class and all its contained features.
+ * @since 3.0
+ */
+ public List getDBTables();
+
+ /**
+ * Get the mapping of the many-valued feature.
+ *
+ * @param feature
+ * the feature for which the mapping should be returned. feature.isMany() has to be
+ * true.
+ * @return the list mapping corresponding to the feature.
+ */
+ public IListMapping getListMapping(EStructuralFeature feature);
+
+ /**
+ * @since 3.0
+ */
+ public List getListMappings();
+
+ /**
+ * @since 4.0
+ */
+ public List getValueMappings();
+
+ /**
+ * Read a revision. The branch and timestamp to be read are derived from the branchPoint which is set to the Revision.
+ * Note that non-audit stores only support {@link CDOBranchPoint#UNSPECIFIED_DATE} and non-branching stores only
+ * support the main branch.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param revision
+ * the revision object into which the data should be read. The revision has to be have its ID set to the
+ * requested object's ID. The version is ignored, as the version parameter is used to determine the version
+ * to be read.
+ * @param listChunk
+ * the chunk size to read attribute lists.
+ * @return true, if the revision has been found and read correctly. false if the revision
+ * could not be found. In this case, the content of revision is undefined.
+ */
+ public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk);
+
+ /**
+ * Write the revision data to the database.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param revision
+ * the revision to write.
+ * @param mapType
+ * true if the type of the object is supposed to be mapped, false otherwise.
+ * @param revise
+ * true if the previous revision is supposed to be revised, false otherwise.
+ * @param monitor
+ * the monitor to indicate progress.
+ * @since 4.0
+ */
+ public void writeRevision(IDBStoreAccessor accessor, InternalCDORevision revision, boolean mapType, boolean revise,
+ OMMonitor monitor);
+
+ /**
+ * Detaches (deletes) a CDO object leaving a "ghost" revision behind.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param id
+ * the id to revise.
+ * @param version
+ * the last valid version.
+ * @param timeStamp
+ * the timestamp of detach.
+ * @param monitor
+ * the monitor to indicate progress.
+ * @since 3.0
+ */
+ public void detachObject(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp,
+ OMMonitor monitor);
+
+ /**
+ * Create a prepared statement which returns all IDs of instances of the corresponding class.
+ *
+ * @param accessor
+ * the accessor to use to create the statement
+ * @return the prepared statement ready to be executed using result.executeQuery().
+ * @since 3.0
+ */
+ public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor);
+
+ /**
+ * Create a prepared statement which returns all IDs of instances of the corresponding class.
+ *
+ * @param accessor
+ * the accessor to use to create the statement
+ * @param folderId
+ * the ID of the containing folder. 0 means none.
+ * @param name
+ * the name of the resource node to look up
+ * @param exactMatch
+ * if true, name must match exactly, otherwise all resource nodes starting with
+ * name are returned.
+ * @param branchPoint
+ * a branchPoint (branch and timestamp). A timestamp in the past if past versions should be looked up. In
+ * case of no audit support, this must be {@link CDORevision#UNSPECIFIED_DATE}. In case of non branching
+ * support the branch id must be equal to {@link CDOBranch#MAIN_BRANCH_ID}.
+ * @return the prepared statement ready to be executed using result.executeQuery().
+ * @throws ImplementationError
+ * if called on a mapping which does not map an EClass instanceof CDOResourceNode.
+ * @since 3.0
+ */
+ public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name,
+ boolean exactMatch, CDOBranchPoint branchPoint);
+
+ /**
+ * Passes all revisions of the store to the {@link CDORevisionHandler handler} if all of the following
+ * conditions are met:
+ *
+ *
The branch parameter is null or equal to revision.getBranch().
+ *
The timeStamp parameter is {@link CDOBranchPoint#UNSPECIFIED_DATE} or equal to
+ * revision.getTimeStamp().
+ *
+ *
+ * @see IMappingStrategy#handleRevisions(IDBStoreAccessor, org.eclipse.emf.ecore.EClass, CDOBranch, long, boolean,
+ * CDORevisionHandler)
+ * @since 4.0
+ */
+ public void handleRevisions(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, boolean exactTime,
+ CDORevisionHandler handler);
+
+ /**
+ * Returns a set of CDOIDs that have at least one revision in any of the passed branches and time ranges.
+ * DetachedCDORevisions must also be considered!
+ *
+ * @see IStoreAccessor#readChangeSet(OMMonitor, CDOChangeSetSegment...)
+ * @since 3.0
+ */
+ public Set readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments);
+
+ /**
+ * Retrieve cross-references from DB
+ *
+ * @param idString
+ * a string of the form "(id1, id2, id3, ...)" which can be used directly in SQL to form the where-part
+ * "SELECT * FROM foobar WHERE foobar.target IN [idString]".
+ * @see IStoreAccessor#queryXRefs(QueryXRefsContext)
+ * @since 4.0
+ */
+ public boolean queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context, String idString);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java
new file mode 100644
index 0000000000..5a1b13f4b4
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingAuditSupport.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+/**
+ * Interface which complements {@link IClassMapping} with methods to facilitate audit support.
+ *
+ * @see IMappingStrategy#hasAuditSupport()
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IClassMappingAuditSupport
+{
+ /**
+ * Read a specific version of a revision. If this method returns true it is guaranteed that
+ * revision.getVersion() == version
+ *
+ * @param storeAccessor
+ * the accessor to use.
+ * @param revision
+ * the revision object into which the data should be read. The revision has to be have its ID set to the
+ * requested object's ID. The version is ignored, as the version parameter is used to determine the version
+ * to be read.
+ * @param listChunk
+ * the chunk size to read attribute lists.
+ * @return true, if the revision has been found and read correctly. false if the revision
+ * could not be found. In this case, the content of revision is undefined.
+ * @since 3.0
+ */
+ public boolean readRevisionByVersion(IDBStoreAccessor storeAccessor, InternalCDORevision revision, int listChunk);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java
new file mode 100644
index 0000000000..86bf12ed72
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingDeltaSupport.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+/**
+ * Interface which complements {@link IClassMapping} with methods to facilitate revision delta support.
+ *
+ * @see IMappingStrategy#hasDeltaSupport()
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IClassMappingDeltaSupport
+{
+ /**
+ * Write a revision delta.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param delta
+ * the delta to write.
+ * @param created
+ * the creation timestamp of the new version
+ * @param monitor
+ * the monitor to report progress.
+ */
+ public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created,
+ OMMonitor monitor);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java
new file mode 100644
index 0000000000..bb09fa2e0d
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - major refactoring
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.ddl.IDBTable;
+
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Interface for mapping features with isMany() == true.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IListMapping
+{
+ /**
+ * Return the mapped feature.
+ *
+ * @return the mapped feature.
+ */
+ public EStructuralFeature getFeature();
+
+ /**
+ * Returns all DB tables which are used by this feature.
+ *
+ * @return a collection of all tables of this feature.
+ */
+ public Collection getDBTables();
+
+ /**
+ * Write a complete list of values to the database.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param revision
+ * the revision containing the list to be written.
+ */
+ public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision);
+
+ /**
+ * Read the list size and the complete list or the first part of it.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param revision
+ * the revision into which the list values should be read.
+ * @param listChunk
+ * indicating the lazy loading behavior: {@link CDORevision#UNCHUNKED} means that the whole list should be
+ * read. Else, if listChunk >= 0, the list is filled with {@link InternalCDOList#UNINITIALIZED}
+ * and only the first listChunk values are read.
+ */
+ public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk);
+
+ /**
+ * Used to load-on-demand chunks of a list.
+ *
+ * @param dbStoreChunkReader
+ * the chunkReader to use
+ * @param chunks
+ * the chunks to read
+ * @param where
+ * the where-clause to use in order to read the chunks.
+ */
+ public void readChunks(IDBStoreChunkReader dbStoreChunkReader, List chunks, String where);
+
+ /**
+ * Hook with which a list mapping is notified that a containing object has been revised. Can be implemented in order
+ * to clean up lists of revised objects.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param id
+ * the ID of the object which has been revised.
+ * @param revised
+ * the timestamp at which the object was revised.
+ * @since 3.0
+ */
+ public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised);
+
+ /**
+ * Retrieve cross-references from DB.
+ *
+ * @see IClassMapping#queryXRefs(IDBStoreAccessor, IStoreAccessor.QueryXRefsContext, String)
+ * @see IStoreAccessor#queryXRefs(IStoreAccessor.QueryXRefsContext)
+ * @since 4.0
+ */
+ public boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere,
+ QueryXRefsContext context, String idString);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java
new file mode 100644
index 0000000000..5ed5698512
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingDeltaSupport.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+
+/**
+ * Interface to complement {@link IListMapping} in order to provide list delta processing support.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IListMappingDeltaSupport
+{
+ /**
+ * Process a set of CDOFeatureDeltas for a many-valued feature.
+ *
+ * @param accessor
+ * the accessor to use
+ * @param id
+ * the ID of the revision affected
+ * @param oldVersion
+ * the original version of the revision
+ * @param newVersion
+ * the new revision of the revision (after the change)
+ * @param created
+ * the creation date for the new revision
+ * @param delta
+ * the {@link CDOListFeatureDelta} which contains the list deltas.
+ * @since 4.0
+ */
+ public void processDelta(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion,
+ long created, CDOListFeatureDelta delta);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java
new file mode 100644
index 0000000000..90c6516cc6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IMappingStrategy.java
@@ -0,0 +1,348 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryResourcesContext;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.internal.db.DBStore;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.util.collection.CloseableIterator;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.ENamedElement;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The mapping strategy acts as a connection between the DBStore and the database management (and OR-mapping) classes.
+ * The {@link DBStore} uses methods of this interface to create and lookup mappings (or mappers, as they could also be
+ * named as such) and to get properties and informations about the mappings used. The mapping classes (e.g., instances
+ * of IClassMapping and IListMapping) also use this class as a central point of information and as a resource of common
+ * functionalities.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface IMappingStrategy
+{
+ /**
+ * Name of the integer property that configures the maximum length for table names. A value of zero indicates the
+ * value of the {@link IDBAdapter#getMaxTableNameLength() db adapter} to be used.
+ */
+ public static final String PROP_MAX_TABLE_NAME_LENGTH = "maxTableNameLength"; //$NON-NLS-1$
+
+ /**
+ * Name of the integer property that configures the maximum length for column names. A value of zero indicates the
+ * value of the {@link IDBAdapter#getMaxFieldNameLength() db adapter} to be used.
+ */
+ public static final String PROP_MAX_FIELD_NAME_LENGTH = "maxFieldNameLength"; //$NON-NLS-1$
+
+ /**
+ * Name of the String property that specifies a common prefix for table names.
+ */
+ public static final String PROP_TABLE_NAME_PREFIX = "tableNamePrefix"; //$NON-NLS-1$
+
+ /**
+ * Name of the boolean property that configures whether the table names are made of simple class names or of qualified
+ * class names.
+ */
+ public static final String PROP_QUALIFIED_NAMES = "qualifiedNames"; //$NON-NLS-1$
+
+ /**
+ * Name of the boolean property that configures whether table names and column names are always suffixed with the
+ * internal DBID or only in cases where generated names violate the naming constraints of the underlying backend.
+ */
+ public static final String PROP_FORCE_NAMES_WITH_ID = "forceNamesWithID"; //$NON-NLS-1$
+
+ /**
+ * Name of the integer property that configures the size of the object type in-memory cache. Possible configuration
+ * values are:
+ *
+ *
0 (zero). Don't use memory caching.
+ *
>0. Use memory caching with the cache size given.
+ *
+ * Default is a memory cache size of 10,000,000.
+ *
+ *
+ * @since 4.0
+ */
+ public static final String PROP_OBJECT_TYPE_CACHE_SIZE = "objectTypeCacheSize"; //$NON-NLS-1$
+
+ /**
+ * @return the store, this MappingStrategy instance belongs to.
+ */
+ public IDBStore getStore();
+
+ /**
+ * Set the store to which this MappingStrategy instance belongs. Should only be called by the {@link DBStore}, and
+ * only once to initialize the connection between {@link DBStore} and mapping strategy.
+ *
+ * @param dbStore
+ * the DBStore instance to which this MappingStrategy instance belongs.
+ */
+ public void setStore(IDBStore dbStore);
+
+ /**
+ * Factory for value mappings of single-valued attributes.
+ *
+ * @param feature
+ * the feature for which a mapping should be created. It must hold feature.isMany() == false.
+ * @return the mapping created.
+ */
+ public ITypeMapping createValueMapping(EStructuralFeature feature);
+
+ /**
+ * Factory for value mappings of multi-valued-attributes.
+ *
+ * @param containingClass
+ * the class containing the feature.
+ * @param feature
+ * the feature for which a mapping should be created. It must hold feature.isMany() == true.
+ */
+ public IListMapping createListMapping(EClass containingClass, EStructuralFeature feature);
+
+ /**
+ * Create a suitable table name which can be used to map the given element. Should only be called by mapping classes.
+ *
+ * @param element
+ * the element for which the name should be created. It must hold:
+ * element instanceof EClass || element instanceof EPackage.
+ * @return the created table name. It is guaranteed that the table name is compatible with the chosen database.
+ */
+ public String getTableName(ENamedElement element);
+
+ /**
+ * Create a suitable table name which can be used to map the given element. Should only be called by mapping classes.
+ * Should only be called by mapping classes.
+ *
+ * @param containingClass
+ * the class containeng the feature.
+ * @param feature
+ * the feature for which the table name should be created.
+ * @return the created table name. It is guaranteed that the table name is compatible with the chosen database.
+ */
+ public String getTableName(EClass containingClass, EStructuralFeature feature);
+
+ /**
+ * Create a suitable column name which can be used to map the given element. Should only be called by mapping classes.
+ *
+ * @param feature
+ * the feature for which the column name should be created.
+ * @return the created column name. It is guaranteed that the name is compatible with the chosen database.
+ */
+ public String getFieldName(EStructuralFeature feature);
+
+ /**
+ * Create and initialize the mapping infrastructure for the given packages. Should be called from the DBStore or the
+ * DBStoreAccessor.
+ *
+ * @param connection
+ * the connection to use.
+ * @param packageUnits
+ * the packages whose elements should be mapped.
+ * @param monitor
+ * the monitor to report progress.
+ */
+ public void createMapping(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor);
+
+ /**
+ * Remove the mapping infrastructure for the given packages. Should be called from the DBStore or the DBStoreAccessor.
+ *
+ * @param connection
+ * the connection to use.
+ * @param packageUnits
+ * the packages for which the mappings should be removed
+ * @since 4.0
+ */
+ // Bugzilla 298632
+ public void removeMapping(Connection connection, InternalCDOPackageUnit[] packageUnits);
+
+ /**
+ * Look up an existing class mapping for the given class. Before this method is called, the class mapping must have
+ * been initialized by calling {@link #createMapping(Connection, InternalCDOPackageUnit[], OMMonitor)} on its
+ * containing package.
+ *
+ * @param eClass
+ * the class to look up.
+ * @return the class mapping.
+ */
+ public IClassMapping getClassMapping(EClass eClass);
+
+ /**
+ * Returns all class mappings of this strategy.
+ *
+ * @since 4.0
+ */
+ public Map getClassMappings();
+
+ /**
+ * Returns all class mappings of this strategy.
+ *
+ * @since 4.0
+ */
+ public Map getClassMappings(boolean createOnDemand);
+
+ /**
+ * Query if this mapping supports revision deltas.
+ * If this method returns true, it is guaranteed that all class mappings returned by
+ * {@link #getClassMapping(EClass)} implement {@link IClassMappingDeltaSupport}.
+ *
+ * @return true if revision deltas are supported, false else.
+ */
+ public boolean hasDeltaSupport();
+
+ /**
+ * Query if this mapping supports audits.
+ * If this method returns true, it is guaranteed that all class mappings returned by
+ * {@link #getClassMapping(EClass)} implement {@link IClassMappingAuditSupport}.
+ *
+ * @return true if audits are supported, false else.
+ */
+ public boolean hasAuditSupport();
+
+ /**
+ * Query if this mapping supports branches.
+ *
+ * @return true if branches are supported, false else.
+ * @since 3.0
+ */
+ public boolean hasBranchingSupport();
+
+ /**
+ * Executes a resource query.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param context
+ * the context from which the query parameters are read and to which the result is written.
+ */
+ public void queryResources(IDBStoreAccessor accessor, QueryResourcesContext context);
+
+ /**
+ * Executes a cross reference query.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @param context
+ * the context from which the query parameters are read and to which the result is written.
+ * @since 3.0
+ */
+ public void queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context);
+
+ /**
+ * Read the type (i.e. class) of the object referred to by a given ID.
+ *
+ * @param accessor
+ * the accessor to use to look up the type.
+ * @param id
+ * the ID of the object for which the type is to be determined.
+ * @return the type of the object.
+ */
+ public CDOClassifierRef readObjectType(IDBStoreAccessor accessor, CDOID id);
+
+ /**
+ * Get an iterator over all instances of objects in the store.
+ *
+ * @param accessor
+ * the accessor to use.
+ * @return the iterator.
+ */
+ public CloseableIterator readObjectIDs(IDBStoreAccessor accessor);
+
+ /**
+ * Return the maximum object id used in the store. This is used by the DBStore if a previous crash is discovered
+ * during the startup process. Should only be called by the DBStore and only during startup.
+ *
+ * @param dbAdapter
+ * the dbAdapter to use to access the database
+ * @param connection
+ * the connection to use to access the database
+ * @since 4.0
+ */
+ public void repairAfterCrash(IDBAdapter dbAdapter, Connection connection);
+
+ /**
+ * Returns the configuration properties of this mapping strategy.
+ *
+ * @since 4.0
+ */
+ public Map getProperties();
+
+ /**
+ * Set configuration properties for this mapping strategy. Should only be called by the factory creating the mapping
+ * strategy instance.
+ *
+ * @param properties
+ * the configuration properties to set.
+ */
+ public void setProperties(Map properties);
+
+ /**
+ * Passes all revisions of the store to the {@link CDORevisionHandler handler} if all of the following
+ * conditions are met:
+ *
+ *
The eClass parameter is null or equal to revision.getEClass().
+ *
The branch parameter is null or equal to revision.getBranch().
+ *
The timeStamp parameter is {@link CDOBranchPoint#UNSPECIFIED_DATE} or equal to
+ * revision.getTimeStamp().
+ *
+ *
+ * @since 4.0
+ */
+ public void handleRevisions(IDBStoreAccessor accessor, EClass eClass, CDOBranch branch, long timeStamp,
+ boolean exactTime, CDORevisionHandler handler);
+
+ /**
+ * Returns a set of CDOIDs that have at least one revision in any of the passed branches and time ranges.
+ * DetachedCDORevisions must also be considered!
+ *
+ * @see IStoreAccessor#readChangeSet(OMMonitor, CDOChangeSetSegment...)
+ * @since 4.0
+ */
+ public Set readChangeSet(IDBStoreAccessor accessor, OMMonitor monitor, CDOChangeSetSegment[] segments);
+
+ /**
+ * @since 3.0
+ */
+ public void rawExport(IDBStoreAccessor accessor, CDODataOutput out, int lastReplicatedBranchID, int lastBranchID,
+ long lastReplicatedCommitTime, long lastCommitTime) throws IOException;
+
+ /**
+ * @since 4.0
+ */
+ public void rawImport(IDBStoreAccessor accessor, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException;
+
+ /**
+ * @since 4.0
+ */
+ public String getListJoin(String attrTable, String listTable);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java
new file mode 100644
index 0000000000..c6d463c15e
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/ITypeMapping.java
@@ -0,0 +1,278 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - major refactoring
+ * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
+
+import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingRegistry;
+import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingUtil;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.factory.IFactory;
+
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+
+/**
+ * Mapping of single values to and from the database.
+ *
+ * @author Eike Stepper
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public interface ITypeMapping
+{
+ /**
+ * @return The feature which is associated with this mapping.
+ */
+ public EStructuralFeature getFeature();
+
+ /**
+ * @return The db field which is associated with this mapping.
+ */
+ public IDBField getField();
+
+ /**
+ * @return The db type which is associated with this mapping.
+ * @since 3.0
+ */
+ public DBType getDBType();
+
+ /**
+ * @since 4.0
+ */
+ public void setMappingStrategy(IMappingStrategy mappingStrategy);
+
+ /**
+ * @since 4.0
+ */
+ public void setFeature(EStructuralFeature feature);
+
+ /**
+ * @since 4.0
+ */
+ public void setDBType(DBType dbType);
+
+ /**
+ * Creates the DBField and adds it to the given table. The name of the DBField is derived from the feature.
+ *
+ * @param table
+ * the table to add this field to.
+ */
+ public void createDBField(IDBTable table);
+
+ /**
+ * Creates the DBField and adds it to the given table. The name of the DBField is explicitly determined by the
+ * corresponding parameter.
+ *
+ * @param table
+ * the table to add this field to.
+ * @param fieldName
+ * the name for the DBField.
+ */
+ public void createDBField(IDBTable table, String fieldName);
+
+ /**
+ * Sets the DBField. The name of the DBField is explicitly determined by the corresponding parameter.
+ *
+ * @param table
+ * the table to add this field to.
+ * @param fieldName
+ * the name for the DBField.
+ * @since 3.0
+ */
+ public void setDBField(IDBTable table, String fieldName);
+
+ /**
+ * Set the given value to the JDBC {@link PreparedStatement} using an appropriate setXxx method.
+ *
+ * @param stmt
+ * the prepared statement to set the value
+ * @param index
+ * the index to use for the setXxx method.
+ * @param value
+ * the value to set.
+ * @throws SQLException
+ * if the setXxx throws it.
+ */
+ public void setValue(PreparedStatement stmt, int index, Object value) throws SQLException;
+
+ /**
+ * Set the feature's default value to the JDBC {@link PreparedStatement} using an appropriate setXxx
+ * method.
+ *
+ * @param stmt
+ * the prepared statement to set the value
+ * @param index
+ * the index to use for the setXxx method.
+ * @throws SQLException
+ * if the setXxx throws it.
+ * @since 3.0
+ */
+ public void setDefaultValue(PreparedStatement stmt, int index) throws SQLException;
+
+ /**
+ * Set a value of the given revision to the JDBC {@link PreparedStatement} using an appropriate setXxx
+ * method. The feature from which the value is taken is determined by {@link #getFeature()}.
+ *
+ * @param stmt
+ * the prepared statement to set the value
+ * @param index
+ * the index to use for the setXxx method.
+ * @param value
+ * the revision to get the value to set from.
+ * @throws SQLException
+ * if the setXxx throws it.
+ */
+ public void setValueFromRevision(PreparedStatement stmt, int index, InternalCDORevision value) throws SQLException;
+
+ /**
+ * Read the value from a {@link ResultSet} and convert it from the DB to the CDO representation. The resultSet field
+ * to read from is determined automatically by the internal {@link #getField()} name.
+ *
+ * @param resultSet
+ * the result set to read from
+ * @return the read value
+ * @throws SQLException
+ * if reading the value throws an SQLException
+ * @since 3.0
+ */
+ public Object readValue(ResultSet resultSet) throws SQLException;
+
+ /**
+ * Read a value from a {@link ResultSet}, convert it from the DB to the CDO representation and set it to the feature
+ * of the revision. The feature is determined by getFeature() The resultSet field to read from is determined
+ * automatically by the internal {@link #getField()} name.
+ *
+ * @param resultSet
+ * the result set to read from
+ * @param revision
+ * the revision to which the value should be set.
+ * @throws SQLException
+ * if reading the value throws an SQLException
+ * @since 3.0
+ */
+ public void readValueToRevision(ResultSet resultSet, InternalCDORevision revision) throws SQLException;
+
+ /**
+ * A descriptor which describes one type mapping class. The descriptor is encoded in the factoryType which is used as
+ * a string description for the extension point mechanism. Translations and instantiations can be done using the
+ * methods in {@link TypeMappingUtil}.
+ *
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+ public interface Descriptor
+ {
+ /**
+ * The factoryType of the factory which can create the type mapping
+ */
+ public String getFactoryType();
+
+ /**
+ * The ID of the described type mapping.
+ */
+ public String getID();
+
+ /**
+ * The source (i.e., model) type that can be mapped by the type mapping.
+ */
+ public EClassifier getEClassifier();
+
+ /**
+ * The target (i.e., db) type that can be mapped by the type mapping.
+ */
+ public DBType getDBType();
+
+ }
+
+ /**
+ * A global (singleton) registry which collects all available type mappings which are either available in the CDO
+ * core, as declared extensions, or registered manually.
+ *
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+ public interface Registry
+ {
+ /**
+ * The one global (singleton) registry instance.
+ */
+ public static Registry INSTANCE = new TypeMappingRegistry();
+
+ /**
+ * Register a type mapping by descriptor.
+ */
+ public void registerTypeMapping(ITypeMapping.Descriptor descriptor);
+
+ /**
+ * Provides a list of all DBTypes for which type mappings exist in the registry. This is used in feature map tables
+ * to create columns for all of these types.
+ */
+ public Collection getDefaultFeatureMapDBTypes();
+ }
+
+ /**
+ * A provider for type mapping information. This provider is used by the {@link TypeMappingRegistry} to create an
+ * {@link ITypeMapping} instance suitable for a given feature and DB field. Usually, one factory is responsible for
+ * one type mapping.
+ *
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+ public interface Provider
+ {
+ /**
+ * The one global (singleton) provider instance.
+ */
+ public static Provider INSTANCE = (Provider)Registry.INSTANCE;
+
+ /**
+ * Create an {@link ITypeMapping} implementation.
+ *
+ * @param mappingStrategy
+ * the mapping strategy
+ * @param feature
+ * the feature the new type mapping shall be responsible for
+ * @return the newly created {@link ITypeMapping} instance
+ */
+ public ITypeMapping createTypeMapping(IMappingStrategy mappingStrategy, EStructuralFeature feature);
+ }
+
+ /**
+ * A factory for typeMappings. This is a regular Net4j factory registered by the respective extension point. It
+ * enhances the regular factory using a descriptor which is translated from and to the factoryType by the methods in
+ * {@link TypeMappingUtil}.
+ *
+ * @author Stefan Winkler
+ * @since 4.0
+ */
+ public interface Factory extends IFactory
+ {
+ /**
+ * The Net4j factory product group for type mappings
+ */
+ public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.db.typeMappings";
+
+ /**
+ * Return the descriptor of the kind of type mapping created by this factory.
+ */
+ public ITypeMapping.Descriptor getDescriptor();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java
new file mode 100644
index 0000000000..cec7a9382a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/package-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+
+/**
+ * Server concepts for dealing with mapping strategies and mappings for classes, lists and types.
+ */
+package org.eclipse.emf.cdo.server.db.mapping;
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java
new file mode 100644
index 0000000000..53e53996bb
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/package-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+
+/**
+ * Server concepts for dealing with DB stores and accessors.
+ */
+package org.eclipse.emf.cdo.server.db;
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/AbstractPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/AbstractPreparedStatementCache.java
new file mode 100644
index 0000000000..2ffd031698
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/AbstractPreparedStatementCache.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Winkler - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+
+import java.sql.Connection;
+
+/**
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public abstract class AbstractPreparedStatementCache extends Lifecycle implements IPreparedStatementCache
+{
+ private Connection connection;
+
+ public AbstractPreparedStatementCache()
+ {
+ }
+
+ public final Connection getConnection()
+ {
+ return connection;
+ }
+
+ public final void setConnection(Connection connection)
+ {
+ checkInactive();
+ this.connection = connection;
+ }
+
+ @Override
+ protected void doBeforeActivate()
+ {
+ checkState(connection, "Must have valid connection to start"); //$NON-NLS-1$
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java
new file mode 100644
index 0000000000..dc3e19b6db
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java
@@ -0,0 +1,267 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Stefan Winkler - 249610: [DB] Support external references (Implementation)
+ * Andre Dietisheim - bug 256649
+ *
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.spi.db.DBSchema;
+
+/**
+ * @author Eike Stepper
+ */
+public class CDODBSchema extends DBSchema
+{
+ public static final CDODBSchema INSTANCE = new CDODBSchema();
+
+ /**
+ * DBTable cdo_properties
+ */
+ public static final IDBTable PROPERTIES = INSTANCE.addTable("cdo_properties"); //$NON-NLS-1$
+
+ public static final IDBField PROPERTIES_NAME = //
+ PROPERTIES.addField("name", DBType.VARCHAR, 255); //$NON-NLS-1$
+
+ public static final IDBField PROPERTIES_VALUE = //
+ PROPERTIES.addField("value", DBType.LONGVARCHAR); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_PROPERTIES_PK = //
+ PROPERTIES.addIndex(IDBIndex.Type.PRIMARY_KEY, PROPERTIES_NAME);
+
+ public static final String SQL_DELETE_PROPERTIES = "DELETE FROM " + PROPERTIES + " WHERE " + PROPERTIES_NAME + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ public static final String SQL_INSERT_PROPERTIES = "INSERT INTO " + PROPERTIES + " (" + PROPERTIES_NAME + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + PROPERTIES_VALUE + ") VALUES (?, ?)"; //$NON-NLS-1$
+
+ public static final String SQL_SELECT_PROPERTIES = "SELECT " + PROPERTIES_VALUE + " FROM " + PROPERTIES + " WHERE " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + PROPERTIES_NAME + "=?"; //$NON-NLS-1$
+
+ public static final String SQL_SELECT_ALL_PROPERTIES = "SELECT " + PROPERTIES_NAME + ", " + PROPERTIES_VALUE //$NON-NLS-1$ //$NON-NLS-2$
+ + " FROM " + PROPERTIES; //$NON-NLS-1$
+
+ /**
+ * DBTable cdo_package_units
+ */
+ public static final IDBTable PACKAGE_UNITS = INSTANCE.addTable("cdo_package_units"); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_UNITS_ID = //
+ PACKAGE_UNITS.addField("id", DBType.VARCHAR, 255); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_UNITS_ORIGINAL_TYPE = //
+ PACKAGE_UNITS.addField("original_type", DBType.INTEGER); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_UNITS_TIME_STAMP = //
+ PACKAGE_UNITS.addField("time_stamp", DBType.BIGINT); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_UNITS_PACKAGE_DATA = //
+ PACKAGE_UNITS.addField("package_data", DBType.BLOB); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_PACKAGE_UNITS_PK = //
+ PACKAGE_UNITS.addIndex(IDBIndex.Type.PRIMARY_KEY, PACKAGE_UNITS_ID);
+
+ /**
+ * DBTable cdo_packages
+ */
+ public static final IDBTable PACKAGE_INFOS = INSTANCE.addTable("cdo_package_infos"); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_INFOS_URI = //
+ PACKAGE_INFOS.addField("uri", DBType.VARCHAR, 255); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_INFOS_PARENT = //
+ PACKAGE_INFOS.addField("parent", DBType.VARCHAR, 255); //$NON-NLS-1$
+
+ public static final IDBField PACKAGE_INFOS_UNIT = //
+ PACKAGE_INFOS.addField("unit", DBType.VARCHAR, 255); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_PACKAGE_INFOS_PK = //
+ PACKAGE_INFOS.addIndex(IDBIndex.Type.PRIMARY_KEY, PACKAGE_INFOS_URI);
+
+ public static final IDBIndex INDEX_PACKAGE_INFOS_PARENT = //
+ PACKAGE_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, PACKAGE_INFOS_PARENT);
+
+ public static final IDBIndex INDEX_PACKAGE_INFOS_UNIT = //
+ PACKAGE_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, PACKAGE_INFOS_UNIT);
+
+ /**
+ * DBTable cdo_branches
+ */
+ public static final IDBTable BRANCHES = INSTANCE.addTable("cdo_branches"); //$NON-NLS-1$
+
+ public static final IDBField BRANCHES_ID = //
+ BRANCHES.addField("id", DBType.INTEGER); //$NON-NLS-1$
+
+ public static final IDBField BRANCHES_NAME = //
+ BRANCHES.addField("name", DBType.VARCHAR); //$NON-NLS-1$
+
+ public static final IDBField BRANCHES_BASE_BRANCH_ID = //
+ BRANCHES.addField("base_id", DBType.INTEGER); //$NON-NLS-1$
+
+ public static final IDBField BRANCHES_BASE_TIMESTAMP = //
+ BRANCHES.addField("base_time", DBType.BIGINT); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_BRANCHES_ID = //
+ BRANCHES.addIndex(IDBIndex.Type.PRIMARY_KEY, BRANCHES_ID);
+
+ public static final String SQL_CREATE_BRANCH = "INSERT INTO " + BRANCHES + " (" + BRANCHES_ID + ", " + BRANCHES_NAME //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + ", " + BRANCHES_BASE_BRANCH_ID + ", " + BRANCHES_BASE_TIMESTAMP + ") VALUES (?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ public static final String SQL_LOAD_BRANCH = "SELECT " + BRANCHES_NAME + ", " + BRANCHES_BASE_BRANCH_ID + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + BRANCHES_BASE_TIMESTAMP + " FROM " + BRANCHES + " WHERE " + BRANCHES_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ public static final String SQL_LOAD_SUB_BRANCHES = "SELECT " + BRANCHES_ID + ", " + BRANCHES_NAME + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + BRANCHES_BASE_TIMESTAMP + " FROM " + BRANCHES + " WHERE " + BRANCHES_BASE_BRANCH_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ public static final String SQL_LOAD_BRANCHES = "SELECT " + BRANCHES_ID + ", " + BRANCHES_NAME + ", " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ BRANCHES_BASE_BRANCH_ID + ", " + BRANCHES_BASE_TIMESTAMP //$NON-NLS-1$
+ + " FROM " + BRANCHES + " WHERE " + BRANCHES_ID + " BETWEEN ? AND ? ORDER BY " + BRANCHES_ID; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ /**
+ * DBTable cdo_commit_infos
+ */
+ public static final IDBTable COMMIT_INFOS = INSTANCE.addTable("cdo_commit_infos"); //$NON-NLS-1$
+
+ public static final IDBField COMMIT_INFOS_TIMESTAMP = //
+ COMMIT_INFOS.addField("commit_time", DBType.BIGINT); //$NON-NLS-1$
+
+ public static final IDBField COMMIT_INFOS_PREVIOUS_TIMESTAMP = //
+ COMMIT_INFOS.addField("previous_time", DBType.BIGINT); //$NON-NLS-1$
+
+ public static final IDBField COMMIT_INFOS_BRANCH = //
+ COMMIT_INFOS.addField("branch_id", DBType.INTEGER); //$NON-NLS-1$
+
+ public static final IDBField COMMIT_INFOS_USER = //
+ COMMIT_INFOS.addField("user_id", DBType.VARCHAR); //$NON-NLS-1$
+
+ public static final IDBField COMMIT_INFOS_COMMENT = //
+ COMMIT_INFOS.addField("commit_comment", DBType.VARCHAR); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_COMMIT_INFOS_PK = //
+ COMMIT_INFOS.addIndex(IDBIndex.Type.PRIMARY_KEY, COMMIT_INFOS_TIMESTAMP);
+
+ public static final IDBIndex INDEX_COMMIT_INFOS_BRANCH = //
+ COMMIT_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, COMMIT_INFOS_BRANCH);
+
+ public static final IDBIndex INDEX_COMMIT_INFOS_USER = //
+ COMMIT_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, COMMIT_INFOS_USER);
+
+ public static final String SQL_CREATE_COMMIT_INFO = "INSERT INTO " + COMMIT_INFOS + "(" + COMMIT_INFOS_TIMESTAMP //$NON-NLS-1$ //$NON-NLS-2$
+ + ", " + COMMIT_INFOS_PREVIOUS_TIMESTAMP + ", " + COMMIT_INFOS_BRANCH + ", " + COMMIT_INFOS_USER + ", " + COMMIT_INFOS_COMMENT + ") " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ + "VALUES (?, ?, ?, ?, ?)"; //$NON-NLS-1$
+
+ /**
+ * DBTable cdo_lobs
+ */
+ public static final IDBTable LOBS = INSTANCE.addTable("cdo_lobs"); //$NON-NLS-1$
+
+ public static final IDBField LOBS_ID = //
+ LOBS.addField("id", DBType.VARCHAR, 64); //$NON-NLS-1$
+
+ public static final IDBField LOBS_SIZE = //
+ LOBS.addField("size", DBType.BIGINT); //$NON-NLS-1$
+
+ public static final IDBField LOBS_BDATA = //
+ LOBS.addField("bdata", DBType.BLOB); //$NON-NLS-1$
+
+ public static final IDBField LOBS_CDATA = //
+ LOBS.addField("cdata", DBType.CLOB); //$NON-NLS-1$
+
+ public static final IDBIndex INDEX_LOBS_ID = //
+ LOBS.addIndex(IDBIndex.Type.PRIMARY_KEY, LOBS_ID);
+
+ public static final String SQL_QUERY_LOBS = "SELECT 1 FROM " + CDODBSchema.LOBS + " WHERE " + CDODBSchema.LOBS_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ public static final String SQL_HANDLE_LOBS = "SELECT " + CDODBSchema.LOBS_ID + ", " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_BDATA + ", " + CDODBSchema.LOBS_CDATA + " FROM " + CDODBSchema.LOBS; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+
+ public static final String SQL_LOAD_LOB = "SELECT " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_BDATA + ", " + CDODBSchema.LOBS_CDATA + " FROM " + CDODBSchema.LOBS + " WHERE " + CDODBSchema.LOBS_ID + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+
+ public static final String SQL_WRITE_BLOB = "INSERT INTO " + CDODBSchema.LOBS + "(" + CDODBSchema.LOBS_ID + ", " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_BDATA + ") VALUES(?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+
+ public static final String SQL_WRITE_CLOB = "INSERT INTO " + CDODBSchema.LOBS + "(" + CDODBSchema.LOBS_ID + ", " + CDODBSchema.LOBS_SIZE + ", " + CDODBSchema.LOBS_CDATA + ") VALUES(?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+
+ /**
+ * Name of object table
+ */
+ public static final String CDO_OBJECTS = "cdo_objects"; //$NON-NLS-1$
+
+ /**
+ * Field names of attribute tables
+ */
+ public static final String ATTRIBUTES_ID = "cdo_id"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_BRANCH = "cdo_branch"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_VERSION = "cdo_version"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_CLASS = "cdo_class"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_CREATED = "cdo_created"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_REVISED = "cdo_revised"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_RESOURCE = "cdo_resource"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_CONTAINER = "cdo_container"; //$NON-NLS-1$
+
+ public static final String ATTRIBUTES_FEATURE = "cdo_feature"; //$NON-NLS-1$
+
+ /**
+ * Field names of list tables
+ */
+ public static final String LIST_FEATURE = "cdo_feature"; //$NON-NLS-1$
+
+ public static final String LIST_REVISION_ID = "cdo_source"; //$NON-NLS-1$
+
+ public static final String LIST_REVISION_VERSION = "cdo_version"; //$NON-NLS-1$
+
+ public static final String LIST_REVISION_VERSION_ADDED = "cdo_version_added"; //$NON-NLS-1$
+
+ public static final String LIST_REVISION_VERSION_REMOVED = "cdo_version_removed"; //$NON-NLS-1$
+
+ public static final String LIST_REVISION_BRANCH = "cdo_branch"; //$NON-NLS-1$
+
+ public static final String LIST_IDX = "cdo_idx"; //$NON-NLS-1$
+
+ public static final String LIST_VALUE = "cdo_value"; //$NON-NLS-1$
+
+ /**
+ * Field names of featuremap tables
+ */
+ public static final String FEATUREMAP_REVISION_ID = "cdo_id"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_VERSION = "cdo_version"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_VERSION_ADDED = "cdo_version_added"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_VERSION_REMOVED = "cdo_version_removed"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_BRANCH = "cdo_branch"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_IDX = "cdo_idx"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_TAG = "cdo_tag"; //$NON-NLS-1$
+
+ public static final String FEATUREMAP_VALUE = "cdo_value"; //$NON-NLS-1$
+
+ private CDODBSchema()
+ {
+ super("CDO"); //$NON-NLS-1$
+ }
+
+ static
+ {
+ INSTANCE.lock();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java
new file mode 100644
index 0000000000..148115658f
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBAnnotation.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Kai Schlamp - initial API and implementation
+ * Eike Stepper - maintenance
+ * Kai Schlamp - Bug 284680 - [DB] Provide annotation to bypass ClassMapping
+ * Stefan Winkler - maintenance
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.ecore.EModelElement;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+
+/**
+ * @author Kai Schlamp
+ */
+public enum DBAnnotation
+{
+ TABLE_MAPPING("tableMapping"), //
+ TABLE_NAME("tableName"), //
+ COLUMN_NAME("columnName"), //
+ COLUMN_TYPE("columnType"), //
+ COLUMN_LENGTH("columnLength"), //
+ TYPE_MAPPING("typeMapping");
+
+ public final static String SOURCE_URI = "http://www.eclipse.org/CDO/DBStore";
+
+ public final static String TABLE_MAPPING_NONE = "NONE";
+
+ private String keyword;
+
+ private DBAnnotation(String keyword)
+ {
+ this.keyword = keyword;
+ }
+
+ public String getKeyword()
+ {
+ return keyword == null ? super.toString() : keyword;
+ }
+
+ /**
+ * @return A non-empty string or null.
+ */
+ public String getValue(EModelElement element)
+ {
+ String value = EcoreUtil.getAnnotation(element, SOURCE_URI, keyword);
+ if (value != null && value.length() == 0)
+ {
+ return null;
+ }
+
+ return value;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getKeyword();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java
new file mode 100644
index 0000000000..c81aafe6ab
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBBrowserPage.java
@@ -0,0 +1,208 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.server.CDOServerBrowser;
+import org.eclipse.emf.cdo.server.CDOServerBrowser.AbstractPage;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.IDBConnectionProvider;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+
+import java.io.PrintStream;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+
+/**
+ * @author Eike Stepper
+ * @since 4.0
+ */
+public class DBBrowserPage extends AbstractPage
+{
+ public DBBrowserPage()
+ {
+ super("tables", "Database Tables");
+ }
+
+ public boolean canDisplay(InternalRepository repository)
+ {
+ return repository.getStore() instanceof IDBConnectionProvider;
+ }
+
+ public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out)
+ {
+ IDBConnectionProvider connectionProvider = (IDBConnectionProvider)repository.getStore();
+ Connection connection = null;
+
+ try
+ {
+ connection = connectionProvider.getConnection();
+
+ out.print("
+ * Takes into account the {@link CDOQueryInfo#getMaxResults()} and the {@link SQLQueryHandler#FIRST_RESULT} (numbered
+ * from 0) values for paging.
+ *
+ * By default (parameter {@link SQLQueryHandler#CDO_OBJECT_QUERY} == true) a query for CDO Objects is exectued. The
+ * SQL query must return the CDO ID in the first column for this to work. If you set
+ * {@link SQLQueryHandler#CDO_OBJECT_QUERY} parameter to false, the value of the first column of a row itself is
+ * returned.
+ *
+ * By default (parameter {@link SQLQueryHandler#QUERY_STATEMENT} == true) query statements are executed. Set this
+ * parameter to false for update/DDL statements.
+ *
+ * It is possible to use variables inside the SQL string with ":" as prefix. E.g.
+ * "SELECT cdo_id FROM Company WHERE name LIKE :name". The value must then be set by using a parameter. E.g.
+ * query.setParameter(":name", "Foo%");
+ *
+ * @param info
+ * the object containing the query and parameters
+ * @param context
+ * the query results are placed in the context
+ * @see IQueryHandler#executeQuery(CDOQueryInfo, IQueryContext)
+ */
+ public void executeQuery(CDOQueryInfo info, IQueryContext context)
+ {
+ String language = info.getQueryLanguage();
+ if (!QUERY_LANGUAGE.equals(language))
+ {
+ throw new IllegalArgumentException("Unsupported query language: " + language);
+ }
+
+ IIDHandler idHandler = storeAccessor.getStore().getIDHandler();
+ Connection connection = storeAccessor.getConnection();
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ String query = info.getQueryString();
+
+ try
+ {
+ int firstResult = -1;
+ boolean queryStatement = true;
+ boolean objectQuery = true;
+ boolean mapQuery = false;
+
+ HashMap> paramMap = new HashMap>();
+ query = parse(query, paramMap);
+ statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+
+ for (String key : info.getParameters().keySet())
+ {
+ if (FIRST_RESULT.equalsIgnoreCase(key))
+ {
+ final Object o = info.getParameters().get(key);
+ if (o != null)
+ {
+ try
+ {
+ firstResult = (Integer)o;
+ }
+ catch (ClassCastException ex)
+ {
+ throw new IllegalArgumentException("Parameter " + FIRST_RESULT + " must be an integer but it is a " + o
+ + " class " + o.getClass().getName(), ex);
+ }
+ }
+ }
+ else if (QUERY_STATEMENT.equalsIgnoreCase(key))
+ {
+ final Object o = info.getParameters().get(key);
+ if (o != null)
+ {
+ try
+ {
+ queryStatement = (Boolean)o;
+ }
+ catch (ClassCastException ex)
+ {
+ throw new IllegalArgumentException("Parameter " + QUERY_STATEMENT + " must be an boolean but it is a "
+ + o + " class " + o.getClass().getName(), ex);
+ }
+ }
+ }
+ else if (CDO_OBJECT_QUERY.equalsIgnoreCase(key))
+ {
+ final Object o = info.getParameters().get(key);
+ if (o != null)
+ {
+ try
+ {
+ objectQuery = (Boolean)o;
+ }
+ catch (ClassCastException ex)
+ {
+ throw new IllegalArgumentException("Parameter " + CDO_OBJECT_QUERY + " must be a boolean but it is a "
+ + o + " class " + o.getClass().getName(), ex);
+ }
+ }
+ }
+ else if (MAP_QUERY.equalsIgnoreCase(key))
+ {
+ final Object o = info.getParameters().get(key);
+ if (o != null)
+ {
+ try
+ {
+ mapQuery = (Boolean)o;
+ }
+ catch (ClassCastException ex)
+ {
+ throw new IllegalArgumentException("Parameter " + MAP_QUERY + " must be a boolean but it is a " + o
+ + " class " + o.getClass().getName(), ex);
+ }
+ }
+ }
+ else
+ {
+ if (!paramMap.containsKey(key) || paramMap.get(key) == null)
+ {
+ throw new IllegalArgumentException("No parameter value found for named parameter " + key);
+ }
+
+ Integer[] indexes = paramMap.get(key).toArray(new Integer[0]);
+ for (int i = 0; i < indexes.length; i++)
+ {
+ Object parameter = info.getParameters().get(key);
+ statement.setObject(indexes[i], parameter);
+ }
+ }
+ }
+
+ if (queryStatement)
+ {
+ resultSet = statement.executeQuery();
+ if (firstResult > -1)
+ {
+ resultSet.absolute(firstResult);
+ }
+
+ String[] columnNames = null;
+ if (mapQuery)
+ {
+ columnNames = new String[resultSet.getMetaData().getColumnCount()];
+ for (int i = 1; i <= columnNames.length; i++)
+ {
+ columnNames[i - 1] = resultSet.getMetaData().getColumnName(i);
+ }
+ }
+
+ int maxResults = info.getMaxResults();
+ int counter = 0;
+
+ while (resultSet.next())
+ {
+ if (maxResults != CDOQueryInfo.UNLIMITED_RESULTS && counter++ >= maxResults)
+ {
+ break;
+ }
+
+ if (objectQuery)
+ {
+ CDOID result = idHandler.getCDOID(resultSet, 1);
+ context.addResult(result);
+ }
+ else
+ {
+ int columnCount = resultSet.getMetaData().getColumnCount();
+ if (columnCount == 1)
+ {
+ Object result = convertValue(resultSet.getObject(1));
+ context.addResult(mapQuery ? toMap(columnNames, new Object[] { result }) : result);
+ }
+ else
+ {
+ Object[] results = new Object[columnCount];
+ for (int i = 0; i < columnCount; i++)
+ {
+ results[i] = convertValue(resultSet.getObject(i + 1));
+ }
+
+ context.addResult(mapQuery ? toMap(columnNames, results) : results);
+ }
+ }
+ }
+ }
+ else
+ {
+ int result = statement.executeUpdate();
+ context.addResult(result);
+ }
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException("Problem while executing SQL query: " + query, ex);
+ }
+ finally
+ {
+ DBUtil.close(resultSet);
+ DBUtil.close(statement);
+ }
+ }
+
+ private Object convertValue(Object value)
+ {
+ if (value instanceof Clob)
+ {
+ Clob clob = (Clob)value;
+
+ try
+ {
+ value = clob.getSubString(1, (int)clob.length());
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException("Could not extract CLOB value", ex);
+ }
+ }
+
+ return value;
+ }
+
+ private Map toMap(String[] columnNames, Object[] results)
+ {
+ Map ret = new HashMap();
+
+ for (int i = 0; i < columnNames.length; i++)
+ {
+ String columnName = columnNames[i];
+ ret.put(columnName, results[i]);
+ }
+
+ return ret;
+ }
+
+ private String parse(String query, Map> paramMap)
+ {
+ int length = query.length();
+ StringBuilder builder = new StringBuilder(length);
+
+ boolean inSingleQuote = false;
+ boolean inDoubleQuote = false;
+ int index = 1;
+
+ for (int i = 0; i < length; i++)
+ {
+ char c = query.charAt(i);
+ if (inSingleQuote)
+ {
+ if (c == '\'')
+ {
+ inSingleQuote = false;
+ }
+ }
+ else if (inDoubleQuote)
+ {
+ if (c == '"')
+ {
+ inDoubleQuote = false;
+ }
+ }
+ else
+ {
+ if (c == '\'')
+ {
+ inSingleQuote = true;
+ }
+ else if (c == '"')
+ {
+ inDoubleQuote = true;
+ }
+ else if (c == ':' && i + 1 < length && Character.isJavaIdentifierStart(query.charAt(i + 1)))
+ {
+ int j = i + 2;
+ while (j < length && Character.isJavaIdentifierPart(query.charAt(j)))
+ {
+ j++;
+ }
+
+ String name = query.substring(i + 1, j);
+ c = '?';
+ i += name.length();
+
+ List indexList = paramMap.get(name);
+ if (indexList == null)
+ {
+ indexList = new ArrayList();
+ paramMap.put(name, indexList);
+ }
+
+ indexList.add(new Integer(index));
+ index++;
+ }
+ }
+
+ builder.append(c);
+ }
+
+ return builder.toString();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java
new file mode 100644
index 0000000000..0ea0e9109b
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java
@@ -0,0 +1,291 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Winkler - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.util.ImplementationError;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.HashMap;
+
+/**
+ * @author Stefan Winkler
+ * @since 2.0
+ */
+public class SmartPreparedStatementCache extends AbstractPreparedStatementCache
+{
+ private Cache cache;
+
+ private HashMap checkedOut = new HashMap();
+
+ public SmartPreparedStatementCache(int capacity)
+ {
+ cache = new Cache(capacity);
+ }
+
+ public PreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability)
+ {
+ CachedPreparedStatement cachedStatement = cache.remove(sql);
+ if (cachedStatement == null)
+ {
+ cachedStatement = createCachedPreparedStatement(sql, reuseProbability);
+ }
+
+ PreparedStatement result = cachedStatement.getPreparedStatement();
+ checkedOut.put(result, cachedStatement);
+
+ return result;
+ }
+
+ /**
+ * @param ps
+ * the prepared statement to be released to the cache, or null.
+ */
+ public void releasePreparedStatement(PreparedStatement ps)
+ {
+ if (ps != null) // Bug 276926: Silently accept ps == null and do nothing.
+ {
+ CachedPreparedStatement cachedStatement = checkedOut.remove(ps);
+ cache.put(cachedStatement);
+ }
+ }
+
+ @Override
+ protected void doBeforeDeactivate() throws Exception
+ {
+ if (!checkedOut.isEmpty())
+ {
+ OM.LOG.warn("Statement leak detected"); //$NON-NLS-1$
+ }
+ }
+
+ private CachedPreparedStatement createCachedPreparedStatement(String sql, ReuseProbability reuseProbability)
+ {
+ try
+ {
+ Connection connection = getConnection();
+ PreparedStatement stmt = connection.prepareStatement(sql);
+ return new CachedPreparedStatement(sql, reuseProbability, stmt);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private static final class Cache
+ {
+ private CacheList lists[];
+
+ private HashMap lookup;
+
+ private int capacity;
+
+ public Cache(int capacity)
+ {
+ this.capacity = capacity;
+
+ lookup = new HashMap(capacity);
+
+ lists = new CacheList[ReuseProbability.values().length];
+ for (ReuseProbability prob : ReuseProbability.values())
+ {
+ lists[prob.ordinal()] = new CacheList();
+ }
+ }
+
+ public void put(CachedPreparedStatement cachedStatement)
+ {
+ // refresh age
+ cachedStatement.touch();
+
+ // put into appripriate list
+ lists[cachedStatement.getProbability().ordinal()].add(cachedStatement);
+
+ // put into lookup table
+ if (lookup.put(cachedStatement.getSQL(), cachedStatement) != null)
+ {
+ throw new ImplementationError(cachedStatement.getSQL() + " already in cache"); //$NON-NLS-1$
+ }
+
+ // handle capacity overflow
+ if (lookup.size() > capacity)
+ {
+ evictOne();
+ }
+ }
+
+ private void evictOne()
+ {
+ long maxAge = -1;
+ int ordinal = -1;
+
+ for (ReuseProbability prob : ReuseProbability.values())
+ {
+ if (!lists[prob.ordinal()].isEmpty())
+ {
+ long age = lists[prob.ordinal()].tail().getAge();
+ if (maxAge < age)
+ {
+ maxAge = age;
+ ordinal = prob.ordinal();
+ }
+ }
+ }
+
+ remove(lists[ordinal].tail().getSQL());
+ }
+
+ public CachedPreparedStatement remove(String sql)
+ {
+ CachedPreparedStatement result = lookup.remove(sql);
+ if (result == null)
+ {
+ return null;
+ }
+
+ lists[result.getProbability().ordinal()].remove(result);
+ return result;
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private class CacheList
+ {
+ private CachedPreparedStatement first;
+
+ private CachedPreparedStatement last;
+
+ public CacheList()
+ {
+ }
+
+ public void add(CachedPreparedStatement s)
+ {
+ if (first == null)
+ {
+ first = s;
+ last = s;
+ s.previous = null;
+ s.next = null;
+ }
+ else
+ {
+ first.previous = s;
+ s.next = first;
+ first = s;
+ }
+ }
+
+ public void remove(CachedPreparedStatement s)
+ {
+ if (s == first)
+ {
+ first = s.next;
+ }
+
+ if (s.next != null)
+ {
+ s.next.previous = s.previous;
+ }
+
+ if (s == last)
+ {
+ last = s.previous;
+ }
+
+ if (s.previous != null)
+ {
+ s.previous.next = s.next;
+ }
+
+ s.previous = null;
+ s.next = null;
+ }
+
+ public CachedPreparedStatement tail()
+ {
+ return last;
+ }
+
+ public boolean isEmpty()
+ {
+ return first == null;
+ }
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ private static final class CachedPreparedStatement
+ {
+ private long timeStamp;
+
+ private String sql;
+
+ private ReuseProbability probability;
+
+ private PreparedStatement statement;
+
+ /**
+ * DL field
+ */
+ private CachedPreparedStatement previous;
+
+ /**
+ * DL field
+ */
+ private CachedPreparedStatement next;
+
+ public CachedPreparedStatement(String sql, ReuseProbability prob, PreparedStatement stmt)
+ {
+ this.sql = sql;
+ probability = prob;
+ statement = stmt;
+ timeStamp = System.currentTimeMillis();
+ }
+
+ public PreparedStatement getPreparedStatement()
+ {
+ return statement;
+ }
+
+ public long getAge()
+ {
+ long currentTime = System.currentTimeMillis();
+ return (currentTime - timeStamp) * probability.ordinal();
+ }
+
+ public void touch()
+ {
+ timeStamp = System.currentTimeMillis();
+ }
+
+ public String getSQL()
+ {
+ return sql;
+ }
+
+ public ReuseProbability getProbability()
+ {
+ return probability;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java
new file mode 100644
index 0000000000..803958428d
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/StringIDHandler.java
@@ -0,0 +1,249 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.mapping.CoreTypeMappings;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class StringIDHandler extends Lifecycle implements IIDHandler
+{
+ public static final Set OBJECT_ID_TYPES = Collections.singleton(CDOID.ObjectType.STRING);
+
+ public static final CDOID MIN = CDOID.NULL;
+
+ public static final CDOID MAX = create(Long.toString(Long.MAX_VALUE));
+
+ private DBStore store;
+
+ private long lastObjectID = 0;
+
+ private long nextLocalObjectID = Long.MAX_VALUE;
+
+ public StringIDHandler(DBStore store)
+ {
+ this.store = store;
+ }
+
+ public DBStore getStore()
+ {
+ return store;
+ }
+
+ public int compare(CDOID id1, CDOID id2)
+ {
+ if (id1.getType() == CDOID.Type.OBJECT && id2.getType() == CDOID.Type.OBJECT)
+ {
+ return Long.valueOf(value(id1)).compareTo(Long.valueOf(value(id2)));
+ }
+
+ return id1.compareTo(id2);
+ }
+
+ public DBType getDBType()
+ {
+ return DBType.VARCHAR;
+ }
+
+ public Set getObjectIDTypes()
+ {
+ return OBJECT_ID_TYPES;
+ }
+
+ public CDOID createCDOID(String val)
+ {
+ return create(val);
+ }
+
+ public synchronized CDOID getLastObjectID()
+ {
+ return CDOIDUtil.createString("" + lastObjectID);
+ }
+
+ public synchronized void setLastObjectID(CDOID lastObjectID)
+ {
+ this.lastObjectID = Long.parseLong(value(lastObjectID));
+ }
+
+ public void adjustLastObjectID(CDOID maxID)
+ {
+ // TODO: implement StringIDHandler.adjustLastObjectID(maxID)
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized CDOID getNextLocalObjectID()
+ {
+ return CDOIDUtil.createString("" + nextLocalObjectID);
+ }
+
+ public synchronized void setNextLocalObjectID(CDOID nextLocalObjectID)
+ {
+ this.nextLocalObjectID = Long.parseLong(value(nextLocalObjectID));
+ }
+
+ public synchronized CDOID getNextCDOID(CDORevision revision)
+ {
+ if (revision.getBranch().isLocal())
+ {
+ return CDOIDUtil.createString("" + nextLocalObjectID--);
+ }
+
+ return CDOIDUtil.createString("" + ++lastObjectID);
+ }
+
+ public boolean isLocalCDOID(CDOID id)
+ {
+ if (id.getType() == CDOID.Type.OBJECT)
+ {
+ return Long.parseLong(value(id)) > nextLocalObjectID;
+ }
+
+ return false;
+ }
+
+ public ITypeMapping getObjectTypeMapping()
+ {
+ return new CoreTypeMappings.TMObject();
+ }
+
+ public void appendCDOID(StringBuilder builder, CDOID id)
+ {
+ builder.append("'");
+ builder.append(value(id));
+ builder.append("'");
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException
+ {
+ setCDOID(stmt, column, id, CDOBranchPoint.INVALID_DATE);
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException
+ {
+ String value = value(id);
+ stmt.setString(column, value == null || value.length() == 0 ? "0" : value);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException
+ {
+ String id = resultSet.getString(column);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException
+ {
+ String id = resultSet.getString(name);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getMinCDOID()
+ {
+ return MIN;
+ }
+
+ public CDOID getMaxCDOID()
+ {
+ return MAX;
+ }
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime)
+ {
+ return create(uri);
+ }
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id)
+ {
+ return value(id);
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ // Do nothing
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ // Do nothing
+ }
+
+ private static CDOID create(String id)
+ {
+ if (id == null)
+ {
+ return null;
+ }
+
+ int length = id.length();
+ if (length == 0)
+ {
+ return null;
+ }
+
+ char firstChar = id.charAt(0);
+ if (length == 1 && firstChar == '0')
+ {
+ return null;
+ }
+
+ if (Character.isDigit(firstChar))
+ {
+ long value = Long.parseLong(id);
+ if (value < 0)
+ {
+ throw new IllegalArgumentException("Illegal ID value: " + id);
+ }
+
+ return CDOIDUtil.createString(id);
+ }
+
+ return CDOIDUtil.createExternal(id);
+ }
+
+ private static String value(CDOID id)
+ {
+ return CDOIDUtil.getString(id);
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java
new file mode 100644
index 0000000000..088cf83c62
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java
@@ -0,0 +1,237 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOID.ObjectType;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.protocol.CDODataInput;
+import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.mapping.CoreTypeMappings;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public class UUIDHandler extends Lifecycle implements IIDHandler
+{
+ public static final Set OBJECT_ID_TYPES = Collections.singleton(ObjectType.UUID);
+
+ private static final char NULL_CHAR = '0';
+
+ private static final String NULL_STRING = Character.toString(NULL_CHAR);
+
+ private static final char INTERNAL_CHAR = '@';
+
+ private static final String INTERNAL_STRING = Character.toString(INTERNAL_CHAR);
+
+ private DBStore store;
+
+ public UUIDHandler(DBStore store)
+ {
+ this.store = store;
+ }
+
+ public DBStore getStore()
+ {
+ return store;
+ }
+
+ public int compare(CDOID id1, CDOID id2)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public DBType getDBType()
+ {
+ return DBType.VARCHAR;
+ }
+
+ public Set getObjectIDTypes()
+ {
+ return OBJECT_ID_TYPES;
+ }
+
+ public CDOID createCDOID(String val)
+ {
+ return create(INTERNAL_STRING + val);
+ }
+
+ public synchronized CDOID getLastObjectID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized void setLastObjectID(CDOID lastObjectID)
+ {
+ // Do nothing
+ }
+
+ public void adjustLastObjectID(CDOID maxID)
+ {
+ // Do nothing
+ }
+
+ public synchronized CDOID getNextLocalObjectID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public synchronized void setNextLocalObjectID(CDOID nextLocalObjectID)
+ {
+ // Do nothing
+ }
+
+ public synchronized CDOID getNextCDOID(CDORevision revision)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isLocalCDOID(CDOID id)
+ {
+ return false;
+ }
+
+ public ITypeMapping getObjectTypeMapping()
+ {
+ return new CoreTypeMappings.TMObject();
+ }
+
+ public void appendCDOID(StringBuilder builder, CDOID id)
+ {
+ builder.append("'");
+ builder.append(value(id));
+ builder.append("'");
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id) throws SQLException
+ {
+ setCDOID(stmt, column, id, CDOBranchPoint.INVALID_DATE);
+ }
+
+ public void setCDOID(PreparedStatement stmt, int column, CDOID id, long commitTime) throws SQLException
+ {
+ stmt.setString(column, value(id));
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, int column) throws SQLException
+ {
+ String id = resultSet.getString(column);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getCDOID(ResultSet resultSet, String name) throws SQLException
+ {
+ String id = resultSet.getString(name);
+ if (resultSet.wasNull())
+ {
+ return null;
+ }
+
+ return create(id);
+ }
+
+ public CDOID getMinCDOID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOID getMaxCDOID()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public CDOID mapURI(IDBStoreAccessor accessor, String uri, long commitTime)
+ {
+ return CDOIDUtil.createExternal(uri);
+ }
+
+ public String unmapURI(IDBStoreAccessor accessor, CDOID id)
+ {
+ return CDOIDUtil.getString(id);
+ }
+
+ public void rawExport(Connection connection, CDODataOutput out, long fromCommitTime, long toCommitTime)
+ throws IOException
+ {
+ // Do nothing
+ }
+
+ public void rawImport(Connection connection, CDODataInput in, long fromCommitTime, long toCommitTime,
+ OMMonitor monitor) throws IOException
+ {
+ // Do nothing
+ }
+
+ private static CDOID create(String id)
+ {
+ if (id == null)
+ {
+ return null;
+ }
+
+ int length = id.length();
+ if (length == 0)
+ {
+ return null;
+ }
+
+ char firstChar = id.charAt(0);
+ if (length == 1 && firstChar == NULL_CHAR)
+ {
+ return null;
+ }
+
+ if (firstChar == INTERNAL_CHAR)
+ {
+ byte[] bytes = CDOIDUtil.decodeUUID(id.substring(1));
+ return CDOIDUtil.createUUID(bytes);
+ }
+
+ return CDOIDUtil.createExternal(id);
+ }
+
+ private static String value(CDOID id)
+ {
+ if (CDOIDUtil.isNull(id))
+ {
+ return NULL_STRING;
+ }
+
+ if (id.isExternal())
+ {
+ return CDOIDUtil.getString(id);
+ }
+
+ return INTERNAL_STRING + id.toURIFragment();
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java
new file mode 100644
index 0000000000..d45d95c8f7
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/bundle/OM.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.internal.db.bundle;
+
+import org.eclipse.net4j.util.om.OMBundle;
+import org.eclipse.net4j.util.om.OMPlatform;
+import org.eclipse.net4j.util.om.OSGiActivator;
+import org.eclipse.net4j.util.om.log.OMLogger;
+import org.eclipse.net4j.util.om.trace.OMTracer;
+
+/**
+ * The Operations & Maintenance class of this bundle.
+ *
+ * @author Eike Stepper
+ */
+public abstract class OM
+{
+ public static final String BUNDLE_ID = "org.eclipse.emf.cdo.server.db"; //$NON-NLS-1$
+
+ public static final OMBundle BUNDLE = OMPlatform.INSTANCE.bundle(BUNDLE_ID, OM.class);
+
+ public static final OMTracer DEBUG = BUNDLE.tracer("debug"); //$NON-NLS-1$
+
+ public static final OMLogger LOG = BUNDLE.logger();
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class Activator extends OSGiActivator
+ {
+ public Activator()
+ {
+ super(BUNDLE);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/jdbc/WrappedPreparedStatement.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/jdbc/WrappedPreparedStatement.java
new file mode 100644
index 0000000000..d4781d312a
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/jdbc/WrappedPreparedStatement.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db.jdbc;
+
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import java.sql.PreparedStatement;
+import java.text.MessageFormat;
+
+/**
+ * Wrapper for a prepared statement that is cleaned up when it is cached in a WeakReferenceCache and gc'd. Note that
+ * this is just a wrapper with access to its wrapped object. There's no interface delegation, because the interface
+ * delegation would also put the necessity to wrap resultSets and maybe even more, which seems to much overkill for a
+ * simple internal implementation.
+ *
+ * @author Stefan Winkler
+ */
+public class WrappedPreparedStatement
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, WrappedPreparedStatement.class);
+
+ private PreparedStatement wrappedStatement;
+
+ public WrappedPreparedStatement(PreparedStatement ps)
+ {
+ wrappedStatement = ps;
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Wrapping Statement: {0}", wrappedStatement); //$NON-NLS-1$
+ }
+ }
+
+ public PreparedStatement getWrappedStatement()
+ {
+ return wrappedStatement;
+ }
+
+ public PreparedStatement unwrapStatement()
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("UnWrapping Statement: {0}", wrappedStatement); //$NON-NLS-1$
+ }
+
+ PreparedStatement result = wrappedStatement;
+ wrappedStatement = null;
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("Wrapped[{0}]", wrappedStatement); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void finalize() throws Throwable
+ {
+ if (wrappedStatement != null)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Closing statement: {0}", wrappedStatement); //$NON-NLS-1$
+ }
+
+ DBUtil.close(wrappedStatement);
+ wrappedStatement = null;
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java
new file mode 100644
index 0000000000..d9c76b325f
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java
@@ -0,0 +1,641 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - major refactoring
+ * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444
+ * Stefan Winkler - Bug 282976: [DB] Influence Mappings through EAnnotations
+ * Kai Schlamp - Bug 284680 - [DB] Provide annotation to bypass ClassMapping
+ * Stefan Winkler - maintenance
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.model.CDOModelUtil;
+import org.eclipse.emf.cdo.common.model.EMFUtil;
+import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
+import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext;
+import org.eclipse.emf.cdo.server.StoreThreadLocal;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IMetaDataManager;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.DBAnnotation;
+import org.eclipse.emf.cdo.server.internal.db.ObjectIDIterator;
+import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
+import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBSchema;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.collection.CloseableIterator;
+import org.eclipse.net4j.util.lifecycle.Lifecycle;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.ENamedElement;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.FeatureMapUtil;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * This abstract base class implements those methods which are most likely common to most mapping strategies. It can be
+ * used to derive custom mapping strategy implementation.
+ *
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public abstract class AbstractMappingStrategy extends Lifecycle implements IMappingStrategy
+{
+ // --------- database name generation strings --------------
+ protected static final String NAME_SEPARATOR = "_"; //$NON-NLS-1$
+
+ protected static final String TYPE_PREFIX_FEATURE = "F"; //$NON-NLS-1$
+
+ protected static final String TYPE_PREFIX_CLASS = "C"; //$NON-NLS-1$
+
+ protected static final String TYPE_PREFIX_PACKAGE = "P"; //$NON-NLS-1$
+
+ protected static final String GENERAL_PREFIX = "X"; //$NON-NLS-1$
+
+ protected static final String GENERAL_SUFFIX = "0"; //$NON-NLS-1$
+
+ /**
+ * Prefix for unsettable feature helper columns
+ */
+ protected static final String CDO_SET_PREFIX = "cdo_set_"; //$NON-NLS-1$
+
+ protected static final String FEATURE_TABLE_SUFFIX = "_list"; //$NON-NLS-1$
+
+ private IDBStore store;
+
+ private Map properties;
+
+ private ConcurrentMap classMappings;
+
+ private boolean allClassMappingsCreated;
+
+ public AbstractMappingStrategy()
+ {
+ classMappings = new ConcurrentHashMap();
+ }
+
+ // -- property related methods -----------------------------------------
+
+ public synchronized Map getProperties()
+ {
+ if (properties == null)
+ {
+ properties = new HashMap();
+ }
+
+ return properties;
+ }
+
+ public synchronized void setProperties(Map properties)
+ {
+ this.properties = properties;
+ }
+
+ private int getMaxTableNameLength()
+ {
+ String value = getProperties().get(PROP_MAX_TABLE_NAME_LENGTH);
+ return value == null ? store.getDBAdapter().getMaxTableNameLength() : Integer.valueOf(value);
+ }
+
+ private int getMaxFieldNameLength()
+ {
+ String value = getProperties().get(PROP_MAX_FIELD_NAME_LENGTH);
+ return value == null ? store.getDBAdapter().getMaxFieldNameLength() : Integer.valueOf(value);
+ }
+
+ private boolean isQualifiedNames()
+ {
+ String value = getProperties().get(PROP_QUALIFIED_NAMES);
+ return value == null ? false : Boolean.valueOf(value);
+ }
+
+ private boolean isForceNamesWithID()
+ {
+ String value = getProperties().get(PROP_FORCE_NAMES_WITH_ID);
+ return value == null ? false : Boolean.valueOf(value);
+ }
+
+ private String getTableNamePrefix()
+ {
+ String value = getProperties().get(PROP_TABLE_NAME_PREFIX);
+ return StringUtil.safe(value);
+ }
+
+ // -- getters and setters ----------------------------------------------
+
+ public final IDBStore getStore()
+ {
+ return store;
+ }
+
+ public final void setStore(IDBStore dbStore)
+ {
+ checkInactive();
+ store = dbStore;
+ }
+
+ protected final IMetaDataManager getMetaDataManager()
+ {
+ return getStore().getMetaDataManager();
+ }
+
+ // -- object id related methods ----------------------------------------
+
+ public void handleRevisions(IDBStoreAccessor accessor, EClass eClass, CDOBranch branch, long timeStamp,
+ boolean exactTime, CDORevisionHandler handler)
+ {
+ if (eClass == null)
+ {
+ Collection values = getClassMappings().values();
+ for (IClassMapping mapping : values)
+ {
+ mapping.handleRevisions(accessor, branch, timeStamp, exactTime, handler);
+ }
+ }
+ else
+ {
+ IClassMapping classMapping = getClassMapping(eClass);
+ classMapping.handleRevisions(accessor, branch, timeStamp, exactTime, handler);
+ }
+ }
+
+ public Set readChangeSet(IDBStoreAccessor accessor, OMMonitor monitor, CDOChangeSetSegment[] segments)
+ {
+ Set result = new HashSet();
+ Collection classMappings = getClassMappings().values();
+
+ monitor.begin(classMappings.size());
+
+ try
+ {
+ for (IClassMapping mapping : classMappings)
+ {
+ Async async = monitor.forkAsync();
+
+ try
+ {
+ Set ids = mapping.readChangeSet(accessor, segments);
+ result.addAll(ids);
+ }
+ finally
+ {
+ async.stop();
+ }
+ }
+
+ return result;
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public CloseableIterator readObjectIDs(IDBStoreAccessor accessor)
+ {
+ Collection classes = getClassesWithObjectInfo();
+ final Iterator classIt = classes.iterator();
+
+ return new ObjectIDIterator(this, accessor)
+ {
+ private PreparedStatement currentStatement;
+
+ @Override
+ protected ResultSet getNextResultSet()
+ {
+ while (classIt.hasNext())
+ {
+ EClass eClass = classIt.next();
+ IClassMapping mapping = getClassMapping(eClass);
+ currentStatement = mapping.createObjectIDStatement(getAccessor());
+
+ ResultSet resultSet = null;
+
+ try
+ {
+ resultSet = currentStatement.executeQuery();
+ return resultSet;
+ }
+ catch (Exception ex)
+ {
+ DBUtil.close(resultSet); // only on error
+ releaseCurrentStatement();
+ throw new DBException(ex);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void closeCurrentResultSet()
+ {
+ super.closeCurrentResultSet();
+ releaseCurrentStatement();
+ }
+
+ private void releaseCurrentStatement()
+ {
+ IPreparedStatementCache statementCache = getAccessor().getStatementCache();
+ statementCache.releasePreparedStatement(currentStatement);
+ currentStatement = null;
+ }
+ };
+ }
+
+ protected abstract Collection getClassesWithObjectInfo();
+
+ // -- database name demangling methods ---------------------------------
+
+ public String getTableName(ENamedElement element)
+ {
+ String name = null;
+ String typePrefix = null;
+
+ if (element instanceof EClass)
+ {
+ typePrefix = TYPE_PREFIX_CLASS;
+ name = DBAnnotation.TABLE_NAME.getValue(element);
+ if (name == null)
+ {
+ name = isQualifiedNames() ? EMFUtil.getQualifiedName((EClass)element, NAME_SEPARATOR) : element.getName();
+ }
+ }
+ else if (element instanceof EPackage)
+ {
+ typePrefix = TYPE_PREFIX_PACKAGE;
+ name = DBAnnotation.TABLE_NAME.getValue(element);
+ if (name == null)
+ {
+ name = isQualifiedNames() ? EMFUtil.getQualifiedName((EPackage)element, NAME_SEPARATOR) : element.getName();
+ }
+ }
+ else
+ {
+ throw new ImplementationError("Unknown element: " + element); //$NON-NLS-1$
+ }
+
+ String prefix = getTableNamePrefix();
+ if (prefix.length() != 0 && !prefix.endsWith(NAME_SEPARATOR))
+ {
+ prefix += NAME_SEPARATOR;
+ }
+
+ return getName(prefix + name, typePrefix + getUniqueID(element), getMaxTableNameLength());
+ }
+
+ public String getTableName(EClass eClass, EStructuralFeature feature)
+ {
+ String name = DBAnnotation.TABLE_NAME.getValue(eClass);
+ if (name == null)
+ {
+ name = isQualifiedNames() ? EMFUtil.getQualifiedName(eClass, NAME_SEPARATOR) : eClass.getName();
+ }
+
+ name += NAME_SEPARATOR;
+ name += feature.getName();
+ name += FEATURE_TABLE_SUFFIX;
+
+ String prefix = getTableNamePrefix();
+ if (prefix.length() != 0 && !prefix.endsWith(NAME_SEPARATOR))
+ {
+ prefix += NAME_SEPARATOR;
+ }
+
+ return getName(prefix + name, TYPE_PREFIX_FEATURE + getUniqueID(feature), getMaxTableNameLength());
+ }
+
+ public String getFieldName(EStructuralFeature feature)
+ {
+ String name = DBAnnotation.COLUMN_NAME.getValue(feature);
+ if (name == null)
+ {
+ name = getName(feature.getName(), TYPE_PREFIX_FEATURE + getUniqueID(feature), getMaxFieldNameLength());
+ }
+
+ return name;
+ }
+
+ public String getUnsettableFieldName(EStructuralFeature feature)
+ {
+ String name = DBAnnotation.COLUMN_NAME.getValue(feature);
+ if (name != null)
+ {
+ return CDO_SET_PREFIX + name;
+ }
+
+ return getName(CDO_SET_PREFIX + feature.getName(), TYPE_PREFIX_FEATURE + getUniqueID(feature),
+ getMaxFieldNameLength());
+ }
+
+ private String getName(String name, String suffix, int maxLength)
+ {
+ if (!store.getDBAdapter().isValidFirstChar(name.charAt(0)))
+ {
+ name = GENERAL_PREFIX + name;
+ }
+
+ boolean forceNamesWithID = isForceNamesWithID();
+ if (!forceNamesWithID && store.getDBAdapter().isReservedWord(name))
+ {
+ name = name + GENERAL_SUFFIX;
+ }
+
+ if (name.length() > maxLength || forceNamesWithID)
+ {
+ suffix = NAME_SEPARATOR + suffix.replace('-', 'S');
+ int length = Math.min(name.length(), maxLength - suffix.length());
+ name = name.substring(0, length) + suffix;
+ }
+
+ return name;
+ }
+
+ private String getUniqueID(ENamedElement element)
+ {
+ long timeStamp;
+ CommitContext commitContext = StoreThreadLocal.getCommitContext();
+ if (commitContext != null)
+ {
+ timeStamp = commitContext.getBranchPoint().getTimeStamp();
+ }
+ else
+ {
+ // This happens outside a commit, i.e. at system init time.
+ // Ensure that resulting ext refs are not replicated!
+ timeStamp = CDOBranchPoint.INVALID_DATE;
+
+ // timeStamp = getStore().getRepository().getTimeStamp();
+ }
+
+ CDOID result = getMetaDataManager().getMetaID(element, timeStamp);
+
+ StringBuilder builder = new StringBuilder();
+ CDOIDUtil.write(builder, result);
+ return builder.toString();
+ }
+
+ // -- factories for mapping of classes, values, lists ------------------
+
+ public void createMapping(Connection connection, InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
+ {
+ Async async = null;
+ monitor.begin();
+
+ try
+ {
+ async = monitor.forkAsync();
+
+ try
+ {
+ mapPackageUnits(packageUnits, connection, false);
+ }
+ finally
+ {
+ if (async != null)
+ {
+ async.stop();
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ public void removeMapping(Connection connection, InternalCDOPackageUnit[] packageUnits)
+ {
+ mapPackageUnits(packageUnits, connection, true);
+ }
+
+ private void mapPackageUnits(InternalCDOPackageUnit[] packageUnits, Connection connection, boolean unmap)
+ {
+ if (packageUnits != null && packageUnits.length != 0)
+ {
+ for (InternalCDOPackageUnit packageUnit : packageUnits)
+ {
+ mapPackageInfos(packageUnit.getPackageInfos(), connection, unmap);
+ }
+ }
+ }
+
+ private void mapPackageInfos(InternalCDOPackageInfo[] packageInfos, Connection connection, boolean unmap)
+ {
+ boolean supportingEcore = getStore().getRepository().isSupportingEcore();
+ for (InternalCDOPackageInfo packageInfo : packageInfos)
+ {
+ EPackage ePackage = packageInfo.getEPackage();
+ if (!CDOModelUtil.isCorePackage(ePackage) || supportingEcore)
+ {
+ mapClasses(connection, unmap, EMFUtil.getPersistentClasses(ePackage));
+ }
+ }
+ }
+
+ private void mapClasses(Connection connection, boolean unmap, EClass... eClasses)
+ {
+ for (EClass eClass : eClasses)
+ {
+ if (!(eClass.isInterface() || eClass.isAbstract()))
+ {
+ String mappingAnnotation = DBAnnotation.TABLE_MAPPING.getValue(eClass);
+
+ // TODO Maybe we should explicitly report unknown values of the annotation
+ if (mappingAnnotation != null && mappingAnnotation.equalsIgnoreCase(DBAnnotation.TABLE_MAPPING_NONE))
+ {
+ continue;
+ }
+
+ if (!unmap)
+ {
+ // TODO Bugzilla 296087: Before we go ahead with creation, we should check if it's already there
+ IClassMapping mapping = createClassMapping(eClass);
+ getStore().getDBAdapter().createTables(mapping.getDBTables(), connection);
+ }
+ else
+ {
+ IClassMapping mapping = removeClassMapping(eClass);
+ getStore().getDBAdapter().dropTables(mapping.getDBTables(), connection);
+ }
+ }
+ }
+ }
+
+ private IClassMapping createClassMapping(EClass eClass)
+ {
+ IClassMapping mapping = doCreateClassMapping(eClass);
+ if (mapping != null)
+ {
+ classMappings.put(eClass, mapping);
+ }
+
+ return mapping;
+ }
+
+ private IClassMapping removeClassMapping(EClass eClass)
+ {
+ IClassMapping mapping = classMappings.get(eClass);
+ if (mapping != null)
+ {
+ IDBSchema schema = getStore().getDBSchema();
+ for (IDBTable table : mapping.getDBTables())
+ {
+ schema.removeTable(table.getName());
+ }
+ classMappings.remove(eClass);
+ }
+ return mapping;
+ }
+
+ protected abstract IClassMapping doCreateClassMapping(EClass eClass);
+
+ public final IClassMapping getClassMapping(EClass eClass)
+ {
+ if (!isMapped(eClass))
+ {
+ throw new IllegalArgumentException("Class is not mapped: " + eClass);
+ }
+
+ // Try without synchronization first; this will almost always succeed, so it avoids the
+ // performance penalty of syncing in the majority of cases
+ IClassMapping result = classMappings.get(eClass);
+ if (result == null)
+ {
+ // Synchronize on the classMappings to prevent concurrent invocations of createClassMapping
+ // (Synchronizing on the eClass allows for more concurrency, but is risky because application
+ // code may be syncing on the eClass also.)
+ synchronized (classMappings)
+ {
+ // Check again, because other thread may have just added the mapping
+ result = classMappings.get(eClass);
+ if (result == null)
+ {
+ result = createClassMapping(eClass);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public final Map getClassMappings()
+ {
+ return getClassMappings(true);
+ }
+
+ public final Map getClassMappings(boolean createOnDemand)
+ {
+ return doGetClassMappings(createOnDemand);
+ }
+
+ public final Map doGetClassMappings(boolean createOnDemand)
+ {
+ if (createOnDemand)
+ {
+ synchronized (classMappings)
+ {
+ if (!allClassMappingsCreated)
+ {
+ createAllClassMappings();
+ allClassMappingsCreated = true;
+ }
+ }
+ }
+
+ return classMappings;
+ }
+
+ private void createAllClassMappings()
+ {
+ InternalRepository repository = (InternalRepository)getStore().getRepository();
+ InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
+ for (InternalCDOPackageInfo packageInfo : packageRegistry.getPackageInfos())
+ {
+ if (!packageInfo.isSystemPackage())
+ {
+ for (EClassifier eClassifier : packageInfo.getEPackage().getEClassifiers())
+ {
+ if (eClassifier instanceof EClass)
+ {
+ EClass eClass = (EClass)eClassifier;
+ if (isMapped(eClass))
+ {
+ getClassMapping(eClass); // Get or create it
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected abstract boolean isMapped(EClass eClass);
+
+ public ITypeMapping createValueMapping(EStructuralFeature feature)
+ {
+ ITypeMapping.Provider provider = getTypeMappingProvider();
+ return provider.createTypeMapping(this, feature);
+ }
+
+ protected ITypeMapping.Provider getTypeMappingProvider()
+ {
+ return ITypeMapping.Provider.INSTANCE;
+ }
+
+ public final IListMapping createListMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ checkArg(feature.isMany(), "Only many-valued features allowed"); //$NON-NLS-1$
+ IListMapping mapping = doCreateListMapping(containingClass, feature);
+ return mapping;
+ }
+
+ public final IListMapping createFeatureMapMapping(EClass containingClass, EStructuralFeature feature)
+ {
+ checkArg(FeatureMapUtil.isFeatureMap(feature), "Only FeatureMaps allowed"); //$NON-NLS-1$
+ IListMapping mapping = doCreateFeatureMapMapping(containingClass, feature);
+ return mapping;
+ }
+
+ public abstract IListMapping doCreateListMapping(EClass containingClass, EStructuralFeature feature);
+
+ public abstract IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature);
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java
new file mode 100644
index 0000000000..f5baf6a4c6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/CoreTypeMappings.java
@@ -0,0 +1,882 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - bug 271444: [DB] Multiple refactorings
+ * Stefan Winkler - bug 275303: [DB] DBStore does not handle BIG_INTEGER and BIG_DECIMAL
+ * Kai Schlamp - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 282976: [DB] Influence Mappings through EAnnotations
+ * Stefan Winkler - bug 285270: [DB] Support XSD based models
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.lob.CDOBlob;
+import org.eclipse.emf.cdo.common.lob.CDOClob;
+import org.eclipse.emf.cdo.common.lob.CDOLobUtil;
+import org.eclipse.emf.cdo.common.revision.CDORevisionData;
+import org.eclipse.emf.cdo.etypes.EtypesPackage;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.AbstractTypeMapping;
+import org.eclipse.emf.cdo.server.db.mapping.AbstractTypeMappingFactory;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.util.HexUtil;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+
+import org.eclipse.emf.common.util.Enumerator;
+import org.eclipse.emf.ecore.EDataType;
+import org.eclipse.emf.ecore.EEnum;
+import org.eclipse.emf.ecore.EEnumLiteral;
+import org.eclipse.emf.ecore.EFactory;
+import org.eclipse.emf.ecore.EcorePackage;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * This is a default implementation for the {@link ITypeMapping} interface which provides default behavor for all common
+ * types.
+ *
+ * @author Eike Stepper
+ */
+public class CoreTypeMappings
+{
+ public static final String ID_PREFIX = "org.eclipse.emf.cdo.server.db.CoreTypeMappings";
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMEnum extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Enum",
+ EcorePackage.eINSTANCE.getEEnum(), DBType.INTEGER));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ // see Bug 271941
+ return resultSet.getInt(getField().getName());
+ // EEnum type = (EEnum)getFeature().getEType();
+ // int value = resultSet.getInt(column);
+ // return type.getEEnumLiteral(value);
+ }
+
+ @Override
+ protected Object getDefaultValue()
+ {
+ EEnum eenum = (EEnum)getFeature().getEType();
+
+ String defaultValueLiteral = getFeature().getDefaultValueLiteral();
+ if (defaultValueLiteral != null)
+ {
+ EEnumLiteral literal = eenum.getEEnumLiteralByLiteral(defaultValueLiteral);
+ return literal.getValue();
+ }
+
+ Enumerator enumerator = (Enumerator)eenum.getDefaultValue();
+ return enumerator.getValue();
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMEnum();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMString extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".StringVarchar", EcorePackage.eINSTANCE.getEString(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".StringLongVarchar", EcorePackage.eINSTANCE.getEString(), DBType.LONGVARCHAR));
+
+ public static final Factory FACTORY_CLOB = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".StringClob",
+ EcorePackage.eINSTANCE.getEString(), DBType.CLOB));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getString(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMString();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMBlob extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BlobStream", EtypesPackage.eINSTANCE.getBlob(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BlobStreamLongVarchar", EtypesPackage.eINSTANCE.getBlob(), DBType.LONGVARCHAR));
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ CDOBlob blob = (CDOBlob)value;
+ stmt.setString(index, HexUtil.bytesToHex(blob.getID()) + "-" + blob.getSize());
+ }
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String str = resultSet.getString(getField().getName());
+ if (str == null)
+ {
+ return null;
+ }
+
+ int pos = str.indexOf('-');
+
+ byte[] id = HexUtil.hexToBytes(str.substring(0, pos));
+ long size = Long.parseLong(str.substring(pos + 1));
+ return CDOLobUtil.createBlob(id, size);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMBlob();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMClob extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".ClobStream", EtypesPackage.eINSTANCE.getClob(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".ClobStreamLongVarchar", EtypesPackage.eINSTANCE.getClob(), DBType.LONGVARCHAR));
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ CDOClob clob = (CDOClob)value;
+ stmt.setString(index, HexUtil.bytesToHex(clob.getID()) + "-" + clob.getSize());
+ }
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String str = resultSet.getString(getField().getName());
+ if (str == null)
+ {
+ return null;
+ }
+
+ int pos = str.indexOf('-');
+
+ byte[] id = HexUtil.hexToBytes(str.substring(0, pos));
+ long size = Long.parseLong(str.substring(pos + 1));
+ return CDOLobUtil.createClob(id, size);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMClob();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMShort extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Short",
+ EcorePackage.eINSTANCE.getEShort(), DBType.SMALLINT));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".ShortObject", EcorePackage.eINSTANCE.getEShortObject(), DBType.SMALLINT));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getShort(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMShort();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMObject extends AbstractTypeMapping
+ {
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ CDOID id = idHandler.getCDOID(resultSet, getField().getName());
+
+ if (id == null && getFeature().isUnsettable())
+ {
+ return CDORevisionData.NIL;
+ }
+
+ return id;
+ }
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ idHandler.setCDOID(stmt, index, (CDOID)value);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMObject();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMLong extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Long",
+ EcorePackage.eINSTANCE.getELong(), DBType.BIGINT));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(
+ ID_PREFIX + ".LongObject", EcorePackage.eINSTANCE.getELongObject(), DBType.BIGINT));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getLong(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMLong();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMInteger extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Integer",
+ EcorePackage.eINSTANCE.getEInt(), DBType.INTEGER));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".IntegerObject", EcorePackage.eINSTANCE.getEIntegerObject(), DBType.INTEGER));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getInt(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMInteger();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMFloat extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Float",
+ EcorePackage.eINSTANCE.getEFloat(), DBType.FLOAT));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".FloatObject", EcorePackage.eINSTANCE.getEFloatObject(), DBType.FLOAT));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getFloat(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMFloat();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMDouble extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Double",
+ EcorePackage.eINSTANCE.getEDouble(), DBType.DOUBLE));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".DoubleObject", EcorePackage.eINSTANCE.getEDoubleObject(), DBType.DOUBLE));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getDouble(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMDouble();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMDate2Timestamp extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Timestamp",
+ EcorePackage.eINSTANCE.getEDate(), DBType.TIMESTAMP));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getTimestamp(getField().getName());
+ }
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ stmt.setTimestamp(index, new Timestamp(((Date)value).getTime()));
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMDate2Timestamp();
+ }
+ }
+ }
+
+ /**
+ * @author Heiko Ahlig
+ */
+ public static class TMDate2Date extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Date",
+ EcorePackage.eINSTANCE.getEDate(), DBType.DATE));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getDate(getField().getName(), Calendar.getInstance());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMDate2Date();
+ }
+ }
+ }
+
+ /**
+ * @author Heiko Ahlig
+ */
+ public static class TMDate2Time extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Time",
+ EcorePackage.eINSTANCE.getEDate(), DBType.TIME));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getTime(getField().getName(), Calendar.getInstance());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMDate2Time();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMCharacter extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Character",
+ EcorePackage.eINSTANCE.getEChar(), DBType.CHAR));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".CharacterObject", EcorePackage.eINSTANCE.getECharacterObject(), DBType.CHAR));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String str = resultSet.getString(getField().getName());
+ if (resultSet.wasNull())
+ {
+ return getFeature().isUnsettable() ? CDORevisionData.NIL : null;
+ }
+
+ return str.charAt(0);
+ }
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ stmt.setString(index, ((Character)value).toString());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMCharacter();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMByte extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Byte",
+ EcorePackage.eINSTANCE.getEByte(), DBType.SMALLINT));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(
+ ID_PREFIX + ".ByteObject", EcorePackage.eINSTANCE.getEByteObject(), DBType.SMALLINT));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getByte(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMByte();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMBytes extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".ByteArray",
+ EcorePackage.eINSTANCE.getEByteArray(), DBType.BLOB));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getBytes(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMBytes();
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TMBoolean extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".Boolean",
+ EcorePackage.eINSTANCE.getEBoolean(), DBType.BOOLEAN));
+
+ public static final Factory FACTORY_OBJECT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BooleanObject", EcorePackage.eINSTANCE.getEBooleanObject(), DBType.BOOLEAN));
+
+ public static final Factory FACTORY_SMALLINT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".Boolean_SMALLINT", EcorePackage.eINSTANCE.getEBoolean(), DBType.SMALLINT));
+
+ public static final Factory FACTORY_OBJECT_SMALLINT = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BooleanObject_SMALLINT", EcorePackage.eINSTANCE.getEBooleanObject(), DBType.SMALLINT));
+
+ @Override
+ public Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ return resultSet.getBoolean(getField().getName());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMBoolean();
+ }
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ public static class TMBigInteger extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".BigInteger",
+ EcorePackage.eINSTANCE.getEBigInteger(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BigIntegerLongVarChar", EcorePackage.eINSTANCE.getEBigInteger(), DBType.LONGVARCHAR));
+
+ @Override
+ protected Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String val = resultSet.getString(getField().getName());
+
+ if (resultSet.wasNull())
+ {
+ return getFeature().isUnsettable() ? CDORevisionData.NIL : null;
+ }
+
+ return new BigInteger(val);
+ }
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ stmt.setString(index, ((BigInteger)value).toString());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMBigInteger();
+ }
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ public static class TMBigDecimal extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".BigDecimal",
+ EcorePackage.eINSTANCE.getEBigDecimal(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".BigDecimalLongVarchar", EcorePackage.eINSTANCE.getEBigDecimal(), DBType.LONGVARCHAR));
+
+ @Override
+ protected Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String val = resultSet.getString(getField().getName());
+
+ if (resultSet.wasNull())
+ {
+ return getFeature().isUnsettable() ? CDORevisionData.NIL : null;
+ }
+
+ return new BigDecimal(val);
+ }
+
+ @Override
+ protected void doSetValue(PreparedStatement stmt, int index, Object value) throws SQLException
+ {
+ stmt.setString(index, ((BigDecimal)value).toPlainString());
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMBigDecimal();
+ }
+ }
+ }
+
+ /**
+ * @author Stefan Winkler
+ */
+ public static class TMCustom extends AbstractTypeMapping
+ {
+ public static final Factory FACTORY_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".CustomVarchar", EcorePackage.eINSTANCE.getEDataType(), DBType.VARCHAR));
+
+ public static final Factory FACTORY_LONG_VARCHAR = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX
+ + ".CustomLongVarchar", EcorePackage.eINSTANCE.getEDataType(), DBType.LONGVARCHAR));
+
+ public static final Factory FACTORY_CLOB = new Factory(TypeMappingUtil.createDescriptor(ID_PREFIX + ".CustomClob",
+ EcorePackage.eINSTANCE.getEDataType(), DBType.CLOB));
+
+ @Override
+ protected Object getResultSetValue(ResultSet resultSet) throws SQLException
+ {
+ String val = resultSet.getString(getField().getName());
+ if (resultSet.wasNull())
+ {
+ return getFeature().isUnsettable() ? CDORevisionData.NIL : null;
+ }
+
+ return val;
+ }
+
+ @Override
+ protected Object getDefaultValue()
+ {
+ Object defaultValue = getFeature().getDefaultValue();
+ if (defaultValue == null)
+ {
+ return null;
+ }
+
+ EFactory factory = getFeature().getEType().getEPackage().getEFactoryInstance();
+ return factory.convertToString((EDataType)getFeature().getEType(), defaultValue);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Factory extends AbstractTypeMappingFactory
+ {
+ public Factory(Descriptor descriptor)
+ {
+ super(descriptor);
+ }
+
+ @Override
+ public ITypeMapping create(String description) throws ProductCreationException
+ {
+ return new TMCustom();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java
new file mode 100644
index 0000000000..f47075d0d2
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingDescriptor.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Winkler - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping;
+
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+
+import org.eclipse.net4j.db.DBType;
+
+import org.eclipse.emf.ecore.EClassifier;
+
+/**
+ * @author Stefan Winkler
+ */
+public class TypeMappingDescriptor implements ITypeMapping.Descriptor
+{
+ private String id;
+
+ private String factoryType;
+
+ private EClassifier eClassifier;
+
+ private DBType dbType;
+
+ public TypeMappingDescriptor(String id, String factoryType, EClassifier eClassifier, DBType dbType)
+ {
+ this.id = id;
+ this.factoryType = factoryType;
+ this.eClassifier = eClassifier;
+ this.dbType = dbType;
+ }
+
+ public String getID()
+ {
+ return id;
+ }
+
+ public String getFactoryType()
+ {
+ return factoryType;
+ }
+
+ public EClassifier getEClassifier()
+ {
+ return eClassifier;
+ }
+
+ public DBType getDBType()
+ {
+ return dbType;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "TypeMappingDescriptor [" + factoryType + "]";
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java
new file mode 100644
index 0000000000..096e2c97a6
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingRegistry.java
@@ -0,0 +1,447 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stefan Winkler - initial API and implementation
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping;
+
+import org.eclipse.emf.cdo.common.model.CDOModelUtil;
+import org.eclipse.emf.cdo.etypes.EtypesPackage;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.DBAnnotation;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.server.internal.db.mapping.TypeMappingUtil.FactoryTypeParserException;
+import org.eclipse.emf.cdo.server.internal.db.messages.Messages;
+
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.IDBAdapter;
+import org.eclipse.net4j.util.collection.Pair;
+import org.eclipse.net4j.util.container.ContainerEvent;
+import org.eclipse.net4j.util.container.IContainerDelta;
+import org.eclipse.net4j.util.container.IContainerDelta.Kind;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.container.IPluginContainer;
+import org.eclipse.net4j.util.event.IEvent;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.factory.IFactory;
+import org.eclipse.net4j.util.factory.IFactoryKey;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EEnum;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.EcorePackage;
+
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * An implementation of both the Registry and Provider interfaces for type mappings. This class is a singleton which
+ * keeps itself in sync with the global factory registry. It reads the available factoryTypes for the type mappings
+ * product type and populates indexes which make it easier to determine and look up the correct factories for a needed
+ * type mapping.
+ *
+ * @author Stefan Winkler
+ */
+public class TypeMappingRegistry implements ITypeMapping.Registry, ITypeMapping.Provider
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, TypeMappingRegistry.class);
+
+ /**
+ * Contains a map from model types to db types which represent default mappings. (I.e., if a model element without db
+ * type annotation is encountered, this map is consulted to retrieve the default type mapping. This map is populated
+ * on a come-first basis. The first mapping for a particular {@link EClassifier} is set as default.
+ */
+ private Map classifierDefaultMapping;
+
+ /**
+ * The main TypeMapping index. For any known pair of model and db types the {@link ITypeMapping.Descriptor} is
+ * registered here.
+ */
+ private Map, ITypeMapping.Descriptor> typeMappingByTypes;
+
+ /**
+ * ID-based index. Can be used to lookup an {@link ITypeMapping.Descriptor} for a given ID.
+ */
+ private Map typeMappingsById;
+
+ /**
+ * A set of all known mapped DBTypes. This is needed for the feature map mappings.
+ */
+ private Set defaultFeatureMapDBTypes;
+
+ /**
+ * A populator which is used to keep the registry in sync with the registered factories of the
+ * {@link IManagedContainer}.
+ */
+ private RegistryPopulator populator = new RegistryPopulator();
+
+ public TypeMappingRegistry()
+ {
+ init();
+ }
+
+ public void init()
+ {
+ populator.disconnect();
+
+ defaultFeatureMapDBTypes = new HashSet();
+ typeMappingsById = new HashMap();
+ typeMappingByTypes = new HashMap, ITypeMapping.Descriptor>();
+ classifierDefaultMapping = new HashMap();
+
+ registerCoreTypeMappings();
+ populator.connect();
+ }
+
+ /**
+ * Register builtin type mapping factories
+ */
+ private void registerCoreTypeMappings()
+ {
+ IManagedContainer container = getContainer();
+ container.registerFactory(CoreTypeMappings.TMBigDecimal.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMBigDecimal.FACTORY_LONG_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMBigInteger.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMBigInteger.FACTORY_LONG_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY_SMALLINT);
+ container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMBoolean.FACTORY_OBJECT_SMALLINT);
+ container.registerFactory(CoreTypeMappings.TMByte.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMByte.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMBytes.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMCharacter.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMCharacter.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMCustom.FACTORY_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMCustom.FACTORY_CLOB);
+ container.registerFactory(CoreTypeMappings.TMCustom.FACTORY_LONG_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMDate2Date.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMDate2Time.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMDate2Timestamp.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMDouble.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMDouble.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMEnum.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMFloat.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMFloat.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMInteger.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMInteger.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMLong.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMLong.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMShort.FACTORY);
+ container.registerFactory(CoreTypeMappings.TMShort.FACTORY_OBJECT);
+ container.registerFactory(CoreTypeMappings.TMString.FACTORY_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMString.FACTORY_CLOB);
+ container.registerFactory(CoreTypeMappings.TMString.FACTORY_LONG_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMBlob.FACTORY_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMBlob.FACTORY_LONG_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMClob.FACTORY_VARCHAR);
+ container.registerFactory(CoreTypeMappings.TMClob.FACTORY_LONG_VARCHAR);
+
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDataType(), DBType.VARCHAR);
+
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBigDecimal(), DBType.VARCHAR);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBigInteger(), DBType.VARCHAR);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBoolean(), DBType.BOOLEAN);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEBooleanObject(), DBType.BOOLEAN);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEByte(), DBType.SMALLINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEByteObject(), DBType.SMALLINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEByteArray(), DBType.BLOB);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEChar(), DBType.CHAR);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getECharacterObject(), DBType.CHAR);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDate(), DBType.TIMESTAMP);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDouble(), DBType.DOUBLE);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEDoubleObject(), DBType.DOUBLE);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEEnum(), DBType.INTEGER);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEFloat(), DBType.FLOAT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEFloatObject(), DBType.FLOAT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEInt(), DBType.INTEGER);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEIntegerObject(), DBType.INTEGER);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getELong(), DBType.BIGINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getELongObject(), DBType.BIGINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEShort(), DBType.SMALLINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEShortObject(), DBType.SMALLINT);
+ classifierDefaultMapping.put(EcorePackage.eINSTANCE.getEString(), DBType.VARCHAR);
+
+ classifierDefaultMapping.put(EtypesPackage.eINSTANCE.getBlob(), DBType.VARCHAR); // TODO Should be DBType.BLOB?
+ classifierDefaultMapping.put(EtypesPackage.eINSTANCE.getClob(), DBType.VARCHAR); // TODO Should be DBType.CLOB?
+ }
+
+ protected IManagedContainer getContainer()
+ {
+ return IPluginContainer.INSTANCE;
+ }
+
+ public void registerTypeMapping(ITypeMapping.Descriptor descriptor)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Registering {0}", descriptor);
+ }
+
+ EClassifier eClassifier = descriptor.getEClassifier();
+ DBType dbType = descriptor.getDBType();
+ Pair sourceTargetPair = new Pair(eClassifier, dbType);
+
+ // currently we do not support more than one typeMapping per source-target type pair
+ if (typeMappingByTypes.containsKey(sourceTargetPair))
+ {
+ OM.LOG.error(Messages.getString("TypeMappingRegistry.4"));
+ return;
+ }
+
+ if (typeMappingsById.containsKey(descriptor.getID()))
+ {
+ OM.LOG.error(MessageFormat.format(Messages.getString("TypeMappingRegistry.5"), descriptor.getID()));
+ return;
+ }
+
+ typeMappingsById.put(descriptor.getID(), descriptor);
+
+ // register first dbType for classifier as default
+ if (!classifierDefaultMapping.containsKey(eClassifier))
+ {
+ classifierDefaultMapping.put(eClassifier, dbType);
+ }
+
+ defaultFeatureMapDBTypes.add(dbType);
+
+ typeMappingByTypes.put(sourceTargetPair, descriptor);
+ }
+
+ public ITypeMapping createTypeMapping(IMappingStrategy mappingStrategy, EStructuralFeature feature)
+ {
+ ITypeMapping typeMapping = null;
+ if (feature instanceof EReference)
+ {
+ IIDHandler idHandler = mappingStrategy.getStore().getIDHandler();
+ typeMapping = idHandler.getObjectTypeMapping();
+ typeMapping.setDBType(idHandler.getDBType());
+ }
+ else
+ {
+ IDBAdapter dbAdapter = mappingStrategy.getStore().getDBAdapter();
+ DBType dbType = getDBType(feature, dbAdapter);
+
+ ITypeMapping.Descriptor descriptor = null;
+
+ String typeMappingID = DBAnnotation.TYPE_MAPPING.getValue(feature);
+ if (typeMappingID != null)
+ {
+ // lookup annotated mapping
+ descriptor = typeMappingsById.get(typeMappingID);
+
+ if (descriptor == null)
+ {
+ OM.LOG.warn(MessageFormat.format(Messages.getString("TypeMappingRegistry.2"), //
+ typeMappingID, feature.toString()));
+ }
+ }
+
+ if (descriptor == null)
+ {
+ // try to find suitable mapping by type
+ descriptor = getMappingByType(feature, dbType);
+ }
+
+ if (descriptor == null)
+ {
+ EClassifier type = getEType(feature);
+ throw new IllegalStateException(MessageFormat.format(Messages.getString("TypeMappingRegistry.1"), feature
+ .getEContainingClass().getName() + "." + feature.getName(),
+ type.getEPackage().getName() + "." + type.getName(), dbType.getKeyword()));
+ }
+
+ IFactory factory = getContainer().getFactory(ITypeMapping.Factory.PRODUCT_GROUP, descriptor.getFactoryType());
+ typeMapping = (ITypeMapping)factory.create(null);
+ typeMapping.setDBType(dbType);
+ }
+
+ typeMapping.setMappingStrategy(mappingStrategy);
+ typeMapping.setFeature(feature);
+ return typeMapping;
+ }
+
+ private EClassifier getEType(EStructuralFeature feature)
+ {
+ EClassifier classifier = feature.getEType();
+ if (classifier instanceof EEnum)
+ {
+ return EcorePackage.eINSTANCE.getEEnum();
+ }
+
+ if (classifier instanceof EClass)
+ {
+ return EcorePackage.eINSTANCE.getEClass();
+ }
+
+ EPackage ePackage = classifier.getEPackage();
+ if (CDOModelUtil.isCorePackage(ePackage))
+ {
+ return classifier;
+ }
+
+ if (CDOModelUtil.isTypesPackage(ePackage))
+ {
+ return classifier;
+ }
+
+ return EcorePackage.eINSTANCE.getEDataType();
+ }
+
+ private DBType getDBType(EStructuralFeature feature, IDBAdapter dbAdapter)
+ {
+ String typeKeyword = DBAnnotation.COLUMN_TYPE.getValue(feature);
+ if (typeKeyword != null)
+ {
+ DBType dbType = DBType.getTypeByKeyword(typeKeyword);
+ if (dbType == null)
+ {
+ throw new IllegalArgumentException("Unsupported columnType (" + typeKeyword + ") annotation of feature "
+ + feature.getName());
+ }
+
+ return dbType;
+ }
+
+ // No annotation present - lookup default DB type.
+ return getDefaultDBType(getEType(feature), dbAdapter);
+ }
+
+ private DBType getDefaultDBType(EClassifier type, IDBAdapter dbAdapter)
+ {
+ DBType result = classifierDefaultMapping.get(type);
+
+ if (result == null)
+ {
+ result = DBType.VARCHAR;
+ }
+
+ // Give the DBAdapter a chance to override the default type, if it's not supported
+ return dbAdapter.adaptType(result);
+ }
+
+ private ITypeMapping.Descriptor getMappingByType(EStructuralFeature feature, DBType dbType)
+ {
+ // First try: lookup specific mapping for the immediate type.
+ ITypeMapping.Descriptor descriptor = typeMappingByTypes.get(new Pair(feature.getEType(),
+ dbType));
+
+ if (descriptor == null)
+ {
+ // Second try: lookup general mapping
+ descriptor = typeMappingByTypes.get(new Pair(getEType(feature), dbType));
+ if (descriptor == null)
+ {
+ // Lookup failed. Give up
+ return null;
+ }
+ }
+
+ return descriptor;
+ }
+
+ public Collection getDefaultFeatureMapDBTypes()
+ {
+ return defaultFeatureMapDBTypes;
+ }
+
+ /**
+ * Keeps the {@link TypeMappingRegistry} in sync with {@link IManagedContainer#getFactoryRegistry()}.
+ *
+ * @author Stefan Winkler
+ */
+ private class RegistryPopulator implements IListener
+ {
+ private IManagedContainer container = getContainer();
+
+ public RegistryPopulator()
+ {
+ }
+
+ /**
+ * Connect to the factory registry.
+ */
+ public void connect()
+ {
+ populateTypeMappingRegistry();
+ container.getFactoryRegistry().addListener(this);
+ }
+
+ public void disconnect()
+ {
+ container.getFactoryRegistry().removeListener(this);
+ }
+
+ private void populateTypeMappingRegistry()
+ {
+ // get available factory types
+ Set factoryTypes = container.getFactoryTypes(ITypeMapping.Factory.PRODUCT_GROUP);
+
+ // parse the descriptor of each factory type
+ for (String factoryType : factoryTypes)
+ {
+ registerFactoryType(factoryType);
+ }
+ }
+
+ private void registerFactoryType(String factoryType)
+ {
+ ITypeMapping.Descriptor desc;
+
+ try
+ {
+ desc = TypeMappingUtil.descriptorFromFactoryType(factoryType);
+ registerTypeMapping(desc);
+ }
+ catch (FactoryTypeParserException ex)
+ {
+ OM.LOG.warn(ex);
+ }
+ }
+
+ public void notifyEvent(IEvent event)
+ {
+ if (event instanceof ContainerEvent>)
+ {
+ @SuppressWarnings("unchecked")
+ ContainerEvent> ev = (ContainerEvent>)event;
+
+ for (IContainerDelta> delta : ev.getDeltas())
+ {
+ IFactoryKey key = delta.getElement().getKey();
+ if (key.getProductGroup().equals(ITypeMapping.Factory.PRODUCT_GROUP))
+ {
+ if (delta.getKind() == Kind.ADDED)
+ {
+ String factoryType = delta.getElement().getKey().getType();
+ registerFactoryType(factoryType);
+ }
+ else
+ // delta.getKind() == Kind.REMOVED
+ {
+ // XXX Runtime removal of typeMappingFactories removal of type mappings is currently not supported.
+ OM.LOG.warn(Messages.getString("TypeMappingRegistry.3"));
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java
new file mode 100644
index 0000000000..1bdbaa1a3f
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/TypeMappingUtil.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping;
+
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.messages.Messages;
+
+import org.eclipse.net4j.db.DBType;
+
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EPackage;
+
+import java.text.MessageFormat;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Stefan Winkler
+ */
+public class TypeMappingUtil
+{
+ private static final Pattern FACTORY_DESCRIPTOR_PATTERN = Pattern.compile("(.+);(.+)#(.+)->(.+)");
+
+ /**
+ * Utility class - no instantiation.
+ */
+ private TypeMappingUtil()
+ {
+ }
+
+ public static ITypeMapping.Descriptor createDescriptor(String id, EClassifier eClassifier, DBType dbType)
+ {
+ String factoryType = createFactoryType(id, eClassifier, dbType);
+ return new TypeMappingDescriptor(id, factoryType, eClassifier, dbType);
+ }
+
+ public static String createFactoryType(String id, EClassifier eClassifier, DBType dbType)
+ {
+ StringBuilder builder = new StringBuilder();
+
+ // id
+ builder.append(id);
+ builder.append(";");
+
+ // classifier
+ builder.append(eClassifier.getEPackage().getNsURI());
+ builder.append("#");
+ builder.append(eClassifier.getName());
+ builder.append("->");
+
+ // dbtype
+ builder.append(dbType.getKeyword());
+
+ return builder.toString();
+ }
+
+ public static ITypeMapping.Descriptor descriptorFromFactoryType(String factoryType) throws FactoryTypeParserException
+ {
+ Matcher matcher = FACTORY_DESCRIPTOR_PATTERN.matcher(factoryType);
+
+ if (!matcher.matches())
+ {
+ throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.1"),
+ factoryType));
+ }
+
+ String id = matcher.group(1);
+ String packageUri = matcher.group(2);
+ String classifierName = matcher.group(3);
+ String typeKeyword = matcher.group(4);
+
+ EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(packageUri);
+ if (ePackage == null)
+ {
+ throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.2"),
+ packageUri, factoryType));
+ }
+
+ EClassifier eClassifier = ePackage.getEClassifier(classifierName);
+ if (eClassifier == null)
+ {
+ throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.3"),
+ classifierName, factoryType));
+ }
+
+ DBType dbType = DBType.getTypeByKeyword(typeKeyword);
+ if (dbType == null)
+ {
+ throw new FactoryTypeParserException(MessageFormat.format(Messages.getString("FactoryTypeParserException.4"),
+ dbType, factoryType));
+ }
+
+ return new TypeMappingDescriptor(id, factoryType, eClassifier, dbType);
+ }
+
+ public static class FactoryTypeParserException extends Exception
+ {
+ private static final long serialVersionUID = 1L;
+
+ public FactoryTypeParserException(String desc)
+ {
+ super(desc);
+ }
+ }
+}
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java
new file mode 100644
index 0000000000..83493b4896
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractFeatureMapTableMapping.java
@@ -0,0 +1,588 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455
+ * Victor Roldan Betancort - Bug 283998: [DB] Chunk reading for multiple chunks fails
+ * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
+ * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDOList;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
+import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
+import org.eclipse.emf.cdo.server.db.IDBStore;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.db.ddl.IDBField;
+import org.eclipse.net4j.db.ddl.IDBIndex.Type;
+import org.eclipse.net4j.db.ddl.IDBTable;
+import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.collection.MoveableList;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.FeatureMap;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This abstract base class provides basic behavior needed for mapping many-valued attributes to tables.
+ *
+ * @author Eike Stepper
+ * @since 3.0
+ */
+public abstract class AbstractFeatureMapTableMapping extends BasicAbstractListTableMapping
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractFeatureMapTableMapping.class);
+
+ /**
+ * The table of this mapping.
+ */
+ private IDBTable table;
+
+ /**
+ * The tags mapped to column names
+ */
+ private HashMap tagMap;
+
+ /**
+ * Column name Set
+ */
+ private List columnNames;
+
+ /**
+ * The type mappings for the value fields.
+ */
+ private Map typeMappings;
+
+ // --------- SQL strings - see initSQLStrings() -----------------
+ private String sqlSelectChunksPrefix;
+
+ private String sqlOrderByIndex;
+
+ protected String sqlInsert;
+
+ private List dbTypes;
+
+ public AbstractFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ initDBTypes();
+ initTable();
+ initSQLStrings();
+ }
+
+ private void initDBTypes()
+ {
+ // TODO add annotation processing here ...
+ ITypeMapping.Registry registry = getTypeMappingRegistry();
+ dbTypes = new ArrayList(registry.getDefaultFeatureMapDBTypes());
+ }
+
+ protected ITypeMapping.Registry getTypeMappingRegistry()
+ {
+ return ITypeMapping.Registry.INSTANCE;
+ }
+
+ private void initTable()
+ {
+ IDBStore store = getMappingStrategy().getStore();
+ String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature());
+ table = store.getDBSchema().addTable(tableName);
+
+ // add fields for keys (cdo_id, version, feature_id)
+ FieldInfo[] fields = getKeyFields();
+ IDBField[] dbFields = new IDBField[fields.length];
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ dbFields[i] = table.addField(fields[i].getName(), fields[i].getDbType());
+ }
+
+ // add field for list index
+ IDBField idxField = table.addField(CDODBSchema.FEATUREMAP_IDX, DBType.INTEGER);
+
+ // add field for FeatureMap tag (MetaID for Feature in CDO registry)
+ IDBField tagField = table.addField(CDODBSchema.FEATUREMAP_TAG, store.getIDHandler().getDBType());
+
+ tagMap = new HashMap();
+ typeMappings = new HashMap();
+ columnNames = new ArrayList();
+
+ // create columns for all DBTypes
+ for (DBType type : getDBTypes())
+ {
+ String column = CDODBSchema.FEATUREMAP_VALUE + "_" + type.name();
+ table.addField(column, type);
+ columnNames.add(column);
+ }
+
+ table.addIndex(Type.NON_UNIQUE, dbFields);
+ table.addIndex(Type.NON_UNIQUE, idxField);
+ table.addIndex(Type.NON_UNIQUE, tagField);
+ }
+
+ protected abstract FieldInfo[] getKeyFields();
+
+ protected abstract void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException;
+
+ public Collection getDBTables()
+ {
+ return Arrays.asList(table);
+ }
+
+ private void initSQLStrings()
+ {
+ String tableName = getTable().getName();
+ FieldInfo[] fields = getKeyFields();
+
+ // ---------------- SELECT to read chunks ----------------------------
+ StringBuilder builder = new StringBuilder();
+ builder.append("SELECT ");
+
+ builder.append(CDODBSchema.FEATUREMAP_TAG);
+ builder.append(", ");
+
+ Iterator iter = columnNames.iterator();
+ while (iter.hasNext())
+ {
+ builder.append(iter.next());
+ if (iter.hasNext())
+ {
+ builder.append(", ");
+ }
+ }
+
+ builder.append(" FROM ");
+ builder.append(tableName);
+ builder.append(" WHERE ");
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ builder.append(fields[i].getName());
+ if (i + 1 < fields.length)
+ {
+ // more to come
+ builder.append("=? AND ");
+ }
+ else
+ {
+ // last one
+ builder.append("=? ");
+ }
+ }
+
+ sqlSelectChunksPrefix = builder.toString();
+
+ sqlOrderByIndex = " ORDER BY " + CDODBSchema.FEATUREMAP_IDX; //$NON-NLS-1$
+
+ // INSERT with dynamic field name
+ // TODO: Better: universal INSERT-Statement, because of stmt caching!
+
+ // ----------------- INSERT - prefix -----------------
+ builder = new StringBuilder("INSERT INTO ");
+ builder.append(tableName);
+ builder.append(" ("); //$NON-NLS-1$
+ for (int i = 0; i < fields.length; i++)
+ {
+ builder.append(fields[i].getName());
+ builder.append(", "); //$NON-NLS-1$
+ }
+
+ for (int i = 0; i < columnNames.size(); i++)
+ {
+ builder.append(columnNames.get(i));
+ builder.append(", "); //$NON-NLS-1$
+ }
+
+ builder.append(CDODBSchema.FEATUREMAP_IDX);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.FEATUREMAP_TAG);
+ builder.append(") VALUES ("); //$NON-NLS-1$
+ for (int i = 0; i < fields.length + columnNames.size(); i++)
+ {
+ builder.append("?, ");
+ }
+
+ builder.append("?, ?)");
+ sqlInsert = builder.toString();
+ }
+
+ protected List getDBTypes()
+ {
+ return dbTypes;
+ }
+
+ protected final IDBTable getTable()
+ {
+ return table;
+ }
+
+ protected final List getColumnNames()
+ {
+ return columnNames;
+ }
+
+ protected final Map getTypeMappings()
+ {
+ return typeMappings;
+ }
+
+ protected final Map getTagMap()
+ {
+ return tagMap;
+ }
+
+ public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
+ {
+ MoveableList