conditional mapping

This commit is contained in:
Keith Donald 2009-10-17 05:33:04 +00:00
parent 3c4596a424
commit 9584a81687
11 changed files with 291 additions and 80 deletions

View File

@ -26,6 +26,12 @@ import org.springframework.util.ClassUtils;
*/ */
final class DefaultMappingTargetFactory implements MappingTargetFactory { final class DefaultMappingTargetFactory implements MappingTargetFactory {
private static final DefaultMappingTargetFactory INSTANCE = new DefaultMappingTargetFactory();
private DefaultMappingTargetFactory() {
}
public boolean supports(TypeDescriptor targetType) { public boolean supports(TypeDescriptor targetType) {
return ClassUtils.hasConstructor(targetType.getType(), null); return ClassUtils.hasConstructor(targetType.getType(), null);
} }
@ -34,4 +40,8 @@ final class DefaultMappingTargetFactory implements MappingTargetFactory {
return BeanUtils.instantiate(targetType.getType()); return BeanUtils.instantiate(targetType.getType());
} }
public static MappingTargetFactory getInstance() {
return INSTANCE;
}
} }

View File

@ -31,10 +31,13 @@ final class FieldToFieldMapping implements SpelMapping {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private final Converter converter; private final Converter converter;
public FieldToFieldMapping(Expression sourceField, Expression targetField, Converter<?, ?> converter) { private final Expression condition;
public FieldToFieldMapping(Expression sourceField, Expression targetField, Converter<?, ?> converter, Expression condition) {
this.sourceField = sourceField; this.sourceField = sourceField;
this.targetField = targetField; this.targetField = targetField;
this.converter = converter; this.converter = converter;
this.condition = condition;
} }
public String getSourceField() { public String getSourceField() {
@ -51,6 +54,9 @@ final class FieldToFieldMapping implements SpelMapping {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void map(SpelMappingContext context) { public void map(SpelMappingContext context) {
if (!context.conditionHolds(this.condition)) {
return;
}
try { try {
Object value = context.getSourceFieldValue(this.sourceField); Object value = context.getSourceFieldValue(this.sourceField);
if (this.converter != null) { if (this.converter != null) {

View File

@ -28,10 +28,13 @@ final class FieldToMultiFieldMapping implements SpelMapping {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private final Mapper targetFieldMapper; private final Mapper targetFieldMapper;
public FieldToMultiFieldMapping(Expression sourceField, Mapper<?, ?> targetFieldMapper) { private final Expression condition;
public FieldToMultiFieldMapping(Expression sourceField, Mapper<?, ?> targetFieldMapper, Expression condition) {
this.sourceField = sourceField; this.sourceField = sourceField;
this.targetFieldMapper = targetFieldMapper; this.targetFieldMapper = targetFieldMapper;
this.condition = condition;
} }
public String getSourceField() { public String getSourceField() {
@ -44,6 +47,9 @@ final class FieldToMultiFieldMapping implements SpelMapping {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void map(SpelMappingContext context) { public void map(SpelMappingContext context) {
if (!context.conditionHolds(this.condition)) {
return;
}
try { try {
Object value = context.getSourceFieldValue(this.sourceField); Object value = context.getSourceFieldValue(this.sourceField);
this.targetFieldMapper.map(value, context.getTarget()); this.targetFieldMapper.map(value, context.getTarget());

View File

@ -50,7 +50,7 @@ public interface MapperBuilder<S, T> {
* The source and target field names will be the same value. * The source and target field names will be the same value.
* For example, calling <code>addMapping("order")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>order</code> field on the target. * For example, calling <code>addMapping("order")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>order</code> field on the target.
* This is a convenience method for calling {@link #addMapping(String, String)} with the same source and target value.. * This is a convenience method for calling {@link #addMapping(String, String)} with the same source and target value..
* @param fieldExpression the field mapping expression * @param field the field mapping expression
* @return this, for configuring additional field mapping options fluently * @return this, for configuring additional field mapping options fluently
*/ */
MapperBuilder<S, T> addMapping(String field); MapperBuilder<S, T> addMapping(String field);
@ -60,7 +60,7 @@ public interface MapperBuilder<S, T> {
* The source and target field expressions will be the same value. * The source and target field expressions will be the same value.
* For example, calling <code>addMapping("order")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>order</code> field on the target. * For example, calling <code>addMapping("order")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>order</code> field on the target.
* This is a convenience method for calling {@link #addMapping(String, String, Converter)} with the same source and target value.. * This is a convenience method for calling {@link #addMapping(String, String, Converter)} with the same source and target value..
* @param fieldExpression the field mapping expression * @param field the field mapping expression
* @param converter the converter that will convert the source field value before mapping the value to the target field * @param converter the converter that will convert the source field value before mapping the value to the target field
* @return this, for configuring additional field mapping options fluently * @return this, for configuring additional field mapping options fluently
*/ */
@ -81,8 +81,8 @@ public interface MapperBuilder<S, T> {
* Register a mapping between a source field and a target field. * Register a mapping between a source field and a target field.
* Use this method when the name of the source field and the name of the target field are different. * Use this method when the name of the source field and the name of the target field are different.
* For example, calling <code>addMapping("order", "primaryOrder")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>primaryOrder</code> field on the target. * For example, calling <code>addMapping("order", "primaryOrder")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>primaryOrder</code> field on the target.
* @param sourceFieldExpression the source field mapping expression * @param sourceField the source field mapping expression
* @param targetFieldExpression the target field mapping expression * @param targetField the target field mapping expression
* @return this, for configuring additional field mapping options fluently * @return this, for configuring additional field mapping options fluently
*/ */
MapperBuilder<S, T> addMapping(String sourceField, String targetField); MapperBuilder<S, T> addMapping(String sourceField, String targetField);
@ -91,8 +91,8 @@ public interface MapperBuilder<S, T> {
* Register a mapping between a source field and a target field that first converts the source field value using the provided Converter. * Register a mapping between a source field and a target field that first converts the source field value using the provided Converter.
* Use this method when the name of the source field and the name of the target field are different. * Use this method when the name of the source field and the name of the target field are different.
* For example, calling <code>addMapping("order", "primaryOrder")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>primaryOrder</code> field on the target. * For example, calling <code>addMapping("order", "primaryOrder")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>primaryOrder</code> field on the target.
* @param sourceFieldExpression the source field mapping expression * @param sourceField the source field mapping expression
* @param targetFieldExpression the target field mapping expression * @param targetField the target field mapping expression
* @return this, for configuring additional field mapping options fluently * @return this, for configuring additional field mapping options fluently
*/ */
MapperBuilder<S, T> addMapping(String sourceField, String targetField, Converter<?, ?> converter); MapperBuilder<S, T> addMapping(String sourceField, String targetField, Converter<?, ?> converter);
@ -107,6 +107,71 @@ public interface MapperBuilder<S, T> {
*/ */
MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper); MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper);
/**
* Register a conditional mapping between a source field and a target field.
* The source and target field names will be the same value.
* @param field the field mapping expression
* @param condition the boolean expression that determines if this mapping executes
* @return this, for configuring additional field mapping options fluently
*/
MapperBuilder<S, T> addConditionalMapping(String field, String condition);
/**
* Register a condition mapping between a source field and a target field that first converts the source field value using the provided Converter.
* The source and target field expressions will be the same value.
* @param field the field mapping expression
* @param converter the converter that converts the source field value before mapping
* @param condition the boolean expression that determines if this mapping executes
* @return this, for configuring additional field mapping options fluently
*/
MapperBuilder<S, T> addConditionalMapping(String field, Converter<?, ?> converter, String condition);
/**
* Register a conditional mapping between a source field and multiple target fields.
* Use this method when you need to map a single source field value to multiple fields on the target.
* For example, calling <code>addMapping("name", firstAndLastNameMapper)</code> might register a mapping that maps the <code>name</code> field on the source to the <code>firstName</code> and <code>lastName</code> fields on the target.
* The target field {@link Mapper} will be passed the value of the source field for its source and the target object T for its target.
* @param field the source field expression
* @param mapper the mapper of the target fields
* @param condition the boolean expression that determines if this mapping executes
* @return this, for configuring additional field mapping options fluently
*/
MapperBuilder<S, T> addConditionalMapping(String field, Mapper<?, T> mapper, String condition);
/**
* Register a conditional mapping between a source field and a target field.
* Use this method when the name of the source field and the name of the target field are different.
* For example, calling <code>addMapping("order", "primaryOrder")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>primaryOrder</code> field on the target.
* @param sourceField the source field mapping expression
* @param targetField the target field mapping expression
* @param condition the boolean expression that determines if this mapping executes
* @return this, for configuring additional field mapping options fluently
*/
MapperBuilder<S, T> addConditionalMapping(String sourceField, String targetField, String condition);
/**
* Register a conditional mapping between a source field and a target field that first converts the source field value using the provided Converter.
* Use this method when the name of the source field and the name of the target field are different.
* For example, calling <code>addMapping("order", "primaryOrder")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>primaryOrder</code> field on the target.
* @param sourceField the source field mapping expression
* @param targetField the target field mapping expression
* @param converter the converter that converts the source field value before mapping
* @param condition the boolean expression that determines if this mapping executes
* @return this, for configuring additional field mapping options fluently
*/
MapperBuilder<S, T> addConditionalMapping(String sourceField, String targetField, Converter<?, ?> converter, String condition);
/**
* Register a conditional mapping between multiple source fields and a single target field.
* For example, calling <code>addMapping(dateAndTimeFieldsToDateTimeFieldMapper)</code> might register a mapping that maps the <code>date</code> and <code>time</code> fields on the source to the <code>dateTime</code> field on the target.
* The provided {@link Mapper} will be passed the source object S for its source and the target object T for its target.
* @param fields the source field mapping expressions
* @param mapper the fields to field mapper
* @param condition the boolean expression that determines if this mapping executes
* @return this, for configuring additional field mapping options fluently
*/
MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper, String condition);
/** /**
* Register a Mapper that will be used to map between nested source and target fields of a specific sourceType/targetType pair. * Register a Mapper that will be used to map between nested source and target fields of a specific sourceType/targetType pair.
* The source and target field types are determined by introspecting the parameterized types on the Mapper generic interface. * The source and target field types are determined by introspecting the parameterized types on the Mapper generic interface.
@ -130,11 +195,18 @@ public interface MapperBuilder<S, T> {
* Register a custom type converter to use to convert between two mapped types. * Register a custom type converter to use to convert between two mapped types.
* The Converter may convert between simple types, such as Strings to Dates. * The Converter may convert between simple types, such as Strings to Dates.
* Alternatively, it may convert between complex types and initiate a recursive mapping operation between two object fields. * Alternatively, it may convert between complex types and initiate a recursive mapping operation between two object fields.
* @return this, for configuring additional field mapping options fluently
* @see Converter * @see Converter
* @see MappingConverter * @see MappingConverter
*/ */
MapperBuilder<S, T> addConverter(Converter<?, ?> converter); MapperBuilder<S, T> addConverter(Converter<?, ?> converter);
/**
* Set the source fields to exclude from mapping.
* @param fields the source fields as var args
*/
MapperBuilder<S, T> setExcludedFields(String... fields);
/** /**
* Get the Mapper produced by this builder. * Get the Mapper produced by this builder.
* Call this method after instructing the builder. * Call this method after instructing the builder.

View File

@ -23,7 +23,7 @@ final class MappingConversionService extends DefaultConversionService {
@Override @Override
protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
return new MappingConverter(new SpelMapper()); return new MappingConverter(new SpelMapper(), null);
} }
} }

View File

@ -33,15 +33,6 @@ final class MappingConverter implements GenericConverter {
private final MappingTargetFactory mappingTargetFactory; private final 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. * 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. * Uses the specified MappingTargetFactory to create the target object to map and return.
@ -49,7 +40,11 @@ final class MappingConverter implements GenericConverter {
*/ */
public MappingConverter(Mapper mapper, MappingTargetFactory mappingTargetFactory) { public MappingConverter(Mapper mapper, MappingTargetFactory mappingTargetFactory) {
this.mapper = mapper; this.mapper = mapper;
this.mappingTargetFactory = mappingTargetFactory; if (mappingTargetFactory != null) {
this.mappingTargetFactory = mappingTargetFactory;
} else {
this.mappingTargetFactory = DefaultMappingTargetFactory.getInstance();
}
} }
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {

View File

@ -16,6 +16,7 @@
package org.springframework.mapping.support; package org.springframework.mapping.support;
import org.springframework.core.style.StylerUtils; import org.springframework.core.style.StylerUtils;
import org.springframework.expression.Expression;
import org.springframework.mapping.Mapper; import org.springframework.mapping.Mapper;
/** /**
@ -29,9 +30,12 @@ final class MultiFieldToFieldMapping implements SpelMapping {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private final Mapper multiFieldMapper; private final Mapper multiFieldMapper;
public MultiFieldToFieldMapping(String[] fields, Mapper<?, ?> multiFieldMapper) { private Expression condition;
public MultiFieldToFieldMapping(String[] fields, Mapper<?, ?> multiFieldMapper, Expression condition) {
this.fields = fields; this.fields = fields;
this.multiFieldMapper = multiFieldMapper; this.multiFieldMapper = multiFieldMapper;
this.condition = condition;
} }
public String[] getSourceFields() { public String[] getSourceFields() {
@ -49,6 +53,9 @@ final class MultiFieldToFieldMapping implements SpelMapping {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void map(SpelMappingContext context) { public void map(SpelMappingContext context) {
if (!context.conditionHolds(this.condition)) {
return;
}
try { try {
this.multiFieldMapper.map(context.getSource(), context.getTarget()); this.multiFieldMapper.map(context.getSource(), context.getTarget());
} catch (Exception e) { } catch (Exception e) {

View File

@ -71,64 +71,32 @@ final class SpelMapper implements Mapper<Object, Object> {
this.mappableTypeFactory = mappableTypeFactory; this.mappableTypeFactory = mappableTypeFactory;
} }
public void addMapping(String sourceFieldExpression, String targetFieldExpression, Converter<?, ?> converter) { public void setExcludedFields(String[] fields) {
// TODO
}
public void addMapping(String sourceFieldExpression, String targetFieldExpression, Converter<?, ?> converter,
String condition) {
Expression sourceField = parseSourceField(sourceFieldExpression); Expression sourceField = parseSourceField(sourceFieldExpression);
Expression targetField = parseTargetField(targetFieldExpression); Expression targetField = parseTargetField(targetFieldExpression);
FieldToFieldMapping mapping = new FieldToFieldMapping(sourceField, targetField, converter); FieldToFieldMapping mapping = new FieldToFieldMapping(sourceField, targetField, converter,
parseCondition(condition));
this.mappings.add(mapping); this.mappings.add(mapping);
} }
public void addMapping(String field, Mapper<?, ?> mapper) { public void addMapping(String field, Mapper<?, ?> mapper, String condition) {
this.mappings.add(new FieldToMultiFieldMapping(parseSourceField(field), mapper)); this.mappings.add(new FieldToMultiFieldMapping(parseSourceField(field), mapper, parseCondition(condition)));
} }
public void addMapping(String[] fields, Mapper<?, ?> mapper) { public void addMapping(String[] fields, Mapper<?, ?> mapper, String condition) {
this.mappings.add(new MultiFieldToFieldMapping(fields, mapper)); this.mappings.add(new MultiFieldToFieldMapping(fields, mapper, parseCondition(condition)));
} }
/**
* 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 DefaultMappingTargetFactory}.
* This method is a convenience method for {@link #addNestedMapper(Class, Class, Mapper)}.
* @param nestedMapper the nested mapper
*/
public void addNestedMapper(Mapper<?, ?> nestedMapper) {
Class<?>[] typeInfo = getRequiredTypeInfo(nestedMapper);
addNestedMapper(typeInfo[0], typeInfo[1], nestedMapper);
}
/**
* 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 MappingTargetFactory}.
* This method is a convenience method for {@link #addNestedMapper(Class, Class, Mapper, MappingTargetFactory)}.
* @param nestedMapper the nested mapper
* @param targetFactory the nested mapper's target factory
*/
public void addNestedMapper(Mapper<?, ?> nestedMapper, MappingTargetFactory targetFactory) { public void addNestedMapper(Mapper<?, ?> nestedMapper, MappingTargetFactory targetFactory) {
Class<?>[] typeInfo = getRequiredTypeInfo(nestedMapper); Class<?>[] typeInfo = getRequiredTypeInfo(nestedMapper);
addNestedMapper(typeInfo[0], typeInfo[1], nestedMapper, targetFactory); 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 DefaultMappingTargetFactory}.
* @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) {
this.conversionService.addGenericConverter(sourceType, targetType, new MappingConverter(nestedMapper));
}
/**
* 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, public void addNestedMapper(Class<?> sourceType, Class<?> targetType, Mapper<?, ?> nestedMapper,
MappingTargetFactory targetFactory) { MappingTargetFactory targetFactory) {
this.conversionService.addGenericConverter(sourceType, targetType, new MappingConverter(nestedMapper, this.conversionService.addGenericConverter(sourceType, targetType, new MappingConverter(nestedMapper,
@ -170,25 +138,33 @@ final class SpelMapper implements Mapper<Object, Object> {
// internal helpers // internal helpers
private Expression parseSourceField(String sourceFieldExpression) { private Expression parseSourceField(String sourceFieldExpression) {
Expression sourceExp;
try { try {
sourceExp = sourceExpressionParser.parseExpression(sourceFieldExpression); return sourceExpressionParser.parseExpression(sourceFieldExpression);
} catch (ParseException e) { } catch (ParseException e) {
throw new IllegalArgumentException("The mapping source '" + sourceFieldExpression throw new IllegalArgumentException("The mapping source '" + sourceFieldExpression
+ "' is not a parseable value expression", e); + "' is not a parseable value expression", e);
} }
return sourceExp; }
private Expression parseCondition(String condition) {
if (condition == null) {
return null;
}
try {
return sourceExpressionParser.parseExpression(condition);
} catch (ParseException e) {
throw new IllegalArgumentException("The mapping condition '" + condition
+ "' is not a parseable value expression", e);
}
} }
private Expression parseTargetField(String targetFieldExpression) { private Expression parseTargetField(String targetFieldExpression) {
Expression targetExp;
try { try {
targetExp = targetExpressionParser.parseExpression(targetFieldExpression); return targetExpressionParser.parseExpression(targetFieldExpression);
} catch (ParseException e) { } catch (ParseException e) {
throw new IllegalArgumentException("The mapping target '" + targetFieldExpression throw new IllegalArgumentException("The mapping target '" + targetFieldExpression
+ "' is not a parseable property expression", e); + "' is not a parseable property expression", e);
} }
return targetExp;
} }
private Class<?>[] getRequiredTypeInfo(Mapper<?, ?> mapper) { private Class<?>[] getRequiredTypeInfo(Mapper<?, ?> mapper) {
@ -221,7 +197,7 @@ final class SpelMapper implements Mapper<Object, Object> {
} }
try { try {
if (targetExpression.isWritable(targetContext)) { if (targetExpression.isWritable(targetContext)) {
autoMappings.add(new FieldToFieldMapping(sourceExpression, targetExpression, null)); autoMappings.add(new FieldToFieldMapping(sourceExpression, targetExpression, null, null));
} }
} catch (EvaluationException e) { } catch (EvaluationException e) {
@ -246,4 +222,5 @@ final class SpelMapper implements Mapper<Object, Object> {
} }
return false; return false;
} }
} }

View File

@ -40,37 +40,68 @@ final class SpelMapperBuilder<S, T> implements MapperBuilder<S, T> {
} }
public MapperBuilder<S, T> addMapping(String field) { public MapperBuilder<S, T> addMapping(String field) {
this.mapper.addMapping(field, field, null); this.mapper.addMapping(field, field, null, null);
return this; return this;
} }
public MapperBuilder<S, T> addMapping(String field, Converter<?, ?> converter) { public MapperBuilder<S, T> addMapping(String field, Converter<?, ?> converter) {
this.mapper.addMapping(field, field, converter); this.mapper.addMapping(field, field, converter, null);
return this; return this;
} }
public MapperBuilder<S, T> addMapping(String field, Mapper<?, T> mapper) { public MapperBuilder<S, T> addMapping(String field, Mapper<?, T> mapper) {
this.mapper.addMapping(field, mapper); this.mapper.addMapping(field, mapper, null);
return this; return this;
} }
public MapperBuilder<S, T> addMapping(String sourceField, String targetField) { public MapperBuilder<S, T> addMapping(String sourceField, String targetField) {
this.mapper.addMapping(sourceField, targetField, null); this.mapper.addMapping(sourceField, targetField, null, null);
return this; return this;
} }
public MapperBuilder<S, T> addMapping(String sourceField, String targetField, Converter<?, ?> converter) { public MapperBuilder<S, T> addMapping(String sourceField, String targetField, Converter<?, ?> converter) {
this.mapper.addMapping(sourceField, targetField, converter); this.mapper.addMapping(sourceField, targetField, converter, null);
return this; return this;
} }
public MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper) { public MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper) {
this.mapper.addMapping(fields, mapper); this.mapper.addMapping(fields, mapper, null);
return this;
}
public MapperBuilder<S, T> addConditionalMapping(String field, String condition) {
this.mapper.addMapping(field, field, null, condition);
return this;
}
public MapperBuilder<S, T> addConditionalMapping(String field, Converter<?, ?> converter, String condition) {
this.mapper.addMapping(field, field, converter, condition);
return this;
}
public MapperBuilder<S, T> addConditionalMapping(String field, Mapper<?, T> mapper, String condition) {
this.mapper.addMapping(field, mapper, condition);
return this;
}
public MapperBuilder<S, T> addConditionalMapping(String sourceField, String targetField, String condition) {
this.mapper.addMapping(sourceField, targetField, null, condition);
return this;
}
public MapperBuilder<S, T> addConditionalMapping(String sourceField, String targetField, Converter<?, ?> converter,
String condition) {
this.mapper.addMapping(sourceField, targetField, converter, condition);
return this;
}
public MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper, String condition) {
this.mapper.addMapping(fields, mapper, condition);
return this; return this;
} }
public MapperBuilder<S, T> addNestedMapper(Mapper<?, ?> nestedMapper) { public MapperBuilder<S, T> addNestedMapper(Mapper<?, ?> nestedMapper) {
this.mapper.addNestedMapper(nestedMapper); this.mapper.addNestedMapper(nestedMapper, null);
return this; return this;
} }
@ -83,6 +114,11 @@ final class SpelMapperBuilder<S, T> implements MapperBuilder<S, T> {
this.mapper.getConverterRegistry().addConverter(converter); this.mapper.getConverterRegistry().addConverter(converter);
return this; return this;
} }
public MapperBuilder<S, T> setExcludedFields(String... fields) {
this.mapper.setExcludedFields(fields);
return this;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Mapper<S, T> getMapper() { public Mapper<S, T> getMapper() {

View File

@ -43,7 +43,14 @@ final class SpelMappingContext {
public Object getTarget() { public Object getTarget() {
return this.targetEvaluationContext.getRootObject().getValue(); return this.targetEvaluationContext.getRootObject().getValue();
} }
public boolean conditionHolds(Expression condition) {
if (condition == null) {
return true;
}
return Boolean.TRUE.equals(condition.getValue(this.sourceEvaluationContext));
}
public Object getSourceFieldValue(Expression sourceField) { public Object getSourceFieldValue(Expression sourceField) {
return sourceField.getValue(this.sourceEvaluationContext); return sourceField.getValue(this.sourceEvaluationContext);
} }
@ -61,4 +68,5 @@ final class SpelMappingContext {
throw new MappingException(this.failures); throw new MappingException(this.failures);
} }
} }
} }

View File

@ -317,6 +317,45 @@ public class MappingTests {
.getActivationDateTime()); .getActivationDateTime());
} }
@Test
public void conditionalMapping() {
Map<String, String> domestic = new HashMap<String, String>();
domestic.put("international", "false");
domestic.put("areaCode", "205");
domestic.put("prefix", "339");
domestic.put("line", "1234");
domestic.put("countryCode", "whatever");
domestic.put("cityCode", "whatever");
Mapper<Map, PhoneNumber> mapper = MapperFactory.mapperBuilder(Map.class, PhoneNumber.class)
.addConditionalMapping("countryCode", "international == 'true'")
.addConditionalMapping("cityCode", "international == 'true'")
.getMapper();
PhoneNumber number = mapper.map(domestic, new PhoneNumber());
assertEquals("205", number.getAreaCode());
assertEquals("339", number.getPrefix());
assertEquals("1234", number.getLine());
assertNull(number.getCountryCode());
assertNull(number.getCityCode());
Map<String, String> international = new HashMap<String, String>();
international.put("international", "true");
international.put("areaCode", "205");
international.put("prefix", "339");
international.put("line", "1234");
international.put("countryCode", "1");
international.put("cityCode", "2");
PhoneNumber number2 = mapper.map(international, new PhoneNumber());
assertEquals("205", number2.getAreaCode());
assertEquals("339", number2.getPrefix());
assertEquals("1234", number2.getLine());
assertEquals("1", number2.getCountryCode());
assertEquals("2", number2.getCityCode());
}
@Test @Test
public void mapList() { public void mapList() {
PersonDto source = new PersonDto(); PersonDto source = new PersonDto();
@ -876,4 +915,59 @@ public class MappingTests {
} }
} }
public static class PhoneNumber {
private String areaCode;
private String prefix;
private String line;
private String countryCode;
private String cityCode;
public String getAreaCode() {
return areaCode;
}
public void setAreaCode(String areaCode) {
this.areaCode = areaCode;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getLine() {
return line;
}
public void setLine(String line) {
this.line = line;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public String getCityCode() {
return cityCode;
}
public void setCityCode(String cityCode) {
this.cityCode = cityCode;
}
}
} }