package org.simpleflatmapper.jdbc;
import org.simpleflatmapper.jdbc.impl.CollectionIndexFieldMapper;
import org.simpleflatmapper.jdbc.impl.MultiIndexQueryPreparer;
import org.simpleflatmapper.jdbc.impl.PreparedStatementIndexedSetterFactory;
import org.simpleflatmapper.jdbc.impl.SingleIndexFieldMapper;
import org.simpleflatmapper.jdbc.impl.MapperQueryPreparer;
import org.simpleflatmapper.jdbc.impl.setter.PreparedStatementIndexSetter;
import org.simpleflatmapper.jdbc.impl.setter.PreparedStatementIndexSetterOnGetter;
import org.simpleflatmapper.jdbc.named.NamedSqlQuery;
import org.simpleflatmapper.map.MappingContext;
import org.simpleflatmapper.map.mapper.AbstractConstantTargetMapperBuilder;
import org.simpleflatmapper.map.MapperConfig;
import org.simpleflatmapper.map.mapper.ColumnDefinition;
import org.simpleflatmapper.map.property.FieldMapperColumnDefinition;
import org.simpleflatmapper.jdbc.property.IndexedSetterFactoryProperty;
import org.simpleflatmapper.jdbc.property.IndexedSetterProperty;
import org.simpleflatmapper.map.mapper.ConstantTargetFieldMapperFactory;
import org.simpleflatmapper.map.mapper.PropertyMapping;
import org.simpleflatmapper.reflect.BiInstantiator;
import org.simpleflatmapper.reflect.Getter;
import org.simpleflatmapper.reflect.IndexedGetter;
import org.simpleflatmapper.reflect.IndexedSetter;
import org.simpleflatmapper.reflect.IndexedSetterFactory;
import org.simpleflatmapper.reflect.Instantiator;
import org.simpleflatmapper.reflect.getter.ArrayIndexedGetter;
import org.simpleflatmapper.reflect.getter.ArraySizeGetter;
import org.simpleflatmapper.reflect.getter.ListIndexedGetter;
import org.simpleflatmapper.reflect.getter.ListSizeGetter;
import org.simpleflatmapper.reflect.meta.ClassMeta;
import org.simpleflatmapper.reflect.meta.DefaultPropertyNameMatcher;
import org.simpleflatmapper.reflect.meta.ObjectClassMeta;
import org.simpleflatmapper.reflect.meta.PropertyMeta;
import org.simpleflatmapper.reflect.primitive.IntGetter;
import org.simpleflatmapper.util.ConstantPredicate;
import org.simpleflatmapper.util.ErrorDoc;
import org.simpleflatmapper.util.ForEachCallBack;
import org.simpleflatmapper.util.Predicate;
import org.simpleflatmapper.util.TypeHelper;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
public class PreparedStatementMapperBuilder<T> extends AbstractConstantTargetMapperBuilder<PreparedStatement, T, JdbcColumnKey, PreparedStatementMapperBuilder<T>> {
private IndexedSetterFactory<PreparedStatement, PropertyMapping<?, ?, JdbcColumnKey, ? extends ColumnDefinition<JdbcColumnKey, ?>>> indexedSetterFactory = PreparedStatementIndexedSetterFactory.INSTANCE;
public PreparedStatementMapperBuilder(
ClassMeta<T> classMeta,
MapperConfig<JdbcColumnKey,
FieldMapperColumnDefinition<JdbcColumnKey>> mapperConfig,
ConstantTargetFieldMapperFactory<PreparedStatement, JdbcColumnKey> preparedStatementFieldMapperFactory) {
super(classMeta, PreparedStatement.class, mapperConfig, preparedStatementFieldMapperFactory);
}
private PreparedStatementMapperBuilder(PreparedStatementMapperBuilder<T> builder) {
this(builder.classMeta, builder.mapperConfig, builder.fieldAppenderFactory);
}
@Override
protected BiInstantiator<T, MappingContext<? super T>, PreparedStatement> getInstantiator() {
return new NullInstantiator<T>();
}
@Override
protected JdbcColumnKey newKey(String column, int i, FieldMapperColumnDefinition<JdbcColumnKey> columnDefinition) {
JdbcColumnKey key = new JdbcColumnKey(column, i);
SqlTypeColumnProperty typeColumnProperty = columnDefinition.lookFor(SqlTypeColumnProperty.class);
if (typeColumnProperty == null) {
FieldMapperColumnDefinition<JdbcColumnKey> globalDef = mapperConfig.columnDefinitions().getColumnDefinition(key);
typeColumnProperty = globalDef.lookFor(SqlTypeColumnProperty.class);
}
if (typeColumnProperty != null) {
return new JdbcColumnKey(key.getName(), key.getIndex(), typeColumnProperty.getSqlType(), key);
}
return key;
}
private static class NullInstantiator<T> implements BiInstantiator<T, MappingContext<? super T>, PreparedStatement> {
@Override
public PreparedStatement newInstance(T o, MappingContext<? super T> context) throws Exception {
throw new UnsupportedOperationException();
}
}
protected int getStartingIndex() {
return 1;
}
public QueryPreparer<T> to(NamedSqlQuery query) {
return to(query, null);
}
public QueryPreparer<T> to(NamedSqlQuery query, String[] generatedKeys) {
PreparedStatementMapperBuilder<T> builder =
new PreparedStatementMapperBuilder<T>(this);
return builder.preparedStatementMapper(query, generatedKeys);
}
private QueryPreparer<T> preparedStatementMapper(NamedSqlQuery query, String[] generatedKeys) {
for(int i = 0; i < query.getParametersSize(); i++) {
addColumn(query.getParameter(i).getName());
}
boolean hasMultiIndex =
propertyMappingsBuilder.forEachProperties(new ForEachCallBack<PropertyMapping<T, ?, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>>>() {
boolean hasMultiIndex;
@Override
public void handle(PropertyMapping<T, ?, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>> pm) {
hasMultiIndex |= isMultiIndex(pm.getPropertyMeta());
}
}).hasMultiIndex;
if (hasMultiIndex) {
return new MultiIndexQueryPreparer<T>(query, buildIndexFieldMappers(), generatedKeys);
} else {
return new MapperQueryPreparer<T>(query, mapper(), generatedKeys);
}
}
private boolean isMultiIndex(PropertyMeta<?, ?> propertyMeta) {
return
TypeHelper.isArray(propertyMeta.getPropertyType())
|| TypeHelper.isAssignable(List.class, propertyMeta.getPropertyType());
}
@SuppressWarnings("unchecked")
public MultiIndexFieldMapper<T>[] buildIndexFieldMappers() {
final List<MultiIndexFieldMapper<T>> fields = new ArrayList<MultiIndexFieldMapper<T>>();
propertyMappingsBuilder.forEachProperties(new ForEachCallBack<PropertyMapping<T, ?, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>>>() {
@Override
public void handle(PropertyMapping<T, ?, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>> pm) {
if (isMultiIndex(pm.getPropertyMeta())) {
fields.add(newCollectionFieldMapper(pm));
} else {
fields.add(newFieldMapper(pm));
}
}
private <P, C> MultiIndexFieldMapper<T> newCollectionFieldMapper(PropertyMapping<T, P, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>> pm) {
PropertyMeta<T, ?> propertyMeta = pm.getPropertyMeta();
IndexedGetter<C, P> indexedGetter;
IntGetter<? super C> sizeGetter;
Getter<T, C> collectionGetter = (Getter<T, C>) propertyMeta.getGetter();
if (TypeHelper.isAssignable(List.class, propertyMeta.getPropertyType())) {
indexedGetter = (IndexedGetter<C, P>) new ListIndexedGetter<P>();
sizeGetter = (IntGetter<C>) new ListSizeGetter();
} else if (TypeHelper.isArray(propertyMeta.getPropertyType())) {
indexedGetter = (IndexedGetter<C, P>) new ArrayIndexedGetter<P>();
sizeGetter = new ArraySizeGetter();
} else {
throw new IllegalArgumentException("Unexpected elementMeta" + propertyMeta);
}
PropertyMeta<C, P> childProperty =
(PropertyMeta<C, P>)
pm.getPropertyMeta().getPropertyClassMeta().newPropertyFinder(ConstantPredicate.<PropertyMeta<?, ?>>truePredicate()).findProperty(DefaultPropertyNameMatcher.of("0"));
final PropertyMapping<C, P, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>> pmchildProperttMeta = pm.propertyMeta(childProperty);
IndexedSetter<PreparedStatement, P> setter = getSetter(pmchildProperttMeta);
return new CollectionIndexFieldMapper<T, C, P>(setter, collectionGetter, sizeGetter, indexedGetter);
}
private <P, C> IndexedSetter<PreparedStatement, P> getSetter(PropertyMapping<C, P, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>> pm) {
IndexedSetter<PreparedStatement, P> setter = null;
IndexedSetterProperty indexedSetterProperty = pm.getColumnDefinition().lookFor(IndexedSetterProperty.class);
if (indexedSetterProperty != null) {
setter = (IndexedSetter<PreparedStatement, P>) indexedSetterProperty.getIndexedSetter();
}
if (setter == null) {
setter = indexedSetterFactory(pm);
}
if (setter == null) {
mapperConfig.mapperBuilderErrorHandler().accessorNotFound(
"Could not find setter for " + pm.getColumnKey()
+ " type " + pm.getPropertyMeta().getPropertyType()
+ " path " + pm.getPropertyMeta().getPath()
+ " See " + ErrorDoc.toUrl("PS_SETTER_NOT_FOUND"));
}
return setter;
}
private <P, C> IndexedSetter<PreparedStatement, P> indexedSetterFactory(PropertyMapping<C, P, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>> pm) {
IndexedSetter<PreparedStatement, P> setter = null;
final IndexedSetterFactoryProperty indexedSetterPropertyFactory = pm.getColumnDefinition().lookFor(IndexedSetterFactoryProperty.class);
if (indexedSetterPropertyFactory != null) {
IndexedSetterFactory<PreparedStatement, PropertyMapping<?, ?, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>>> setterFactory = (IndexedSetterFactory<PreparedStatement, PropertyMapping<?, ?, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>>>) indexedSetterPropertyFactory.getIndexedSetterFactory();
setter = setterFactory.getIndexedSetter(pm);
}
if (setter == null) {
setter = indexedSetterFactory.getIndexedSetter(pm);
}
if (setter == null) {
final ClassMeta<P> classMeta = pm.getPropertyMeta().getPropertyClassMeta();
if (classMeta instanceof ObjectClassMeta) {
ObjectClassMeta<P> ocm = (ObjectClassMeta<P>) classMeta;
if (ocm.getNumberOfProperties() == 1) {
PropertyMeta<P, ?> subProp = ocm.getFirstProperty();
final PropertyMapping<?, ?, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>> subPropertyMapping = pm.propertyMeta(subProp);
IndexedSetter<PreparedStatement, ?> subSetter = indexedSetterFactory(subPropertyMapping);
if (subSetter != null) {
setter = new PreparedStatementIndexSetterOnGetter<Object, P>((PreparedStatementIndexSetter<Object>) subSetter, (Getter<P, Object>) subProp.getGetter());
}
}
}
}
return setter;
}
private <P> MultiIndexFieldMapper<T> newFieldMapper(PropertyMapping<T, P, JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>> pm) {
return new SingleIndexFieldMapper<T, P>(getSetter(pm), pm.getPropertyMeta().getGetter());
}
});
return fields.toArray(new MultiIndexFieldMapper[0]);
}
}