mapper initial commit
This commit is contained in:
parent
5c055ed6dd
commit
341835a142
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
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 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
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 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|||
}
|
||||
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
|
|||
return invokeConverter(converter, source, sourceType, targetType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// subclassing hooks
|
||||
|
||||
/**
|
||||
|
|
@ -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
|
|||
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
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
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…
Reference in New Issue