Keith Donald
15 years ago
14 changed files with 1076 additions and 37 deletions
@ -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); |
||||
|
||||
} |
@ -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"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -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 + "]"; |
||||
} |
||||
|
||||
} |
@ -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; |
||||
} |
||||
|
||||
} |
@ -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; |
||||
} |
||||
|
||||
} |
@ -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); |
||||
|
||||
} |
@ -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() + ">]"; |
||||
} |
||||
|
||||
} |
@ -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); |
||||
|
||||
} |
@ -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; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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 |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
Loading…
Reference in new issue