custom mappable type tests
This commit is contained in:
parent
8fd3de9397
commit
96cfa0c37d
|
|
@ -43,6 +43,6 @@ public class MappableTypeFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Object of type [" + object.getClass().getName()
|
throw new IllegalArgumentException("Object of type [" + object.getClass().getName()
|
||||||
+ "] not mappable - no suitable MappableType exists");
|
+ "] is not mappable - no suitable MappableType exists");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +208,7 @@ public class SpelMapper implements Mapper<Object, Object> {
|
||||||
|
|
||||||
// internal helpers
|
// internal helpers
|
||||||
|
|
||||||
private Class[] getRequiredTypeInfo(Mapper mapper) {
|
private Class<?>[] getRequiredTypeInfo(Mapper<?, ?> mapper) {
|
||||||
return GenericTypeResolver.resolveTypeArguments(mapper.getClass(), Mapper.class);
|
return GenericTypeResolver.resolveTypeArguments(mapper.getClass(), Mapper.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,25 @@ 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.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
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 SpelMapperTests {
|
||||||
|
|
||||||
|
|
@ -339,6 +351,82 @@ public class SpelMapperTests {
|
||||||
assertEquals(source, target.getLineItem().getOrder());
|
assertEquals(source, target.getLineItem().getOrder());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mapCustomMappableType() throws Exception {
|
||||||
|
XmlDocumentLoader loader = new XmlDocumentLoader();
|
||||||
|
loader.setValidating(false);
|
||||||
|
|
||||||
|
Object source = loader.loadDocument(new ClassPathResource("order.xml", getClass())).getDocumentElement();
|
||||||
|
Order target = new Order();
|
||||||
|
|
||||||
|
MappableTypeFactory factory = new MappableTypeFactory();
|
||||||
|
factory.add(new ElementMappableType());
|
||||||
|
factory.add(new BeanMappableType());
|
||||||
|
mapper.setMappableTypeFactory(factory);
|
||||||
|
mapper.map(source, target);
|
||||||
|
|
||||||
|
assertEquals(1, target.getNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ElementMappableType implements MappableType<Element> {
|
||||||
|
|
||||||
|
public boolean isInstance(Object object) {
|
||||||
|
return object instanceof Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getFields(Element object) {
|
||||||
|
NamedNodeMap map = object.getAttributes();
|
||||||
|
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) {
|
||||||
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||||
|
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 {
|
||||||
|
return canRead(context, target, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 void mapCustomMappableTypeNotSupported() throws Exception {
|
||||||
|
Order source = new Order();
|
||||||
|
Order target = new Order();
|
||||||
|
|
||||||
|
MappableTypeFactory factory = new MappableTypeFactory();
|
||||||
|
mapper.setMappableTypeFactory(factory);
|
||||||
|
mapper.map(source, target);
|
||||||
|
}
|
||||||
|
|
||||||
public static class Order {
|
public static class Order {
|
||||||
|
|
||||||
private int number;
|
private int number;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
<order number="1">
|
||||||
|
<lineitem amount="30.00" />
|
||||||
|
</order>
|
||||||
Loading…
Reference in New Issue