diff options
author | lresende <lresende@13f79535-47bb-0310-9956-ffa450edef68> | 2009-11-02 22:22:29 +0000 |
---|---|---|
committer | lresende <lresende@13f79535-47bb-0310-9956-ffa450edef68> | 2009-11-02 22:22:29 +0000 |
commit | c6103c7c9a6b6971d2aa0b8e3997608cb5f2c36f (patch) | |
tree | b3bf9651ebfd1c6f6230f69342cd2fc6686a7b98 /das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder | |
parent | dbfb7d4e41886fd91f33e434c57d207061106c38 (diff) |
Move the das folder as new trunk for das sub project
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@832144 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder')
12 files changed, 2148 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 diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/schema/ResultSetTypeMap.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/schema/ResultSetTypeMap.java new file mode 100644 index 0000000000..35701f88d0 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/schema/ResultSetTypeMap.java @@ -0,0 +1,137 @@ +/* + * 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.schema; + +import java.sql.Types; + +import org.apache.tuscany.sdo.SDOPackage; + +import commonj.sdo.Type; +import commonj.sdo.helper.TypeHelper; + +/** + */ +public class ResultSetTypeMap { + + public static final ResultSetTypeMap INSTANCE = new ResultSetTypeMap(); + + /** + * Constructor for ResultSetTypeMap. + */ + protected ResultSetTypeMap() { + // Empty Constructor + } + + /** + * + * @param type + * @param isNullable + * @return + */ + public Type getEDataType(int type, boolean isNullable) { + + TypeHelper helper = TypeHelper.INSTANCE; + SDOPackage.eINSTANCE.eClass(); + switch (type) { + + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + return helper.getType("commonj.sdo", "String"); + + case Types.NUMERIC: + case Types.DECIMAL: + return helper.getType("commonj.sdo", "Decimal"); + + case Types.BIT: + case Types.BOOLEAN: + if (isNullable) { + return helper.getType("commonj.sdo", "Boolean"); + } + return helper.getType("commonj.sdo", "boolean"); + + + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + if (isNullable) { + return helper.getType("commonj.sdo", "IntObject"); + } + + return helper.getType("commonj.sdo", "Int"); + + + case Types.BIGINT: + if (isNullable) { + return helper.getType("commonj.sdo", "Long"); + } + return helper.getType("commonj.sdo", "long"); + + case Types.REAL: + if (isNullable) { + return helper.getType("commonj.sdo", "Float"); + } + return helper.getType("commonj.sdo", "float"); + + + case Types.FLOAT: + case Types.DOUBLE: + if (isNullable) { + return helper.getType("commonj.sdo", "Double"); + } + return helper.getType("commonj.sdo", "double"); + + + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + return helper.getType("commonj.sdo", "Bytes"); + + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return helper.getType("commonj.sdo", "Date"); + + case Types.CLOB: + return helper.getType("commonj.sdo", "Object"); + + case Types.BLOB: + return helper.getType("commonj.sdo", "Bytes"); + + case Types.ARRAY: + return helper.getType("commonj.sdo", "Object"); + + case Types.DISTINCT: + case Types.STRUCT: + case Types.REF: + case Types.DATALINK: + case Types.JAVA_OBJECT: + return helper.getType("commonj.sdo", "Object"); + + default: + return helper.getType("commonj.sdo", "Object"); + } + + } + + public Type getType(int columnType, boolean b) { + return getEDataType(columnType, b); + } + +} |