org.simpleflatmapper.map.mapper.ConstantSourceMapperBuilder Maven / Gradle / Ivy
Show all versions of sfm-map Show documentation
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.impl.GetterMapper;
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.ArrayElementPropertyMeta;
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> {
private static final FieldKey[] FIELD_KEYS = new FieldKey[0];
private final Type target;
private final ConstantSourceFieldMapperFactory fieldMapperFactory;
protected final PropertyMappingsBuilder> propertyMappingsBuilder;
protected final ReflectionService reflectionService;
private final List> additionalMappers = new ArrayList>();
private final MapperSource super S, K> mapperSource;
private final MapperConfig> mapperConfig;
protected final MappingContextFactoryBuilder super S, K> mappingContextFactoryBuilder;
private final KeyFactory keyFactory;
public ConstantSourceMapperBuilder(
final MapperSource super S, K> mapperSource,
final ClassMeta classMeta,
final MapperConfig> mapperConfig,
MappingContextFactoryBuilder super S, K> mappingContextFactoryBuilder,
KeyFactory keyFactory) throws MapperBuildingException {
this(mapperSource, classMeta, mapperConfig, mappingContextFactoryBuilder, keyFactory, null);
}
public ConstantSourceMapperBuilder(
final MapperSource super S, K> mapperSource,
final ClassMeta classMeta,
final MapperConfig> mapperConfig,
MappingContextFactoryBuilder super S, K> mappingContextFactoryBuilder,
KeyFactory keyFactory, PropertyFinder propertyFinder) throws MapperBuildingException {
this.mapperSource = requireNonNull("fieldMapperSource", mapperSource);
this.mapperConfig = requireNonNull("mapperConfig", mapperConfig);
this.mappingContextFactoryBuilder = mappingContextFactoryBuilder;
this.fieldMapperFactory = new ConstantSourceFieldMapperFactoryImpl(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 addMapping(K key, final FieldMapperColumnDefinition columnDefinition) {
final FieldMapperColumnDefinition composedDefinition = columnDefinition.compose(mapperConfig.columnDefinitions().getColumnDefinition(key));
final K mappedColumnKey = composedDefinition.rename(key);
if (columnDefinition.getCustomFieldMapper() != null) {
addMapper((FieldMapper) columnDefinition.getCustomFieldMapper());
} else {
PropertyMapping> propertyMapping = propertyMappingsBuilder.addProperty(mappedColumnKey, composedDefinition);
if (propertyMapping != null) {
FieldMapperColumnDefinition effectiveColumnDefinition = propertyMapping.getColumnDefinition();
if (effectiveColumnDefinition.isKey() && effectiveColumnDefinition.keyAppliesTo().test(propertyMapping.getPropertyMeta())) {
mappingContextFactoryBuilder.addKey(key);
}
}
}
return this;
}
public Mapper mapper() {
// look for property with a default value property but no definition.
mapperConfig
.columnDefinitions()
.forEach(
DefaultValueProperty.class,
new BiConsumer, 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(columnProperty.getValue()), mapperSource.source(), columnProperty.getValue().getClass());
final FieldMapperColumnDefinition columnDefinition =
FieldMapperColumnDefinition.identity().add(columnProperty,
getterProperty);
propertyMappingsBuilder.addPropertyIfPresent(keyFactory.newKey(name, propertyMappingsBuilder.maxIndex() + 1), columnDefinition);
}
}
});
FieldMapper[] fields = fields();
InstantiatorAndFieldMappers constructorFieldMappersAndInstantiator = getConstructorFieldMappersAndInstantiator();
Mapper mapper;
if (isEligibleForAsmMapper()) {
try {
mapper =
reflectionService
.getAsmFactory()
.registerOrCreate(MapperAsmFactory.class,
new UnaryFactory() {
@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(fields, constructorFieldMappersAndInstantiator.fieldMappers, constructorFieldMappersAndInstantiator.instantiator);
}
}
} else {
mapper = new MapperImpl(fields, constructorFieldMappersAndInstantiator.fieldMappers, constructorFieldMappersAndInstantiator.instantiator);
}
return mapper;
}
public boolean hasJoin() {
return mappingContextFactoryBuilder.isRoot()
&& !mappingContextFactoryBuilder.hasNoDependentKeys();
}
private Class getTargetClass() {
return TypeHelper.toClass(target);
}
@SuppressWarnings("unchecked")
private InstantiatorAndFieldMappers getConstructorFieldMappersAndInstantiator() throws MapperBuildingException {
InstantiatorFactory instantiatorFactory = reflectionService.getInstantiatorFactory();
try {
ConstructorInjections constructorInjections = constructorInjections();
Map, ?>> injections = constructorInjections.parameterGetterMap;
MapperBiInstantiatorFactory mapperBiInstantiatorFactory = new MapperBiInstantiatorFactory(instantiatorFactory);
GetterFactory super S, K> getterFactory = fieldMapperAsGetterFactory();
BiInstantiator, T> instantiator =
mapperBiInstantiatorFactory.
>
getBiInstantiator(mapperSource.source(), target, propertyMappingsBuilder, injections, getterFactory, reflectionService.builderIgnoresNullValues());
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, ?>> injections = new HashMap, ?>>();
final List> fieldMappers = new ArrayList>();
propertyMappingsBuilder.forEachConstructorProperties(new ForEachCallBack>>() {
@SuppressWarnings("unchecked")
@Override
public void handle(PropertyMapping> propertyMapping) {
if (!isTargetForMapperFieldMapper(propertyMapping)) {
PropertyMeta pm = propertyMapping.getPropertyMeta();
ConstructorPropertyMeta cProp = (ConstructorPropertyMeta) 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, 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>> properties = e.propertyMappings;
final MappingContextFactoryBuilder currentBuilder = getMapperContextFactoryBuilder(e.owner, properties);
final Mapper mapper;
if (properties.size() == 1 && properties.get(0).getPropertyMeta() instanceof ArrayElementPropertyMeta) {
mapper = getterPropertyMapper(e.owner, properties.get(0));
} else {
mapper = subPropertyMapper(e.owner, properties, currentBuilder);
}
ConstructorPropertyMeta meta = (ConstructorPropertyMeta) 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 Mapper getterPropertyMapper(PropertyMeta owner, PropertyMapping> propertyMapping) {
PropertyMeta pm = propertyMapping.getPropertyMeta();
final Getter super S, P> getter =
(Getter super S, P>) fieldMapperFactory.getGetterFromSource(propertyMapping.getColumnKey(), pm.getPropertyType(), propertyMapping.getColumnDefinition(), pm.getPropertyClassMetaSupplier());
return new GetterMapper(getter);
}
private MappingContextFactoryBuilder getMapperContextFactoryBuilder(PropertyMeta, ?> owner, List>> properties) {
final List subKeys = getSubKeys(properties);
return mappingContextFactoryBuilder.newBuilder(subKeys, owner);
}
@SuppressWarnings("unchecked")
private FieldMapper newMapperFieldMapper(List>> properties, PropertyMeta meta, Mapper mapper, MappingContextFactoryBuilder mappingContextFactoryBuilder) {
final MapperFieldMapper fieldMapper =
new MapperFieldMapper((Mapper) mapper,
(Setter) meta.getSetter(),
mappingContextFactoryBuilder.nullChecker(),
mappingContextFactoryBuilder.currentIndex());
return wrapFieldMapperWithErrorHandler(properties.get(0), fieldMapper);
}
@SuppressWarnings("unchecked")
private BiFunction, P> newMapperGetterAdapter(Mapper mapper, MappingContextFactoryBuilder builder) {
return new MapperBiFunctionAdapter((Mapper)mapper, builder.nullChecker(), builder.currentIndex());
}
// call use towards sub jdbcMapper
// the keys are initialised
private
void addMapping(K columnKey, FieldMapperColumnDefinition columnDefinition, PropertyMeta prop) {
propertyMappingsBuilder.addProperty(columnKey, columnDefinition, prop);
}
@SuppressWarnings("unchecked")
private FieldMapper[] fields() {
final List> fields = new ArrayList>();
propertyMappingsBuilder.forEachProperties(new ForEachCallBack>>() {
@Override
public void handle(PropertyMapping> t) {
if (t == null || isTargetForMapperFieldMapper(t)) return;
PropertyMeta meta = t.getPropertyMeta();
if (meta == null || (meta instanceof SelfPropertyMeta)) return;
if (!meta.isConstructorProperty() && !isTargetForMapperFieldMapper(t)) {
fields.add(newFieldMapper(t));
}
}
});
for(PropertyPerOwner e :
getSubPropertyPerOwner()) {
if (!e.owner.isConstructorProperty()) {
final MappingContextFactoryBuilder currentBuilder = getMapperContextFactoryBuilder(e.owner, e.propertyMappings);
final Mapper mapper;
if (e.propertyMappings.size() == 1 && e.propertyMappings.get(0).getPropertyMeta() instanceof ArrayElementPropertyMeta) {
mapper = getterPropertyMapper(e.owner, e.propertyMappings.get(0));
} else {
mapper = subPropertyMapper(e.owner, e.propertyMappings, currentBuilder);
}
fields.add(newMapperFieldMapper(e.propertyMappings, e.owner, mapper, currentBuilder));
}
}
for(FieldMapper mapper : additionalMappers) {
fields.add(mapper);
}
return fields.toArray(new FieldMapper[0]);
}
private boolean isTargetForMapperFieldMapper(PropertyMapping pm) {
return pm.getPropertyMeta().isSubProperty() || (pm.getPropertyMeta() instanceof ArrayElementPropertyMeta && pm.getColumnDefinition().isKey());
}
private List getSubPropertyPerOwner() {
final List subPropertiesList = new ArrayList();
propertyMappingsBuilder.forEachProperties(new ForEachCallBack>>() {
@SuppressWarnings("unchecked")
@Override
public void handle(PropertyMapping> t) {
if (t == null) return;
PropertyMeta meta = t.getPropertyMeta();
if (meta == null) return;
if (isTargetForMapperFieldMapper(t)) {
addSubProperty(t, meta, t.getColumnKey());
}
}
private void addSubProperty(PropertyMapping> pm, PropertyMeta propertyMeta, K key) {
PropertyMeta propertyOwner = getOwner(propertyMeta);
List>> props = getList(propertyOwner);
if (props == null) {
props = new ArrayList>>();
subPropertiesList.add(new PropertyPerOwner(propertyOwner, props));
}
props.add(pm);
}
private PropertyMeta getOwner(PropertyMeta propertyMeta) {
if (propertyMeta.isSubProperty()) {
return ((SubPropertyMeta)propertyMeta).getOwnerProperty();
}
return propertyMeta;
}
private List>> getList(PropertyMeta, ?> owner) {
for(PropertyPerOwner tuple : subPropertiesList) {
if (tuple.owner.equals(owner)) {
return tuple.propertyMappings;
}
}
return null;
}
});
return subPropertiesList;
}
@SuppressWarnings("unchecked")
private Mapper subPropertyMapper(PropertyMeta owner, List>> properties, MappingContextFactoryBuilder mappingContextFactoryBuilder) {
final ConstantSourceMapperBuilder builder =
newSubBuilder(owner.getPropertyClassMeta(),
mappingContextFactoryBuilder,
(PropertyFinder) propertyMappingsBuilder.getPropertyFinder().getSubPropertyFinder(owner));
for(PropertyMapping> pm : properties) {
final SubPropertyMeta propertyMeta = (SubPropertyMeta) pm.getPropertyMeta();
final PropertyMeta subProperty = ((SubPropertyMeta) propertyMeta).getSubProperty();
builder.addMapping(pm.getColumnKey(), pm.getColumnDefinition(), subProperty);
}
return builder.mapper();
}
@SuppressWarnings("unchecked")
protected FieldMapper newFieldMapper(PropertyMapping> t) {
FieldMapper fieldMapper = (FieldMapper) t.getColumnDefinition().getCustomFieldMapper();
if (fieldMapper == null) {
fieldMapper = fieldMapperFactory.newFieldMapper(t, mappingContextFactoryBuilder, mapperConfig.mapperBuilderErrorHandler());
}
return wrapFieldMapperWithErrorHandler(t, fieldMapper);
}
private FieldMapper wrapFieldMapperWithErrorHandler(final PropertyMapping> t, final FieldMapper fieldMapper) {
if (fieldMapper != null && mapperConfig.hasFieldMapperErrorHandler()) {
return new FieldErrorHandlerMapper(t.getColumnKey(), fieldMapper, mapperConfig.fieldMapperErrorHandler());
}
return fieldMapper;
}
public void addMapper(FieldMapper mapper) {
additionalMappers.add(mapper);
}
private ConstantSourceMapperBuilder newSubBuilder(
ClassMeta classMeta,
MappingContextFactoryBuilder mappingContextFactoryBuilder,
PropertyFinder propertyFinder) {
return new ConstantSourceMapperBuilder(
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 getSubKeys(List>> properties) {
List keys = new ArrayList();
// look for keys property of the object
for (PropertyMapping> pm : properties) {
if (pm.getPropertyMeta().isSubProperty()) {
SubPropertyMeta subPropertyMeta = (SubPropertyMeta) pm.getPropertyMeta();
if (!(subPropertyMeta.getSubProperty() instanceof ArrayElementPropertyMeta)) {
// ignore ArrayElementPropertyMeta as it's a direct getter and will be managed in the setter
if (pm.getColumnDefinition().isKey()) {
if (pm.getColumnDefinition().keyAppliesTo().test(subPropertyMeta.getSubProperty())) {
keys.add(pm.getColumnKey());
}
}
}
} else {
if (pm.getColumnDefinition().isKey()) {
if (pm.getColumnDefinition().keyAppliesTo().test(pm.getPropertyMeta())) {
keys.add(pm.getColumnKey());
}
}
}
}
return keys;
}
private class InstantiatorAndFieldMappers {
private final FieldMapper[] fieldMappers;
private final BiInstantiator, T> instantiator;
private InstantiatorAndFieldMappers(FieldMapper[] fieldMappers, BiInstantiator, T> instantiator) {
this.fieldMappers = fieldMappers;
this.instantiator = instantiator;
}
}
private class ConstructorInjections {
private final Map, ?>> parameterGetterMap;
private final FieldMapper[] fieldMappers;
private ConstructorInjections(Map, ?>> parameterGetterMap, FieldMapper[] fieldMappers) {
this.parameterGetterMap = parameterGetterMap;
this.fieldMappers = fieldMappers;
}
}
private class PropertyPerOwner {
private final PropertyMeta owner;
private final List>> propertyMappings;
private PropertyPerOwner(PropertyMeta owner, List>> propertyMappings) {
this.owner = owner;
this.propertyMappings = propertyMappings;
}
}
private class FieldMapperFactoryGetterFactoryAdapter implements GetterFactory {
@SuppressWarnings("unchecked")
@Override
public Getter newGetter(Type target, K key, Object... properties) {
FieldMapperColumnDefinition columnDefinition = FieldMapperColumnDefinition.identity().add(properties);
return (Getter) fieldMapperFactory.getGetterFromSource(key, target , columnDefinition, new ClassMetaSupplier(target));
}
}
private class ClassMetaSupplier
implements Supplier> {
private final Type target;
public ClassMetaSupplier(Type target) {
this.target = target;
}
@Override
public ClassMeta get() {
return reflectionService.
getClassMeta(target);
}
}
}