mappable type factory extension point / polish
This commit is contained in:
parent
e3983fe796
commit
8fd3de9397
|
|
@ -25,7 +25,7 @@ import java.util.List;
|
|||
* @see Mapper#map(Object, Object)
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class MappingException extends RuntimeException {
|
||||
public final class MappingException extends RuntimeException {
|
||||
|
||||
private List<MappingFailure> mappingFailures;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ package org.springframework.mapping;
|
|||
* Indicates an individual mapping failed.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class MappingFailure {
|
||||
public final class MappingFailure {
|
||||
|
||||
private final Throwable cause;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,11 @@ import org.springframework.expression.EvaluationContext;
|
|||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
||||
|
||||
class BeanMappableType implements MappableType<Object> {
|
||||
final class BeanMappableType implements MappableType<Object> {
|
||||
|
||||
public boolean isInstance(Object object) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<String> getFields(Object object) {
|
||||
Set<String> fields = new LinkedHashSet<String>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Default mappable type factory that registers Map and Bean mappable types.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
final class DefaultMappableTypeFactory extends MappableTypeFactory {
|
||||
public DefaultMappableTypeFactory() {
|
||||
add(new MapMappableType());
|
||||
add(new BeanMappableType());
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ import org.springframework.util.ClassUtils;
|
|||
* @author Keith Donald
|
||||
* @see BeanUtils#instantiate(Class)
|
||||
*/
|
||||
class DefaultMapperTargetFactory implements MapperTargetFactory {
|
||||
final class DefaultMapperTargetFactory implements MapperTargetFactory {
|
||||
|
||||
public boolean supports(TypeDescriptor targetType) {
|
||||
return ClassUtils.hasConstructor(targetType.getType(), null);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,11 @@ import org.springframework.expression.EvaluationContext;
|
|||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
||||
|
||||
class MapMappableType implements MappableType<Map<? extends Object, ? extends Object>> {
|
||||
final class MapMappableType implements MappableType<Map<? extends Object, ? extends Object>> {
|
||||
|
||||
public boolean isInstance(Object object) {
|
||||
return object instanceof Map<?, ?>;
|
||||
}
|
||||
|
||||
public Set<String> getFields(Map<? extends Object, ? extends Object> object) {
|
||||
LinkedHashSet<String> fields = new LinkedHashSet<String>(object.size(), 1);
|
||||
|
|
|
|||
|
|
@ -37,4 +37,10 @@ interface MappableType<T> {
|
|||
*/
|
||||
EvaluationContext getEvaluationContext(T object, ConversionService conversionService);
|
||||
|
||||
/**
|
||||
* Is this object to map an instanceof this mappable type?
|
||||
* @param object the object to test
|
||||
*/
|
||||
boolean isInstance(Object object);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Generic MappableTypeFactory that has no mappable types registered by default.
|
||||
* Call {@link #add(MappableType)} to register.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class MappableTypeFactory {
|
||||
|
||||
private Set<MappableType<?>> mappableTypes = new LinkedHashSet<MappableType<?>>();
|
||||
|
||||
/**
|
||||
* Add a MappableType to this factory.
|
||||
* @param mappableType the mappable type
|
||||
*/
|
||||
public void add(MappableType<?> mappableType) {
|
||||
this.mappableTypes.add(mappableType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> MappableType<T> getMappableType(T object) {
|
||||
for (MappableType type : mappableTypes) {
|
||||
if (type.isInstance(object)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Object of type [" + object.getClass().getName()
|
||||
+ "] not mappable - no suitable MappableType exists");
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ import org.springframework.mapping.Mapper;
|
|||
* The default MapperTargetFactory instantiates a target by calling its default constructor.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class MapperConverter implements GenericConverter {
|
||||
public final class MapperConverter implements GenericConverter {
|
||||
|
||||
private Mapper mapper;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import org.springframework.core.NamedThreadLocal;
|
|||
* @author Keith Donald
|
||||
* @see SpelMapper#map(Object, Object)
|
||||
*/
|
||||
class MappingContextHolder {
|
||||
abstract class MappingContextHolder {
|
||||
|
||||
private static final ThreadLocal<Stack<Object>> mappingContextHolder = new NamedThreadLocal<Stack<Object>>(
|
||||
"Mapping context");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConverter;
|
||||
|
||||
final class MappingConversionService extends DefaultConversionService {
|
||||
|
||||
@Override
|
||||
protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return new MapperConverter(new SpelMapper());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -19,17 +19,13 @@ import java.util.Collections;
|
|||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConverter;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Expression;
|
||||
|
|
@ -39,11 +35,13 @@ import org.springframework.expression.spel.standard.SpelExpressionParserConfigur
|
|||
import org.springframework.mapping.Mapper;
|
||||
import org.springframework.mapping.MappingException;
|
||||
import org.springframework.mapping.MappingFailure;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A general-purpose object mapper implementation based on the Spring Expression Language (SpEL).
|
||||
* @author Keith Donald
|
||||
* @see #setAutoMappingEnabled(boolean)
|
||||
* @see #setMappableTypeFactory(MappableTypeFactory)
|
||||
* @see #addMapping(String)
|
||||
* @see #addMapping(String, String)
|
||||
* @see #getConverterRegistry()
|
||||
|
|
@ -52,8 +50,6 @@ public class SpelMapper implements Mapper<Object, Object> {
|
|||
|
||||
private static final Log logger = LogFactory.getLog(SpelMapper.class);
|
||||
|
||||
private static final MappableTypeFactory mappableTypeFactory = new MappableTypeFactory();
|
||||
|
||||
private static final SpelExpressionParser sourceExpressionParser = new SpelExpressionParser();
|
||||
|
||||
private static final SpelExpressionParser targetExpressionParser = new SpelExpressionParser(
|
||||
|
|
@ -62,6 +58,8 @@ public class SpelMapper implements Mapper<Object, Object> {
|
|||
|
||||
private final Set<SpelMapping> mappings = new LinkedHashSet<SpelMapping>();
|
||||
|
||||
private MappableTypeFactory mappableTypeFactory = new DefaultMappableTypeFactory();
|
||||
|
||||
private boolean autoMappingEnabled = true;
|
||||
|
||||
private MappingConversionService conversionService = new MappingConversionService();
|
||||
|
|
@ -77,6 +75,15 @@ public class SpelMapper implements Mapper<Object, Object> {
|
|||
this.autoMappingEnabled = autoMappingEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the factory for {@link MappableType mappable types} supported by this mapper.
|
||||
* Default is {@link DefaultMappableTypeFactory}.
|
||||
* @param mappableTypeFactory the mappableTypeFactory
|
||||
*/
|
||||
public void setMappableTypeFactory(MappableTypeFactory mappableTypeFactory) {
|
||||
this.mappableTypeFactory = mappableTypeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a field mapping.
|
||||
* The source and target field expressions will be the same value.
|
||||
|
|
@ -117,8 +124,10 @@ public class SpelMapper implements Mapper<Object, Object> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a Mapper to apply to complex nested property mappings of a specific sourceType/targetType pair.
|
||||
* The source and target types are determined by introspecting the parameterized types on the implementation's Mapper generic interface.
|
||||
* Adds a Mapper that will map the fields of a nested sourceType/targetType pair.
|
||||
* The source and target field types are determined by introspecting the parameterized types on the implementation's Mapper generic interface.
|
||||
* The target instance that is mapped is constructed by a {@link DefaultMapperTargetFactory}.
|
||||
* This method is a convenience method for {@link #addNestedMapper(Class, Class, Mapper)}.
|
||||
* @param nestedMapper the nested mapper
|
||||
*/
|
||||
public void addNestedMapper(Mapper<?, ?> nestedMapper) {
|
||||
|
|
@ -127,9 +136,23 @@ public class SpelMapper implements Mapper<Object, Object> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a Mapper to apply to complex nested property mappings of a specific sourceType/targetType pair.
|
||||
* @param sourceType the source nested property type
|
||||
* @param targetType the target nested property type
|
||||
* Adds a Mapper that will map the fields of a nested sourceType/targetType pair.
|
||||
* The source and target field types are determined by introspecting the parameterized types on the implementation's Mapper generic interface.
|
||||
* The target instance that is mapped is constructed by the provided {@link MapperTargetFactory}.
|
||||
* This method is a convenience method for {@link #addNestedMapper(Class, Class, Mapper, MapperTargetFactory)}.
|
||||
* @param nestedMapper the nested mapper
|
||||
* @param targetFactory the nested mapper's target factory
|
||||
*/
|
||||
public void addNestedMapper(Mapper<?, ?> nestedMapper, MapperTargetFactory targetFactory) {
|
||||
Class<?>[] typeInfo = getRequiredTypeInfo(nestedMapper);
|
||||
addNestedMapper(typeInfo[0], typeInfo[1], nestedMapper, targetFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Mapper that will map the fields of a nested sourceType/targetType pair.
|
||||
* The target instance that is mapped is constructed by a {@link DefaultMapperTargetFactory}.
|
||||
* @param sourceType the source nested object property type
|
||||
* @param targetType the target nested object property type
|
||||
* @param nestedMapper the nested mapper
|
||||
*/
|
||||
public void addNestedMapper(Class<?> sourceType, Class<?> targetType, Mapper<?, ?> nestedMapper) {
|
||||
|
|
@ -137,8 +160,21 @@ public class SpelMapper implements Mapper<Object, Object> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return this mapper's converter registry.
|
||||
* Allows for registration of simple type converters as well as converters that map nested objects using a Mapper.
|
||||
* Adds a Mapper that will map the fields of a nested sourceType/targetType pair.
|
||||
* @param sourceType the source nested object property type
|
||||
* @param targetType the target nested object property type
|
||||
* @param nestedMapper the nested mapper
|
||||
* @param targetFactory the nested mapper's target factory
|
||||
*/
|
||||
public void addNestedMapper(Class<?> sourceType, Class<?> targetType, Mapper<?, ?> nestedMapper,
|
||||
MapperTargetFactory targetFactory) {
|
||||
this.conversionService.addGenericConverter(sourceType, targetType, new MapperConverter(nestedMapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this mapper's internal converter registry.
|
||||
* Allows for registration of simple type Converters in addition to MapperConverters that map entire nested object structures using a Mapper.
|
||||
* To register the latter, consider using one of the {@link #addNestedMapper(Mapper) addNestedMapper} variants.
|
||||
* @see Converter
|
||||
* @see MapperConverter
|
||||
*/
|
||||
|
|
@ -147,6 +183,8 @@ public class SpelMapper implements Mapper<Object, Object> {
|
|||
}
|
||||
|
||||
public Object map(Object source, Object target) {
|
||||
Assert.notNull(source, "The source to map from cannot be null");
|
||||
Assert.notNull(target, "The target to map to cannot be null");
|
||||
try {
|
||||
MappingContextHolder.push(source);
|
||||
EvaluationContext sourceContext = getEvaluationContext(source);
|
||||
|
|
@ -234,29 +272,4 @@ public class SpelMapper implements Mapper<Object, Object> {
|
|||
return false;
|
||||
}
|
||||
|
||||
private static class MappingConversionService extends DefaultConversionService {
|
||||
|
||||
@Override
|
||||
protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return new MapperConverter(new SpelMapper());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MappableTypeFactory {
|
||||
|
||||
private static final MapMappableType MAP_MAPPABLE_TYPE = new MapMappableType();
|
||||
|
||||
private static final BeanMappableType BEAN_MAPPABLE_TYPE = new BeanMappableType();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> MappableType<T> getMappableType(T object) {
|
||||
if (object instanceof Map<?, ?>) {
|
||||
return (MappableType<T>) MAP_MAPPABLE_TYPE;
|
||||
} else {
|
||||
return (MappableType<T>) BEAN_MAPPABLE_TYPE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue