/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2011-2013, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.db.reverse;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.sis.feature.FeatureExt;
import org.apache.sis.storage.DataStoreException;
import org.geotoolkit.db.DefaultJDBCFeatureStore;
import org.geotoolkit.db.JDBCFeatureStoreUtilities;
import org.geotoolkit.db.dialect.SQLDialect;
/**
* Description of a table column.
*
* @author Johann Sorel (Geomatys)
*/
public class ColumnMetaModel {
public static enum Type{
/** value is automaticaly generated by the database */
AUTO,
/** value will not be generated by the database, must be provided */
NON_INCREMENTING,
/** value is automaticaly generated by the database using a sequence */
SEQUENCED
}
private final String schema;
private final String table;
private final String name;
private final int sqlType;
private final String sqlTypeName;
private final Class clazz;
private final Type type;
private final String sequenceName;
/**
* @param name column name
* @param sqlType sql type
* @param sqlTypeName sql type name
* @param clazz best java matching type
* @param type if column is a primary key column, define how value is found
*/
public ColumnMetaModel(String schema, String table, String name,
int sqlType, String sqlTypeName, Class clazz, Type type) {
this(schema, table, name,sqlType,sqlTypeName,clazz,type,null);
}
/**
* @param name column name
* @param sqlType sql type
* @param sqlTypeName sql type name
* @param clazz best java matching type
* @param type if column is a primary key column, define how value is found
* @param sequenceName the sequence name if type SEQUENCED
*/
public ColumnMetaModel(String schema, String table, String name,
int sqlType, String sqlTypeName, Class clazz, Type type, String sequenceName) {
this.schema = schema;
this.table = table;
this.name = name;
this.sqlType = sqlType;
this.sqlTypeName = sqlTypeName;
this.clazz = clazz;
this.type = type;
this.sequenceName = sequenceName;
}
public String getSchema() {
return schema;
}
public String getTable() {
return table;
}
public String getName() {
return name;
}
public int getSqlType() {
return sqlType;
}
public String getSqlTypeName() {
return sqlTypeName;
}
public Class getJavaType() {
return clazz;
}
public Type getType() {
return type;
}
public String getSequenceName() {
return sequenceName;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(name);
sb.append('[');
sb.append(sqlType);
sb.append(',');
sb.append(sqlTypeName);
sb.append(',');
sb.append(type);
sb.append(']');
return sb.toString();
}
/**
* Calculate the next column value.
*/
public Object nextColumnValue(final DefaultJDBCFeatureStore store, final Connection cx)
throws SQLException, DataStoreException {
Object next = null;
final SQLDialect dialect = store.getDialect();
if(type == Type.AUTO){
next = dialect.nextValue(this, cx);
} else if(type == Type.SEQUENCED){
next = dialect.nextValue(this, cx);
} else {
// generate a default value if possible
if (Number.class.isAssignableFrom(clazz)) {
//search the max value.
final StringBuilder sql = new StringBuilder();
sql.append("SELECT MAX(");
dialect.encodeColumnName(sql, getName());
sql.append(") FROM ");
dialect.encodeSchemaAndTableName(sql, schema, table);
final Statement st = cx.createStatement();
ResultSet rs = null;
try {
rs = st.executeQuery(sql.toString());
rs.next();
next = rs.getObject(1);
} finally {
JDBCFeatureStoreUtilities.closeSafe(store.getLogger(),null, st, rs);
}
if(next == null){
// This probably means there was no data in the table, set to 1
// TODO: probably better to do a count to check... but if this
// value already exists the db will throw an error when it tries
// to insert
next = 1;
}else if (clazz == Short.class || clazz == Integer.class || clazz == Long.class
|| BigInteger.class.isAssignableFrom(clazz)
|| BigDecimal.class.isAssignableFrom(clazz) ) {
next = ((Number)next).longValue() +1;
}else if (clazz == Float.class){
next = Math.nextUp( ((Number)next).floatValue() );
}else if (clazz == Double.class){
next = Math.nextUp( ((Number)next).doubleValue() );
}else{
//can't calculate for other types
next = 1;
}
} else if (CharSequence.class.isAssignableFrom(clazz)) {
//generate a random string
next = FeatureExt.createDefaultFeatureId();
}
if (next == null) {
throw new DataStoreException("Cannot generate key value for column of type: " + clazz.getName());
}
}
return next;
}
}