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

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

Go to download

Java library to map flat record - ResultSet, csv - to java object with minimum configuration and low footprint.

There is a newer version: 1.10.3
Show newest version
package org.sfm.csv;

import org.sfm.csv.impl.*;
import org.sfm.map.*;
import org.sfm.map.impl.*;
import org.sfm.reflect.*;
import org.sfm.reflect.meta.*;
import org.sfm.tuples.Tuple3;
import org.sfm.utils.ErrorHelper;
import org.sfm.utils.ForEachCallBack;

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 int CSV_MAX_METHOD_SIZE = 128;
    public static final int NO_ASM_CSV_HANDLER_THRESHOLD = 4096; // see https://github.com/arnaudroger/SimpleFlatMapper/issues/152
    private final CellValueReaderFactory cellValueReaderFactory;
	private FieldMapperErrorHandler fieldMapperErrorHandler = new RethrowFieldMapperErrorHandler();

	private final MapperBuilderErrorHandler mapperBuilderErrorHandler;
	private RowHandlerErrorHandler rowHandlerErrorHandler = new RethrowRowHandlerErrorHandler();
	private final PropertyNameMatcherFactory propertyNameMatcherFactory;
	private final Type target;
	private final ReflectionService reflectionService;
	private final ColumnDefinitionProvider columnDefinitions;

	private final PropertyMappingsBuilder propertyMappingsBuilder;

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


    private final boolean failOnAsm;
    private final int asmMapperNbFieldsLimit;
	private final int maxMethodSize;

    public CsvMapperBuilder(final Type target) {
		this(target, ReflectionService.newInstance());
	}
	
	@SuppressWarnings("unchecked")
	public CsvMapperBuilder(final Type target, ReflectionService reflectionService) {
		this(target, (ClassMeta)reflectionService.getRootClassMeta(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, new RethrowMapperBuilderErrorHandler(),
                columnDefinitionProvider, new DefaultPropertyNameMatcherFactory(),
                new CellValueReaderFactoryImpl(), 0, false, NO_ASM_CSV_HANDLER_THRESHOLD, CSV_MAX_METHOD_SIZE);
	}

	public CsvMapperBuilder(final Type target, final ClassMeta classMeta,
                            MapperBuilderErrorHandler mapperBuilderErrorHandler,
                            ColumnDefinitionProvider columnDefinitions,
                            PropertyNameMatcherFactory propertyNameMatcherFactory,
                            CellValueReaderFactory cellValueReaderFactory,
                            int minDelayedSetter,
                            boolean failOnAsm,
                            int asmMapperNbFieldsLimit, int maxMethodSize) throws MapperBuildingException {
		this.target = target;
		this.mapperBuilderErrorHandler = mapperBuilderErrorHandler;
        this.minDelayedSetter = minDelayedSetter;
        this.reflectionService = classMeta.getReflectionService();
		this.propertyMappingsBuilder = new PropertyMappingsBuilder(classMeta, propertyNameMatcherFactory, this.mapperBuilderErrorHandler);
		this.propertyNameMatcherFactory = propertyNameMatcherFactory;
		this.columnDefinitions = columnDefinitions;
		this.cellValueReaderFactory = cellValueReaderFactory;
        this.failOnAsm = failOnAsm;
        this.asmMapperNbFieldsLimit = asmMapperNbFieldsLimit;
		this.maxMethodSize = maxMethodSize;
	}

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

	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 String columnKey, int columnIndex) {
		return addMapping(new CsvColumnKey(columnKey, columnIndex), CsvColumnDefinition.IDENTITY);
	}
	
	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;
	}


	private 

void addMapping(PropertyMeta propertyMeta, final CsvColumnKey key, final CsvColumnDefinition columnDefinition) { propertyMappingsBuilder.addProperty(key, columnDefinition, propertyMeta); } private CsvColumnDefinition getColumnDefinition(CsvColumnKey key) { return CsvColumnDefinition.compose(CsvColumnDefinition.dateFormatDefinition(defaultDateFormat), columnDefinitions.getColumnDefinition(key)); } public void setDefaultDateFormat(String defaultDateFormat) { this.defaultDateFormat = defaultDateFormat; } public final CsvMapper mapper() { ParsingContextFactoryBuilder parsingContextFactoryBuilder = new ParsingContextFactoryBuilder(propertyMappingsBuilder.size()); Tuple3, ?>>, Integer, Boolean> constructorParams = buildConstructorParametersDelayedCellSetter(); final Instantiator, T> instantiator = getInstantiator(constructorParams.first()); final CsvColumnKey[] keys = getKeys(); // will build the context factory builder final CellSetter[] setters = getSetters(parsingContextFactoryBuilder, constructorParams.second()); final DelayedCellSetterFactory[] delayedCellSetterFactories = buildDelayedSetters(parsingContextFactoryBuilder, constructorParams.second(), constructorParams.third()); // needs to happen last final CsvMapperCellHandlerFactory csvMapperCellHandlerFactory = newCsvMapperCellHandlerFactory(parsingContextFactoryBuilder, instantiator, keys, delayedCellSetterFactories, setters); return new CsvMapperImpl(csvMapperCellHandlerFactory, delayedCellSetterFactories, setters, getJoinKeys(), rowHandlerErrorHandler); } 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() .createCsvMapperCellHandler(target, delayedCellSetterFactories, setters, instantiator, keys, parsingContextFactory, fieldMapperErrorHandler, maxMethodSize); } catch (Exception e) { if (failOnAsm || true) { return ErrorHelper.rethrow(e); } else { return new CsvMapperCellHandlerFactory(instantiator, keys, parsingContextFactory, fieldMapperErrorHandler); } } } else { return new CsvMapperCellHandlerFactory(instantiator, keys, parsingContextFactory, fieldMapperErrorHandler); } } private boolean isEligibleForAsmHandler() { return reflectionService.getAsmFactory() != null && this.propertyMappingsBuilder.size() < asmMapperNbFieldsLimit; } private CsvColumnKey[] getKeys() { return propertyMappingsBuilder.getKeys().toArray(new CsvColumnKey[propertyMappingsBuilder.size()]); } 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 instantiatorFactory.getInstantiator(new TypeReference>(){}.getType(), target, propertyMappingsBuilder, params, new GetterFactory, CsvColumnKey>() { final CellSetterFactory cellSetterFactory = new CellSetterFactory(cellValueReaderFactory); @Override public

Getter, P> newGetter(Type target, CsvColumnKey key) { return cellSetterFactory.newDelayedGetter(key, target); } }); } catch(Exception e) { return ErrorHelper.rethrow(e); } } private Tuple3, ?>>, Integer, Boolean> buildConstructorParametersDelayedCellSetter() { final BuildConstructorInjections buildConstructorInjections = new BuildConstructorInjections(); propertyMappingsBuilder.forEachProperties(buildConstructorInjections); return new Tuple3, ?>>, Integer, Boolean>(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); @Override public void handle(PropertyMapping propMapping) { if (propMapping != null) { PropertyMeta prop = propMapping.getPropertyMeta(); CsvColumnKey key = propMapping.getColumnKey(); if (prop != null) { if (prop.isConstructorProperty() || prop instanceof DirectClassMeta.DirectPropertyMeta) { delayedSetters[propMapping.getColumnKey().getIndex()] = cellSetterFactory.getDelayedCellSetter(prop.getType(), key.getIndex(), propMapping.getColumnDefinition(), parsingContextFactoryBuilder); } else 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.getType(), propOwner.getClassMeta(), mapperBuilderErrorHandler, columnDefinitions, propertyNameMatcherFactory, cellValueReaderFactory, newMinDelayedSetter, failOnAsm, asmMapperNbFieldsLimit, maxMethodSize); 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.getType(), propOwner.getClassMeta(), mapperBuilderErrorHandler, columnDefinitions, propertyNameMatcherFactory, cellValueReaderFactory, minDelayedSetter, failOnAsm, asmMapperNbFieldsLimit, maxMethodSize); 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); @Override public void handle(PropertyMapping propMapping) { if (propMapping == null) { return; } PropertyMeta prop = propMapping.getPropertyMeta(); if (prop == null || prop instanceof DirectClassMeta.DirectPropertyMeta) { 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, (Setter)prop.getOwnerProperty().getSetter(), indexOfMapper); } }, delayedSetterEnd); return setters; } public final CsvMapperBuilder fieldMapperErrorHandler(final FieldMapperErrorHandler errorHandler) { fieldMapperErrorHandler = errorHandler; return this; } public final CsvMapperBuilder rowHandlerErrorHandler(RowHandlerErrorHandler rowHandlerErrorHandler) { this.rowHandlerErrorHandler = rowHandlerErrorHandler; return this; } 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); 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 = cellSetterFactory.newDelayedGetter(key, meta.getType()); constructorInjections.put(((ConstructorPropertyMeta) meta).getConstructorParameter(), delayedGetter); } else if (meta instanceof DirectClassMeta.DirectPropertyMeta) { 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.getType()); constructorInjections.put(constPropMeta.getConstructorParameter(), delayedGetter); delayedSetterEnd = Math.max(delayedSetterEnd, key.getIndex() + 1); } else if (propMapping.getColumnDefinition().isKey() && propMapping.getColumnDefinition().keyAppliesTo().test(propMapping.getPropertyMeta())) { delayedSetterEnd = Math.max(delayedSetterEnd, key.getIndex() + 1); } } else if (propMapping.getColumnDefinition().isKey() && propMapping.getColumnDefinition().keyAppliesTo().test(propMapping.getPropertyMeta())) { delayedSetterEnd = Math.max(delayedSetterEnd, key.getIndex() + 1); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy