custom mappable type tests

This commit is contained in:
Keith Donald 2009-10-07 21:33:53 +00:00
parent 8fd3de9397
commit 96cfa0c37d
5 changed files with 189 additions and 2 deletions

View File

@ -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");
} }
} }

View File

@ -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);
} }

View File

@ -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;

View File

@ -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();
}
}
}
}

View File

@ -0,0 +1,3 @@
<order number="1">
<lineitem amount="30.00" />
</order>