package org.simpleflatmapper.map.mapper; import org.simpleflatmapper.map.CaseInsensitiveFieldKeyNamePredicate; import org.simpleflatmapper.map.FieldKey; import org.simpleflatmapper.map.FieldMapperErrorHandler; import org.simpleflatmapper.map.IgnoreMapperBuilderErrorHandler; import org.simpleflatmapper.map.ConsumerErrorHandler; import org.simpleflatmapper.map.error.RethrowConsumerErrorHandler; import org.simpleflatmapper.map.error.RethrowMapperBuilderErrorHandler; import org.simpleflatmapper.map.property.GetterProperty; import org.simpleflatmapper.map.property.SetterProperty; import org.simpleflatmapper.reflect.Getter; import org.simpleflatmapper.reflect.ReflectionService; import org.simpleflatmapper.reflect.Setter; import org.simpleflatmapper.reflect.meta.ClassMeta; import org.simpleflatmapper.map.PropertyNameMatcherFactory; import org.simpleflatmapper.map.MapperBuilderErrorHandler; import org.simpleflatmapper.map.MapperConfig; import org.simpleflatmapper.util.ConstantUnaryFactory; import org.simpleflatmapper.util.Predicate; import org.simpleflatmapper.util.TypeReference; import org.simpleflatmapper.util.UnaryFactory; import java.lang.reflect.Member; import java.lang.reflect.Type; import java.util.Map; // 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<K>, CD extends ColumnDefinition<K, CD>, MF extends AbstractMapperFactory<K, CD, MF>> { private FieldMapperErrorHandler<K> fieldMapperErrorHandler = null; private MapperBuilderErrorHandler mapperBuilderErrorHandler = RethrowMapperBuilderErrorHandler.INSTANCE; private ConsumerErrorHandler consumerErrorHandler = RethrowConsumerErrorHandler.INSTANCE; private final AbstractColumnDefinitionProvider<CD, K> columnDefinitions; private final CD 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; public AbstractMapperFactory(AbstractMapperFactory<K, CD, ?> config) { 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; } public AbstractMapperFactory(AbstractColumnDefinitionProvider<CD, K> columnDefinitions, CD identity) { this.columnDefinitions = columnDefinitions; this.identity = identity; } /** * 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<K> fieldMapperErrorHandler) { this.fieldMapperErrorHandler = fieldMapperErrorHandler; return (MF) this; } /** * Change the mapperBuilderErrorHandler to an IgnoreMapperBuilderErrorHandler. * @return the current factory */ public final MF ignorePropertyNotFound() { return mapperBuilderErrorHandler(IgnoreMapperBuilderErrorHandler.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 MapperConfig<K, CD> mapperConfig() { return MapperConfig .<K, CD>config(columnDefinitions) .mapperBuilderErrorHandler(mapperBuilderErrorHandler) .propertyNameMatcherFactory(propertyNameMatcherFactory) .failOnAsm(failOnAsm) .asmMapperNbFieldsLimit(asmMapperNbFieldsLimit) .fieldMapperErrorHandler(fieldMapperErrorHandler) .consumerErrorHandler(consumerErrorHandler) .maxMethodSize(maxMethodSize); } /** * Associate an alias on the property key to rename to value. * @param key the property to rename * @param value then name to rename to * @return the current factory */ public final MF addAlias(String key, String value) { return addColumnDefinition(key, identity.addRename(value)); } /** * Associate the specified columnDefinition to the specified property. * @param key the property * @param columnDefinition the columnDefinition * @return the current factory */ public final MF addColumnDefinition(String key, CD columnDefinition) { columnDefinitions.addColumnDefinition(key, 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, CD columnDefinition) { columnDefinitions.addColumnDefinition(predicate, columnDefinition); return (MF) this; } /** * Associate the specified columnProperties to the property matching the predicate. * @param name the property predicate * @param properties the properties * @return the current factory */ public final MF addColumnProperty(String name, Object... properties) { for(Object property : properties) { columnDefinitions.addColumnProperty(name, 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<K, Object> propertyFactory) { columnDefinitions.addColumnProperty(predicate, 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<String, String> aliases) { for(Map.Entry<String, String> e : aliases.entrySet()) { addAlias(e.getKey(), e.getValue()); } return (MF) this; } /** * @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; } /** * change the number of fields threshold after which an asm jdbcMapper is not generated. * <p> * 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 <T> ClassMeta<T> getClassMeta(TypeReference<T> target) { return getClassMeta(target.getType()); } public final <T> ClassMeta<T> getClassMeta(Class<T> target) { return getClassMeta((Type)target); } public final <T> ClassMeta<T> getClassMeta(Type target) { return getReflectionService().getClassMeta(target); } public final <T> ClassMeta<T> getClassMetaWithExtraInstantiator(TypeReference<T> target, Member instantiator) { return getClassMetaWithExtraInstantiator(target.getType(), instantiator); } public final <T> ClassMeta<T> getClassMetaWithExtraInstantiator(Class<T> target, Member instantiator) { return getClassMetaWithExtraInstantiator((Type) target, instantiator); } public final <T> ClassMeta<T> getClassMetaWithExtraInstantiator(Type target, Member instantiator) { return getReflectionService().getClassMetaExtraInstantiator(target, instantiator); } public ReflectionService getReflectionService() { if (reflectionService == null) { reflectionService = ReflectionService.newInstance(useAsm); } return reflectionService; } public ColumnDefinitionProvider<CD, K> columnDefinitions() { return columnDefinitions; } }