Skip to content

Commit e7c8f1e

Browse files
author
Keith Donald
committed
SPR-6179, additional mapper test cases
1 parent 6ea83af commit e7c8f1e

File tree

9 files changed

+234
-106
lines changed

9 files changed

+234
-106
lines changed

org.springframework.context/src/main/java/org/springframework/mapping/Mapper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ public interface Mapper<S, T> {
2727
* Map the source to the target.
2828
* @param source the source to map from
2929
* @param target the target to map to
30+
* @return the mapped target object
3031
* @throws MappingException if the mapping process failed
3132
*/
32-
void map(S source, T target);
33+
Object map(S source, T target);
3334

3435
}

org.springframework.context/src/main/java/org/springframework/mapping/MappingException.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,12 @@ public String getMessage() {
6060
public void printStackTrace(PrintStream ps) {
6161
super.printStackTrace(ps);
6262
synchronized (ps) {
63-
ps.println("Failure cause traces:");
63+
ps.println();
64+
ps.println("Mapping Failure Traces:");
6465
int i = 1;
6566
for (Iterator<MappingFailure> it = this.mappingFailures.iterator(); it.hasNext(); i++) {
6667
MappingFailure failure = it.next();
67-
ps.println("- MappingFailure #" + i + " Cause: ");
68+
ps.println("- MappingFailure #" + i + ":");
6869
Throwable t = failure.getCause();
6970
if (t != null) {
7071
t.printStackTrace(ps);
@@ -79,11 +80,12 @@ public void printStackTrace(PrintStream ps) {
7980
public void printStackTrace(PrintWriter pw) {
8081
super.printStackTrace(pw);
8182
synchronized (pw) {
82-
pw.println("Failure cause traces:");
83+
pw.println();
84+
pw.println("Mapping Failure Traces:");
8385
int i = 1;
8486
for (Iterator<MappingFailure> it = this.mappingFailures.iterator(); it.hasNext(); i++) {
8587
MappingFailure failure = it.next();
86-
pw.println("- MappingFailure #" + i + " Cause: ");
88+
pw.println("- MappingFailure #" + i + ":");
8789
Throwable t = failure.getCause();
8890
if (t != null) {
8991
t.printStackTrace(pw);

org.springframework.context/src/main/java/org/springframework/mapping/support/SpelMapper.java

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.core.convert.converter.ConverterRegistry;
2626
import org.springframework.core.convert.support.DefaultConversionService;
2727
import org.springframework.expression.EvaluationContext;
28+
import org.springframework.expression.EvaluationException;
2829
import org.springframework.expression.Expression;
2930
import org.springframework.expression.ParseException;
3031
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -60,61 +61,61 @@ public void setAutoMappingEnabled(boolean autoMappingEnabled) {
6061
this.autoMappingEnabled = autoMappingEnabled;
6162
}
6263

63-
public MappingConfiguration addMapping(String expression) {
64-
return addMapping(expression, expression);
64+
public MappingConfiguration addMapping(String fieldExpression) {
65+
return addMapping(fieldExpression, fieldExpression);
6566
}
6667

6768
public ConverterRegistry getConverterRegistry() {
6869
return conversionService;
6970
}
7071

71-
public MappingConfiguration addMapping(String sourceExpression, String targetExpression) {
72+
public MappingConfiguration addMapping(String sourceFieldExpression, String targetFieldExpression) {
7273
Expression sourceExp;
7374
try {
74-
sourceExp = sourceExpressionParser.parseExpression(sourceExpression);
75+
sourceExp = sourceExpressionParser.parseExpression(sourceFieldExpression);
7576
} catch (ParseException e) {
76-
throw new IllegalArgumentException("The mapping source '" + sourceExpression
77+
throw new IllegalArgumentException("The mapping source '" + sourceFieldExpression
7778
+ "' is not a parseable value expression", e);
7879
}
7980
Expression targetExp;
8081
try {
81-
targetExp = targetExpressionParser.parseExpression(targetExpression);
82+
targetExp = targetExpressionParser.parseExpression(targetFieldExpression);
8283
} catch (ParseException e) {
83-
throw new IllegalArgumentException("The mapping target '" + targetExpression
84+
throw new IllegalArgumentException("The mapping target '" + targetFieldExpression
8485
+ "' is not a parseable property expression", e);
8586
}
8687
Mapping mapping = new Mapping(sourceExp, targetExp);
8788
this.mappings.add(mapping);
8889
return mapping;
8990
}
9091

91-
public void map(Object source, Object target) {
92+
public Object map(Object source, Object target) {
9293
EvaluationContext sourceContext = getMappingContext(source);
9394
EvaluationContext targetContext = getMappingContext(target);
9495
List<MappingFailure> failures = new LinkedList<MappingFailure>();
9596
for (Mapping mapping : this.mappings) {
9697
mapping.map(sourceContext, targetContext, failures);
9798
}
98-
Set<Mapping> autoMappings = getAutoMappings(source, target);
99+
Set<Mapping> autoMappings = getAutoMappings(sourceContext, targetContext);
99100
for (Mapping mapping : autoMappings) {
100101
mapping.map(sourceContext, targetContext, failures);
101102
}
102103
if (!failures.isEmpty()) {
103104
throw new MappingException(failures);
104105
}
106+
return target;
105107
}
106108

107109
private EvaluationContext getMappingContext(Object object) {
108110
return mappableTypeFactory.getMappableType(object).getEvaluationContext(object, this.conversionService);
109111
}
110112

111-
private Set<Mapping> getAutoMappings(Object source, Object target) {
113+
private Set<Mapping> getAutoMappings(EvaluationContext sourceContext, EvaluationContext targetContext) {
112114
if (this.autoMappingEnabled) {
113115
Set<Mapping> autoMappings = new LinkedHashSet<Mapping>();
114-
Set<String> sourceFields = getMappableFields(source);
115-
Set<String> targetFields = getMappableFields(target);
116+
Set<String> sourceFields = getMappableFields(sourceContext.getRootObject().getValue());
116117
for (String field : sourceFields) {
117-
if (!explicitlyMapped(field) && targetFields.contains(field)) {
118+
if (!explicitlyMapped(field)) {
118119
Expression sourceExpression;
119120
Expression targetExpression;
120121
try {
@@ -129,8 +130,13 @@ private Set<Mapping> getAutoMappings(Object source, Object target) {
129130
throw new IllegalArgumentException("The mapping target '" + field
130131
+ "' is not a parseable value expression", e);
131132
}
132-
Mapping mapping = new Mapping(sourceExpression, targetExpression);
133-
autoMappings.add(mapping);
133+
try {
134+
if (targetExpression.isWritable(targetContext)) {
135+
autoMappings.add(new Mapping(sourceExpression, targetExpression));
136+
}
137+
} catch (EvaluationException e) {
138+
139+
}
134140
}
135141
}
136142
return autoMappings;
@@ -145,7 +151,7 @@ private Set<String> getMappableFields(Object object) {
145151

146152
private boolean explicitlyMapped(String field) {
147153
for (Mapping mapping : this.mappings) {
148-
if (mapping.getSourceExpressionString().equals(field)) {
154+
if (mapping.getSourceExpressionString().startsWith(field)) {
149155
return true;
150156
}
151157
}

org.springframework.context/src/test/java/org/springframework/mapping/support/SpelMapperTests.java

Lines changed: 91 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.springframework.mapping.support;
22

33
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNull;
45

56
import java.util.ArrayList;
67
import java.util.HashMap;
@@ -31,15 +32,14 @@ public void mapAutomatic() {
3132

3233
@Test
3334
public void mapExplicit() throws MappingException {
34-
mapper.setAutoMappingEnabled(false);
35-
mapper.addMapping("name");
36-
3735
Map<String, Object> source = new HashMap<String, Object>();
3836
source.put("name", "Keith");
3937
source.put("age", 31);
4038

4139
Person target = new Person();
4240

41+
mapper.setAutoMappingEnabled(false);
42+
mapper.addMapping("name");
4343
mapper.map(source, target);
4444

4545
assertEquals("Keith", target.name);
@@ -48,15 +48,14 @@ public void mapExplicit() throws MappingException {
4848

4949
@Test
5050
public void mapAutomaticWithExplictOverrides() {
51-
mapper.addMapping("test", "age");
52-
5351
Map<String, Object> source = new HashMap<String, Object>();
5452
source.put("name", "Keith");
5553
source.put("test", "3");
5654
source.put("favoriteSport", "FOOTBALL");
5755

5856
Person target = new Person();
5957

58+
mapper.addMapping("test", "age");
6059
mapper.map(source, target);
6160

6261
assertEquals("Keith", target.name);
@@ -65,15 +64,29 @@ public void mapAutomaticWithExplictOverrides() {
6564
}
6665

6766
@Test
68-
public void mapSameSourceFieldToMultipleTargets() {
69-
mapper.addMapping("test", "name");
70-
mapper.addMapping("test", "favoriteSport");
67+
public void mapAutomaticIgnoreUnknownField() {
68+
Map<String, Object> source = new HashMap<String, Object>();
69+
source.put("name", "Keith");
70+
source.put("age", 31);
71+
source.put("unknown", "foo");
7172

73+
Person target = new Person();
74+
75+
mapper.map(source, target);
76+
77+
assertEquals("Keith", target.name);
78+
assertEquals(31, target.age);
79+
}
80+
81+
@Test
82+
public void mapSameSourceFieldToMultipleTargets() {
7283
Map<String, Object> source = new HashMap<String, Object>();
7384
source.put("test", "FOOTBALL");
7485

7586
Person target = new Person();
7687

88+
mapper.addMapping("test", "name");
89+
mapper.addMapping("test", "favoriteSport");
7790
mapper.map(source, target);
7891

7992
assertEquals("FOOTBALL", target.name);
@@ -92,32 +105,53 @@ public void mapBean() {
92105

93106
mapper.addMapping("fullName", "name");
94107
mapper.addMapping("sport", "favoriteSport");
95-
96108
mapper.map(source, target);
97109

98110
assertEquals("Keith Donald", target.name);
99111
assertEquals(31, target.age);
100112
assertEquals(Sport.FOOTBALL, target.favoriteSport);
101113
}
102114

115+
@Test
116+
public void mapBeanDeep() {
117+
PersonDto source = new PersonDto();
118+
source.age = "0";
119+
NestedDto nested = new NestedDto();
120+
nested.foo = "bar";
121+
source.setNested(nested);
122+
123+
Person target = new Person();
124+
125+
mapper.addMapping("nested.foo", "nested.foo");
126+
mapper.map(source, target);
127+
128+
assertEquals("bar", target.nested.foo);
129+
}
130+
103131
@Test
104132
public void mapBeanNested() {
105133
PersonDto source = new PersonDto();
106134
source.setFullName("Keith Donald");
107135
source.setAge("31");
108136
source.setSport("FOOTBALL");
137+
NestedDto nested = new NestedDto();
138+
nested.foo = "bar";
139+
source.setNested(nested);
109140

110141
Person target = new Person();
111142

112-
mapper.addMapping("fullName", "nested.fullName");
113-
mapper.addMapping("age", "nested.age");
114-
mapper.addMapping("sport", "nested.sport");
115-
143+
mapper.addMapping("fullName", "name");
144+
mapper.addMapping("sport", "favoriteSport");
145+
mapper.addMapping("nested", "nested").setConverter(new Converter<NestedDto, Nested>() {
146+
public Nested convert(NestedDto source) {
147+
return (Nested) new SpelMapper().map(source, new Nested());
148+
}
149+
});
116150
mapper.map(source, target);
117151

118-
assertEquals("Keith Donald", target.getNested().getFullName());
119-
assertEquals("31", target.nested.age);
120-
assertEquals("FOOTBALL", target.nested.sport);
152+
assertEquals("Keith Donald", target.name);
153+
assertEquals(31, target.age);
154+
assertEquals(Sport.FOOTBALL, target.favoriteSport);
121155
}
122156

123157
@Test
@@ -132,13 +166,30 @@ public void mapList() {
132166

133167
mapper.setAutoMappingEnabled(false);
134168
mapper.addMapping("sports", "favoriteSports");
135-
136169
mapper.map(source, target);
137170

138171
assertEquals(Sport.FOOTBALL, target.favoriteSports.get(0));
139172
assertEquals(Sport.BASKETBALL, target.favoriteSports.get(1));
140173
}
141174

175+
@Test
176+
public void mapListFlatten() {
177+
PersonDto source = new PersonDto();
178+
List<String> sports = new ArrayList<String>();
179+
sports.add("FOOTBALL");
180+
sports.add("BASKETBALL");
181+
source.setSports(sports);
182+
183+
Person target = new Person();
184+
185+
mapper.setAutoMappingEnabled(false);
186+
mapper.addMapping("sports[0]", "favoriteSport");
187+
mapper.map(source, target);
188+
189+
assertEquals(Sport.FOOTBALL, target.favoriteSport);
190+
assertNull(target.favoriteSports);
191+
}
192+
142193
@Test
143194
public void mapMap() {
144195
PersonDto source = new PersonDto();
@@ -187,7 +238,7 @@ public String convert(String source) {
187238
public void mapFailure() {
188239
Map<String, Object> source = new HashMap<String, Object>();
189240
source.put("name", "Keith");
190-
source.put("age", "bogus");
241+
source.put("age", "invalid");
191242
Person target = new Person();
192243
try {
193244
mapper.map(source, target);
@@ -208,7 +259,7 @@ public static class PersonDto {
208259

209260
private Map<String, String> friendRankings;
210261

211-
private NestedDto nestedDto;
262+
private NestedDto nested;
212263

213264
public String getFullName() {
214265
return fullName;
@@ -250,12 +301,12 @@ public void setFriendRankings(Map<String, String> friendRankings) {
250301
this.friendRankings = friendRankings;
251302
}
252303

253-
public NestedDto getNestedDto() {
254-
return nestedDto;
304+
public NestedDto getNested() {
305+
return nested;
255306
}
256307

257-
public void setNestedDto(NestedDto nestedDto) {
258-
this.nestedDto = nestedDto;
308+
public void setNested(NestedDto nested) {
309+
this.nested = nested;
259310
}
260311

261312
}
@@ -277,7 +328,7 @@ public static class Person {
277328

278329
private Sport favoriteSport;
279330

280-
private PersonDto nested;
331+
private Nested nested;
281332

282333
// private Person cyclic;
283334

@@ -317,11 +368,11 @@ public void setFavoriteSport(Sport favoriteSport) {
317368
this.favoriteSport = favoriteSport;
318369
}
319370

320-
public PersonDto getNested() {
371+
public Nested getNested() {
321372
return nested;
322373
}
323374

324-
public void setNested(PersonDto nested) {
375+
public void setNested(Nested nested) {
325376
this.nested = nested;
326377
}
327378

@@ -364,6 +415,20 @@ public boolean equals(Object o) {
364415
}
365416
}
366417

418+
public static class Nested {
419+
420+
private String foo;
421+
422+
public String getFoo() {
423+
return foo;
424+
}
425+
426+
public void setFoo(String foo) {
427+
this.foo = foo;
428+
}
429+
430+
}
431+
367432
public enum Sport {
368433
FOOTBALL, BASKETBALL
369434
}

0 commit comments

Comments
 (0)