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

org.simpleflatmapper.csv.CsvMapperBuilder Maven / Gradle / Ivy

package org.simpleflatmapper.csv;

import org.simpleflatmapper.map.MapperBuildingException;
import org.simpleflatmapper.map.MapperConfig;
import org.simpleflatmapper.map.MappingContext;
import org.simpleflatmapper.map.mapper.ColumnDefinitionProvider;
import org.simpleflatmapper.map.mapper.MapperInstantiatorFactory;
import org.simpleflatmapper.reflect.BiInstantiator;
import org.simpleflatmapper.reflect.Getter;
import org.simpleflatmapper.reflect.Instantiator;
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.GetterFactory;
import org.simpleflatmapper.map.mapper.PropertyMapping;
import org.simpleflatmapper.map.mapper.PropertyMappingsBuilder;
import org.simpleflatmapper.map.mapper.PropertyWithSetterOrConstructor;
import org.simpleflatmapper.reflect.meta.ClassMeta;
import org.simpleflatmapper.reflect.meta.ConstructorPropertyMeta;
import org.simpleflatmapper.reflect.meta.PropertyMeta;
import org.simpleflatmapper.reflect.meta.SelfPropertyMeta;
import org.simpleflatmapper.reflect.meta.SubPropertyMeta;
import org.simpleflatmapper.util.CheckedConsumer;
import org.simpleflatmapper.util.Consumer;
import org.simpleflatmapper.util.TypeReference;
import org.simpleflatmapper.util.UnaryFactory;
import org.simpleflatmapper.csv.property.CustomReaderProperty;
import org.simpleflatmapper.csv.impl.*;
import org.simpleflatmapper.csv.impl.asm.CsvAsmFactory;
import org.simpleflatmapper.csv.mapper.CellSetter;
import org.simpleflatmapper.csv.mapper.CsvMapperCellHandler;
import org.simpleflatmapper.csv.mapper.CsvMapperCellHandlerFactory;
import org.simpleflatmapper.csv.mapper.DelayedCellSetterFactory;
import org.simpleflatmapper.map.property.DefaultDateFormatProperty;
import org.simpleflatmapper.map.property.DefaultValueProperty;
import org.simpleflatmapper.util.BiConsumer;
import org.simpleflatmapper.util.ErrorHelper;
import org.simpleflatmapper.util.ForEachCallBack;
import org.simpleflatmapper.util.Named;
import org.simpleflatmapper.util.Predicate;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CsvMapperBuilder {

	public static final CustomReaderProperty NONE_READER_PROPERTY = new CustomReaderProperty(new CellValueReader() {
		@Override
		public Object read(char[] chars, int offset, int length, ParsingContext parsingContext) {
			throw new UnsupportedOperationException("Default value should not try to read");
		}
	});
	private final Type target;
	private final ReflectionService reflectionService;
	private final MapperConfig mapperConfig;

    private final int minDelayedSetter;

	private final CellValueReaderFactory cellValueReaderFactory;
	private final PropertyMappingsBuilder propertyMappingsBuilder;

	private String defaultDateFormat = "yyyy-MM-dd HH:mm:ss";

    public CsvMapperBuilder(final Type target) {
		this(target, ReflectionService.newInstance());
	}
	
	@SuppressWarnings("unchecked")
	public CsvMapperBuilder(final Type target, ReflectionService reflectionService) {
		this(target, (ClassMeta)reflectionService.getClassMeta(target));
	}

	public CsvMapperBuilder(final Type target, final ClassMeta classMeta) {
		this(target, classMeta, new IdentityCsvColumnDefinitionProvider());
	}

	public CsvMapperBuilder(final Type target, final ClassMeta classMeta, ColumnDefinitionProvider columnDefinitionProvider) {
		this(target, classMeta, 0,
                new CellValueReaderFactoryImpl(), MapperConfig.config(columnDefinitionProvider));
	}

	public CsvMapperBuilder(final Type target,
							final ClassMeta classMeta,
							int minDelayedSetter,
							CellValueReaderFactory cellValueReaderFactory,
							MapperConfig mapperConfig
	) throws MapperBuildingException {
		this.target = target;
        this.minDelayedSetter = minDelayedSetter;
        this.reflectionService = classMeta.getReflectionService();
		this.propertyMappingsBuilder =
				PropertyMappingsBuilder.of(classMeta, mapperConfig, PropertyWithSetterOrConstructor.INSTANCE);
		this.cellValueReaderFactory = cellValueReaderFactory;
		this.mapperConfig = mapperConfig;
	}

	public final CsvMapperBuilder addMapping(final String columnKey) {
		return addMapping(columnKey, propertyMappingsBuilder.size());
	}

	public final CsvMapperBuilder addMapping(final String columnKey, int columnIndex) {
		return addMapping(new CsvColumnKey(columnKey, columnIndex), CsvColumnDefinition.identity());
	}

	public final CsvMapperBuilder addMapping(final String columnKey, final CsvColumnDefinition columnDefinition) {
		return addMapping(columnKey, propertyMappingsBuilder.size(), columnDefinition);
	}

	public final CsvMapperBuilder addMapping(final String columnKey, int columnIndex, CsvColumnDefinition columnDefinition) {
		return addMapping(new CsvColumnKey(columnKey, columnIndex), columnDefinition);
	}

	public final CsvMapperBuilder addMapping(final CsvColumnKey key, final CsvColumnDefinition columnDefinition) {
		final CsvColumnDefinition composedDefinition = CsvColumnDefinition.compose(getColumnDefinition(key), columnDefinition);
		final CsvColumnKey mappedColumnKey = composedDefinition.rename(key);

		propertyMappingsBuilder.addProperty(mappedColumnKey, composedDefinition);

		return this;
	}

	public final CsvMapperBuilder addMapping(final String columnKey, final Object... properties) {
		return addMapping(columnKey, propertyMappingsBuilder.size(), properties);
	}

	public final CsvMapperBuilder addMapping(final String columnKey, int columnIndex, final Object... properties) {
		return addMapping(new CsvColumnKey(columnKey, columnIndex), properties);
	}

	public final CsvMapperBuilder addMapping(final CsvColumnKey key, final Object... properties) {
		return addMapping(key, CsvColumnDefinition.of(properties));
	}

	private 

void addMapping(PropertyMeta propertyMeta, final CsvColumnKey key, final CsvColumnDefinition columnDefinition) { propertyMappingsBuilder.addProperty(key, columnDefinition, propertyMeta); } private CsvColumnDefinition getColumnDefinition(CsvColumnKey key) { CsvColumnDefinition columnDefinition = mapperConfig.columnDefinitions().getColumnDefinition(key); return CsvColumnDefinition.compose(CsvColumnDefinition.of(new DefaultDateFormatProperty(defaultDateFormat)), columnDefinition); } public void setDefaultDateFormat(String defaultDateFormat) { this.defaultDateFormat = defaultDateFormat; } public final CsvMapper mapper() { mapperConfig .columnDefinitions() .forEach( DefaultValueProperty.class, new BiConsumer, DefaultValueProperty>() { @Override public void accept(Predicate predicate, DefaultValueProperty columnProperty) { if (propertyMappingsBuilder.hasKey(predicate)){ return; } if (predicate instanceof Named) { String name = ((Named)predicate).getName(); final CsvColumnDefinition columnDefinition = CsvColumnDefinition.identity().add(columnProperty, NONE_READER_PROPERTY); CsvColumnKey key = new CsvColumnKey(name, propertyMappingsBuilder.size()); propertyMappingsBuilder.addPropertyIfPresent(key, columnDefinition); } } }); ParsingContextFactoryBuilder parsingContextFactoryBuilder = new ParsingContextFactoryBuilder(propertyMappingsBuilder.maxIndex() + 1); ConstructorParametersDelayedCellSetter constructorParams = buildConstructorParametersDelayedCellSetter(); @SuppressWarnings("unchecked") final Instantiator, T> instantiator = getInstantiator(constructorParams.parameterGetterMap); final CsvColumnKey[] keys = getKeys(); // will build the context factory builder final CellSetter[] setters = getSetters(parsingContextFactoryBuilder, constructorParams.index); final DelayedCellSetterFactory[] delayedCellSetterFactories = buildDelayedSetters(parsingContextFactoryBuilder, constructorParams.index, constructorParams.hasKeys); // needs to happen last final CsvMapperCellHandlerFactory csvMapperCellHandlerFactory = newCsvMapperCellHandlerFactory(parsingContextFactoryBuilder, instantiator, keys, delayedCellSetterFactories, setters); return new CsvMapperImpl(csvMapperCellHandlerFactory, delayedCellSetterFactories, setters, getJoinKeys(), mapperConfig.consumerErrorHandler()); } private CsvMapperCellHandlerFactory newCsvMapperCellHandlerFactory(ParsingContextFactoryBuilder parsingContextFactoryBuilder, Instantiator, T> instantiator, CsvColumnKey[] keys, DelayedCellSetterFactory[] delayedCellSetterFactories, CellSetter[] setters ) { final ParsingContextFactory parsingContextFactory = parsingContextFactoryBuilder.newFactory(); if (isEligibleForAsmHandler()) { try { return reflectionService.getAsmFactory() .registerOrCreate(CsvAsmFactory.class, new UnaryFactory() { @Override public CsvAsmFactory newInstance(AsmFactory asmFactory) { return new CsvAsmFactory(asmFactory); } }).createCsvMapperCellHandler(target, delayedCellSetterFactories, setters, instantiator, keys, parsingContextFactory, mapperConfig.fieldMapperErrorHandler(), mapperConfig.maxMethodSize()); } catch (Exception e) { if (mapperConfig.failOnAsm()) { return ErrorHelper.rethrow(e); } else { return new CsvMapperCellHandlerFactory(instantiator, keys, parsingContextFactory, mapperConfig.fieldMapperErrorHandler()); } } } else { return new CsvMapperCellHandlerFactory(instantiator, keys, parsingContextFactory, mapperConfig.fieldMapperErrorHandler()); } } private boolean isEligibleForAsmHandler() { return reflectionService.hasAsmFactory() && this.propertyMappingsBuilder.size() < mapperConfig.asmMapperNbFieldsLimit(); } private CsvColumnKey[] getKeys() { return propertyMappingsBuilder.getKeys().toArray(new CsvColumnKey[0]); } private CsvColumnKey[] getJoinKeys() { final List keys = new ArrayList(); propertyMappingsBuilder.forEachProperties(new ForEachCallBack>() { @Override public void handle(PropertyMapping pm) { if (pm.getColumnDefinition().isKey() && pm.getColumnDefinition().keyAppliesTo().test(pm.getPropertyMeta())) { keys.add(pm.getColumnKey()); } } }); return keys.toArray(new CsvColumnKey[0]); } private Instantiator, T> getInstantiator(Map, ?>> params) throws MapperBuildingException { InstantiatorFactory instantiatorFactory = reflectionService.getInstantiatorFactory(); try { return new MapperInstantiatorFactory(instantiatorFactory) .getInstantiator( new TypeReference>(){}.getType(), target, propertyMappingsBuilder, params, new GetterFactory, CsvColumnKey>() { @Override public

Getter, P> newGetter(Type target, CsvColumnKey key, Object... properties) { return newDelayedGetter(target, key, properties); } }); } catch(Exception e) { return ErrorHelper.rethrow(e); } } private ConstructorParametersDelayedCellSetter buildConstructorParametersDelayedCellSetter() { final BuildConstructorInjections buildConstructorInjections = new BuildConstructorInjections(); propertyMappingsBuilder.forEachProperties(buildConstructorInjections); return new ConstructorParametersDelayedCellSetter( buildConstructorInjections.constructorInjections, buildConstructorInjections.delayedSetterEnd, buildConstructorInjections.hasKeys); } @SuppressWarnings({ "unchecked" }) private DelayedCellSetterFactory[] buildDelayedSetters(final ParsingContextFactoryBuilder parsingContextFactoryBuilder, int delayedSetterEnd, boolean hasKeys) { final Map> delegateMapperBuilders = new HashMap>(); final Map propertyToMapperIndex = new HashMap(); final DelayedCellSetterFactory[] delayedSetters = new DelayedCellSetterFactory[delayedSetterEnd]; final int newMinDelayedSetter = minDelayedSetter != 0 ? minDelayedSetter : hasKeys ? delayedSetterEnd : 0; propertyMappingsBuilder.forEachProperties(new ForEachCallBack>() { final CellSetterFactory cellSetterFactory = new CellSetterFactory(cellValueReaderFactory, mapperConfig.mapperBuilderErrorHandler()); @Override public void handle(PropertyMapping propMapping) { if (propMapping != null) { PropertyMeta prop = propMapping.getPropertyMeta(); CsvColumnKey key = propMapping.getColumnKey(); if (prop != null) { if (prop.isSubProperty()) { addSubProperty(delegateMapperBuilders, prop, key, propMapping.getColumnDefinition()); }else { delayedSetters[propMapping.getColumnKey().getIndex()] = cellSetterFactory.getDelayedCellSetter(prop, key.getIndex(), propMapping.getColumnDefinition(), parsingContextFactoryBuilder); } } } } private void addSubProperty( Map> delegateMapperBuilders, PropertyMeta prop, CsvColumnKey key, CsvColumnDefinition columnDefinition) { SubPropertyMeta subPropertyMeta = (SubPropertyMeta)prop; final PropertyMeta propOwner = subPropertyMeta.getOwnerProperty(); CsvMapperBuilder delegateMapperBuilder = (CsvMapperBuilder) delegateMapperBuilders .get(propOwner.getName()); if (delegateMapperBuilder == null) { delegateMapperBuilder = new CsvMapperBuilder(propOwner.getPropertyType(), propOwner.getPropertyClassMeta(), newMinDelayedSetter, cellValueReaderFactory, mapperConfig); delegateMapperBuilders.put(propOwner.getName(), delegateMapperBuilder); } Integer currentIndex = propertyToMapperIndex.get(propOwner.getName()); if (currentIndex == null || currentIndex < key.getIndex()) { propertyToMapperIndex.put(propOwner.getName(), key.getIndex()); } delegateMapperBuilder.addMapping(subPropertyMeta.getSubProperty(), key, columnDefinition); } }, 0, delayedSetterEnd); final Map> mappers = new HashMap>(); propertyMappingsBuilder.forEachProperties(new ForEachCallBack>() { @Override public void handle(PropertyMapping propMapping) { if (propMapping == null) return; PropertyMeta prop = propMapping.getPropertyMeta(); if (prop.isSubProperty()) { addSubPropertyDelayedSetter(delegateMapperBuilders, delayedSetters, propMapping.getColumnKey().getIndex(), prop); } } private void addSubPropertyDelayedSetter( Map> delegateMapperBuilders, DelayedCellSetterFactory[] delayedSetters, int setterIndex, PropertyMeta prop) { PropertyMeta subProp = ((SubPropertyMeta) prop).getOwnerProperty(); final String propName = subProp.getName(); CsvMapper mapper = (CsvMapper) mappers.get(propName); if (mapper == null) { CsvMapperBuilder delegateMapperBuilder = (CsvMapperBuilder) delegateMapperBuilders.get(propName); mapper = delegateMapperBuilder.mapper(); mappers.put(propName, mapper); } int indexOfMapper = propertyToMapperIndex.get(propName); Setter setter = null; if (!subProp.isConstructorProperty()) { setter = subProp.getSetter(); } delayedSetters[setterIndex] = new DelegateMarkerDelayedCellSetterFactory(mapper, setter, setterIndex, indexOfMapper); } }, 0, delayedSetterEnd); return delayedSetters; } @SuppressWarnings({ "unchecked", "rawtypes" }) private CellSetter[] getSetters(final ParsingContextFactoryBuilder parsingContextFactoryBuilder, final int delayedSetterEnd) { final Map> delegateMapperBuilders = new HashMap>(); final Map propertyToMapperIndex = new HashMap(); // calculate maxIndex int maxIndex = propertyMappingsBuilder.forEachProperties(new ForEachCallBack>() { int maxIndex = delayedSetterEnd; @Override public void handle(PropertyMapping propMapping) { if (propMapping != null) { maxIndex = Math.max(propMapping.getColumnKey().getIndex(), maxIndex); PropertyMeta prop = propMapping.getPropertyMeta(); if (prop != null) { CsvColumnKey key = propMapping.getColumnKey(); if (prop.isConstructorProperty()) { throw new IllegalStateException("Unexpected ConstructorPropertyMeta at " + key.getIndex()); } else if (prop.isSubProperty()) { final PropertyMeta propOwner = ((SubPropertyMeta)prop).getOwnerProperty(); CsvMapperBuilder delegateMapperBuilder = delegateMapperBuilders .get(propOwner.getName()); if (delegateMapperBuilder == null) { delegateMapperBuilder = new CsvMapperBuilder(propOwner.getPropertyType(), propOwner.getPropertyClassMeta(), minDelayedSetter, cellValueReaderFactory, mapperConfig); delegateMapperBuilders.put(propOwner.getName(), delegateMapperBuilder); } Integer currentIndex = propertyToMapperIndex.get(propOwner.getName()); if (currentIndex == null || currentIndex < key.getIndex()) { propertyToMapperIndex.put(propOwner.getName(), key.getIndex()); } delegateMapperBuilder.addMapping(((SubPropertyMeta) prop).getSubProperty(), key, propMapping.getColumnDefinition()); } } } } }, delayedSetterEnd).maxIndex; // builder se setters final CellSetter[] setters = new CellSetter[maxIndex + 1 - delayedSetterEnd]; propertyMappingsBuilder.forEachProperties(new ForEachCallBack>() { final Map> mappers = new HashMap>(); final CellSetterFactory cellSetterFactory = new CellSetterFactory(cellValueReaderFactory, mapperConfig.mapperBuilderErrorHandler()); @Override public void handle(PropertyMapping propMapping) { if (propMapping == null) { return; } PropertyMeta prop = propMapping.getPropertyMeta(); if (prop == null || prop instanceof SelfPropertyMeta) { return; } if (prop instanceof SubPropertyMeta) { DelegateMarkerSetter delegateMarkerSetter = getDelegateMarkerSetter((SubPropertyMeta) prop); setters[propMapping.getColumnKey().getIndex()- delayedSetterEnd] = delegateMarkerSetter; } else { setters[propMapping.getColumnKey().getIndex()- delayedSetterEnd] = cellSetterFactory.getCellSetter(prop, propMapping.getColumnKey().getIndex(), propMapping.getColumnDefinition(), parsingContextFactoryBuilder); } } private DelegateMarkerSetter getDelegateMarkerSetter(SubPropertyMeta prop) { final String propName = prop.getOwnerProperty().getName(); CsvMapperImpl mapper = (CsvMapperImpl) mappers.get(propName); if (mapper == null) { CsvMapperBuilder delegateMapperBuilder = (CsvMapperBuilder) delegateMapperBuilders.get(propName); mapper = (CsvMapperImpl) delegateMapperBuilder.mapper(); mappers.put(propName, mapper); } int indexOfMapper = propertyToMapperIndex.get(propName); return new DelegateMarkerSetter(mapper, prop.getOwnerProperty().getSetter(), indexOfMapper); } }, delayedSetterEnd); return setters; } public void addDefaultHeaders() { addDefaultHeaders(propertyMappingsBuilder.getClassMeta(), ""); } private

void addDefaultHeaders(final ClassMeta

classMeta, final String prefix) { classMeta.forEachProperties(new Consumer>() { @Override public void accept(PropertyMeta propertyMeta) { String currentName = prefix + propertyMeta.getPath(); if (cellValueReaderFactory.getReader(propertyMeta.getPropertyType(), 0, CsvColumnDefinition.identity(), new ParsingContextFactoryBuilder(1)) == null) { addDefaultHeaders(propertyMeta.getPropertyClassMeta(), currentName + "_"); } else { addMapping(currentName); } } }); } private class BuildConstructorInjections implements ForEachCallBack> { final CellSetterFactory cellSetterFactory; private final Map, ?>> constructorInjections; int delayedSetterEnd; boolean hasKeys; public BuildConstructorInjections() { this.constructorInjections = new HashMap, ?>>(); cellSetterFactory = new CellSetterFactory(cellValueReaderFactory, mapperConfig.mapperBuilderErrorHandler()); delayedSetterEnd = minDelayedSetter; } @SuppressWarnings("unchecked") @Override public void handle(PropertyMapping propMapping) { if (propMapping == null) return; PropertyMeta meta = propMapping.getPropertyMeta(); hasKeys = hasKeys || propMapping.getColumnDefinition().isKey(); if (meta == null) return; final CsvColumnKey key = propMapping.getColumnKey(); if (meta.isConstructorProperty()) { delayedSetterEnd = Math.max(delayedSetterEnd, key.getIndex() + 1); Getter, ?> delayedGetter = newDelayedGetter(meta.getPropertyType(), key, propMapping.getColumnDefinition().properties()); constructorInjections.put(((ConstructorPropertyMeta) meta).getParameter(), delayedGetter); } else if (meta instanceof SelfPropertyMeta) { delayedSetterEnd = Math.max(delayedSetterEnd, key.getIndex() + 1); } else if (meta.isSubProperty()) { SubPropertyMeta subMeta = (SubPropertyMeta) meta; if (subMeta.getOwnerProperty().isConstructorProperty()) { ConstructorPropertyMeta constPropMeta = (ConstructorPropertyMeta) subMeta.getOwnerProperty(); Getter, ?> delayedGetter = cellSetterFactory.newDelayedGetter(key, constPropMeta.getPropertyType()); constructorInjections.put(constPropMeta.getParameter(), delayedGetter); delayedSetterEnd = Math.max(delayedSetterEnd, key.getIndex() + 1); } else if ( (propMapping.getColumnDefinition().isKey() && propMapping.getColumnDefinition().keyAppliesTo().test(propMapping.getPropertyMeta())) || subMeta.getSubProperty() instanceof SelfPropertyMeta) { delayedSetterEnd = Math.max(delayedSetterEnd, key.getIndex() + 1); } } else if ((propMapping.getColumnDefinition().isKey() && propMapping.getColumnDefinition().keyAppliesTo().test(propMapping.getPropertyMeta())) || propMapping.getColumnDefinition().has(DefaultValueProperty.class)) { delayedSetterEnd = Math.max(delayedSetterEnd, key.getIndex() + 1); } } } private

Getter, P> newDelayedGetter(Type propertyType, CsvColumnKey key, Object... properties) { final CellSetterFactory cellSetterFactory = new CellSetterFactory(cellValueReaderFactory, mapperConfig.mapperBuilderErrorHandler()); for(Object prop :properties) { if (prop instanceof DefaultValueProperty) { return new DelayedGetter(key.getIndex()); } } return cellSetterFactory.newDelayedGetter(key, propertyType); } // Map, ?>>, Integer, Boolean private static class ConstructorParametersDelayedCellSetter { private final Map, ?>> parameterGetterMap; private final int index; private final boolean hasKeys; private ConstructorParametersDelayedCellSetter(Map, ?>> parameterGetterMap, int index, boolean hasKeys) { this.parameterGetterMap = parameterGetterMap; this.index = index; this.hasKeys = hasKeys; } } }