/*
* Copyright 2012 The Solmix Project
*
* This 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; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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.
*
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.gnu.org/licenses/
* or see the FSF site: http://www.fsf.org.
*/
package org.solmix.sql;
import java.io.Reader;
import java.io.StringWriter;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.solmix.runtime.SystemContext;
import org.solmix.api.datasource.DataSource;
import org.solmix.api.exception.SlxException;
import org.solmix.api.jaxb.Efield;
import org.solmix.api.jaxb.Tfield;
import org.solmix.api.jaxb.ToperationBinding;
import org.solmix.api.types.Texception;
import org.solmix.api.types.Tmodule;
import org.solmix.commons.util.DataUtils;
import org.solmix.commons.util.IOUtils;
import org.solmix.fmk.SlxContext;
import org.solmix.fmk.base.Reflection;
import org.solmix.fmk.event.EventWorker;
import org.solmix.fmk.event.EventWorkerFactory;
import org.solmix.fmk.util.DataTools;
import org.solmix.fmk.util.SLXDate;
/**
*
* @author solmix.f@gmail.com
* @version 110035 2011-3-30
*/
@SuppressWarnings("unchecked")
public class SQLTransform
{
private static Logger log = LoggerFactory.getLogger(SQLTransform.class.getName());
/**
* @param dbName
* @return
*/
public static boolean hasBrokenCursorAPIs(SQLDriver driver) {
return driver.hasBrokenCursorAPIs();
}
public static List<Object> toListOfMapsOrBeans(ResultSet rs, boolean brokenCursorAPIs, List<SQLDataSource> dataSources) throws SQLException,
SlxException {
return toListOfMapsOrBeans(rs, -1L, brokenCursorAPIs, dataSources);
}
public static List<Object> toListOfMapsOrBeans(ResultSet rs, long numRows, boolean brokenCursorAPIs, List<SQLDataSource> dataSources)
throws SQLException, SlxException {
return toListOfMapsOrBeans(rs, numRows, brokenCursorAPIs, dataSources, null);
}
public static List<Object> toListOfMapsOrBeans(ResultSet rs, SQLDriver driver, List<SQLDataSource> dataSources, ToperationBinding opConfig)
throws SQLException, SlxException {
return toListOfMapsOrBeans(rs, -1L, hasBrokenCursorAPIs(driver), dataSources, opConfig);
}
public static List<Object> toListOfMapsOrBeans(ResultSet rs, boolean brokenCursorAPIs, List<SQLDataSource> dataSources, ToperationBinding opConfig)
throws SQLException, SlxException {
return toListOfMapsOrBeans(rs, -1L, brokenCursorAPIs, dataSources, opConfig);
}
public static List<Object> toListOfMapsOrBeans(ResultSet rs, long numRows, SQLDriver driver, List<SQLDataSource> dataSources,
ToperationBinding opConfig) throws SQLException, SlxException {
return toListOfMapsOrBeans(rs, numRows, hasBrokenCursorAPIs(driver), dataSources, opConfig);
}
/**
* @param resultSet
* @param l
* @param hasBrokenCursorAPIs
* @param dataSources
* @param opConfig
* @return
* @throws SQLException
* @throws SlxException
*/
private static List<Object> toListOfMapsOrBeans(ResultSet resultSet, long rowNum, boolean hasBrokenCursorAPIs, List<SQLDataSource> dataSources,
ToperationBinding opConfig) throws SQLException, SlxException {
List<Object> __return = new ArrayList<Object>();
Map<String, String> _caseInsensitiveMap = new HashMap<String, String>();
ResultSetMetaData _rsmd;
boolean _useColumnLabel = false;
String _beanClassName = null;
List<String> _outputs = null;
if (opConfig != null) {
// _beanClassName=opConfig.getBeanClassName();
String outputsString = opConfig.getOutputs();
if (outputsString != null) {
_outputs = new ArrayList<String>();
String outputsArray[] = outputsString.split(",");
for (int i = 0; i < outputsArray.length; i++)
_outputs.add(outputsArray[i].trim());
}
}
/**
* get bean class name from datasource.
*/
long _$ = System.currentTimeMillis();
// If ResultSet is null.
if (hasBrokenCursorAPIs) {
if (!resultSet.next())
return __return;
} else {
boolean isBeforeFirst = false;
boolean isAfterLast = false;
try {
isBeforeFirst = resultSet.isBeforeFirst();
isAfterLast = resultSet.isAfterLast();
} catch (SQLException ignored) {
log.debug("isBeforeFirst()/isAfterLast() throwing exceptions .", ignored);
}
if ((isBeforeFirst || isAfterLast || resultSet.getRow() == 0) && !resultSet.next())
return __return;
}
_rsmd = resultSet.getMetaData();
if (dataSources != null) {
DataSource firstDS = dataSources.get(0);
if (firstDS instanceof SQLDataSource) {
SQLDriver driver = ((SQLDataSource) firstDS).getDriver();
_useColumnLabel = driver.useColumnLabelInMetadata();
}
_beanClassName = (String) DataUtils.getProperty("bean", opConfig, firstDS.getContext().getTdataSource());
}
if (dataSources != null) {
int count = _rsmd.getColumnCount();
for (int i = 1; i <= count; i++) {
String fieldName = null;
String columnName;
if (_useColumnLabel)
columnName = _rsmd.getColumnLabel(i);
else
columnName = _rsmd.getColumnName(i);
for (Object ds : dataSources) {
List<String> names = ((DataSource) ds).getContext().getFieldNames();
for (String name : names) {
if (name.equalsIgnoreCase(columnName)) {
fieldName = name;
break;
}
}
}
if (fieldName != null)
_caseInsensitiveMap.put(columnName, fieldName);
else
_caseInsensitiveMap.put(columnName, columnName);
}
}// END ?DS
long i = 0;
do {
if (i >= rowNum && rowNum != -1L)
break;
Map map = toAttributeMap(resultSet, dataSources, _rsmd, _useColumnLabel, _caseInsensitiveMap, _outputs);
if (DataUtils.isNullOrEmpty(_beanClassName))
__return.add(map);
else {
try {
Object bean = Reflection.newInstance(_beanClassName);
DataUtils.setProperties(map, bean);
__return.add(bean);
} catch (Exception e) {
throw new SlxException(Tmodule.DATASOURCE, Texception.CAN_NOT_INSTANCE, e);
}
}
/**
* java.sql.ResultSet.next() move cursor to new row set.
*/
if (!resultSet.next())
break;
i++;
} while (true);
long $_ = System.currentTimeMillis();
createEventWork().createAndFireTimeEvent($_ - _$, "SQLTransform (" + __return.size() + " rows): ");
return __return;
}
public static Map<String, ?> toAttributeMap(ResultSet resultSet) throws SQLException {
return toAttributeMap(resultSet, (List<SQLDataSource>) null, null, true, null, null);
}
/**
* @param resultSet
* @param rsmd
* @param useColumnLabel
* @param caseInsensitiveMap
* @param outputs
* @return
* @throws SQLException
*/
public static Map<String, ?> toAttributeMap(ResultSet resultSet, List<SQLDataSource> dataSources, ResultSetMetaData rsmd, boolean useColumnLabel,
Map<String, String> caseInsensitiveMap, List<String> outputs) throws SQLException {
if (rsmd == null)
rsmd = resultSet.getMetaData();
int count = rsmd.getColumnCount();
Map<String, Object> __return = new HashMap<String, Object>();
for (int colCursor = 1; colCursor <= count; colCursor++) {
String columnName;
if (useColumnLabel)
columnName = rsmd.getColumnLabel(colCursor);
else
columnName = rsmd.getColumnName(colCursor);
if (caseInsensitiveMap != null && caseInsensitiveMap.get(columnName) != null)
columnName = caseInsensitiveMap.get(columnName);
Object obj = resultSet.getObject(colCursor);
if (outputs != null && !outputs.contains(columnName))
continue;
if (obj == null) {
__return.put(columnName, obj);
continue;
}
if (dataSources != null) {
for (Object o : dataSources) {
DataSource ds = (DataSource) o;
Tfield field = ds.getContext().getField(columnName);
if (field == null)
continue;
if ((obj instanceof Clob)) {
Reader read = resultSet.getCharacterStream(colCursor);
StringWriter sw = new StringWriter();
try {
IOUtils.copyCharacterStreams(read, sw);
} catch (Exception e) {
throw new SQLException(e.getMessage());
}
obj = sw.toString();
} else if (field.getType() == Efield.BOOLEAN) {
String sqlType = field.getSqlStorageStrategy();
if ("number".equals(sqlType) || "integer".equals(sqlType) || "singleChar10".equals(sqlType))
obj = "1".equals(obj.toString()) ? ((Object) (Boolean.TRUE)) : ((Object) (Boolean.FALSE));
else if ("singleCharYN".equals(sqlType))
obj = "Y".equals(obj.toString()) ? ((Object) (Boolean.TRUE)) : ((Object) (Boolean.FALSE));
else if ("singleCharTF".equals(sqlType))
obj = "T".equals(obj.toString()) ? ((Object) (Boolean.TRUE)) : ((Object) (Boolean.FALSE));
else
obj = Boolean.valueOf(obj.toString());
} else if (DataTools.isBinary(field)) {
obj = resultSet.getBinaryStream(colCursor);
} else if (field.getType() == Efield.DATE || field.getType() == Efield.DATETIME) {
String sqlType = field.getSqlStorageStrategy();
String dateFormat = field.getDateFormat();
if ("number".equals(sqlType) || "text".equals(sqlType) || DataUtils.isNotNullAndEmpty(dateFormat)) {
if (DataUtils.isNullOrEmpty(dateFormat))
dateFormat = "yyyyMMdd";
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
try {
String str = obj.toString();
if (str.length() < dateFormat.length()) {
for (int j = str.length(); j < dateFormat.length(); j++)
str = (new StringBuilder()).append("0").append(str).toString();
}
obj = sdf.parse(str);
} catch (ParseException pe) {
log.warn((new StringBuilder()).append("Unable to parse a valid date, time or datetime out of value ").append(obj).append(
" using format string ").append(dateFormat).toString());
}
} else if (obj instanceof Date) {
obj = new SLXDate(((Date) obj).getTime());
} else {
log.warn((new StringBuilder()).append("received a non-java.util.Date class: ").append(obj.getClass().getName()).append(
"for date field: ").append(field.getName()).toString());
}
} else {
obj = ds.transformFieldValue(field, obj);
}
}
}// END ?dataSources.
__return.put(columnName, obj);
}
return __return;
}
public static List<Map<String, ?>> toListOfMaps(ResultSet rs) throws SQLException {
return toListOfMaps(rs, -1L, false);
}
public static List<Map<String, ?>> toListOfMaps(ResultSet rs, SQLDriver driver) throws SQLException {
return toListOfMaps(rs, -1L, hasBrokenCursorAPIs(driver));
}
public static List<Map<String, ?>> toListOfMaps(ResultSet rs, boolean brokenCursorAPIs) throws SQLException {
return toListOfMaps(rs, -1L, brokenCursorAPIs);
}
public static List<Map<String, ?>> toListOfMaps(ResultSet rs, long numRows) throws SQLException {
return toListOfMaps(rs, numRows, false);
}
public static List<Map<String, ?>> toListOfMaps(ResultSet rs, long numRows, SQLDriver driver) throws SQLException {
return toListOfMaps(rs, numRows, hasBrokenCursorAPIs(driver));
}
public static List<Map<String, ?>> toListOfMaps(ResultSet resultSet, long numRows, boolean hasBrokenCursorAPIs) throws SQLException {
List<Map<String, ?>> __return = new ArrayList<Map<String, ?>>(128);
/**
* get bean class name from datasource.
*/
long _$ = System.currentTimeMillis();
// If ResultSet is null.
if (hasBrokenCursorAPIs) {
if (!resultSet.next())
return __return;
} else {
boolean isBeforeFirst = false;
boolean isAfterLast = false;
try {
isBeforeFirst = resultSet.isBeforeFirst();
isAfterLast = resultSet.isAfterLast();
} catch (SQLException ignored) {
log.debug("isBeforeFirst()/isAfterLast() throwing exceptions .", ignored);
}
if ((isBeforeFirst || isAfterLast || resultSet.getRow() == 0) && !resultSet.next())
return __return;
}
long i = 0;
do {
if (i >= numRows && numRows != -1L)
break;
Map<String, ?> map = toAttributeMap(resultSet);
__return.add(map);
/**
* java.sql.ResultSet.next() move cursor to new row set.
*/
if (!resultSet.next())
break;
i++;
} while (true);
long $_ = System.currentTimeMillis();
createEventWork().createAndFireTimeEvent($_ - _$, new StringBuilder().append("SQLTransform (" ).append( __return.size() ).append( " rows): ").toString());
return __return;
}
public static Map<String, List<Object>> toMapOfLists(ResultSet rs) throws SQLException {
Map<String, List<Object>> result = new HashMap<String, List<Object>>(128);
ResultSetMetaData header = rs.getMetaData();
for (int ii = 1; ii <= header.getColumnCount(); ii++)
result.put(header.getColumnName(ii), new ArrayList<Object>());
while (rs.next()) {
int ii = 1;
while (ii <= header.getColumnCount()) {
result.get(header.getColumnName(ii)).add(rs.getObject(ii));
ii++;
}
}
return result;
}
public static List<List<Object>> toFormatList(ResultSet results, List<String> column) throws SQLException {
List<List<Object>> __return = new ArrayList<List<Object>>(128);
if (results == null)
return __return;
ResultSetMetaData header = results.getMetaData();
List<Object> _tmp;
boolean writeFlag = false;
while (results.next()) {
int i = 1;
_tmp = new ArrayList<Object>();
while (i <= header.getColumnCount()) {
if (!writeFlag) {
if (column == null) {
column = new ArrayList<String>();
}
column.add(header.getColumnName(i));
}
_tmp.add(results.getObject(i));
i++;
}
writeFlag = true;
__return.add(_tmp);
}
return __return;
}
public static List<Object> toValuesList(ResultSet results, String column) throws SQLException {
List<Object> valuesList = new ArrayList<Object>(128);
do {
if (!results.next())
break;
Object value = results.getObject(column.toUpperCase());
if (value != null)
valuesList.add(value);
} while (true);
return valuesList;
}
/**
* @param <T>
* @param results
* @param clz
* @return
* @throws SQLException
*/
public static <T> List<T> toListofBeans(ResultSet results, Class<T> clz) throws SQLException {
List<?> list = toListOfMaps(results);
List<T> _return = new ArrayList<T>();
if (list == null)
return null;
try {
for (Object o : list) {
Map<Object, Object> data = null;
if (o instanceof Map<?, ?>) {
data = (Map<Object, Object>) o;
} else {
continue;
}
T obj = Reflection.newInstance(clz);
DataUtils.setProperties(data, obj, false);
_return.add(obj);
}
} catch (Exception e) {
log.error("can not transform data to bean object", e);
}
return _return;
}
public static EventWorker createEventWork( ) {
return createEventWork(SlxContext.getThreadSystemContext());
}
public static EventWorker createEventWork(final SystemContext sc) {
EventWorkerFactory factory = EventWorkerFactory.getInstance();
return factory.createWorker(sc);
}
}