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

org.sfm.jdbc.ResultSetMapperBuilderImpl Maven / Gradle / Ivy

package org.sfm.jdbc;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.sfm.map.FieldMapper;
import org.sfm.map.FieldMapperErrorHandler;
import org.sfm.map.FieldMapperImpl;
import org.sfm.map.MapperBuilderErrorHandler;
import org.sfm.map.MapperBuildingException;
import org.sfm.map.RethrowFieldMapperErrorHandler;
import org.sfm.map.RethrowMapperBuilderErrorHandler;
import org.sfm.reflect.ClassMeta;
import org.sfm.reflect.ConstructorPropertyMeta;
import org.sfm.reflect.Getter;
import org.sfm.reflect.Instantiator;
import org.sfm.reflect.InstantiatorFactory;
import org.sfm.reflect.PropertyFinder;
import org.sfm.reflect.PropertyMeta;
import org.sfm.reflect.ReflectionService;
import org.sfm.reflect.Setter;
import org.sfm.reflect.SubPropertyMeta;
import org.sfm.reflect.asm.ConstructorParameter;

public final class ResultSetMapperBuilderImpl implements ResultSetMapperBuilder {

	private FieldMapperErrorHandler fieldMapperErrorHandler = new RethrowFieldMapperErrorHandler();
	private MapperBuilderErrorHandler mapperBuilderErrorHandler = new RethrowMapperBuilderErrorHandler();
	private JdbcMapperErrorHandler jdbcMapperErrorHandler = new RethrowJdbcMapperErrorHandler();
	
	private final Class target;
	
	private final PrimitiveFieldMapperFactory primitiveFieldMapperFactory;

	private final PropertyFinder propertyFinder;
	private final ReflectionService reflectionService;
	
	
	private final List> fields = new ArrayList>();
	private final Map> constructorInjections;
	private final List> subProperties = new ArrayList<>();
	
	private final Map aliases;
	private final Map> customMappings;
	
	private int columnIndex = 1;
	
	public ResultSetMapperBuilderImpl(final Class target) throws MapperBuildingException {
		this(target, new ReflectionService());
	}
	public ResultSetMapperBuilderImpl(final Class target, ReflectionService reflectService) throws MapperBuildingException {
		this(target, reflectService, null, null);
	}
	public ResultSetMapperBuilderImpl(final Class target, ReflectionService reflectService, final Map aliases, Map> customMappings) throws MapperBuildingException {
		this(target, new ClassMeta(target, reflectService), aliases, customMappings);
	}
	
	public ResultSetMapperBuilderImpl(final Class target, final ClassMeta classMeta, final Map aliases, Map> customMappings) throws MapperBuildingException {
		this.target = target;
		this.reflectionService = classMeta.getReflectionService();
		this.primitiveFieldMapperFactory = new PrimitiveFieldMapperFactory<>(reflectionService.getSetterFactory());
		this.constructorInjections = new HashMap>();
		this.propertyFinder = new PropertyFinder<>(classMeta);
		this.aliases = aliases;
		this.customMappings = customMappings;
	}


	@Override
	public final ResultSetMapperBuilder fieldMapperErrorHandler(final FieldMapperErrorHandler errorHandler) {
		if (!fields.isEmpty()) {
			throw new IllegalStateException(
					"Error Handler need to be set before adding fields");
		}
		fieldMapperErrorHandler = errorHandler;
		return this;
	}

	@Override
	public final ResultSetMapperBuilder mapperBuilderErrorHandler(final MapperBuilderErrorHandler errorHandler) {
		mapperBuilderErrorHandler = errorHandler;
		return this;
	}

	@Override
	public final ResultSetMapperBuilder addNamedColumn(final String column) {
		return addNamedColumn(column, ResultSetGetterFactory.UNDEFINED);
	}

	@Override
	public final ResultSetMapperBuilder addIndexedColumn(final String column) {
		return addIndexedColumn(column, columnIndex ++);
	}

	@Override
	public final ResultSetMapperBuilder addIndexedColumn(final String column, final int columnIndex) {
		return addIndexedColumn(column, columnIndex, ResultSetGetterFactory.UNDEFINED);
	}
	
	@Override
	public final ResultSetMapperBuilder addMapping(final String property, final String column) {
		return addMapping(property, column, ResultSetGetterFactory.UNDEFINED);
	}
	
	@Override
	public final ResultSetMapperBuilder addMapping(final String property, final int column) {
		return addMapping(property, column, ResultSetGetterFactory.UNDEFINED);
	}
	
	@Override
	public final ResultSetMapperBuilder addNamedColumn(final String column, final int sqlType) {
		return addMapping(columnToPropertyName(column), column, sqlType);
	}
	
	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;
	}
	@Override
	public final ResultSetMapperBuilder addIndexedColumn(final String column, final int columnIndex, final int sqlType) {
		return addMapping(columnToPropertyName(column), column, columnIndex, sqlType);
	}

	@Override
	public final ResultSetMapperBuilder addMapping(final String propertyName, final int columnIndex, final int sqlType) {
		return addMapping(propertyName, "column:"+ columnIndex, columnIndex, sqlType);
	}
	
	@Override
	public final ResultSetMapperBuilder addMapping(final String propertyName, final String columnName, final int sqlType) {
		FieldMapper fieldMapper = getCustomMapper(columnName);
		if (fieldMapper != null) {
			fields.add(fieldMapper);
		} else {
			final PropertyMeta property = propertyFinder.findProperty(propertyName);
			if (property == null) {
				mapperBuilderErrorHandler.setterNotFound(target, propertyName);
			} else {
				addMapping(property, columnName, sqlType);
			}
		}
		return this;
	}
	
	public final ResultSetMapperBuilder addMapping(final String propertyName, final String columnName, final int columnIndex, final int sqlType) {
		FieldMapper fieldMapper = getCustomMapper(columnName);
		if (fieldMapper != null) {
			fields.add(fieldMapper);
		} else {
			final PropertyMeta property = propertyFinder.findProperty(propertyName);
			if (property == null) {
					mapperBuilderErrorHandler.setterNotFound(target, propertyName);
			} else {
				addMapping(property, columnName, columnIndex, sqlType);
			}
		}
		return this;
	}
	@SuppressWarnings("unchecked")
	public FieldMapper getCustomMapper(final String columnName) {
		return customMappings != null ? (FieldMapper) customMappings.get(columnName.toUpperCase()) : null;
	}
	
	@Override
	public final ResultSetMapperBuilder addMapping(final ResultSetMetaData metaData) throws SQLException {
		for(int i = 1; i <= metaData.getColumnCount(); i++) {
			addIndexedColumn(metaData.getColumnLabel(i), i, metaData.getColumnType(i));
		}
		
		return this;
	}
	
	@Override
	public final JdbcMapper mapper() throws MapperBuildingException {
		if (reflectionService.isAsmActivated()) {
			try {
				return reflectionService.getAsmFactory().createJdbcMapper(fields(), getInstantiator(), target, jdbcMapperErrorHandler);
			} catch(Exception e) {
				return new JdbcMapperImpl(fields(), getInstantiator(), jdbcMapperErrorHandler);
			}
		} else {
			return new JdbcMapperImpl(fields(), getInstantiator(), jdbcMapperErrorHandler);
		}
	}

	private Instantiator getInstantiator() throws MapperBuildingException {
		InstantiatorFactory instantiatorFactory = reflectionService.getInstantiatorFactory();
		if (!reflectionService.isAsmPresent()) {
			try {
				return instantiatorFactory.getInstantiator(ResultSet.class, target);
			} catch(Exception e) {
				throw new MapperBuildingException(e.getMessage(), e);
			}
		} else {
			try {
				return instantiatorFactory.getInstantiator(ResultSet.class, propertyFinder.getEligibleConstructorDefinitions(), constructorInjections());
			} catch(Exception e) {
				throw new MapperBuildingException(e.getMessage(), e);
			}
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Map> constructorInjections() {
		Map> injections = new HashMap<>(constructorInjections);
		
		for(SubProperty subProp : subProperties) {
			PropertyMeta prop = subProp.subProperty.getProperty();
			if (prop instanceof ConstructorPropertyMeta) {
				ResultSetMapperBuilderImpl builder = subProp.mapperBuilder;
				
				final JdbcMapper mapper = (JdbcMapper) builder.mapper();
				
				Getter getter = new JdbcMapperGetterAdapter<>(mapper); 
				
				injections.put(((ConstructorPropertyMeta) prop).getConstructorParameter(), getter);
			}
			
		}
		
		return injections;
	}
	public final Class getTarget() {
		return target;
	}

	@Override
	@SuppressWarnings("unchecked")
	public final FieldMapper[] fields() {
		List> fields = new ArrayList<>(this.fields);
		
		for(SubProperty subProp : subProperties) {
			PropertyMeta prop = subProp.subProperty.getProperty();
			if (!(prop instanceof ConstructorPropertyMeta)) {
				Setter setter = (Setter) prop.getSetter();
				
				JdbcMapper mapper = (JdbcMapper) subProp.mapperBuilder.mapper();
				
				Getter getter = new JdbcMapperGetterAdapter<>(mapper);
				
				fields.add(new FieldMapperImpl(prop.getName(), getter, setter, fieldMapperErrorHandler));
			}
			
		}
		
		return fields.toArray(new FieldMapper[fields.size()]);
	}

	private SubProperty getOrAddSubPropertyMapperBuilder(SubPropertyMeta property) {
		
		for(SubProperty subProp : subProperties) {
			if (subProp.subProperty.getName().equals(property.getName())) {
				return subProp;
			}
 		}
		
		ResultSetMapperBuilderImpl builder = new ResultSetMapperBuilderImpl<>(property.getType(), property.getClassMeta(), aliases, customMappings);
		SubProperty subProp = new SubProperty(builder, property);
		
		subProperties.add(subProp);
		
		return subProp;
	}
	

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void addMapping(PropertyMeta property, String column, int sqlType) {
		if (property instanceof ConstructorPropertyMeta) {
			ConstructorParameter constructorParameter = ((ConstructorPropertyMeta) property).getConstructorParameter();
			constructorInjections.put(constructorParameter, ResultSetGetterFactory.newGetter(constructorParameter.getType(), column, sqlType));
		} else if (property instanceof SubPropertyMeta) {
			SubProperty subProp = getOrAddSubPropertyMapperBuilder((SubPropertyMeta)property);
			subProp.mapperBuilder.addMapping(((SubPropertyMeta) property).getSubProperty(), column, sqlType);
		} else if (property.getType().isPrimitive()) {
			FieldMapper fieldMapper = primitiveFieldMapperFactory.primitiveFieldMapper(column, property.getSetter(), column, fieldMapperErrorHandler);
			fields.add(fieldMapper);
		} else {
			FieldMapper fieldMapper = objectFieldMapper(column, property.getSetter(), sqlType);
			fields.add(fieldMapper);
		}
	
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void addMapping(PropertyMeta property, String columnName, int column, int sqlType) {
		if (property instanceof ConstructorPropertyMeta) {
			ConstructorParameter constructorParameter = ((ConstructorPropertyMeta) property).getConstructorParameter();
			constructorInjections.put(constructorParameter, ResultSetGetterFactory.newGetter(constructorParameter.getType(), column, sqlType));
		} else if (property instanceof SubPropertyMeta) {
			SubProperty subProp = getOrAddSubPropertyMapperBuilder((SubPropertyMeta)property);
			subProp.mapperBuilder.addMapping(((SubPropertyMeta) property).getSubProperty(), columnName, column, sqlType);
		} else if (property.getType().isPrimitive()) {
			FieldMapper fieldMapper = primitiveFieldMapperFactory.primitiveFieldMapper(column, property.getSetter(), columnName, fieldMapperErrorHandler);
			fields.add(fieldMapper);
		} else {
			FieldMapper fieldMapper = objectFieldMapper(columnName, column, property.getSetter(), sqlType);
			fields.add(fieldMapper);
		}
		
	
	}

	@SuppressWarnings("unchecked")
	private FieldMapper objectFieldMapper(String column, Setter setter, int sqlType) {
		Class type = setter.getPropertyType();
		Getter getter = ResultSetGetterFactory.newGetter(type, column, sqlType);
		if (getter == null) {
			mapperBuilderErrorHandler.getterNotFound("No getter for column " + column + " type " + type);
			return null;
		} else {
			return new FieldMapperImpl(column, getter, (Setter) setter, fieldMapperErrorHandler);
		}
	}

	@SuppressWarnings("unchecked")
	private FieldMapper objectFieldMapper(String columnName, int column, Setter setter, int sqlType) {
		Class type = setter.getPropertyType();
		Getter getter = ResultSetGetterFactory.newGetter(type, column, sqlType);
		if (getter == null) {
			mapperBuilderErrorHandler.getterNotFound("No getter for column " + columnName + " type " + type);
			return null;
		} else {
			return new FieldMapperImpl(columnName, getter, (Setter) setter,	fieldMapperErrorHandler);
		}
	}
	
	private static class SubProperty {
		final ResultSetMapperBuilderImpl mapperBuilder;
		final SubPropertyMeta subProperty;

		SubProperty(ResultSetMapperBuilderImpl mapperBuilder,
				SubPropertyMeta subProperty) {
			this.mapperBuilder = mapperBuilder;
			this.subProperty = subProperty;
		}

	}

	@Override
	public ResultSetMapperBuilder addMapper(FieldMapper mapper) {
		fields.add(mapper);
		return this;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy