org.sfm.csv.CsvMapperBuilder Maven / Gradle / Ivy
Show all versions of simpleFlatMapper Show documentation
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.utils.ForEachCallBack;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
public class CsvMapperBuilder {
private final Class> SOURCE_UNTYPE = DelayedCellSetter[].class;
@SuppressWarnings("unchecked")
private final Class[]> SOURCE = (Class[]>) SOURCE_UNTYPE;
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 int syncSetterStart;
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.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());
}
public CsvMapperBuilder(final Type target, final ClassMeta classMeta,
MapperBuilderErrorHandler mapperBuilderErrorHandler, ColumnDefinitionProvider columnDefinitions, PropertyNameMatcherFactory propertyNameMatcherFactory, CellValueReaderFactory cellValueReaderFactory) throws MapperBuildingException {
this.target = target;
this.mapperBuilderErrorHandler = mapperBuilderErrorHandler;
this.reflectionService = classMeta.getReflectionService();
this.propertyMappingsBuilder = new PropertyMappingsBuilder(classMeta, propertyNameMatcherFactory, this.mapperBuilderErrorHandler);
this.propertyNameMatcherFactory = propertyNameMatcherFactory;
this.columnDefinitions = columnDefinitions;
this.cellValueReaderFactory = cellValueReaderFactory;
}
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());
return new CsvMapperImpl(getInstantiator(),
buildDelayedSetters(parsingContextFactoryBuilder),
getSetters(parsingContextFactoryBuilder), getKeys(), parsingContextFactoryBuilder.newFactory(), fieldMapperErrorHandler, rowHandlerErrorHandler);
}
private CsvColumnKey[] getKeys() {
return propertyMappingsBuilder.getKeys().toArray(new CsvColumnKey[propertyMappingsBuilder.size()]);
}
private Instantiator[], T> getInstantiator() throws MapperBuildingException {
InstantiatorFactory instantiatorFactory = reflectionService.getInstantiatorFactory();
try {
return instantiatorFactory.getInstantiator(SOURCE, target, propertyMappingsBuilder, buildConstructorParametersDelayedCellSetter(), 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) {
throw new MapperBuildingException(e.getMessage(), e);
}
}
private Map[], ?>> buildConstructorParametersDelayedCellSetter() {
final Map[], ?>> constructorInjections = new HashMap[], ?>>();
propertyMappingsBuilder.forEachProperties(new ForEachCallBack>() {
final CellSetterFactory cellSetterFactory = new CellSetterFactory(cellValueReaderFactory);
@SuppressWarnings("unchecked")
@Override
public void handle(PropertyMapping propMapping) {
if(propMapping == null) return;
PropertyMeta meta = propMapping.getPropertyMeta();
if (meta == null) return;
final CsvColumnKey key = propMapping.getColumnKey();
if (meta.isConstructorProperty()) {
syncSetterStart = Math.max(syncSetterStart, key.getIndex() + 1);
Getter[], ?> delayedGetter = cellSetterFactory.newDelayedGetter(key, meta.getType());
constructorInjections.put(((ConstructorPropertyMeta) meta).getConstructorParameter(), delayedGetter);
} else if (meta instanceof DirectClassMeta.DirectPropertyMeta) {
syncSetterStart = Math.max(syncSetterStart, key.getIndex() + 1);
} else if (meta.isSubProperty()) {
SubPropertyMeta subMeta = (SubPropertyMeta) meta;
if (subMeta.getOwnerProperty().isConstructorProperty()) {
ConstructorPropertyMeta, ?> constPropMeta = (ConstructorPropertyMeta, ?>) subMeta.getOwnerProperty();
if (!constructorInjections.containsKey(constPropMeta.getConstructorParameter())) {
Getter[], ?> delayedGetter = cellSetterFactory.newDelayedGetter(key, constPropMeta.getType());
constructorInjections.put(constPropMeta.getConstructorParameter(), delayedGetter);
}
syncSetterStart = Math.max(syncSetterStart, key.getIndex() + 1);
}
}
}
});
return constructorInjections;
}
@SuppressWarnings({ "unchecked" })
private DelayedCellSetterFactory[] buildDelayedSetters(final ParsingContextFactoryBuilder parsingContextFactoryBuilder) {
final Map> delegateMapperBuilders = new HashMap>();
final DelayedCellSetterFactory[] delayedSetters = new DelayedCellSetterFactory[syncSetterStart];
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);
delegateMapperBuilders.put(propOwner.getName(), delegateMapperBuilder);
}
delegateMapperBuilder.addMapping(subPropertyMeta.getSubProperty(), key, columnDefinition);
}
}, 0, syncSetterStart);
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, mappers, propMapping.getColumnKey().getIndex(), prop);
}
}
private void addSubPropertyDelayedSetter(
Map> delegateMapperBuilders,
DelayedCellSetterFactory[] delayedSetters,
Map> mappers, 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);
}
if (subProp instanceof ConstructorPropertyMeta) {
delayedSetters[setterIndex] = new DelegateMarkerDelayedCellSetter(mapper);
} else {
delayedSetters[setterIndex] = new DelegateMarkerDelayedCellSetter(mapper, subProp.getSetter());
}
}
}, 0, syncSetterStart);
return delayedSetters;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private CellSetter[] getSetters(final ParsingContextFactoryBuilder parsingContextFactoryBuilder) {
final Map> delegateMapperBuilders = new HashMap>();
int maxIndex = propertyMappingsBuilder.forEachProperties(new ForEachCallBack>() {
int maxIndex = syncSetterStart;
@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);
delegateMapperBuilders.put(propOwner.getName(), delegateMapperBuilder);
}
delegateMapperBuilder.addMapping(((SubPropertyMeta) prop).getSubProperty(), key, propMapping.getColumnDefinition());
}
}
}
}
}, syncSetterStart).maxIndex;
final CellSetter[] setters = new CellSetter[maxIndex + 1 - syncSetterStart];
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) {
final String propName = ((SubPropertyMeta)prop).getOwnerProperty().getName();
CsvMapper> mapper = mappers.get(propName);
if (mapper == null) {
CsvMapperBuilder> delegateMapperBuilder = delegateMapperBuilders .get(propName);
mapper = delegateMapperBuilder.mapper();
mappers.put(propName, mapper);
}
setters[propMapping.getColumnKey().getIndex()- syncSetterStart] = new DelegateMarkerSetter(mapper, ((SubPropertyMeta) prop).getOwnerProperty().getSetter());
} else {
setters[propMapping.getColumnKey().getIndex()- syncSetterStart] = cellSetterFactory.getCellSetter(prop, propMapping.getColumnKey().getIndex(), propMapping.getColumnDefinition(), parsingContextFactoryBuilder);
}
}
}, syncSetterStart);
return setters;
}
public final CsvMapperBuilder fieldMapperErrorHandler(final FieldMapperErrorHandler errorHandler) {
fieldMapperErrorHandler = errorHandler;
return this;
}
public final CsvMapperBuilder rowHandlerErrorHandler(RowHandlerErrorHandler rowHandlerErrorHandler) {
this.rowHandlerErrorHandler = rowHandlerErrorHandler;
return this;
}
}