MapperFactory and MapperBuilder config API
This commit is contained in:
parent
5616770a27
commit
a221fb5718
|
|
@ -30,6 +30,6 @@ public interface Mapper<S, T> {
|
||||||
* @return the mapped target object
|
* @return the mapped target object
|
||||||
* @throws MappingException if the mapping process failed
|
* @throws MappingException if the mapping process failed
|
||||||
*/
|
*/
|
||||||
Object map(S source, T target);
|
T map(S source, T target);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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.TypeDescriptor;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a mapping target by calling a converter.
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
final class ConverterMappingTargetFactory implements MappingTargetFactory {
|
||||||
|
|
||||||
|
private Converter converter;
|
||||||
|
|
||||||
|
public ConverterMappingTargetFactory(Converter converter) {
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supports(TypeDescriptor targetType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object createTarget(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||||
|
return this.converter.convert(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* 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.expression.Expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapping between a source field and a target field.
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
final class FieldToFieldMapping implements SpelMapping {
|
||||||
|
|
||||||
|
private final Expression sourceField;
|
||||||
|
|
||||||
|
private final Expression targetField;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private final Converter converter;
|
||||||
|
|
||||||
|
public FieldToFieldMapping(Expression sourceField, Expression targetField, Converter<?, ?> converter) {
|
||||||
|
this.sourceField = sourceField;
|
||||||
|
this.targetField = targetField;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSourceField() {
|
||||||
|
return this.sourceField.getExpressionString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetField() {
|
||||||
|
return this.targetField.getExpressionString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void map(SpelMappingContext context) {
|
||||||
|
try {
|
||||||
|
Object value = context.getSourceFieldValue(this.sourceField);
|
||||||
|
if (this.converter != null) {
|
||||||
|
value = this.converter.convert(value);
|
||||||
|
}
|
||||||
|
context.setTargetFieldValue(this.targetField, value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
context.addMappingFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return getSourceField().hashCode() + getTargetField().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof FieldToFieldMapping)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FieldToFieldMapping m = (FieldToFieldMapping) o;
|
||||||
|
return getSourceField().equals(m.getSourceField()) && getTargetField().equals(m.getTargetField());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "[FieldToFieldMapping<" + getSourceField() + " -> " + getTargetField() + ">]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* 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.expression.Expression;
|
||||||
|
import org.springframework.mapping.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapping between a source field and several target fields.
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
final class FieldToMultiFieldMapping implements SpelMapping {
|
||||||
|
|
||||||
|
private final Expression sourceField;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private final Mapper targetFieldMapper;
|
||||||
|
|
||||||
|
public FieldToMultiFieldMapping(Expression sourceField, Mapper<?, ?> targetFieldMapper) {
|
||||||
|
this.sourceField = sourceField;
|
||||||
|
this.targetFieldMapper = targetFieldMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSourceField() {
|
||||||
|
return this.sourceField.getExpressionString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void map(SpelMappingContext context) {
|
||||||
|
try {
|
||||||
|
Object value = context.getSourceFieldValue(this.sourceField);
|
||||||
|
this.targetFieldMapper.map(value, context.getTarget());
|
||||||
|
} catch (Exception e) {
|
||||||
|
context.addMappingFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return getSourceField().hashCode() + this.targetFieldMapper.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof FieldToMultiFieldMapping)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FieldToMultiFieldMapping m = (FieldToMultiFieldMapping) o;
|
||||||
|
return getSourceField().equals(m.getSourceField()) && this.targetFieldMapper.equals(m.targetFieldMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "[FieldToFieldMapping<" + getSourceField() + " -> " + this.targetFieldMapper + ">]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,7 @@ import java.util.Set;
|
||||||
* Call {@link #add(MappableType)} to register.
|
* Call {@link #add(MappableType)} to register.
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
*/
|
*/
|
||||||
public class MappableTypeFactory {
|
class MappableTypeFactory {
|
||||||
|
|
||||||
private Set<MappableType<?>> mappableTypes = new LinkedHashSet<MappableType<?>>();
|
private Set<MappableType<?>> mappableTypes = new LinkedHashSet<MappableType<?>>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* 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.mapping.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fluent interface for configuring a {@link Mapper} between a source type and a target type.
|
||||||
|
* To use, call one or more of the builder methods on this class, then {@link #getMapper()} to obtain the Mapper instance.
|
||||||
|
* @author Keith Donald
|
||||||
|
* @param <S> the source type to map from
|
||||||
|
* @param <T> the target type to map to
|
||||||
|
* @see #setAutoMappingEnabled(boolean)
|
||||||
|
* @see #addMapping(String)
|
||||||
|
* @see #addMapping(String, Converter)
|
||||||
|
* @see #addMapping(String, Mapper)
|
||||||
|
* @see #addMapping(String, String)
|
||||||
|
* @see #addMapping(String, String, Converter)
|
||||||
|
* @see #addMapping(Mapper)
|
||||||
|
* @see #addConverter(Converter)
|
||||||
|
* @see #getMapper()
|
||||||
|
*/
|
||||||
|
public interface MapperBuilder<S, T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether "auto mapping" is enabled.
|
||||||
|
* When enabled, source and target fields with the same name will automatically be mapped unless an explicit mapping override has been registered.
|
||||||
|
* Set to false to require explicit registration of all source-to-target mapping rules.
|
||||||
|
* Default is enabled (true).
|
||||||
|
* @param autoMappingEnabled auto mapping status
|
||||||
|
*/
|
||||||
|
MapperBuilder<S, T> setAutoMappingEnabled(boolean autoMappingEnabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a mapping between a source field and a target field.
|
||||||
|
* The source and target field names will be the same value.
|
||||||
|
* For example, calling <code>addMapping("order")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>order</code> field on the target.
|
||||||
|
* This is a convenience method for calling {@link #addMapping(String, String)} with the same source and target value..
|
||||||
|
* @param fieldExpression the field mapping expression
|
||||||
|
* @return this, for configuring additional field mapping options fluently
|
||||||
|
*/
|
||||||
|
MapperBuilder<S, T> addMapping(String field);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a mapping between a source field and a target field that first converts the source field value using the provided Converter.
|
||||||
|
* The source and target field expressions will be the same value.
|
||||||
|
* For example, calling <code>addMapping("order")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>order</code> field on the target.
|
||||||
|
* This is a convenience method for calling {@link #addMapping(String, String, Converter)} with the same source and target value..
|
||||||
|
* @param fieldExpression the field mapping expression
|
||||||
|
* @param converter the converter that will convert the source field value before mapping the value to the target field
|
||||||
|
* @return this, for configuring additional field mapping options fluently
|
||||||
|
*/
|
||||||
|
MapperBuilder<S, T> addMapping(String field, Converter<?, ?> converter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a mapping between a source field and multiple target fields.
|
||||||
|
* Use this method when you need to map a single source field value to multiple fields on the target.
|
||||||
|
* For example, calling <code>addMapping("name", firstAndLastNameMapper)</code> might register a mapping that maps the <code>name</code> field on the source to the <code>firstName</code> and <code>lastName</code> fields on the target.
|
||||||
|
* The target field {@link Mapper} will be passed the value of the source field for its source and the target object T for its target.
|
||||||
|
* @param field the source field expression
|
||||||
|
* @param mapper the mapper of the target fields
|
||||||
|
* @return this, for configuring additional field mapping options fluently
|
||||||
|
*/
|
||||||
|
MapperBuilder<S, T> addMapping(String field, Mapper<?, T> mapper);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a mapping between a source field and a target field.
|
||||||
|
* Use this method when the name of the source field and the name of the target field are different.
|
||||||
|
* For example, calling <code>addMapping("order", "primaryOrder")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>primaryOrder</code> field on the target.
|
||||||
|
* @param sourceFieldExpression the source field mapping expression
|
||||||
|
* @param targetFieldExpression the target field mapping expression
|
||||||
|
* @return this, for configuring additional field mapping options fluently
|
||||||
|
*/
|
||||||
|
MapperBuilder<S, T> addMapping(String sourceField, String targetField);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a mapping between a source field and a target field that first converts the source field value using the provided Converter.
|
||||||
|
* Use this method when the name of the source field and the name of the target field are different.
|
||||||
|
* For example, calling <code>addMapping("order", "primaryOrder")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>primaryOrder</code> field on the target.
|
||||||
|
* @param sourceFieldExpression the source field mapping expression
|
||||||
|
* @param targetFieldExpression the target field mapping expression
|
||||||
|
* @return this, for configuring additional field mapping options fluently
|
||||||
|
*/
|
||||||
|
MapperBuilder<S, T> addMapping(String sourceField, String targetField, Converter<?, ?> converter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a mapping between multiple source fields and a single target field.
|
||||||
|
* For example, calling <code>addMapping(dateAndTimeFieldsToDateTimeFieldMapper)</code> might register a mapping that maps the <code>date</code> and <code>time</code> fields on the source to the <code>dateTime</code> field on the target.
|
||||||
|
* The provided {@link Mapper} will be passed the source object S for its source and the target object T for its target.
|
||||||
|
* @param mapper the fields to field mapper
|
||||||
|
* @return this, for configuring additional field mapping options fluently
|
||||||
|
*/
|
||||||
|
MapperBuilder<S, T> addMapping(Mapper<S, T> mapper);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a Mapper that will be used to map between nested source and target fields of a specific sourceType/targetType pair.
|
||||||
|
* The source and target field types are determined by introspecting the parameterized types on the Mapper generic interface.
|
||||||
|
* The target instance that is mapped is constructed by calling its default constructor.
|
||||||
|
* @param nestedMapper the nested mapper
|
||||||
|
* @return this, for configuring additional field mapping options fluently
|
||||||
|
*/
|
||||||
|
MapperBuilder<S, T> addNestedMapper(Mapper<?, ?> nestedMapper);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a Mapper that will be used to map between nested source and target fields of a specific sourceType/targetType pair.
|
||||||
|
* The source and target field types are determined by introspecting the parameterized types on the Mapper generic interface.
|
||||||
|
* The target instance that is mapped is constructed by calling the provided Converter.
|
||||||
|
* @param nestedMapper the nested mapper
|
||||||
|
* @param converter the target converter
|
||||||
|
* @return this, for configuring additional field mapping options fluently
|
||||||
|
*/
|
||||||
|
MapperBuilder<S, T> addNestedMapper(Mapper<?, ?> nestedMapper, Converter<?, ?> converter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a custom type converter to use to convert between two mapped types.
|
||||||
|
* The Converter may convert between simple types, such as Strings to Dates.
|
||||||
|
* Alternatively, it may convert between complex types and initiate a recursive mapping operation between two object fields.
|
||||||
|
* @see Converter
|
||||||
|
* @see MappingConverter
|
||||||
|
*/
|
||||||
|
MapperBuilder<S, T> addConverter(Converter<?, ?> converter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Mapper produced by this builder.
|
||||||
|
* Call this method after instructing the builder.
|
||||||
|
* @return the Mapper between S and T ready for use
|
||||||
|
*/
|
||||||
|
Mapper<S, T> getMapper();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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.mapping.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for creating general-purpose Mappers without depending on a concrete Mapper implementation class.
|
||||||
|
* @see #defaultMapper()
|
||||||
|
* @see #mapperBuilder()
|
||||||
|
* @see #mapperBuilder(Class, Class)
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
public class MapperFactory {
|
||||||
|
|
||||||
|
private static final SpelMapper DEFAULT_MAPPER = new SpelMapper();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default Mapper instance suitable for mapping between most object types using "auto mapping" based on field names.
|
||||||
|
* The Mapper returned is shared and immutable and should not be downcast & modified.
|
||||||
|
* @return the default mapper
|
||||||
|
*/
|
||||||
|
public static Mapper<Object, Object> defaultMapper() {
|
||||||
|
return DEFAULT_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a builder for a new Mapper instance, allowing customization of object mapping policy.
|
||||||
|
* @return the MapperBuilder
|
||||||
|
*/
|
||||||
|
public static MapperBuilder<Object, Object> mapperBuilder() {
|
||||||
|
return new SpelMapperBuilder<Object, Object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a builder for a new Mapper instance that maps between objects of sourceType and targetType.
|
||||||
|
* Allows for customization of object mapping policy.
|
||||||
|
* Use this method as an alterntative to {@link #mapperBuilder()} when you'd like more type-safety and validation when configuring and using the Mapper.
|
||||||
|
* @return the MapperBuilder
|
||||||
|
*/
|
||||||
|
public static <S, T> MapperBuilder<S, T> mapperBuilder(Class<S> sourceType, Class<T> targetType) {
|
||||||
|
return new SpelMapperBuilder<S, T>(sourceType, targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An individual mapping definition between two fields.
|
|
||||||
* @author Keith Donald
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
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() + ">]";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExclude() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures that this mapping should be excluded (ignored and not executed).
|
|
||||||
*/
|
|
||||||
void setExclude();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -24,7 +24,7 @@ import org.springframework.core.NamedThreadLocal;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @see SpelMapper#map(Object, Object)
|
* @see SpelMapper#map(Object, Object)
|
||||||
*/
|
*/
|
||||||
public abstract class MappingContextHolder {
|
abstract class MappingContextHolder {
|
||||||
|
|
||||||
private static final ThreadLocal<Stack<Object>> mappingContextHolder = new NamedThreadLocal<Stack<Object>>(
|
private static final ThreadLocal<Stack<Object>> mappingContextHolder = new NamedThreadLocal<Stack<Object>>(
|
||||||
"Mapping context");
|
"Mapping context");
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ import org.springframework.mapping.Mapper;
|
||||||
* The default MapperTargetFactory instantiates a target by calling its default constructor.
|
* The default MapperTargetFactory instantiates a target by calling its default constructor.
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
*/
|
*/
|
||||||
public final class MappingConverter implements GenericConverter {
|
final class MappingConverter implements GenericConverter {
|
||||||
|
|
||||||
private Mapper mapper;
|
private final Mapper mapper;
|
||||||
|
|
||||||
private MappingTargetFactory mappingTargetFactory;
|
private final MappingTargetFactory mappingTargetFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Converter that delegates to the mapper to complete the type conversion process.
|
* Creates a new Converter that delegates to the mapper to complete the type conversion process.
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import org.springframework.mapping.Mapper;
|
||||||
* @see MappingConverter
|
* @see MappingConverter
|
||||||
* @see Mapper#map(Object, Object)
|
* @see Mapper#map(Object, Object)
|
||||||
*/
|
*/
|
||||||
public interface MappingTargetFactory {
|
interface MappingTargetFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does this factory support creating mapping targets of the specified type
|
* Does this factory support creating mapping targets of the specified type
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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.mapping.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapping between several source fields and a target field.
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
final class MultiFieldToFieldMapping implements SpelMapping {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private final Mapper multiFieldMapper;
|
||||||
|
|
||||||
|
public MultiFieldToFieldMapping(Mapper<?, ?> multiFieldMapper) {
|
||||||
|
this.multiFieldMapper = multiFieldMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void map(SpelMappingContext context) {
|
||||||
|
try {
|
||||||
|
this.multiFieldMapper.map(context.getSource(), context.getTarget());
|
||||||
|
} catch (Exception e) {
|
||||||
|
context.addMappingFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return this.multiFieldMapper.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof MultiFieldToFieldMapping)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MultiFieldToFieldMapping m = (MultiFieldToFieldMapping) o;
|
||||||
|
return this.multiFieldMapper.equals(m.multiFieldMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "[MultiFieldToFieldMapping<" + this.multiFieldMapper + ">]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,8 +17,6 @@ package org.springframework.mapping.support;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
|
@ -33,28 +31,19 @@ import org.springframework.expression.ParseException;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParserConfiguration;
|
import org.springframework.expression.spel.standard.SpelExpressionParserConfiguration;
|
||||||
import org.springframework.mapping.Mapper;
|
import org.springframework.mapping.Mapper;
|
||||||
import org.springframework.mapping.MappingException;
|
|
||||||
import org.springframework.mapping.MappingFailure;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A general-purpose object mapper implementation based on the Spring Expression Language (SpEL).
|
* A general-purpose object mapper implementation based on the Spring Expression Language (SpEL).
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @see #setAutoMappingEnabled(boolean)
|
|
||||||
* @see #setMappableTypeFactory(MappableTypeFactory)
|
|
||||||
* @see #addMapping(String)
|
|
||||||
* @see #addMapping(String, String)
|
|
||||||
* @see #addNestedMapper(Mapper)
|
|
||||||
* @see #addNestedMapper(Mapper, MappingTargetFactory)
|
|
||||||
* @see #getConverterRegistry()
|
|
||||||
*/
|
*/
|
||||||
public class SpelMapper implements Mapper<Object, Object> {
|
final class SpelMapper implements Mapper<Object, Object> {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(SpelMapper.class);
|
private static final Log logger = LogFactory.getLog(SpelMapper.class);
|
||||||
|
|
||||||
private static final SpelExpressionParser sourceExpressionParser = new SpelExpressionParser();
|
private final SpelExpressionParser sourceExpressionParser = new SpelExpressionParser();
|
||||||
|
|
||||||
private static final SpelExpressionParser targetExpressionParser = new SpelExpressionParser(
|
private final SpelExpressionParser targetExpressionParser = new SpelExpressionParser(
|
||||||
SpelExpressionParserConfiguration.CreateObjectIfAttemptToReferenceNull
|
SpelExpressionParserConfiguration.CreateObjectIfAttemptToReferenceNull
|
||||||
| SpelExpressionParserConfiguration.GrowListsOnIndexBeyondSize);
|
| SpelExpressionParserConfiguration.GrowListsOnIndexBeyondSize);
|
||||||
|
|
||||||
|
|
@ -66,63 +55,35 @@ public class SpelMapper implements Mapper<Object, Object> {
|
||||||
|
|
||||||
private MappingConversionService conversionService = new MappingConversionService();
|
private MappingConversionService conversionService = new MappingConversionService();
|
||||||
|
|
||||||
/**
|
public SpelMapper() {
|
||||||
* Sets whether "auto mapping" is enabled.
|
|
||||||
* When enabled, source and target fields with the same name will automatically be mapped unless an explicit mapping override has been registered.
|
}
|
||||||
* Set to false to require explicit registration of all source-to-target mapping rules.
|
|
||||||
* Default is enabled (true).
|
public SpelMapper(Class sourceType, Class targetType) {
|
||||||
* @param autoMappingEnabled auto mapping status
|
// TODO - addMapping assertions based on specified sourceType and targetType
|
||||||
*/
|
}
|
||||||
|
|
||||||
public void setAutoMappingEnabled(boolean autoMappingEnabled) {
|
public void setAutoMappingEnabled(boolean autoMappingEnabled) {
|
||||||
this.autoMappingEnabled = autoMappingEnabled;
|
this.autoMappingEnabled = autoMappingEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the factory for {@link MappableType mappable types} supported by this mapper.
|
|
||||||
* Default is {@link DefaultMappableTypeFactory}.
|
|
||||||
* @param mappableTypeFactory the mappableTypeFactory
|
|
||||||
*/
|
|
||||||
public void setMappableTypeFactory(MappableTypeFactory mappableTypeFactory) {
|
public void setMappableTypeFactory(MappableTypeFactory mappableTypeFactory) {
|
||||||
this.mappableTypeFactory = mappableTypeFactory;
|
this.mappableTypeFactory = mappableTypeFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void addMapping(String sourceFieldExpression, String targetFieldExpression, Converter<?, ?> converter) {
|
||||||
* Register a field mapping.
|
Expression sourceField = parseSourceField(sourceFieldExpression);
|
||||||
* The source and target field expressions will be the same value.
|
Expression targetField = parseTargetField(targetFieldExpression);
|
||||||
* For example, calling <code>addMapping("order")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>order</code> field on the target.
|
FieldToFieldMapping mapping = new FieldToFieldMapping(sourceField, targetField, converter);
|
||||||
* This is a convenience method for calling {@link #addMapping(String, String)} with the same source and target value..
|
this.mappings.add(mapping);
|
||||||
* @param fieldExpression the field mapping expression
|
|
||||||
* @return this, for configuring additional field mapping options fluently
|
|
||||||
*/
|
|
||||||
public MappingConfiguration addMapping(String fieldExpression) {
|
|
||||||
return addMapping(fieldExpression, fieldExpression);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void addMapping(String field, Mapper mapper) {
|
||||||
* Register a mapping between a source and target field.
|
this.mappings.add(new FieldToMultiFieldMapping(parseSourceField(field), mapper));
|
||||||
* For example, calling <code>addMapping("order", "primaryOrder")</code> will register a mapping that maps between the <code>order</code> field on the source and the <code>primaryOrder</code> field on the target.
|
}
|
||||||
* @param sourceFieldExpression the source field mapping expression
|
|
||||||
* @param targetFieldExpression the target field mapping expression
|
public void addMapping(Mapper mapper) {
|
||||||
* @return this, for configuring additional field mapping options fluently
|
this.mappings.add(new MultiFieldToFieldMapping(mapper));
|
||||||
*/
|
|
||||||
public MappingConfiguration addMapping(String sourceFieldExpression, String targetFieldExpression) {
|
|
||||||
Expression sourceExp;
|
|
||||||
try {
|
|
||||||
sourceExp = sourceExpressionParser.parseExpression(sourceFieldExpression);
|
|
||||||
} catch (ParseException e) {
|
|
||||||
throw new IllegalArgumentException("The mapping source '" + sourceFieldExpression
|
|
||||||
+ "' is not a parseable value expression", e);
|
|
||||||
}
|
|
||||||
Expression targetExp;
|
|
||||||
try {
|
|
||||||
targetExp = targetExpressionParser.parseExpression(targetFieldExpression);
|
|
||||||
} catch (ParseException e) {
|
|
||||||
throw new IllegalArgumentException("The mapping target '" + targetFieldExpression
|
|
||||||
+ "' is not a parseable property expression", e);
|
|
||||||
}
|
|
||||||
SpelMapping mapping = new SpelMapping(sourceExp, targetExp);
|
|
||||||
this.mappings.add(mapping);
|
|
||||||
return mapping;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -174,13 +135,6 @@ public class SpelMapper implements Mapper<Object, Object> {
|
||||||
targetFactory));
|
targetFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return this mapper's internal converter registry.
|
|
||||||
* Allows for registration of simple type Converters in addition to MapperConverters that map entire nested object structures using a Mapper.
|
|
||||||
* To register the latter, consider using one of the {@link #addNestedMapper(Mapper) addNestedMapper} variants.
|
|
||||||
* @see Converter
|
|
||||||
* @see MappingConverter
|
|
||||||
*/
|
|
||||||
public ConverterRegistry getConverterRegistry() {
|
public ConverterRegistry getConverterRegistry() {
|
||||||
return conversionService;
|
return conversionService;
|
||||||
}
|
}
|
||||||
|
|
@ -192,17 +146,21 @@ public class SpelMapper implements Mapper<Object, Object> {
|
||||||
MappingContextHolder.push(source);
|
MappingContextHolder.push(source);
|
||||||
EvaluationContext sourceContext = getEvaluationContext(source);
|
EvaluationContext sourceContext = getEvaluationContext(source);
|
||||||
EvaluationContext targetContext = getEvaluationContext(target);
|
EvaluationContext targetContext = getEvaluationContext(target);
|
||||||
List<MappingFailure> failures = new LinkedList<MappingFailure>();
|
SpelMappingContext context = new SpelMappingContext(sourceContext, targetContext);
|
||||||
for (SpelMapping mapping : this.mappings) {
|
for (SpelMapping mapping : this.mappings) {
|
||||||
doMap(mapping, sourceContext, targetContext, failures);
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug(MappingContextHolder.getLevel() + mapping);
|
||||||
|
}
|
||||||
|
mapping.map(context);
|
||||||
}
|
}
|
||||||
Set<SpelMapping> autoMappings = getAutoMappings(sourceContext, targetContext);
|
Set<FieldToFieldMapping> autoMappings = getAutoMappings(sourceContext, targetContext);
|
||||||
for (SpelMapping mapping : autoMappings) {
|
for (SpelMapping mapping : autoMappings) {
|
||||||
doMap(mapping, sourceContext, targetContext, failures);
|
if (logger.isDebugEnabled()) {
|
||||||
}
|
logger.debug(MappingContextHolder.getLevel() + mapping + " (auto)");
|
||||||
if (!failures.isEmpty()) {
|
}
|
||||||
throw new MappingException(failures);
|
mapping.map(context);
|
||||||
}
|
}
|
||||||
|
context.handleFailures();
|
||||||
return target;
|
return target;
|
||||||
} finally {
|
} finally {
|
||||||
MappingContextHolder.pop();
|
MappingContextHolder.pop();
|
||||||
|
|
@ -211,6 +169,28 @@ public class SpelMapper implements Mapper<Object, Object> {
|
||||||
|
|
||||||
// internal helpers
|
// internal helpers
|
||||||
|
|
||||||
|
private Expression parseSourceField(String sourceFieldExpression) {
|
||||||
|
Expression sourceExp;
|
||||||
|
try {
|
||||||
|
sourceExp = sourceExpressionParser.parseExpression(sourceFieldExpression);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new IllegalArgumentException("The mapping source '" + sourceFieldExpression
|
||||||
|
+ "' is not a parseable value expression", e);
|
||||||
|
}
|
||||||
|
return sourceExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression parseTargetField(String targetFieldExpression) {
|
||||||
|
Expression targetExp;
|
||||||
|
try {
|
||||||
|
targetExp = targetExpressionParser.parseExpression(targetFieldExpression);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new IllegalArgumentException("The mapping target '" + targetFieldExpression
|
||||||
|
+ "' is not a parseable property expression", e);
|
||||||
|
}
|
||||||
|
return targetExp;
|
||||||
|
}
|
||||||
|
|
||||||
private Class<?>[] getRequiredTypeInfo(Mapper<?, ?> mapper) {
|
private Class<?>[] getRequiredTypeInfo(Mapper<?, ?> mapper) {
|
||||||
return GenericTypeResolver.resolveTypeArguments(mapper.getClass(), Mapper.class);
|
return GenericTypeResolver.resolveTypeArguments(mapper.getClass(), Mapper.class);
|
||||||
}
|
}
|
||||||
|
|
@ -219,17 +199,9 @@ public class SpelMapper implements Mapper<Object, Object> {
|
||||||
return mappableTypeFactory.getMappableType(object).getEvaluationContext(object, this.conversionService);
|
return mappableTypeFactory.getMappableType(object).getEvaluationContext(object, this.conversionService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doMap(SpelMapping mapping, EvaluationContext sourceContext, EvaluationContext targetContext,
|
private Set<FieldToFieldMapping> getAutoMappings(EvaluationContext sourceContext, EvaluationContext targetContext) {
|
||||||
List<MappingFailure> failures) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug(MappingContextHolder.getLevel() + mapping);
|
|
||||||
}
|
|
||||||
mapping.map(sourceContext, targetContext, failures);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<SpelMapping> getAutoMappings(EvaluationContext sourceContext, EvaluationContext targetContext) {
|
|
||||||
if (this.autoMappingEnabled) {
|
if (this.autoMappingEnabled) {
|
||||||
Set<SpelMapping> autoMappings = new LinkedHashSet<SpelMapping>();
|
Set<FieldToFieldMapping> autoMappings = new LinkedHashSet<FieldToFieldMapping>();
|
||||||
Set<String> sourceFields = getMappableFields(sourceContext.getRootObject().getValue());
|
Set<String> sourceFields = getMappableFields(sourceContext.getRootObject().getValue());
|
||||||
for (String field : sourceFields) {
|
for (String field : sourceFields) {
|
||||||
if (!explicitlyMapped(field)) {
|
if (!explicitlyMapped(field)) {
|
||||||
|
|
@ -249,7 +221,7 @@ public class SpelMapper implements Mapper<Object, Object> {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (targetExpression.isWritable(targetContext)) {
|
if (targetExpression.isWritable(targetContext)) {
|
||||||
autoMappings.add(new SpelMapping(sourceExpression, targetExpression));
|
autoMappings.add(new FieldToFieldMapping(sourceExpression, targetExpression, null));
|
||||||
}
|
}
|
||||||
} catch (EvaluationException e) {
|
} catch (EvaluationException e) {
|
||||||
|
|
||||||
|
|
@ -268,11 +240,11 @@ public class SpelMapper implements Mapper<Object, Object> {
|
||||||
|
|
||||||
private boolean explicitlyMapped(String field) {
|
private boolean explicitlyMapped(String field) {
|
||||||
for (SpelMapping mapping : this.mappings) {
|
for (SpelMapping mapping : this.mappings) {
|
||||||
if (mapping.getSourceExpressionString().startsWith(field)) {
|
if (mapping instanceof FieldToFieldMapping
|
||||||
|
&& ((FieldToFieldMapping) mapping).getSourceField().startsWith(field)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* 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.mapping.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MapperBuilder that builds {@link SpelMapper} instances.
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
final class SpelMapperBuilder<S, T> implements MapperBuilder<S, T> {
|
||||||
|
|
||||||
|
private final SpelMapper mapper;
|
||||||
|
|
||||||
|
public SpelMapperBuilder() {
|
||||||
|
this.mapper = new SpelMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpelMapperBuilder(Class<S> sourceType, Class<T> targetType) {
|
||||||
|
this.mapper = new SpelMapper(sourceType, targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperBuilder<S, T> setAutoMappingEnabled(boolean autoMappingEnabled) {
|
||||||
|
this.mapper.setAutoMappingEnabled(autoMappingEnabled);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperBuilder<S, T> addMapping(String field) {
|
||||||
|
this.mapper.addMapping(field, field, null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperBuilder<S, T> addMapping(String field, Converter<?, ?> converter) {
|
||||||
|
this.mapper.addMapping(field, field, converter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperBuilder<S, T> addMapping(String field, Mapper<?, T> mapper) {
|
||||||
|
this.mapper.addMapping(field, mapper);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperBuilder<S, T> addMapping(String sourceField, String targetField) {
|
||||||
|
this.mapper.addMapping(sourceField, targetField, null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperBuilder<S, T> addMapping(String sourceField, String targetField, Converter<?, ?> converter) {
|
||||||
|
this.mapper.addMapping(sourceField, targetField, converter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperBuilder<S, T> addMapping(Mapper<S, T> mapper) {
|
||||||
|
this.mapper.addMapping(mapper);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperBuilder<S, T> addNestedMapper(Mapper<?, ?> nestedMapper) {
|
||||||
|
this.mapper.addNestedMapper(nestedMapper);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperBuilder<S, T> addNestedMapper(Mapper<?, ?> nestedMapper, Converter<?, ?> converter) {
|
||||||
|
this.mapper.addNestedMapper(nestedMapper, new ConverterMappingTargetFactory(converter));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapperBuilder<S, T> addConverter(Converter<?, ?> converter) {
|
||||||
|
this.mapper.getConverterRegistry().addConverter(converter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Mapper<S, T> getMapper() {
|
||||||
|
return (Mapper<S, T>) this.mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -15,97 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.mapping.support;
|
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An individual mapping definition between two fields.
|
* A single {@link SpelMapper} mapping.
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
*/
|
*/
|
||||||
class SpelMapping implements MappingConfiguration {
|
interface SpelMapping {
|
||||||
|
|
||||||
private Expression source;
|
|
||||||
|
|
||||||
private Expression target;
|
|
||||||
|
|
||||||
private GenericConverter converter;
|
|
||||||
|
|
||||||
private boolean exclude;
|
|
||||||
|
|
||||||
public SpelMapping(Expression source, Expression target) {
|
|
||||||
this.source = source;
|
|
||||||
this.target = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
// implementing MappingConfiguration
|
|
||||||
|
|
||||||
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 setExclude() {
|
|
||||||
this.exclude = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public methods
|
|
||||||
|
|
||||||
public String getSourceExpressionString() {
|
|
||||||
return this.source.getExpressionString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTargetExpressionString() {
|
|
||||||
return this.target.getExpressionString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void map(EvaluationContext sourceContext, EvaluationContext targetContext,
|
|
||||||
Collection<MappingFailure> failures) {
|
|
||||||
if (exclude) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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 SpelMapping)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SpelMapping m = (SpelMapping) o;
|
|
||||||
return getSourceExpressionString().equals(m.getSourceExpressionString())
|
|
||||||
&& getTargetExpressionString().equals(m.getTargetExpressionString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return "[Mapping<" + getSourceExpressionString() + " -> " + getTargetExpressionString() + ">]";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute this mapping.
|
||||||
|
* @param context the mapping context
|
||||||
|
*/
|
||||||
|
void map(SpelMappingContext context);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* 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.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.mapping.MappingException;
|
||||||
|
import org.springframework.mapping.MappingFailure;
|
||||||
|
|
||||||
|
final class SpelMappingContext {
|
||||||
|
|
||||||
|
private final EvaluationContext sourceEvaluationContext;
|
||||||
|
|
||||||
|
private final EvaluationContext targetEvaluationContext;
|
||||||
|
|
||||||
|
private final List<MappingFailure> failures = new LinkedList<MappingFailure>();
|
||||||
|
|
||||||
|
public SpelMappingContext(EvaluationContext sourceEvaluationContext, EvaluationContext targetEvaluationContext) {
|
||||||
|
this.sourceEvaluationContext = sourceEvaluationContext;
|
||||||
|
this.targetEvaluationContext = targetEvaluationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getSource() {
|
||||||
|
return this.sourceEvaluationContext.getRootObject().getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getTarget() {
|
||||||
|
return this.targetEvaluationContext.getRootObject().getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getSourceFieldValue(Expression sourceField) {
|
||||||
|
return sourceField.getValue(this.sourceEvaluationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetFieldValue(Expression targetField, Object value) {
|
||||||
|
targetField.setValue(this.targetEvaluationContext, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addMappingFailure(Throwable cause) {
|
||||||
|
this.failures.add(new MappingFailure(cause));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleFailures() {
|
||||||
|
if (!this.failures.isEmpty()) {
|
||||||
|
throw new MappingException(this.failures);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,11 +29,11 @@ import org.springframework.ui.format.Formatter;
|
||||||
public class DateTimeFormatter implements Formatter<DateTime> {
|
public class DateTimeFormatter implements Formatter<DateTime> {
|
||||||
|
|
||||||
private org.joda.time.format.DateTimeFormatter formatter;
|
private org.joda.time.format.DateTimeFormatter formatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link DateTimeFormatter} for the given JodaTime formatting pattern.
|
* Creates a new {@link DateTimeFormatter} for the given JodaTime formatting pattern.
|
||||||
* @param pattern
|
* @param pattern
|
||||||
* @see DateTimeFormat
|
* @see DateTimeFormat#forPattern(String)
|
||||||
*/
|
*/
|
||||||
public DateTimeFormatter(String pattern) {
|
public DateTimeFormatter(String pattern) {
|
||||||
this.formatter = DateTimeFormat.forPattern(pattern);
|
this.formatter = DateTimeFormat.forPattern(pattern);
|
||||||
|
|
@ -41,6 +41,7 @@ public class DateTimeFormatter implements Formatter<DateTime> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link DateTimeFormatter} for the given JodaTime formatter.
|
* Creates a new {@link DateTimeFormatter} for the given JodaTime formatter.
|
||||||
|
* @param formatter the Date Formatter instance
|
||||||
*/
|
*/
|
||||||
public DateTimeFormatter(org.joda.time.format.DateTimeFormatter formatter) {
|
public DateTimeFormatter(org.joda.time.format.DateTimeFormatter formatter) {
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
|
|
|
||||||
|
|
@ -7,30 +7,27 @@ import static org.junit.Assert.assertTrue;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.format.ISODateTimeFormat;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.core.convert.ConversionService;
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.expression.AccessException;
|
|
||||||
import org.springframework.expression.EvaluationContext;
|
|
||||||
import org.springframework.expression.PropertyAccessor;
|
|
||||||
import org.springframework.expression.TypedValue;
|
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
|
||||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
|
||||||
import org.springframework.mapping.Mapper;
|
import org.springframework.mapping.Mapper;
|
||||||
import org.springframework.mapping.MappingException;
|
import org.springframework.mapping.MappingException;
|
||||||
import org.w3c.dom.Element;
|
|
||||||
import org.w3c.dom.NamedNodeMap;
|
|
||||||
|
|
||||||
public class SpelMapperTests {
|
public class MappingTests {
|
||||||
|
|
||||||
private SpelMapper mapper = new SpelMapper();
|
@Test
|
||||||
|
public void testDefaultMapper() {
|
||||||
|
EmployeeDto dto = new EmployeeDto();
|
||||||
|
dto.setFirstName("Keith");
|
||||||
|
dto.setLastName("Donald");
|
||||||
|
Employee emp = (Employee) MapperFactory.defaultMapper().map(dto, new Employee());
|
||||||
|
assertEquals("Keith", emp.getFirstName());
|
||||||
|
assertEquals("Donald", emp.getLastName());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mapAutomatic() {
|
public void mapAutomatic() {
|
||||||
|
|
@ -40,7 +37,7 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.map(source, target);
|
MapperFactory.defaultMapper().map(source, target);
|
||||||
|
|
||||||
assertEquals("Keith", target.name);
|
assertEquals("Keith", target.name);
|
||||||
assertEquals(31, target.age);
|
assertEquals(31, target.age);
|
||||||
|
|
@ -54,8 +51,8 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.setAutoMappingEnabled(false);
|
Mapper<Object, Object> mapper = MapperFactory.mapperBuilder().setAutoMappingEnabled(false).addMapping("name")
|
||||||
mapper.addMapping("name");
|
.getMapper();
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals("Keith", target.name);
|
assertEquals("Keith", target.name);
|
||||||
|
|
@ -71,7 +68,7 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.addMapping("test", "age");
|
Mapper<Object, Object> mapper = MapperFactory.mapperBuilder().addMapping("test", "age").getMapper();
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals("Keith", target.name);
|
assertEquals("Keith", target.name);
|
||||||
|
|
@ -88,29 +85,12 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.map(source, target);
|
MapperFactory.defaultMapper().map(source, target);
|
||||||
|
|
||||||
assertEquals("Keith", target.name);
|
assertEquals("Keith", target.name);
|
||||||
assertEquals(31, target.age);
|
assertEquals(31, target.age);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mapAutomaticWithExclusions() {
|
|
||||||
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.addMapping("test").setExclude();
|
|
||||||
mapper.map(source, target);
|
|
||||||
|
|
||||||
assertEquals("Keith", target.name);
|
|
||||||
assertEquals(0, target.age);
|
|
||||||
assertEquals(Sport.FOOTBALL, target.favoriteSport);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mapSameSourceFieldToMultipleTargets() {
|
public void mapSameSourceFieldToMultipleTargets() {
|
||||||
Map<String, Object> source = new HashMap<String, Object>();
|
Map<String, Object> source = new HashMap<String, Object>();
|
||||||
|
|
@ -118,8 +98,8 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.addMapping("test", "name");
|
Mapper<Object, Object> mapper = MapperFactory.mapperBuilder().addMapping("test", "name").addMapping("test",
|
||||||
mapper.addMapping("test", "favoriteSport");
|
"favoriteSport").getMapper();
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals("FOOTBALL", target.name);
|
assertEquals("FOOTBALL", target.name);
|
||||||
|
|
@ -136,8 +116,8 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.addMapping("fullName", "name");
|
Mapper<Object, Object> mapper = MapperFactory.mapperBuilder().addMapping("fullName", "name").addMapping(
|
||||||
mapper.addMapping("sport", "favoriteSport");
|
"sport", "favoriteSport").getMapper();
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals("Keith Donald", target.name);
|
assertEquals("Keith Donald", target.name);
|
||||||
|
|
@ -155,7 +135,8 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.addMapping("nested.foo");
|
Mapper<PersonDto, Person> mapper = MapperFactory.mapperBuilder(PersonDto.class, Person.class).addMapping(
|
||||||
|
"nested.foo").getMapper();
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals("bar", target.nested.foo);
|
assertEquals("bar", target.nested.foo);
|
||||||
|
|
@ -170,8 +151,8 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.setAutoMappingEnabled(false);
|
Mapper<PersonDto, Person> mapper = MapperFactory.mapperBuilder(PersonDto.class, Person.class)
|
||||||
mapper.addMapping("nested");
|
.setAutoMappingEnabled(false).addMapping("nested").getMapper();
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals("bar", target.nested.foo);
|
assertEquals("bar", target.nested.foo);
|
||||||
|
|
@ -186,57 +167,21 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
SpelMapper nestedMapper = new SpelMapper();
|
Mapper<NestedDto, Nested> nestedMapper = MapperFactory.mapperBuilder(NestedDto.class, Nested.class).addMapping(
|
||||||
nestedMapper.setAutoMappingEnabled(false);
|
"foo", new Converter<String, String>() {
|
||||||
nestedMapper.addMapping("foo").setConverter(new Converter<String, String>() {
|
public String convert(String source) {
|
||||||
public String convert(String source) {
|
return source + " and baz";
|
||||||
return source + " and baz";
|
}
|
||||||
}
|
}).getMapper();
|
||||||
});
|
|
||||||
mapper.addNestedMapper(NestedDto.class, Nested.class, nestedMapper);
|
Mapper<PersonDto, Person> mapper = MapperFactory.mapperBuilder(PersonDto.class, Person.class)
|
||||||
|
.setAutoMappingEnabled(false).addMapping("nested").addNestedMapper(nestedMapper).getMapper();
|
||||||
|
|
||||||
mapper.setAutoMappingEnabled(false);
|
|
||||||
mapper.addMapping("nested");
|
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals("bar and baz", target.nested.foo);
|
assertEquals("bar and baz", target.nested.foo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mapBeanNestedCustomNestedMapperCustomMappingTargetFactory() {
|
|
||||||
PersonDto source = new PersonDto();
|
|
||||||
final NestedDto nested = new NestedDto();
|
|
||||||
nested.foo = "bar";
|
|
||||||
source.setNested(nested);
|
|
||||||
|
|
||||||
Person target = new Person();
|
|
||||||
|
|
||||||
SpelMapper nestedMapper = new SpelMapper();
|
|
||||||
nestedMapper.setAutoMappingEnabled(false);
|
|
||||||
nestedMapper.addMapping("foo").setConverter(new Converter<String, String>() {
|
|
||||||
public String convert(String source) {
|
|
||||||
return source + " and baz";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mapper.addNestedMapper(NestedDto.class, Nested.class, nestedMapper, new MappingTargetFactory() {
|
|
||||||
public boolean supports(TypeDescriptor targetType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object createTarget(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
|
||||||
NestedDto nestedDto = (NestedDto) source;
|
|
||||||
assertEquals(nested, nestedDto);
|
|
||||||
return new Nested();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mapper.setAutoMappingEnabled(false);
|
|
||||||
mapper.addMapping("nested");
|
|
||||||
mapper.map(source, target);
|
|
||||||
|
|
||||||
assertEquals("bar and baz", target.nested.foo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mapBeanNestedCustomNestedMapperHandCoded() {
|
public void mapBeanNestedCustomNestedMapperHandCoded() {
|
||||||
PersonDto source = new PersonDto();
|
PersonDto source = new PersonDto();
|
||||||
|
|
@ -247,52 +192,130 @@ public class SpelMapperTests {
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
Mapper<NestedDto, Nested> nestedMapper = new Mapper<NestedDto, Nested>() {
|
Mapper<NestedDto, Nested> nestedMapper = new Mapper<NestedDto, Nested>() {
|
||||||
public Object map(NestedDto source, Nested target) {
|
public Nested map(NestedDto source, Nested target) {
|
||||||
target.foo = source.foo + " and baz";
|
target.setFoo(source.getFoo() + " and baz");
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
mapper.addNestedMapper(nestedMapper);
|
|
||||||
|
|
||||||
mapper.setAutoMappingEnabled(false);
|
Mapper<PersonDto, Person> mapper = MapperFactory.mapperBuilder(PersonDto.class, Person.class)
|
||||||
mapper.addMapping("nested");
|
.setAutoMappingEnabled(false).addMapping("nested").addNestedMapper(nestedMapper).getMapper();
|
||||||
|
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals("bar and baz", target.nested.foo);
|
assertEquals("bar and baz", target.nested.foo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mapBeanNestedCustomConverterDelegatingToMapper() {
|
public void mapBeanNestedCustomNestedMapperConverterAsTargetFactory() {
|
||||||
PersonDto source = new PersonDto();
|
PersonDto source = new PersonDto();
|
||||||
NestedDto nested = new NestedDto();
|
final NestedDto nested = new NestedDto();
|
||||||
nested.foo = "bar";
|
nested.foo = "bar";
|
||||||
source.setNested(nested);
|
source.setNested(nested);
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.getConverterRegistry().addConverter(new Converter<NestedDto, Nested>() {
|
Mapper<NestedDto, Nested> nestedMapper = MapperFactory.mapperBuilder(NestedDto.class, Nested.class).addMapping(
|
||||||
public Nested convert(NestedDto source) {
|
"foo", new Converter<String, String>() {
|
||||||
// allows construction of target to be controlled by the converter
|
|
||||||
Nested nested = new Nested();
|
|
||||||
// mapping can do whatever, here we delegate to nested SpelMapper
|
|
||||||
SpelMapper nestedMapper = new SpelMapper();
|
|
||||||
nestedMapper.addMapping("foo").setConverter(new Converter<String, String>() {
|
|
||||||
public String convert(String source) {
|
public String convert(String source) {
|
||||||
return source + " and baz";
|
return source + " and baz";
|
||||||
}
|
}
|
||||||
});
|
}).getMapper();
|
||||||
return (Nested) nestedMapper.map(source, nested);
|
|
||||||
}
|
Mapper<PersonDto, Person> mapper = MapperFactory.mapperBuilder(PersonDto.class, Person.class)
|
||||||
});
|
.setAutoMappingEnabled(false).addMapping("nested").addNestedMapper(nestedMapper,
|
||||||
|
new Converter<NestedDto, Nested>() {
|
||||||
|
public Nested convert(NestedDto source) {
|
||||||
|
assertEquals(nested, source);
|
||||||
|
return new Nested();
|
||||||
|
}
|
||||||
|
}).getMapper();
|
||||||
|
|
||||||
mapper.setAutoMappingEnabled(false);
|
|
||||||
mapper.addMapping("nested");
|
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals("bar and baz", target.nested.foo);
|
assertEquals("bar and baz", target.nested.foo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mapBeanNestedCustomConverterDelegatingToMapper() {
|
||||||
|
PersonDto source = new PersonDto();
|
||||||
|
final NestedDto nested = new NestedDto();
|
||||||
|
nested.foo = "bar";
|
||||||
|
source.setNested(nested);
|
||||||
|
|
||||||
|
Person target = new Person();
|
||||||
|
|
||||||
|
Mapper<PersonDto, Person> mapper = MapperFactory.mapperBuilder(PersonDto.class, Person.class)
|
||||||
|
.setAutoMappingEnabled(false).addMapping("nested", new Converter<NestedDto, Nested>() {
|
||||||
|
public Nested convert(NestedDto source) {
|
||||||
|
Mapper<NestedDto, Nested> nestedMapper = MapperFactory.mapperBuilder(NestedDto.class,
|
||||||
|
Nested.class).addMapping("foo", new Converter<String, String>() {
|
||||||
|
public String convert(String source) {
|
||||||
|
return source + " and baz";
|
||||||
|
}
|
||||||
|
}).getMapper();
|
||||||
|
return nestedMapper.map(source, new Nested());
|
||||||
|
}
|
||||||
|
}).getMapper();
|
||||||
|
|
||||||
|
mapper.map(source, target);
|
||||||
|
|
||||||
|
assertEquals("bar and baz", target.nested.foo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomMapper() {
|
||||||
|
Mapper<CreateAccountDto, Account> mapper = MapperFactory.mapperBuilder(CreateAccountDto.class, Account.class)
|
||||||
|
.setAutoMappingEnabled(false)
|
||||||
|
// field to field of different name
|
||||||
|
.addMapping("accountNumber", "number")
|
||||||
|
// field to multiple fields
|
||||||
|
.addMapping("name", new Mapper<String, Account>() {
|
||||||
|
public Account map(String name, Account account) {
|
||||||
|
String[] names = name.split(" ");
|
||||||
|
account.setFirstName(names[0]);
|
||||||
|
account.setLastName(names[1]);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// field to field with type conversion
|
||||||
|
.addMapping("address", new Converter<String, Address>() {
|
||||||
|
public Address convert(String address) {
|
||||||
|
String[] fields = address.split(" ");
|
||||||
|
Address addr = new Address();
|
||||||
|
addr.setStreet(fields[0]);
|
||||||
|
addr.setCity(fields[1]);
|
||||||
|
addr.setState(fields[2]);
|
||||||
|
addr.setZip(fields[3]);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// multiple fields to field
|
||||||
|
.addMapping(new Mapper<CreateAccountDto, Account>() {
|
||||||
|
public Account map(CreateAccountDto source, Account target) {
|
||||||
|
DateTime dateTime = ISODateTimeFormat.dateTime().parseDateTime(
|
||||||
|
source.getActivationDate() + "T" + source.getActivationTime());
|
||||||
|
target.setActivationDateTime(dateTime);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}).getMapper();
|
||||||
|
CreateAccountDto dto = new CreateAccountDto();
|
||||||
|
dto.setAccountNumber("123456789");
|
||||||
|
dto.setName("Keith Donald");
|
||||||
|
dto.setActivationDate("2009-10-12");
|
||||||
|
dto.setActivationTime("12:00:00.000Z");
|
||||||
|
dto.setAddress("2009BelAireEstates PalmBay FL 35452");
|
||||||
|
Account account = mapper.map(dto, new Account());
|
||||||
|
assertEquals("Keith", account.getFirstName());
|
||||||
|
assertEquals("Donald", account.getLastName());
|
||||||
|
assertEquals("2009BelAireEstates", account.getAddress().getStreet());
|
||||||
|
assertEquals("PalmBay", account.getAddress().getCity());
|
||||||
|
assertEquals("FL", account.getAddress().getState());
|
||||||
|
assertEquals("35452", account.getAddress().getZip());
|
||||||
|
assertEquals(ISODateTimeFormat.dateTime().parseDateTime("2009-10-12T12:00:00.000Z"), account
|
||||||
|
.getActivationDateTime());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mapList() {
|
public void mapList() {
|
||||||
PersonDto source = new PersonDto();
|
PersonDto source = new PersonDto();
|
||||||
|
|
@ -303,8 +326,8 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.setAutoMappingEnabled(false);
|
Mapper<PersonDto, Person> mapper = MapperFactory.mapperBuilder(PersonDto.class, Person.class)
|
||||||
mapper.addMapping("sports", "favoriteSports");
|
.setAutoMappingEnabled(false).addMapping("sports", "favoriteSports").getMapper();
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals(Sport.FOOTBALL, target.favoriteSports.get(0));
|
assertEquals(Sport.FOOTBALL, target.favoriteSports.get(0));
|
||||||
|
|
@ -321,8 +344,8 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.setAutoMappingEnabled(false);
|
Mapper<PersonDto, Person> mapper = MapperFactory.mapperBuilder(PersonDto.class, Person.class)
|
||||||
mapper.addMapping("sports[0]", "favoriteSport");
|
.setAutoMappingEnabled(false).addMapping("sports[0]", "favoriteSport").getMapper();
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals(Sport.FOOTBALL, target.favoriteSport);
|
assertEquals(Sport.FOOTBALL, target.favoriteSport);
|
||||||
|
|
@ -339,13 +362,15 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.setAutoMappingEnabled(false);
|
Mapper<PersonDto, Person> mapper = MapperFactory.mapperBuilder(PersonDto.class, Person.class)
|
||||||
mapper.addMapping("friendRankings", "friendRankings");
|
.setAutoMappingEnabled(false).addMapping("friendRankings").addConverter(
|
||||||
mapper.getConverterRegistry().addConverter(new Converter<String, Person>() {
|
new Converter<String, Person>() {
|
||||||
public Person convert(String source) {
|
public Person convert(String source) {
|
||||||
return new Person(source);
|
return new Person(source);
|
||||||
}
|
}
|
||||||
});
|
}).getMapper();
|
||||||
|
mapper.map(source, target);
|
||||||
|
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals(new Integer(1), target.friendRankings.get(new Person("Keri")));
|
assertEquals(new Integer(1), target.friendRankings.get(new Person("Keri")));
|
||||||
|
|
@ -360,12 +385,13 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
|
|
||||||
mapper.addMapping("name").setConverter(new Converter<String, String>() {
|
Mapper<Object, Object> mapper = MapperFactory.mapperBuilder().addMapping("name",
|
||||||
public String convert(String source) {
|
new Converter<String, String>() {
|
||||||
String[] names = source.split(" ");
|
public String convert(String source) {
|
||||||
return names[0] + " P. " + names[1];
|
String[] names = source.split(" ");
|
||||||
}
|
return names[0] + " P. " + names[1];
|
||||||
});
|
}
|
||||||
|
}).getMapper();
|
||||||
mapper.map(source, target);
|
mapper.map(source, target);
|
||||||
|
|
||||||
assertEquals("Keith P. Donald", target.name);
|
assertEquals("Keith P. Donald", target.name);
|
||||||
|
|
@ -379,7 +405,7 @@ public class SpelMapperTests {
|
||||||
source.put("age", "invalid");
|
source.put("age", "invalid");
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
try {
|
try {
|
||||||
mapper.map(source, target);
|
MapperFactory.defaultMapper().map(source, target);
|
||||||
} catch (MappingException e) {
|
} catch (MappingException e) {
|
||||||
assertEquals(1, e.getMappingFailureCount());
|
assertEquals(1, e.getMappingFailureCount());
|
||||||
}
|
}
|
||||||
|
|
@ -393,7 +419,7 @@ public class SpelMapperTests {
|
||||||
source.setFavoriteSport(Sport.FOOTBALL);
|
source.setFavoriteSport(Sport.FOOTBALL);
|
||||||
source.cyclic = source;
|
source.cyclic = source;
|
||||||
Person target = new Person();
|
Person target = new Person();
|
||||||
mapper.map(source, target);
|
MapperFactory.defaultMapper().map(source, target);
|
||||||
assertEquals("Keith", target.getName());
|
assertEquals("Keith", target.getName());
|
||||||
assertEquals(3, target.getAge());
|
assertEquals(3, target.getAge());
|
||||||
assertEquals(Sport.FOOTBALL, target.getFavoriteSport());
|
assertEquals(Sport.FOOTBALL, target.getFavoriteSport());
|
||||||
|
|
@ -411,133 +437,209 @@ public class SpelMapperTests {
|
||||||
|
|
||||||
Order target = new Order();
|
Order target = new Order();
|
||||||
|
|
||||||
mapper.map(source, target);
|
MapperFactory.defaultMapper().map(source, target);
|
||||||
assertEquals(1, target.getNumber());
|
assertEquals(1, target.getNumber());
|
||||||
assertTrue(item != target.getLineItem());
|
assertTrue(item != target.getLineItem());
|
||||||
assertEquals(new BigDecimal("30.00"), target.getLineItem().getAmount());
|
assertEquals(new BigDecimal("30.00"), target.getLineItem().getAmount());
|
||||||
assertEquals(source, target.getLineItem().getOrder());
|
assertEquals(source, target.getLineItem().getOrder());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
public static class EmployeeDto {
|
||||||
public void mapCustomMappableType() throws Exception {
|
|
||||||
XmlDocumentLoader loader = new XmlDocumentLoader();
|
|
||||||
loader.setValidating(false);
|
|
||||||
|
|
||||||
Object source = loader.loadDocument(new ClassPathResource("order.xml", getClass())).getDocumentElement();
|
private String firstName;
|
||||||
Order target = new Order();
|
|
||||||
|
|
||||||
MappableTypeFactory factory = new MappableTypeFactory();
|
private String lastName;
|
||||||
factory.add(new ElementMappableType());
|
|
||||||
factory.add(new BeanMappableType());
|
|
||||||
mapper.setMappableTypeFactory(factory);
|
|
||||||
mapper.map(source, target);
|
|
||||||
|
|
||||||
assertEquals(1, target.getNumber());
|
public String getFirstName() {
|
||||||
}
|
return firstName;
|
||||||
|
|
||||||
static class ElementMappableType implements MappableType<Element> {
|
|
||||||
|
|
||||||
public boolean isInstance(Object object) {
|
|
||||||
return object instanceof Element;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getFields(Element object) {
|
public void setFirstName(String firstName) {
|
||||||
NamedNodeMap map = object.getAttributes();
|
this.firstName = firstName;
|
||||||
Set<String> fields = new LinkedHashSet<String>();
|
|
||||||
for (int i = 0; i < map.getLength(); i++) {
|
|
||||||
fields.add(map.item(i).getNodeName());
|
|
||||||
}
|
|
||||||
return fields;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EvaluationContext getEvaluationContext(Element object, ConversionService conversionService) {
|
public String getLastName() {
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
return lastName;
|
||||||
context.setRootObject(object);
|
}
|
||||||
context.setTypeConverter(new StandardTypeConverter(conversionService));
|
|
||||||
context.addPropertyAccessor(new PropertyAccessor() {
|
|
||||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
|
||||||
Element e = (Element) target;
|
|
||||||
return e.hasAttribute(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
public void setLastName(String lastName) {
|
||||||
return canRead(context, target, name);
|
this.lastName = lastName;
|
||||||
}
|
|
||||||
|
|
||||||
public Class<?>[] getSpecificTargetClasses() {
|
|
||||||
return new Class[] { Element.class };
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
|
||||||
Element e = (Element) target;
|
|
||||||
return new TypedValue(e.getAttribute(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(EvaluationContext context, Object target, String name, Object newValue)
|
|
||||||
throws AccessException {
|
|
||||||
Element e = (Element) target;
|
|
||||||
e.setAttribute(name, (String) newValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
public static class Employee {
|
||||||
public void mapCustomMappableTypeNotSupported() throws Exception {
|
|
||||||
Order source = new Order();
|
private String firstName;
|
||||||
Order target = new Order();
|
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
MappableTypeFactory factory = new MappableTypeFactory();
|
|
||||||
mapper.setMappableTypeFactory(factory);
|
|
||||||
mapper.map(source, target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Order {
|
public static class CreateAccountDto {
|
||||||
|
|
||||||
private int number;
|
private String accountNumber;
|
||||||
|
|
||||||
private LineItem lineItem;
|
private String name;
|
||||||
|
|
||||||
public int getNumber() {
|
private String address;
|
||||||
|
|
||||||
|
private String activationDate;
|
||||||
|
|
||||||
|
private String activationTime;
|
||||||
|
|
||||||
|
public String getAccountNumber() {
|
||||||
|
return accountNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccountNumber(String accountNumber) {
|
||||||
|
this.accountNumber = accountNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(String address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getActivationDate() {
|
||||||
|
return activationDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivationDate(String activationDate) {
|
||||||
|
this.activationDate = activationDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getActivationTime() {
|
||||||
|
return activationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivationTime(String activationTime) {
|
||||||
|
this.activationTime = activationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Account {
|
||||||
|
|
||||||
|
private String number;
|
||||||
|
|
||||||
|
private String firstName;
|
||||||
|
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
private Address address;
|
||||||
|
|
||||||
|
private DateTime activationDateTime;
|
||||||
|
|
||||||
|
public String getNumber() {
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNumber(int number) {
|
public void setNumber(String number) {
|
||||||
this.number = number;
|
this.number = number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LineItem getLineItem() {
|
public String getFirstName() {
|
||||||
return lineItem;
|
return firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLineItem(LineItem lineItem) {
|
public void setFirstName(String firstName) {
|
||||||
this.lineItem = lineItem;
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(Address address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime getActivationDateTime() {
|
||||||
|
return activationDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivationDateTime(DateTime activationDateTime) {
|
||||||
|
this.activationDateTime = activationDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LineItem {
|
public static class Address {
|
||||||
|
|
||||||
private BigDecimal amount;
|
private String street;
|
||||||
|
|
||||||
private Order order;
|
private String city;
|
||||||
|
|
||||||
public BigDecimal getAmount() {
|
private String state;
|
||||||
return amount;
|
|
||||||
|
private String zip;
|
||||||
|
|
||||||
|
public String getStreet() {
|
||||||
|
return street;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAmount(BigDecimal amount) {
|
public void setStreet(String street) {
|
||||||
this.amount = amount;
|
this.street = street;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Order getOrder() {
|
public String getCity() {
|
||||||
return order;
|
return city;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOrder(Order order) {
|
public void setCity(String city) {
|
||||||
this.order = order;
|
this.city = city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(String state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getZip() {
|
||||||
|
return zip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZip(String zip) {
|
||||||
|
this.zip = zip;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -725,4 +827,52 @@ public class SpelMapperTests {
|
||||||
public enum Sport {
|
public enum Sport {
|
||||||
FOOTBALL, BASKETBALL
|
FOOTBALL, BASKETBALL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Order {
|
||||||
|
|
||||||
|
private int number;
|
||||||
|
|
||||||
|
private LineItem lineItem;
|
||||||
|
|
||||||
|
public int getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumber(int number) {
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LineItem getLineItem() {
|
||||||
|
return lineItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLineItem(LineItem lineItem) {
|
||||||
|
this.lineItem = lineItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LineItem {
|
||||||
|
|
||||||
|
private BigDecimal amount;
|
||||||
|
|
||||||
|
private Order order;
|
||||||
|
|
||||||
|
public BigDecimal getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAmount(BigDecimal amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrder(Order order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
package org.springframework.mapping.support;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.springframework.core.io.Resource;
|
|
||||||
import org.springframework.util.xml.SimpleSaxErrorHandler;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
import org.xml.sax.EntityResolver;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
|
|
||||||
public class XmlDocumentLoader {
|
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(XmlDocumentLoader.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JAXP attribute used to configure the schema language for validation.
|
|
||||||
*/
|
|
||||||
private static final String SCHEMA_LANGUAGE_ATTRIBUTE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JAXP attribute value indicating the XSD schema language.
|
|
||||||
*/
|
|
||||||
private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag indicating if the XML document parser will perform schema validation.
|
|
||||||
*/
|
|
||||||
private boolean validating = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The spring-webflow schema resolution strategy.
|
|
||||||
*/
|
|
||||||
private EntityResolver entityResolver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether or not the XML parser will validate the document.
|
|
||||||
*/
|
|
||||||
public boolean isValidating() {
|
|
||||||
return validating;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set if the XML parser should validate the document and thus enforce a schema. Defaults to true.
|
|
||||||
*/
|
|
||||||
public void setValidating(boolean validating) {
|
|
||||||
this.validating = validating;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the SAX entity resolver used by the XML parser.
|
|
||||||
*/
|
|
||||||
public EntityResolver getEntityResolver() {
|
|
||||||
return entityResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a SAX entity resolver to be used for parsing. Can be overridden for custom entity resolution, for example
|
|
||||||
* relative to some specific base path.
|
|
||||||
*/
|
|
||||||
public void setEntityResolver(EntityResolver entityResolver) {
|
|
||||||
this.entityResolver = entityResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Document loadDocument(Resource resource) throws IOException, ParserConfigurationException, SAXException {
|
|
||||||
InputStream is = null;
|
|
||||||
try {
|
|
||||||
is = resource.getInputStream();
|
|
||||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
||||||
factory.setValidating(isValidating());
|
|
||||||
factory.setNamespaceAware(true);
|
|
||||||
try {
|
|
||||||
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
throw new IllegalStateException("Unable to validate using XSD: Your JAXP provider [" + factory
|
|
||||||
+ "] does not support XML Schema. "
|
|
||||||
+ "Are you running on Java 1.4 or below with Apache Crimson? "
|
|
||||||
+ "If so you must upgrade to Apache Xerces (or Java 5 or >) for full XSD support.");
|
|
||||||
}
|
|
||||||
DocumentBuilder docBuilder = factory.newDocumentBuilder();
|
|
||||||
docBuilder.setErrorHandler(new SimpleSaxErrorHandler(logger));
|
|
||||||
docBuilder.setEntityResolver(getEntityResolver());
|
|
||||||
return docBuilder.parse(is);
|
|
||||||
} finally {
|
|
||||||
if (is != null) {
|
|
||||||
is.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<order number="1">
|
|
||||||
<lineitem amount="30.00" />
|
|
||||||
</order>
|
|
||||||
Loading…
Reference in New Issue