package org.simpleflatmapper.map.mapper; import org.simpleflatmapper.map.FieldKey; import org.simpleflatmapper.map.FieldMapper; import org.simpleflatmapper.map.Mapper; import org.simpleflatmapper.map.MapperConfig; import org.simpleflatmapper.map.MappingContext; import org.simpleflatmapper.map.PropertyWithGetter; import org.simpleflatmapper.map.asm.MapperAsmFactory; import org.simpleflatmapper.map.property.FieldMapperColumnDefinition; import org.simpleflatmapper.map.property.ConstantValueProperty; import org.simpleflatmapper.map.context.KeySourceGetter; import org.simpleflatmapper.map.context.MappingContextFactoryBuilder; import org.simpleflatmapper.reflect.BiInstantiator; import org.simpleflatmapper.reflect.ReflectionService; import org.simpleflatmapper.reflect.ScoredGetter; import org.simpleflatmapper.reflect.asm.AsmFactory; import org.simpleflatmapper.reflect.getter.ConstantGetter; import org.simpleflatmapper.reflect.meta.ClassMeta; import org.simpleflatmapper.reflect.meta.ObjectPropertyMeta; import org.simpleflatmapper.reflect.meta.PropertyMeta; import org.simpleflatmapper.util.ErrorHelper; import org.simpleflatmapper.util.ForEachCallBack; import org.simpleflatmapper.util.TypeHelper; import org.simpleflatmapper.util.UnaryFactory; import java.util.ArrayList; import java.util.List; public abstract class AbstractConstantTargetMapperBuilder<S, T, K extends FieldKey<K>, B extends AbstractConstantTargetMapperBuilder<S, T, K , B>> { private final ReflectionService reflectionService; protected final MapperConfig<K, FieldMapperColumnDefinition<K>> mapperConfig; protected final PropertyMappingsBuilder<T, K, FieldMapperColumnDefinition<K>> propertyMappingsBuilder; protected final ConstantTargetFieldMapperFactory<S, K> fieldAppenderFactory; protected final ClassMeta<T> classMeta; private final Class<S> sourceClass; private int currentIndex = getStartingIndex(); public AbstractConstantTargetMapperBuilder( ClassMeta<T> classMeta, Class<S> sourceClass, MapperConfig<K, FieldMapperColumnDefinition<K>> mapperConfig, ConstantTargetFieldMapperFactory<S, K> fieldAppenderFactory) { this.sourceClass = sourceClass; this.fieldAppenderFactory = fieldAppenderFactory; this.reflectionService = classMeta.getReflectionService(); this.mapperConfig = mapperConfig; this.propertyMappingsBuilder = PropertyMappingsBuilder.of(classMeta, mapperConfig, PropertyWithGetter.INSTANCE); this.classMeta = classMeta; } public B addColumn(String column) { return addColumn(column, FieldMapperColumnDefinition.<K>identity()); } public B addColumn(String column, Object... properties) { FieldMapperColumnDefinition<K> columnDefinition = FieldMapperColumnDefinition.of(properties); return addColumn(newKey(column, currentIndex++, columnDefinition), columnDefinition); } public B addColumn(K key, Object... properties) { return addColumn(key, FieldMapperColumnDefinition.<K>identity().add(properties)); } public B addColumn(String column, FieldMapperColumnDefinition<K> columnDefinition) { return addColumn(newKey(column, currentIndex++, columnDefinition), columnDefinition); } @SuppressWarnings("unchecked") public B addColumn(K key, FieldMapperColumnDefinition<K> columnDefinition) { final FieldMapperColumnDefinition<K> composedDefinition = columnDefinition.compose(mapperConfig.columnDefinitions().getColumnDefinition(key)); final K mappedColumnKey = composedDefinition.rename(key); if (composedDefinition.has(ConstantValueProperty.class)) { ConstantValueProperty staticValueProperty = composedDefinition.lookFor(ConstantValueProperty.class); PropertyMeta<T, Object> meta = new ObjectPropertyMeta<T, Object>(key.getName(), classMeta.getType(), reflectionService, staticValueProperty.getType(), ScoredGetter.of(new ConstantGetter<T, Object>(staticValueProperty.getValue()), 1), null, null); propertyMappingsBuilder.addProperty(key, columnDefinition, meta); } else { propertyMappingsBuilder.addProperty(mappedColumnKey, composedDefinition); } return (B) this; } @SuppressWarnings("unchecked") public Mapper<T, S> mapper() { final List<FieldMapper<T, S>> mappers = new ArrayList<FieldMapper<T, S>>(); final MappingContextFactoryBuilder mappingContextFactoryBuilder = new MappingContextFactoryBuilder(new KeySourceGetter<K, T>() { @Override public Object getValue(K key, T source) throws Exception { throw new UnsupportedOperationException(); } }); propertyMappingsBuilder.forEachProperties( new ForEachCallBack<PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>>>() { @Override public void handle(PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>> pm) { preFieldProcess(mappers, pm); FieldMapper<T, S> fieldMapper = fieldAppenderFactory.newFieldMapper( pm, mappingContextFactoryBuilder, mapperConfig.mapperBuilderErrorHandler()); mappers.add(fieldMapper); postFieldProcess(mappers, pm); } } ); postMapperProcess(mappers); Mapper<T, S> mapper; FieldMapper[] fields = mappers.toArray(new FieldMapper[0]); BiInstantiator<T, MappingContext<? super T>, S> instantiator = getInstantiator(); if (mappers.size() < 256) { try { mapper = reflectionService .getAsmFactory() .registerOrCreate(MapperAsmFactory.class, new UnaryFactory<AsmFactory, MapperAsmFactory>() { @Override public MapperAsmFactory newInstance(AsmFactory asmFactory) { return new MapperAsmFactory(asmFactory); } }) .<T, S>createMapper( getKeys(), fields, new FieldMapper[0], instantiator, TypeHelper.<T>toClass(classMeta.getType()), sourceClass ); } catch (Throwable e) { if (mapperConfig.failOnAsm()) { return ErrorHelper.rethrow(e); } else { mapper = new MapperImpl<T, S>(fields, new FieldMapper[0], instantiator); } } } else { mapper = new MapperImpl<T, S>( fields, new FieldMapper[0], instantiator); } return new ContextualMapper<T, S>(mapper, mappingContextFactoryBuilder.newFactory()); } protected void postMapperProcess(List<FieldMapper<T,S>> mappers) { } protected void postFieldProcess(List<FieldMapper<T,S>> mappers, PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>> pm) { } protected void preFieldProcess(List<FieldMapper<T,S>> mappers, PropertyMapping<T, ?, K, FieldMapperColumnDefinition<K>> pm) { } protected int getStartingIndex() { return 0; } protected abstract BiInstantiator<T, MappingContext<? super T>, S> getInstantiator(); protected abstract K newKey(String column, int i, FieldMapperColumnDefinition<K> columnDefinition); private FieldKey<?>[] getKeys() { return propertyMappingsBuilder.getKeys().toArray(new FieldKey[0]); } }