package org.simpleflatmapper.map.mapper; import org.simpleflatmapper.converter.ConverterService; import org.simpleflatmapper.map.FieldKey; import org.simpleflatmapper.map.MapperBuildingException; import org.simpleflatmapper.map.MappingContext; import org.simpleflatmapper.map.asm.MapperAsmFactory; import org.simpleflatmapper.map.fieldmapper.MapperFieldMapper; import org.simpleflatmapper.map.property.DefaultValueProperty; import org.simpleflatmapper.map.property.FieldMapperColumnDefinition; import org.simpleflatmapper.map.property.GetterProperty; import org.simpleflatmapper.map.context.MappingContextFactoryBuilder; import org.simpleflatmapper.map.impl.FieldErrorHandlerMapper; import org.simpleflatmapper.map.fieldmapper.ConstantSourceFieldMapperFactory; import org.simpleflatmapper.map.fieldmapper.ConstantSourceFieldMapperFactoryImpl; import org.simpleflatmapper.reflect.BiInstantiator; import org.simpleflatmapper.reflect.Getter; import org.simpleflatmapper.reflect.InstantiatorFactory; import org.simpleflatmapper.reflect.Parameter; import org.simpleflatmapper.reflect.ReflectionService; import org.simpleflatmapper.reflect.Setter; import org.simpleflatmapper.reflect.asm.AsmFactory; import org.simpleflatmapper.reflect.getter.BiFunctionGetter; import org.simpleflatmapper.reflect.getter.ConstantGetter; import org.simpleflatmapper.reflect.getter.GetterFactory; import org.simpleflatmapper.reflect.getter.NullGetter; import org.simpleflatmapper.reflect.meta.ClassMeta; import org.simpleflatmapper.reflect.meta.ConstructorPropertyMeta; import org.simpleflatmapper.reflect.meta.PropertyFinder; import org.simpleflatmapper.reflect.meta.PropertyMeta; import org.simpleflatmapper.reflect.meta.SelfPropertyMeta; import org.simpleflatmapper.reflect.meta.SubPropertyMeta; import org.simpleflatmapper.map.FieldMapper; import org.simpleflatmapper.map.Mapper; import org.simpleflatmapper.map.MapperConfig; import org.simpleflatmapper.reflect.setter.NullSetter; import org.simpleflatmapper.util.BiConsumer; import org.simpleflatmapper.util.BiFunction; import org.simpleflatmapper.util.ErrorDoc; import org.simpleflatmapper.util.ErrorHelper; import org.simpleflatmapper.util.ForEachCallBack; import org.simpleflatmapper.util.Named; import org.simpleflatmapper.util.Predicate; import org.simpleflatmapper.util.Supplier; import org.simpleflatmapper.util.TypeHelper; import org.simpleflatmapper.util.UnaryFactory; import java.lang.reflect.Type; import java.util.*; import static org.simpleflatmapper.util.Asserts.requireNonNull; public final class ConstantSourceMapperBuilder<S, T, K extends FieldKey<K>> { private static final FieldKey[] FIELD_KEYS = new FieldKey[0]; private final Type target; private final ConstantSourceFieldMapperFactory<S, K> fieldMapperFactory; protected final PropertyMappingsBuilder<T, K,FieldMapperColumnDefinition<K>> propertyMappingsBuilder; protected final ReflectionService reflectionService; private final List<FieldMapper<S, T>> additionalMappers = new ArrayList<FieldMapper<S, T>>(); private final MapperSource<? super S, K> mapperSource; private final MapperConfig<K, FieldMapperColumnDefinition<K>> mapperConfig; protected final MappingContextFactoryBuilder<? super S, K> mappingContextFactoryBuilder; private final KeyFactory<K> keyFactory; public ConstantSourceMapperBuilder( final MapperSource<? super S, K> mapperSource, final ClassMeta<T> classMeta, final MapperConfig<K, FieldMapperColumnDefinition<K>> mapperConfig, MappingContextFactoryBuilder<? super S, K> mappingContextFactoryBuilder, KeyFactory<K> keyFactory) throws MapperBuildingException { this(mapperSource, classMeta, mapperConfig, mappingContextFactoryBuilder, keyFactory, null); } public ConstantSourceMapperBuilder( final MapperSource<? super S, K> mapperSource, final ClassMeta<T> classMeta, final MapperConfig<K, FieldMapperColumnDefinition<K>> mapperConfig, MappingContextFactoryBuilder<? super S, K> mappingContextFactoryBuilder, KeyFactory<K> keyFactory, PropertyFinder<T> propertyFinder) throws MapperBuildingException { this.mapperSource = requireNonNull("fieldMapperSource", mapperSource); this.mapperConfig = requireNonNull("mapperConfig", mapperConfig); this.mappingContextFactoryBuilder = mappingContextFactoryBuilder; this.fieldMapperFactory = new ConstantSourceFieldMapperFactoryImpl<S, K>(mapperSource.getterFactory(), ConverterService.getInstance(), mapperSource.source()); this.keyFactory = keyFactory; this.propertyMappingsBuilder = PropertyMappingsBuilder.of(classMeta, mapperConfig, PropertyWithSetterOrConstructor.INSTANCE, propertyFinder); this.target = requireNonNull("classMeta", classMeta).getType(); this.reflectionService = requireNonNull("classMeta", classMeta).getReflectionService(); } @SuppressWarnings("unchecked") public final ConstantSourceMapperBuilder<S, T, K> addMapping(K key, final FieldMapperColumnDefinition<K> columnDefinition) { final FieldMapperColumnDefinition<K> composedDefinition = columnDefinition.compose(mapperConfig.columnDefinitions().getColumnDefinition(key)); final K mappedColumnKey = composedDefinition.rename(key); if (columnDefinition.getCustomFieldMapper() != null) { addMapper((FieldMapper<S, T>) columnDefinition.getCustomFieldMapper()); } else { PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>> propertyMapping = propertyMappingsBuilder.addProperty(mappedColumnKey, composedDefinition); if (propertyMapping != null) { FieldMapperColumnDefinition<K> effectiveColumnDefinition = propertyMapping.getColumnDefinition(); if (effectiveColumnDefinition.isKey() && effectiveColumnDefinition.keyAppliesTo().test(propertyMapping.getPropertyMeta())) { mappingContextFactoryBuilder.addKey(key); } } } return this; } public Mapper<S, T> mapper() { // look for property with a default value property but no definition. mapperConfig .columnDefinitions() .forEach( DefaultValueProperty.class, new BiConsumer<Predicate<? super K>, DefaultValueProperty>() { @Override public void accept(Predicate<? super K> predicate, DefaultValueProperty columnProperty) { if (propertyMappingsBuilder.hasKey(predicate)){ return; } if (predicate instanceof Named) { String name = ((Named)predicate).getName(); GetterProperty getterProperty = new GetterProperty(new ConstantGetter<S, Object>(columnProperty.getValue()), mapperSource.source(), columnProperty.getValue().getClass()); final FieldMapperColumnDefinition<K> columnDefinition = FieldMapperColumnDefinition.<K>identity().add(columnProperty, getterProperty); propertyMappingsBuilder.addPropertyIfPresent(keyFactory.newKey(name, propertyMappingsBuilder.maxIndex() + 1), columnDefinition); } } }); FieldMapper<S, T>[] fields = fields(); InstantiatorAndFieldMappers constructorFieldMappersAndInstantiator = getConstructorFieldMappersAndInstantiator(); Mapper<S, T> mapper; if (isEligibleForAsmMapper()) { try { mapper = reflectionService .getAsmFactory() .registerOrCreate(MapperAsmFactory.class, new UnaryFactory<AsmFactory, MapperAsmFactory>() { @Override public MapperAsmFactory newInstance(AsmFactory asmFactory) { return new MapperAsmFactory(asmFactory); } }) .createMapper( getKeys(), fields, constructorFieldMappersAndInstantiator.fieldMappers, constructorFieldMappersAndInstantiator.instantiator, mapperSource.source(), getTargetClass() ); } catch (Throwable e) { if (mapperConfig.failOnAsm()) { return ErrorHelper.rethrow(e); } else { mapper = new MapperImpl<S, T>(fields, constructorFieldMappersAndInstantiator.fieldMappers, constructorFieldMappersAndInstantiator.instantiator); } } } else { mapper = new MapperImpl<S, T>(fields, constructorFieldMappersAndInstantiator.fieldMappers, constructorFieldMappersAndInstantiator.instantiator); } return mapper; } public boolean hasJoin() { return mappingContextFactoryBuilder.isRoot() && !mappingContextFactoryBuilder.hasNoDependentKeys(); } private Class<T> getTargetClass() { return TypeHelper.toClass(target); } @SuppressWarnings("unchecked") private InstantiatorAndFieldMappers getConstructorFieldMappersAndInstantiator() throws MapperBuildingException { InstantiatorFactory instantiatorFactory = reflectionService.getInstantiatorFactory(); try { ConstructorInjections constructorInjections = constructorInjections(); Map<Parameter, BiFunction<? super S , ? super MappingContext<? super S>, ?>> injections = constructorInjections.parameterGetterMap; MapperBiInstantiatorFactory mapperBiInstantiatorFactory = new MapperBiInstantiatorFactory(instantiatorFactory); GetterFactory<? super S, K> getterFactory = fieldMapperAsGetterFactory(); BiInstantiator<S, MappingContext<? super S>, T> instantiator = mapperBiInstantiatorFactory. <S, T, K, FieldMapperColumnDefinition<K>> getBiInstantiator(mapperSource.source(), target, propertyMappingsBuilder, injections, getterFactory); return new InstantiatorAndFieldMappers(constructorInjections.fieldMappers, instantiator); } catch(Exception e) { return ErrorHelper.rethrow(e); } } private GetterFactory<? super S, K> fieldMapperAsGetterFactory() { return new FieldMapperFactoryGetterFactoryAdapter(); } @SuppressWarnings("unchecked") private ConstructorInjections constructorInjections() { final Map<Parameter, BiFunction<? super S, ? super MappingContext<? super S>, ?>> injections = new HashMap<Parameter, BiFunction<? super S, ? super MappingContext<? super S>, ?>>(); final List<FieldMapper<S, T>> fieldMappers = new ArrayList<FieldMapper<S, T>>(); propertyMappingsBuilder.forEachConstructorProperties(new ForEachCallBack<PropertyMapping<T,?,K, FieldMapperColumnDefinition<K>>>() { @SuppressWarnings("unchecked") @Override public void handle(PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>> propertyMapping) { PropertyMeta<T, ?> pm = propertyMapping.getPropertyMeta(); ConstructorPropertyMeta<T, ?> cProp = (ConstructorPropertyMeta<T, ?>) pm; Parameter parameter = cProp.getParameter(); Getter<? super S, ?> getter = fieldMapperFactory.getGetterFromSource(propertyMapping.getColumnKey(), pm.getPropertyType(), propertyMapping.getColumnDefinition(), pm.getPropertyClassMetaSupplier()); if (NullGetter.isNull(getter)) { mapperConfig.mapperBuilderErrorHandler() .accessorNotFound("Could not find getter for " + propertyMapping.getColumnKey() + " type " + propertyMapping.getPropertyMeta().getPropertyType() + " path " + propertyMapping.getPropertyMeta().getPath() + " See " + ErrorDoc.toUrl("FMMB_GETTER_NOT_FOUND")); } else { injections.put(parameter, new BiFunctionGetter<S, MappingContext<? super S>, Object>(getter)); } if (!NullSetter.isNull(cProp.getSetter())) { fieldMappers.add(fieldMapperFactory.newFieldMapper(propertyMapping, mappingContextFactoryBuilder, mapperConfig.mapperBuilderErrorHandler())); } } }); for(PropertyPerOwner e : getSubPropertyPerOwner()) { if (e.owner.isConstructorProperty()) { final List<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>> properties = e.propertyMappings; final MappingContextFactoryBuilder currentBuilder = getMapperContextFactoryBuilder(e.owner, properties); final Mapper<S, ?> mapper = subPropertyMapper(e.owner, properties, currentBuilder); ConstructorPropertyMeta<T, ?> meta = (ConstructorPropertyMeta<T, ?>) e.owner; injections.put(meta.getParameter(), newMapperGetterAdapter(mapper, currentBuilder)); fieldMappers.add(newMapperFieldMapper(properties, meta, mapper, currentBuilder)); } } return new ConstructorInjections(injections, fieldMappers.toArray(new FieldMapper[0])); } private MappingContextFactoryBuilder getMapperContextFactoryBuilder(PropertyMeta<?, ?> owner, List<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>> properties) { final List<K> subKeys = getSubKeys(properties); return mappingContextFactoryBuilder.newBuilder(subKeys, owner); } @SuppressWarnings("unchecked") private <P> FieldMapper<S, T> newMapperFieldMapper(List<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>> properties, PropertyMeta<T, ?> meta, Mapper<S, ?> mapper, MappingContextFactoryBuilder<S, K> mappingContextFactoryBuilder) { final MapperFieldMapper<S, T, P> fieldMapper = new MapperFieldMapper<S, T, P>((Mapper<S, P>) mapper, (Setter<T, P>) meta.getSetter(), mappingContextFactoryBuilder.nullChecker(), mappingContextFactoryBuilder.currentIndex()); return wrapFieldMapperWithErrorHandler(properties.get(0), fieldMapper); } @SuppressWarnings("unchecked") private <P> BiFunction<S, MappingContext<? super S>, P> newMapperGetterAdapter(Mapper<S, ?> mapper, MappingContextFactoryBuilder<S, K> builder) { return new MapperBiFunctionAdapter<S, P>((Mapper<S, P>)mapper, builder.nullChecker(), builder.currentIndex()); } // call use towards sub jdbcMapper // the keys are initialised private <P> void addMapping(K columnKey, FieldMapperColumnDefinition<K> columnDefinition, PropertyMeta<T, P> prop) { propertyMappingsBuilder.addProperty(columnKey, columnDefinition, prop); } @SuppressWarnings("unchecked") private FieldMapper<S, T>[] fields() { final List<FieldMapper<S, T>> fields = new ArrayList<FieldMapper<S, T>>(); propertyMappingsBuilder.forEachProperties(new ForEachCallBack<PropertyMapping<T,?,K, FieldMapperColumnDefinition<K>>>() { @Override public void handle(PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>> t) { if (t == null) return; PropertyMeta<T, ?> meta = t.getPropertyMeta(); if (meta == null || (meta instanceof SelfPropertyMeta)) return; if (!meta.isConstructorProperty() && !meta.isSubProperty()) { fields.add(newFieldMapper(t)); } } }); for(PropertyPerOwner e : getSubPropertyPerOwner()) { if (!e.owner.isConstructorProperty()) { final MappingContextFactoryBuilder currentBuilder = getMapperContextFactoryBuilder(e.owner, e.propertyMappings); final Mapper<S, ?> mapper = subPropertyMapper(e.owner, e.propertyMappings, currentBuilder); fields.add(newMapperFieldMapper(e.propertyMappings, e.owner, mapper, currentBuilder)); } } for(FieldMapper<S, T> mapper : additionalMappers) { fields.add(mapper); } return fields.toArray(new FieldMapper[0]); } private List<PropertyPerOwner> getSubPropertyPerOwner() { final List<PropertyPerOwner> subPropertiesList = new ArrayList<PropertyPerOwner>(); propertyMappingsBuilder.forEachProperties(new ForEachCallBack<PropertyMapping<T,?,K, FieldMapperColumnDefinition<K>>>() { @SuppressWarnings("unchecked") @Override public void handle(PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>> t) { if (t == null) return; PropertyMeta<T, ?> meta = t.getPropertyMeta(); if (meta == null) return; if (meta.isSubProperty()) { addSubProperty(t, (SubPropertyMeta<T, ?, ?>) meta, t.getColumnKey()); } } private <P> void addSubProperty(PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>> pm, SubPropertyMeta<T, ?, ?> subPropertyMeta, K key) { PropertyMeta<T, ?> propertyOwner = subPropertyMeta.getOwnerProperty(); List<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>> props = getList(propertyOwner); if (props == null) { props = new ArrayList<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>>(); subPropertiesList.add(new PropertyPerOwner(propertyOwner, props)); } props.add(pm); } private List<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>> getList(PropertyMeta<?, ?> owner) { for(PropertyPerOwner tuple : subPropertiesList) { if (tuple.owner.equals(owner)) { return tuple.propertyMappings; } } return null; } }); return subPropertiesList; } @SuppressWarnings("unchecked") private <P> Mapper<S, P> subPropertyMapper(PropertyMeta<T, P> owner, List<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>> properties, MappingContextFactoryBuilder<S, K> mappingContextFactoryBuilder) { final ConstantSourceMapperBuilder<S, P, K> builder = newSubBuilder(owner.getPropertyClassMeta(), mappingContextFactoryBuilder, (PropertyFinder<P>) propertyMappingsBuilder.getPropertyFinder().getSubPropertyFinder(owner)); for(PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>> pm : properties) { final SubPropertyMeta<T, P, ?> propertyMeta = (SubPropertyMeta<T, P, ?>) pm.getPropertyMeta(); final PropertyMeta<P, ?> subProperty = ((SubPropertyMeta<T, P, ?>) propertyMeta).getSubProperty(); builder.addMapping(pm.getColumnKey(), pm.getColumnDefinition(), subProperty); } return builder.mapper(); } @SuppressWarnings("unchecked") protected <P> FieldMapper<S, T> newFieldMapper(PropertyMapping<T, P, K, FieldMapperColumnDefinition<K>> t) { FieldMapper<S, T> fieldMapper = (FieldMapper<S, T>) t.getColumnDefinition().getCustomFieldMapper(); if (fieldMapper == null) { fieldMapper = fieldMapperFactory.newFieldMapper(t, mappingContextFactoryBuilder, mapperConfig.mapperBuilderErrorHandler()); } return wrapFieldMapperWithErrorHandler(t, fieldMapper); } private <P> FieldMapper<S, T> wrapFieldMapperWithErrorHandler(final PropertyMapping<T, P, K, FieldMapperColumnDefinition<K>> t, final FieldMapper<S, T> fieldMapper) { if (fieldMapper != null && mapperConfig.hasFieldMapperErrorHandler()) { return new FieldErrorHandlerMapper<S, T, K>(t.getColumnKey(), fieldMapper, mapperConfig.fieldMapperErrorHandler()); } return fieldMapper; } public void addMapper(FieldMapper<S, T> mapper) { additionalMappers.add(mapper); } private <ST> ConstantSourceMapperBuilder<S, ST, K> newSubBuilder( ClassMeta<ST> classMeta, MappingContextFactoryBuilder<S, K> mappingContextFactoryBuilder, PropertyFinder<ST> propertyFinder) { return new ConstantSourceMapperBuilder<S, ST, K>( mapperSource, classMeta, mapperConfig, mappingContextFactoryBuilder, keyFactory, propertyFinder); } private FieldKey<?>[] getKeys() { return propertyMappingsBuilder.getKeys().toArray(FIELD_KEYS); } private boolean isEligibleForAsmMapper() { return reflectionService.isAsmActivated() && propertyMappingsBuilder.size() < mapperConfig.asmMapperNbFieldsLimit(); } @SuppressWarnings("unchecked") private List<K> getSubKeys(List<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>> properties) { List<K> keys = new ArrayList<K>(); // look for keys property of the object for (PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>> pm : properties) { SubPropertyMeta<T, ?, ?> subPropertyMeta = (SubPropertyMeta<T, ?, ?>) pm.getPropertyMeta(); if (pm.getColumnDefinition().isKey()) { if (pm.getColumnDefinition().keyAppliesTo().test(subPropertyMeta.getSubProperty())) { keys.add(pm.getColumnKey()); } } } return keys; } private class InstantiatorAndFieldMappers { private final FieldMapper<S, T>[] fieldMappers; private final BiInstantiator<S, MappingContext<? super S>, T> instantiator; private InstantiatorAndFieldMappers(FieldMapper<S, T>[] fieldMappers, BiInstantiator<S, MappingContext<? super S>, T> instantiator) { this.fieldMappers = fieldMappers; this.instantiator = instantiator; } } private class ConstructorInjections { private final Map<Parameter, BiFunction<? super S, ? super MappingContext<? super S>, ?>> parameterGetterMap; private final FieldMapper<S, T>[] fieldMappers; private ConstructorInjections(Map<Parameter, BiFunction<? super S, ? super MappingContext<? super S>, ?>> parameterGetterMap, FieldMapper<S, T>[] fieldMappers) { this.parameterGetterMap = parameterGetterMap; this.fieldMappers = fieldMappers; } } private class PropertyPerOwner { private final PropertyMeta<T, ?> owner; private final List<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>> propertyMappings; private PropertyPerOwner(PropertyMeta<T, ?> owner, List<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>> propertyMappings) { this.owner = owner; this.propertyMappings = propertyMappings; } } private class FieldMapperFactoryGetterFactoryAdapter implements GetterFactory<S, K> { @SuppressWarnings("unchecked") @Override public <P> Getter<S, P> newGetter(Type target, K key, Object... properties) { FieldMapperColumnDefinition<K> columnDefinition = FieldMapperColumnDefinition.<K>identity().add(properties); return (Getter<S, P>) fieldMapperFactory.getGetterFromSource(key, target , columnDefinition, new ClassMetaSupplier<P>(target)); } } private class ClassMetaSupplier<P> implements Supplier<ClassMeta<P>> { private final Type target; public ClassMetaSupplier(Type target) { this.target = target; } @Override public ClassMeta<P> get() { return reflectionService.<P>getClassMeta(target); } } }