org.simpleflatmapper.map.mapper.AbstractMapperFactory Maven / Gradle / Ivy
Show all versions of sfm-map Show documentation
package org.simpleflatmapper.map.mapper;
import org.simpleflatmapper.map.*;
import org.simpleflatmapper.map.context.MappingContextFactoryBuilder;
import org.simpleflatmapper.map.error.RethrowConsumerErrorHandler;
import org.simpleflatmapper.map.error.RethrowMapperBuilderErrorHandler;
import org.simpleflatmapper.map.getter.ComposedContextualGetterFactory;
import org.simpleflatmapper.map.getter.ContextualGetter;
import org.simpleflatmapper.map.getter.ContextualGetterFactory;
import org.simpleflatmapper.map.property.*;
import org.simpleflatmapper.reflect.Getter;
import org.simpleflatmapper.reflect.IndexedGetter;
import org.simpleflatmapper.reflect.ReflectionService;
import org.simpleflatmapper.reflect.meta.ClassMeta;
import org.simpleflatmapper.reflect.property.SpeculativeArrayIndexResolutionProperty;
import org.simpleflatmapper.util.*;
import java.lang.reflect.Member;
import java.lang.reflect.Type;
import java.util.*;
// I don't really like using inheritance but did not see any other way
// to avoid rewriting a lot of delegate method...
@SuppressWarnings("unchecked")
public abstract class AbstractMapperFactory<
K extends FieldKey,
MF extends AbstractMapperFactory, S> {
private FieldMapperErrorHandler fieldMapperErrorHandler = null;
private MapperBuilderErrorHandler mapperBuilderErrorHandler = RethrowMapperBuilderErrorHandler.INSTANCE;
private ConsumerErrorHandler consumerErrorHandler = RethrowConsumerErrorHandler.INSTANCE;
private final AbstractColumnDefinitionProvider columnDefinitions;
private final List> typedPredicatedPredicatedColunnPropertyFactories = new ArrayList>();
protected final List> discriminators = new ArrayList>();
private final ColumnDefinition identity;
private boolean useAsm = true;
private boolean failOnAsm = false;
private int asmMapperNbFieldsLimit = MapperConfig.NO_ASM_MAPPER_THRESHOLD;
private PropertyNameMatcherFactory propertyNameMatcherFactory = DefaultPropertyNameMatcherFactory.DEFAULT;
private ReflectionService reflectionService = null;
private int maxMethodSize = MapperConfig.MAX_METHOD_SIZE;
private boolean assumeInjectionModifiesValues;
private Predicate super S> rowFilter = null;
private boolean unorderedJoin;
protected ContextualGetterFactory super S, K> getterFactory;
public AbstractMapperFactory(AbstractMapperFactory config) {
this.fieldMapperErrorHandler = config.fieldMapperErrorHandler;
this.mapperBuilderErrorHandler = config.mapperBuilderErrorHandler;
this.consumerErrorHandler = config.consumerErrorHandler;
this.columnDefinitions = config.columnDefinitions;
this.identity = config.identity;
this.useAsm = config.useAsm;
this.failOnAsm = config.failOnAsm;
this.asmMapperNbFieldsLimit = config.asmMapperNbFieldsLimit;
this.propertyNameMatcherFactory = config.propertyNameMatcherFactory;
this.reflectionService = config.reflectionService;
this.maxMethodSize = config.maxMethodSize;
this.assumeInjectionModifiesValues = config.assumeInjectionModifiesValues;
this.rowFilter = config.rowFilter;
this.unorderedJoin = config.unorderedJoin;
this.getterFactory = config.getterFactory;
}
public AbstractMapperFactory(AbstractColumnDefinitionProvider columnDefinitions, ColumnDefinition identity, ContextualGetterFactory super S, K> getterFactory) {
this.columnDefinitions = columnDefinitions;
this.identity = identity;
this.getterFactory = getterFactory;
}
public AbstractMapperFactory(AbstractColumnDefinitionProvider columnDefinitions, ColumnDefinition identity, Function> getterFactoryFactory) {
this.columnDefinitions = columnDefinitions;
this.identity = identity;
this.getterFactory = getterFactoryFactory.apply((MF)this);
}
/**
* the FieldMapperErrorHandler is called when a error occurred when mapping a field from the source to the target.
* By default it just throw the Exception.
* @param fieldMapperErrorHandler the new FieldMapperErrorHandler
* @return the current factory
*/
public final MF fieldMapperErrorHandler(final FieldMapperErrorHandler fieldMapperErrorHandler) {
this.fieldMapperErrorHandler = fieldMapperErrorHandler;
return (MF) this;
}
/**
* Enabled support for unordered join at the root level.
* To support that the mapper will need to load the data on the first call and keep a map of id to object.
* It is more costly to enable that use with caution.
*
* @return the current factory
*/
public final MF unorderedJoin() {
this.unorderedJoin = true;
return (MF) this;
}
/**
* Change the mapperBuilderErrorHandler to an IgnoreMapperBuilderErrorHandler.
* @return the current factory
*/
public final MF ignorePropertyNotFound() {
return addColumnProperty(ConstantPredicate.truePredicate(), OptionalProperty.INSTANCE);
}
/**
* Set the new MapperBuilderErrorHandler. the MapperBuilderErrorHandler is called when an error occurred or a property is not found in the builder while creating the jdbcMapper.
* @param mapperBuilderErrorHandler the MapperBuilderErrorHandler
* @return the current factory
*/
public final MF mapperBuilderErrorHandler(final MapperBuilderErrorHandler mapperBuilderErrorHandler) {
this.mapperBuilderErrorHandler = mapperBuilderErrorHandler;
return (MF) this;
}
/**
* the ConsumerErrorHandler is called when an exception is thrown by the CheckedConsumer in the forEach call.
* @param consumerErrorHandler the new ConsumerErrorHandler
* @return the current factory
*/
public final MF consumerErrorHandler(final ConsumerErrorHandler consumerErrorHandler) {
this.consumerErrorHandler = consumerErrorHandler;
return (MF) this;
}
@Deprecated
public final MF rowHandlerErrorHandler(final ConsumerErrorHandler rowHandlerErrorHandler) {
return consumerErrorHandler(rowHandlerErrorHandler);
}
/**
*
* @param useAsm false if you want to disable asm generation of Mappers, Getter and Setter. This would be active by default if asm is present in a compatible version.
* @return the current factory
*/
public final MF useAsm(final boolean useAsm) {
if (reflectionService != null) throw new IllegalStateException("Reflection service is set cannot change useAsm");
this.useAsm = useAsm;
return (MF) this;
}
/**
* Override the default implementation of the ReflectionService.
* @param reflectionService the overriding newInstance
* @return the current factory
*/
public final MF reflectionService(final ReflectionService reflectionService) {
this.reflectionService = reflectionService;
return (MF) this;
}
public final MF rowFilter(final Predicate super S> rowFilter) {
this.rowFilter = rowFilter;
return (MF) this;
}
public final MapperConfig mapperConfig() {
return mapperConfig(Object.class);
}
public final MapperConfig mapperConfig(Type targetType) {
return MapperConfig
.config(enrichColumnDefinitions(columnDefinitions(targetType)))
.mapperBuilderErrorHandler(mapperBuilderErrorHandler)
.propertyNameMatcherFactory(propertyNameMatcherFactory)
.failOnAsm(failOnAsm)
.asmMapperNbFieldsLimit(asmMapperNbFieldsLimit)
.fieldMapperErrorHandler(fieldMapperErrorHandler)
.consumerErrorHandler(consumerErrorHandler)
.maxMethodSize(maxMethodSize)
.assumeInjectionModifiesValues(assumeInjectionModifiesValues)
.discriminators(discriminators)
.rowFilter(rowFilter)
.unorderedJoin(unorderedJoin);
}
public AbstractColumnDefinitionProvider enrichColumnDefinitions(AbstractColumnDefinitionProvider columnDefinitions) {
return columnDefinitions;
}
/**
* Associate an alias on the property key to rename to value.
* @param column the column name to rename
* @param actualPropertyName then name to rename to match the actual property name
* @return the current factory
*/
public final MF addAlias(String column, String actualPropertyName) {
return addColumnDefinition(column, identity.addRename(actualPropertyName));
}
/**
* Associate an alias on the property key to rename to value on the specific type.
* @param column the column name to rename
* @param actualPropertyName then name to rename to match the actual property name
* @return the current factory
*/
public final MF addAliasForType(Type type, String column, String actualPropertyName) {
return addColumnPropertyForType(type, column, new RenameProperty(actualPropertyName));
}
/**
* Associate the specified columnDefinition to the specified property.
* @param column the name of the column
* @param columnDefinition the columnDefinition
* @return the current factory
*/
public final MF addColumnDefinition(String column, ColumnDefinition columnDefinition) {
columnDefinitions.addColumnDefinition(column, columnDefinition);
return (MF) this;
}
/**
* Associate the specified columnDefinition to the property matching the predicate.
* @param predicate the property predicate
* @param columnDefinition the columnDefinition
* @return the current factory
*/
public final MF addColumnDefinition(Predicate super K> predicate, ColumnDefinition columnDefinition) {
columnDefinitions.addColumnDefinition(predicate, columnDefinition);
return (MF) this;
}
/**
* Associate the specified columnProperties to the property matching the predicate.
* @param column the column name
* @param properties the properties
* @return the current factory
*/
public final MF addColumnProperty(String column, Object... properties) {
for(Object property : properties) {
columnDefinitions.addColumnProperty(column, property);
}
return (MF) this;
}
/**
* Associate the specified columnProperties to the property matching the predicate.
* @param predicate the property predicate
* @param properties the properties
* @return the current factory
*/
public final MF addColumnProperty(Predicate super K> predicate, Object... properties) {
for(Object property : properties) {
columnDefinitions.addColumnProperty(predicate, property);
}
return (MF) this;
}
/**
* Associate the specified columnProperties to the property matching the predicate.
* @param predicate the property predicate
* @param propertyFactory the properties
* @return the current factory
*/
public final MF addColumnProperty(Predicate super K> predicate, UnaryFactory propertyFactory) {
columnDefinitions.addColumnProperty(predicate, propertyFactory);
return (MF) this;
}
/**
* Associate the specified columnProperties to the property matching the key predicate and type.
* @param type the type
* @param column the property predicate
* @param properties the properties
* @return the current factory
*/
public final MF addColumnPropertyForType(Type type, String column, Object... properties) {
return addColumnPropertyForType(type, CaseInsensitiveFieldKeyNamePredicate.of(column), properties);
}
/**
* Associate the specified columnProperties to the property matching the key predicate and type.
* @param type the type
* @param keyPredicate the property predicate
* @param properties the properties
* @return the current factory
*/
public final MF addColumnPropertyForType(Type type, Predicate super K> keyPredicate, Object... properties) {
for(final Object property : properties) {
addColumnPropertyForType(type, keyPredicate, new UnaryFactory() {
@Override
public Object newInstance(K k) {
return property;
}
});
}
return (MF) this;
}
/**
* Associate the specified columnProperties to the property matching the key predicate and type.
* @param type the type
* @param keyPredicate the property predicate
* @param propertyFactory the properties
* @return the current factory
*/
public final MF addColumnPropertyForType(final Type type, Predicate super K> keyPredicate, UnaryFactory propertyFactory) {
return addColumnPropertyForType(new Predicate() {
@Override
public boolean test(Type t) {
return TypeHelper.areEquals(type, t);
}
}, keyPredicate, propertyFactory);
}
/**
* Associate the specified columnProperties to the property matching the key predicate and type predicate.
* @param typePredicate the type predicate
* @param keyPredicate the property predicate
* @param propertyFactory the properties
* @return the current factory
*/
public final MF addColumnPropertyForType(Predicate typePredicate, Predicate super K> keyPredicate, UnaryFactory propertyFactory) {
typedPredicatedPredicatedColunnPropertyFactories.add(new TypedPredicatedPredicatedColumnPropertyFactory(typePredicate, new AbstractColumnDefinitionProvider.PredicatedColumnPropertyFactory(keyPredicate, propertyFactory)));
return (MF) this;
}
/**
* Override the default PropertyNameMatcherFactory with the specified factory.
* @param propertyNameMatcherFactory the factory
* @return the current factory
*/
public final MF propertyNameMatcherFactory(PropertyNameMatcherFactory propertyNameMatcherFactory) {
this.propertyNameMatcherFactory = propertyNameMatcherFactory;
return (MF) this;
}
/**
* Associate the aliases value to the property key.
* @param aliases the key value pair
* @return the current factory
*/
public final MF addAliases(Map aliases) {
for(Map.Entry e : aliases.entrySet()) {
addAlias(e.getKey(), e.getValue());
}
return (MF) this;
}
/**
* Ignore column that match the predicate.
* @param predicate the predicate.
* @return the current factory
*/
public final MF ignoreColumns(Predicate super K> predicate) {
return addColumnProperty(predicate, new IgnoreProperty());
}
/**
* Ignore column with the specified names, case insensitive.
* @param columnNames the columnNames.
* @return the current factory
*/
public final MF ignoreColumns(String... columnNames) {
return ignoreColumns(Arrays.asList(columnNames));
}
public MF addGetterFactory(ContextualGetterFactory getterFactory) {
this.getterFactory = ComposedContextualGetterFactory.composed(getterFactory, this.getterFactory);
return (MF) this;
}
public MF addGetterForType(final Type type, final Function> getterFactory) {
return addGetterForType(new Predicate() {
@Override
public boolean test(Type t) {
return TypeHelper.isAssignable(t, type);
}
}, getterFactory);
}
public MF addGetterForType(final Type type, final ContextualGetterFactory getterFactory) {
return addGetterForType(new Predicate() {
@Override
public boolean test(Type t) {
return TypeHelper.isAssignable(t, type);
}
}, getterFactory);
}
public MF addGetterForType(final Type type, final IndexedGetter indexedGetter) {
return addColumnProperty(ConstantPredicate.truePredicate(), GetterFactoryProperty.forType(type, indexedGetter));
}
public MF addGetterForType(final Predicate typePredicate, final Function> getterFactory) {
return addGetterFactory(new ContextualGetterFactory() {
@Override
public ContextualGetter newGetter(Type target, K key, MappingContextFactoryBuilder, K> mappingContextFactoryBuilder, Object... properties) {
if (typePredicate.test(target)) {
return (ContextualGetter) getterFactory.apply(key);
}
return null;
}
});
}
public MF addGetterForType(final Predicate typePredicate, final ContextualGetterFactory getterFactory) {
return addGetterFactory(new ContextualGetterFactory() {
@Override
public ContextualGetter newGetter(Type target, K key, MappingContextFactoryBuilder, K> mappingContextFactoryBuilder, Object... properties) {
if (typePredicate.test(target)) {
return getterFactory.newGetter(target, key, mappingContextFactoryBuilder, properties);
}
return null;
}
});
}
/**
* Ignore column with the specified names, case insensitive.
* @param columnNames the columnNames.
* @return the current factory
*/
public final MF ignoreColumns(final Collection columnNames) {
final Set columnSet = new HashSet();
for(String c : columnNames) columnSet.add(c.toUpperCase());
return ignoreColumns(new Predicate() {
@Override
public boolean test(K k) {
return columnSet.contains(k.getName().toUpperCase());
}
});
}
/**
* @param b true if we want the builder to fail on asm generation failure
* @return the current factory
*/
public final MF failOnAsm(boolean b) {
this.failOnAsm = b;
return (MF) this;
}
/**
* if set to true, it will assume any constructor that takes a list as a constructor argument will need
* to be mapped using a builder to protect against the actual value being changed in the constructor.
*
* @param b true to make the factory being paranoid about constructor injection of aggregation
* @return the current factory
*/
public final MF assumeInjectionModifiesValues(boolean b) {
this.assumeInjectionModifiesValues = b;
return (MF) this;
}
/**
* change the number of fields threshold after which an asm jdbcMapper is not generated.
*
* the default value is calculated from the benchmark results, currently 240.
* @param asmMapperNbFieldsLimit the limit after which it does not use asm for the jdbcMapper.
* @return the factory
*/
public final MF asmMapperNbFieldsLimit(final int asmMapperNbFieldsLimit) {
this.asmMapperNbFieldsLimit = asmMapperNbFieldsLimit;
return (MF) this;
}
/**
* Number needs to be a power of 2, do not use if you don't know what it does.
* @param maxMethodSize the max method size, needs be a power of 2.
* @return the factory.
*/
public final MF maxMethodSize(final int maxMethodSize) {
this.maxMethodSize = maxMethodSize;
return (MF) this;
}
/**
* Mark the specified columns as keys.
* @param columns the columns
* @return the current factory
*/
public final MF addKeys(String... columns) {
for(String col : columns) {
addColumnDefinition(col, identity.addKey());
}
return (MF) this;
}
/**
* @return the current ConsumerErrorHandler
*/
public final ConsumerErrorHandler consumerErrorHandler() {
return consumerErrorHandler;
}
public final ClassMeta getClassMeta(TypeReference target) {
return getClassMeta(target.getType());
}
public final ClassMeta getClassMeta(Class target) {
return getClassMeta((Type)target);
}
public final ClassMeta getClassMeta(Type target) {
return getReflectionService().getClassMeta(target);
}
public final ClassMeta getClassMetaWithExtraInstantiator(TypeReference target, Member instantiator) {
return getClassMetaWithExtraInstantiator(target.getType(), instantiator);
}
public final ClassMeta getClassMetaWithExtraInstantiator(Class target, Member instantiator) {
return getClassMetaWithExtraInstantiator((Type) target, instantiator);
}
public final ClassMeta getClassMetaWithExtraInstantiator(Type target, Member instantiator) {
return getReflectionService().getClassMetaExtraInstantiator(target, instantiator);
}
public ReflectionService getReflectionService() {
if (reflectionService == null) {
reflectionService = ReflectionService.newInstance(useAsm);
}
return reflectionService;
}
public ColumnDefinitionProvider columnDefinitions() {
return columnDefinitions;
}
public AbstractColumnDefinitionProvider columnDefinitions(Type targetType) {
AbstractColumnDefinitionProvider provider = columnDefinitions.copy();
for(TypedPredicatedPredicatedColumnPropertyFactory f : typedPredicatedPredicatedColunnPropertyFactories) {
if (f.predicate.test(targetType)) {
provider.addColumnProperty(f.predicatedColumnPropertyFactory);
}
}
return provider;
}
public DiscriminatorDSL discriminator(Class commonType) {
return discriminator((Type)commonType);
}
public DiscriminatorDSL discriminator(Type commonType) {
return new DiscriminatorDSL((MF) this, commonType);
}
/**
* @deprecated use {@link #discriminator(Type)} dsl
*/
@Deprecated
public MF discriminator(Type commonType, Consumer> consumer) {
return this.discriminator(commonType)._with(consumer);
}
/**
* @deprecated use {@link #discriminator(Type)} dsl
*/
@Deprecated
public MF discriminator(Type commonType, final Getter super S, KT> getter, Consumer> consumer) {
return this.discriminator(commonType).onGetter(getter).with(consumer);
}
/**
* @deprecated use {@link #discriminator(Type)} dsl
*/
@Deprecated
public MF discriminator(Class commonType, Consumer> consumer) {
return discriminator((Type)commonType, consumer);
}
/**
* @deprecated use {@link #discriminator(Type)} dsl
*/
@Deprecated
public MF discriminator(Class commonType, Getter super S, V> getter, Consumer> consumer) {
return discriminator((Type)commonType, getter, consumer);
}
/**
* @deprecated use {@link #discriminator(Type)} dsl
*/
@Deprecated
public MF discriminator(Class commonType, final String discriminatorColumn, CheckedBiFunction discriminatorFieldAccessor, Consumer> consumer) {
return discriminator((Type) commonType, discriminatorColumn, discriminatorFieldAccessor, consumer);
}
/**
* @deprecated use {@link #discriminator(Type)} dsl
*/
@Deprecated
public MF discriminator(Type commonType, final String discriminatorColumn, final CheckedBiFunction discriminatorFieldAccessor, Consumer> consumer) {
return this.discriminator(commonType).onColumnWithNamedGetter(discriminatorColumn, discriminatorFieldAccessor).with(consumer);
}
public MF enableSpeculativeArrayIndexResolution() {
addColumnProperty(ConstantPredicate.truePredicate(), SpeculativeArrayIndexResolutionProperty.INSTANCE);
return (MF) this;
}
public static class DiscriminatorConditionBuilder, KT, T> {
private final DiscriminatorBuilder discriminatorBuilder;
private final Function, Getter super S, ? extends KT>> getterFactory;
public DiscriminatorConditionBuilder(DiscriminatorBuilder discriminatorBuilder, Function, Getter super S, ? extends KT>> getterFactory) {
this.discriminatorBuilder = discriminatorBuilder;
this.getterFactory = getterFactory;
}
public DiscriminatorConditionBuilder when(KT value, Type type) {
return discriminatorCase(value, type);
}
public DiscriminatorConditionBuilder when(KT value, Class type) {
return discriminatorCase(value, type);
}
public DiscriminatorConditionBuilder when(KT value, ClassMeta extends T> classMeta) {
return discriminatorCase(value, classMeta);
}
public DiscriminatorConditionBuilder when(Predicate predicate, Type type) {
return discriminatorCase(predicate, type);
}
public DiscriminatorConditionBuilder when(Predicate predicate, Class type) {
return discriminatorCase(predicate, type);
}
public DiscriminatorConditionBuilder when(Predicate predicate, ClassMeta extends T> classMeta) {
return discriminatorCase(predicate, classMeta);
}
/**
* @deprecated use {@link #when(Object, Type)}
*/
@Deprecated
public DiscriminatorConditionBuilder discriminatorCase(KT value, Type type) {
return discriminatorCase(toEqualsPredicate(value), type);
}
/**
* @deprecated use {@link #when(Object, Class)} ()} instead.
*/
@Deprecated
public DiscriminatorConditionBuilder discriminatorCase(KT value, Class type) {
return discriminatorCase(toEqualsPredicate(value), type);
}
/**
* @deprecated use {@link #when(Object, ClassMeta)} ()} instead.
*/
@Deprecated
public DiscriminatorConditionBuilder discriminatorCase(KT value, ClassMeta extends T> classMeta) {
return discriminatorCase(toEqualsPredicate(value), classMeta);
}
/**
* @deprecated use {@link #when(Predicate, Type)} ()} instead.
*/
@Deprecated
public DiscriminatorConditionBuilder discriminatorCase(Predicate predicate, Type type) {
discriminatorBuilder.when(toSourcePredicate(predicate), type);
return this;
}
/**
* @deprecated use {@link #when(Predicate, Class)} ()} instead.
*/
@Deprecated
public DiscriminatorConditionBuilder discriminatorCase(Predicate predicate, Class type) {
discriminatorBuilder.when(toSourcePredicate(predicate), type);
return this;
}
/**
* @deprecated use {@link #when(Predicate, ClassMeta)} ()} instead.
*/
@Deprecated
public DiscriminatorConditionBuilder discriminatorCase(Predicate predicate, ClassMeta extends T> classMeta) {
discriminatorBuilder.when(toSourcePredicate(predicate), classMeta);
return this;
}
private Predicate toEqualsPredicate(KT value) {
return EqualsPredicate.of(value);
}
private Function, Predicate> toSourcePredicate(final Predicate predicate) {
return new Function, Predicate>() {
@Override
public Predicate apply(List ks) {
Getter super S, ? extends KT> getter = getterFactory.apply(ks);
return new SourcePredicate(predicate, getter);
}
};
}
static class SourcePredicate implements Predicate {
final Predicate super V> predicate;
final Getter super S, ? extends V> getter;
public SourcePredicate(Predicate super V> predicate, Getter super S, ? extends V> getter) {
this.predicate = predicate;
this.getter = getter;
}
@Override
public boolean test(S s) {
try {
return predicate.test(getter.get(s));
} catch (Exception e) {
return ErrorHelper.rethrow(e);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SourcePredicate, ?> that = (SourcePredicate, ?>) o;
if (predicate != null ? !predicate.equals(that.predicate) : that.predicate != null) return false;
return getter != null ? getter.equals(that.getter) : that.getter == null;
}
@Override
public int hashCode() {
int result = predicate != null ? predicate.hashCode() : 0;
result = 31 * result + (getter != null ? getter.hashCode() : 0);
return result;
}
}
}
public static final class DiscriminatorBuilder, T> {
private final Type commonType;
private final ReflectionService reflectionService;
List> cases = new ArrayList>();
public DiscriminatorBuilder(Type type, ReflectionService reflectionService) {
this.commonType = type;
this.reflectionService = reflectionService;
}
public DiscriminatorBuilder when(Function, Predicate> predicateFactory, ClassMeta extends T> classMeta) {
return discriminatorCase(predicateFactory, classMeta);
}
public DiscriminatorBuilder when(Function, Predicate> predicateFactory, Class extends T> target) {
return when(predicateFactory, reflectionService.getClassMeta(target));
}
public DiscriminatorBuilder when(Function, Predicate> predicateFactory, Type target) {
if (!TypeHelper.isAssignable(commonType, target)) {
throw new IllegalArgumentException("type " + target + " is not a subclass of " + commonType);
}
return discriminatorCase(predicateFactory, reflectionService.getClassMeta(target));
}
public DiscriminatorBuilder when(Predicate predicate, ClassMeta extends T> classMeta) {
return discriminatorCase(predicate, classMeta);
}
public DiscriminatorBuilder when(Predicate predicate, Class extends T> target) {
return discriminatorCase(predicate, target);
}
public DiscriminatorBuilder when(Predicate predicate, Type target) {
return discriminatorCase(predicate, target);
}
public DiscriminatorBuilder defaultType(Class extends T> target) {
return defaultType((Type) target);
}
public DiscriminatorBuilder defaultType(Type target) {
return discriminatorCase(ConstantPredicate.truePredicate(), target);
}
public DiscriminatorBuilder discriminatorCase(final Predicate predicate, ClassMeta extends T> classMeta) {
return discriminatorCase(new Function, Predicate>() {
@Override
public Predicate apply(List ks) {
return predicate;
}
}, classMeta
);
}
public DiscriminatorBuilder discriminatorCase(final Function, Predicate> predicateFactory, ClassMeta extends T> classMeta) {
MapperConfig.DiscriminatorCase dCase = new MapperConfig.DiscriminatorCase(predicateFactory, classMeta);
cases.add(dCase);
return this;
}
public DiscriminatorBuilder discriminatorCase(Predicate predicate, Class extends T> target) {
return discriminatorCase(predicate, reflectionService.getClassMeta(target));
}
public DiscriminatorBuilder discriminatorCase(Predicate predicate, Type target) {
if (!TypeHelper.isAssignable(commonType, target)) {
throw new IllegalArgumentException("type " + target + " is not a subclass of " + commonType);
}
return discriminatorCase(predicate, reflectionService.getClassMeta(target));
}
}
private class TypedPredicatedPredicatedColumnPropertyFactory {
private final Predicate predicate;
private final AbstractColumnDefinitionProvider.PredicatedColumnPropertyFactory predicatedColumnPropertyFactory;
private TypedPredicatedPredicatedColumnPropertyFactory(Predicate predicate, AbstractColumnDefinitionProvider.PredicatedColumnPropertyFactory predicatedColumnPropertyFactory) {
this.predicate = predicate;
this.predicatedColumnPropertyFactory = predicatedColumnPropertyFactory;
}
}
public static class DiscriminatorDSL, MF extends AbstractMapperFactory, S, T> {
private final MF mapperFactory;
private final Type commonType;
public DiscriminatorDSL(MF mapperFactory, Type commonType) {
this.mapperFactory = mapperFactory;
this.commonType = commonType;
}
public DiscriminatorOnColumnDSL onColumn(final Predicate super K> discriminatorColumnPredicate, final Class discriminatorColunnType) {
Function, Getter super S, ? extends KT>> getterFactory = new Function, Getter super S, ? extends KT>>() {
@Override
public Getter super S, ? extends KT> apply(List ks) {
if (ks.isEmpty()) throw new IllegalStateException("No discriminatory field found " + discriminatorColumnPredicate);
if (ks.size() != 1) throw new IllegalStateException("Found multiple discriminator field " + ks);
K k = ks.get(0);
final ContextualGetter super S, KT> getter = mapperFactory.getterFactory.newGetter(discriminatorColunnType, k, null);
return new Getter() {
@Override
public KT get(S target) throws Exception {
return getter.get(target, null);
}
};
}
};
return new DiscriminatorOnColumnDSL(mapperFactory, commonType, discriminatorColumnPredicate, getterFactory);
}
public DiscriminatorOnColumnDSL onColumn(String columnName, Class discriminatorColunnType) {
return onColumn(CaseInsensitiveFieldKeyNamePredicate.of(columnName), discriminatorColunnType);
}
public DiscriminatorOnColumnDSL onColumnWithNamedGetter(String columnName, CheckedBiFunction getter) {
return onColumnWithNamedGetter(CaseInsensitiveFieldKeyNamePredicate.of(columnName), getter);
}
public DiscriminatorOnColumnDSL onColumnWithNamedGetter(final Predicate super K> discriminatorColumnPredicate, final CheckedBiFunction getter) {
Function, Getter super S, ? extends KT>> getterFactory = new Function, Getter super S, ? extends KT>>() {
@Override
public Getter super S, ? extends KT> apply(List ks) {
if (ks.isEmpty()) throw new IllegalStateException("No discriminatory field found " + discriminatorColumnPredicate);
if (ks.size() != 1) throw new IllegalStateException("Found multiple discriminator field " + ks);
K k = ks.get(0);
final String columnName = k.getName();
return new Getter() {
@Override
public KT get(S target) throws Exception {
return getter.apply(target, columnName);
}
};
}
};
return new DiscriminatorOnColumnDSL(mapperFactory, commonType, discriminatorColumnPredicate, getterFactory);
}
public DiscriminatorOnColumnDSL onColumnWithIndexedGetter(String columnName, IndexedGetter getter) {
return onColumnWithIndexedGetter(CaseInsensitiveFieldKeyNamePredicate.of(columnName), getter);
}
public DiscriminatorOnColumnDSL onColumnWithIndexedGetter(final Predicate super K> discriminatorColumnPredicate, final IndexedGetter getter) {
Function, Getter super S, ? extends KT>> getterFactory = new Function, Getter super S, ? extends KT>>() {
@Override
public Getter super S, ? extends KT> apply(List ks) {
if (ks.isEmpty()) throw new IllegalStateException("No discriminatory field found " + discriminatorColumnPredicate);
if (ks.size() != 1) throw new IllegalStateException("Found multiple discriminator field " + ks);
K k = ks.get(0);
final int columnIndex = k.getIndex();
return new Getter() {
@Override
public KT get(S target) throws Exception {
return getter.get(target, columnIndex);
}
};
}
};
return new DiscriminatorOnColumnDSL(mapperFactory, commonType, discriminatorColumnPredicate, getterFactory);
}
public DiscriminatorOnColumnDSL onGetter(final Getter super S, ? extends KT> getter) {
Function, Getter super S, ? extends KT>> getterFactory = new Function, Getter super S, ? extends KT>>() {
@Override
public Getter super S, ? extends KT> apply(List ks) {
return getter;
}
};
return new DiscriminatorOnColumnDSL(mapperFactory, commonType, ConstantPredicate.falsePredicate(), getterFactory);
}
public MF with(Class extends T> implementation) {
return with((Type)implementation);
}
public MF with(Type implementation) {
DiscriminatorBuilder db = new DiscriminatorBuilder(commonType, mapperFactory.getReflectionService());
db.defaultType(implementation);
mapperFactory.discriminators.add(new MapperConfig.Discriminator(commonType, db.cases.toArray(new MapperConfig.DiscriminatorCase[0]), ConstantPredicate.truePredicate()));
return mapperFactory;
}
/*
* Backward compatible
*/
private MF _with(Consumer> consumer) {
DiscriminatorBuilder db = new DiscriminatorBuilder(commonType, mapperFactory.getReflectionService());
consumer.accept(db);
mapperFactory.discriminators.add(new MapperConfig.Discriminator(commonType, db.cases.toArray(new MapperConfig.DiscriminatorCase[0]), ConstantPredicate.truePredicate()));
return mapperFactory;
}
}
public static class DiscriminatorOnColumnDSL, MF extends AbstractMapperFactory, S, T, KT> {
private final MF mapperFactory;
private final Type commonType;
private final Predicate super K> discriminatorColumnPredicate;
private final Function, Getter super S, ? extends KT>> getterFactory;
public DiscriminatorOnColumnDSL(MF mapperFactory, Type commonType, Predicate super K> discriminatorColumnPredicate, Function, Getter super S, ? extends KT>> getterFactory) {
this.mapperFactory = mapperFactory;
this.commonType = commonType;
this.discriminatorColumnPredicate = discriminatorColumnPredicate;
this.getterFactory = getterFactory;
}
public MF with(Consumer> consumer) {
mapperFactory.addColumnProperty(discriminatorColumnPredicate, OptionalProperty.INSTANCE, new DiscriminatorColumnProperty(commonType));
DiscriminatorBuilder db = new DiscriminatorBuilder(commonType, mapperFactory.getReflectionService());
DiscriminatorConditionBuilder dcb = new DiscriminatorConditionBuilder(db, getterFactory);
consumer.accept(dcb);
mapperFactory.discriminators.add(new MapperConfig.Discriminator(commonType, db.cases.toArray(new MapperConfig.DiscriminatorCase[0]), discriminatorColumnPredicate));
return mapperFactory;
}
}
}