recursive mapping support

This commit is contained in:
Keith Donald 2009-10-05 20:28:10 +00:00
parent fd8935ba0b
commit 0a843e60a6
7 changed files with 151 additions and 6 deletions

View File

@ -17,6 +17,7 @@ package org.springframework.mapping;
/**
* Indicates an individual mapping failed.
* TODO - other fields required here?
* @author Keith Donald
*/
public class MappingFailure {

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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<Object, Object> {
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<Object, Object> {
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<Object, Object> {
}
}
}
}

View File

@ -142,11 +142,7 @@ public class SpelMapperTests {
mapper.addMapping("fullName", "name");
mapper.addMapping("sport", "favoriteSport");
mapper.addMapping("nested", "nested").setConverter(new Converter<NestedDto, Nested>() {
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);