/*
* Copyright 2008 Pavel Syrtsov
*
* Licensed 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 com.sf.ddao.orm.rsmapper.rowmapper;
import com.sf.ddao.LoadAwareBean;
import com.sf.ddao.orm.RSMapperFactoryRegistry;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.*;
/**
* Created by: Pavel Syrtsov
* Date: Apr 8, 2007
* Time: 6:12:20 PM
*/
public class BeanRowMapperFactory implements RowMapperFactory {
private Constructor constructor;
private Map<String, PropertyDescriptor> props;
private static class PropertyMapper {
private Method writeMethod;
private RowMapper columnMapper;
public PropertyMapper(String name, PropertyDescriptor propertyDescriptor) {
writeMethod = propertyDescriptor.getWriteMethod();
Class<?> propertyType = propertyDescriptor.getPropertyType();
columnMapper = RSMapperFactoryRegistry.getScalarRowMapperFactory(propertyType, name, true).get();
}
public void map(ResultSet resultSet, Object result) throws Exception {
Object value = columnMapper.map(resultSet);
writeMethod.invoke(result, value);
}
@Override
public String toString() {
return writeMethod.toString();
}
}
public RowMapper get() {
return new BeanRowMapper();
}
public BeanRowMapperFactory(Class itemType) {
for (Constructor c : itemType.getDeclaredConstructors()) {
if (c.getParameterTypes().length == 0) {
c.setAccessible(true);
constructor = c;
}
}
if (constructor == null) {
throw new RuntimeException("Type " + itemType + " has to have parameterless constructor");
}
BeanInfo beanInfo;
try {
beanInfo = java.beans.Introspector.getBeanInfo(itemType);
} catch (IntrospectionException e) {
throw new RuntimeException(e);
}
props = new HashMap<String, PropertyDescriptor>();
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
if (propertyDescriptor.getWriteMethod() == null) {
continue;
}
String name = propertyDescriptor.getName();
name = name.toLowerCase();
props.put(name, propertyDescriptor);
}
}
private class BeanRowMapper implements RowMapper {
private List<PropertyMapper> mappers = null;
public Object map(ResultSet rs) throws SQLException {
Object result;
try {
if (mappers == null) {
init(rs);
}
result = constructor.newInstance();
} catch (SQLException e) {
throw e;
} catch (Exception e) {
throw new SQLException(e);
}
for (PropertyMapper propertyMapper : mappers) {
try {
propertyMapper.map(rs, result);
} catch (Exception e) {
throw new SQLException("Error mapping property " + propertyMapper.writeMethod, e);
}
}
if (result instanceof LoadAwareBean) {
LoadAwareBean loadAwareBean = (LoadAwareBean) result;
loadAwareBean.beanIsLoaded();
}
return result;
}
private synchronized void init(ResultSet resultSet) throws Exception {
ResultSetMetaData metaData = resultSet.getMetaData();
int count = metaData.getColumnCount();
List<PropertyMapper> mappers = new ArrayList<PropertyMapper>(count);
Set<String> colNames = new HashSet<String>();
for (int i = 1; i <= count; i++) {
final String colName = metaData.getColumnName(i);
String name = colName.toLowerCase();
name = stripUnderscore(name);
if (!colNames.add(name)) {
throw new SQLException("Ambiguous column name " + colName + "(" + colNames + ")");
}
PropertyDescriptor propertyDescriptor = props.get(name);
if (propertyDescriptor == null) {
throw new SQLException("Column " + colName + " doesn't have matching property");
}
mappers.add(new PropertyMapper(colName, propertyDescriptor));
}
this.mappers = mappers;
}
}
private static String stripUnderscore(String name) {
StringBuilder sb = new StringBuilder(name.length());
for (char ch : name.toCharArray()) {
if (ch != '_') {
sb.append(ch);
}
}
return sb.toString();
}
}