diff --git a/org.springframework.context/src/main/java/org/springframework/mapping/MappingFailure.java b/org.springframework.context/src/main/java/org/springframework/mapping/MappingFailure.java index f8d57499ef1..6827b8cbef6 100644 --- a/org.springframework.context/src/main/java/org/springframework/mapping/MappingFailure.java +++ b/org.springframework.context/src/main/java/org/springframework/mapping/MappingFailure.java @@ -17,6 +17,7 @@ package org.springframework.mapping; /** * Indicates an individual mapping failed. + * TODO - other fields required here? * @author Keith Donald */ public class MappingFailure { diff --git a/org.springframework.context/src/main/java/org/springframework/mapping/support/DefaultMapperTargetFactory.java b/org.springframework.context/src/main/java/org/springframework/mapping/support/DefaultMapperTargetFactory.java new file mode 100644 index 00000000000..dff7268f35a --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/mapping/support/DefaultMapperTargetFactory.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.mapping.support; + +import org.springframework.beans.BeanUtils; +import org.springframework.core.convert.TypeDescriptor; + +/** + * Creates a mapping target by calling its default constructor. + * @author Keith Donald + * @see BeanUtils#instantiate(Class) + */ +class DefaultMapperTargetFactory implements MapperTargetFactory { + + public Object createTarget(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + return BeanUtils.instantiate(targetType.getType()); + } + +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/mapping/support/MapperConverter.java b/org.springframework.context/src/main/java/org/springframework/mapping/support/MapperConverter.java new file mode 100644 index 00000000000..50afa02e54d --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/mapping/support/MapperConverter.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.mapping.support; + +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.support.GenericConverter; +import org.springframework.mapping.Mapper; + +/** + * Adapts a Mapper to a Converter, allowing the conversion between two object types to be completed by a Mapper. + * Delegates to a {@link MapperTargetFactory} to construct the conversion target object that will be mapped. + * The default MapperTargetFactory instantiates a target by calling its default constructor. + * @author Keith Donald + */ +public class MapperConverter implements GenericConverter { + + private Mapper mapper; + + private MapperTargetFactory mappingTargetFactory; + + public MapperConverter(Mapper mapper) { + this(mapper, new DefaultMapperTargetFactory()); + } + + public MapperConverter(Mapper mapper, MapperTargetFactory mappingTargetFactory) { + this.mapper = mapper; + this.mappingTargetFactory = mappingTargetFactory; + } + + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + if (source == null) { + return null; + } + Object target = this.mappingTargetFactory.createTarget(source, sourceType, targetType); + return this.mapper.map(source, target); + } + +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/mapping/support/MapperTargetFactory.java b/org.springframework.context/src/main/java/org/springframework/mapping/support/MapperTargetFactory.java new file mode 100644 index 00000000000..35e7e7b0a2a --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/mapping/support/MapperTargetFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.mapping.support; + +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.mapping.Mapper; + +/** + * A factory for customizing how the target of a map operation is constructed. + * Used by a {@link MapperConverter} when executing a type conversion. + * @author Keith Donald + * @see MapperConverter + * @see Mapper#map(Object, Object) + */ +public interface MapperTargetFactory { + + /** + * Create the target object to be mapped to. + * @param source the source object to map from + * @param sourceType the source object type descriptor + * @param targetType the target object type descriptor + * @return the target + */ + public Object createTarget(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/mapping/support/Mapping.java b/org.springframework.context/src/main/java/org/springframework/mapping/support/Mapping.java index 317bc0fbd2e..96a35985ba5 100644 --- a/org.springframework.context/src/main/java/org/springframework/mapping/support/Mapping.java +++ b/org.springframework.context/src/main/java/org/springframework/mapping/support/Mapping.java @@ -26,6 +26,10 @@ import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.mapping.MappingFailure; +/** + * An individual mapping definition between two fields. + * @author Keith Donald + */ class Mapping implements MappingConfiguration { private Expression source; diff --git a/org.springframework.context/src/main/java/org/springframework/mapping/support/SpelMapper.java b/org.springframework.context/src/main/java/org/springframework/mapping/support/SpelMapper.java index efeb5649b1b..07202934a2c 100644 --- a/org.springframework.context/src/main/java/org/springframework/mapping/support/SpelMapper.java +++ b/org.springframework.context/src/main/java/org/springframework/mapping/support/SpelMapper.java @@ -22,8 +22,12 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.core.convert.support.GenericConverter; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; import org.springframework.expression.Expression; @@ -55,12 +59,16 @@ public class SpelMapper implements Mapper { private boolean autoMappingEnabled = true; - private final DefaultConversionService conversionService = new DefaultConversionService(); + private MappingConversionService conversionService = new MappingConversionService(); public void setAutoMappingEnabled(boolean autoMappingEnabled) { this.autoMappingEnabled = autoMappingEnabled; } + public void setConversionService(ConversionService conversionService) { + this.conversionService.setParent(conversionService); + } + public MappingConfiguration addMapping(String fieldExpression) { return addMapping(fieldExpression, fieldExpression); } @@ -158,6 +166,20 @@ public class SpelMapper implements Mapper { return false; } + private class MappingConversionService extends GenericConversionService { + + public MappingConversionService() { + setParent(new DefaultConversionService()); + } + + @Override + protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { + GenericConverter converter = super.getConverter(sourceType, targetType); + return converter != null ? converter : new MapperConverter(new SpelMapper()); + } + + } + private static class MappableTypeFactory { private static final MapMappableType MAP_MAPPABLE_TYPE = new MapMappableType(); @@ -173,4 +195,5 @@ public class SpelMapper implements Mapper { } } } + } diff --git a/org.springframework.context/src/test/java/org/springframework/mapping/support/SpelMapperTests.java b/org.springframework.context/src/test/java/org/springframework/mapping/support/SpelMapperTests.java index c90b0e01c95..696abd8ffdf 100644 --- a/org.springframework.context/src/test/java/org/springframework/mapping/support/SpelMapperTests.java +++ b/org.springframework.context/src/test/java/org/springframework/mapping/support/SpelMapperTests.java @@ -142,11 +142,7 @@ public class SpelMapperTests { mapper.addMapping("fullName", "name"); mapper.addMapping("sport", "favoriteSport"); - mapper.addMapping("nested", "nested").setConverter(new Converter() { - public Nested convert(NestedDto source) { - return (Nested) new SpelMapper().map(source, new Nested()); - } - }); + mapper.addMapping("nested", "nested"); mapper.map(source, target); assertEquals("Keith Donald", target.name);