/*
* This software is distributed under the terms of the FSF
* Gnu Lesser General Public License (see lgpl.txt).
*
* This program is distributed WITHOUT ANY WARRANTY. See the
* GNU General Public License for more details.
*/
package com.scooterframework.orm.sqldataexpress.processor;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.scooterframework.common.util.Converters;
import com.scooterframework.orm.sqldataexpress.connection.UserDatabaseConnection;
import com.scooterframework.orm.sqldataexpress.exception.BaseSQLException;
import com.scooterframework.orm.sqldataexpress.object.ColumnInfo;
import com.scooterframework.orm.sqldataexpress.object.Cursor;
import com.scooterframework.orm.sqldataexpress.object.OmniDTO;
import com.scooterframework.orm.sqldataexpress.object.Parameter;
import com.scooterframework.orm.sqldataexpress.object.RowInfo;
import com.scooterframework.orm.sqldataexpress.service.SqlServiceConstants;
import com.scooterframework.orm.sqldataexpress.util.SqlUtil;
/**
* DataProcessorImpl class.
*
* @author (Fei) John Chen
*/
abstract public class DataProcessorImpl implements DataProcessor {
protected DatabaseMetaData dbmd;
private boolean hasCheckedSupportsGetGeneratedKeys;
private boolean supportsGetGeneratedKeys;
/**
* execute
*/
public OmniDTO execute(UserDatabaseConnection udc, Map<String, Object> inputs)
throws BaseSQLException {
return execute(udc, inputs, null);
}
/**
* execute with output filter
*/
abstract public OmniDTO execute(UserDatabaseConnection udc, Map<String, Object> inputs, Map<String, String> outputFilters)
throws BaseSQLException;
/**
* Returns DatabaseMetaData.
* @return an instance of DatabaseMetaData.
*/
public DatabaseMetaData getDatabaseMetaData() {
return dbmd;
}
/**
* Sets DatabaseMetaData.
* @param dbmd
*/
public void setDatabaseMetaData(DatabaseMetaData dbmd) {
this.dbmd = dbmd;
}
/**
* Checks to see if the connection supports generated keys.
* @return true if supports.
*/
protected boolean supportsGetGeneratedKeys() {
if (hasCheckedSupportsGetGeneratedKeys) return supportsGetGeneratedKeys;
try {
supportsGetGeneratedKeys = dbmd.supportsGetGeneratedKeys();
hasCheckedSupportsGetGeneratedKeys = true;
}
catch(Exception ex) {
;
}
return supportsGetGeneratedKeys;
}
protected Set<String> getAllowedColumns(Map<String, String> outputFilter, Cursor cursor) {
Set<String> allowedColumns = new HashSet<String>();
if (outputFilter == null) outputFilter = new HashMap<String, String>();
Set<String> exceptColumns = null;
String exceptColumnsStr = outputFilter.get(SqlServiceConstants.OUTPUT_FILTER_EXCEPT);
if (exceptColumnsStr != null && exceptColumnsStr.trim().length() > 0)
exceptColumns = Converters.convertStringToSet(exceptColumnsStr.toUpperCase());
Set<String> onlyColumns = null;
String onlyColumnsStr = outputFilter.get(SqlServiceConstants.OUTPUT_FILTER_ONLY);
if (onlyColumnsStr != null && onlyColumnsStr.trim().length() > 0)
onlyColumns = Converters.convertStringToSet(onlyColumnsStr.toUpperCase());
int columnWidth = cursor.getDimension();
for (int i = 0; i < columnWidth; i++) {
ColumnInfo ci = cursor.getColumnInfo(i);
String columnName = ci.getColumnName();
if ((exceptColumns != null && exceptColumns.contains(columnName)) ||
(onlyColumns != null && !onlyColumns.contains(columnName))) {
continue;
}
allowedColumns.add(columnName);
}
return allowedColumns;
}
// add those column names in the outputs to a new string array.
protected RowInfo getFilteredHeaderInfo(Set<String> allowedColumns, Cursor cursor) {
RowInfo header = new RowInfo();
int columnWidth = cursor.getDimension();
List<ColumnInfo> allowedColumnInfos = new ArrayList<ColumnInfo>();
for (int i = 0; i < columnWidth; i++) {
ColumnInfo ci = cursor.getColumnInfo(i);
if (allowedColumns.contains(ci.getColumnName())) {
allowedColumnInfos.add(ci);
}
}
header.setColumnInfoList(allowedColumnInfos);
return header;
}
protected void setNull(PreparedStatement pstmt, int parameterIndex, int targetSqlType)
throws SQLException {
if (Parameter.UNKNOWN_SQL_DATA_TYPE != targetSqlType) {
pstmt.setNull(parameterIndex, targetSqlType);
}
else {
pstmt.setNull(parameterIndex, java.sql.Types.OTHER);
}
}
protected void setObject(PreparedStatement pstmt, Object obj, Parameter p)
throws SQLException {
int parameterIndex = p.getIndex();
int targetSqlType = p.getSqlDataType();
if (obj == null) {
setNull(pstmt, parameterIndex, targetSqlType);
}
else {
if (targetSqlType == Types.DATE ||
targetSqlType == Types.TIME ||
targetSqlType == Types.TIMESTAMP) {
//This is date/time type.
setDateTimeObject(pstmt, parameterIndex, obj, targetSqlType);
}
else if (targetSqlType == Types.BOOLEAN) {
setBooleanObject(pstmt, parameterIndex, obj, targetSqlType);
}
else if (targetSqlType == Types.BIT ||
targetSqlType == Types.TINYINT ||
targetSqlType == Types.SMALLINT ||
targetSqlType == Types.INTEGER ||
targetSqlType == Types.BIGINT ||
targetSqlType == Types.REAL ||
targetSqlType == Types.FLOAT ||
targetSqlType == Types.DOUBLE ||
targetSqlType == Types.DECIMAL ||
targetSqlType == Types.NUMERIC
) {
setNumericObject(pstmt, obj, p);
}
else if (targetSqlType == Types.BINARY ||
targetSqlType == Types.VARBINARY ||
targetSqlType == Types.LONGVARBINARY
) {
setBinaryObject(pstmt, obj, p);
}
else if (targetSqlType == Types.BLOB ||
targetSqlType == Types.CLOB
) {
setBigData(pstmt, obj, p);
}
else {
//delegate to the underlying jdbc-driver implementation
pstmt.setObject(parameterIndex, obj, targetSqlType);
}
}
}
protected void setBooleanObject(PreparedStatement pstmt, int parameterIndex,
Object parameterObj, int targetSqlType)
throws SQLException {
if (parameterObj instanceof Boolean) {
pstmt.setBoolean(parameterIndex, ((Boolean)parameterObj).booleanValue());
} else if (parameterObj instanceof String) {
pstmt.setBoolean(parameterIndex, "true".equalsIgnoreCase((String)parameterObj) ||
"t".equalsIgnoreCase((String)parameterObj) ||
!"0".equalsIgnoreCase((String)parameterObj) ||
"y".equalsIgnoreCase((String)parameterObj) ||
"yes".equalsIgnoreCase((String)parameterObj));
} else if (parameterObj instanceof Number) {
int intValue = ((Number)parameterObj).intValue();
pstmt.setBoolean(parameterIndex, intValue != 0);
} else {
throw new SQLException("Cannot convert from " + parameterObj.getClass().getName() +
" to BOOLEAN for object " + parameterObj +
" at index " + parameterIndex + ".");
}
}
protected void setNumericObject(PreparedStatement pstmt, Object obj, Parameter p)
throws SQLException {
int parameterIndex = p.getIndex();
int targetSqlType = p.getSqlDataType();
if (targetSqlType == Types.TINYINT ||
targetSqlType == Types.SMALLINT ||
targetSqlType == Types.INTEGER) {
int x = convert2int(obj, p);
pstmt.setInt(parameterIndex, x);
}
else if (targetSqlType == Types.BIGINT) {
long x = convert2long(obj, p);
pstmt.setLong(parameterIndex, x);
}
else if (targetSqlType == Types.FLOAT) {
float x = convert2float(obj, p);
pstmt.setFloat(parameterIndex, x);
}
else if (targetSqlType == Types.DOUBLE || targetSqlType == Types.REAL) {
double x = convert2double(obj, p);
pstmt.setDouble(parameterIndex, x);
}
else if (targetSqlType == Types.DECIMAL) {
BigDecimal x = convert2BigDecimal(obj, p);
pstmt.setBigDecimal(parameterIndex, x);
}
else {
pstmt.setObject(parameterIndex, obj, targetSqlType);
}
}
/**
* Stores object as binary type in database.
*
* Acceptable types of <tt>obj</tt> are <tt>java.io.InputStream</tt>,
* <tt>byte[]</tt>, <tt>java.io.File</tt>, <tt>java.lang.String</tt> and
* any object whose content can be obtained from its
* <tt>toString()</tt> method.
*
* @param pstmt PreparedStatement statement
* @param obj The data to be persisted
* @param p Parameter instance
* @throws SQLException
*/
protected void setBinaryObject(PreparedStatement pstmt, Object obj, Parameter p)
throws SQLException {
try {
if (obj != null) {
InputStream is = getInputStream(obj);
pstmt.setBinaryStream(p.getIndex(), is, is.available());
is.close();
}
else {
pstmt.setBinaryStream(p.getIndex(), (InputStream)null, 0);
}
}
catch(Exception ex) {
throw new SQLException(ex.getMessage());
}
}
/**
* Stores object as either blob or clob type in database.
*
* Acceptable types of <tt>obj</tt> are <tt>java.io.InputStream</tt>,
* <tt>byte[]</tt>, <tt>java.io.File</tt>, <tt>java.lang.String</tt> and
* any object whose content can be obtained from its
* <tt>toString()</tt> method.
*
* @param pstmt PreparedStatement statement
* @param obj The data to be persisted
* @param p Parameter instance
* @throws SQLException
*/
protected void setBigData(PreparedStatement pstmt, Object obj, Parameter p)
throws SQLException {
try {
int targetSqlType = p.getSqlDataType();
if (targetSqlType == Types.BLOB) {
if (obj != null) {
InputStream is = getInputStream(obj);
pstmt.setBinaryStream(p.getIndex(), is, is.available());
is.close();
}
else {
pstmt.setBinaryStream(p.getIndex(), (InputStream)null, 0);
}
}
else if (targetSqlType == Types.CLOB) {
if (obj != null) {
String tmp = (String)obj;
int strLength = tmp.length();
Reader r = new StringReader(tmp);
pstmt.setCharacterStream(p.getIndex(), r, strLength);
r.close();
}
else {
pstmt.setCharacterStream(p.getIndex(), (Reader)null, 0);
}
}
}
catch(Exception ex) {
throw new SQLException(ex.getMessage());
}
}
protected InputStream getInputStream(Object data) throws FileNotFoundException {
InputStream is = null;
if (data != null) {
if (data instanceof InputStream) {
is = (InputStream)data;
}
else if (data instanceof byte[]) {
is = new ByteArrayInputStream((byte[])data);
}
else if (data instanceof File) {
is = new FileInputStream((File)data);
}
else if (data instanceof String) {
is = new ByteArrayInputStream(((String)data).getBytes());
}
else {
is = new ByteArrayInputStream((data.toString()).getBytes());
}
}
return is;
}
protected int convert2int(Object obj, Parameter p) {
Integer i = null;
try {
i = Integer.valueOf(obj.toString());
}
catch(Exception ex) {
throw new IllegalArgumentException("Failed to convert object of " +
"value \"" + obj + "\" to Integer for parameter \"" +
p + "\".");
}
return i.intValue();
}
protected long convert2long(Object obj, Parameter p) {
Long l = null;
try {
l = new Long(obj.toString());
}
catch(Exception ex) {
throw new IllegalArgumentException("Failed to convert object of " +
"value \"" + obj + "\" to Long for parameter \"" +
p + "\".");
}
return l.longValue();
}
protected float convert2float(Object obj, Parameter p) {
Float f = null;
try {
f = new Float(obj.toString());
}
catch(Exception ex) {
throw new IllegalArgumentException("Failed to convert object of " +
"value \"" + obj + "\" to Float for parameter \"" +
p + "\".");
}
return f.floatValue();
}
protected double convert2double(Object obj, Parameter p) {
Double d = null;
try {
d = new Double(obj.toString());
}
catch(Exception ex) {
throw new IllegalArgumentException("Failed to convert object of " +
"value \"" + obj + "\" to Double for parameter \"" +
p + "\".");
}
return d.doubleValue();
}
protected BigDecimal convert2BigDecimal(Object obj, Parameter p) {
BigDecimal b = null;
try {
b = new BigDecimal(obj.toString());
}
catch(Exception ex) {
throw new IllegalArgumentException("Failed to convert object of " +
"value \"" + obj + "\" to BigDecimal for parameter \"" +
p + "\".");
}
return b;
}
protected void setDateTimeObject(PreparedStatement pstmt, int parameterIndex,
Object parameterObj, int targetSqlType)
throws SQLException {
try {
switch (targetSqlType) {
case Types.DATE:
java.sql.Date parameterAsSQLDate = null;
if (parameterObj instanceof String) {
parameterAsSQLDate = SqlUtil.convertStringToSQLDate((String)parameterObj);
}
else if (parameterObj instanceof java.sql.Date) {
parameterAsSQLDate = (java.sql.Date) parameterObj;
}
else if (parameterObj instanceof java.util.Date) {//This includes Time, Timestamp cases.
parameterAsSQLDate = new java.sql.Date(((java.util.Date)parameterObj).getTime());
}
else {
parameterAsSQLDate = SqlUtil.convertStringToSQLDate(parameterObj.toString());
}
pstmt.setDate(parameterIndex, parameterAsSQLDate);
break;
case Types.TIMESTAMP:
java.sql.Timestamp parameterAsSQLTimestamp = null;
if (parameterObj instanceof String) {
parameterAsSQLTimestamp = SqlUtil.convertStringToSQLTimestamp((String)parameterObj);
}
else if (parameterObj instanceof java.sql.Timestamp) {
parameterAsSQLTimestamp = (java.sql.Timestamp) parameterObj;
}
else if (parameterObj instanceof java.util.Date) {//This includes Date, Time cases.
parameterAsSQLTimestamp = new java.sql.Timestamp(((java.util.Date)parameterObj).getTime());
}
else {
parameterAsSQLTimestamp = SqlUtil.convertStringToSQLTimestamp(parameterObj.toString());
}
pstmt.setTimestamp(parameterIndex, parameterAsSQLTimestamp);
break;
case Types.TIME:
java.sql.Time parameterAsSQLTime = null;
if (parameterObj instanceof String) {
parameterAsSQLTime = SqlUtil.convertStringToSQLTime((String)parameterObj);
}
else if (parameterObj instanceof java.sql.Time) {
parameterAsSQLTime = (java.sql.Time) parameterObj;
}
else if (parameterObj instanceof java.util.Date) {//This includes Date, Timestamp cases.
parameterAsSQLTime = new java.sql.Time(((java.util.Date)parameterObj).getTime());
}
else {
parameterAsSQLTime = SqlUtil.convertStringToSQLTime(parameterObj.toString());
}
pstmt.setTime(parameterIndex, parameterAsSQLTime);
break;
default:
pstmt.setObject(parameterIndex, parameterObj, targetSqlType);
}
}
catch(Exception ex) {
if (ex instanceof SQLException) {
throw (SQLException) ex;
}
else {
String message = "Failed to setObject: " + ex.getMessage() +
", Param Index = " + parameterIndex +
", TargetSqlType = " + targetSqlType +
", parameterObj = " + parameterObj + ".";
throw new SQLException(message);
}
}
}
}