This commit is contained in:
Keith Donald 2009-10-15 15:49:26 +00:00
parent e99cda3739
commit 3fb3549997
6 changed files with 86 additions and 8 deletions

View File

@ -30,7 +30,7 @@ final class DefaultMappingTargetFactory implements MappingTargetFactory {
return ClassUtils.hasConstructor(targetType.getType(), null);
}
public Object createTarget(TypeDescriptor targetType) {
public Object createTarget(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return BeanUtils.instantiate(targetType.getType());
}

View File

@ -24,7 +24,7 @@ import org.springframework.core.NamedThreadLocal;
* @author Keith Donald
* @see SpelMapper#map(Object, Object)
*/
abstract class MappingContextHolder {
public abstract class MappingContextHolder {
private static final ThreadLocal<Stack<Object>> mappingContextHolder = new NamedThreadLocal<Stack<Object>>(
"Mapping context");

View File

@ -33,10 +33,20 @@ public final class MappingConverter implements GenericConverter {
private MappingTargetFactory mappingTargetFactory;
/**
* Creates a new Converter that delegates to the mapper to complete the type conversion process.
* Uses a {@link DefaultMappingTargetFactory} to create the target object to map and return.
* @param mapper the mapper
*/
public MappingConverter(Mapper mapper) {
this(mapper, new DefaultMappingTargetFactory());
}
/**
* Creates a new Converter that delegates to the mapper to complete the type conversion process.
* Uses the specified MappingTargetFactory to create the target object to map and return.
* @param mapper the mapper
*/
public MappingConverter(Mapper mapper, MappingTargetFactory mappingTargetFactory) {
this.mapper = mapper;
this.mappingTargetFactory = mappingTargetFactory;
@ -52,7 +62,7 @@ public final class MappingConverter implements GenericConverter {
if (sourceType.isAssignableTo(targetType) && isCopyByReference(sourceType, targetType)) {
return source;
}
return createAndMap(targetType, source, sourceType);
return createTargetAndMap(source, sourceType, targetType);
}
private boolean isCopyByReference(TypeDescriptor sourceType, TypeDescriptor targetType) {
@ -63,13 +73,13 @@ public final class MappingConverter implements GenericConverter {
}
}
private Object createAndMap(TypeDescriptor targetType, Object source, TypeDescriptor sourceType) {
private Object createTargetAndMap(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (this.mappingTargetFactory.supports(targetType)) {
Object target = this.mappingTargetFactory.createTarget(targetType);
Object target = this.mappingTargetFactory.createTarget(source, sourceType, targetType);
return this.mapper.map(source, target);
} else {
IllegalStateException cause = new IllegalStateException("["
+ this.mappingTargetFactory.getClass().getName() + "] does not support target type ["
+ this.mappingTargetFactory.getClass().getName() + "] does not support targetType ["
+ targetType.getName() + "]");
throw new ConversionFailedException(sourceType, targetType, source, cause);
}

View File

@ -41,6 +41,6 @@ public interface MappingTargetFactory {
* @param targetType the target object type descriptor
* @return the target
*/
public Object createTarget(TypeDescriptor targetType);
public Object createTarget(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

View File

@ -170,7 +170,8 @@ public class SpelMapper implements Mapper<Object, Object> {
*/
public void addNestedMapper(Class<?> sourceType, Class<?> targetType, Mapper<?, ?> nestedMapper,
MappingTargetFactory targetFactory) {
this.conversionService.addGenericConverter(sourceType, targetType, new MappingConverter(nestedMapper));
this.conversionService.addGenericConverter(sourceType, targetType, new MappingConverter(nestedMapper,
targetFactory));
}
/**

View File

@ -14,6 +14,7 @@ import java.util.Set;
import org.junit.Test;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.ClassPathResource;
import org.springframework.expression.AccessException;
@ -201,6 +202,41 @@ public class SpelMapperTests {
assertEquals("bar and baz", target.nested.foo);
}
@Test
public void mapBeanNestedCustomNestedMapperCustomMappingTargetFactory() {
PersonDto source = new PersonDto();
final NestedDto nested = new NestedDto();
nested.foo = "bar";
source.setNested(nested);
Person target = new Person();
SpelMapper nestedMapper = new SpelMapper();
nestedMapper.setAutoMappingEnabled(false);
nestedMapper.addMapping("foo").setConverter(new Converter<String, String>() {
public String convert(String source) {
return source + " and baz";
}
});
mapper.addNestedMapper(NestedDto.class, Nested.class, nestedMapper, new MappingTargetFactory() {
public boolean supports(TypeDescriptor targetType) {
return true;
}
public Object createTarget(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
NestedDto nestedDto = (NestedDto) source;
assertEquals(nested, nestedDto);
return new Nested();
}
});
mapper.setAutoMappingEnabled(false);
mapper.addMapping("nested");
mapper.map(source, target);
assertEquals("bar and baz", target.nested.foo);
}
@Test
public void mapBeanNestedCustomNestedMapperHandCoded() {
PersonDto source = new PersonDto();
@ -226,6 +262,37 @@ public class SpelMapperTests {
assertEquals("bar and baz", target.nested.foo);
}
@Test
public void mapBeanNestedCustomConverterDelegatingToMapper() {
PersonDto source = new PersonDto();
NestedDto nested = new NestedDto();
nested.foo = "bar";
source.setNested(nested);
Person target = new Person();
mapper.getConverterRegistry().addConverter(new Converter<NestedDto, Nested>() {
public Nested convert(NestedDto source) {
// allows construction of target to be controlled by the converter
Nested nested = new Nested();
// mapping can do whatever, here we delegate to nested SpelMapper
SpelMapper nestedMapper = new SpelMapper();
nestedMapper.addMapping("foo").setConverter(new Converter<String, String>() {
public String convert(String source) {
return source + " and baz";
}
});
return (Nested) nestedMapper.map(source, nested);
}
});
mapper.setAutoMappingEnabled(false);
mapper.addMapping("nested");
mapper.map(source, target);
assertEquals("bar and baz", target.nested.foo);
}
@Test
public void mapList() {
PersonDto source = new PersonDto();