blob: 39f35150ec7906f4b58a155d64286c86e59291bf [file] [log] [blame]
/*
* 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);
}
}