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

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

package org.sfm.csv;

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

import org.sfm.csv.primitive.BooleanDelayedGetter;
import org.sfm.csv.primitive.ByteDelayedGetter;
import org.sfm.csv.primitive.CharDelayedGetter;
import org.sfm.csv.primitive.DoubleDelayedGetter;
import org.sfm.csv.primitive.FloatDelayedGetter;
import org.sfm.csv.primitive.IntDelayedGetter;
import org.sfm.csv.primitive.LongDelayedGetter;
import org.sfm.csv.primitive.ShortDelayedGetter;
import org.sfm.map.FieldMapperErrorHandler;
import org.sfm.map.MapperBuilderErrorHandler;
import org.sfm.map.MapperBuildingException;
import org.sfm.map.RethrowFieldMapperErrorHandler;
import org.sfm.map.RethrowMapperBuilderErrorHandler;
import org.sfm.map.RethrowRowHandlerErrorHandler;
import org.sfm.map.RowHandlerErrorHandler;
import org.sfm.reflect.Getter;
import org.sfm.reflect.Instantiator;
import org.sfm.reflect.InstantiatorFactory;
import org.sfm.reflect.ReflectionService;
import org.sfm.reflect.TypeHelper;
import org.sfm.reflect.asm.ConstructorParameter;
import org.sfm.reflect.meta.ClassMeta;
import org.sfm.reflect.meta.ConstructorPropertyMeta;
import org.sfm.reflect.meta.PropertyFinder;
import org.sfm.reflect.meta.PropertyMeta;
import org.sfm.reflect.meta.SubPropertyMeta;

public class CsvMapperBuilder {

	@SuppressWarnings("rawtypes")
	private static final Class SOURCE = DelayedCellSetter[].class;
	
	private FieldMapperErrorHandler fieldMapperErrorHandler = new RethrowFieldMapperErrorHandler();
	private MapperBuilderErrorHandler mapperBuilderErrorHandler = new RethrowMapperBuilderErrorHandler();
	private RowHandlerErrorHandler rowHandlerErrorHandler = new RethrowRowHandlerErrorHandler();
	private final Type target;
	private final PropertyFinder propertyFinder;
	private final ReflectionService reflectionService;
	private final Map aliases;
	private final Map> customReaders;
	private final List columns = new ArrayList();

	
	private List> properties = new ArrayList>();
	private int syncSetterStart;

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

	public CsvMapperBuilder(final Type target) {
		this(target, new ReflectionService(),
				new HashMap(), 
				new HashMap>());
	}
	
	@SuppressWarnings("unchecked")
	public CsvMapperBuilder(final Type target, ReflectionService reflectionService,
			Map aliases, Map> customReaders) throws MapperBuildingException {
		this(target, (ClassMeta) reflectionService.getClassMeta(target), aliases, customReaders);
	}
	
	public CsvMapperBuilder(final Type target, final ClassMeta classMeta,
			Map aliases, Map> customReaders) throws MapperBuildingException {
		this.target = target;
		this.reflectionService = classMeta.getReflectionService();
		this.propertyFinder = classMeta.newPropertyFinder();
		this.aliases = aliases;
		this.customReaders = customReaders;
	}

	public final CsvMapperBuilder addMapping(final String columnKey) {
		return addMapping(columnKey, properties.size());
	}
	
	public final CsvMapperBuilder addMapping(final String columnKey, int columnIndex) {
		final String mappedColumnKey = columnToPropertyName(columnKey);
		
		while(properties.size() < columnIndex) {
			properties.add(null);
			columns.add(null);
		}
		columns.add(columnKey);
		
		final PropertyMeta prop = propertyFinder.findProperty(mappedColumnKey);
		
		if (prop != null) {
			properties.add(prop);
		} else {
			mapperBuilderErrorHandler.propertyNotFound(target, mappedColumnKey);
			properties.add(null);
		}
		
		return this;
	}
	
	private void addMapping(PropertyMeta subProperty, int i, String column) {
		while (i >= properties.size()) {
			properties.add(null);
			columns.add(null);
		}
		properties.set(i, subProperty);
		columns.set(i, column);
		
	}
	
	public void setDefaultDateFormat(String defaultDateFormat) {
		this.defaultDateFormat = defaultDateFormat;
	}

	public final CsvMapper mapper() {
		return new CsvMapperImpl(getInstantiator(), getDelayedSetters(), getSetters(), getParserContextFactory(), fieldMapperErrorHandler, rowHandlerErrorHandler);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private CellSetter[] getSetters() {
		Map> delegateMapperBuilders = new HashMap>();
		
		CellSetterFactory cellSetterFactory = new CellSetterFactory(customReaders);
		
		List> setters = new ArrayList>();

		for(int i = syncSetterStart; i < properties.size(); i++) {
			PropertyMeta prop = properties.get(i);
			if (prop != null) {
				if (prop instanceof ConstructorPropertyMeta) {
					throw new IllegalStateException("Unexpected ConstructorPropertyMeta at " + i);
				} else if (prop instanceof SubPropertyMeta) {
					final PropertyMeta powner = ((SubPropertyMeta)prop).getProperty();
					CsvMapperBuilder delegateMapperBuilder = delegateMapperBuilders .get(powner.getName());
					
					if (delegateMapperBuilder == null) {
						delegateMapperBuilder = new CsvMapperBuilder(powner.getType(), powner.getClassMeta(), aliases, customReaders);
						delegateMapperBuilders.put(powner.getName(), delegateMapperBuilder);
					}
					
					delegateMapperBuilder.addMapping(((SubPropertyMeta) prop).getSubProperty(), i, columns.get(i));
					
					setters.add(null);
					
				} else {
					setters.add(cellSetterFactory.getCellSetter(prop.getSetter(), i, columns.get(i)));
				}
			} else {
				setters.add(null);
			}
		}
		
		Map> mappers = new HashMap>();
		for(int i = syncSetterStart; i < properties.size(); i++) {
			PropertyMeta prop = properties.get(i);
			if (prop instanceof SubPropertyMeta) {
				final String propName = ((SubPropertyMeta)prop).getProperty().getName();
				
				CsvMapper mapper = mappers.get(propName);
				if (mapper == null) {
					CsvMapperBuilder delegateMapperBuilder = delegateMapperBuilders .get(propName);
					mapper = delegateMapperBuilder.mapper();
					mappers.put(propName, mapper);
				}
				setters.set(i , new DelegateMarkerSetter(mapper, ((SubPropertyMeta) prop).getProperty().getSetter()));
			} 
		}
		
		return setters.toArray(new CellSetter[0]);
	}
	
	@SuppressWarnings("rawtypes")
	private ParsingContextFactory getParserContextFactory() {
		ParsingContextFactory pcf = new ParsingContextFactory(properties.size());
		
		for(int i = 0; i < properties.size(); i++) {
			PropertyMeta prop = properties.get(i);
			if (prop != null) {
				Class target;
				if (prop instanceof SubPropertyMeta) {
					target = TypeHelper.toClass(((SubPropertyMeta) prop)
							.getFinalType());
				} else {
					target = TypeHelper.toClass(prop.getType());
				}
				if (Date.class.equals(target)) {
					pcf.setDateFormat(i, getDateFormat(i));
				}
			}
		}
		
		return pcf;
	}

	private String getDateFormat(int i) {
		return defaultDateFormat;
	}
	
	@SuppressWarnings({ "rawtypes" })
	private  Instantiator  getInstantiator() throws MapperBuildingException {

		int lastConstructorArg = -1;
		Map> constructorInjections = new HashMap>();
		for(int i = 0; i < properties.size(); i++) {
			PropertyMeta meta = properties.get(i);
			if (meta instanceof ConstructorPropertyMeta) {
				lastConstructorArg = i;
				constructorInjections.put(((ConstructorPropertyMeta) meta).getConstructorParameter(), newDelayedGetter(i, meta.getType()));
			} else if (meta instanceof SubPropertyMeta) {
				SubPropertyMeta subMeta = (SubPropertyMeta) meta;
				if  (subMeta.getProperty() instanceof ConstructorPropertyMeta) {
					ConstructorPropertyMeta constPropMeta = (ConstructorPropertyMeta) subMeta.getProperty();
					if (!constructorInjections.containsKey(constPropMeta.getConstructorParameter())) {
						constructorInjections.put(constPropMeta.getConstructorParameter(), newDelayedGetter(i, constPropMeta.getType()));
					}
					lastConstructorArg = i;
				}
			}
		}
		
		if (lastConstructorArg != -1) {
			syncSetterStart = lastConstructorArg + 1;
		}
		
		InstantiatorFactory instantiatorFactory = reflectionService.getInstantiatorFactory();
		try {
			if (!reflectionService.isAsmPresent()) {
				return ( Instantiator ) instantiatorFactory.getInstantiator(SOURCE, propertyFinder.getClassToInstantiate());
			} else {
				return instantiatorFactory.getInstantiator(SOURCE, propertyFinder.getEligibleConstructorDefinitions(), constructorInjections, customReaders.isEmpty());
			}
		} catch(Exception e) {
			throw new MapperBuildingException(e.getMessage(), e);
		}
	}
	
	@SuppressWarnings("rawtypes")
	private Getter newDelayedGetter(int i, Type type) {
		Class clazz = TypeHelper.toClass(type);
		if (clazz.isPrimitive() && ! customReaders.containsKey(columns.get(i))) {
			if (boolean.class.equals(clazz)) {
				return new BooleanDelayedGetter(i);
			} else if (byte.class.equals(clazz)) {
				return new ByteDelayedGetter(i);
			} else if (char.class.equals(clazz)) {
				return new CharDelayedGetter(i);
			} else if (short.class.equals(clazz)) {
				return new ShortDelayedGetter(i);
			} else if (int.class.equals(clazz)) {
				return new IntDelayedGetter(i);
			} else if (long.class.equals(clazz)) {
				return new LongDelayedGetter(i);
			} else if (float.class.equals(clazz)) {
				return new FloatDelayedGetter(i);
			} else if (double.class.equals(clazz)) {
				return new DoubleDelayedGetter(i);
			} else {
				throw new IllegalArgumentException("Unexpected primitive " + clazz);
			}
		} else {
			return new DelayedGetter(i);
		}
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private DelayedCellSetterFactory[] getDelayedSetters() {
		
		Map> delegateMapperBuilders = new HashMap>();
		List> delayedSetters = new ArrayList>(syncSetterStart);
		CellSetterFactory cellSetterFactory = new CellSetterFactory(customReaders);

		for(int i = 0; i < syncSetterStart; i++) {
			PropertyMeta prop = properties.get(i);
			if (prop != null) {
				if (prop instanceof ConstructorPropertyMeta) {
					delayedSetters.add((DelayedCellSetterFactory)cellSetterFactory.getDelayedCellSetter(prop.getType(), i, columns.get(i)));
				}  else if (prop instanceof SubPropertyMeta) {
					final PropertyMeta powner = ((SubPropertyMeta)prop).getProperty();
					CsvMapperBuilder delegateMapperBuilder = delegateMapperBuilders .get(powner.getName());
					
					if (delegateMapperBuilder == null) {
						delegateMapperBuilder = new CsvMapperBuilder(powner.getType(), powner.getClassMeta(), aliases, customReaders);
						delegateMapperBuilders.put(powner.getName(), delegateMapperBuilder);
					}
					
					delegateMapperBuilder.addMapping(((SubPropertyMeta) prop).getSubProperty(), i, columns.get(i));
					
					delayedSetters.add(null);
				}else {
					delayedSetters.add(cellSetterFactory.getDelayedCellSetter(prop.getSetter(), i, columns.get(i)));
				}
			} else {
				delayedSetters.add(null);
			}
		}
		
		Map> mappers = new HashMap>();
		for(int i = 0; i < syncSetterStart; i++) {
			PropertyMeta prop = properties.get(i);
			if (prop instanceof SubPropertyMeta) {
				PropertyMeta subProp = ((SubPropertyMeta) prop).getProperty();
				
				final String propName = subProp.getName();
				
				CsvMapper mapper = mappers.get(propName);
				if (mapper == null) {
					CsvMapperBuilder delegateMapperBuilder = delegateMapperBuilders .get(propName);
					mapper = delegateMapperBuilder.mapper();
					mappers.put(propName, mapper);
				}
				
				if (subProp instanceof ConstructorPropertyMeta) {
					delayedSetters.set(i , new DelegateMarkerDelayedCellSetter(mapper));
				} else {
					delayedSetters.set(i , new DelegateMarkerDelayedCellSetter(mapper, subProp.getSetter()));
				}
				
			}
		}
		
		return delayedSetters.toArray(new DelayedCellSetterFactory[syncSetterStart]);
	}
	
	private String columnToPropertyName(String column) {
		if (aliases == null || aliases.isEmpty()) {
			return column;
		} 
		String alias = aliases.get(column.toUpperCase());
		if (alias == null) {
			return column;
		}
		return alias;
	}
	
	public final CsvMapperBuilder fieldMapperErrorHandler(final FieldMapperErrorHandler errorHandler) {
		if (!properties.isEmpty()) {
			throw new IllegalStateException(
					"Error Handler need to be set before adding fields");
		}
		fieldMapperErrorHandler = errorHandler;
		return this;
	}

	public final CsvMapperBuilder mapperBuilderErrorHandler(final MapperBuilderErrorHandler errorHandler) {
		mapperBuilderErrorHandler = errorHandler;
		return this;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy