Browse Source

mapper initial commit

pull/23217/head
Keith Donald 15 years ago
parent
commit
341835a142
  1. 2
      org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java
  2. 34
      org.springframework.context/src/main/java/org/springframework/mapping/Mapper.java
  3. 97
      org.springframework.context/src/main/java/org/springframework/mapping/MappingException.java
  4. 52
      org.springframework.context/src/main/java/org/springframework/mapping/MappingFailure.java
  5. 49
      org.springframework.context/src/main/java/org/springframework/mapping/support/BeanMappableType.java
  6. 48
      org.springframework.context/src/main/java/org/springframework/mapping/support/MapMappableType.java
  7. 40
      org.springframework.context/src/main/java/org/springframework/mapping/support/MappableType.java
  8. 94
      org.springframework.context/src/main/java/org/springframework/mapping/support/Mapping.java
  9. 53
      org.springframework.context/src/main/java/org/springframework/mapping/support/MappingConfiguration.java
  10. 170
      org.springframework.context/src/main/java/org/springframework/mapping/support/SpelMapper.java
  11. 370
      org.springframework.context/src/test/java/org/springframework/mapping/support/SpelMapperTests.java
  12. 25
      org.springframework.core/src/main/java/org/springframework/core/convert/support/ConverterFactoryGenericConverter.java
  13. 37
      org.springframework.core/src/main/java/org/springframework/core/convert/support/ConverterGenericConverter.java
  14. 42
      org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java

2
org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java

@ -572,7 +572,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra @@ -572,7 +572,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
}
} catch (InstantiationException e) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, "Could not instantiate propertyType [" + type.getName() + "] to auto-grow nestd property path");
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, "Could not instantiate propertyType [" + type.getName() + "] to auto-grow nested property path");
} catch (IllegalAccessException e) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, "Could not instantiate propertyType [" + type.getName() + "] to auto-grow nested property path");
}

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

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
/*
* 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;
/**
* Maps between a source and target.
* @author Keith Donald
* @param <S> the source type mapped from
* @param <T> the target type mapped to
*/
public interface Mapper<S, T> {
/**
* Map the source to the target.
* @param source the source to map from
* @param target the target to map to
* @throws MappingException if the mapping process failed
*/
void map(S source, T target);
}

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

@ -0,0 +1,97 @@ @@ -0,0 +1,97 @@
/*
* 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;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
/**
* Thrown in a map operation fails.
* @see Mapper#map(Object, Object)
* @author Keith Donald
*/
public class MappingException extends RuntimeException {
private List<MappingFailure> mappingFailures;
public MappingException(List<MappingFailure> mappingFailures) {
super((String) null);
this.mappingFailures = mappingFailures;
}
public int getMappingFailureCount() {
return this.mappingFailures.size();
}
public List<MappingFailure> getMappingFailures() {
return this.mappingFailures;
}
@Override
public String getMessage() {
StringBuilder sb = new StringBuilder(getMappingFailureCount() + " mapping failure(s) occurred:");
int i = 1;
for (Iterator<MappingFailure> it = this.mappingFailures.iterator(); it.hasNext(); i++) {
MappingFailure failure = it.next();
sb.append(" #").append(i + ") ").append(failure.getMessage());
if (it.hasNext()) {
sb.append(",");
}
}
return sb.toString();
}
@Override
public void printStackTrace(PrintStream ps) {
super.printStackTrace(ps);
synchronized (ps) {
ps.println("Failure cause traces:");
int i = 1;
for (Iterator<MappingFailure> it = this.mappingFailures.iterator(); it.hasNext(); i++) {
MappingFailure failure = it.next();
ps.println("- MappingFailure #" + i + " Cause: ");
Throwable t = failure.getCause();
if (t != null) {
t.printStackTrace(ps);
} else {
ps.println("null");
}
}
}
}
@Override
public void printStackTrace(PrintWriter pw) {
super.printStackTrace(pw);
synchronized (pw) {
pw.println("Failure cause traces:");
int i = 1;
for (Iterator<MappingFailure> it = this.mappingFailures.iterator(); it.hasNext(); i++) {
MappingFailure failure = it.next();
pw.println("- MappingFailure #" + i + " Cause: ");
Throwable t = failure.getCause();
if (t != null) {
t.printStackTrace(pw);
} else {
pw.println("null");
}
}
}
}
}

52
org.springframework.context/src/main/java/org/springframework/mapping/MappingFailure.java

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
/*
* 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;
/**
* Indicates an individual mapping failed.
* @author Keith Donald
*/
public class MappingFailure {
private final Throwable cause;
/**
* Create a new mapping failure caused by the exception.
* @param cause the failure cause
*/
public MappingFailure(Throwable cause) {
this.cause = cause;
}
/**
* The failure message.
*/
public String getMessage() {
return getCause().getMessage();
}
/**
* The cause of this failure.
*/
public Throwable getCause() {
return cause;
}
public String toString() {
return "[MappingFailure cause = " + cause + "]";
}
}

49
org.springframework.context/src/main/java/org/springframework/mapping/support/BeanMappableType.java

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
/*
* 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.beans.PropertyDescriptor;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.beans.BeanUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter;
class BeanMappableType implements MappableType<Object> {
public Set<String> getFields(Object object) {
Set<String> fields = new LinkedHashSet<String>();
PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(object.getClass());
for (PropertyDescriptor descriptor : descriptors) {
String propertyName = descriptor.getName();
if (propertyName.equals("class")) {
continue;
}
fields.add(descriptor.getName());
}
return fields;
}
public EvaluationContext getEvaluationContext(Object instance, ConversionService conversionService) {
StandardEvaluationContext context = new StandardEvaluationContext(instance);
context.setTypeConverter(new StandardTypeConverter(conversionService));
return context;
}
}

48
org.springframework.context/src/main/java/org/springframework/mapping/support/MapMappableType.java

@ -0,0 +1,48 @@ @@ -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.Map;
import java.util.Set;
import org.springframework.context.expression.MapAccessor;
import org.springframework.core.convert.ConversionService;
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>> {
public Set<String> getFields(Map<? extends Object, ? extends Object> object) {
LinkedHashSet<String> fields = new LinkedHashSet<String>(object.size(), 1);
for (Object key : object.keySet()) {
if (key != null && key instanceof String) {
fields.add((String) key);
}
}
return fields;
}
public EvaluationContext getEvaluationContext(Map<? extends Object, ? extends Object> object,
ConversionService conversionService) {
StandardEvaluationContext context = new StandardEvaluationContext(object);
context.setTypeConverter(new StandardTypeConverter(conversionService));
context.addPropertyAccessor(new MapAccessor());
return context;
}
}

40
org.springframework.context/src/main/java/org/springframework/mapping/support/MappableType.java

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
/*
* 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.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.EvaluationContext;
/**
* Encapsulates mapping context for a type of object.
* @param <T> the object type
* @author Keith Donald
*/
interface MappableType<T> {
/**
* The fields of the object that are eligible for mapping, including any nested fields.
*/
Set<String> getFields(T object);
/**
* A evaluation context for accessing the object.
*/
EvaluationContext getEvaluationContext(T object, ConversionService conversionService);
}

94
org.springframework.context/src/main/java/org/springframework/mapping/support/Mapping.java

@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
/*
* 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.Collection;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.support.ConverterFactoryGenericConverter;
import org.springframework.core.convert.support.ConverterGenericConverter;
import org.springframework.core.convert.support.GenericConverter;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.mapping.MappingFailure;
class Mapping implements MappingConfiguration {
private Expression source;
private Expression target;
private GenericConverter converter;
public Mapping(Expression source, Expression target) {
this.source = source;
this.target = target;
}
public String getSourceExpressionString() {
return this.source.getExpressionString();
}
public String getTargetExpressionString() {
return this.target.getExpressionString();
}
public MappingConfiguration setConverter(Converter<?, ?> converter) {
return setGenericConverter(new ConverterGenericConverter(converter));
}
public MappingConfiguration setConverterFactory(ConverterFactory<?, ?> converter) {
return setGenericConverter(new ConverterFactoryGenericConverter(converter));
}
public MappingConfiguration setGenericConverter(GenericConverter converter) {
this.converter = converter;
return this;
}
public void map(EvaluationContext sourceContext, EvaluationContext targetContext,
Collection<MappingFailure> failures) {
try {
Object value = this.source.getValue(sourceContext);
if (this.converter != null) {
value = this.converter.convert(value, this.source.getValueTypeDescriptor(sourceContext), this.target
.getValueTypeDescriptor(targetContext));
}
this.target.setValue(targetContext, value);
} catch (Exception e) {
failures.add(new MappingFailure(e));
}
}
public int hashCode() {
return getSourceExpressionString().hashCode() + getTargetExpressionString().hashCode();
}
public boolean equals(Object o) {
if (!(o instanceof Mapping)) {
return false;
}
Mapping m = (Mapping) o;
return getSourceExpressionString().equals(m.getSourceExpressionString())
&& getTargetExpressionString().equals(m.getTargetExpressionString());
}
public String toString() {
return "[Mapping<" + getSourceExpressionString() + " -> " + getTargetExpressionString() + ">]";
}
}

53
org.springframework.context/src/main/java/org/springframework/mapping/support/MappingConfiguration.java

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
/*
* 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.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.support.GenericConverter;
/**
* A fluent API for configuring a mapping.
* @see SpelMapper#addMapping(String)
* @see SpelMapper#addMapping(String, String)
* @author Keith Donald
*/
public interface MappingConfiguration {
/**
* Set the type converter to use during this mapping.
* @param converter the converter
* @return this, for call chaining
*/
MappingConfiguration setConverter(Converter<?, ?> converter);
/**
* Set the type converter factory to use during this mapping.
* @param converter the converter factory
* @return this, for call chaining
*/
MappingConfiguration setConverterFactory(ConverterFactory<?, ?> converterFactory);
/**
* Set the generic converter to use during this mapping.
* A generic converter allows access to source and target field type descriptors.
* These descriptors provide additional context that can be used during type conversion.
* @param converter the generic converter
* @return this, for call chaining
*/
MappingConfiguration setGenericConverter(GenericConverter converter);
}

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

@ -0,0 +1,170 @@ @@ -0,0 +1,170 @@
/*
* 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.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParserConfiguration;
import org.springframework.mapping.Mapper;
import org.springframework.mapping.MappingException;
import org.springframework.mapping.MappingFailure;
/**
* A generic object mapper implementation based on the Spring Expression Language (SpEL).
* @author Keith Donald
* @see #setAutoMappingEnabled(boolean)
* @see #addMapping(String)
* @see #addMapping(String, String)
*/
public class SpelMapper implements Mapper<Object, Object> {
private static final MappableTypeFactory mappableTypeFactory = new MappableTypeFactory();
private static final SpelExpressionParser sourceExpressionParser = new SpelExpressionParser();
private static final SpelExpressionParser targetExpressionParser = new SpelExpressionParser(
SpelExpressionParserConfiguration.CreateObjectIfAttemptToReferenceNull
| SpelExpressionParserConfiguration.GrowListsOnIndexBeyondSize);
private final Set<Mapping> mappings = new LinkedHashSet<Mapping>();
private boolean autoMappingEnabled = true;
private final DefaultConversionService conversionService = new DefaultConversionService();
public void setAutoMappingEnabled(boolean autoMappingEnabled) {
this.autoMappingEnabled = autoMappingEnabled;
}
public MappingConfiguration addMapping(String expression) {
return addMapping(expression, expression);
}
public ConverterRegistry getConverterRegistry() {
return conversionService;
}
public MappingConfiguration addMapping(String sourceExpression, String targetExpression) {
Expression sourceExp;
try {
sourceExp = sourceExpressionParser.parseExpression(sourceExpression);
} catch (ParseException e) {
throw new IllegalArgumentException("The mapping source '" + sourceExpression
+ "' is not a parseable value expression", e);
}
Expression targetExp;
try {
targetExp = targetExpressionParser.parseExpression(targetExpression);
} catch (ParseException e) {
throw new IllegalArgumentException("The mapping target '" + targetExpression
+ "' is not a parseable property expression", e);
}
Mapping mapping = new Mapping(sourceExp, targetExp);
this.mappings.add(mapping);
return mapping;
}
public void map(Object source, Object target) {
EvaluationContext sourceContext = getMappingContext(source);
EvaluationContext targetContext = getMappingContext(target);
List<MappingFailure> failures = new LinkedList<MappingFailure>();
for (Mapping mapping : this.mappings) {
mapping.map(sourceContext, targetContext, failures);
}
Set<Mapping> autoMappings = getAutoMappings(source, target);
for (Mapping mapping : autoMappings) {
mapping.map(sourceContext, targetContext, failures);
}
if (!failures.isEmpty()) {
throw new MappingException(failures);
}
}
private EvaluationContext getMappingContext(Object object) {
return mappableTypeFactory.getMappableType(object).getEvaluationContext(object, this.conversionService);
}
private Set<Mapping> getAutoMappings(Object source, Object target) {
if (this.autoMappingEnabled) {
Set<Mapping> autoMappings = new LinkedHashSet<Mapping>();
Set<String> sourceFields = getMappableFields(source);
Set<String> targetFields = getMappableFields(target);
for (String field : sourceFields) {
if (!explicitlyMapped(field) && targetFields.contains(field)) {
Expression sourceExpression;
Expression targetExpression;
try {
sourceExpression = sourceExpressionParser.parseExpression(field);
} catch (ParseException e) {
throw new IllegalArgumentException("The mapping source '" + field
+ "' is not a parseable value expression", e);
}
try {
targetExpression = targetExpressionParser.parseExpression(field);
} catch (ParseException e) {
throw new IllegalArgumentException("The mapping target '" + field
+ "' is not a parseable value expression", e);
}
Mapping mapping = new Mapping(sourceExpression, targetExpression);
autoMappings.add(mapping);
}
}
return autoMappings;
} else {
return Collections.emptySet();
}
}
private Set<String> getMappableFields(Object object) {
return mappableTypeFactory.getMappableType(object).getFields(object);
}
private boolean explicitlyMapped(String field) {
for (Mapping mapping : this.mappings) {
if (mapping.getSourceExpressionString().equals(field)) {
return true;
}
}
return false;
}
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;
}
}
}
}

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

@ -0,0 +1,370 @@ @@ -0,0 +1,370 @@
package org.springframework.mapping.support;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.core.convert.converter.Converter;
import org.springframework.mapping.MappingException;
public class SpelMapperTests {
private SpelMapper mapper = new SpelMapper();
@Test
public void mapAutomatic() {
Map<String, Object> source = new HashMap<String, Object>();
source.put("name", "Keith");
source.put("age", 31);
Person target = new Person();
mapper.map(source, target);
assertEquals("Keith", target.name);
assertEquals(31, target.age);
}
@Test
public void mapExplicit() throws MappingException {
mapper.setAutoMappingEnabled(false);
mapper.addMapping("name");
Map<String, Object> source = new HashMap<String, Object>();
source.put("name", "Keith");
source.put("age", 31);
Person target = new Person();
mapper.map(source, target);
assertEquals("Keith", target.name);
assertEquals(0, target.age);
}
@Test
public void mapAutomaticWithExplictOverrides() {
mapper.addMapping("test", "age");
Map<String, Object> source = new HashMap<String, Object>();
source.put("name", "Keith");
source.put("test", "3");
source.put("favoriteSport", "FOOTBALL");
Person target = new Person();
mapper.map(source, target);
assertEquals("Keith", target.name);
assertEquals(3, target.age);
assertEquals(Sport.FOOTBALL, target.favoriteSport);
}
@Test
public void mapSameSourceFieldToMultipleTargets() {
mapper.addMapping("test", "name");
mapper.addMapping("test", "favoriteSport");
Map<String, Object> source = new HashMap<String, Object>();
source.put("test", "FOOTBALL");
Person target = new Person();
mapper.map(source, target);
assertEquals("FOOTBALL", target.name);
assertEquals(0, target.age);
assertEquals(Sport.FOOTBALL, target.favoriteSport);
}
@Test
public void mapBean() {
PersonDto source = new PersonDto();
source.setFullName("Keith Donald");
source.setAge("31");
source.setSport("FOOTBALL");
Person target = new Person();
mapper.addMapping("fullName", "name");
mapper.addMapping("sport", "favoriteSport");
mapper.map(source, target);
assertEquals("Keith Donald", target.name);
assertEquals(31, target.age);
assertEquals(Sport.FOOTBALL, target.favoriteSport);
}
@Test
public void mapBeanNested() {
PersonDto source = new PersonDto();
source.setFullName("Keith Donald");
source.setAge("31");
source.setSport("FOOTBALL");
Person target = new Person();
mapper.addMapping("fullName", "nested.fullName");
mapper.addMapping("age", "nested.age");
mapper.addMapping("sport", "nested.sport");
mapper.map(source, target);
assertEquals("Keith Donald", target.getNested().getFullName());
assertEquals("31", target.nested.age);
assertEquals("FOOTBALL", target.nested.sport);
}
@Test
public void mapList() {
PersonDto source = new PersonDto();
List<String> sports = new ArrayList<String>();
sports.add("FOOTBALL");
sports.add("BASKETBALL");
source.setSports(sports);
Person target = new Person();
mapper.setAutoMappingEnabled(false);
mapper.addMapping("sports", "favoriteSports");
mapper.map(source, target);
assertEquals(Sport.FOOTBALL, target.favoriteSports.get(0));
assertEquals(Sport.BASKETBALL, target.favoriteSports.get(1));
}
@Test
public void mapMap() {
PersonDto source = new PersonDto();
Map<String, String> friendRankings = new HashMap<String, String>();
friendRankings.put("Keri", "1");
friendRankings.put("Alf", "2");
source.setFriendRankings(friendRankings);
Person target = new Person();
mapper.setAutoMappingEnabled(false);
mapper.addMapping("friendRankings", "friendRankings");
mapper.getConverterRegistry().addConverter(new Converter<String, Person>() {
public Person convert(String source) {
return new Person(source);
}
});
mapper.map(source, target);
assertEquals(new Integer(1), target.friendRankings.get(new Person("Keri")));
assertEquals(new Integer(2), target.friendRankings.get(new Person("Alf")));
}
@Test
public void mapFieldConverter() {
Map<String, Object> source = new HashMap<String, Object>();
source.put("name", "Keith Donald");
source.put("age", 31);
Person target = new Person();
mapper.addMapping("name").setConverter(new Converter<String, String>() {
public String convert(String source) {
String[] names = source.split(" ");
return names[0] + " P. " + names[1];
}
});
mapper.map(source, target);
assertEquals("Keith P. Donald", target.name);
assertEquals(31, target.age);
}
@Test
public void mapFailure() {
Map<String, Object> source = new HashMap<String, Object>();
source.put("name", "Keith");
source.put("age", "bogus");
Person target = new Person();
try {
mapper.map(source, target);
} catch (MappingException e) {
assertEquals(1, e.getMappingFailureCount());
}
}
public static class PersonDto {
private String fullName;
private String age;
private String sport;
private List<String> sports;
private Map<String, String> friendRankings;
private NestedDto nestedDto;
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSport() {
return sport;
}
public void setSport(String sport) {
this.sport = sport;
}
public List<String> getSports() {
return sports;
}
public void setSports(List<String> sports) {
this.sports = sports;
}
public Map<String, String> getFriendRankings() {
return friendRankings;
}
public void setFriendRankings(Map<String, String> friendRankings) {
this.friendRankings = friendRankings;
}
public NestedDto getNestedDto() {
return nestedDto;
}
public void setNestedDto(NestedDto nestedDto) {
this.nestedDto = nestedDto;
}
}
public static class NestedDto {
private String foo;
public String getFoo() {
return foo;
}
}
public static class Person {
private String name;
private int age;
private Sport favoriteSport;
private PersonDto nested;
// private Person cyclic;
private List<Sport> favoriteSports;
private Map<Person, Integer> friendRankings;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Sport getFavoriteSport() {
return favoriteSport;
}
public void setFavoriteSport(Sport favoriteSport) {
this.favoriteSport = favoriteSport;
}
public PersonDto getNested() {
return nested;
}
public void setNested(PersonDto nested) {
this.nested = nested;
}
/*
public Person getCyclic() {
return cyclic;
}
public void setCyclic(Person cyclic) {
this.cyclic = cyclic;
}
*/
public List<Sport> getFavoriteSports() {
return favoriteSports;
}
public void setFavoriteSports(List<Sport> favoriteSports) {
this.favoriteSports = favoriteSports;
}
public Map<Person, Integer> getFriendRankings() {
return friendRankings;
}
public void setFriendRankings(Map<Person, Integer> friendRankings) {
this.friendRankings = friendRankings;
}
public int hashCode() {
return name.hashCode();
}
public boolean equals(Object o) {
if (!(o instanceof Person)) {
return false;
}
Person p = (Person) o;
return name.equals(p.name);
}
}
public enum Sport {
FOOTBALL, BASKETBALL
}
}

25
org.springframework.core/src/main/java/org/springframework/core/convert/support/ConverterFactoryGenericConverter.java

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
/**
*
*/
package org.springframework.core.convert.support;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConverterFactory;
/**
* Adapts a ConverterFactory to the uniform GenericConverter interface.
* @author Keith Donald
*/
@SuppressWarnings("unchecked")
public final class ConverterFactoryGenericConverter implements GenericConverter {
private final ConverterFactory converterFactory;
public ConverterFactoryGenericConverter(ConverterFactory converterFactory) {
this.converterFactory = converterFactory;
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
}
}

37
org.springframework.core/src/main/java/org/springframework/core/convert/support/ConverterGenericConverter.java

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
/*
* 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.core.convert.support;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
/**
* Adapts a Converter to the uniform GenericConverter interface.
* @author Keith Donald
*/
@SuppressWarnings("unchecked")
public final class ConverterGenericConverter implements GenericConverter {
private final Converter converter;
public ConverterGenericConverter(Converter converter) {
this.converter = converter;
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.converter.convert(source);
}
}

42
org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java

@ -130,7 +130,7 @@ public class GenericConversionService implements ConversionService, ConverterReg @@ -130,7 +130,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
Class sourceType = typeInfo[0];
Class targetType = typeInfo[1];
getSourceMap(sourceType).put(targetType, new ConverterAdapter(converter));
getSourceMap(sourceType).put(targetType, new ConverterGenericConverter(converter));
}
public void addConverterFactory(ConverterFactory<?, ?> converterFactory) {
@ -141,7 +141,7 @@ public class GenericConversionService implements ConversionService, ConverterReg @@ -141,7 +141,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
Class sourceType = typeInfo[0];
Class targetType = typeInfo[1];
getSourceMap(sourceType).put(targetType, new ConverterFactoryAdapter(converterFactory));
getSourceMap(sourceType).put(targetType, new ConverterFactoryGenericConverter(converterFactory));
}
public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
@ -188,7 +188,7 @@ public class GenericConversionService implements ConversionService, ConverterReg @@ -188,7 +188,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
return invokeConverter(converter, source, sourceType, targetType);
}
// subclassing hooks
/**
@ -242,10 +242,10 @@ public class GenericConversionService implements ConversionService, ConverterReg @@ -242,10 +242,10 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
}
// internal helpers
private Class[] getRequiredTypeInfo(Object converter, Class ifc) {
private Class[] getRequiredTypeInfo(Object converter, Class genericIfc) {
Class[] typeInfo = new Class[2];
if (converter instanceof ConverterInfo) {
ConverterInfo info = (ConverterInfo) converter;
@ -254,7 +254,7 @@ public class GenericConversionService implements ConversionService, ConverterReg @@ -254,7 +254,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
return typeInfo;
}
else {
return GenericTypeResolver.resolveTypeArguments(converter.getClass(), ifc);
return GenericTypeResolver.resolveTypeArguments(converter.getClass(), genericIfc);
}
}
@ -370,34 +370,4 @@ public class GenericConversionService implements ConversionService, ConverterReg @@ -370,34 +370,4 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
}
private static class ConverterAdapter implements GenericConverter {
private Converter converter;
public ConverterAdapter(Converter converter) {
this.converter = converter;
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.converter.convert(source);
}
}
private static class ConverterFactoryAdapter implements GenericConverter {
private ConverterFactory converterFactory;
public ConverterFactoryAdapter(ConverterFactory converterFactory) {
this.converterFactory = converterFactory;
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
}
}
}

Loading…
Cancel
Save