All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.simpleflatmapper.map.mapper.AbstractMapperFactory Maven / Gradle / Ivy

Go to download

Java library to map flat record - ResultSet, csv - to java object with minimum configuration and low footprint.

There is a newer version: 9.0.2
Show newest version
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 rowFilter = null;
	private boolean unorderedJoin;

	protected ContextualGetterFactory 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 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 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 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 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 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 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 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 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 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 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 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 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 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> getterFactory; public DiscriminatorConditionBuilder(DiscriminatorBuilder discriminatorBuilder, Function, Getter> 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 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 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 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 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 getter = getterFactory.apply(ks); return new SourcePredicate(predicate, getter); } }; } static class SourcePredicate implements Predicate { final Predicate predicate; final Getter getter; public SourcePredicate(Predicate predicate, Getter 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 classMeta) { return discriminatorCase(predicateFactory, classMeta); } public DiscriminatorBuilder when(Function, Predicate> predicateFactory, Class 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 classMeta) { return discriminatorCase(predicate, classMeta); } public DiscriminatorBuilder when(Predicate predicate, Class target) { return discriminatorCase(predicate, target); } public DiscriminatorBuilder when(Predicate predicate, Type target) { return discriminatorCase(predicate, target); } public DiscriminatorBuilder defaultType(Class target) { return defaultType((Type) target); } public DiscriminatorBuilder defaultType(Type target) { return discriminatorCase(ConstantPredicate.truePredicate(), target); } public DiscriminatorBuilder discriminatorCase(final Predicate predicate, ClassMeta classMeta) { return discriminatorCase(new Function, Predicate>() { @Override public Predicate apply(List ks) { return predicate; } }, classMeta ); } public DiscriminatorBuilder discriminatorCase(final Function, Predicate> predicateFactory, ClassMeta classMeta) { MapperConfig.DiscriminatorCase dCase = new MapperConfig.DiscriminatorCase(predicateFactory, classMeta); cases.add(dCase); return this; } public DiscriminatorBuilder discriminatorCase(Predicate predicate, Class 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 discriminatorColumnPredicate, final Class discriminatorColunnType) { Function, Getter> getterFactory = new Function, Getter>() { @Override public Getter 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 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 discriminatorColumnPredicate, final CheckedBiFunction getter) { Function, Getter> getterFactory = new Function, Getter>() { @Override public Getter 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 discriminatorColumnPredicate, final IndexedGetter getter) { Function, Getter> getterFactory = new Function, Getter>() { @Override public Getter 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 getter) { Function, Getter> getterFactory = new Function, Getter>() { @Override public Getter apply(List ks) { return getter; } }; return new DiscriminatorOnColumnDSL(mapperFactory, commonType, ConstantPredicate.falsePredicate(), getterFactory); } public MF with(Class 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 discriminatorColumnPredicate; private final Function, Getter> getterFactory; public DiscriminatorOnColumnDSL(MF mapperFactory, Type commonType, Predicate discriminatorColumnPredicate, Function, Getter> 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; } } }