summaryrefslogtreecommitdiffstats
path: root/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl
diff options
context:
space:
mode:
Diffstat (limited to 'das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl')
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DataObjectMaker.java123
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DefaultConverter.java84
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/GraphBuilderMetadata.java283
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/MultiTableRegistry.java116
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultMetadata.java411
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultSetProcessor.java180
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultSetRow.java390
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/RowObjects.java205
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/SingleTableRegistry.java49
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableData.java123
-rw-r--r--das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableRegistry.java47
11 files changed, 2011 insertions, 0 deletions
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DataObjectMaker.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DataObjectMaker.java
new file mode 100644
index 0000000000..0e5fc48c0c
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DataObjectMaker.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import commonj.sdo.DataObject;
+import commonj.sdo.Property;
+import commonj.sdo.Type;
+import commonj.sdo.helper.DataFactory;
+
+public final class DataObjectMaker {
+
+ private final DataObject rootObject;
+ private final Map containmentPropertyMap;
+ private final Map typeMap;
+
+ private static final Logger logger = Logger.getLogger(DataObjectMaker.class);
+
+ public DataObjectMaker(DataObject root) {
+ this.rootObject = root;
+ containmentPropertyMap = new HashMap();
+ typeMap = new HashMap();
+ Iterator i = this.rootObject.getType().getProperties().iterator();
+ while (i.hasNext()) {
+ Property p = (Property) i.next();
+ Type type = p.getType();
+ String typeName = type.getName();
+ typeMap.put(typeName, type);
+ if (p.isContainment()) {
+ containmentPropertyMap.put(typeName, p);
+ }
+ }
+ }
+
+ /**
+ * @param tableData
+ * @return
+ */
+ public DataObject createAndAddDataObject(TableData tableData, ResultMetadata resultMetadata) {
+ // Get a Type from the package and create a standalone DataObject
+
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Looking for Type for " + tableData.getTableName());
+ }
+
+ String tableName = tableData.getTableName();
+ Type tableClass = (Type) typeMap.get(tableName);
+
+ if (tableClass == null) {
+ throw new RuntimeException("An SDO Type with name " + tableData.getTableName() + " was not found");
+ }
+
+ DataObject obj = DataFactory.INSTANCE.create(tableClass);
+
+ // Now, check to see if the root data object has a containment reference
+ // to this EClass. If so, add it to the graph. If not, it will be taken
+ // care of when we process relationships
+ Property containmentProp = (Property) containmentPropertyMap.get(tableName);
+ if (containmentProp != null) {
+ if (containmentProp.isMany()) {
+ rootObject.getList(containmentProp).add(obj);
+ } else {
+ this.rootObject.set(containmentProp, obj);
+ }
+ }
+
+ // Set the column values
+ Iterator columnNames = resultMetadata.getPropertyNames(tableData.getTableName()).iterator();
+ Type objType = obj.getType();
+ while (columnNames.hasNext()) {
+ String propertyName = (String) columnNames.next();
+ Property p = objType.getProperty(propertyName);
+ if (p == null) {
+ // Try again, ignoring case
+ p = findProperty(objType, propertyName);
+ if (p == null) {
+ throw new RuntimeException("Type " + obj.getType().getName()
+ + " does not contain a property named " + propertyName);
+ }
+ }
+
+ Object value = tableData.getColumnData(propertyName);
+
+ obj.set(p, value);
+ }
+
+ return obj;
+ }
+
+ // temporary, ignoring case
+ private Property findProperty(Type type, String columnName) {
+ Iterator properties = type.getProperties().iterator();
+ while (properties.hasNext()) {
+ Property p = (Property) properties.next();
+ if (columnName.equalsIgnoreCase(p.getName())) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DefaultConverter.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DefaultConverter.java
new file mode 100644
index 0000000000..a08f184b9a
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DefaultConverter.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.sql.Blob;
+import java.sql.SQLException;
+
+import org.apache.tuscany.das.rdb.Converter;
+
+public class DefaultConverter implements Converter {
+
+ public DefaultConverter() {
+ super();
+ }
+
+ public Object getColumnValue(Object data) {
+ return data;
+ }
+
+ public Object getPropertyValue(Object data) {
+ // if (type.isInstance(data))
+ // return data;
+ //
+ // if ( data == null )
+ // return null;
+ //
+ // String name = type.getInstanceClass().getName();
+ // if (name == "java.lang.Byte" || name == "byte") {
+ // return new Byte(data.toString());
+ // }
+ //
+ // else if (name == "java.lang.Double" || name == "double") {
+ // return new Double(data.toString());
+ // }
+ //
+ // else if (name == "java.lang.Float" || name == "float") {
+ // return new Float(data.toString());
+ // }
+ //
+ // else if (name == "java.lang.Integer" || name == "int") {
+ // return new Integer(data.toString());
+ // }
+ //
+ // else if (name == "java.lang.Long" || name == "long") {
+ // return new Long(data.toString());
+ // }
+ //
+ // else if (name == "java.lang.Short" || name == "short") {
+ // return new Short(data.toString());
+ // }
+ //
+ // else if (name == "java.lang.String") {
+ // return String.valueOf(data.toString());
+ // }
+
+ if (data instanceof Blob) {
+ Blob b = (Blob) data;
+ try {
+ return b.getBytes(1, (int) b.length());
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return data;
+
+ }
+}
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/GraphBuilderMetadata.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/GraphBuilderMetadata.java
new file mode 100644
index 0000000000..5be56d77d8
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/GraphBuilderMetadata.java
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.apache.tuscany.das.rdb.config.Config;
+import org.apache.tuscany.das.rdb.config.Relationship;
+import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper;
+import org.apache.tuscany.das.rdb.impl.ResultSetShape;
+import org.apache.tuscany.sdo.api.SDOUtil;
+
+import commonj.sdo.Property;
+import commonj.sdo.Type;
+import commonj.sdo.helper.HelperContext;
+import commonj.sdo.impl.HelperProvider;
+
+/**
+ */
+public final class GraphBuilderMetadata {
+
+ private static final Logger logger = Logger.getLogger(GraphBuilderMetadata.class);
+
+ private MappingWrapper configWrapper;
+
+ private final Collection resultSets = new ArrayList();
+
+ private String typeURI;
+
+ private List definedTypes;
+
+ private Type rootType;
+
+ private HelperContext defaultHelperContext = HelperProvider.getDefaultContext();
+ private HelperContext helperContext = HelperProvider.getInstance().getDefaultContext();
+
+
+ public GraphBuilderMetadata(List results, Config model, ResultSetShape shape) throws SQLException {
+ this.configWrapper = new MappingWrapper(model);
+ if (model != null) {
+ this.typeURI = model.getDataObjectModel();
+ }
+
+ Iterator i = results.iterator();
+ while (i.hasNext()) {
+ ResultSet rs = (ResultSet) i.next();
+ ResultMetadata resultMetadata = new ResultMetadata(rs, configWrapper, shape);
+ resultSets.add(resultMetadata);
+ }
+
+ }
+
+ /**
+ * Returns the collection of ResultMetadata objects
+ */
+ public Collection getResultMetadata() {
+ return this.resultSets;
+ }
+
+ /**
+ * Returns the set of defined relationships
+ */
+
+ public Collection getRelationships() {
+ return configWrapper.getConfig().getRelationship();
+ }
+
+ /**
+ * Returns the root Type
+ */
+ public Type getRootType() {
+ if (this.rootType == null) {
+ if (this.typeURI == null) {
+ createDynamicTypes();
+ } else {
+ createDynamicRoot();
+ }
+ }
+
+ return this.rootType;
+ }
+
+ public MappingWrapper getConfigWrapper() {
+ return this.configWrapper;
+ }
+
+ /**
+ * Creates a set of SDO Types based on the query results and supplied config information
+ */
+
+ private void createDynamicTypes() {
+
+ Type root = SDOUtil.createType(helperContext, getDefaultURI(), "DataGraphRoot", false);
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("GBMD.createDynamicTypes():created Type for "+getDefaultURI());
+ }
+
+ Iterator iter = getResultMetadata().iterator();
+ while (iter.hasNext()) {
+
+ ResultMetadata resultMetadata = (ResultMetadata) iter.next();
+
+ // Create a Type for each Table represented in the ResultSet
+ Iterator names = resultMetadata.getAllTablePropertyNames().iterator();
+ while (names.hasNext()) {
+ String tableName = (String) names.next();
+ if (root.getProperty(tableName) == null) {
+ Type tableType = SDOUtil.createType(helperContext, getDefaultURI(), tableName, false);
+ Property property = SDOUtil.createProperty(root, tableName, tableType);
+ SDOUtil.setMany(property, true);
+ SDOUtil.setContainment(property, true);
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("GBMD.createDynamicTypes():CREATING NEW TABLE TYPE & PROPERTY :"+tableName);
+ }
+ }
+ }
+
+ // TODO tablePropertyMap is temporary until Tuscany-203 is fixed
+ Map tablePropertyMap = new HashMap();
+
+ for (int i = 1; i <= resultMetadata.getResultSetSize(); i++) {
+
+ Property ref = root.getProperty(resultMetadata.getTablePropertyName(i));
+
+ if (ref == null) {
+ throw new RuntimeException("Could not find table " + resultMetadata.getTablePropertyName(i)
+ + " in the SDO model");
+ }
+
+ // TODO Temporary code to check to see if a property has already been added.
+ // Replace when Tuscany-203 is fixed
+ List addedProperties = (List) tablePropertyMap.get(ref.getName());
+ if (addedProperties == null) {
+ addedProperties = new ArrayList();
+ tablePropertyMap.put(ref.getName(), addedProperties);
+ }
+
+
+
+ String columnName = resultMetadata.getColumnPropertyName(i);
+
+ // TODO temporary check until Tuscany-203 is fixed
+ if (!addedProperties.contains(columnName)) {
+ addedProperties.add(columnName);
+ Type atype = resultMetadata.getDataType(i);
+
+ SDOUtil.createProperty(ref.getType(), columnName, atype);
+
+ }
+
+ }
+ }
+
+ MappingWrapper wrapper = getConfigWrapper();
+ Iterator i = getRelationships().iterator();
+ while (i.hasNext()) {
+ Relationship r = (Relationship) i.next();
+
+ String parentName = wrapper.getTableTypeName(r.getPrimaryKeyTable());
+ String childName = wrapper.getTableTypeName(r.getForeignKeyTable());
+
+ if (parentName == null) {
+ throw new RuntimeException("The parent table (" + r.getPrimaryKeyTable()
+ + ") in relationship " + r.getName()
+ + " was not found in the mapping information.");
+ } else if (childName == null) {
+ throw new RuntimeException("The child table (" + r.getForeignKeyTable()
+ + ") in relationship " + r.getName()
+ + " was not found in the mapping information.");
+ }
+
+ Property parentProperty = root.getProperty(parentName);
+ Property childProperty = root.getProperty(childName);
+
+ if (parentProperty == null) {
+ throw new RuntimeException("The parent table (" + parentName + ") in relationship "
+ + r.getName() + " was not found.");
+ } else if (childProperty == null) {
+ throw new RuntimeException("The child table (" + childName + ") in relationship "
+ + r.getName() + " was not found.");
+ }
+
+ Type parent = parentProperty.getType();
+ Type child = childProperty.getType();
+
+ Property parentProp = SDOUtil.createProperty(parent, r.getName(), child);
+ Property childProp = SDOUtil.createProperty(child, r.getName() + "_opposite", parent);
+ SDOUtil.setOpposite(parentProp, childProp);
+ SDOUtil.setOpposite(childProp, parentProp);
+ SDOUtil.setMany(parentProp, r.isMany());
+ }
+
+ this.rootType = root;
+ }
+
+ private String getDefaultURI() {
+ return "http:///org.apache.tuscany.das.rdb/das";
+ }
+
+ /**
+ * Create a dynamic root Type to act as a container Type for a set of generated Types
+ *
+ */
+ private void createDynamicRoot() {
+ Type root = SDOUtil.createType(helperContext, getDefaultURI() + "/DataGraphRoot", "DataGraphRoot", false);
+
+ List types = getDefinedTypes();
+
+ if (types == null) {
+ throw new RuntimeException("SDO Types have not been registered for URI " + typeURI);
+ }
+
+ Iterator i = types.iterator();
+ while (i.hasNext()) {
+ Type type = (Type) i.next();
+ Property property = SDOUtil.createProperty(root, type.getName(), type);
+ SDOUtil.setContainment(property, true);
+ SDOUtil.setMany(property, true);
+ }
+ this.rootType = root;
+ }
+
+ public List getDefinedTypes() {
+ if (this.definedTypes == null) {
+ List types = null;
+ List defaultTypes = null;
+ if (this.typeURI == null) {
+ types = SDOUtil.getTypes(helperContext, getDefaultURI());
+ defaultTypes = SDOUtil.getTypes(defaultHelperContext, getDefaultURI());
+ if(defaultTypes != null){
+ if(types == null) {
+ types = defaultTypes;
+ }
+ else {
+ types.addAll(defaultTypes);
+ }
+ }
+ this.definedTypes = types;
+ } else {
+ types = SDOUtil.getTypes(helperContext, typeURI);
+ defaultTypes = SDOUtil.getTypes(defaultHelperContext, typeURI);
+ if(defaultTypes != null){
+ if(types == null) {
+ types = defaultTypes;
+ }
+ else {
+ types.addAll(defaultTypes);
+ }
+ }
+
+ if(rootType != null)
+ types.add(rootType);
+ this.definedTypes = types;
+ }
+ }
+ return this.definedTypes;
+ }
+
+}
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/MultiTableRegistry.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/MultiTableRegistry.java
new file mode 100644
index 0000000000..bc3ca440b9
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/MultiTableRegistry.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import commonj.sdo.DataObject;
+
+/**
+ *
+ * Used to store and look up table objects based on primary key This could be a lot more
+ * efficient if we could use LinkedHashMap from JDK 1.4
+ */
+public class MultiTableRegistry implements TableRegistry {
+ private static final Logger logger = Logger.getLogger(MultiTableRegistry.class);
+
+ private Map tableNameMap;
+
+ private Map tableValueMap;
+
+ public MultiTableRegistry() {
+ tableNameMap = new HashMap();
+ tableValueMap = new HashMap();
+ }
+
+ /**
+ * Get the table with the specified name and primary key
+ *
+ * @param tableName
+ * @param primaryKey
+ * @return EDataObject
+ */
+ public DataObject get(String tableName, List primaryKey) {
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Looking for table " + tableName + " with PK " + primaryKey);
+ this.logger.debug("\tReturning " + getPkMap(tableName).get(primaryKey));
+ }
+ return (DataObject) getPkMap(tableName).get(primaryKey);
+ }
+
+ /**
+ * Add the table with the specified name and primary key
+ *
+ * @param tableName
+ * @param primaryKey
+ * @param value
+ */
+ public void put(String tableName, List primaryKey, DataObject value) {
+ if (getPkMap(tableName).put(primaryKey, value) == null) {
+ getCreateValueList(tableName).add(value);
+ }
+ }
+
+ /**
+ * Get the HashMap that contains the primary key to table object mappings.
+ *
+ * @param tableName
+ * @return HashMap
+ */
+ private Map getPkMap(String tableName) {
+ Map pkMap = (HashMap) tableNameMap.get(tableName);
+ if (pkMap == null) {
+ pkMap = new HashMap();
+ tableNameMap.put(tableName, pkMap);
+ }
+ return pkMap;
+ }
+
+ private List getCreateValueList(String tableName) {
+ List values = (List) tableValueMap.get(tableName);
+ if (values == null) {
+ values = new ArrayList();
+ tableValueMap.put(tableName, values);
+ }
+ return values;
+ }
+
+ public boolean contains(String tableName, List primaryKey) {
+ return get(tableName, primaryKey) == null ? false : true;
+
+ }
+
+ public void remove(String tableName, List primaryKey) {
+ getPkMap(tableName).get(primaryKey);
+ Map pkMap = (HashMap) tableNameMap.get(tableName);
+
+ if(pkMap.get(primaryKey) != null) {
+ pkMap.remove(primaryKey);
+ }
+
+ if(pkMap.size() == 0) {
+ tableNameMap.remove(tableName);
+ }
+ }
+} \ No newline at end of file
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultMetadata.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultMetadata.java
new file mode 100644
index 0000000000..39f35150ec
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultMetadata.java
@@ -0,0 +1,411 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tuscany.das.rdb.Converter;
+import org.apache.tuscany.das.rdb.config.Column;
+import org.apache.tuscany.das.rdb.config.Config;
+import org.apache.tuscany.das.rdb.config.Table;
+import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper;
+import org.apache.tuscany.das.rdb.config.wrapper.TableWrapper;
+import org.apache.tuscany.das.rdb.impl.ResultSetShape;
+import org.apache.tuscany.das.rdb.impl.SDODataTypes;
+
+import commonj.sdo.Type;
+
+public final class ResultMetadata {
+
+ private Map tableToPropertyMap = new HashMap();
+
+ private List typeNames = new ArrayList();
+
+ private List propertyNames = new ArrayList();
+
+ private final ResultSet resultSet;
+
+ private final ResultSetShape resultSetShape;
+
+ private final MappingWrapper configWrapper;
+
+ private Converter[] converters;
+
+ private Map tableToPrimaryKeysMap = new HashMap();
+
+ //JIRA-952
+ public ResultMetadata(ResultSet rs, MappingWrapper cfgWrapper, ResultSetShape shape) throws SQLException {
+
+ this.resultSet = rs;
+ this.configWrapper = cfgWrapper;
+
+ if (shape == null) {
+ this.resultSetShape = new ResultSetShape(rs.getMetaData(), configWrapper.getConfig());
+ } else {
+ this.resultSetShape = shape;
+ }
+
+ this.converters = new Converter[resultSetShape.getColumnCount()];
+
+ Map impliedRelationships = new HashMap();
+ String schemaName = "";
+ String idSpell = null;
+ for (int i = 1; i <= resultSetShape.getColumnCount(); i++) {
+ String tableName = resultSetShape.getTableName(i);
+ schemaName = resultSetShape.getSchemaName(i);
+ if (( tableName == null ) || ( tableName.equals(""))) {
+ throw new RuntimeException("Unable to obtain table information from JDBC. DAS configuration must specify ResultDescriptors");
+ }
+ String typeName = null;
+
+ if(this.configWrapper.getConfig().isDatabaseSchemaNameSupported()){
+ typeName = configWrapper.getTableTypeName(schemaName+"."+tableName);
+ }
+ else{
+ typeName = configWrapper.getTableTypeName(tableName);
+ }
+ String columnName = resultSetShape.getColumnName(i);
+
+ String colName = "";
+ if (columnName.regionMatches(true, columnName.length()-3, "_ID", 0, 3)) {
+ idSpell = columnName.substring(columnName.length()-3, columnName.length());
+ if(this.configWrapper.getConfig().isDatabaseSchemaNameSupported()){
+ colName = schemaName+"."+columnName;
+ impliedRelationships.put(colName, schemaName+"."+tableName);
+ }
+ else{
+ colName = columnName;
+ impliedRelationships.put(colName, tableName);
+ }
+ } else if (columnName.equalsIgnoreCase("ID")) {
+ configWrapper.addImpliedPrimaryKey(schemaName, tableName, columnName);
+ }
+
+ String propertyName = null;
+
+ if(this.configWrapper.getConfig().isDatabaseSchemaNameSupported()){
+ propertyName = configWrapper.getColumnPropertyName(schemaName+"."+tableName, columnName);
+ }
+ else{
+ propertyName = configWrapper.getColumnPropertyName(tableName, columnName);
+ }
+ String converterName = null;
+
+ if(this.configWrapper.getConfig().isDatabaseSchemaNameSupported()){
+ converterName = configWrapper.getConverter(schemaName+"."+tableName, resultSetShape.getColumnName(i));
+ }
+ else{
+ converterName = configWrapper.getConverter(tableName, resultSetShape.getColumnName(i));
+ }
+
+ converters[i - 1] = loadConverter(converterName, resultSetShape.getColumnType(i));
+
+ typeNames.add(typeName);
+ propertyNames.add(propertyName);
+
+ Collection properties = (Collection) tableToPropertyMap.get(typeName);
+ if (properties == null) {
+ properties = new ArrayList();
+ }
+ properties.add(propertyName);
+ tableToPropertyMap.put(typeName, properties);
+
+ }
+
+ //System.out.println("tableToPropertyMap "+tableToPropertyMap);
+ fillTableToPrimaryKeysMap();
+
+ Iterator i = impliedRelationships.keySet().iterator();
+ while (i.hasNext()) {
+ String columnName = (String) i.next();
+ String pkTableName = columnName.substring(0, columnName.indexOf(idSpell));//_id, _Id, _iD, _ID anything
+ String fkTableName = (String) impliedRelationships.get(columnName);
+ List pkTableProperties = (List) tableToPropertyMap.get(configWrapper.getTableTypeName(pkTableName));
+ if ((pkTableProperties != null) && (pkTableProperties.contains("ID"))) {
+ configWrapper.addImpliedRelationship(pkTableName, fkTableName, columnName);
+ }
+ }
+ // Add any tables defined in the model but not included in the ResultSet
+ // to the list of propertyNames
+ Config model = configWrapper.getConfig();
+ if (model != null) {
+ Iterator tablesFromModel = model.getTable().iterator();
+ while (tablesFromModel.hasNext()) {
+ TableWrapper t = new TableWrapper((Table) tablesFromModel.next());
+ if (tableToPropertyMap.get(t.getTypeName()) == null) {
+ tableToPropertyMap.put(t.getTypeName(), Collections.EMPTY_LIST);
+ }
+ }
+ }
+ }
+
+ //Now fill tableToPrimaryKeysMap.Complete for whichever tables are there in tableToPrimaryKeysMap,
+ //Also case of implied PK and it is not there in SELECT, provide way to still fill it in
+ //tableToPrimaryKeysMap - the column should be present in Config (though not defed as PK)
+ //And consider the classic case, when we assume all columns to be PKs - when no info
+ //in config for table or "all columns"
+ private void fillTableToPrimaryKeysMap(){
+ Iterator itr = tableToPropertyMap.keySet().iterator();
+ while(itr.hasNext()){
+ String curTableName = (String)itr.next();
+ boolean treatAllPKs = false;//flag for, when all cols need to be treated as PKs
+
+ if(tableToPrimaryKeysMap.containsKey(curTableName)){
+ continue;//don't keep refilling same hashset for each ResultMetadata constructor,
+ }
+
+ List columnsForTable = null;
+ if(configWrapper.getTableByTypeName(curTableName) != null) {
+ columnsForTable = configWrapper.getTableByTypeName(curTableName).getColumn();
+ }
+ else if(configWrapper.getTable(curTableName) != null){
+ columnsForTable = configWrapper.getTable(curTableName).getColumn();
+ configWrapper.getTable(curTableName).setTypeName(curTableName);//keep configWrapper consistent with Type info
+ }
+ else{
+ treatAllPKs = true;//can not find table/type, need to consider all columns as PKs
+ }
+
+ if(columnsForTable != null){
+ for(int ii=0; ii<columnsForTable.size(); ii++){
+ Column curCol = (Column)columnsForTable.get(ii);
+
+ if(curCol.isPrimaryKey() || curCol.getColumnName().equalsIgnoreCase("ID")){//need to compare col name
+ //with ID as that is the one from dbms metadata or resul set shape metadata
+ //but when putting in map, need to put property and if not present then column
+ Collection pks = (Collection) tableToPrimaryKeysMap.get(curTableName);
+ if(pks == null){
+ pks = new HashSet();
+ }
+
+ if(curCol.getPropertyName() != null){
+ pks.add(curCol.getPropertyName());
+ }
+ else{
+ pks.add(curCol.getColumnName());
+ curCol.setPropertyName(curCol.getColumnName());//make config consistent
+ if(!((Collection)tableToPropertyMap.get(curTableName)).contains(curCol.getColumnName())){
+ ((Collection)tableToPropertyMap.get(curTableName)).add(curCol.getColumnName());
+ }
+ }
+ tableToPrimaryKeysMap.put(curTableName, pks);
+ }
+ }
+ }
+ else{
+ treatAllPKs = true;//table present in cfg , but no cols
+ }
+
+ if(treatAllPKs){
+ tableToPrimaryKeysMap.put(curTableName, null);//case when all columns are considered PKs
+ }
+ }
+ }
+
+ private Converter loadConverter(String converterName, Type type) {
+ if (converterName != null) {
+
+ try {
+ Class converterClazz = Class.forName(converterName, true,
+ Thread.currentThread().getContextClassLoader());
+ if (null != converterClazz) {
+ return (Converter) converterClazz.newInstance();
+ }
+
+ converterClazz = Class.forName(converterName);
+ if (converterClazz != null) {
+ return (Converter) converterClazz.newInstance();
+ }
+ } catch (ClassNotFoundException ex) {
+ throw new RuntimeException(ex);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ } catch (InstantiationException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ if (SDODataTypes.BYTES.getName().equals(type.getName()) && SDODataTypes.BYTES.getURI().equals(type.getURI())) {
+ return new DefaultConverter();
+ } else {
+ return null;
+ }
+ }
+
+ public String getColumnPropertyName(int i) {
+ return (String) propertyNames.get(i - 1);
+ }
+
+ public String getDatabaseColumnName(int i) {
+ return resultSetShape.getColumnName(i);
+ }
+
+ public String getTableName(String columnName) {
+ return (String) typeNames.get(propertyNames.indexOf(columnName));
+ }
+
+ public int getTableSize(String tableName) {
+ return ((Collection) tableToPropertyMap.get(tableName)).size();
+ }
+
+ public Type getDataType(String columnName) {
+ return resultSetShape.getColumnType(propertyNames.indexOf(columnName));
+ }
+
+ public String getTablePropertyName(int i) {
+ return (String) typeNames.get(i - 1);
+ }
+
+ public Collection getAllTablePropertyNames() {
+ return tableToPropertyMap.keySet();
+ }
+
+ public HashSet getAllPKsForTable(String tableName){
+ if(tableToPrimaryKeysMap.containsKey(tableName))
+ return (HashSet)tableToPrimaryKeysMap.get(tableName);
+ else{
+ HashSet tmpHashSet = new HashSet();
+ tmpHashSet.add("");//case when there were cols in cfg but could not find any PK in it and no ID column in cfg/result set
+ return tmpHashSet;
+ }
+
+ }
+
+ public String toString() {
+
+ StringBuffer result = new StringBuffer(super.toString());
+ result.append(" (Table Names: ");
+ Iterator i = typeNames.iterator();
+ while (i.hasNext()) {
+ String tableName = (String) i.next();
+ result.append(' ');
+ result.append(tableName);
+ result.append(',');
+ }
+
+ result.append(" columnNames: ");
+
+ i = propertyNames.iterator();
+ while (i.hasNext()) {
+ String columnName = (String) i.next();
+ result.append(' ');
+ result.append(columnName);
+ result.append(',');
+ }
+
+ result.append(" mappingModel: ");
+ result.append(this.configWrapper.getConfig());
+
+ result.append(" resultSet: ");
+ result.append(resultSet);
+
+ result.append(" resultSetSize: ");
+ result.append(resultSetShape.getColumnCount());
+ result.append(')');
+ return result.toString();
+
+ }
+
+ /**
+ * @return
+ */
+ public int getNumberOfTables() {
+ return tableToPropertyMap.keySet().size();
+ }
+
+ /**
+ * Return whether the column at the given position is part of a primary key.
+ * If we don't have this information, we assume every column is a primary
+ * key. This results in uniqueness checks using all columns in a table.
+ *
+ * @param i
+ * @return
+ */
+ public boolean isPKColumn(int i) {
+
+ Table t = configWrapper.getTableByTypeName(getTablePropertyName(i));
+ if (t == null) {
+ return true;
+ }
+
+ // If no Columns have been defined, consider every column to be part of
+ // the PK
+ if (t.getColumn().isEmpty()) {
+ return true;
+ }
+
+ Column c = configWrapper.getColumn(t, getDatabaseColumnName(i));
+
+ if (c == null) {
+ return false;
+ }
+
+ if (c.isPrimaryKey()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @param i
+ * @return Type
+ */
+ public Type getDataType(int i) {
+ return resultSetShape.getColumnType(i);
+ }
+
+ /**
+ * @param tableName
+ * @return Collection
+ */
+ public Collection getPropertyNames(String tableName) {
+ return (Collection) tableToPropertyMap.get(tableName);
+ }
+
+ public ResultSet getResultSet() {
+ return this.resultSet;
+ }
+
+ public int getResultSetSize() {
+ return resultSetShape.getColumnCount();
+ }
+
+ public boolean isRecursive() {
+ return configWrapper.hasRecursiveRelationships();
+ }
+
+ public Set getRecursiveTypeNames() {
+ return configWrapper.getRecursiveTypeNames();
+ }
+
+ public Object convert(int i, Object data) {
+ return (converters[i - 1] == null) ? data : converters[i - 1].getPropertyValue(data);
+ }
+
+}
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultSetProcessor.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultSetProcessor.java
new file mode 100644
index 0000000000..fa17ebb098
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultSetProcessor.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+
+import commonj.sdo.DataObject;
+
+/**
+ *
+ * A ResultSetProcessor is used to transform the data in a ResultSet into a set of inter-related EDataObjects.
+ */
+public class ResultSetProcessor {
+ private static final Logger logger = Logger.getLogger(ResultSetProcessor.class);
+
+ private TableRegistry registry;
+
+ private GraphBuilderMetadata metadata;
+
+ private final DataObjectMaker doMaker;
+
+ private final RowObjects tableObjects;
+
+ public ResultSetProcessor(DataObject g, GraphBuilderMetadata gbmd) {
+
+ this.metadata = gbmd;
+ if (metadata.getRelationships().size() == 0) {
+ registry = new SingleTableRegistry();
+ } else {
+ registry = new MultiTableRegistry();
+ }
+
+ doMaker = new DataObjectMaker(g);
+
+ tableObjects = new RowObjects(metadata, registry);
+
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug(metadata);
+ }
+
+ }
+
+ /**
+ * Process the ResultSet. For each row in the ResultSet, a
+ *
+ * @link ResultSetRow object will be created to represent the row as a set of EDataObjects. Then,
+ * the relevant relationships will be constructed
+ * between each object in the
+ * @link ResultSetRow.
+ *
+ * @param start
+ * @param end
+ */
+ public void processResults(int start, int end) throws SQLException {
+
+ Iterator i = metadata.getResultMetadata().iterator();
+ while (i.hasNext()) {
+ ResultMetadata resultMetadata = (ResultMetadata) i.next();
+ ResultSet results = resultMetadata.getResultSet();
+
+ processResultSet(results, resultMetadata, start, end);
+
+ // TODO These statements HAVE to be closed or we will have major problems
+ // results.getStatement().close();
+ results.close();
+ }
+ }
+
+ private void processResultSet(ResultSet rs, ResultMetadata rsMetadata, int start, int end) throws SQLException {
+ ResultSetRow rsr = new ResultSetRow(rsMetadata);
+ if (rs.getType() == ResultSet.TYPE_FORWARD_ONLY) {
+ while (rs.next() && start < end) {
+ rsr.processRow(rs);
+ int rootRowsCreated = addRowToGraph(rsr, rsMetadata);
+ start += rootRowsCreated;
+ }
+ } else {
+ int position = start;
+ while (rs.absolute(position) && start < end) {
+ rsr.processRow(rs);
+ int rootRowsCreated = addRowToGraph(rsr, rsMetadata);
+ start += rootRowsCreated;
+ ++position;
+ }
+ }
+ }
+
+ /**
+ * @param row
+ * @param resultMetadata
+ * @return the number of root rows created
+ */
+ private int addRowToGraph(ResultSetRow row, ResultMetadata resultMetadata) throws SQLException {
+ int rootRowsCreated = 0;
+ int objectsCreated = 0;
+ boolean recursive = row.isRecursive();
+ Set rootTableNames = metadata.getConfigWrapper().getRootTableNames();
+ tableObjects.clear();
+ Iterator tables = row.getAllTableData().iterator();
+ while (tables.hasNext()) {
+ TableData rawDataFromRow = (TableData) tables.next();
+
+ if (!rawDataFromRow.hasValidPrimaryKey() ||
+ (rawDataFromRow.hasNullPrimaryKey() && !rawDataFromRow.isTableEmpty())) {//some PK null , but other data present
+ //continue; - need to throw exception as anyway the result will give a wrong impression
+ //when any one table in result set misses PK column or has null value in PK column
+ throw new RuntimeException("Table "+rawDataFromRow.getTableName()+" in query does not include Primary Key "+
+ "column or has null value in it, can not proceed!");
+ }
+
+ String tableName = rawDataFromRow.getTableName();
+ DataObject tableObject = registry.get(tableName, rawDataFromRow.getPrimaryKeyValues());
+ boolean newlyCreated = (tableObject == null);
+ // check whether row is a new root row
+ if (newlyCreated) {
+ objectsCreated++;
+ // increment root row count
+ // in case of recursive table, assuming that first table occurrence is the root
+ if (rootTableNames.contains(tableName) && rawDataFromRow.getIndex() == 0) rootRowsCreated++;
+ // get whole table data
+ // (only for non-recursive statements; recursive statements already have the whole table data)
+ if (!recursive) rawDataFromRow = row.processRowForTable(tableName);
+ // create data object
+ tableObject = doMaker.createAndAddDataObject(rawDataFromRow, resultMetadata);
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Putting table " + tableName + " with PK "
+ + rawDataFromRow.getPrimaryKeyValues() + " into registry");
+ }
+
+ registry.put(tableName, rawDataFromRow.getPrimaryKeyValues(), tableObject);
+ }
+ else{
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Not Null tableObject");
+ }
+ }
+
+ if(tableObject != null){
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Do not allow any Null tableObject in tableObjects");
+ }
+ tableObjects.put(tableName, tableObject, newlyCreated);
+ }
+ }
+
+ if (objectsCreated == 0) {
+ // duplicated row
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Found duplicated row");
+ }
+ } else {
+ tableObjects.processRelationships();
+ }
+
+ return rootRowsCreated;
+
+ }
+
+}
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultSetRow.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultSetRow.java
new file mode 100644
index 0000000000..70e0961b09
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultSetRow.java
@@ -0,0 +1,390 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+
+/**
+ *
+ * A ResultSetRow is used to transform a row of a ResultSet into a set of EDataObjects.
+ */
+public class ResultSetRow {
+ private static final Logger logger = Logger.getLogger(ResultSetRow.class);
+
+ private final ResultMetadata metadata;
+ private final boolean recursive;
+ private final int resultSetSize;
+ private Collection allTableNames;
+ private Set recursiveTablePropertyNames;
+ private Set tablesWithNoPK = new HashSet();
+ private String[] tablePropertyNames;
+ private String[] columnPropertyNames;
+ private boolean[] isPKColumn;
+ private int[] indexesForPKs;
+ private Map indexesByTablePropertyName = new HashMap();
+ private Map tableMap = new HashMap();
+ private List allTableData = new ArrayList();
+ private ResultSet currentResultSet;
+
+ /**
+ * Method ResultSetRow.
+ *
+ * @param m
+ * the result metadata
+ */
+ public ResultSetRow(ResultMetadata m) throws SQLException {
+ this.metadata = m;
+ this.recursiveTablePropertyNames = m.getRecursiveTypeNames();
+ this.recursive = (recursiveTablePropertyNames.size() > 0);
+ this.resultSetSize = m.getResultSetSize();
+ cacheMetadata();
+ getAllTableNamesForRS();
+ getTablesWithNoPK();
+ }
+
+ /**
+ * Processes a single row in the ResultSet.
+ *
+ * @param rs
+ * A ResultSet positioned on the desired row
+ */
+ public final void processRow(ResultSet rs) throws SQLException {
+ // clear previous data
+ for (Iterator itTableData = allTableData.iterator(); itTableData.hasNext(); ) {
+ TableData tableData = (TableData) itTableData.next();
+ tableData.clear();
+ }
+ allTableData = new ArrayList();
+ // set current resultSet
+ currentResultSet = rs;
+ // process row
+ if (recursive) {
+ processRecursiveRow();
+ } else {
+ processNonRecursiveRow();
+ }
+ }
+
+ public final TableData processRowForTable(String tablePropertyName) throws SQLException {
+ int[] indexes = (int[]) indexesByTablePropertyName.get(tablePropertyName);
+ TableData table = getRawData(tablePropertyName);
+ int count = indexes.length;
+ for (int j = 0; j < count; j++) {
+ int i = indexes[j];
+ if (!isPKColumn[i]) {
+ // skipping primary key columns since they've already been processed by processRow()
+ Object data = getObject(currentResultSet, i);
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Adding column: " + columnPropertyNames[i] + "\tValue: "
+ + data + "\tTable: "
+ + tablePropertyNames[i]);
+ }
+ table.addData(columnPropertyNames[i], false, data);
+ }
+ }
+ return table;
+ }
+
+ private void processNonRecursiveRow() throws SQLException {
+ // parse primary keys only
+ // the rest will be parsed as needed
+ int count = indexesForPKs.length;
+ for (int j = 0; j < count; j++) {
+ int i = indexesForPKs[j];
+ Object data = getObject(currentResultSet, i);
+ if (data == null) {
+ // primary key is null, check other columns
+ String tablePropertyName = tablePropertyNames[i];
+ // if table data already exists then this has already been done
+ if (!tableMap.containsKey(tablePropertyName)) {
+ TableData table = getRawData(tablePropertyName);
+ processRowForTable(tablePropertyName);
+ // add table data only if not empty
+ if (!table.isTableEmpty()) {
+ table.addData(columnPropertyNames[i], true, data);
+ allTableData.add(table);
+ }
+ }
+ } else {
+ // add table data
+ TableData table = getRawData(tablePropertyNames[i]);
+ if (!allTableData.contains(table)) allTableData.add(table);
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Adding column: " + columnPropertyNames[i] + "\tValue: "
+ + data + "\tTable: "
+ + tablePropertyNames[i]);
+ }
+ table.addData(columnPropertyNames[i], true, data);
+ }
+ }
+ checkResultSetMissesPK();
+ }
+
+ //get all table names involved in current result set
+ //can not use metadata.getAllTablePropertyNames()
+ //as it gives table names for all tables from Config
+ private void getAllTableNamesForRS(){
+ allTableNames = new HashSet(resultSetSize);
+ for (int i = 1; i <= resultSetSize; i++) {
+ allTableNames.add(tablePropertyNames[i]);
+ }
+ }
+
+ private void cacheMetadata() {
+ tablePropertyNames = new String[resultSetSize + 1];
+ columnPropertyNames = new String[resultSetSize + 1];
+ isPKColumn = new boolean[resultSetSize + 1];
+ String tablePropertyName = null;
+ boolean isPK;
+ List pkColumnList = new ArrayList();
+ // loop thru indexes
+ for (int i = 1; i <= resultSetSize; i++) {
+ columnPropertyNames[i] = metadata.getColumnPropertyName(i);
+ tablePropertyName = metadata.getTablePropertyName(i);
+ tablePropertyNames[i] = tablePropertyName;
+ List indexes = (List) indexesByTablePropertyName.get(tablePropertyName);
+ if (indexes == null) {
+ indexes = new ArrayList();
+ indexesByTablePropertyName.put(tablePropertyName, indexes);
+ }
+ indexes.add(new Integer(i));
+ isPK = metadata.isPKColumn(i);
+ isPKColumn[i] = isPK;
+ if (isPK) {
+ pkColumnList.add(new Integer(i));
+ }
+ }
+ // reorganize indexes by table property name
+ for (Iterator itTablePropertyNames = indexesByTablePropertyName.keySet().iterator(); itTablePropertyNames.hasNext(); ) {
+ tablePropertyName = (String) itTablePropertyNames.next();
+ List indexes = (List) indexesByTablePropertyName.get(tablePropertyName);
+ int count = indexes.size();
+ int[] indexArray = new int[count];
+ for (int i = 0; i < count; i++) {
+ indexArray[i] = ((Integer) indexes.get(i)).intValue();
+ }
+ indexesByTablePropertyName.put(tablePropertyName, indexArray);
+ }
+ // reorganize indexes for PKs
+ int count = pkColumnList.size();
+ indexesForPKs = new int[count];
+ for (int i = 0; i < count; i++) {
+ indexesForPKs[i] = ((Integer) pkColumnList.get(i)).intValue();
+ }
+ }
+
+ private void getTablesWithNoPK(){
+ //case when result set omits PK column, take care of compound PKs too
+ boolean tableRSHasPK;
+ Iterator itr = allTableNames.iterator();
+ while(itr.hasNext()){
+ tableRSHasPK = false;
+ String currentTableName = (String)itr.next();
+ HashSet pks = metadata.getAllPKsForTable(currentTableName);
+ HashSet pksInRS = new HashSet();
+ for(int j=1; j<=resultSetSize; j++){
+ if(currentTableName.equals(tablePropertyNames[j]) &&
+ isPKColumn[j] ){
+ pksInRS.add(columnPropertyNames[j]);
+ }
+ }
+
+ //if pks null, means its classic case when all cols should be PKs
+ if(pks == null){
+ tableRSHasPK = true;
+ }
+ //case when there were cols in cfg but could not find any PK in it and no ID column in cfg
+ else if(pks != null && pks.size()==1 && pks.contains("")){
+ tableRSHasPK = false;
+ }
+ else if(pks != null && pksInRS.size() == pks.size()){
+ Iterator itr1 = pks.iterator();
+ int indx=0;
+ while(itr1.hasNext()){
+ if(!pksInRS.contains((String)itr1.next())){
+ indx++;
+ }
+ }
+
+ if(indx == 0){
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("has PK TRUE - matched");
+ }
+ tableRSHasPK = true;
+ }else{
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("has PK FALSE- mismatched");
+ }
+ tableRSHasPK = false;
+ }
+ }
+ else{
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("has PK FALSE - rest all cases");
+ }
+ }
+
+ if (!tableRSHasPK) tablesWithNoPK.add(currentTableName);
+
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("table "+currentTableName+" hasValidPK "+tableRSHasPK);
+ }
+ }
+ }
+
+ private void checkResultSetMissesPK(){
+ //Default is TRUE(from TableData), so consider only FALSE case
+ Iterator itr = tablesWithNoPK.iterator();
+ while(itr.hasNext()){
+ String currentTableName = (String)itr.next();
+ TableData table = getRawData(currentTableName);
+ table.setValidPrimaryKey(false);
+ allTableData.add(table);
+ }
+ }
+
+ private void processRecursiveRow() throws SQLException {
+ int i = 1;
+ // create map to keep track of recursive indexes
+ // each recursive table contains a 0-based index to keep track of the sequence
+ Map recursiveIndexes = new HashMap();
+ for (Iterator itTablePropertyNames = recursiveTablePropertyNames.iterator(); itTablePropertyNames.hasNext(); ) {
+ recursiveIndexes.put(itTablePropertyNames.next(), new Integer(-1));
+ }
+
+ // loop thru result set columns
+ // assuming that the columns of each recursive table are grouped together (metadata do not allow for further granularity)
+ while (i <= resultSetSize) {
+ TableData table;
+ String tablePropertyName = tablePropertyNames[i];
+ if (recursiveTablePropertyNames.contains(tablePropertyName)) {
+ // increment current recursive index for table
+ int recursiveIndex = ((Integer) recursiveIndexes.get(tablePropertyName)).intValue() + 1;
+ recursiveIndexes.put(tablePropertyName, new Integer(recursiveIndex));
+ // get table data
+ table = getRecursiveRawData(tablePropertyName, recursiveIndex);
+ } else {
+ table = getRawData(tablePropertyNames[i]);
+ }
+
+ while ((i <= resultSetSize) && (isPKColumn[i])) {
+ Object data = getObject(currentResultSet, i);
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Adding column: " + columnPropertyNames[i]
+ + "\tValue: " + data + "\tTable: "
+ + tablePropertyNames[i]);
+ }
+ table.addData(columnPropertyNames[i], true, data);
+ i++;
+ }
+
+ while ((i <= resultSetSize) && (!isPKColumn[i])) {
+ Object data = getObject(currentResultSet, i);
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Adding column: " + columnPropertyNames[i]
+ + "\tValue: " + data + "\tTable: "
+ + tablePropertyNames[i]);
+ }
+ table.addData(columnPropertyNames[i], false, data);
+ i++;
+ }
+
+ // skip table if empty
+ if (table.isTableEmpty()) {
+ table.clear();
+ } else {
+ this.allTableData.add(table);
+ }
+
+ }
+
+ checkResultSetMissesPK();
+ }
+
+ /**
+ * @param rs
+ * @param metadata
+ * @param i
+ * @return
+ */
+ private Object getObject(ResultSet rs, int i) throws SQLException {
+
+ Object data = rs.getObject(i);
+
+ if (rs.wasNull()) {
+ return null;
+ }
+
+ return metadata.convert(i, data);
+
+ }
+
+ /**
+ * Returns a HashMap that holds data for the specified table If the HashMap
+ * doesn't exist, it will be created. This is used internally to build
+ * the ResultSetRow, whereas getTable is used externally to retrieve existing table data.
+ *
+ * @param tableName
+ * The name of the table
+ * @return HashMap
+ */
+ private TableData getRawData(String tableName) {
+
+ TableData table = (TableData) tableMap.get(tableName);
+
+ if (table == null) {
+ table = new TableData(tableName);
+ tableMap.put(tableName, table);
+ }
+
+ return table;
+ }
+
+ private TableData getRecursiveRawData(String tableName, int recursiveIndex) {
+ String key = tableName + ":" + recursiveIndex;
+ TableData table = (TableData) tableMap.get(key);
+
+ if (table == null) {
+ table = new TableData(tableName, recursiveIndex);
+ tableMap.put(key, table);
+ }
+
+ return table;
+ }
+
+ public List getAllTableData() {
+ return this.allTableData;
+ }
+
+ public boolean isRecursive() {
+ return recursive;
+ }
+
+}
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/RowObjects.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/RowObjects.java
new file mode 100644
index 0000000000..b41b7379c8
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/RowObjects.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.apache.tuscany.das.rdb.config.KeyPair;
+import org.apache.tuscany.das.rdb.config.Relationship;
+import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper;
+
+import commonj.sdo.DataObject;
+import commonj.sdo.Property;
+
+public class RowObjects {
+ private static final Logger logger = Logger.getLogger(RowObjects.class);
+
+ private Map objectsByTableName;
+
+ private List tableObjects;
+
+ private Set newTableObjectNames;
+
+ private Map tableTypeNames;
+
+ private Map relationshipMap;
+
+ private final GraphBuilderMetadata metadata;
+
+ private final TableRegistry registry;
+
+ private final MappingWrapper configWrapper;
+
+ private final boolean hasRecursiveRelationships;
+
+ public RowObjects(GraphBuilderMetadata metadata, TableRegistry registry) {
+ objectsByTableName = new HashMap();
+ tableObjects = new ArrayList();
+ newTableObjectNames = new HashSet();
+ tableTypeNames = new HashMap();
+ this.metadata = metadata;
+ this.registry = registry;
+ this.configWrapper = metadata.getConfigWrapper();
+ this.hasRecursiveRelationships = configWrapper.hasRecursiveRelationships();
+ if (!hasRecursiveRelationships) buildRelationshipMap();
+ }
+
+ private void buildRelationshipMap() {
+ relationshipMap = new HashMap();
+ Iterator i = metadata.getRelationships().iterator();
+ while (i.hasNext()) {
+ Relationship r = (Relationship) i.next();
+ String parentTypeName = getTableTypeName(r.getPrimaryKeyTable());
+ String childTypeName = getTableTypeName(r.getForeignKeyTable());
+ // Add relationship under child type name
+ List relationships = (List) relationshipMap.get(childTypeName);
+ if (relationships == null) {
+ relationships = new ArrayList();
+ relationshipMap.put(childTypeName, relationships);
+ }
+ relationships.add(r);
+ // Add relationship under parent type name
+ relationships = (List) relationshipMap.get(parentTypeName);
+ if (relationships == null) {
+ relationships = new ArrayList();
+ relationshipMap.put(parentTypeName, relationships);
+ }
+ relationships.add(r);
+ }
+ }
+
+ public void clear() {
+ objectsByTableName.clear();
+ tableObjects.clear();
+ newTableObjectNames.clear();
+ }
+
+ public void put(String key, DataObject value, boolean newlyCreated) {
+ objectsByTableName.put(key, value);
+ tableObjects.add(value);
+ if (newlyCreated) newTableObjectNames.add(key);
+ }
+
+ public DataObject get(String tablePropertyName) {
+ return (DataObject) objectsByTableName.get(tablePropertyName);
+ }
+
+ private String getTableTypeName(String tableName) {
+ String typeName = (String) tableTypeNames.get(tableName);
+ if (typeName == null) {
+ typeName = configWrapper.getTableTypeName(tableName);
+ tableTypeNames.put(tableName, typeName);
+ }
+ return typeName;
+ }
+
+ public void processRelationships() {
+ if (hasRecursiveRelationships) {
+ processRecursiveRelationships(configWrapper);
+ return;
+ }
+
+ // the relationship needs to be set only if the parent or the child is newly created
+ // otherwise the relationship has already been set
+ Set relationshipsToSet = new HashSet();
+ Iterator itNewTableObjectNames = newTableObjectNames.iterator();
+ while (itNewTableObjectNames.hasNext()) {
+ List relationships = (List) relationshipMap.get((String) itNewTableObjectNames.next());
+ if (relationships != null) relationshipsToSet.addAll(relationships);
+ }
+
+ Iterator i = relationshipsToSet.iterator();
+ while (i.hasNext()) {
+ Relationship r = (Relationship) i.next();
+
+ String parentTypeName = getTableTypeName(r.getPrimaryKeyTable());
+ String childTypeName = getTableTypeName(r.getForeignKeyTable());
+ DataObject parent = get(parentTypeName);
+ DataObject child = get(childTypeName);
+
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Parent table: " + parent);
+ this.logger.debug("Child table: " + child);
+ }
+
+ setOrAdd(parent, child, r.getName());
+ }
+ }
+
+ private void processRecursiveRelationships(MappingWrapper wrapper) {
+ Iterator i = tableObjects.iterator();
+ while (i.hasNext()) {
+ DataObject table = (DataObject) i.next();
+
+ Iterator relationships = wrapper.getRelationshipsByChildTable(table.getType().getName()).iterator();
+ while (relationships.hasNext()) {
+ Relationship r = (Relationship) relationships.next();
+
+ DataObject parentTable = findParentTable(table, r, wrapper);
+
+
+ setOrAdd(parentTable, table, r.getName());
+ }
+
+ }
+ }
+
+ private void setOrAdd(DataObject parent, DataObject child, String propertyName) {
+ if (parent == null || child == null) return;
+ Property p = parent.getType().getProperty(propertyName);
+ if (p.isMany()) {
+ parent.getList(p).add(child);
+ } else {
+ parent.set(p, child);
+ }
+ }
+
+ private DataObject findParentTable(DataObject childTable, Relationship r, MappingWrapper wrapper) {
+
+ List fkValue = new ArrayList();
+ Iterator keyPairs = r.getKeyPair().iterator();
+ while (keyPairs.hasNext()) {
+ KeyPair pair = (KeyPair) keyPairs.next();
+ String childProperty = wrapper.getColumnPropertyName(r.getPrimaryKeyTable(), pair.getForeignKeyColumn());
+
+ Property p = childTable.getType().getProperty(childProperty);
+ fkValue.add(childTable.get(p));
+ }
+
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Trying to find parent of " + r.getForeignKeyTable() + " with FK " + fkValue);
+ }
+
+ DataObject parentTable = registry.get(r.getPrimaryKeyTable(), fkValue);
+
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Parent table from registry: " + parentTable);
+ }
+
+ return parentTable;
+ }
+
+}
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/SingleTableRegistry.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/SingleTableRegistry.java
new file mode 100644
index 0000000000..54474fa4b3
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/SingleTableRegistry.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.util.List;
+
+import commonj.sdo.DataObject;
+
+public class SingleTableRegistry implements TableRegistry {
+
+
+ public SingleTableRegistry() {
+ // Empty Constructor
+ }
+
+ public DataObject get(String tableName, List primaryKey) {
+ return null;
+ }
+
+
+ public void put(String tableName, List primaryKey, DataObject value) {
+ // do nothing
+
+ }
+
+ public boolean contains(String name, List list) {
+ return false;
+ }
+
+ public void remove(String tableName, List primaryKey) {
+
+ }
+}
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableData.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableData.java
new file mode 100644
index 0000000000..a99f7ff12f
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableData.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+public final class TableData {
+ private static final Logger logger = Logger.getLogger(TableData.class);
+
+ private Map columnData;
+
+ private List primaryKey;;
+
+ private final String name;
+
+ private int index = 0;
+
+ private boolean hasValidPrimaryKey = true;
+
+ private boolean hasNullPrimaryKey = false;
+
+ public TableData(String tableName) {
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Creating TableData for table " + tableName);
+ }
+
+ this.name = tableName;
+ this.columnData = new HashMap();
+ this.primaryKey = new ArrayList();
+ }
+
+ public TableData(String tableName, int index) {
+ this(tableName);
+ this.index = index;
+ }
+
+ public void clear() {
+ columnData.clear();
+ primaryKey.clear();
+ hasValidPrimaryKey = true;
+ hasNullPrimaryKey = false;
+ }
+
+ public void addData(String columnName, boolean isPrimaryKeyColumn, Object data) {
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Adding column " + columnName + " with value " + data);
+ }
+
+ if(data != null)
+ columnData.put(columnName, data);
+ if (isPrimaryKeyColumn) {
+ if (data == null) {
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Column " + columnName + " is a primary key column and is null");
+ }
+ hasNullPrimaryKey = true;
+ //hasValidPrimaryKey = false; - if uncommented and JIRA-1464, RecursiveTests.testReadEngineParts() will fail
+ } else {
+ primaryKey.add(data);
+ }
+ }
+ }
+
+ public Object getColumnData(String columnName) {
+ return columnData.get(columnName);
+ }
+
+ public String getTableName() {
+ return this.name;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * @return
+ */
+ public List getPrimaryKeyValues() {
+ // the primary key is kept in the table registry
+ // so return a copy to prevent deletion
+ List primaryKeyCopy = new ArrayList(primaryKey.size());
+ primaryKeyCopy.addAll(primaryKey);
+ return primaryKeyCopy;
+ }
+
+ public boolean hasValidPrimaryKey() {
+ return hasValidPrimaryKey;
+ }
+
+ public void setValidPrimaryKey(boolean hasValidPK){
+ this.hasValidPrimaryKey = hasValidPK;
+ }
+
+ public boolean isTableEmpty(){
+ return columnData.keySet().isEmpty();
+ }
+
+ public boolean hasNullPrimaryKey(){
+ return this.hasNullPrimaryKey;
+ }
+}
diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableRegistry.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableRegistry.java
new file mode 100644
index 0000000000..7a433fe240
--- /dev/null
+++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableRegistry.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.das.rdb.graphbuilder.impl;
+
+import java.util.List;
+
+import commonj.sdo.DataObject;
+
+public interface TableRegistry {
+ /**
+ * Get the table with the specified name and primary key
+ *
+ * @param tableName
+ * @param primaryKey
+ * @return DataObject
+ */
+ DataObject get(String tableName, List primaryKey);
+
+ /**
+ * Add the table with the specified name and primary key
+ *
+ * @param tableName
+ * @param primaryKey
+ * @param value
+ */
+ void put(String tableName, List primaryKey, DataObject value);
+
+ boolean contains(String name, List list);
+
+ void remove(String tableName, List primaryKey);
+} \ No newline at end of file