package org.simpleflatmapper.jdbc; import org.simpleflatmapper.converter.Converter; import org.simpleflatmapper.converter.UncheckedConverter; import org.simpleflatmapper.converter.UncheckedConverterHelper; import org.simpleflatmapper.jdbc.impl.ResultSetEnumarable; import org.simpleflatmapper.map.ConsumerErrorHandler; import org.simpleflatmapper.map.MappingContext; import org.simpleflatmapper.map.mapper.DiscriminatorMapper; import org.simpleflatmapper.map.property.FieldMapperColumnDefinition; import org.simpleflatmapper.util.Enumarable; import org.simpleflatmapper.util.ErrorHelper; import org.simpleflatmapper.util.TypeReference; import org.simpleflatmapper.util.Predicate; import org.simpleflatmapper.util.UnaryFactory; import java.lang.reflect.Type; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * The builder is used to build a DiscriminatorMapper that will instantiate * different types depending on the value of a specified field. * @param <T> the root type of the jdbcMapper */ public class DiscriminatorJdbcBuilder<T> { private final String column; private final JdbcMapperFactory jdbcMapperFactory; private final List<DiscriminatorJdbcSubBuilder> builders = new ArrayList<DiscriminatorJdbcSubBuilder>(); public DiscriminatorJdbcBuilder(String column,JdbcMapperFactory jdbcMapperFactory) { this.column = column; this.jdbcMapperFactory = jdbcMapperFactory; } /** * Add a discriminator value with its associated type. * @param value the value * @param type the type * @return the current builder */ public DiscriminatorJdbcSubBuilder when(String value, Type type) { return when(new DiscriminatorPredicate(value), type); } /** * Add a discriminator matching predicate with its associated type. * @param predicate the predicate * @param type the type * @return the current builder */ public DiscriminatorJdbcSubBuilder when(Predicate<String> predicate, Type type) { final DiscriminatorJdbcSubBuilder subBuilder = new DiscriminatorJdbcSubBuilder(predicate, type); builders.add(subBuilder); return subBuilder; } /** * Add a discriminator value with its associated class. * @param value the value * @param type the class * @return the current builder */ public DiscriminatorJdbcSubBuilder when(String value, Class<? extends T> type) { return when(value, (Type)type); } /** * Add a discriminator value with its associated type specified by the type reference. * @param value the value * @param type the type reference * @return the current builder */ public DiscriminatorJdbcSubBuilder when(String value, TypeReference<? extends T> type) { return when(value, type.getType()); } /** * * @return a new jdbcMapper based on the current state */ public JdbcMapper<T> mapper() { List<DiscriminatorMapper.PredicatedMapper<ResultSet, ResultSet, T, SQLException>> mappers = new ArrayList<DiscriminatorMapper.PredicatedMapper<ResultSet, ResultSet, T, SQLException>>(); for(DiscriminatorJdbcSubBuilder subBuilder : builders) { JdbcMapper<T> mapper = subBuilder.createMapper(); Predicate<ResultSet> predicate = new ResultSetDiscriminatorPredicate(column, subBuilder.predicate); mappers.add(new DiscriminatorMapper.PredicatedMapper<ResultSet, ResultSet, T, SQLException>(predicate, mapper, mapper)); } DiscriminatorJdbcMapper<T> discriminatorMapper = new DiscriminatorJdbcMapper<T>( mappers, new UnaryFactory<ResultSet, Enumarable<ResultSet>>() { @Override public Enumarable<ResultSet> newInstance(ResultSet resultSet) { return new ResultSetEnumarable(resultSet); } }, UncheckedConverterHelper.<ResultSet, String>toUnchecked( new Converter<ResultSet, String>() { @Override public String convert(ResultSet in) throws SQLException { return column + ":" + in.getObject(column); } }), jdbcMapperFactory.consumerErrorHandler()); return discriminatorMapper; } private static class DiscriminatorJdbcMapper<T> extends DiscriminatorMapper<ResultSet, ResultSet, T, SQLException> implements JdbcMapper<T> { public DiscriminatorJdbcMapper(List<PredicatedMapper<ResultSet, ResultSet, T, SQLException>> predicatedMappers, UnaryFactory<ResultSet, Enumarable<ResultSet>> rowEnumarableFactory, UncheckedConverter<ResultSet, String> errorConverter, ConsumerErrorHandler consumerErrorHandler) { super(predicatedMappers, rowEnumarableFactory, errorConverter, consumerErrorHandler); } @Override public MappingContext<? super ResultSet> newMappingContext(ResultSet resultSet) throws SQLException { return ((JdbcMapper<T>)getMapper(resultSet)).newMappingContext(resultSet); } } private static class DiscriminatorPredicate implements Predicate<String> { private final String value; private DiscriminatorPredicate(String value) { this.value = value; } @Override public boolean test(String discriminatorValue) { return value == null ? discriminatorValue == null : value.equals(discriminatorValue); } @Override public String toString() { return "DiscriminatorPredicate{value='" + value + '\'' + '}'; } } private static class ResultSetDiscriminatorPredicate implements Predicate<ResultSet> { private final String discriminatorColumn; private final Predicate<String> predicate; public ResultSetDiscriminatorPredicate(String discriminatorColumn, Predicate<String> predicate) { this.discriminatorColumn = discriminatorColumn; this.predicate = predicate; } @Override public boolean test(ResultSet resultSet) { try { return predicate.test(resultSet.getString(discriminatorColumn)); } catch (SQLException e) { return ErrorHelper.<Boolean>rethrow(e); } } } public class DiscriminatorJdbcSubBuilder { private final Type type; private final Predicate<String> predicate; private JdbcMapperBuilder<T> builder = null; public DiscriminatorJdbcSubBuilder(Predicate<String> predicate, Type type) { this.type = type; this.predicate = predicate; } /** * Static property definition. * @see JdbcMapperBuilder * @param column the property * @return the current builder */ public DiscriminatorJdbcSubBuilder addMapping(String column) { return addMapping(column, FieldMapperColumnDefinition.<JdbcColumnKey>identity()); } /** * Static property definition. * @see JdbcMapperBuilder * @param column the property * @param columnDefinition the property definition * @return the current builder */ public DiscriminatorJdbcSubBuilder addMapping(String column, FieldMapperColumnDefinition<JdbcColumnKey> columnDefinition) { initBuilder(); builder.addMapping(column, columnDefinition); return this; } private void initBuilder() { if (builder == null) { builder = jdbcMapperFactory.newBuilder(type); } } /** * Static property definition. * @see JdbcMapperBuilder * @param column the property * @param index the property index * @param columnDefinition the property definition * @return the current builder */ public DiscriminatorJdbcSubBuilder addMapping(String column, int index, FieldMapperColumnDefinition<JdbcColumnKey> columnDefinition) { initBuilder(); builder.addMapping(column, index, columnDefinition); return this; } /** * @see DiscriminatorJdbcBuilder * @return return a DiscriminatorMapper based on the current state of the builder */ public JdbcMapper<T> mapper() { return DiscriminatorJdbcBuilder.this.mapper(); } /** * Add a discriminator matching predicate with its associated type. * @param value the value * @param type the type * @return the current builder */ public DiscriminatorJdbcSubBuilder when(String value, Type type) { return DiscriminatorJdbcBuilder.this.when(value, type); } /** * Add a discriminator value with its associated type. * @param value the value * @param type the type * @return the current builder */ public DiscriminatorJdbcSubBuilder when(String value, Class<? extends T> type) { return when(value, (Type)type); } /** * Add a discriminator value with its associated type. * @param value the value * @param type the type * @return the current builder */ public DiscriminatorJdbcSubBuilder when(String value, TypeReference<? extends T> type) { return when(value, type.getType()); } /** * Add a discriminator matching predicate with its associated type. * @param predicate the predicate * @param type the type * @return the current builder */ public DiscriminatorJdbcSubBuilder when(Predicate<String> predicate, Type type) { return DiscriminatorJdbcBuilder.this.when(predicate, type); } private JdbcMapper<T> createMapper() { if (builder != null) { return builder.mapper(); } else { return jdbcMapperFactory.newMapper(type); } } } }