This commit is contained in:
Keith Donald 2009-10-17 04:03:56 +00:00
parent f55b54ec3b
commit f63c3d5313
9 changed files with 108 additions and 32 deletions

View File

@ -45,6 +45,10 @@ final class FieldToFieldMapping implements SpelMapping {
return this.targetField.getExpressionString();
}
public boolean mapsField(String field) {
return getSourceField().equals(field);
}
@SuppressWarnings("unchecked")
public void map(SpelMappingContext context) {
try {

View File

@ -38,6 +38,10 @@ final class FieldToMultiFieldMapping implements SpelMapping {
return this.sourceField.getExpressionString();
}
public boolean mapsField(String field) {
return getSourceField().equals(field);
}
@SuppressWarnings("unchecked")
public void map(SpelMappingContext context) {
try {

View File

@ -101,10 +101,11 @@ public interface MapperBuilder<S, T> {
* Register a 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
* @return this, for configuring additional field mapping options fluently
*/
MapperBuilder<S, T> addMapping(Mapper<S, T> mapper);
MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper);
/**
* Register a Mapper that will be used to map between nested source and target fields of a specific sourceType/targetType pair.

View File

@ -15,6 +15,7 @@
*/
package org.springframework.mapping.support;
import org.springframework.core.style.StylerUtils;
import org.springframework.mapping.Mapper;
/**
@ -23,13 +24,29 @@ import org.springframework.mapping.Mapper;
*/
final class MultiFieldToFieldMapping implements SpelMapping {
private String[] fields;
@SuppressWarnings("unchecked")
private final Mapper multiFieldMapper;
public MultiFieldToFieldMapping(Mapper<?, ?> multiFieldMapper) {
public MultiFieldToFieldMapping(String[] fields, Mapper<?, ?> multiFieldMapper) {
this.fields = fields;
this.multiFieldMapper = multiFieldMapper;
}
public String[] getSourceFields() {
return fields;
}
public boolean mapsField(String field) {
for (String f : this.fields) {
if (f.equals(field)) {
return true;
}
}
return false;
}
@SuppressWarnings("unchecked")
public void map(SpelMappingContext context) {
try {
@ -52,7 +69,7 @@ final class MultiFieldToFieldMapping implements SpelMapping {
}
public String toString() {
return "[MultiFieldToFieldMapping<" + this.multiFieldMapper + ">]";
return "[MultiFieldToFieldMapping<" + StylerUtils.style(this.fields) + " -> " + this.multiFieldMapper + ">]";
}
}

View File

@ -78,12 +78,12 @@ final class SpelMapper implements Mapper<Object, Object> {
this.mappings.add(mapping);
}
public void addMapping(String field, Mapper mapper) {
public void addMapping(String field, Mapper<?, ?> mapper) {
this.mappings.add(new FieldToMultiFieldMapping(parseSourceField(field), mapper));
}
public void addMapping(Mapper mapper) {
this.mappings.add(new MultiFieldToFieldMapping(mapper));
public void addMapping(String[] fields, Mapper<?, ?> mapper) {
this.mappings.add(new MultiFieldToFieldMapping(fields, mapper));
}
/**
@ -240,8 +240,7 @@ final class SpelMapper implements Mapper<Object, Object> {
private boolean explicitlyMapped(String field) {
for (SpelMapping mapping : this.mappings) {
if (mapping instanceof FieldToFieldMapping
&& ((FieldToFieldMapping) mapping).getSourceField().startsWith(field)) {
if (mapping.mapsField(field)) {
return true;
}
}

View File

@ -64,8 +64,8 @@ final class SpelMapperBuilder<S, T> implements MapperBuilder<S, T> {
return this;
}
public MapperBuilder<S, T> addMapping(Mapper<S, T> mapper) {
this.mapper.addMapping(mapper);
public MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper) {
this.mapper.addMapping(fields, mapper);
return this;
}

View File

@ -21,6 +21,11 @@ package org.springframework.mapping.support;
*/
interface SpelMapping {
/**
* Return true if this maps the source field.
*/
boolean mapsField(String field);
/**
* Execute this mapping.
* @param context the mapping context

View File

@ -291,18 +291,19 @@ public class MappingTests {
}
})
// multiple fields to field
.addMapping(new Mapper<CreateAccountDto, Account>() {
public Account map(CreateAccountDto source, Account target) {
DateTime dateTime = ISODateTimeFormat.dateTime().parseDateTime(
source.getActivationDate() + "T" + source.getActivationTime());
target.setActivationDateTime(dateTime);
return target;
}
}).getMapper();
.addMapping(new String[] { "activationDay", "activationTime " },
new Mapper<CreateAccountDto, Account>() {
public Account map(CreateAccountDto source, Account target) {
DateTime dateTime = ISODateTimeFormat.dateTime().parseDateTime(
source.getActivationDay() + "T" + source.getActivationTime());
target.setActivationDateTime(dateTime);
return target;
}
}).getMapper();
CreateAccountDto dto = new CreateAccountDto();
dto.setAccountNumber("123456789");
dto.setName("Keith Donald");
dto.setActivationDate("2009-10-12");
dto.setActivationDay("2009-10-12");
dto.setActivationTime("12:00:00.000Z");
dto.setAddress("2009BelAireEstates PalmBay FL 35452");
Account account = mapper.map(dto, new Account());
@ -500,7 +501,7 @@ public class MappingTests {
private String address;
private String activationDate;
private String activationDay;
private String activationTime;
@ -528,12 +529,12 @@ public class MappingTests {
this.address = address;
}
public String getActivationDate() {
return activationDate;
public String getActivationDay() {
return activationDay;
}
public void setActivationDate(String activationDate) {
this.activationDate = activationDate;
public void setActivationDay(String activationDay) {
this.activationDay = activationDay;
}
public String getActivationTime() {

View File

@ -1599,7 +1599,8 @@ public void testDefaultSpelMappingBehavior() {
<title>Registering Explicit Mappings</title>
<para>
When default mapping rules are not sufficient, explicit mapping rules can be registered by obtaining a <classname>MapperBuilder</classname> and using it to construct a <classname>Mapper</classname>.
Explicit mapping rules always override the default:
Explicit mapping rules always override the default.
The MapperBuilder provides a fluent API for registering object-to-object Mapping rules:
</para>
<programlisting language="java"><![CDATA[
Mapper<PersonDto, Person> mapper =
@ -1616,16 +1617,60 @@ Mapper<PersonDto, Person> mapper =
Since the two property names are not the same, default auto-mapping would never be performed.
Handle a situation like this by explicitly registering a mapping rule:
</para>
<programlisting language="java">builder.addMapping("name", "fullName")</programlisting>
<programlisting language="java"><![CDATA[
builder.addMapping("name", "fullName")]]>
</programlisting>
<para>
In this example, the <literal>name</literal> field will be mapped to the <literal>fullName</literal> field when the mapper is executed.
In the example above, the <literal>name</literal> field will be mapped to the <literal>fullName</literal> field when the mapper is executed.
No default mapping will be performed for <literal>name</literal> since an explicit mapping rule has been configured for this field.
</para>
</section>
<section id="mapping.SpelMapper-Explicit-singleFieldToMultipleField">
<title>Mapping a single field value to multiple fields</title>
<para>
Suppose you need to map <literal>PersonDto.name</literal> to <literal>Person.firstName</literal> and <literal>Person.lastName</literal>.
Handle a field-to-multi-field requirement like this by explicitly registering a mapping rule:
</para>
<programlisting language="java"><![CDATA[
builder.addMapping("name", new Mapper<String, Person>() {
public Person map(String name, Person person) {
String[] names = name.split(" ");
person.setFirstName(names[0]);
person.setLastName(names[1]);
return person;
}
});]]>
</programlisting>
<para>
In the example above, the first part of the <literal>name</literal> field will be mapped to the <literal>firstName</literal> field and the second part will be mapped to the <literal>lastName</literal> field.
No default mapping will be performed for <literal>name</literal> since an explicit mapping rule has been configured for this field.
</para>
</section>
<section id="mapping.SpelMapper-Explicit-multipleFieldsToField">
<title>Mapping a single field value to multiple fields</title>
<para>
Suppose you need to map <literal>CreateAccountDto.activationDay</literal> and <literal>CreateAccountDto.activationTime</literal> to <literal>Account.activationDateTime</literal>.
Handle a multi-field-to-field requirement like this by explicitly registering a mapping rule:
</para>
<programlisting language="java"><![CDATA[
builder.addMapping(new String[] { "activationDay", "activationTime" }, new Mapper<CreateAccountDto, AccountDto>() {
public Account map(CreateAccountDto dto, Account account) {
DateTime dateTime = ISODateTimeFormat.dateTime().parseDateTime(
dto.getActivationDay() + "T" + dto.getActivationTime());
account.setActivationDateTime(dateTime);
return account;
}
});]]>
</programlisting>
<para>
In the example above, the <literal>activationDay</literal> and <literal>activationTime</literal> fields are mapped to the single <literal>activationDateTime</literal> field.
No default mapping is performed for <literal>activationDay</literal> and <literal>activationTime</literal> since an explicit mapping rule has been configured for these fields.
</para>
</section>
<section id="mapping.SpelMapper-Explicit-forcing">
<title>Forcing Explicit Mappings</title>
<para>
You can require that all mapping rules must be defined explicitly by disabling the "auto mapping" feature:
You can require that all mapping rules be defined explicitly by disabling the "auto mapping" feature:
</para>
<programlisting language="java"><![CDATA[
builder.setAutoMappingEnabled(false);]]>
@ -1651,14 +1696,14 @@ builder.addMapping("name", "fullName").setConverter() { new Converter<String, St
<title>Ignoring Fields</title>
<para>
Sometimes you need to exclude a specific field on a source object from being mapped.
Do this by marking a mapping as excluded:
Do this by marking one or more source fields as excluded:
</para>
<programlisting language="java">builder.setExcludedFields("name");</programlisting>
</section>
<section id="mapper.SpelMapper-CustomTypeConverters">
<title>Registering Custom Type Converters</title>
<para>
You can also install Converters to convert values of different types in a custom way:
You may also install Converters to convert values of different types in a custom way:
</para>
<programlisting language="java"><![CDATA[
builder.addConverter(new Converter<String, Date>() {
@ -1675,7 +1720,7 @@ builder.addConverter(new Converter<String, Date>() {
<section id="mapper.SpelMapper-CustomNestedMappers">
<title>Registering Custom Nested Mappers</title>
<para>
When mapping between two large object graphs, you may need to register explicit mapping rules for nested bean properties.
When mapping between two object graphs, you may find you need to register explicit mapping rules for nested bean properties.
Do this by adding a nested Mapper:
</para>
<programlisting language="java"><![CDATA[
@ -1687,7 +1732,7 @@ builder.addNestedMapper(new Mapper<AddressDto, Address>() {
});]]>
</programlisting>
<para>
The example above registers a nested Mapper that will map nested AddressDto properties to nested Address properties.
The example Mapper above will map nested AddressDto properties to nested Address properties.
This particular nested Mapper is "hand-coded", but it could have easily been another Mapper instance built by a MapperBuilder.
</para>
</section>