practicalxml-commits Mailing List for Practical XML (Page 9)
Brought to you by:
kdgregory
You can subscribe to this list here.
2008 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(6) |
Nov
(4) |
Dec
(35) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2009 |
Jan
(5) |
Feb
|
Mar
|
Apr
(7) |
May
|
Jun
|
Jul
(12) |
Aug
(24) |
Sep
(39) |
Oct
(16) |
Nov
(4) |
Dec
(7) |
2010 |
Jan
(10) |
Feb
|
Mar
(2) |
Apr
(1) |
May
|
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
(4) |
Dec
(3) |
2011 |
Jan
(1) |
Feb
|
Mar
(1) |
Apr
(1) |
May
(3) |
Jun
|
Jul
|
Aug
(1) |
Sep
(10) |
Oct
(1) |
Nov
(1) |
Dec
(7) |
2012 |
Jan
(1) |
Feb
|
Mar
|
Apr
(1) |
May
(9) |
Jun
|
Jul
(5) |
Aug
(6) |
Sep
|
Oct
(1) |
Nov
|
Dec
|
2013 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(16) |
Jul
|
Aug
(6) |
Sep
(10) |
Oct
|
Nov
(2) |
Dec
|
2014 |
Jan
(5) |
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(2) |
Aug
|
Sep
|
Oct
(6) |
Nov
|
Dec
|
2015 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(5) |
2016 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(1) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-17 17:13:45
|
Revision: 109 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=109&view=rev Author: kdgregory Date: 2009-08-17 17:13:36 +0000 (Mon, 17 Aug 2009) Log Message: ----------- implement XML->Bean conversions Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanOptions.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/AbstractBeanConverterTestCase.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java Added Paths: ----------- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanDriver.java Removed Paths: ------------- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java 2009-08-15 18:57:35 UTC (rev 108) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java 2009-08-17 17:13:36 UTC (rev 109) @@ -32,6 +32,21 @@ */ public class ConversionHelper { + private static Map<String,Class<?>> XSD_TYPE_TO_JAVA_CLASS + = new HashMap<String,Class<?>>(); + static + { + XSD_TYPE_TO_JAVA_CLASS.put("xsd:string", String.class); + XSD_TYPE_TO_JAVA_CLASS.put("xsd:boolean", Boolean.class); + XSD_TYPE_TO_JAVA_CLASS.put("xsd:byte", Byte.class); + XSD_TYPE_TO_JAVA_CLASS.put("xsd:short", Short.class); + XSD_TYPE_TO_JAVA_CLASS.put("xsd:int", Integer.class); + XSD_TYPE_TO_JAVA_CLASS.put("xsd:long", Long.class); + XSD_TYPE_TO_JAVA_CLASS.put("xsd:decimal", BigDecimal.class); + XSD_TYPE_TO_JAVA_CLASS.put("xsd:dateTime", Date.class); + } + + // this is not static because the helpers are inner classes private Map<Class<?>,ConversionHandler<?>> _helpers = new HashMap<Class<?>,ConversionHandler<?>>(); @@ -48,6 +63,14 @@ _helpers.put(BigInteger.class, new BigIntegerConversionHandler()); _helpers.put(BigDecimal.class, new BigDecimalConversionHandler()); _helpers.put(Date.class, new DateConversionHandler()); + + _helpers.put(Boolean.TYPE, new BooleanConversionHandler()); + _helpers.put(Byte.TYPE, new ByteConversionHandler()); + _helpers.put(Short.TYPE, new ShortConversionHandler()); + _helpers.put(Integer.TYPE, new IntegerConversionHandler()); + _helpers.put(Long.TYPE, new LongConversionHandler()); + _helpers.put(Float.TYPE, new FloatConversionHandler()); + _helpers.put(Double.TYPE, new DoubleConversionHandler()); } private boolean _useXsdFormatting; @@ -85,6 +108,16 @@ ConversionHandler<?> helper = _helpers.get(klass); return (helper == null) ? null : helper.getXsiType(); } + + + /** + * Returns the Java type that best matches the given Schema type name, + * <code>null</code> if we do not have an appropriate mapping. + */ + public Class<?> getJavaType(String xsdTypename) + { + return XSD_TYPE_TO_JAVA_CLASS.get(xsdTypename); + } /** Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanDriver.java 2009-08-15 18:57:35 UTC (rev 108) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanDriver.java 2009-08-17 17:13:36 UTC (rev 109) @@ -14,22 +14,434 @@ package net.sf.practicalxml.converter.xml2bean; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import javax.xml.XMLConstants; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.converter.ConversionException; +import net.sf.practicalxml.converter.ConversionHelper; +import net.sf.practicalxml.internal.StringUtils; + import org.w3c.dom.Element; +import org.w3c.dom.Node; /** - * Driver class for converting an XML DOM into a Java bean, using a {@link - * Xml2BeanHandler} to manage data conversion. + * Driver class for converting an XML DOM into a Java bean. Normal usage is + * to create a single instance of this class with desired options, then use + * it for multiple conversions. This class is thread-safe. + * <p> + * This class assumes that the source XML will be in the form produced by + * {@link Bean2XmlDriver}: each element either contains child elements, or + * a text node containing the element's value. However, conversion is + * driven by the parameters passed to {@link #convert}, not by the content + * of the XML document; this can lead to some unexpected behavior: + * <ul> + * <li> Bean classes are introspected, and recursively processed base on + * the property descriptors. If the bean has multiple setter methods + * for a property, the method selected is arbitrarily chosen by the + * JavaBeans introspector. You can pass an option that looks for a + * setters using <code>String</code>, but this is not recommended + * from a performance perspective. + * <li> JDK collection types do not carry type information, so the only + * way to properly convert them is using an <code>xsi:type</code> + * attribute on the child elements. If this attribute is missing + * or cannot be interpreted, the element will be processed as if + * it is a <code>String</code>. + * <li> The converter will to pick an appropriate implementation class + * if given one of the JDK collections interfaces: <code> + * ArrayList</code> for <code>List</code> or <code>Collection</code>, + * <code>TreeSet</code> for <code>SortedSet</code>, <code>HashSet</code> + * for any other <code>Set</code>, and likewise <code>TreeMap</code> and + * <code>HashMap</code> for <code>SortedMap</code> and <code>Map</code>. + * </ul> */ public class Xml2BeanDriver { + private EnumSet<Xml2BeanOptions> _options; + private ConversionHelper _helper; + private Map<Class<?>,Map<String,Method>> _introspectedClasses; + + + public Xml2BeanDriver(Xml2BeanOptions... options) + { + _options = EnumSet.noneOf(Xml2BeanOptions.class); + for (Xml2BeanOptions option : options) + _options.add(option); + + _helper = new ConversionHelper(_options.contains(Xml2BeanOptions.EXPECT_XSD_FORMAT)); + _introspectedClasses = new HashMap<Class<?>,Map<String,Method>>(); + } + + +//---------------------------------------------------------------------------- +// Public Methods +//---------------------------------------------------------------------------- + /** - * Fills a bean-style object from an XML element. For each child of the - * passed element, tries to find a property with the same name, invokes - * the appropriate conversion handler method, and sets the property from - * the result. + * Attempts to convert the passed DOM subtree into an object of the + * specified class. */ - public void dispatch(Element elem, Object bean) + public <T> T convert(Element elem, Class<T> klass) { + return klass.cast(convertWithoutCast(elem, klass)); } + + +//---------------------------------------------------------------------------- +// Internal Conversion Methods +//---------------------------------------------------------------------------- + + + + /** + * Attempts to convert the passed DOM subtree into an object of the + * specified class. Note that this version does not use generics, + * and does not try to cast the result, whereas the public version + * does. Internally, we want to treat <code>Integer.TYPE</code> the + * same as <code>Integer.class</code>, and the cast prevents that. + */ + public Object convertWithoutCast(Element elem, Class<?> klass) + { + validateXsiType(elem, klass); + if (isAllowableNull(elem)) + return null; + + Object obj = tryConvertAsPrimitive(elem, klass); + if (obj == null) + obj = tryConvertAsArray(elem, klass); + if (obj == null) + obj = tryConvertAsSimpleCollection(elem, klass); + if (obj == null) + obj = tryConvertAsMap(elem, klass); + if (obj == null) + obj = tryConvertAsBean(elem, klass); + return obj; + } + + + private boolean isAllowableNull(Element elem) + { + String text = getText(elem); + if ((text != null) || hasElementChildren(elem)) + return false; + + if (_options.contains(Xml2BeanOptions.REQUIRE_XSI_NIL)) + { + String attr = elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil"); + if (!attr.equals("true")) + throw new ConversionException( + "missing xsi:nil: " + DomUtil.getAbsolutePath(elem)); + } + + return true; + } + + + private Object tryConvertAsPrimitive(Element elem, Class<?> klass) + { + if (_helper.getXsdType(klass) == null) + return null; + + if (hasElementChildren(elem)) + throw new ConversionException( + "expecting primitive; has children: " + DomUtil.getAbsolutePath(elem)); + + return _helper.parse(getText(elem), klass); + } + + + private Object tryConvertAsArray(Element elem, Class<?> klass) + { + Class<?> childKlass = klass.getComponentType(); + if (childKlass == null) + return null; + + List<Element> children = DomUtil.getChildren(elem); + Object result = Array.newInstance(childKlass, children.size()); + int idx = 0; + for (Element child : children) + { + Array.set(result, idx++, convertWithoutCast(child, childKlass)); + } + return result; + } + + + private Object tryConvertAsSimpleCollection(Element elem, Class<?> klass) + { + Collection<Object> result = instantiateCollection(klass); + if (result == null) + return null; + + List<Element> children = DomUtil.getChildren(elem); + for (Element child : children) + { + Class<?> childClass = getClassFromXsiType(child); + if (childClass == null) + childClass = String.class; + result.add(convertWithoutCast(child, childClass)); + } + return result; + } + + + private Object tryConvertAsMap(Element elem, Class<?> klass) + { + Map<Object,Object> result = instantiateMap(klass); + if (result == null) + return null; + + List<Element> children = DomUtil.getChildren(elem); + for (Element child : children) + { + String key = child.getAttribute("key"); + if (StringUtils.isEmpty(key)) + key = DomUtil.getLocalName(child); + Class<?> childClass = getClassFromXsiType(child); + if (childClass == null) + childClass = String.class; + result.put(key, convertWithoutCast(child, childClass)); + } + return result; + } + + + private Object tryConvertAsBean(Element elem, Class<?> klass) + { + Object bean = instantiateBean(elem, klass); + + List<Element> children = DomUtil.getChildren(elem); + for (Element child : children) + { + Method setter = getSetterMethod(klass, child); + if (setter == null) + continue; + + Class<?> childClass = setter.getParameterTypes()[0]; + Object childValue = convertWithoutCast(child, childClass); + invokeSetter(elem, bean, setter, childValue); + } + return bean; + } + + +//---------------------------------------------------------------------------- +// Other Internals +//---------------------------------------------------------------------------- + + /** + * Returns the text content of an element, applying appropriate options. + */ + private String getText(Element elem) + { + String text = DomUtil.getText(elem); + if (StringUtils.isBlank(text) + && _options.contains(Xml2BeanOptions.CONVERT_BLANK_AS_NULL)) + text = null; + return text; + } + + + /** + * Returns the <code>xsi:type</code> attribute value, <code>null</code> if + * it's not set. + */ + private String getXsiType(Element elem) + { + String xsiType = elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type"); + return StringUtils.isEmpty(xsiType) + ? null + : xsiType; + } + + + /** + * Examines an element's <code>xsi:type</code> attribute, if any, and + * returns the Java class corresponding to it. Used when converting + * collection types, which don't have type information that can be + * introspected, and also to validate non-XSD types. + */ + private Class<?> getClassFromXsiType(Element elem) + { + String xsiType = getXsiType(elem); + if (xsiType == null) + return null; + + if (xsiType.startsWith("java:")) + { + String javaType = xsiType.substring(5); + try + { + return Class.forName(javaType); + } + catch (ClassNotFoundException ee) + { + throw new ConversionException( + "invalid Java type specification (" + javaType + "): " + + DomUtil.getAbsolutePath(elem), + ee); + } + } + return _helper.getJavaType(xsiType); + } + + + private void validateXsiType(Element elem, Class<?> klass) + { + if (!_options.contains(Xml2BeanOptions.REQUIRE_XSI_TYPE)) + return; + + String xsiType = elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type"); + if (StringUtils.isEmpty(xsiType)) + throw new ConversionException( + "missing xsi:type: " + DomUtil.getAbsolutePath(elem)); + + if (xsiType.equals(_helper.getXsdType(klass))) + return; + + Class<?> xsiKlass = getClassFromXsiType(elem); + if (klass.isAssignableFrom(xsiKlass)) + return; + + throw new ConversionException( + "invalid xsi:type (\"" + xsiType + "\" for " + klass.getName() + "): " + + DomUtil.getAbsolutePath(elem)); + } + + + private boolean hasElementChildren(Element elem) + { + Node child = elem.getFirstChild(); + while (child != null) + { + if (child instanceof Element) + return true; + child = child.getNextSibling(); + } + return false; + } + + + private Method getSetterMethod(Class<?> beanKlass, Element child) + { + Map<String,Method> methodMap = _introspectedClasses.get(beanKlass); + if (methodMap == null) + methodMap = introspect(beanKlass); + + Method setter = methodMap.get(DomUtil.getLocalName(child)); + if ((setter == null) && !_options.contains(Xml2BeanOptions.IGNORE_MISSING_PROPERTIES)) + { + throw new ConversionException( + "can't find property setter in " + beanKlass.getName() + ": " + + DomUtil.getAbsolutePath(child)); + } + + return setter; + } + + + private Map<String,Method> introspect(Class<?> klass) + { + Map<String,Method> methodMap = new HashMap<String,Method>(); + try + { + BeanInfo info = Introspector.getBeanInfo(klass, Object.class); + for (PropertyDescriptor propDesc : info.getPropertyDescriptors()) + { + Method setter = propDesc.getWriteMethod(); + if (setter != null) + methodMap.put(propDesc.getName(), setter); + } + } + catch (IntrospectionException e) + { + throw new ConversionException("unable to introspect", e); + } + + _introspectedClasses.put(klass, methodMap); + return methodMap; + } + + + /** + * Attempts to create a <code>Collection</code> instance appropriate for + * the passed class, returns <code>null</code> if unable. + */ + private Collection<Object> instantiateCollection(Class<?> klass) + { + if (SortedSet.class.isAssignableFrom(klass)) + return new TreeSet<Object>(); + else if (Set.class.isAssignableFrom(klass)) + return new HashSet<Object>(); + else if (List.class.isAssignableFrom(klass)) + return new ArrayList<Object>(); + else if (Collection.class.isAssignableFrom(klass)) + return new ArrayList<Object>(); + else + return null; + } + + + /** + * Attempts to create a <code>Map</code> instance appropriate for the + * passed class, returns <code>null</code> if unable. + */ + private Map<Object,Object> instantiateMap(Class<?> klass) + { + if (SortedMap.class.isAssignableFrom(klass)) + return new TreeMap<Object,Object>(); + else if (Map.class.isAssignableFrom(klass)) + return new HashMap<Object,Object>(); + else + return null; + } + + + private Object instantiateBean(Element elem, Class<?> klass) + { + try + { + return klass.newInstance(); + } + catch (Exception ee) + { + throw new ConversionException( + "unable to instantiate bean: " + DomUtil.getAbsolutePath(elem), + ee); + } + } + + + private void invokeSetter(Element elem, Object bean, Method setter, Object value) + { + try + { + setter.invoke(bean, value); + } + catch (Exception ee) + { + throw new ConversionException( + "unable to set property: " + DomUtil.getAbsolutePath(elem), + ee); + } + } } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanOptions.java 2009-08-15 18:57:35 UTC (rev 108) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanOptions.java 2009-08-17 17:13:36 UTC (rev 109) @@ -30,25 +30,22 @@ /** - * If present, the converter ignores elements that don't correspond to - * settable properties of the bean. + * Expect data (in particular, dates) to be formatted per XML Schema spec. */ - IGNORE_MISSING_PROPERTIES, + EXPECT_XSD_FORMAT, /** - * If present, the converter will ignore any objects that cannot be - * converted (many of the JDK-provided classes fall into this category, - * because they're not bean-structured). + * If present, the converter ignores elements that don't correspond to + * settable properties of the bean. */ - IGNORE_UNCONVERTIBLE_OBJECTS, + IGNORE_MISSING_PROPERTIES, /** - * If present, the converter will use a setter method taking a String, in - * preference to any other type. Default behavior is to pick the method - * with the most restrictive type (and considering numeric types as more - * restrictive than <code>String</code>). + * If present, the converter will look for a setter method taking a + * <code>String</code>, in preference to a non-string method returned + * from the bean introspector. */ PREFER_STRING_SETTER, Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/AbstractBeanConverterTestCase.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/AbstractBeanConverterTestCase.java 2009-08-15 18:57:35 UTC (rev 108) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/AbstractBeanConverterTestCase.java 2009-08-17 17:13:36 UTC (rev 109) @@ -81,7 +81,7 @@ new PrimitiveValue(Double.valueOf(1234567890.5), "xsd:decimal", "1234567890.5"), new PrimitiveValue(new BigInteger("123456789012345"), "xsd:decimal", "123456789012345"), new PrimitiveValue(new BigDecimal("123456789012345.123456789012345"), "xsd:decimal", "123456789012345.123456789012345"), - new PrimitiveValue(new Date(1247551703704L), "xsd:dateTime", "2009-07-14T06:08:23") + new PrimitiveValue(new Date(1247551703000L), "xsd:dateTime", "2009-07-14T06:08:23") }; @@ -170,23 +170,23 @@ assertXsiType(message, elem, expectedType); assertXsiNil(message, elem, isNil); } - + protected void assertNameTypeValue( - Element elem, + Element elem, String expectedName, String expectedType, String expectedValue) { assertNameTypeValue("", elem, expectedName, expectedType, expectedValue); } - - + + protected void assertNameTypeValue( - String message, Element elem, + String message, Element elem, String expectedName, String expectedType, String expectedValue) { if (message.length() > 0) message += " "; - + assertName(message + "name", elem, expectedName); assertXsiType(message + "xsi:type", elem, expectedType); assertValue(message + "value", elem, expectedValue); Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java 2009-08-15 18:57:35 UTC (rev 108) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java 2009-08-17 17:13:36 UTC (rev 109) @@ -54,6 +54,20 @@ // Test Cases //---------------------------------------------------------------------------- + public void testClassToXsdType() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertEquals("xsd:string", helper.getXsdType(String.class)); + assertEquals("xsd:string", helper.getXsdType(Character.class)); + assertEquals(String.class, helper.getJavaType("xsd:string")); + + assertEquals("xsd:boolean", helper.getXsdType(Boolean.class)); + assertEquals(Boolean.class, helper.getJavaType("xsd:boolean")); + + } + + public void testUnknownClass() throws Exception { ConversionHelper helper = new ConversionHelper(); Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanDriver.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanDriver.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanDriver.java 2009-08-17 17:13:36 UTC (rev 109) @@ -0,0 +1,708 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +import javax.xml.XMLConstants; +import org.w3c.dom.Element; + +import net.sf.practicalxml.converter.xml2bean.Xml2BeanDriver; +import net.sf.practicalxml.converter.xml2bean.Xml2BeanOptions; + + +import static net.sf.practicalxml.builder.XmlBuilder.*; + + +public class TestXml2BeanDriver +extends AbstractBeanConverterTestCase +{ + public TestXml2BeanDriver(String name) + { + super(name); + } + + +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + private static Element createTestData(net.sf.practicalxml.builder.Node... childNodes) + { + return element("root", childNodes) + .toDOM().getDocumentElement(); + } + + + private static net.sf.practicalxml.builder.Node xsiType(String typeName) + { + return attribute(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, + "type", + typeName); + } + + + private static net.sf.practicalxml.builder.Node xsiNil(boolean isNil) + { + return attribute(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, + "nil", + isNil ? "true" : "false"); + } + + + private static void assertConversionError( + String message, Xml2BeanDriver driver, Element elem, Class<?> klass) + { + try + { + driver.convert(elem, klass); + fail(message); + } + catch (ConversionException ee) + { /* success */ } + } + + +//---------------------------------------------------------------------------- +// Test Classes +//---------------------------------------------------------------------------- + + public static class ReadOnlyBean + { + private String _sval; + public String getSval() { return _sval; } + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + public void testConvertPrimitivesDefault() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + for (PrimitiveValue value : PRIMITIVE_VALUES) + { + Element src = createTestData(text(value.getDefaultText())); + Object dst = driver.convert(src, value.getKlass()); + assertEquals(value.getKlass().getName(), value.getValue(), dst); + } + } + + + public void testConvertPrimitivesXsdFormat() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(Xml2BeanOptions.EXPECT_XSD_FORMAT); + + for (PrimitiveValue value : PRIMITIVE_VALUES) + { + Element src = createTestData(text(value.getXsdText())); + Object dst = driver.convert(src, value.getKlass()); + assertEquals(value.getKlass().getName(), value.getValue(), dst); + } + } + + + public void testConvertPrimitivesRequireXsiType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(Xml2BeanOptions.REQUIRE_XSI_TYPE); + + Element valid = createTestData(text("foo"), xsiType("xsd:string")); + Object dst = driver.convert(valid, String.class); + assertEquals("foo", dst); + + Element invalid = createTestData(text("foo")); + assertConversionError("converted element missing xsi:type", + driver, invalid, String.class); + } + + + public void testConvertPrimitiveWithWrongXsiType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(Xml2BeanOptions.REQUIRE_XSI_TYPE); + + Element invalid = createTestData(text("foo"), xsiType("xsd:int")); + assertConversionError("converted element with incorrect xsi:type", + driver, invalid, String.class); + } + + + public void testConvertPrimitiveWithChildElement() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element invalid = createTestData(text("foo"), element("bar")); + assertConversionError("converted primitive with element content", + driver, invalid, String.class); + } + + + public void testConvertNullDefault() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element src = createTestData(); + assertNull(driver.convert(src, String.class)); + } + + + public void testConvertNullRequireXsiNull() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(Xml2BeanOptions.REQUIRE_XSI_NIL); + + Element valid = createTestData(xsiNil(true)); + assertNull(driver.convert(valid, String.class)); + + Element invalid = createTestData(); + assertConversionError("able to convert null data with REQUIRE_XSI_NIL set", + driver, invalid, String.class); + } + + + public void testConvertEmptyStringDefault() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + String str = " \n \t "; + Element src = createTestData(text(str)); + Object dst = driver.convert(src, String.class); + assertEquals(str, dst); + } + + + public void testConvertEmptyStringToNull() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(Xml2BeanOptions.CONVERT_BLANK_AS_NULL); + + Element src = createTestData(text(" \n\t ")); + assertNull(driver.convert(src, String.class)); + } + + + public void testConvertEmptyStringToNullAndRequireXsiNull() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver( + Xml2BeanOptions.CONVERT_BLANK_AS_NULL, + Xml2BeanOptions.REQUIRE_XSI_NIL); + + Element valid = createTestData(text(" \t "), xsiNil(true)); + assertNull(driver.convert(valid, String.class)); + + Element invalid = createTestData(text(" \t ")); + assertConversionError("able to convert blank data with CONVERT_BLANK_AS_NULL and REQUIRE_XSI_NIL set", + driver, invalid, String.class); + } + + + public void testConvertPrimitiveArray() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + // note that the child element names shouldn't matter ... doesn't + // have to be the "data" of Bean2Xml + Element data = createTestData( + element("foo", text("12")), + element("bar", text("78")), + element("baz", text("-17"))); + + int[] result = driver.convert(data, int[].class); + assertEquals(3, result.length); + assertEquals(12, result[0]); + assertEquals(78, result[1]); + assertEquals(-17, result[2]); + } + + + public void testConvertPrimitiveArrayRequireXsiType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(Xml2BeanOptions.REQUIRE_XSI_TYPE); + + Element valid = createTestData( + xsiType("java:" + int[].class.getName()), + element("foo", text("12"), xsiType("xsd:int"))); + + int[] result = driver.convert(valid, int[].class); + assertEquals(1, result.length); + assertEquals(12, result[0]); + + Element invalid = createTestData( + element("foo", text("12"), xsiType("xsd:int"))); + + assertConversionError("able to convert with REQUIRE_XSI_TYPE set", + driver, invalid, int[].class); + } + + + public void testConvertListAssumingString() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + // note that the child element names shouldn't matter ... doesn't + // have to be the "data" of Bean2Xml + Element data = createTestData( + element("a", text("foo")), + element("b", text("bar")), + element("b", text("baz")), + element("c", text("bar"))); + + List<?> result = driver.convert(data, List.class); + assertEquals(4, result.size()); + + Iterator<?> itx = result.iterator(); + assertEquals("foo", itx.next()); + assertEquals("bar", itx.next()); + assertEquals("baz", itx.next()); + assertEquals("bar", itx.next()); + } + + + public void testConvertListWithXsiType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("a", text("foo"), xsiType("xsd:string")), + element("b", text("123"), xsiType("xsd:int")), + element("b", text("123.0"), xsiType("xsd:decimal")), + element("c", text("456"), xsiType("xsd:string"))); + + List<?> result = driver.convert(data, List.class); + assertEquals(4, result.size()); + + Iterator<?> itx = result.iterator(); + assertEquals("foo", itx.next()); + assertEquals(Integer.valueOf(123), itx.next()); + assertEquals(new BigDecimal("123.0"), itx.next()); + assertEquals("456", itx.next()); + } + + public void testConvertListWithJavaType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("a", text("1234"), + xsiType("java:java.math.BigInteger"))); + + List<?> result = driver.convert(data, List.class); + assertEquals(1, result.size()); + assertEquals(new BigInteger("1234"), result.iterator().next()); + + } + + + public void testConvertListWithBogusJavaType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("a", text("foo"), xsiType("java:foo"))); + + assertConversionError("converted unknown type", + driver, data, List.class); + } + + + public void testConvertSortedSetAssumingString() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("a", text("foo")), + element("b", text("bar")), + element("b", text("baz")), + element("c", text("bar"))); + + SortedSet<?> result = driver.convert(data, SortedSet.class); + assertEquals(3, result.size()); + + Iterator<?> itx = result.iterator(); + assertEquals("bar", itx.next()); + assertEquals("baz", itx.next()); + assertEquals("foo", itx.next()); + } + + + public void testConvertSetAssumingString() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("a", text("foo")), + element("b", text("bar")), + element("b", text("baz")), + element("c", text("bar"))); + + Set<?> result = driver.convert(data, Set.class); + assertEquals(3, result.size()); + assertTrue(result.contains("bar")); + assertTrue(result.contains("baz")); + assertTrue(result.contains("foo")); + } + + + public void testConvertCollectionAssumingString() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("a", text("foo")), + element("b", text("bar")), + element("b", text("baz")), + element("c", text("bar"))); + + Collection<?> result = driver.convert(data, Collection.class); + assertEquals(4, result.size()); + + Iterator<?> itx = result.iterator(); + assertEquals("foo", itx.next()); + assertEquals("bar", itx.next()); + assertEquals("baz", itx.next()); + assertEquals("bar", itx.next()); + } + + + // this handles the case where we're processing a bean that uses interfaces + // but the source document has a concrete type + public void testConvertCollectionRequireXsiTypeWithConcreteType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(Xml2BeanOptions.REQUIRE_XSI_TYPE); + + Element data = createTestData( + xsiType("java:java.util.ArrayList"), + element("a", text("foo"), xsiType("xsd:string")), + element("b", text("123"), xsiType("xsd:int"))); + + Collection<?> result = driver.convert(data, Collection.class); + assertEquals(2, result.size()); + + Iterator<?> itx = result.iterator(); + assertEquals("foo", itx.next()); + assertEquals(Integer.valueOf(123), itx.next()); + } + + + + public void testConvertMapDefaultKeyAssumingString() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + // we want distinct child names -- and overlapping ones -- because + // the converter should ignore them -- also note duplicate key + Element data = createTestData( + element("a", text("foo"), attribute("key", "argle")), + element("b", text("bar"), attribute("key", "bargle")), + element("b", text("baz"), attribute("key", "argle")), + element("c", text("bar"), attribute("key", "wargle"))); + + Map<?,?> result = driver.convert(data, Map.class); + assertEquals(3, result.size()); + assertEquals("baz", result.get("argle")); + assertEquals("bar", result.get("bargle")); + assertEquals("bar", result.get("wargle")); + } + + + public void testConvertSortedMapDefaultKeyAssumingString() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("a", text("foo"), attribute("key", "argle")), + element("b", text("bar"), attribute("key", "bargle")), + element("c", text("arb"), attribute("key", "wargle")), + element("b", text("baz"), attribute("key", "argle"))); + + SortedMap<?,?> result = driver.convert(data, SortedMap.class); + assertEquals(3, result.size()); + + Iterator<?> itx = result.keySet().iterator(); + assertEquals("argle", itx.next()); + assertEquals("bargle", itx.next()); + assertEquals("wargle", itx.next()); + + assertEquals("baz", result.get("argle")); + assertEquals("bar", result.get("bargle")); + assertEquals("arb", result.get("wargle")); + } + + + public void testConvertMapNameAsKeyAssumingString() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + // no attributes this time, but note the duplicate element name + Element data = createTestData( + element("a", text("foo")), + element("b", text("bar")), + element("b", text("baz")), + element("c", text("bar"))); + + Map<?,?> result = driver.convert(data, Map.class); + assertEquals(3, result.size()); + assertEquals("foo", result.get("a")); + assertEquals("baz", result.get("b")); + assertEquals("bar", result.get("c")); + } + + + public void testConvertMapNameAsKeyUsingXsiType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + // this time we have unique element names + Element data = createTestData( + element("a", text("foo"), xsiType("xsd:string")), + element("b", text("123"), xsiType("xsd:int")), + element("c", text("123.0"), xsiType("xsd:decimal")), + element("d", text("456"), xsiType("xsd:string"))); + + Map<?,?> result = driver.convert(data, Map.class); + assertEquals(4, result.size()); + assertEquals("foo", result.get("a")); + assertEquals(Integer.valueOf(123), result.get("b")); + assertEquals(new BigDecimal("123.0"), result.get("c")); + assertEquals("456", result.get("d")); + } + + + public void testConvertMapNameAsKeyUsingJavaType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + // this time we have unique element names + Element data = createTestData( + element("b", text("123"), xsiType("java:java.lang.Integer"))); + + Map<?,?> result = driver.convert(data, Map.class); + assertEquals(1, result.size()); + assertEquals(Integer.valueOf(123), result.get("b")); + } + + + public void testSimpleBeanDefault() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("sval", text("foo")), + element("ival", text("123")), + element("dval", text("123.456")), + element("bval", text("true"))); + + SimpleBean result = driver.convert(data, SimpleBean.class); + assertEquals("foo", result.getSval()); + assertEquals(123, result.getIval()); + assertEquals(new BigDecimal("123.456"), result.getDval()); + assertEquals(true, result.isBval()); + } + + + public void testSimpleBeanWithMissingValues() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("sval", text("foo")), + element("ival", text("123"))); + + SimpleBean result = driver.convert(data, SimpleBean.class); + assertEquals("foo", result.getSval()); + assertEquals(123, result.getIval()); + assertNull(result.getDval()); + assertEquals(false, result.isBval()); + } + + + public void testSimpleBeanRequireXsiType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(Xml2BeanOptions.REQUIRE_XSI_TYPE); + + Element valid = createTestData( + xsiType("java:" + SimpleBean.class.getName()), + element("sval", text("foo"), xsiType("xsd:string")), + element("ival", text("123"), xsiType("xsd:int")), + element("dval", text("123.456"),xsiType("xsd:decimal")), + element("bval", text("true"), xsiType("xsd:boolean"))); + + SimpleBean result = driver.convert(valid, SimpleBean.class); + assertEquals("foo", result.getSval()); + assertEquals(123, result.getIval()); + assertEquals(new BigDecimal("123.456"), result.getDval()); + assertEquals(true, result.isBval()); + + Element invalid1 = createTestData( + element("sval", text("foo"), xsiType("xsd:string")), + element("ival", text("123"), xsiType("xsd:int")), + element("dval", text("123.456"),xsiType("xsd:decimal")), + element("bval", text("true"), xsiType("xsd:boolean"))); + assertConversionError("didn't throw when missing xsi:type on top level", + driver, invalid1, SimpleBean.class); + + Element invalid2 = createTestData( + xsiType("java:" + SimpleBean.class.getName()), + element("sval", text("foo")), + element("ival", text("123")), + element("dval", text("123.456")), + element("bval", text("true"))); + assertConversionError("didn't throw when missing xsi:type on component level", + driver, invalid2, SimpleBean.class); + } + + + public void testSimpleBeanWithExtraValues() throws Exception + { + Element data = createTestData( + element("sval", text("foo")), + element("ival", text("123")), + element("zippy", text("pinhead"))); + + Xml2BeanDriver driver1 = new Xml2BeanDriver(); + + assertConversionError("converted bean when extra fields present in XML", + driver1, data, SimpleBean.class); + + Xml2BeanDriver driver2 = new Xml2BeanDriver(Xml2BeanOptions.IGNORE_MISSING_PROPERTIES); + + SimpleBean result = driver2.convert(data, SimpleBean.class); + assertEquals("foo", result.getSval()); + assertEquals(123, result.getIval()); + assertNull(result.getDval()); + assertEquals(false, result.isBval()); + } + + + public void testBeanArray() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("idx0", + element("sval", text("foo")), + element("ival", text("123")), + element("dval", text("123.456")), + element("bval", text("true"))), + element("idx1", + element("sval", text("bar")), + element("ival", text("456")), + element("dval", text("456.789")), + element("bval", text("false")))); + + SimpleBean[] result = driver.convert(data, SimpleBean[].class); + assertEquals(2, result.length); + + assertEquals("foo", result[0].getSval()); + assertEquals(123, result[0].getIval()); + assertEquals(new BigDecimal("123.456"), result[0].getDval()); + assertEquals(true, result[0].isBval()); + + assertEquals("bar", result[1].getSval()); + assertEquals(456, result[1].getIval()); + assertEquals(new BigDecimal("456.789"), result[1].getDval()); + assertEquals(false, result[1].isBval()); + } + + + public void testConvertCompoundBean() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + + Element data = createTestData( + element("simple", + element("sval", text("foo")), + element("ival", text("123")), + element("dval", text("456")), + element("bval", text("true"))), + element("primArray", + element("idx1", text("1")), + element("idx2", text("2")), + element("idx3", text("3"))), + element("stringList", + element("idx1", text("foo")), + element("idx2", text("bar")), + element("idx3", text("baz")))); + + CompoundBean result = driver.convert(data, CompoundBean.class); + + assertEquals("foo", result.getSimple().getSval()); + assertEquals(123, result.getSimple().getIval()); + assertEquals(456, result.getSimple().getDval().intValue()); // laziness prevails + assertEquals(true, result.getSimple().isBval()); + assertEquals(1, result.getPrimArray()[0]); + assertEquals(2, result.getPrimArray()[1]); + assertEquals(3, result.getPrimArray()[2]); + assertEquals("foo", result.getStringList().get(0)); + assertEquals("bar", result.getStringList().get(1)); + assertEquals("baz", result.getStringList().get(2)); + } + + + public void testConvertCompoundBeanRequireXsiType() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(Xml2BeanOptions.REQUIRE_XSI_TYPE); + + Element data = createTestData( + xsiType("java:" + CompoundBean.class.getName()), + element("simple", + xsiType("java:" + SimpleBean.class.getName()), + element("sval", text("foo"), xsiType("xsd:string")), + element("ival", text("123"), xsiType("xsd:int")), + element("dval", text("456"), xsiType("xsd:decimal")), + element("bval", text("true"), xsiType("xsd:boolean"))), + element("primArray", + xsiType("java:" + int[].class.getName()), + element("idx1", text("1"), xsiType("xsd:int")), + element("idx2", text("2"), xsiType("xsd:int")), + element("idx3", text("3"), xsiType("xsd:int"))), + element("stringList", + xsiType("java:" + List.class.getName()), + element("idx1", text("foo"), xsiType("xsd:string")), + element("idx2", text("bar"), xsiType("xsd:string")), + element("idx3", text("baz"), xsiType("xsd:string")))); + + CompoundBean result = driver.convert(data, CompoundBean.class); + + assertEquals("foo", result.getSimple().getSval()); + assertEquals(123, result.getSimple().getIval()); + assertEquals(456, result.getSimple().getDval().intValue()); + assertEquals(true, result.getSimple().isBval()); + assertEquals(1, result.getPrimArray()[0]); + assertEquals(2, result.getPrimArray()[1]); + assertEquals(3, result.getPrimArray()[2]); + assertEquals("foo", result.getStringList().get(0)); + assertEquals("bar", result.getStringList().get(1)); + assertEquals("baz", result.getStringList().get(2)); + } + + + public void testReadOnlyBean() throws Exception + { + Xml2BeanDriver driver = new Xml2BeanDriver(); + Element data = createTestData( + element("sval", text("foo"))); + + assertConversionError("converted bean without setter", + driver, data, ReadOnlyBean.class); + } +} Property changes on: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanDriver.java ___________________________________________________________________ Added: svn:executable + * Deleted: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java 2009-08-15 18:57:35 UTC (rev 108) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java 2009-08-17 17:13:36 UTC (rev 109) @@ -1,409 +0,0 @@ -// Copyright 2008-2009 severally by the contributors -// -// 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 net.sf.practicalxml.converter; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import javax.xml.XMLConstants; -import org.w3c.dom.Element; - -import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.builder.XmlBuilder; -import net.sf.practicalxml.converter.xml2bean.Xml2BeanHandler; -import net.sf.practicalxml.converter.xml2bean.Xml2BeanOptions; - - -public class TestXml2BeanHandler -extends AbstractBeanConverterTestCase -{ - public TestXml2BeanHandler(String name) - { - super(name); - } - - -//---------------------------------------------------------------------------- -// Support Code -//---------------------------------------------------------------------------- - - /** - * Builds a DOM tree with a single element, containing the specified - * text as a child of the root. We don't care about the element's name; - * it's the text that's important. - */ - private Element createElement(String data) - { - Element elem = XmlBuilder.element("data").toDOM().getDocumentElement(); - if (data != null) - DomUtil.appendText(elem, data); - return elem; - } - - - /** - * Reflectively executes the named method, and asserts that it throws - * a conversion exception. This method exists to keep clutter out of - * the test methods. - */ - private void assertConversionFailure( - Xml2BeanHandler handler, String methodName, - Element elem, String failureText) - throws Exception - { - Method method = handler.getClass().getMethod(methodName, Element.class); - try - { - method.invoke(handler, elem); - fail(methodName + ": " + failureText); - } - catch (InvocationTargetException ee) - { - if (ee.getCause().getClass() != ConversionException.class) - fail(methodName + "threw unexpected exception: " + ee.getCause()); - // otherwise, we're successful - } - } - - -//---------------------------------------------------------------------------- -// Test Cases -//---------------------------------------------------------------------------- - - public void testConvertString() throws Exception - { - Xml2BeanHandler handler = new Xml2BeanHandler(); - - Element data1 = createElement("foo"); - assertEquals("foo", handler.convertString(data1)); - - Element data2 = createElement(""); - assertEquals("", handler.convertString(data2)); - } - - - public void testConvertCharacter() throws Exception - { - Xml2BeanHandler handler = new Xml2BeanHandler(); - - Element data1 = createElement("A"); - assertEquals(Character.valueOf('A'), handler.convertCharacter(data1)); - - Element data2 = createElement("\uFB01"); - assertEquals(Character.valueOf('\uFB01'), handler.convertCharacter(data2)); - - Element data3 = createElement(""); - assertEquals(Character.valueOf('\0'), handler.convertCharacter(data3)); - - Element data4 = createElement("ix"); - assertConversionFailure(handler, "convertCharacter", data4, "converted invalid value"); - } - - - public void testConvertBoolean() throws Exception - { - Xml2BeanHandler handler = new Xml2BeanHandler(); - - Element data1 = createElement("true"); - assertEquals(Boolean.TRUE, handler.convertBoolean(data1)); - - Element data2 = createElement("1"); - assertEquals(Boolean.TRUE, handler.convertBoolean(data2)); - - Element data3 = createElement("false"); - assertEquals(Boolean.FALSE, handler.convertBoolean(data3)); - - Element data4 = createElement("0"); - assertEquals(Boolean.FALSE, handler.convertBoolean(data4)); - - Element data5 = createElement("ix"); - assertConversionFailure(handler, "convertBoolean", data5, "converted invalid value"); - - Element data6 = createElement(""); - assertConversionFailure(handler, "convertBoolean", data6, "converted empty value"); - } - - - public void testConvertByte() throws Exception - { - Xml2BeanHandler handler = new Xml2BeanHandler(); - - Element data1 = createElement("123"); - assertEquals(Byte.valueOf((byte)123), handler.convertByte(data1)); - - Element data2 = createElement("-123"); - assertEquals(Byte.valueOf((byte)-123), handler.convertByte(data2)); - - Element data3 = createElement("ix"); - assertConversionFailure(handler, "convertByte", data3, "converted invalid value"); - - Element data4 = createElement("1234567"); - assertConversionFailure(handler, "convertByte", data4, "converted too-large value"); - - Element data5 = createElement(""); - assertConversionFailure(handler, "convertByte", data5, "converted empty value"); - } - - - public void testConvertShort() throws Exception - { - Xml2BeanHandler handler = new Xml2BeanHandler(); - - Element data1 = createElement("12345"); - assertEquals(Short.valueOf((short)12345), handler.convertShort(data1)); - - Element data2 = createElement("-12345"); - assertEquals(Short.valueOf((short)-12345), handler.convertShort(data2)); - - Element data3 = createElement("ix"); - assertConversionFailure(handler, "convertShort", data3, "converted non-numeric value"); - - Element data4 = createElement("1234567"); - assertConversionFailure(handler, "convertShort", data4, "converted too-large value"); - - Element data5 = createElement("123.45"); - assertConversionFailure(handler, "convertShort", data5, "converted non-integer value"); - - Element data6 = createElement(""); - assertConversionFailure(handler, "convertShort", data6, "converted empty value"); - } - - - public void testConvertInteger() throws Exception - { - Xml2BeanHandler handler = new Xml2BeanHandler(); - - Element data1 = createElement("123456789"); - assertEquals(Integer.valueOf(123456789), handler.convertInteger(data1)); - - Element data2 = createElement("-123456789"); - assertEquals(Integer.valueOf(-123456789), handler.convertInteger(data2)); - - Element data3 = createElement("ix"); - assertConversionFailure(handler, "convertInteger", data3, "converted non-numeric value"); - - Element data4 = createElement("123456789012345"); - assertConversionFailure(handler, "convertInteger", data4, "converted too-large value"); - - Element data5 = createElement("123.45"); - assertConversionFailure(handler, "convertInteger", data5, "converted non-integer value"); - - Element data6 = createElement(""); - assertConversionFailure(handler, "convertInteger", data6, "converted empty value"); - } - - - public void testConvertLong() throws Exception - { - Xml2BeanHandler handler = new Xml2BeanHandler(); - - Element data1 = createElement("1234567890123456"); - assertE... [truncated message content] |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-15 18:57:45
|
Revision: 108 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=108&view=rev Author: kdgregory Date: 2009-08-15 18:57:35 +0000 (Sat, 15 Aug 2009) Log Message: ----------- fixed bug caused by re-arranging code in ctor Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java 2009-08-15 18:13:24 UTC (rev 107) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java 2009-08-15 18:57:35 UTC (rev 108) @@ -46,9 +46,9 @@ public Bean2XmlDriver(Bean2XmlOptions... options) { - _helper = new ConversionHelper(shouldUseXsdFormatting()); for (Bean2XmlOptions option : options) _options.add(option); + _helper = new ConversionHelper(shouldUseXsdFormatting()); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-15 18:13:31
|
Revision: 107 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=107&view=rev Author: kdgregory Date: 2009-08-15 18:13:24 +0000 (Sat, 15 Aug 2009) Log Message: ----------- comment change, private var name change Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java 2009-08-14 20:20:09 UTC (rev 106) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java 2009-08-15 18:13:24 UTC (rev 107) @@ -35,22 +35,20 @@ /** - * Driver class for converting bean data to an XML representation. In normal - * usage, an instance of this class is constructed with the desired conversion - * options, then {@link #convert} is called with the compound object to be - * converted. The driver may be reused for multiple conversions, and is - * thread-safe. + * Driver class for converting a Java bean into an XML DOM. Normal usage is + * to create a single instance of this class with desired options, then use + * it for multiple conversions. This class is thread-safe. */ public class Bean2XmlDriver { + private ConversionHelper _helper; private EnumSet<Bean2XmlOptions> _options = EnumSet.noneOf(Bean2XmlOptions.class); - private ConversionHelper _primitiveHelper; public Bean2XmlDriver(Bean2XmlOptions... options) { + _helper = new ConversionHelper(shouldUseXsdFormatting()); for (Bean2XmlOptions option : options) _options.add(option); - _primitiveHelper = new ConversionHelper(shouldUseXsdFormatting()); } @@ -158,10 +156,10 @@ if (obj != null) klass = obj.getClass(); - String objType = _primitiveHelper.getXsdType(klass); + String objType = _helper.getXsdType(klass); if ((obj == null) || (objType != null)) { - appender.appendValue(name, objType, _primitiveHelper.stringify(obj)); + appender.appendValue(name, objType, _helper.stringify(obj)); return true; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-14 20:20:14
|
Revision: 106 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=106&view=rev Author: kdgregory Date: 2009-08-14 20:20:09 +0000 (Fri, 14 Aug 2009) Log Message: ----------- attach dummy xsi:nil to root element to prevent repetitive namespace defs by serializer Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java 2009-08-14 20:05:39 UTC (rev 105) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java 2009-08-14 20:20:09 UTC (rev 106) @@ -24,6 +24,8 @@ import java.util.EnumSet; import java.util.Map; +import javax.xml.XMLConstants; + import org.w3c.dom.Element; import net.sf.practicalxml.DomUtil; @@ -76,6 +78,7 @@ public Element convert(Object obj, String nsUri, String rootName) { Element root = DomUtil.newDocument(nsUri, rootName); + doXsiNamespaceHack(root); convert(obj, "", new DirectAppender(root, _options)); return root; } @@ -123,6 +126,32 @@ } + /** + * Introduces the XML Schema Instance namespace into the DOM tree using a + * meaningless attribute. The Xerces serializer does not attempt to promote + * namespace definitions above the subtree in which they first appear, which + * means that the XSI definition could be repeated many times throughout the + * serialized tree, adding bulk to the serialized representation. + * <p> + * By putting "nil=false" at the root element, we will keep the serializer + * from inserting all these definitions. This has to happen <em>before</em> + * any actual conversion, in case some bozo passes <code>null</code> to + * the top-level conversion routine. + * <p> + * Note that we only do this if <code>xsi:nil</code> is enabled by itself. + * If <code>xsi:type</code> is enabled, the converter will attach that + * attribute to the root instead, thereby establishing the namespace context. + */ + private void doXsiNamespaceHack(Element root) + { + if (_options.contains(Bean2XmlOptions.XSI_NIL) + && !_options.contains(Bean2XmlOptions.XSI_TYPE)) + { + root.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil", "false"); + } + } + + private boolean tryToConvertAsPrimitiveOrNull( Object obj, Class<?> klass, String name, Appender appender) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-14 20:05:46
|
Revision: 105 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=105&view=rev Author: kdgregory Date: 2009-08-14 20:05:39 +0000 (Fri, 14 Aug 2009) Log Message: ----------- rename PrimitiveConversionHelper to ConversionHelper Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanHandler.java Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java Removed Paths: ------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/PrimitiveConversionHelper.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestPrimitiveConversionHelper.java Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java (from rev 99, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/PrimitiveConversionHelper.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java 2009-08-14 20:05:39 UTC (rev 105) @@ -0,0 +1,446 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import net.sf.practicalxml.XmlUtil; + + +/** + * Responsible for converting "primitive" types -- those with unambiguous + * string representations -- to/from such a representation. + */ +public class ConversionHelper +{ + // this is not static because the helpers are inner classes + private Map<Class<?>,ConversionHandler<?>> _helpers + = new HashMap<Class<?>,ConversionHandler<?>>(); + { + _helpers.put(String.class, new StringConversionHandler()); + _helpers.put(Character.class, new CharacterConversionHandler()); + _helpers.put(Boolean.class, new BooleanConversionHandler()); + _helpers.put(Byte.class, new ByteConversionHandler()); + _helpers.put(Short.class, new ShortConversionHandler()); + _helpers.put(Integer.class, new IntegerConversionHandler()); + _helpers.put(Long.class, new LongConversionHandler()); + _helpers.put(Float.class, new FloatConversionHandler()); + _helpers.put(Double.class, new DoubleConversionHandler()); + _helpers.put(BigInteger.class, new BigIntegerConversionHandler()); + _helpers.put(BigDecimal.class, new BigDecimalConversionHandler()); + _helpers.put(Date.class, new DateConversionHandler()); + } + + private boolean _useXsdFormatting; + + + /** + * Default constructor, which uses Java formatting. + */ + public ConversionHelper() + { + // nothing to see here + } + + + /** + * Constructor that allows selection of Java or XSD formatting. + */ + public ConversionHelper(boolean useXsdFormatting) + { + _useXsdFormatting = useXsdFormatting; + } + + +//---------------------------------------------------------------------------- +// Public Methods +//---------------------------------------------------------------------------- + + /** + * Returns the XSD type name to use for stringified objects of the + * specified class, <code>null</code> if this converter can't convert + * instances of the class. + */ + public String getXsdType(Class<?> klass) + { + ConversionHandler<?> helper = _helpers.get(klass); + return (helper == null) ? null : helper.getXsiType(); + } + + + /** + * Converts the passed object into its string representation, according + * to the options currently in effect. Passing <code>null</code> will + * return <code>null</code>. Throws {@link ConversionException} if + * unable to convert the passed object. + */ + public String stringify(Object obj) + { + if (obj == null) + return null; + + try + { + return getHelper(obj.getClass()).stringify(obj); + } + catch (Exception ee) + { + if (ee instanceof ConversionException) + throw (ConversionException)ee; + throw new ConversionException("unable to convert: " + obj, ee); + } + } + + + /** + * Parses the passed string into an object of the desired class. Passing + * <code>null</code> will return <code>null</code>, passing an empty + * string will typically throw a {@link ConversionException}. + */ + public Object parse(String str, Class<?> klass) + { + if (str == null) + return null; + + try + { + return getHelper(klass).parse(str); + } + catch (Exception ee) + { + if (ee instanceof ConversionException) + throw (ConversionException)ee; + throw new ConversionException("unable to parse: " + str, ee); + } + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + /** + * Returns the appropriate conversion helper or throws. + */ + @SuppressWarnings(value="unchecked") + private ConversionHandler getHelper(Class<?> klass) + { + ConversionHandler<?> helper = _helpers.get(klass); + if (helper == null) + throw new ConversionException("unable to get helper: " + klass.getName()); + return helper; + } + + + /** + * Each primitive class has its own conversion handler that is responsible + * for converting to/from a string representation. Handlers are guaranteed + * to receive non-null objects/strings. + * <p> + * This interface is parameterized so that the compiler will generate + * bridge methods for implementation classes. Elsewhere, we don't care + * about parameterization, so wildcard or drop it (see {@link #getHelper}). + * <p> + * Implementation classes are expected to be inner classes, so that they + * have access to configuration information (such as formatting rules). + * <p> + * Implementation classes are permitted to throw any exception; caller is + * expected to catch them and translate to a {@link ConversionException}. + */ + private static interface ConversionHandler<T> + { + public String getXsiType(); + public String stringify(T obj) throws Exception; + public T parse(String str) throws Exception; + } + + + private class StringConversionHandler + implements ConversionHandler<String> + { + public String getXsiType() + { + return "xsd:string"; + } + + public String stringify(String obj) + { + return String.valueOf(obj); + } + + public String parse(String str) + { + return str; + } + } + + + private class CharacterConversionHandler + implements ConversionHandler<Character> + { + private final Character NUL = Character.valueOf('\0'); + + public String getXsiType() + { + return "xsd:string"; + } + + public String stringify(Character obj) + { + if (obj.equals(NUL)) + return ""; + return obj.toString(); + } + + public Character parse(String str) + { + if (str.length() == 0) + return NUL; + if (str.length() > 1) + throw new ConversionException( + "attempted to convert multi-character string: \"" + str + "\""); + return Character.valueOf(str.charAt(0)); + } + } + + + private class BooleanConversionHandler + implements ConversionHandler<Boolean> + { + public String getXsiType() + { + return "xsd:boolean"; + } + + public String stringify(Boolean obj) + { + return _useXsdFormatting + ? XmlUtil.formatXsdBoolean(obj.booleanValue()) + : obj.toString(); + } + + public Boolean parse(String str) + { + return _useXsdFormatting + ? XmlUtil.parseXsdBoolean(str) + : Boolean.parseBoolean(str); + } + } + + + private class ByteConversionHandler + implements ConversionHandler<Byte> + { + public String getXsiType() + { + return "xsd:byte"; + } + + public String stringify(Byte obj) + { + return obj.toString(); + } + + public Byte parse(String str) + { + return Byte.valueOf(str.trim()); + } + } + + + private class ShortConversionHandler + implements ConversionHandler<Short> + { + public String getXsiType() + { + return "xsd:short"; + } + + public String stringify(Short obj) + { + return obj.toString(); + } + + public Short parse(String str) + { + return Short.valueOf(str.trim()); + } + } + + + private class IntegerConversionHandler + implements ConversionHandler<Integer> + { + public String getXsiType() + { + return "xsd:int"; + } + + public String stringify(Integer obj) + { + return obj.toString(); + } + + public Integer parse(String str) + { + return Integer.valueOf(str.trim()); + } + } + + + private class LongConversionHandler + implements ConversionHandler<Long> + { + public String getXsiType() + { + return "xsd:long"; + } + + public String stringify(Long obj) + { + return obj.toString(); + } + + public Long parse(String str) + { + return Long.valueOf(str.trim()); + } + } + + + private class FloatConversionHandler + implements ConversionHandler<Float> + { + public String getXsiType() + { + return "xsd:decimal"; + } + + public String stringify(Float obj) + { + return _useXsdFormatting + ? XmlUtil.formatXsdDecimal(obj) + : obj.toString(); + } + + public Float parse(String str) + { + return Float.valueOf(str.trim()); + } + } + + + private class DoubleConversionHandler + implements ConversionHandler<Double> + { + public String getXsiType() + { + return "xsd:decimal"; + } + + public String stringify(Double obj) + { + return _useXsdFormatting + ? XmlUtil.formatXsdDecimal(obj) + : obj.toString(); + } + + public Double parse(String str) + { + return Double.valueOf(str.trim()); + } + } + + + private class BigIntegerConversionHandler + implements ConversionHandler<BigInteger> + { + public String getXsiType() + { + return "xsd:decimal"; + } + + public String stringify(BigInteger obj) + { + return obj.toString(); + } + + public BigInteger parse(String str) + { + return new BigInteger(str.trim()); + } + } + + + private class BigDecimalConversionHandler + implements ConversionHandler<BigDecimal> + { + public String getXsiType() + { + return "xsd:decimal"; + } + + public String stringify(BigDecimal obj) + { + return obj.toString(); + } + + public BigDecimal parse(String str) + { + return new BigDecimal(str.trim()); + } + } + + + private class DateConversionHandler + implements ConversionHandler<Date> + { + // format as specified by Date.toString() JavaDoc + private DateFormat _defaultFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"); + + public String getXsiType() + { + return "xsd:dateTime"; + } + + public String stringify(Date obj) + { + return _useXsdFormatting + ? XmlUtil.formatXsdDatetime(obj) + : obj.toString(); + } + + public Date parse(String str) + throws ParseException + { + if (_useXsdFormatting) + return XmlUtil.parseXsdDatetime(str); + else + { + synchronized (_defaultFormat) + { + return _defaultFormat.parse(str); + } + } + } + } +} Deleted: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/PrimitiveConversionHelper.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/PrimitiveConversionHelper.java 2009-08-14 19:00:11 UTC (rev 104) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/PrimitiveConversionHelper.java 2009-08-14 20:05:39 UTC (rev 105) @@ -1,446 +0,0 @@ -// Copyright 2008-2009 severally by the contributors -// -// 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 net.sf.practicalxml.converter; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import net.sf.practicalxml.XmlUtil; - - -/** - * Responsible for converting "primitive" types -- those with unambiguous - * string representations -- to/from such a representation. - */ -public class PrimitiveConversionHelper -{ - // this is not static because the helpers are inner classes - private Map<Class<?>,ConversionHandler<?>> _helpers - = new HashMap<Class<?>,ConversionHandler<?>>(); - { - _helpers.put(String.class, new StringConversionHandler()); - _helpers.put(Character.class, new CharacterConversionHandler()); - _helpers.put(Boolean.class, new BooleanConversionHandler()); - _helpers.put(Byte.class, new ByteConversionHandler()); - _helpers.put(Short.class, new ShortConversionHandler()); - _helpers.put(Integer.class, new IntegerConversionHandler()); - _helpers.put(Long.class, new LongConversionHandler()); - _helpers.put(Float.class, new FloatConversionHandler()); - _helpers.put(Double.class, new DoubleConversionHandler()); - _helpers.put(BigInteger.class, new BigIntegerConversionHandler()); - _helpers.put(BigDecimal.class, new BigDecimalConversionHandler()); - _helpers.put(Date.class, new DateConversionHandler()); - } - - private boolean _useXsdFormatting; - - - /** - * Default constructor, which uses Java formatting. - */ - public PrimitiveConversionHelper() - { - // nothing to see here - } - - - /** - * Constructor that allows selection of Java or XSD formatting. - */ - public PrimitiveConversionHelper(boolean useXsdFormatting) - { - _useXsdFormatting = useXsdFormatting; - } - - -//---------------------------------------------------------------------------- -// Public Methods -//---------------------------------------------------------------------------- - - /** - * Returns the XSD type name to use for stringified objects of the - * specified class, <code>null</code> if this converter can't convert - * instances of the class. - */ - public String getXsdType(Class<?> klass) - { - ConversionHandler<?> helper = _helpers.get(klass); - return (helper == null) ? null : helper.getXsiType(); - } - - - /** - * Converts the passed object into its string representation, according - * to the options currently in effect. Passing <code>null</code> will - * return <code>null</code>. Throws {@link ConversionException} if - * unable to convert the passed object. - */ - public String stringify(Object obj) - { - if (obj == null) - return null; - - try - { - return getHelper(obj.getClass()).stringify(obj); - } - catch (Exception ee) - { - if (ee instanceof ConversionException) - throw (ConversionException)ee; - throw new ConversionException("unable to convert: " + obj, ee); - } - } - - - /** - * Parses the passed string into an object of the desired class. Passing - * <code>null</code> will return <code>null</code>, passing an empty - * string will typically throw a {@link ConversionException}. - */ - public Object parse(String str, Class<?> klass) - { - if (str == null) - return null; - - try - { - return getHelper(klass).parse(str); - } - catch (Exception ee) - { - if (ee instanceof ConversionException) - throw (ConversionException)ee; - throw new ConversionException("unable to parse: " + str, ee); - } - } - - -//---------------------------------------------------------------------------- -// Internals -//---------------------------------------------------------------------------- - - /** - * Returns the appropriate conversion helper or throws. - */ - @SuppressWarnings(value="unchecked") - private ConversionHandler getHelper(Class<?> klass) - { - ConversionHandler<?> helper = _helpers.get(klass); - if (helper == null) - throw new ConversionException("unable to get helper: " + klass.getName()); - return helper; - } - - - /** - * Each primitive class has its own conversion handler that is responsible - * for converting to/from a string representation. Handlers are guaranteed - * to receive non-null objects/strings. - * <p> - * This interface is parameterized so that the compiler will generate - * bridge methods for implementation classes. Elsewhere, we don't care - * about parameterization, so wildcard or drop it (see {@link #getHelper}). - * <p> - * Implementation classes are expected to be inner classes, so that they - * have access to configuration information (such as formatting rules). - * <p> - * Implementation classes are permitted to throw any exception; caller is - * expected to catch them and translate to a {@link ConversionException}. - */ - private static interface ConversionHandler<T> - { - public String getXsiType(); - public String stringify(T obj) throws Exception; - public T parse(String str) throws Exception; - } - - - private class StringConversionHandler - implements ConversionHandler<String> - { - public String getXsiType() - { - return "xsd:string"; - } - - public String stringify(String obj) - { - return String.valueOf(obj); - } - - public String parse(String str) - { - return str; - } - } - - - private class CharacterConversionHandler - implements ConversionHandler<Character> - { - private final Character NUL = Character.valueOf('\0'); - - public String getXsiType() - { - return "xsd:string"; - } - - public String stringify(Character obj) - { - if (obj.equals(NUL)) - return ""; - return obj.toString(); - } - - public Character parse(String str) - { - if (str.length() == 0) - return NUL; - if (str.length() > 1) - throw new ConversionException( - "attempted to convert multi-character string: \"" + str + "\""); - return Character.valueOf(str.charAt(0)); - } - } - - - private class BooleanConversionHandler - implements ConversionHandler<Boolean> - { - public String getXsiType() - { - return "xsd:boolean"; - } - - public String stringify(Boolean obj) - { - return _useXsdFormatting - ? XmlUtil.formatXsdBoolean(obj.booleanValue()) - : obj.toString(); - } - - public Boolean parse(String str) - { - return _useXsdFormatting - ? XmlUtil.parseXsdBoolean(str) - : Boolean.parseBoolean(str); - } - } - - - private class ByteConversionHandler - implements ConversionHandler<Byte> - { - public String getXsiType() - { - return "xsd:byte"; - } - - public String stringify(Byte obj) - { - return obj.toString(); - } - - public Byte parse(String str) - { - return Byte.valueOf(str.trim()); - } - } - - - private class ShortConversionHandler - implements ConversionHandler<Short> - { - public String getXsiType() - { - return "xsd:short"; - } - - public String stringify(Short obj) - { - return obj.toString(); - } - - public Short parse(String str) - { - return Short.valueOf(str.trim()); - } - } - - - private class IntegerConversionHandler - implements ConversionHandler<Integer> - { - public String getXsiType() - { - return "xsd:int"; - } - - public String stringify(Integer obj) - { - return obj.toString(); - } - - public Integer parse(String str) - { - return Integer.valueOf(str.trim()); - } - } - - - private class LongConversionHandler - implements ConversionHandler<Long> - { - public String getXsiType() - { - return "xsd:long"; - } - - public String stringify(Long obj) - { - return obj.toString(); - } - - public Long parse(String str) - { - return Long.valueOf(str.trim()); - } - } - - - private class FloatConversionHandler - implements ConversionHandler<Float> - { - public String getXsiType() - { - return "xsd:decimal"; - } - - public String stringify(Float obj) - { - return _useXsdFormatting - ? XmlUtil.formatXsdDecimal(obj) - : obj.toString(); - } - - public Float parse(String str) - { - return Float.valueOf(str.trim()); - } - } - - - private class DoubleConversionHandler - implements ConversionHandler<Double> - { - public String getXsiType() - { - return "xsd:decimal"; - } - - public String stringify(Double obj) - { - return _useXsdFormatting - ? XmlUtil.formatXsdDecimal(obj) - : obj.toString(); - } - - public Double parse(String str) - { - return Double.valueOf(str.trim()); - } - } - - - private class BigIntegerConversionHandler - implements ConversionHandler<BigInteger> - { - public String getXsiType() - { - return "xsd:decimal"; - } - - public String stringify(BigInteger obj) - { - return obj.toString(); - } - - public BigInteger parse(String str) - { - return new BigInteger(str.trim()); - } - } - - - private class BigDecimalConversionHandler - implements ConversionHandler<BigDecimal> - { - public String getXsiType() - { - return "xsd:decimal"; - } - - public String stringify(BigDecimal obj) - { - return obj.toString(); - } - - public BigDecimal parse(String str) - { - return new BigDecimal(str.trim()); - } - } - - - private class DateConversionHandler - implements ConversionHandler<Date> - { - // format as specified by Date.toString() JavaDoc - private DateFormat _defaultFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"); - - public String getXsiType() - { - return "xsd:dateTime"; - } - - public String stringify(Date obj) - { - return _useXsdFormatting - ? XmlUtil.formatXsdDatetime(obj) - : obj.toString(); - } - - public Date parse(String str) - throws ParseException - { - if (_useXsdFormatting) - return XmlUtil.parseXsdDatetime(str); - else - { - synchronized (_defaultFormat) - { - return _defaultFormat.parse(str); - } - } - } - } -} Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java 2009-08-14 19:00:11 UTC (rev 104) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java 2009-08-14 20:05:39 UTC (rev 105) @@ -28,7 +28,7 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.converter.ConversionException; -import net.sf.practicalxml.converter.PrimitiveConversionHelper; +import net.sf.practicalxml.converter.ConversionHelper; import net.sf.practicalxml.converter.bean2xml.Bean2XmlAppenders.*; @@ -42,13 +42,13 @@ public class Bean2XmlDriver { private EnumSet<Bean2XmlOptions> _options = EnumSet.noneOf(Bean2XmlOptions.class); - private PrimitiveConversionHelper _primitiveHelper; + private ConversionHelper _primitiveHelper; public Bean2XmlDriver(Bean2XmlOptions... options) { for (Bean2XmlOptions option : options) _options.add(option); - _primitiveHelper = new PrimitiveConversionHelper(shouldUseXsdFormatting()); + _primitiveHelper = new ConversionHelper(shouldUseXsdFormatting()); } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanHandler.java 2009-08-14 19:00:11 UTC (rev 104) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanHandler.java 2009-08-14 20:05:39 UTC (rev 105) @@ -18,7 +18,7 @@ import net.sf.practicalxml.XmlException; import net.sf.practicalxml.XmlUtil; import net.sf.practicalxml.converter.ConversionException; -import net.sf.practicalxml.converter.PrimitiveConversionHelper; +import net.sf.practicalxml.converter.ConversionHelper; import net.sf.practicalxml.internal.StringUtils; import java.util.Arrays; @@ -42,7 +42,7 @@ public class Xml2BeanHandler { private EnumSet<Xml2BeanOptions> _options; - private PrimitiveConversionHelper _primitiveHelper; + private ConversionHelper _primitiveHelper; /** @@ -54,7 +54,7 @@ for (Xml2BeanOptions option : options) _options.add(option); - _primitiveHelper = new PrimitiveConversionHelper(true); + _primitiveHelper = new ConversionHelper(true); } Copied: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java (from rev 99, branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestPrimitiveConversionHelper.java) =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java 2009-08-14 20:05:39 UTC (rev 105) @@ -0,0 +1,529 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; + + +public class TestConversionHelper +extends AbstractBeanConverterTestCase +{ + public TestConversionHelper(String name) + { + super(name); + } + + +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + private void assertFailsConversionToObject( + String message, + ConversionHelper helper, + String str, + Class<?> klass) + { + try + { + helper.parse(str, klass); + fail(message); + } + catch (ConversionException ee) + { + // success! + } + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + public void testUnknownClass() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertNull(helper.getXsdType(Class.class)); + } + + + public void testConvertNull() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertNull(helper.stringify(null)); + assertNull(helper.parse(null, Object.class)); + } + + + public void testConvertString() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertEquals("xsd:string", helper.getXsdType(String.class)); + + assertEquals("foo", helper.stringify("foo")); + assertEquals("foo", helper.parse("foo", String.class)); + + assertEquals("", helper.stringify("")); + assertEquals("", helper.parse("", String.class)); + } + + + public void testConvertCharacter() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertEquals("xsd:string", helper.getXsdType(Character.class)); + + Character simple = Character.valueOf('A'); + assertEquals("A", helper.stringify(simple)); + assertEquals(simple, helper.parse("A", Character.class)); + + Character nul = Character.valueOf('\0'); + assertEquals("", helper.stringify(nul)); + assertEquals(nul, helper.parse("", Character.class)); + + assertFailsConversionToObject( + "converted multi-character string", + helper, "ix", Character.class); + } + + + public void testConvertBooleanDefault() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + // default boolean conversion is compatible with XSD type def + assertEquals("xsd:boolean", helper.getXsdType(Boolean.class)); + + String sTrue = Boolean.TRUE.toString(); + assertEquals(sTrue, helper.stringify(Boolean.TRUE)); + assertEquals(Boolean.TRUE, helper.parse(sTrue, Boolean.class)); + + String sFalse = Boolean.FALSE.toString(); + assertEquals(sFalse, helper.stringify(Boolean.FALSE)); + assertEquals(Boolean.FALSE, helper.parse(sFalse, Boolean.class)); + + assertEquals(Boolean.FALSE, helper.parse("ix", Boolean.class)); + assertEquals(Boolean.FALSE, helper.parse("", Boolean.class)); + } + + + public void testConvertBooleanXsd() throws Exception + { + ConversionHelper helper = new ConversionHelper(true); + + assertEquals("xsd:boolean", helper.getXsdType(Boolean.class)); + + assertEquals("true", helper.stringify(Boolean.TRUE)); + assertEquals(Boolean.TRUE, helper.parse("true", Boolean.class)); + assertEquals(Boolean.TRUE, helper.parse("1", Boolean.class)); + + assertEquals("false", helper.stringify(Boolean.FALSE)); + assertEquals(Boolean.FALSE, helper.parse("false", Boolean.class)); + assertEquals(Boolean.FALSE, helper.parse("0", Boolean.class)); + + assertFailsConversionToObject( + "converted multi-character string", + helper, "ix", Boolean.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Boolean.class); + } + + + public void testConvertByte() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertEquals("xsd:byte", helper.getXsdType(Byte.class)); + + String str1 = "123"; + Byte val1 = Byte.valueOf((byte)123); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Byte.class)); + + String str2 = "-123"; + Byte val2 = Byte.valueOf((byte)-123); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Byte.class)); + + String str3 = " -123 "; + Byte val3 = Byte.valueOf((byte)-123); + assertEquals(val3, helper.parse(str3, Byte.class)); + + assertFailsConversionToObject( + "converted too-large value", + helper, "1234567", Byte.class); + + assertFailsConversionToObject( + "converted non-integer value", + helper, "1.23", Byte.class); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Byte.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Byte.class); + } + + + public void testConvertShort() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertEquals("xsd:short", helper.getXsdType(Short.class)); + + String str1 = "12345"; + Short val1 = Short.valueOf((short)12345); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Short.class)); + + String str2 = "-12345"; + Short val2 = Short.valueOf((short)-12345); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Short.class)); + + String str3 = " -12345 "; + Short val3 = Short.valueOf((short)-12345); + assertEquals(val3, helper.parse(str3, Short.class)); + + assertFailsConversionToObject( + "converted too-large value", + helper, "1234567", Short.class); + + assertFailsConversionToObject( + "converted non-integer value", + helper, "123.45", Short.class); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Short.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Short.class); + } + + + public void testConvertInteger() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertEquals("xsd:int", helper.getXsdType(Integer.class)); + + String str1 = "1234567"; + Integer val1 = Integer.valueOf(1234567); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Integer.class)); + + String str2 = "-1234567"; + Integer val2 = Integer.valueOf(-1234567); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Integer.class)); + + String str3 = " -1234567 "; + Integer val3 = Integer.valueOf(-1234567); + assertEquals(val3, helper.parse(str3, Integer.class)); + + assertFailsConversionToObject( + "converted too-large value", + helper, "123456789012345", Integer.class); + + assertFailsConversionToObject( + "converted non-integer value", + helper, "123.45", Integer.class); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Integer.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Integer.class); + } + + + public void testConvertLong() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertEquals("xsd:long", helper.getXsdType(Long.class)); + + String str1 = "1234567890"; + Long val1 = Long.valueOf(1234567890L); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Long.class)); + + String str2 = "-1234567890"; + Long val2 = Long.valueOf(-1234567890L); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Long.class)); + + String str3 = " -1234567890 "; + Long val3 = Long.valueOf(-1234567890L); + assertEquals(val3, helper.parse(str3, Long.class)); + + assertFailsConversionToObject( + "converted too-large value", + helper, "123456789012345678901234567890", Long.class); + + assertFailsConversionToObject( + "converted non-integer value", + helper, "123.45", Long.class); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Long.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Long.class); + } + + + public void testConvertFloatDefault() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + // default float conversion is compatible with XSD type def + assertEquals("xsd:decimal", helper.getXsdType(Float.class)); + + // note: for default-format tests, strings are generated from values + Float val1 = Float.valueOf(1234f); + String str1 = val1.toString(); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Float.class)); + + Float val2 = Float.valueOf(-1234f); + String str2 = val2.toString(); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Float.class)); + + String str3 = " -1234.5 "; + Float val3 = Float.valueOf(-1234.5f); + assertEquals(val3, helper.parse(str3, Float.class)); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Float.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Float.class); + } + + + public void testConvertFloatXsd() throws Exception + { + ConversionHelper helper = new ConversionHelper(true); + + assertEquals("xsd:decimal", helper.getXsdType(Float.class)); + + String str1 = "1234.0"; + Float val1 = Float.valueOf(1234f); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Float.class)); + + String str2 = "-1234.0"; + Float val2 = Float.valueOf(-1234f); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Float.class)); + + String str3 = " -1234.5 "; + Float val3 = Float.valueOf(-1234.5f); + assertEquals(val3, helper.parse(str3, Float.class)); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Float.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Float.class); + } + + + public void testConvertDoubleDefault() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + // default double conversion is compatible with XSD type def + assertEquals("xsd:decimal", helper.getXsdType(Double.class)); + + // note: for default-format tests, strings are generated from values + Double val1 = Double.valueOf(1234567890.5); + String str1 = val1.toString(); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Double.class)); + + Double val2 = Double.valueOf(-1234567890.1); + String str2 = val2.toString(); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Double.class)); + + String str3 = " -1234.5 "; + Double val3 = Double.valueOf(-1234.5); + assertEquals(val3, helper.parse(str3, Double.class)); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Double.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Double.class); + } + + + public void testConvertDoubleXsd() throws Exception + { + ConversionHelper helper = new ConversionHelper(true); + + assertEquals("xsd:decimal", helper.getXsdType(Double.class)); + + // while for XSD-format tests, we want to verify the strings + String str1 = "1234567890.5"; + Double val1 = Double.valueOf(1234567890.5); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Double.class)); + + String str2 = "-1234567890.1"; + Double val2 = Double.valueOf(-1234567890.1); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Double.class)); + + String str3 = " -1234.5 "; + Double val3 = Double.valueOf(-1234.5); + assertEquals(val3, helper.parse(str3, Double.class)); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Double.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Double.class); + } + + + public void testConvertBigInteger() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertEquals("xsd:decimal", helper.getXsdType(BigInteger.class)); + + String str1 = "123456789012345678901234567890"; + BigInteger val1 = new BigInteger(str1); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, BigInteger.class)); + + String str2 = "-123456789012345678901234567890"; + BigInteger val2 = new BigInteger(str2); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, BigInteger.class)); + + assertFailsConversionToObject( + "converted non-integer value", + helper, "123.45", BigInteger.class); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", BigInteger.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", BigInteger.class); + } + + + public void testConvertBigDecimal() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + assertEquals("xsd:decimal", helper.getXsdType(BigDecimal.class)); + + String str1 = "12345678901234567890.123456789"; + BigDecimal val1 = new BigDecimal(str1); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, BigDecimal.class)); + + String str2 = "-12345678901234567890.123456789"; + BigDecimal val2 = new BigDecimal(str2); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, BigDecimal.class)); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", BigDecimal.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", BigDecimal.class); + } + + + public void testConvertDateDefault() throws Exception + { + ConversionHelper helper = new ConversionHelper(); + + // default date conversion is NOT compatible with XSD type def, + // so this is misleading + assertEquals("xsd:dateTime", helper.getXsdType(Date.class)); + + // for default conversion, we create string from value + Date val1 = new Date(1247551703000L); + String str1 = val1.toString(); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Date.class)); + + + assertFailsConversionToObject( + "converted non-date value", + helper, "ix", Date.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Date.class); + } + + + public void testConvertDateXsd() throws Exception + { + ConversionHelper helper = new ConversionHelper(true); + + assertEquals("xsd:dateTime", helper.getXsdType(Date.class)); + + Date val1 = new Date(1247551703000L); + String str1 = "2009-07-14T06:08:23"; + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Date.class)); + + assertFailsConversionToObject( + "converted non-date value", + helper, "ix", Date.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Date.class); + } +} Deleted: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestPrimitiveConversionHelper.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestPrimitiveConversionHelper.java 2009-08-14 19:00:11 UTC (rev 104) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestPrimitiveConversionHelper.java 2009-08-14 20:05:39 UTC (rev 105) @@ -1,529 +0,0 @@ -// Copyright 2008-2009 severally by the contributors -// -// 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 net.sf.practicalxml.converter; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Date; - - -public class TestPrimitiveConversionHelper -extends AbstractBeanConverterTestCase -{ - public TestPrimitiveConversionHelper(String name) - { - super(name); - } - - -//---------------------------------------------------------------------------- -// Support Code -//---------------------------------------------------------------------------- - - private void assertFailsConversionToObject( - String message, - PrimitiveConversionHelper helper, - String str, - Class<?> klass) - { - try - { - helper.parse(str, klass); - fail(message); - } - catch (ConversionException ee) - { - // success! - } - } - - -//---------------------------------------------------------------------------- -// Test Cases -//---------------------------------------------------------------------------- - - public void testUnknownClass() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); - - assertNull(helper.getXsdType(Class.class)); - } - - - public void testConvertNull() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); - - assertNull(helper.stringify(null)); - assertNull(helper.parse(null, Object.class)); - } - - - public void testConvertString() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); - - assertEquals("xsd:string", helper.getXsdType(String.class)); - - assertEquals("foo", helper.stringify("foo")); - assertEquals("foo", helper.parse("foo", String.class)); - - assertEquals("", helper.stringify("")); - assertEquals("", helper.parse("", String.class)); - } - - - public void testConvertCharacter() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); - - assertEquals("xsd:string", helper.getXsdType(Character.class)); - - Character simple = Character.valueOf('A'); - assertEquals("A", helper.stringify(simple)); - assertEquals(simple, helper.parse("A", Character.class)); - - Character nul = Character.valueOf('\0'); - assertEquals("", helper.stringify(nul)); - assertEquals(nul, helper.parse("", Character.class)); - - assertFailsConversionToObject( - "converted multi-character string", - helper, "ix", Character.class); - } - - - public void testConvertBooleanDefault() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); - - // default boolean conversion is compatible with XSD type def - assertEquals("xsd:boolean", helper.getXsdType(Boolean.class)); - - String sTrue = Boolean.TRUE.toString(); - assertEquals(sTrue, helper.stringify(Boolean.TRUE)); - assertEquals(Boolean.TRUE, helper.parse(sTrue, Boolean.class)); - - String sFalse = Boolean.FALSE.toString(); - assertEquals(sFalse, helper.stringify(Boolean.FALSE)); - assertEquals(Boolean.FALSE, helper.parse(sFalse, Boolean.class)); - - assertEquals(Boolean.FALSE, helper.parse("ix", Boolean.class)); - assertEquals(Boolean.FALSE, helper.parse("", Boolean.class)); - } - - - public void testConvertBooleanXsd() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(true); - - assertEquals("xsd:boolean", helper.getXsdType(Boolean.class)); - - assertEquals("true", helper.stringify(Boolean.TRUE)); - assertEquals(Boolean.TRUE, helper.parse("true", Boolean.class)); - assertEquals(Boolean.TRUE, helper.parse("1", Boolean.class)); - - assertEquals("false", helper.stringify(Boolean.FALSE)); - assertEquals(Boolean.FALSE, helper.parse("false", Boolean.class)); - assertEquals(Boolean.FALSE, helper.parse("0", Boolean.class)); - - assertFailsConversionToObject( - "converted multi-character string", - helper, "ix", Boolean.class); - - assertFailsConversionToObject( - "converted empty string", - helper, "", Boolean.class); - } - - - public void testConvertByte() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); - - assertEquals("xsd:byte", helper.getXsdType(Byte.class)); - - String str1 = "123"; - Byte val1 = Byte.valueOf((byte)123); - assertEquals(str1, helper.stringify(val1)); - assertEquals(val1, helper.parse(str1, Byte.class)); - - String str2 = "-123"; - Byte val2 = Byte.valueOf((byte)-123); - assertEquals(str2, helper.stringify(val2)); - assertEquals(val2, helper.parse(str2, Byte.class)); - - String str3 = " -123 "; - Byte val3 = Byte.valueOf((byte)-123); - assertEquals(val3, helper.parse(str3, Byte.class)); - - assertFailsConversionToObject( - "converted too-large value", - helper, "1234567", Byte.class); - - assertFailsConversionToObject( - "converted non-integer value", - helper, "1.23", Byte.class); - - assertFailsConversionToObject( - "converted non-numeric value", - helper, "ix", Byte.class); - - assertFailsConversionToObject( - "converted empty string", - helper, "", Byte.class); - } - - - public void testConvertShort() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); - - assertEquals("xsd:short", helper.getXsdType(Short.class)); - - String str1 = "12345"; - Short val1 = Short.valueOf((short)12345); - assertEquals(str1, helper.stringify(val1)); - assertEquals(val1, helper.parse(str1, Short.class)); - - String str2 = "-12345"; - Short val2 = Short.valueOf((short)-12345); - assertEquals(str2, helper.stringify(val2)); - assertEquals(val2, helper.parse(str2, Short.class)); - - String str3 = " -12345 "; - Short val3 = Short.valueOf((short)-12345); - assertEquals(val3, helper.parse(str3, Short.class)); - - assertFailsConversionToObject( - "converted too-large value", - helper, "1234567", Short.class); - - assertFailsConversionToObject( - "converted non-integer value", - helper, "123.45", Short.class); - - assertFailsConversionToObject( - "converted non-numeric value", - helper, "ix", Short.class); - - assertFailsConversionToObject( - "converted empty string", - helper, "", Short.class); - } - - - public void testConvertInteger() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); - - assertEquals("xsd:int", helper.getXsdType(Integer.class)); - - String str1 = "1234567"; - Integer val1 = Integer.valueOf(1234567); - assertEquals(str1, helper.stringify(val1)); - assertEquals(val1, helper.parse(str1, Integer.class)); - - String str2 = "-1234567"; - Integer val2 = Integer.valueOf(-1234567); - assertEquals(str2, helper.stringify(val2)); - assertEquals(val2, helper.parse(str2, Integer.class)); - - String str3 = " -1234567 "; - Integer val3 = Integer.valueOf(-1234567); - assertEquals(val3, helper.parse(str3, Integer.class)); - - assertFailsConversionToObject( - "converted too-large value", - helper, "123456789012345", Integer.class); - - assertFailsConversionToObject( - "converted non-integer value", - helper, "123.45", Integer.class); - - assertFailsConversionToObject( - "converted non-numeric value", - helper, "ix", Integer.class); - - assertFailsConversionToObject( - "converted empty string", - helper, "", Integer.class); - } - - - public void testConvertLong() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); - - assertEquals("xsd:long", helper.getXsdType(Long.class)); - - String str1 = "1234567890"; - Long val1 = Long.valueOf(1234567890L); - assertEquals(str1, helper.stringify(val1)); - assertEquals(val1, helper.parse(str1, Long.class)); - - String str2 = "-1234567890"; - Long val2 = Long.valueOf(-1234567890L); - assertEquals(str2, helper.stringify(val2)); - assertEquals(val2, helper.parse(str2, Long.class)); - - String str3 = " -1234567890 "; - Long val3 = Long.valueOf(-1234567890L); - assertEquals(val3, helper.parse(str3, Long.class)); - - assertFailsConversionToObject( - "converted too-large value", - helper, "123456789012345678901234567890", Long.class); - - assertFailsConversionToObject( - "converted non-integer value", - helper, "123.45", Long.class); - - assertFailsConversionToObject( - "converted non-numeric value", - helper, "ix", Long.class); - - assertFailsConversionToObject( - "converted empty string", - helper, "", Long.class); - } - - - public void testConvertFloatDefault() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); - - // default float conversion is compatible with XSD type def - assertEquals("xsd:decimal", helper.getXsdType(Float.class)); - - // note: for default-format tests, strings are generated from values - Float val1 = Float.valueOf(1234f); - String str1 = val1.toString(); - assertEquals(str1, helper.stringify(val1)); - assertEquals(val1, helper.parse(str1, Float.class)); - - Float val2 = Float.valueOf(-1234f); - String str2 = val2.toString(); - assertEquals(str2, helper.stringify(val2)); - assertEquals(val2, helper.parse(str2, Float.class)); - - String str3 = " -1234.5 "; - Float val3 = Float.valueOf(-1234.5f); - assertEquals(val3, helper.parse(str3, Float.class)); - - assertFailsConversionToObject( - "converted non-numeric value", - helper, "ix", Float.class); - - assertFailsConversionToObject( - "converted empty string", - helper, "", Float.class); - } - - - public void testConvertFloatXsd() throws Exception - { - PrimitiveConversionHelper helper = new PrimitiveConversionHelper(true); - - assertEquals("xsd:decimal", helper.getXsdType(Float.class)); - - String str1 = "1234.0"; - Float val1 = Float.valueOf(1234f); - assertEquals(str1, helper.stringify(val1)); - assertEquals(val1, helper.parse(str1, Float.class)); - - String str2 = "-1234.0"; - Float val2 = Float.valueOf(-1234f); - assertEquals(str2, helper.stringify(val2)); - assertEquals(val2, helper.parse(str2, Float.class)); - - String str3 = " -1234.5 "; - Float val3 = Float.valueOf(-1234.5f); - assertEquals(val3, helper.parse(str3, Float.class)); - - assertFailsConversionToObject( - ... [truncated message content] |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-14 19:41:28
|
The package and class structure has changed pretty extensively; Sourceforge held up the original commit notification because it was too large. If you're playing with this, I suggest blowing away your working copy and checking out the latest from the branch. -kdg |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-14 19:00:17
|
Revision: 104 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=104&view=rev Author: kdgregory Date: 2009-08-14 19:00:11 +0000 (Fri, 14 Aug 2009) Log Message: ----------- historical release tag Added Paths: ----------- tags/rel-1.0.3/ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-14 18:59:19
|
Revision: 103 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=103&view=rev Author: kdgregory Date: 2009-08-14 18:59:10 +0000 (Fri, 14 Aug 2009) Log Message: ----------- historical release tag Added Paths: ----------- tags/rel-1.0.2/ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-14 18:57:05
|
Revision: 102 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=102&view=rev Author: kdgregory Date: 2009-08-14 18:56:56 +0000 (Fri, 14 Aug 2009) Log Message: ----------- historical release tag Added Paths: ----------- tags/rel-1.0.1/trunk/ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-14 18:54:10
|
Revision: 101 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=101&view=rev Author: kdgregory Date: 2009-08-14 18:54:03 +0000 (Fri, 14 Aug 2009) Log Message: ----------- historical release tag Added Paths: ----------- tags/rel-1.0.1/ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-14 18:41:56
|
Revision: 100 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=100&view=rev Author: kdgregory Date: 2009-08-14 18:41:47 +0000 (Fri, 14 Aug 2009) Log Message: ----------- repackage Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlAppenders.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlAppenders.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlOptions.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanHandler.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanOptions.java Removed Paths: ------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlAppenders.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlOptions.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java Deleted: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlAppenders.java 2009-08-14 18:27:36 UTC (rev 99) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlAppenders.java 2009-08-14 18:41:47 UTC (rev 100) @@ -1,235 +0,0 @@ -// Copyright 2008-2009 severally by the contributors -// -// 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 net.sf.practicalxml.converter; - -import java.util.EnumSet; - -import javax.xml.XMLConstants; - -import org.w3c.dom.Element; - -import net.sf.practicalxml.DomUtil; - - -/** - * Packaging class used for XML output appenders. This class is a temporary - * hack, as I move intelligence into {@link Bean2XmlDriver}; the contained - * classes will end up in a new package, once I figure out what the package - * structure should be. - */ -public abstract class Bean2XmlAppenders -{ - /** - * An <code>Appender</code> appends children to a single node of the - * output tree. The driver is responsible for creating new appenders - * for each compound element, including the root, and providing those - * appenders with options to control output generation. - */ - public interface Appender - { - /** - * Appends a value element to the current element. Value elements have - * associated text, but no other children. - * - * @param name Name to be associated with the node. - * @param type Type to be associated with the node; may or may - * not be written to the output, depending on the - * appender's options. - * @param value The node's value. May be <code>null</code>, in - * which case the appender decides whether or not - * to actually append the node. - * - * @return The appended element. This is a convenience for subclasses, - * which may want to set additional attributes after their - * super has done the work of appending the element. - * @throws ConversionException if unable to append the node. - */ - public Element appendValue(String name, String type, String value); - - - /** - * Appends a container element to the current element. Container - * elements have other elements as children, and may have a type, - * but do not have an associated value. - */ - public Element appendContainer(String name, String type); - } - - - /** - * Base class for XML appenders, providing helper methods for subclasses. - */ - private static abstract class AbstractAppender - implements Appender - { - private EnumSet<Bean2XmlOptions> _options; - - public AbstractAppender(EnumSet<Bean2XmlOptions> options) - { - _options = options; - } - - protected boolean isOptionSet(Bean2XmlOptions option) - { - return _options.contains(option); - } - - protected boolean shouldSkip(Object value) - { - return (value == null) && !_options.contains(Bean2XmlOptions.XSI_NIL); - } - - protected void setType(Element elem, String type) - { - if (isOptionSet(Bean2XmlOptions.XSI_TYPE)) - elem.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type", type); - } - - protected void setValue(Element elem, String value) - { - if (value != null) - DomUtil.setText(elem, value); - else if (isOptionSet(Bean2XmlOptions.XSI_NIL)) - elem.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil", "true"); - } - } - - - /** - * Basic appender, which appends new elements to a parent. - */ - public static class BasicAppender - extends AbstractAppender - { - private Element _parent; - - public BasicAppender(Element parent, EnumSet<Bean2XmlOptions> options) - { - super(options); - _parent = parent; - } - - public Element appendValue(String name, String type, String value) - { - if (shouldSkip(value)) - return null; - - Element child = DomUtil.appendChildInheritNamespace(_parent, name); - setType(child, type); - setValue(child, value); - return child; - } - - public Element appendContainer(String name, String type) - { - Element child = DomUtil.appendChildInheritNamespace(_parent, name); - setType(child, type); - return child; - } - } - - - /** - * Appender for children of an indexed/iterated item (array, list, or set). - * Each element will have an incremented <code>index</code> attribute that - * indicates the position of the element within the iteration. - */ - public static class IndexedAppender - extends BasicAppender - { - int _index = 0; - - public IndexedAppender(Element parent, EnumSet<Bean2XmlOptions> options) - { - super(parent, options); - } - - - @Override - public Element appendValue(String name, String type, String value) - { - Element child = super.appendValue(name, type, value); - if (child != null) - child.setAttribute("index", String.valueOf(_index++)); - return child; - } - } - - - /** - * Appender for children of a <code>Map</code>. Depending on options, - * will either create children named after the key, or a generic "data" - * child with the key as an attribute. - */ - public static class MapAppender - extends BasicAppender - { - public MapAppender(Element parent, EnumSet<Bean2XmlOptions> options) - { - super(parent, options); - } - - - @Override - public Element appendValue(String name, String type, String value) - { - Element child = null; - if (isOptionSet(Bean2XmlOptions.INTROSPECT_MAPS)) - { - child = super.appendValue(name, type, value); - } - else - { - child = super.appendValue("data", type, value); - if (child != null) - child.setAttribute("key", name); - } - return child; - } - } - - - /** - * An appender that sets values directly on the "parent" element. Used for - * the conversion root element. - */ - public static class DirectAppender - extends AbstractAppender - { - private Element _elem; - - public DirectAppender(Element elem, EnumSet<Bean2XmlOptions> options) - { - super(options); - _elem = elem; - } - - public Element appendValue(String name, String type, String value) - { - if (!shouldSkip(value)) - { - setType(_elem, type); - setValue(_elem, value); - } - return _elem; - } - - public Element appendContainer(String name, String type) - { - setType(_elem, type); - return _elem; - } - } -} Deleted: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlDriver.java 2009-08-14 18:27:36 UTC (rev 99) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlDriver.java 2009-08-14 18:41:47 UTC (rev 100) @@ -1,234 +0,0 @@ -// Copyright 2008-2009 severally by the contributors -// -// 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 net.sf.practicalxml.converter; - -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Map; - -import org.w3c.dom.Element; - -import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.converter.Bean2XmlAppenders.Appender; -import net.sf.practicalxml.converter.Bean2XmlAppenders.BasicAppender; -import net.sf.practicalxml.converter.Bean2XmlAppenders.DirectAppender; -import net.sf.practicalxml.converter.Bean2XmlAppenders.IndexedAppender; -import net.sf.practicalxml.converter.Bean2XmlAppenders.MapAppender; - - -/** - * Driver class for converting bean data to an XML representation. In normal - * usage, an instance of this class is constructed with the desired conversion - * options, then {@link #convert} is called with the compound object to be - * converted. The driver may be reused for multiple conversions, and is - * thread-safe. - */ -public class Bean2XmlDriver -{ - private EnumSet<Bean2XmlOptions> _options = EnumSet.noneOf(Bean2XmlOptions.class); - private PrimitiveConversionHelper _primitiveHelper; - - public Bean2XmlDriver(Bean2XmlOptions... options) - { - for (Bean2XmlOptions option : options) - _options.add(option); - _primitiveHelper = new PrimitiveConversionHelper(shouldUseXsdFormatting()); - } - - -//---------------------------------------------------------------------------- -// Public methods -//---------------------------------------------------------------------------- - - /** - * Creates an XML DOM with the specified root element name, and fills it - * by introspecting the passed object (see {@link #introspect} for - * treatment of simple objects). - */ - public Element convert(Object obj, String rootName) - { - return convert(obj, null, rootName); - } - - - /** - * Creates an XML DOM with the specified root element name and namespace - * URI, and fills it by introspecting the passed object (see {@link - * #introspect} for treatment of simple objects). The namespace URI (and - * prefix, if provided) will be used for all child elements. - */ - public Element convert(Object obj, String nsUri, String rootName) - { - Element root = DomUtil.newDocument(nsUri, rootName); - convert(obj, "", new DirectAppender(root, _options)); - return root; - } - - - /** - * Introspects the passed object, and appends its contents to the output. - * This method is public to allow non-standard conversions, such as - * appending into an existing tree, or (in the future, if we introduce an - * appender factory) producing non-XML output. - */ - public void convert(Object obj, String name, Appender appender) - { - // these methods have side effects! - // empty blocks and comments are there to keep Eclipse happy - if (tryToConvertAsPrimitiveOrNull(obj, null, name, appender)) - { /* it was converted */ } - else if (tryToConvertAsArray(obj, name, appender)) - { /* it was converted */ } - else if (tryToConvertAsMap(obj, name, appender)) - { /* it was converted */ } - else if (tryToConvertAsCollection(obj, name, appender)) - { /* it was converted */ } - else if (tryToConvertAsBean(obj, name, appender)) - { /* it was converted */ } - else - throw new ConversionException("unable to convert: " + obj.getClass().getName()); - } - - -//---------------------------------------------------------------------------- -// Internals -//---------------------------------------------------------------------------- - - private boolean shouldUseXsdFormatting() - { - return _options.contains(Bean2XmlOptions.XSD_FORMAT) - || _options.contains(Bean2XmlOptions.XSI_TYPE); - } - - - private String getJavaXsiType(Object obj) - { - return "java:" + obj.getClass().getName(); - } - - - private boolean tryToConvertAsPrimitiveOrNull( - Object obj, Class<?> klass, String name, Appender appender) - { - if (obj != null) - klass = obj.getClass(); - - String objType = _primitiveHelper.getXsdType(klass); - if ((obj == null) || (objType != null)) - { - appender.appendValue(name, objType, _primitiveHelper.stringify(obj)); - return true; - } - - return false; - } - - - private boolean tryToConvertAsArray(Object array, String name, Appender appender) - { - if (!array.getClass().isArray()) - return false; - - Element parent = appender.appendContainer(name, getJavaXsiType(array)); - Appender childAppender = new IndexedAppender(parent, _options); - int length = Array.getLength(array); - for (int idx = 0 ; idx < length ; idx++) - { - Object value = Array.get(array, idx); - convert(value, "data", childAppender); - } - return true; - } - - - private boolean tryToConvertAsMap(Object obj, String name, Appender appender) - { - if (!(obj instanceof Map)) - return false; - - Element parent = appender.appendContainer(name, getJavaXsiType(obj)); - Appender childAppender = new MapAppender(parent, _options); - for (Map.Entry<?,?> entry : ((Map<?,?>)obj).entrySet()) - { - convert(entry.getValue(), String.valueOf(entry.getKey()), childAppender); - } - return true; - } - - - private boolean tryToConvertAsCollection(Object obj, String name, Appender appender) - { - if (!(obj instanceof Collection)) - return false; - - Element parent = appender.appendContainer(name, getJavaXsiType(obj)); - Appender childAppender = new IndexedAppender(parent, _options); - for (Object value : (Collection<?>)obj) - { - convert(value, "data", childAppender); - } - return true; - } - - - private boolean tryToConvertAsBean(Object bean, String name, Appender appender) - { - Element parent = appender.appendContainer(name, getJavaXsiType(bean)); - Appender childAppender = new BasicAppender(parent, _options); - try - { - BeanInfo info = Introspector.getBeanInfo(bean.getClass(), Object.class); - PropertyDescriptor[] props = info.getPropertyDescriptors(); - for (int ii = 0 ; ii < props.length ; ii++) - convertBeanProperty(bean, props[ii], childAppender); - } - catch (IntrospectionException ee) - { - throw new ConversionException("introspection failure", ee); - } - return true; - } - - - private void convertBeanProperty( - Object bean, PropertyDescriptor propDesc, Appender appender) - { - String name = propDesc.getName(); - Class<?> type = propDesc.getPropertyType(); - Object value; - try - { - Method getter = propDesc.getReadMethod(); - if (getter == null) - return; - value = getter.invoke(bean); - } - catch (Exception ee) - { - throw new ConversionException("unable to retrieve bean value", ee); - } - - if (value == null) - tryToConvertAsPrimitiveOrNull(value, type, name, appender); - else - convert(value, name, appender); - } -} Deleted: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlOptions.java 2009-08-14 18:27:36 UTC (rev 99) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlOptions.java 2009-08-14 18:41:47 UTC (rev 100) @@ -1,55 +0,0 @@ -// Copyright 2008-2009 severally by the contributors -// -// 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 net.sf.practicalxml.converter; - - -/** - * Options used by {@link Bean2XmlHandler} to control the structure of the - * DOM tree. - */ -public enum Bean2XmlOptions -{ - /** - * Outputs values using formats defined by XML Schema, rather than Java's - * <code>String.valueOf()</code> method. Note that these formats are not - * flagged in the element, so sender and receiver will have to agree on - * the format. - */ - XSD_FORMAT, - - /** - * Will add an <code>xsi:type</code> attribute to each element. For values - * covered by the XML Schema simple types, this attribute's value will be - * "<code>xsd:XXX</code>", where XXX is the XSD type name. For complex - * types, this attribute's value will be "<code>java:XXX</code>", where - * XXX is the fully-qualified classname. - * <p> - * <em>This option implies {@link #XSD_FORMAT} for simple types</em>. - */ - XSI_TYPE, - - /** - * Report null values using the <code>xsi:nil="true"</code> attribute. If - * not present, null values are ignored, and not added to DOM tree. - */ - XSI_NIL, - - /** - * Output maps in an "introspected" format, where the name of each item - * is the map key (rather than "data"), and the "key" attribute is omitted. - * If any key is not a valid XML identifier, the converter will throw. - */ - INTROSPECT_MAPS -} Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java 2009-08-14 18:27:36 UTC (rev 99) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java 2009-08-14 18:41:47 UTC (rev 100) @@ -16,7 +16,10 @@ import org.w3c.dom.Document; +import net.sf.practicalxml.converter.bean2xml.Bean2XmlDriver; +import net.sf.practicalxml.converter.bean2xml.Bean2XmlOptions; + /** * Converts Java objects (not just beans) to and from an XML representation. * This was originally developed to support RESTful web services, without the Deleted: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanDriver.java 2009-08-14 18:27:36 UTC (rev 99) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanDriver.java 2009-08-14 18:41:47 UTC (rev 100) @@ -1,35 +0,0 @@ -// Copyright 2008-2009 severally by the contributors -// -// 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 net.sf.practicalxml.converter; - -import org.w3c.dom.Element; - - -/** - * Driver class for converting an XML DOM into a Java bean, using a {@link - * Xml2BeanHandler} to manage data conversion. - */ -public class Xml2BeanDriver -{ - /** - * Fills a bean-style object from an XML element. For each child of the - * passed element, tries to find a property with the same name, invokes - * the appropriate conversion handler method, and sets the property from - * the result. - */ - public void dispatch(Element elem, Object bean) - { - } -} Deleted: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java 2009-08-14 18:27:36 UTC (rev 99) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java 2009-08-14 18:41:47 UTC (rev 100) @@ -1,192 +0,0 @@ -// Copyright 2008-2009 severally by the contributors -// -// 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 net.sf.practicalxml.converter; - -import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.XmlException; -import net.sf.practicalxml.XmlUtil; -import net.sf.practicalxml.internal.StringUtils; - -import java.util.Arrays; -import java.util.Date; -import java.util.EnumSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import javax.xml.XMLConstants; - -import org.w3c.dom.Element; - - -/** - * Invoked by {@link Xml2BeanDriver} to convert a DOM <code>Element</code> - * into the appropriate Java object. Unlike {@link Bean2XmlHandler}, there - * will only be one instance of this object created during conversion; all - * intermediate data can be held on the stack. - */ -public class Xml2BeanHandler -{ - private EnumSet<Xml2BeanOptions> _options; - private PrimitiveConversionHelper _primitiveHelper; - - - /** - * Public constructor, allowing various options specifications. - */ - public Xml2BeanHandler(Xml2BeanOptions... options) - { - _options = EnumSet.noneOf(Xml2BeanOptions.class); - for (Xml2BeanOptions option : options) - _options.add(option); - - _primitiveHelper = new PrimitiveConversionHelper(true); - } - - -//---------------------------------------------------------------------------- -// Public Methods -//---------------------------------------------------------------------------- - - public Boolean convertBoolean(Element elem) - { - return (Boolean)_primitiveHelper.parse(getText(elem), Boolean.class); - } - - - public Byte convertByte(Element elem) - { - return (Byte)_primitiveHelper.parse(getText(elem), Byte.class); - } - - - public Character convertCharacter(Element elem) - { - return (Character)_primitiveHelper.parse(getText(elem), Character.class); - } - - - public Date convertDate(Element elem) - { - throw new UnsupportedOperationException("not implemented yet"); - } - - - public Double convertDouble(Element elem) - { - return (Double)_primitiveHelper.parse(getText(elem), Double.class); - } - - - public Float convertFloat(Element elem) - { - return (Float)_primitiveHelper.parse(getText(elem), Float.class); - } - - - public Integer convertInteger(Element elem) - { - return (Integer)_primitiveHelper.parse(getText(elem), Integer.class); - } - - - public Long convertLong(Element elem) - { - return (Long)_primitiveHelper.parse(getText(elem), Long.class); - } - - - public Number convertNumber(Element elem) - { - throw new UnsupportedOperationException("not implemented yet"); - } - - - public Short convertShort(Element elem) - { - return (Short)_primitiveHelper.parse(getText(elem), Short.class); - } - - - public String convertString(Element elem) - { - return (String)_primitiveHelper.parse(getText(elem), String.class); - } - - - public List<?> convertList(Element elem) - { - throw new UnsupportedOperationException("not implemented yet"); - } - - - public Set<?> convertSet(Element elem) - { - throw new UnsupportedOperationException("not implemented yet"); - } - - - public Map<?,?> convertMap(Element elem) - { - throw new UnsupportedOperationException("not implemented yet"); - } - - - public Object convertObject(Element elem) - { - throw new UnsupportedOperationException("not implemented yet"); - } - - - public Object[] convertObjectArray(Element elem) - { - throw new UnsupportedOperationException("not implemented yet"); - } - - -//---------------------------------------------------------------------------- -// Internals -//---------------------------------------------------------------------------- - - /** - * Returns the text from a passed element, applying any low-level options - * along the way. - */ - private String getText(Element elem) - { - String text = DomUtil.getText(elem); - if (_options.contains(Xml2BeanOptions.CONVERT_BLANK_AS_NULL) && StringUtils.isBlank(text)) - text = null; - validateXsiNil(elem, text); - return text; - } - - - /** - * Checks for elements that require <code>xsi:nil</code>, and throws if missing. - */ - private void validateXsiNil(Element elem, String text) - { - if (text != null) - return; - if (!_options.contains(Xml2BeanOptions.REQUIRE_XSI_NIL)) - return; - - String attr = elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil"); - if ((attr == null) || !attr.equals("true")) - throw new ConversionException("empty element without required xsi:nil"); - } - -} Deleted: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java 2009-08-14 18:27:36 UTC (rev 99) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java 2009-08-14 18:41:47 UTC (rev 100) @@ -1,71 +0,0 @@ -// Copyright 2008-2009 severally by the contributors -// -// 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 net.sf.practicalxml.converter; - - -/** - * Options used by {@link Xml2BeanHandler} to control the way that DOM trees - * are translated to Java beans. - */ -public enum Xml2BeanOptions -{ - /** - * If present, the converter will treat all elements with empty text nodes - * as if they were empty elements -- in other words, <code>null</code>. - * Note that this flag will interact with <code>REQUIRE_XSI_NIL</code>. - */ - CONVERT_BLANK_AS_NULL, - - - /** - * If present, the converter ignores elements that don't correspond to - * settable properties of the bean. - */ - IGNORE_MISSING_PROPERTIES, - - - /** - * If present, the converter will ignore any objects that cannot be - * converted (many of the JDK-provided classes fall into this category, - * because they're not bean-structured). - */ - IGNORE_UNCONVERTIBLE_OBJECTS, - - - /** - * If present, the converter will use a setter method taking a String, in - * preference to any other type. Default behavior is to pick the method - * with the most restrictive type (and considering numeric types as more - * restrictive than <code>String</code>). - */ - PREFER_STRING_SETTER, - - - /** - * If present, the converter requires an <code>xsi:nil</code> attribute - * on any empty nodes, and will throw if it's not present. Default is to - * treat empty nodes as <code>null</code>. - */ - REQUIRE_XSI_NIL, - - - /** - * If present, the converter requires an <code>xsi:type</code> attribute - * on each element, and will throw if it's not present. Default behavior - * uses the <code>xsi:type</code> value to choose between different setter - * methods, but otherwise ignores it. - */ - REQUIRE_XSI_TYPE -} Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlAppenders.java (from rev 99, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlAppenders.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlAppenders.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlAppenders.java 2009-08-14 18:41:47 UTC (rev 100) @@ -0,0 +1,235 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter.bean2xml; + +import java.util.EnumSet; + +import javax.xml.XMLConstants; + +import org.w3c.dom.Element; + +import net.sf.practicalxml.DomUtil; + + +/** + * Packaging class used for XML output appenders. This class is a temporary + * hack, as I move intelligence into {@link Bean2XmlDriver}; the contained + * classes will end up in a new package, once I figure out what the package + * structure should be. + */ +public abstract class Bean2XmlAppenders +{ + /** + * An <code>Appender</code> appends children to a single node of the + * output tree. The driver is responsible for creating new appenders + * for each compound element, including the root, and providing those + * appenders with options to control output generation. + */ + public interface Appender + { + /** + * Appends a value element to the current element. Value elements have + * associated text, but no other children. + * + * @param name Name to be associated with the node. + * @param type Type to be associated with the node; may or may + * not be written to the output, depending on the + * appender's options. + * @param value The node's value. May be <code>null</code>, in + * which case the appender decides whether or not + * to actually append the node. + * + * @return The appended element. This is a convenience for subclasses, + * which may want to set additional attributes after their + * super has done the work of appending the element. + * @throws ConversionException if unable to append the node. + */ + public Element appendValue(String name, String type, String value); + + + /** + * Appends a container element to the current element. Container + * elements have other elements as children, and may have a type, + * but do not have an associated value. + */ + public Element appendContainer(String name, String type); + } + + + /** + * Base class for XML appenders, providing helper methods for subclasses. + */ + private static abstract class AbstractAppender + implements Appender + { + private EnumSet<Bean2XmlOptions> _options; + + public AbstractAppender(EnumSet<Bean2XmlOptions> options) + { + _options = options; + } + + protected boolean isOptionSet(Bean2XmlOptions option) + { + return _options.contains(option); + } + + protected boolean shouldSkip(Object value) + { + return (value == null) && !_options.contains(Bean2XmlOptions.XSI_NIL); + } + + protected void setType(Element elem, String type) + { + if (isOptionSet(Bean2XmlOptions.XSI_TYPE)) + elem.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type", type); + } + + protected void setValue(Element elem, String value) + { + if (value != null) + DomUtil.setText(elem, value); + else if (isOptionSet(Bean2XmlOptions.XSI_NIL)) + elem.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil", "true"); + } + } + + + /** + * Basic appender, which appends new elements to a parent. + */ + public static class BasicAppender + extends AbstractAppender + { + private Element _parent; + + public BasicAppender(Element parent, EnumSet<Bean2XmlOptions> options) + { + super(options); + _parent = parent; + } + + public Element appendValue(String name, String type, String value) + { + if (shouldSkip(value)) + return null; + + Element child = DomUtil.appendChildInheritNamespace(_parent, name); + setType(child, type); + setValue(child, value); + return child; + } + + public Element appendContainer(String name, String type) + { + Element child = DomUtil.appendChildInheritNamespace(_parent, name); + setType(child, type); + return child; + } + } + + + /** + * Appender for children of an indexed/iterated item (array, list, or set). + * Each element will have an incremented <code>index</code> attribute that + * indicates the position of the element within the iteration. + */ + public static class IndexedAppender + extends BasicAppender + { + int _index = 0; + + public IndexedAppender(Element parent, EnumSet<Bean2XmlOptions> options) + { + super(parent, options); + } + + + @Override + public Element appendValue(String name, String type, String value) + { + Element child = super.appendValue(name, type, value); + if (child != null) + child.setAttribute("index", String.valueOf(_index++)); + return child; + } + } + + + /** + * Appender for children of a <code>Map</code>. Depending on options, + * will either create children named after the key, or a generic "data" + * child with the key as an attribute. + */ + public static class MapAppender + extends BasicAppender + { + public MapAppender(Element parent, EnumSet<Bean2XmlOptions> options) + { + super(parent, options); + } + + + @Override + public Element appendValue(String name, String type, String value) + { + Element child = null; + if (isOptionSet(Bean2XmlOptions.INTROSPECT_MAPS)) + { + child = super.appendValue(name, type, value); + } + else + { + child = super.appendValue("data", type, value); + if (child != null) + child.setAttribute("key", name); + } + return child; + } + } + + + /** + * An appender that sets values directly on the "parent" element. Used for + * the conversion root element. + */ + public static class DirectAppender + extends AbstractAppender + { + private Element _elem; + + public DirectAppender(Element elem, EnumSet<Bean2XmlOptions> options) + { + super(options); + _elem = elem; + } + + public Element appendValue(String name, String type, String value) + { + if (!shouldSkip(value)) + { + setType(_elem, type); + setValue(_elem, value); + } + return _elem; + } + + public Element appendContainer(String name, String type) + { + setType(_elem, type); + return _elem; + } + } +} Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java (from rev 99, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlDriver.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlDriver.java 2009-08-14 18:41:47 UTC (rev 100) @@ -0,0 +1,232 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter.bean2xml; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Map; + +import org.w3c.dom.Element; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.converter.ConversionException; +import net.sf.practicalxml.converter.PrimitiveConversionHelper; +import net.sf.practicalxml.converter.bean2xml.Bean2XmlAppenders.*; + + +/** + * Driver class for converting bean data to an XML representation. In normal + * usage, an instance of this class is constructed with the desired conversion + * options, then {@link #convert} is called with the compound object to be + * converted. The driver may be reused for multiple conversions, and is + * thread-safe. + */ +public class Bean2XmlDriver +{ + private EnumSet<Bean2XmlOptions> _options = EnumSet.noneOf(Bean2XmlOptions.class); + private PrimitiveConversionHelper _primitiveHelper; + + public Bean2XmlDriver(Bean2XmlOptions... options) + { + for (Bean2XmlOptions option : options) + _options.add(option); + _primitiveHelper = new PrimitiveConversionHelper(shouldUseXsdFormatting()); + } + + +//---------------------------------------------------------------------------- +// Public methods +//---------------------------------------------------------------------------- + + /** + * Creates an XML DOM with the specified root element name, and fills it + * by introspecting the passed object (see {@link #introspect} for + * treatment of simple objects). + */ + public Element convert(Object obj, String rootName) + { + return convert(obj, null, rootName); + } + + + /** + * Creates an XML DOM with the specified root element name and namespace + * URI, and fills it by introspecting the passed object (see {@link + * #introspect} for treatment of simple objects). The namespace URI (and + * prefix, if provided) will be used for all child elements. + */ + public Element convert(Object obj, String nsUri, String rootName) + { + Element root = DomUtil.newDocument(nsUri, rootName); + convert(obj, "", new DirectAppender(root, _options)); + return root; + } + + + /** + * Introspects the passed object, and appends its contents to the output. + * This method is public to allow non-standard conversions, such as + * appending into an existing tree, or (in the future, if we introduce an + * appender factory) producing non-XML output. + */ + public void convert(Object obj, String name, Appender appender) + { + // these methods have side effects! + // empty blocks and comments are there to keep Eclipse happy + if (tryToConvertAsPrimitiveOrNull(obj, null, name, appender)) + { /* it was converted */ } + else if (tryToConvertAsArray(obj, name, appender)) + { /* it was converted */ } + else if (tryToConvertAsMap(obj, name, appender)) + { /* it was converted */ } + else if (tryToConvertAsCollection(obj, name, appender)) + { /* it was converted */ } + else if (tryToConvertAsBean(obj, name, appender)) + { /* it was converted */ } + else + throw new ConversionException("unable to convert: " + obj.getClass().getName()); + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + private boolean shouldUseXsdFormatting() + { + return _options.contains(Bean2XmlOptions.XSD_FORMAT) + || _options.contains(Bean2XmlOptions.XSI_TYPE); + } + + + private String getJavaXsiType(Object obj) + { + return "java:" + obj.getClass().getName(); + } + + + private boolean tryToConvertAsPrimitiveOrNull( + Object obj, Class<?> klass, String name, Appender appender) + { + if (obj != null) + klass = obj.getClass(); + + String objType = _primitiveHelper.getXsdType(klass); + if ((obj == null) || (objType != null)) + { + appender.appendValue(name, objType, _primitiveHelper.stringify(obj)); + return true; + } + + return false; + } + + + private boolean tryToConvertAsArray(Object array, String name, Appender appender) + { + if (!array.getClass().isArray()) + return false; + + Element parent = appender.appendContainer(name, getJavaXsiType(array)); + Appender childAppender = new IndexedAppender(parent, _options); + int length = Array.getLength(array); + for (int idx = 0 ; idx < length ; idx++) + { + Object value = Array.get(array, idx); + convert(value, "data", childAppender); + } + return true; + } + + + private boolean tryToConvertAsMap(Object obj, String name, Appender appender) + { + if (!(obj instanceof Map)) + return false; + + Element parent = appender.appendContainer(name, getJavaXsiType(obj)); + Appender childAppender = new MapAppender(parent, _options); + for (Map.Entry<?,?> entry : ((Map<?,?>)obj).entrySet()) + { + convert(entry.getValue(), String.valueOf(entry.getKey()), childAppender); + } + return true; + } + + + private boolean tryToConvertAsCollection(Object obj, String name, Appender appender) + { + if (!(obj instanceof Collection)) + return false; + + Element parent = appender.appendContainer(name, getJavaXsiType(obj)); + Appender childAppender = new IndexedAppender(parent, _options); + for (Object value : (Collection<?>)obj) + { + convert(value, "data", childAppender); + } + return true; + } + + + private boolean tryToConvertAsBean(Object bean, String name, Appender appender) + { + Element parent = appender.appendContainer(name, getJavaXsiType(bean)); + Appender childAppender = new BasicAppender(parent, _options); + try + { + BeanInfo info = Introspector.getBeanInfo(bean.getClass(), Object.class); + PropertyDescriptor[] props = info.getPropertyDescriptors(); + for (int ii = 0 ; ii < props.length ; ii++) + convertBeanProperty(bean, props[ii], childAppender); + } + catch (IntrospectionException ee) + { + throw new ConversionException("introspection failure", ee); + } + return true; + } + + + private void convertBeanProperty( + Object bean, PropertyDescriptor propDesc, Appender appender) + { + String name = propDesc.getName(); + Class<?> type = propDesc.getPropertyType(); + Object value; + try + { + Method getter = propDesc.getReadMethod(); + if (getter == null) + return; + value = getter.invoke(bean); + } + catch (Exception ee) + { + throw new ConversionException("unable to retrieve bean value", ee); + } + + if (value == null) + tryToConvertAsPrimitiveOrNull(value, type, name, appender); + else + convert(value, name, appender); + } +} Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlOptions.java (from rev 93, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlOptions.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlOptions.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/Bean2XmlOptions.java 2009-08-14 18:41:47 UTC (rev 100) @@ -0,0 +1,55 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter.bean2xml; + + +/** + * Options used by {@link Bean2XmlHandler} to control the structure of the + * DOM tree. + */ +public enum Bean2XmlOptions +{ + /** + * Outputs values using formats defined by XML Schema, rather than Java's + * <code>String.valueOf()</code> method. Note that these formats are not + * flagged in the element, so sender and receiver will have to agree on + * the format. + */ + XSD_FORMAT, + + /** + * Will add an <code>xsi:type</code> attribute to each element. For values + * covered by the XML Schema simple types, this attribute's value will be + * "<code>xsd:XXX</code>", where XXX is the XSD type name. For complex + * types, this attribute's value will be "<code>java:XXX</code>", where + * XXX is the fully-qualified classname. + * <p> + * <em>This option implies {@link #XSD_FORMAT} for simple types</em>. + */ + XSI_TYPE, + + /** + * Report null values using the <code>xsi:nil="true"</code> attribute. If + * not present, null values are ignored, and not added to DOM tree. + */ + XSI_NIL, + + /** + * Output maps in an "introspected" format, where the name of each item + * is the map key (rather than "data"), and the "key" attribute is omitted. + * If any key is not a valid XML identifier, the converter will throw. + */ + INTROSPECT_MAPS +} Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanDriver.java (from rev 96, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanDriver.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanDriver.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanDriver.java 2009-08-14 18:41:47 UTC (rev 100) @@ -0,0 +1,35 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter.xml2bean; + +import org.w3c.dom.Element; + + +/** + * Driver class for converting an XML DOM into a Java bean, using a {@link + * Xml2BeanHandler} to manage data conversion. + */ +public class Xml2BeanDriver +{ + /** + * Fills a bean-style object from an XML element. For each child of the + * passed element, tries to find a property with the same name, invokes + * the appropriate conversion handler method, and sets the property from + * the result. + */ + public void dispatch(Element elem, Object bean) + { + } +} Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanHandler.java (from rev 98, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanHandler.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanHandler.java 2009-08-14 18:41:47 UTC (rev 100) @@ -0,0 +1,194 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter.xml2bean; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.XmlException; +import net.sf.practicalxml.XmlUtil; +import net.sf.practicalxml.converter.ConversionException; +import net.sf.practicalxml.converter.PrimitiveConversionHelper; +import net.sf.practicalxml.internal.StringUtils; + +import java.util.Arrays; +import java.util.Date; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.xml.XMLConstants; + +import org.w3c.dom.Element; + + +/** + * Invoked by {@link Xml2BeanDriver} to convert a DOM <code>Element</code> + * into the appropriate Java object. Unlike {@link Bean2XmlHandler}, there + * will only be one instance of this object created during conversion; all + * intermediate data can be held on the stack. + */ +public class Xml2BeanHandler +{ + private EnumSet<Xml2BeanOptions> _options; + private PrimitiveConversionHelper _primitiveHelper; + + + /** + * Public constructor, allowing various options specifications. + */ + public Xml2BeanHandler(Xml2BeanOptions... options) + { + _options = EnumSet.noneOf(Xml2BeanOptions.class); + for (Xml2BeanOptions option : options) + _options.add(option); + + _primitiveHelper = new PrimitiveConversionHelper(true); + } + + +//---------------------------------------------------------------------------- +// Public Methods +//---------------------------------------------------------------------------- + + public Boolean convertBoolean(Element elem) + { + return (Boolean)_primitiveHelper.parse(getText(elem), Boolean.class); + } + + + public Byte convertByte(Element elem) + { + return (Byte)_primitiveHelper.parse(getText(elem), Byte.class); + } + + + public Character convertCharacter(Element elem) + { + return (Character)_primitiveHelper.parse(getText(elem), Character.class); + } + + + public Date convertDate(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Double convertDouble(Element elem) + { + return (Double)_primitiveHelper.parse(getText(elem), Double.class); + } + + + public Float convertFloat(Element elem) + { + return (Float)_primitiveHelper.parse(getText(elem), Float.class); + } + + + public Integer convertInteger(Element elem) + { + return (Integer)_primitiveHelper.parse(getText(elem), Integer.class); + } + + + public Long convertLong(Element elem) + { + return (Long)_primitiveHelper.parse(getText(elem), Long.class); + } + + + public Number convertNumber(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Short convertShort(Element elem) + { + return (Short)_primitiveHelper.parse(getText(elem), Short.class); + } + + + public String convertString(Element elem) + { + return (String)_primitiveHelper.parse(getText(elem), String.class); + } + + + public List<?> convertList(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Set<?> convertSet(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Map<?,?> convertMap(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Object convertObject(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Object[] convertObjectArray(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + /** + * Returns the text from a passed element, applying any low-level options + * along the way. + */ + private String getText(Element elem) + { + String text = DomUtil.getText(elem); + if (_options.contains(Xml2BeanOptions.CONVERT_BLANK_AS_NULL) && StringUtils.isBlank(text)) + text = null; + validateXsiNil(elem, text); + return text; + } + + + /** + * Checks for elements that require <code>xsi:nil</code>, and throws if missing. + */ + private void validateXsiNil(Element elem, String text) + { + if (text != null) + return; + if (!_options.contains(Xml2BeanOptions.REQUIRE_XSI_NIL)) + return; + + String attr = elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil"); + if ((attr == null) || !attr.equals("true")) + throw new ConversionException("empty element without required xsi:nil"); + } + +} Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanOptions.java (from rev 98, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanOptions.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/Xml2BeanOptions.java 2009-08-14 18:41:47 UTC (rev 100) @@ -0,0 +1,71 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 CONDITI... [truncated message content] |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-12 20:35:45
|
Revision: 98 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=98&view=rev Author: kdgregory Date: 2009-08-12 20:35:22 +0000 (Wed, 12 Aug 2009) Log Message: ----------- checkpoint - flesh out parsing options, move primitive conversion to own class Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlHandler.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/PrimitiveConversionHelper.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestPrimitiveConversionHelper.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlHandler.java 2009-08-10 22:30:50 UTC (rev 97) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlHandler.java 2009-08-12 20:35:22 UTC (rev 98) @@ -17,6 +17,7 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.XmlUtil; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Date; import java.util.EnumSet; @@ -46,6 +47,7 @@ { private EnumSet<Bean2XmlOptions> _options; private Appender _appender; + private PrimitiveConversionHelper _primitiveHelper; /** @@ -54,10 +56,13 @@ */ public Bean2XmlHandler(Element appendTo, Bean2XmlOptions... options) { - _appender = new Appender(appendTo); _options = EnumSet.noneOf(Bean2XmlOptions.class); for (Bean2XmlOptions option : options) _options.add(option); + + _appender = new Appender(appendTo); + + _primitiveHelper = new PrimitiveConversionHelper(shouldFormatAsXsd()); } @@ -68,6 +73,7 @@ { _appender = appender; _options = parent._options; + _primitiveHelper = parent._primitiveHelper; } @@ -77,83 +83,69 @@ public void convert(String name, Boolean value) { - Object formatted = value; - if (shouldFormatAsXsd()) - formatted = value.booleanValue() ? "true" : "false"; - - _appender.append(name, "xsd:boolean", formatted); + appendPrimitive(name, Boolean.class, value); } public void convert(String name, Byte value) { - _appender.append(name, "xsd:byte", value); + appendPrimitive(name, Byte.class, value); } public void convert(String name, Character value) { - _appender.append(name, "xsd:string", value); + appendPrimitive(name, Character.class, value); } public void convert(String name, Date value) { - Object formatted = value; - if (shouldFormatAsXsd()) - formatted = XmlUtil.formatXsdDatetime(value); - - _appender.append(name, "xsd:dateTime", formatted); + appendPrimitive(name, Date.class, value); } public void convert(String name, Double value) { - Object formatted = value; - if (shouldFormatAsXsd()) - formatted = XmlUtil.formatXsdDecimal(value); - - _appender.append(name, "xsd:decimal", formatted); + appendPrimitive(name, Double.class, value); } public void convert(String name, Float value) { - Object formatted = value; - if (shouldFormatAsXsd()) - formatted = XmlUtil.formatXsdDecimal(value); - - _appender.append(name, "xsd:decimal", formatted); + appendPrimitive(name, Float.class, value); } public void convert(String name, Integer value) { - _appender.append(name, "xsd:int", value); + appendPrimitive(name, Integer.class, value); } public void convert(String name, Long value) { - _appender.append(name, "xsd:long", value); + appendPrimitive(name, Long.class, value); } + // FIXME - passes tests but not pretty ... + // the multitude of convert methods will go away, however public void convert(String name, Number value) { - _appender.append(name, "xsd:decimal", value); + appendPrimitive(name, BigDecimal.class, value); } public void convert(String name, Short value) { - _appender.append(name, "xsd:short", value); + appendPrimitive(name, Short.class, value); } public void convert(String name, String value) { - _appender.append(name, "xsd:string", value); + appendPrimitive(name, String.class, value); } @@ -229,6 +221,19 @@ /** + * Common code for primitive objects. + */ + public void appendPrimitive(String name, Class<?> klass, Object value) + { + String xsdType = _primitiveHelper.getXsdType(klass); + if (xsdType == null) + throw new ConversionException("internal error: " + klass.getName() + + " passed as primitive"); + _appender.append(name, xsdType, _primitiveHelper.stringify(value)); + } + + + /** * Common code for sequenced objects -- arrays, lists, and sets. */ private void appendSequence(Element parent, Iterator<?> itx, boolean isIndexed) Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/PrimitiveConversionHelper.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/PrimitiveConversionHelper.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/PrimitiveConversionHelper.java 2009-08-12 20:35:22 UTC (rev 98) @@ -0,0 +1,446 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import net.sf.practicalxml.XmlUtil; + + +/** + * Responsible for converting "primitive" types -- those with unambiguous + * string representations -- to/from such a representation. + */ +public class PrimitiveConversionHelper +{ + // this is not static because the helpers are inner classes + private Map<Class<?>,ConversionHandler<?>> _helpers + = new HashMap<Class<?>,ConversionHandler<?>>(); + { + _helpers.put(String.class, new StringConversionHandler()); + _helpers.put(Character.class, new CharacterConversionHandler()); + _helpers.put(Boolean.class, new BooleanConversionHandler()); + _helpers.put(Byte.class, new ByteConversionHandler()); + _helpers.put(Short.class, new ShortConversionHandler()); + _helpers.put(Integer.class, new IntegerConversionHandler()); + _helpers.put(Long.class, new LongConversionHandler()); + _helpers.put(Float.class, new FloatConversionHandler()); + _helpers.put(Double.class, new DoubleConversionHandler()); + _helpers.put(BigInteger.class, new BigIntegerConversionHandler()); + _helpers.put(BigDecimal.class, new BigDecimalConversionHandler()); + _helpers.put(Date.class, new DateConversionHandler()); + } + + private boolean _useXsdFormatting; + + + /** + * Default constructor, which uses Java formatting. + */ + public PrimitiveConversionHelper() + { + // nothing to see here + } + + + /** + * Constructor that allows selection of Java or XSD formatting. + */ + public PrimitiveConversionHelper(boolean useXsdFormatting) + { + _useXsdFormatting = useXsdFormatting; + } + + +//---------------------------------------------------------------------------- +// Public Methods +//---------------------------------------------------------------------------- + + /** + * Returns the XSD type name to use for stringified objects of the + * specified class, <code>null</code> if this converter can't convert + * instances of the class. + */ + public String getXsdType(Class<?> klass) + { + ConversionHandler<?> helper = _helpers.get(klass); + return (helper == null) ? null : helper.getXsdType(); + } + + + /** + * Converts the passed object into its string representation, according + * to the options currently in effect. Passing <code>null</code> will + * return <code>null</code>. Throws {@link ConversionException} if + * unable to convert the passed object. + */ + public String stringify(Object obj) + { + if (obj == null) + return null; + + try + { + return getHelper(obj.getClass()).stringify(obj); + } + catch (Exception ee) + { + if (ee instanceof ConversionException) + throw (ConversionException)ee; + throw new ConversionException("unable to convert: " + obj, ee); + } + } + + + /** + * Parses the passed string into an object of the desired class. Passing + * <code>null</code> will return <code>null</code>, passing an empty + * string will typically throw a {@link ConversionException}. + */ + public Object parse(String str, Class<?> klass) + { + if (str == null) + return null; + + try + { + return getHelper(klass).parse(str); + } + catch (Exception ee) + { + if (ee instanceof ConversionException) + throw (ConversionException)ee; + throw new ConversionException("unable to parse: " + str, ee); + } + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + /** + * Returns the appropriate conversion helper or throws. + */ + @SuppressWarnings(value="unchecked") + private ConversionHandler getHelper(Class<?> klass) + { + ConversionHandler<?> helper = _helpers.get(klass); + if (helper == null) + throw new ConversionException("unable to get helper: " + klass.getName()); + return helper; + } + + + /** + * Each primitive class has its own conversion handler that is responsible + * for converting to/from a string representation. Handlers are guaranteed + * to receive non-null objects/strings. + * <p> + * This interface is parameterized so that the compiler will generate + * bridge methods for implementation classes. Elsewhere, we don't care + * about parameterization, so wildcard or drop it (see {@link #getHelper}). + * <p> + * Implementation classes are expected to be inner classes, so that they + * have access to configuration information (such as formatting rules). + * <p> + * Implementation classes are permitted to throw any exception; caller is + * expected to catch them and translate to a {@link ConversionException}. + */ + private static interface ConversionHandler<T> + { + public String getXsdType(); + public String stringify(T obj) throws Exception; + public T parse(String str) throws Exception; + } + + + private class StringConversionHandler + implements ConversionHandler<String> + { + public String getXsdType() + { + return "xsd:string"; + } + + public String stringify(String obj) + { + return String.valueOf(obj); + } + + public String parse(String str) + { + return str; + } + } + + + private class CharacterConversionHandler + implements ConversionHandler<Character> + { + private final Character NUL = Character.valueOf('\0'); + + public String getXsdType() + { + return "xsd:string"; + } + + public String stringify(Character obj) + { + if (obj.equals(NUL)) + return ""; + return obj.toString(); + } + + public Character parse(String str) + { + if (str.length() == 0) + return NUL; + if (str.length() > 1) + throw new ConversionException( + "attempted to convert multi-character string: \"" + str + "\""); + return Character.valueOf(str.charAt(0)); + } + } + + + private class BooleanConversionHandler + implements ConversionHandler<Boolean> + { + public String getXsdType() + { + return "xsd:boolean"; + } + + public String stringify(Boolean obj) + { + return _useXsdFormatting + ? XmlUtil.formatXsdBoolean(obj.booleanValue()) + : obj.toString(); + } + + public Boolean parse(String str) + { + return _useXsdFormatting + ? XmlUtil.parseXsdBoolean(str) + : Boolean.parseBoolean(str); + } + } + + + private class ByteConversionHandler + implements ConversionHandler<Byte> + { + public String getXsdType() + { + return "xsd:byte"; + } + + public String stringify(Byte obj) + { + return obj.toString(); + } + + public Byte parse(String str) + { + return Byte.valueOf(str.trim()); + } + } + + + private class ShortConversionHandler + implements ConversionHandler<Short> + { + public String getXsdType() + { + return "xsd:short"; + } + + public String stringify(Short obj) + { + return obj.toString(); + } + + public Short parse(String str) + { + return Short.valueOf(str.trim()); + } + } + + + private class IntegerConversionHandler + implements ConversionHandler<Integer> + { + public String getXsdType() + { + return "xsd:int"; + } + + public String stringify(Integer obj) + { + return obj.toString(); + } + + public Integer parse(String str) + { + return Integer.valueOf(str.trim()); + } + } + + + private class LongConversionHandler + implements ConversionHandler<Long> + { + public String getXsdType() + { + return "xsd:long"; + } + + public String stringify(Long obj) + { + return obj.toString(); + } + + public Long parse(String str) + { + return Long.valueOf(str.trim()); + } + } + + + private class FloatConversionHandler + implements ConversionHandler<Float> + { + public String getXsdType() + { + return "xsd:decimal"; + } + + public String stringify(Float obj) + { + return _useXsdFormatting + ? XmlUtil.formatXsdDecimal(obj) + : obj.toString(); + } + + public Float parse(String str) + { + return Float.valueOf(str.trim()); + } + } + + + private class DoubleConversionHandler + implements ConversionHandler<Double> + { + public String getXsdType() + { + return "xsd:decimal"; + } + + public String stringify(Double obj) + { + return _useXsdFormatting + ? XmlUtil.formatXsdDecimal(obj) + : obj.toString(); + } + + public Double parse(String str) + { + return Double.valueOf(str.trim()); + } + } + + + private class BigIntegerConversionHandler + implements ConversionHandler<BigInteger> + { + public String getXsdType() + { + return "xsd:decimal"; + } + + public String stringify(BigInteger obj) + { + return obj.toString(); + } + + public BigInteger parse(String str) + { + return new BigInteger(str.trim()); + } + } + + + private class BigDecimalConversionHandler + implements ConversionHandler<BigDecimal> + { + public String getXsdType() + { + return "xsd:decimal"; + } + + public String stringify(BigDecimal obj) + { + return obj.toString(); + } + + public BigDecimal parse(String str) + { + return new BigDecimal(str.trim()); + } + } + + + private class DateConversionHandler + implements ConversionHandler<Date> + { + // format as specified by Date.toString() JavaDoc + private DateFormat _defaultFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"); + + public String getXsdType() + { + return "xsd:dateTime"; + } + + public String stringify(Date obj) + { + return _useXsdFormatting + ? XmlUtil.formatXsdDatetime(obj) + : obj.toString(); + } + + public Date parse(String str) + throws ParseException + { + if (_useXsdFormatting) + return XmlUtil.parseXsdDatetime(str); + else + { + synchronized (_defaultFormat) + { + return _defaultFormat.parse(str); + } + } + } + } +} Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java 2009-08-10 22:30:50 UTC (rev 97) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java 2009-08-12 20:35:22 UTC (rev 98) @@ -15,7 +15,9 @@ package net.sf.practicalxml.converter; import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.XmlException; import net.sf.practicalxml.XmlUtil; +import net.sf.practicalxml.internal.StringUtils; import java.util.Arrays; import java.util.Date; @@ -38,6 +40,7 @@ public class Xml2BeanHandler { private EnumSet<Xml2BeanOptions> _options; + private PrimitiveConversionHelper _primitiveHelper; /** @@ -48,6 +51,8 @@ _options = EnumSet.noneOf(Xml2BeanOptions.class); for (Xml2BeanOptions option : options) _options.add(option); + + _primitiveHelper = new PrimitiveConversionHelper(true); } @@ -55,27 +60,21 @@ // Public Methods //---------------------------------------------------------------------------- - public Boolean convertBoolean(Element elem) { - String text = DomUtil.getText(elem); - return Boolean.valueOf(XmlUtil.parseXsdBoolean(text)); + return (Boolean)_primitiveHelper.parse(getText(elem), Boolean.class); } public Byte convertByte(Element elem) { - String text = DomUtil.getText(elem); - return Byte.valueOf(text); + return (Byte)_primitiveHelper.parse(getText(elem), Byte.class); } public Character convertCharacter(Element elem) { - String text = DomUtil.getText(elem); - if (text.length() == 0) - return '\0'; - return Character.valueOf(text.charAt(0)); + return (Character)_primitiveHelper.parse(getText(elem), Character.class); } @@ -87,29 +86,25 @@ public Double convertDouble(Element elem) { - String text = DomUtil.getText(elem); - return Double.valueOf(text); + return (Double)_primitiveHelper.parse(getText(elem), Double.class); } public Float convertFloat(Element elem) { - String text = DomUtil.getText(elem); - return Float.valueOf(text); + return (Float)_primitiveHelper.parse(getText(elem), Float.class); } public Integer convertInteger(Element elem) { - String text = DomUtil.getText(elem); - return Integer.valueOf(text); + return (Integer)_primitiveHelper.parse(getText(elem), Integer.class); } public Long convertLong(Element elem) { - String text = DomUtil.getText(elem); - return Long.valueOf(text); + return (Long)_primitiveHelper.parse(getText(elem), Long.class); } @@ -121,15 +116,13 @@ public Short convertShort(Element elem) { - String text = DomUtil.getText(elem); - return Short.valueOf(text); + return (Short)_primitiveHelper.parse(getText(elem), Short.class); } public String convertString(Element elem) { - String text = DomUtil.getText(elem); - return text; + return (String)_primitiveHelper.parse(getText(elem), String.class); } @@ -167,5 +160,33 @@ // Internals //---------------------------------------------------------------------------- + /** + * Returns the text from a passed element, applying any low-level options + * along the way. + */ + private String getText(Element elem) + { + String text = DomUtil.getText(elem); + if (_options.contains(Xml2BeanOptions.CONVERT_BLANK_AS_NULL) && StringUtils.isBlank(text)) + text = null; + validateXsiNil(elem, text); + return text; + } + + /** + * Checks for elements that require <code>xsi:nil</code>, and throws if missing. + */ + private void validateXsiNil(Element elem, String text) + { + if (text != null) + return; + if (!_options.contains(Xml2BeanOptions.REQUIRE_XSI_NIL)) + return; + + String attr = elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil"); + if ((attr == null) || !attr.equals("true")) + throw new ConversionException("empty element without required xsi:nil"); + } + } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java 2009-08-10 22:30:50 UTC (rev 97) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java 2009-08-12 20:35:22 UTC (rev 98) @@ -22,6 +22,14 @@ public enum Xml2BeanOptions { /** + * If present, the converter will treat all elements with empty text nodes + * as if they were empty elements -- in other words, <code>null</code>. + * Note that this flag will interact with <code>REQUIRE_XSI_NIL</code>. + */ + CONVERT_BLANK_AS_NULL, + + + /** * If present, the converter ignores elements that don't correspond to * settable properties of the bean. */ @@ -37,19 +45,27 @@ /** + * If present, the converter will use a setter method taking a String, in + * preference to any other type. Default behavior is to pick the method + * with the most restrictive type (and considering numeric types as more + * restrictive than <code>String</code>). + */ + PREFER_STRING_SETTER, + + + /** + * If present, the converter requires an <code>xsi:nil</code> attribute + * on any empty nodes, and will throw if it's not present. Default is to + * treat empty nodes as <code>null</code>. + */ + REQUIRE_XSI_NIL, + + + /** * If present, the converter requires an <code>xsi:type</code> attribute * on each element, and will throw if it's not present. Default behavior * uses the <code>xsi:type</code> value to choose between different setter * methods, but otherwise ignores it. */ - REQUIRE_XSI_TYPE, - - - /** - * If present, the converter will use a setter method taking a String, in - * preference to any other type. Default behavior is to pick the method - * with the most restrictive type (and considering numeric types as more - * restrictive than <code>String</code>). - */ - PREFER_STRING_SETTER + REQUIRE_XSI_TYPE } Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestPrimitiveConversionHelper.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestPrimitiveConversionHelper.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestPrimitiveConversionHelper.java 2009-08-12 20:35:22 UTC (rev 98) @@ -0,0 +1,510 @@ +// Copyright (c) Keith D Gregory, all rights reserved +package net.sf.practicalxml.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; + +import junit.framework.TestCase; + +public class TestPrimitiveConversionHelper extends TestCase +{ +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + private void assertFailsConversionToObject( + String message, + PrimitiveConversionHelper helper, + String str, + Class<?> klass) + { + try + { + helper.parse(str, klass); + fail(message); + } + catch (ConversionException ee) + { + // success! + } + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + public void testUnknownClass() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + assertNull(helper.getXsdType(Class.class)); + } + + + public void testConvertNull() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + assertNull(helper.stringify(null)); + assertNull(helper.parse(null, Object.class)); + } + + + public void testConvertString() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + assertEquals("xsd:string", helper.getXsdType(String.class)); + + assertEquals("foo", helper.stringify("foo")); + assertEquals("foo", helper.parse("foo", String.class)); + + assertEquals("", helper.stringify("")); + assertEquals("", helper.parse("", String.class)); + } + + + public void testConvertCharacter() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + assertEquals("xsd:string", helper.getXsdType(Character.class)); + + Character simple = Character.valueOf('A'); + assertEquals("A", helper.stringify(simple)); + assertEquals(simple, helper.parse("A", Character.class)); + + Character nul = Character.valueOf('\0'); + assertEquals("", helper.stringify(nul)); + assertEquals(nul, helper.parse("", Character.class)); + + assertFailsConversionToObject( + "converted multi-character string", + helper, "ix", Character.class); + } + + + public void testConvertBooleanDefault() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + // default boolean conversion is compatible with XSD type def + assertEquals("xsd:boolean", helper.getXsdType(Boolean.class)); + + String sTrue = Boolean.TRUE.toString(); + assertEquals(sTrue, helper.stringify(Boolean.TRUE)); + assertEquals(Boolean.TRUE, helper.parse(sTrue, Boolean.class)); + + String sFalse = Boolean.FALSE.toString(); + assertEquals(sFalse, helper.stringify(Boolean.FALSE)); + assertEquals(Boolean.FALSE, helper.parse(sFalse, Boolean.class)); + + assertEquals(Boolean.FALSE, helper.parse("ix", Boolean.class)); + assertEquals(Boolean.FALSE, helper.parse("", Boolean.class)); + } + + + public void testConvertBooleanXsd() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(true); + + assertEquals("xsd:boolean", helper.getXsdType(Boolean.class)); + + assertEquals("true", helper.stringify(Boolean.TRUE)); + assertEquals(Boolean.TRUE, helper.parse("true", Boolean.class)); + assertEquals(Boolean.TRUE, helper.parse("1", Boolean.class)); + + assertEquals("false", helper.stringify(Boolean.FALSE)); + assertEquals(Boolean.FALSE, helper.parse("false", Boolean.class)); + assertEquals(Boolean.FALSE, helper.parse("0", Boolean.class)); + + assertFailsConversionToObject( + "converted multi-character string", + helper, "ix", Boolean.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Boolean.class); + } + + + public void testConvertByte() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + assertEquals("xsd:byte", helper.getXsdType(Byte.class)); + + String str1 = "123"; + Byte val1 = Byte.valueOf((byte)123); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Byte.class)); + + String str2 = "-123"; + Byte val2 = Byte.valueOf((byte)-123); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Byte.class)); + + String str3 = " -123 "; + Byte val3 = Byte.valueOf((byte)-123); + assertEquals(val3, helper.parse(str3, Byte.class)); + + assertFailsConversionToObject( + "converted too-large value", + helper, "1234567", Byte.class); + + assertFailsConversionToObject( + "converted non-integer value", + helper, "1.23", Byte.class); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Byte.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Byte.class); + } + + + public void testConvertShort() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + assertEquals("xsd:short", helper.getXsdType(Short.class)); + + String str1 = "12345"; + Short val1 = Short.valueOf((short)12345); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Short.class)); + + String str2 = "-12345"; + Short val2 = Short.valueOf((short)-12345); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Short.class)); + + String str3 = " -12345 "; + Short val3 = Short.valueOf((short)-12345); + assertEquals(val3, helper.parse(str3, Short.class)); + + assertFailsConversionToObject( + "converted too-large value", + helper, "1234567", Short.class); + + assertFailsConversionToObject( + "converted non-integer value", + helper, "123.45", Short.class); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Short.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Short.class); + } + + + public void testConvertInteger() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + assertEquals("xsd:int", helper.getXsdType(Integer.class)); + + String str1 = "1234567"; + Integer val1 = Integer.valueOf(1234567); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Integer.class)); + + String str2 = "-1234567"; + Integer val2 = Integer.valueOf(-1234567); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Integer.class)); + + String str3 = " -1234567 "; + Integer val3 = Integer.valueOf(-1234567); + assertEquals(val3, helper.parse(str3, Integer.class)); + + assertFailsConversionToObject( + "converted too-large value", + helper, "123456789012345", Integer.class); + + assertFailsConversionToObject( + "converted non-integer value", + helper, "123.45", Integer.class); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Integer.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Integer.class); + } + + + public void testConvertLong() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + assertEquals("xsd:long", helper.getXsdType(Long.class)); + + String str1 = "1234567890"; + Long val1 = Long.valueOf(1234567890L); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Long.class)); + + String str2 = "-1234567890"; + Long val2 = Long.valueOf(-1234567890L); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Long.class)); + + String str3 = " -1234567890 "; + Long val3 = Long.valueOf(-1234567890L); + assertEquals(val3, helper.parse(str3, Long.class)); + + assertFailsConversionToObject( + "converted too-large value", + helper, "123456789012345678901234567890", Long.class); + + assertFailsConversionToObject( + "converted non-integer value", + helper, "123.45", Long.class); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Long.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Long.class); + } + + + public void testConvertFloatDefault() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + // default float conversion is compatible with XSD type def + assertEquals("xsd:decimal", helper.getXsdType(Float.class)); + + // note: for default-format tests, strings are generated from values + Float val1 = Float.valueOf(1234f); + String str1 = val1.toString(); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Float.class)); + + Float val2 = Float.valueOf(-1234f); + String str2 = val2.toString(); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Float.class)); + + String str3 = " -1234.5 "; + Float val3 = Float.valueOf(-1234.5f); + assertEquals(val3, helper.parse(str3, Float.class)); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Float.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Float.class); + } + + + public void testConvertFloatXsd() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(true); + + assertEquals("xsd:decimal", helper.getXsdType(Float.class)); + + String str1 = "1234.0"; + Float val1 = Float.valueOf(1234f); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Float.class)); + + String str2 = "-1234.0"; + Float val2 = Float.valueOf(-1234f); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Float.class)); + + String str3 = " -1234.5 "; + Float val3 = Float.valueOf(-1234.5f); + assertEquals(val3, helper.parse(str3, Float.class)); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Float.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Float.class); + } + + + public void testConvertDoubleDefault() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + // default double conversion is compatible with XSD type def + assertEquals("xsd:decimal", helper.getXsdType(Double.class)); + + // note: for default-format tests, strings are generated from values + Double val1 = Double.valueOf(1234567890.5); + String str1 = val1.toString(); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Double.class)); + + Double val2 = Double.valueOf(-1234567890.1); + String str2 = val2.toString(); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Double.class)); + + String str3 = " -1234.5 "; + Double val3 = Double.valueOf(-1234.5); + assertEquals(val3, helper.parse(str3, Double.class)); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Double.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Double.class); + } + + + public void testConvertDoubleXsd() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(true); + + assertEquals("xsd:decimal", helper.getXsdType(Double.class)); + + // while for XSD-format tests, we want to verify the strings + String str1 = "1234567890.5"; + Double val1 = Double.valueOf(1234567890.5); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Double.class)); + + String str2 = "-1234567890.1"; + Double val2 = Double.valueOf(-1234567890.1); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, Double.class)); + + String str3 = " -1234.5 "; + Double val3 = Double.valueOf(-1234.5); + assertEquals(val3, helper.parse(str3, Double.class)); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", Double.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Double.class); + } + + + public void testConvertBigInteger() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + assertEquals("xsd:decimal", helper.getXsdType(BigInteger.class)); + + String str1 = "123456789012345678901234567890"; + BigInteger val1 = new BigInteger(str1); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, BigInteger.class)); + + String str2 = "-123456789012345678901234567890"; + BigInteger val2 = new BigInteger(str2); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, BigInteger.class)); + + assertFailsConversionToObject( + "converted non-integer value", + helper, "123.45", BigInteger.class); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", BigInteger.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", BigInteger.class); + } + + + public void testConvertBigDecimal() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + assertEquals("xsd:decimal", helper.getXsdType(BigDecimal.class)); + + String str1 = "12345678901234567890.123456789"; + BigDecimal val1 = new BigDecimal(str1); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, BigDecimal.class)); + + String str2 = "-12345678901234567890.123456789"; + BigDecimal val2 = new BigDecimal(str2); + assertEquals(str2, helper.stringify(val2)); + assertEquals(val2, helper.parse(str2, BigDecimal.class)); + + assertFailsConversionToObject( + "converted non-numeric value", + helper, "ix", BigDecimal.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", BigDecimal.class); + } + + + public void testConvertDateDefault() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); + + // default date conversion is NOT compatible with XSD type def, + // so this is misleading + assertEquals("xsd:dateTime", helper.getXsdType(Date.class)); + + // for default conversion, we create string from value + Date val1 = new Date(1247551703000L); + String str1 = val1.toString(); + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Date.class)); + + + assertFailsConversionToObject( + "converted non-date value", + helper, "ix", Date.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Date.class); + } + + + public void testConvertDateXsd() throws Exception + { + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(true); + + assertEquals("xsd:dateTime", helper.getXsdType(Date.class)); + + Date val1 = new Date(1247551703000L); + String str1 = "2009-07-14T06:08:23"; + assertEquals(str1, helper.stringify(val1)); + assertEquals(val1, helper.parse(str1, Date.class)); + + assertFailsConversionToObject( + "converted non-date value", + helper, "ix", Date.class); + + assertFailsConversionToObject( + "converted empty string", + helper, "", Date.class); + } +} Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java 2009-08-10 22:30:50 UTC (rev 97) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java 2009-08-12 20:35:22 UTC (rev 98) @@ -14,9 +14,13 @@ package net.sf.practicalxml.converter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.xml.XMLConstants; import org.w3c.dom.Element; - +import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.builder.XmlBuilder; @@ -40,16 +44,72 @@ */ private Element createElement(String data) { - return XmlBuilder.element("data", XmlBuilder.text(data)) - .toDOM().getDocumentElement(); + Element elem = XmlBuilder.element("data").toDOM().getDocumentElement(); + if (data != null) + DomUtil.appendText(elem, data); + return elem; } + /** + * Reflectively executes the named method, and asserts that it throws + * a conversion exception. This method exists to keep clutter out of + * the test methods. + */ + private void assertConversionFailure( + Xml2BeanHandler handler, String methodName, + Element elem, String failureText) + throws Exception + { + Method method = handler.getClass().getMethod(methodName, Element.class); + try + { + method.invoke(handler, elem); + fail(methodName + ": " + failureText); + } + catch (InvocationTargetException ee) + { + if (ee.getCause().getClass() != ConversionException.class) + fail(methodName + "threw unexpected exception: " + ee.getCause()); + // otherwise, we're successful + } + } + + //---------------------------------------------------------------------------- // Test Cases //---------------------------------------------------------------------------- + public void testConvertString() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + Element data1 = createElement("foo"); + assertEquals("foo", handler.convertString(data1)); + + Element data2 = createElement(""); + assertEquals("", handler.convertString(data2)); + } + + + public void testConvertCharacter() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element data1 = createElement("A"); + assertEquals(Character.valueOf('A'), handler.convertCharacter(data1)); + + Element data2 = createElement("\uFB01"); + assertEquals(Character.valueOf('\uFB01'), handler.convertCharacter(data2)); + + Element data3 = createElement(""); + assertEquals(Character.valueOf('\0'), handler.convertCharacter(data3)); + + Element data4 = createElement("ix"); + assertConversionFailure(handler, "convertCharacter", data4, "converted invalid value"); + } + + public void testConvertBoolean() throws Exception { Xml2BeanHandler handler = new Xml2BeanHandler(); @@ -66,7 +126,11 @@ Element data4 = createElement("0"); assertEquals(Boolean.FALSE, handler.convertBoolean(data4)); - // FIXME - test failure + Element data5 = createElement("ix"); + assertConversionFailure(handler, "convertBoolean", data5, "converted invalid value"); + + Element data6 = createElement(""); + assertConversionFailure(handler, "convertBoolean", data6, "converted empty value"); } @@ -80,73 +144,116 @@ Element data2 = createElement("-123"); assertEquals(Byte.valueOf((byte)-123), handler.convertByte(data2)); - // FIXME - test failure + Element data3 = createElement("ix"); + assertConversionFailure(handler, "convertByte", data3, "converted invalid value"); + + Element data4 = createElement("1234567"); + assertConversionFailure(handler, "convertByte", data4, "converted too-large value"); + + Element data5 = createElement(""); + assertConversionFailure(handler, "convertByte", data5, "converted empty value"); } - public void testConvertCharacter() throws Exception + public void testConvertShort() throws Exception { Xml2BeanHandler handler = new Xml2BeanHandler(); - Element data1 = createElement("A"); - assertEquals(Character.valueOf('A'), handler.convertCharacter(data1)); + Element data1 = createElement("12345"); + assertEquals(Short.valueOf((short)12345), handler.convertShort(data1)); - Element data2 = createElement("\uFB01"); - assertEquals(Character.valueOf('\uFB01'), handler.convertCharacter(data2)); + Element data2 = createElement("-12345"); + assertEquals(Short.valueOf((short)-12345), handler.convertShort(data2)); - Element data3 = createElement(""); - assertEquals(Character.valueOf('\0'), handler.convertCharacter(data3)); + Element data3 = createElement("ix"); + assertConversionFailure(handler, "convertShort", data3, "converted non-numeric value"); - // FIXME - test failure -- multi-character? - } + Element data4 = createElement("1234567"); + assertConversionFailure(handler, "convertShort", data4, "converted too-large value"); + Element data5 = createElement("123.45"); + assertConversionFailure(handler, "convertShort", data5, "converted non-integer value"); -// public void testConvertDate() throws Exception -// { -// } + Element data6 = createElement(""); + assertConversionFailure(handler, "convertShort", data6, "converted empty value"); + } - public void testConvertDouble() throws Exception + public void testConvertInteger() throws Exception { Xml2BeanHandler handler = new Xml2BeanHandler(); - Element data = createElement("1234567890.125"); - assertEquals(Double.valueOf(1234567890.125), handler.convertDouble(data)); + Element data1 = createElement("123456789"); + assertEquals(Integer.valueOf(123456789), handler.convertInteger(data1)); - // FIXME - test failure + Element data2 = createElement("-123456789"); + assertEquals(Integer.valueOf(-123456789), handler.convertInteger(data2)); + + Element data3 = createElement("ix"); + assertConversionFailure(handler, "convertInteger", data3, "converted non-numeric value"); + + Element data4 = createElement("123456789012345"); + assertConversionFailure(handler, "convertInteger", data4, "converted too-large value"); + + Element data5 = createElement("123.45"); + assertConversionFailure(handler, "convertInteger", data5, "converted non-integer value"); + + Element data6 = createElement(""); + assertConversionFailure(handler, "convertInteger", data6, "converted empty value"); } - public void testConvertFloat() throws Exception + public void testConvertLong() throws Exception { Xml2BeanHandler handler = new Xml2BeanHandler(); - Element data = createElement("1234.5"); - assertEquals(Float.valueOf((float)1234.5), handler.convertFloat(data)); + Element data1 = createElement("1234567890123456"); + assertEquals(Long.valueOf(1234567890123456L), handler.convertLong(data1)); - // FIXME - test failure + Element data2 = createElement("-1234567890123456"); + assertEquals(Long.valueOf(-1234567890123456L), handler.convertLong(data2)); + + Element data3 = createElement("ix"); + assertConversionFailure(handler, "convertLong", data3, "converted non-numeric value"); + + Element data4 = createElement("123456789012345123456789012345"); + assertConversionFailure(handler, "convertLong", data4, "converted too-large value"); + + Element data5 = createElement("123.45"); + assertConversionFailure(handler, "convertLong", data5, "converted non-integer value"); + + Element data6 = createElement(""); + assertConversionFailure(handler, "convertLong", data6, "converted empty value"); } - public void testConvertInteger() throws Exception + public void testConvertFloat() throws Exception { Xml2BeanHandler handler = new Xml2BeanHandler(); - Element data = createElement("123456789"); - assertEquals(Integer.valueOf(123456789), handler.convertInteger(data)); + Element data1 = createElement("1234.5"); + assertEquals(Float.valueOf((float)1234.5), handler.convertFloat(data1)); - // FIXME - test failure + Element data2 = createElement("ix"); + assertConversionFailure(handler, "convertFloat", data2, "converted non-numeric value"); + + Element data3 = createElement(""); + assertConversionFailure(handler, "convertFloat", data3, "converted empty value"); } - public void testConvertLong() throws Exception + public void testConvertDouble() throws Exception { Xml2BeanHandler handler = new Xml2BeanHandler(); - Element data = createElement("1234567890123456"); - assertEquals(Long.valueOf(1234567890123456L), handler.convertLong(data)); + Element data1 = createElement("1234567890.125"); + assertEquals(Double.valueOf(1234567890.125), handler.convertDouble(data1)); - // FIXME - test failure + Element data2 = createElement("ix"); + assertConversionFailure(handler, "convertDouble", data2, "converted non-numeric value"); + + Element data3 = createElement(""); + assertConversionFailure(handler, "convertDouble", data3, "converted empty value"); } @@ -155,25 +262,11 @@ // } - public void testConvertShort() throws Exception - { - Xml2BeanHandler handler = new Xml2BeanHandler(); +// public void testConvertDate() throws Exception +// { +// } - Element data = createElement("12345"); - assertEquals(Short.valueOf((short)12345), handler.convertShort(data)); - // FIXME - test failure - } - - - public void testConvertString() throws Exception - { - Xml2BeanHandler handler = new Xml2BeanHandler(); - - Element data = createElement("foo"); - assertEquals("foo", handler.convertString(data)); - } - // public void testConvertList() throws Exception // { // } @@ -197,4 +290,118 @@ // public void testConvertObjectArray() throws Exception // { // } + + + public void testConvertNull() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element nullData = createElement(null); + + assertNull(handler.convertString(nullData)); + assertNull(handler.convertCharacter(nullData)); + assertNull(handler.convertBoolean(nullData)); + assertNull(handler.convertByte(nullData)); + assertNull(handler.convertShort(nullData)); + assertNull(handler.convertInteger(nullData)); + assertNull(handler.convertLong(nullData)); + assertNull(handler.convertFloat(nullData)); + assertNull(handler.convertDouble(nullData)); +// assertNull(handler.convertNumber(nullData)); FIXME +// assertNull(handler.convertDate(nullData)); FIXME +// assertNull(handler.convertList(nullData)); FIXME +// assertNull(handler.convertMap(nullData)); FIXME +// assertNull(handler.convertSet(nullData)); FIXME +// assertNull(handler.convertObject(nullData)); FIXME +// assertNull(handler.convertObjectArray(nullData)); FIXME + } + + + public void testConvertEmptyAsNull() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(Xml2BeanOptions.CONVERT_BLANK_AS_NULL); + + Element empty = createElement(null); + + assertNull(handler.convertString(empty)); + assertNull(handler.convertCharacter(empty)); + assertNull(handler.convertBoolean(empty)); + assertNull(handler.convertByte(empty)); + assertNull(handler.convertShort(empty)); + assertNull(handler.convertInteger(empty)); + assertNull(handler.convertLong(empty)); + assertNull(handler.convertFloat(empty)); + assertNull(handler.convertDouble(empty)); +// assertNull(handler.convertNumber(empty)); FIXME +// assertNull(handler.convertDate(empty)); FIXME +// assertNull(handler.convertList(empty)); FIXME +// assertNull(handler.convertMap(empty)); FIXME +// assertNull(handler.convertSet(empty)); FIXME +// assertNull(handler.convertObject(empty)); FIXME +// assertNull(handler.convertObjectArray(empty)); FIXME + + Element blank = createElement(" \t\n "); + + assertNull(handler.convertString(blank)); + assertNull(handler.convertCharacter(blank)); + assertNull(handler.convertBoolean(blank)); + assertNull(handler.convertByte(blank)); + assertNull(handler.convertShort(blank)); + assertNull(handler.convertInteger(blank)); + assertNull(handler.convertLong(blank)); + assertNull(handler.convertFloat(blank)); + assertNull(handler.convertDouble(blank)); +// assertNull(handler.convertNumber(blank)); FIXME +// assertNull(handler.convertDate(blank)); FIXME +// assertNull(handler.convertList(blank)); FIXME +// assertNull(handler.convertMap(blank)); FIXME +// assertNull(handler.convertSet(blank)); FIXME +// assertNull(handler.convertObject(blank)); FIXME +// assertNull(handler.convertObjectArray(blank)); FIXME + } + + + public void testRequireXsiNil() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(Xml2BeanOptions.REQUIRE_XSI_NIL); + + Element badData = createElement(null); + + assertConversionFailure(handler, "convertString", badData, "converted empty without xsi:nil"); + assertConversionFailure(handler, "convertCharacter", badData, "converted empty without xsi:nil"); + assertConversionFailure(handler, "convertBoolean", badData, "converted empty without xsi:nil"); + assertConversionFailure(handler, "convertByte", badData, "converted empty without xsi:nil"); + assertConversionFailure(handler, "convertShort", badData, "converted empty without xsi:nil"); + assertConversionFailure(handler, "convertInteger", badData, "converted empty without xsi:nil"); + assertConversionFailure(handler, "convertLong", badData, "converted empty without xsi:nil"); + assertConversionFailure(handler, "convertFloat", badData, "converted empty without xsi:nil"); + assertConversionFailure(handler, "convertDouble", badData, "converted empty without xsi:nil"); +// assertConversionFailure(handler, "convertNumber", badData, "converted empty without xsi:nil"); FIXME +// assertConversionFailure(handler, "convertDate", badData, "converted empty without xsi:nil"); FIXME +// assertConversionFailure(handler, "convertList", badData, "converted empty without xsi:nil"); FIXME +// assertConversionFailure(handler, "convertMap", badData, "converted empty without xsi:nil"); FIXME +// assertConversionFailure(handler, "convertSet", badData, "converted empty without xsi:nil"); FIXME +// assertConversionFailure(handler, "convertObject", badData, "converted empty without xsi:nil"); FIXME +// assertConversionFailure(handler, "convertObjectArray", badData, "converted empty without xsi:nil"); FIXME + + Element goodData = createElement(null); + goodData.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil", "true"); + + assertNull(handler.convertString(goodData)); + assertNull(handler.convertCharacter(goodData)); + assertNull(handler.convertBoolean(goodData)); + assertNull(handler.convertByte(goodData)); + assertNull(handler.convertShort(goodData)); + assertNull(handler.convertInteger(goodData)); + assertNull(handler.convertLong(goodData)); + assertNull(handler.convertFloat(goodData)); + assertNull(handler.convertDouble(goodData)); +// assertNull(handler.convertNumber(goodData)); FIXME +// assertNull(handler.convertDate(goodData)); FIXME +// assertNull(handler.convertList(goodData)); FIXME +// assertNull(handler.convertMap(goodData)); FIXME +// assertNull(handler.convertSet(goodData)); FIXME +// assertNull(handler.convertObject(goodData)); FIXME +// assertNull(handler.convertObjectArray(goodData)); FIXME + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-08-10 22:31:11
|
Revision: 97 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=97&view=rev Author: kdgregory Date: 2009-08-10 22:30:50 +0000 (Mon, 10 Aug 2009) Log Message: ----------- introduce ConversionException Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlDriver.java Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionException.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlDriver.java 2009-07-23 23:28:30 UTC (rev 96) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlDriver.java 2009-08-10 22:30:50 UTC (rev 97) @@ -179,7 +179,7 @@ } catch (IntrospectionException ee) { - throw new RuntimeException("introspection failure", ee); + throw new ConversionException("introspection failure", ee); } } @@ -198,7 +198,7 @@ } catch (Exception ee) { - throw new RuntimeException("unable to retrieve bean value", ee); + throw new ConversionException("unable to retrieve bean value", ee); } dispatch(propDesc.getName(), value, handler); Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionException.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionException.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionException.java 2009-08-10 22:30:50 UTC (rev 97) @@ -0,0 +1,36 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + + +/** + * A runtime exception thrown for any conversion error. Will always have a + * message, and typically contains a wrapped exception. + */ +public class ConversionException +extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public ConversionException(String message, Throwable cause) + { + super(message, cause); + } + + public ConversionException(String message) + { + super(message); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-23 23:28:39
|
Revision: 96 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=96&view=rev Author: kdgregory Date: 2009-07-23 23:28:30 +0000 (Thu, 23 Jul 2009) Log Message: ----------- checkpoint - XML to Bean conversion Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanDriver.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanDriver.java 2009-07-23 23:28:30 UTC (rev 96) @@ -0,0 +1,35 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import org.w3c.dom.Element; + + +/** + * Driver class for converting an XML DOM into a Java bean, using a {@link + * Xml2BeanHandler} to manage data conversion. + */ +public class Xml2BeanDriver +{ + /** + * Fills a bean-style object from an XML element. For each child of the + * passed element, tries to find a property with the same name, invokes + * the appropriate conversion handler method, and sets the property from + * the result. + */ + public void dispatch(Element elem, Object bean) + { + } +} Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanHandler.java 2009-07-23 23:28:30 UTC (rev 96) @@ -0,0 +1,171 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.XmlUtil; + +import java.util.Arrays; +import java.util.Date; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.xml.XMLConstants; + +import org.w3c.dom.Element; + + +/** + * Invoked by {@link Xml2BeanDriver} to convert a DOM <code>Element</code> + * into the appropriate Java object. Unlike {@link Bean2XmlHandler}, there + * will only be one instance of this object created during conversion; all + * intermediate data can be held on the stack. + */ +public class Xml2BeanHandler +{ + private EnumSet<Xml2BeanOptions> _options; + + + /** + * Public constructor, allowing various options specifications. + */ + public Xml2BeanHandler(Xml2BeanOptions... options) + { + _options = EnumSet.noneOf(Xml2BeanOptions.class); + for (Xml2BeanOptions option : options) + _options.add(option); + } + + +//---------------------------------------------------------------------------- +// Public Methods +//---------------------------------------------------------------------------- + + + public Boolean convertBoolean(Element elem) + { + String text = DomUtil.getText(elem); + return Boolean.valueOf(XmlUtil.parseXsdBoolean(text)); + } + + + public Byte convertByte(Element elem) + { + String text = DomUtil.getText(elem); + return Byte.valueOf(text); + } + + + public Character convertCharacter(Element elem) + { + String text = DomUtil.getText(elem); + if (text.length() == 0) + return '\0'; + return Character.valueOf(text.charAt(0)); + } + + + public Date convertDate(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Double convertDouble(Element elem) + { + String text = DomUtil.getText(elem); + return Double.valueOf(text); + } + + + public Float convertFloat(Element elem) + { + String text = DomUtil.getText(elem); + return Float.valueOf(text); + } + + + public Integer convertInteger(Element elem) + { + String text = DomUtil.getText(elem); + return Integer.valueOf(text); + } + + + public Long convertLong(Element elem) + { + String text = DomUtil.getText(elem); + return Long.valueOf(text); + } + + + public Number convertNumber(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Short convertShort(Element elem) + { + String text = DomUtil.getText(elem); + return Short.valueOf(text); + } + + + public String convertString(Element elem) + { + String text = DomUtil.getText(elem); + return text; + } + + + public List<?> convertList(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Set<?> convertSet(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Map<?,?> convertMap(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Object convertObject(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + + public Object[] convertObjectArray(Element elem) + { + throw new UnsupportedOperationException("not implemented yet"); + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + +} Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Xml2BeanOptions.java 2009-07-23 23:28:30 UTC (rev 96) @@ -0,0 +1,55 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + + +/** + * Options used by {@link Xml2BeanHandler} to control the way that DOM trees + * are translated to Java beans. + */ +public enum Xml2BeanOptions +{ + /** + * If present, the converter ignores elements that don't correspond to + * settable properties of the bean. + */ + IGNORE_MISSING_PROPERTIES, + + + /** + * If present, the converter will ignore any objects that cannot be + * converted (many of the JDK-provided classes fall into this category, + * because they're not bean-structured). + */ + IGNORE_UNCONVERTIBLE_OBJECTS, + + + /** + * If present, the converter requires an <code>xsi:type</code> attribute + * on each element, and will throw if it's not present. Default behavior + * uses the <code>xsi:type</code> value to choose between different setter + * methods, but otherwise ignores it. + */ + REQUIRE_XSI_TYPE, + + + /** + * If present, the converter will use a setter method taking a String, in + * preference to any other type. Default behavior is to pick the method + * with the most restrictive type (and considering numeric types as more + * restrictive than <code>String</code>). + */ + PREFER_STRING_SETTER +} Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanHandler.java 2009-07-23 23:28:30 UTC (rev 96) @@ -0,0 +1,200 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import org.w3c.dom.Element; + + +import net.sf.practicalxml.builder.XmlBuilder; + + +public class TestXml2BeanHandler +extends AbstractBeanConverterTestCase +{ + public TestXml2BeanHandler(String name) + { + super(name); + } + + +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + /** + * Builds a DOM tree with a single element, containing the specified + * text as a child of the root. We don't care about the element's name; + * it's the text that's important. + */ + private Element createElement(String data) + { + return XmlBuilder.element("data", XmlBuilder.text(data)) + .toDOM().getDocumentElement(); + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + + public void testConvertBoolean() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element data1 = createElement("true"); + assertEquals(Boolean.TRUE, handler.convertBoolean(data1)); + + Element data2 = createElement("1"); + assertEquals(Boolean.TRUE, handler.convertBoolean(data2)); + + Element data3 = createElement("false"); + assertEquals(Boolean.FALSE, handler.convertBoolean(data3)); + + Element data4 = createElement("0"); + assertEquals(Boolean.FALSE, handler.convertBoolean(data4)); + + // FIXME - test failure + } + + + public void testConvertByte() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element data1 = createElement("123"); + assertEquals(Byte.valueOf((byte)123), handler.convertByte(data1)); + + Element data2 = createElement("-123"); + assertEquals(Byte.valueOf((byte)-123), handler.convertByte(data2)); + + // FIXME - test failure + } + + + public void testConvertCharacter() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element data1 = createElement("A"); + assertEquals(Character.valueOf('A'), handler.convertCharacter(data1)); + + Element data2 = createElement("\uFB01"); + assertEquals(Character.valueOf('\uFB01'), handler.convertCharacter(data2)); + + Element data3 = createElement(""); + assertEquals(Character.valueOf('\0'), handler.convertCharacter(data3)); + + // FIXME - test failure -- multi-character? + } + + +// public void testConvertDate() throws Exception +// { +// } + + + public void testConvertDouble() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element data = createElement("1234567890.125"); + assertEquals(Double.valueOf(1234567890.125), handler.convertDouble(data)); + + // FIXME - test failure + } + + + public void testConvertFloat() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element data = createElement("1234.5"); + assertEquals(Float.valueOf((float)1234.5), handler.convertFloat(data)); + + // FIXME - test failure + } + + + public void testConvertInteger() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element data = createElement("123456789"); + assertEquals(Integer.valueOf(123456789), handler.convertInteger(data)); + + // FIXME - test failure + } + + + public void testConvertLong() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element data = createElement("1234567890123456"); + assertEquals(Long.valueOf(1234567890123456L), handler.convertLong(data)); + + // FIXME - test failure + } + + +// public void testConvertNumber() throws Exception +// { +// } + + + public void testConvertShort() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element data = createElement("12345"); + assertEquals(Short.valueOf((short)12345), handler.convertShort(data)); + + // FIXME - test failure + } + + + public void testConvertString() throws Exception + { + Xml2BeanHandler handler = new Xml2BeanHandler(); + + Element data = createElement("foo"); + assertEquals("foo", handler.convertString(data)); + } + +// public void testConvertList() throws Exception +// { +// } +// +// +// public void testConvertSet() throws Exception +// { +// } +// +// +// public void testConvertMap() throws Exception +// { +// } +// +// +// public void testConvertObject() throws Exception +// { +// } +// +// +// public void testConvertObjectArray() throws Exception +// { +// } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-23 22:23:55
|
Revision: 95 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=95&view=rev Author: kdgregory Date: 2009-07-23 22:23:42 +0000 (Thu, 23 Jul 2009) Log Message: ----------- refactor common code from bean converter testcases into abstract super Modified Paths: -------------- branches/dev-1.1/src/test/java/net/sf/practicalxml/AbstractTestCase.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlHandler.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java Added Paths: ----------- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/AbstractBeanConverterTestCase.java Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/AbstractTestCase.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/AbstractTestCase.java 2009-07-23 22:06:24 UTC (rev 94) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/AbstractTestCase.java 2009-07-23 22:23:42 UTC (rev 95) @@ -14,6 +14,10 @@ package net.sf.practicalxml; +import java.util.List; + +import org.w3c.dom.Element; + import junit.framework.TestCase; /** @@ -41,6 +45,10 @@ } +//---------------------------------------------------------------------------- +// Assertions +//---------------------------------------------------------------------------- + /** * Asserts a multi-line string, after stripping out any '\r' characters. * Needed for a cross-platform build. @@ -55,4 +63,16 @@ } assertEquals(expected, buf.toString()); } + + + /** + * Asserts that an element has the expected number of element children + * (verifies that we didn't append to the wrong element). + */ + protected void assertChildCount(String message, Element parent, int expected) + { + List<Element> children = DomUtil.getChildren(parent); + assertEquals(message + " child count:", expected, children.size()); + } + } Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/AbstractBeanConverterTestCase.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/AbstractBeanConverterTestCase.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/AbstractBeanConverterTestCase.java 2009-07-23 22:23:42 UTC (rev 95) @@ -0,0 +1,108 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import java.math.BigDecimal; +import java.util.List; + +import net.sf.practicalxml.AbstractTestCase; + + +/** + * Provides common support code (primary test bean classes) for the converter + * testcases. Note that the bean classes are public, so that they can be + * instrospected. + */ +public abstract class AbstractBeanConverterTestCase +extends AbstractTestCase +{ + protected AbstractBeanConverterTestCase(String name) + { + super(name); + } + + +//---------------------------------------------------------------------------- +// Test Bean Classes +//---------------------------------------------------------------------------- + + public static class SimpleBean + { + private String _sval; + private int _ival; + private BigDecimal _dval; + private boolean _bval; + + public SimpleBean() + { + // nothign to see here + } + + public SimpleBean(String sval, int ival, BigDecimal dval, boolean bval) + { + _sval = sval; + _ival = ival; + _dval = dval; + _bval = bval; + } + + public String getSval() { return _sval; } + public void setSval(String sval) { _sval = sval; } + + public int getIval() { return _ival; } + public void setIval(int ival) { _ival = ival; } + + public BigDecimal getDval() { return _dval; } + public void setDval(BigDecimal dval) { _dval = dval; } + + public boolean isBval() { return _bval; } + public void setBval(boolean bval) { _bval = bval; } + } + + + public static class CompoundBean + { + private SimpleBean _simple; + private int[] _primArray; + private List<String> _stringList; + + public CompoundBean() + { + // nothing here + } + + public CompoundBean(SimpleBean simple, int[] primArray, List<String> stringList) + { + super(); + _simple = simple; + _primArray = primArray; + _stringList = stringList; + } + + public SimpleBean getSimple() { return _simple; } + public void setSimple(SimpleBean simple) { _simple = simple; } + + public int[] getPrimArray() { return _primArray; } + public void setPrimArray(int[] primArray) { _primArray = primArray; } + + public List<String> getStringList() { return _stringList; } + public void setStringList(List<String> list) { _stringList = list; } + } + + +//---------------------------------------------------------------------------- +// Common Assertions +//---------------------------------------------------------------------------- +} Property changes on: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/AbstractBeanConverterTestCase.java ___________________________________________________________________ Added: svn:executable + * Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlHandler.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlHandler.java 2009-07-23 22:06:24 UTC (rev 94) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlHandler.java 2009-07-23 22:23:42 UTC (rev 95) @@ -28,8 +28,6 @@ import org.w3c.dom.Element; -import junit.framework.TestCase; - import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.OutputUtil; import net.sf.practicalxml.junit.DomAsserts; @@ -37,8 +35,14 @@ public class TestBean2XmlHandler -extends TestCase +extends AbstractBeanConverterTestCase { + public TestBean2XmlHandler(String name) + { + super(name); + } + + //---------------------------------------------------------------------------- // Test Data / Classes //---------------------------------------------------------------------------- @@ -65,87 +69,7 @@ }; - // has to be public so that we can introspect it - public static class SimpleBean - { - private String _sval; - private int _ival; - private BigDecimal _dval; - private boolean _bval; - - public SimpleBean() - { - // nothign to see here - } - - public SimpleBean(String sval, int ival, BigDecimal dval, boolean bval) - { - _sval = sval; - _ival = ival; - _dval = dval; - _bval = bval; - } - - public String getSval() { return _sval; } - public void setSval(String sval) { _sval = sval; } - - public int getIval() { return _ival; } - public void setIval(int ival) { _ival = ival; } - - public BigDecimal getDval() { return _dval; } - public void setDval(BigDecimal dval) { _dval = dval; } - - public boolean isBval() { return _bval; } - public void setBval(boolean bval) { _bval = bval; } - } - - - public static class CompoundBean - { - private SimpleBean _simple; - private int[] _primArray; - private List<String> _stringList; - - public CompoundBean() - { - // nothing here - } - - public CompoundBean(SimpleBean simple, int[] primArray, List<String> stringList) - { - super(); - _simple = simple; - _primArray = primArray; - _stringList = stringList; - } - - public SimpleBean getSimple() { return _simple; } - public void setSimple(SimpleBean simple) { _simple = simple; } - - public int[] getPrimArray() { return _primArray; } - public void setPrimArray(int[] primArray) { _primArray = primArray; } - - public List<String> getStringList() { return _stringList; } - public void setStringList(List<String> list) { _stringList = list; } - } - - -//---------------------------------------------------------------------------- -// Support Code -//---------------------------------------------------------------------------- - /** - * Asserts that an element has the expected number of children (verifies - * that we didn't append to the wrong element). - */ - private void assertChildCount(String message, Element parent, int expected) - { - List<Element> children = DomUtil.getChildren(parent); - assertEquals(message + " child count:", expected, children.size()); - } - - - /** * Asserts the name, type, and value of an element. If any of the expected * values are null, the associated test isn't performed. */ Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java 2009-07-23 22:06:24 UTC (rev 94) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java 2009-07-23 22:23:42 UTC (rev 95) @@ -21,7 +21,6 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; -import junit.framework.TestCase; /** * Tests for the top-level <code>BeanConverter</code> methods. These tend to @@ -29,12 +28,19 @@ * and {@link TestBeanInputHandler}. */ public class TestBeanConverter -extends TestCase +extends AbstractBeanConverterTestCase { + public TestBeanConverter(String name) + { + super(name); + } + + //---------------------------------------------------------------------------- // Support Code //---------------------------------------------------------------------------- + //---------------------------------------------------------------------------- // Test Cases //---------------------------------------------------------------------------- This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-23 22:06:37
|
Revision: 94 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=94&view=rev Author: kdgregory Date: 2009-07-23 22:06:24 +0000 (Thu, 23 Jul 2009) Log Message: ----------- add XmlUtil.formatXsdBoolean(), XmlUtil.parseXsdBoolean() Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/XmlUtil.java branches/dev-1.1/src/test/java/net/sf/practicalxml/TestXmlUtil.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/XmlUtil.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/XmlUtil.java 2009-07-23 21:36:40 UTC (rev 93) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/XmlUtil.java 2009-07-23 22:06:24 UTC (rev 94) @@ -100,6 +100,32 @@ /** + * Parses an XML Schema <code>dateTime</code> value, accepting any of + * the legal formats. Note that this method can also be used to parse + * a generic ISO-8601 date. + * + * @throws XmlException if unable to parse. + */ + public static Date parseXsdDatetime(String value) + { + Calendar cal = GregorianCalendar.getInstance(); + cal.setTimeInMillis(0); + int idx = parserHelper(value, cal, Calendar.YEAR, 0); + idx = parserHelper(value, cal, Calendar.MONTH, idx+1); + idx = parserHelper(value, cal, Calendar.DAY_OF_MONTH, idx+1); + idx = parserHelper(value, cal, Calendar.HOUR_OF_DAY, idx+1); + idx = parserHelper(value, cal, Calendar.MINUTE, idx+1); + idx = parserHelper(value, cal, Calendar.SECOND, idx+1); + if (idx < value.length() && (value.charAt(idx) == '.')) + { + idx = parserHelper(value, cal, Calendar.MILLISECOND, idx+1); + } + parseTimezone(value, cal, idx); + return cal.getTime(); + } + + + /** * Converts a Java <code>double</code> to a string, using the format * specified by XML Schema for <code>decimal</code> elements. This * method wraps the value and calls {@link #formatXsdDecimal(Number)}, @@ -125,28 +151,39 @@ /** - * Parses an XML Schema <code>dateTime</code> object, accepting any of - * the legal formats. Note that this method can also be used to parse - * a generic ISO-8601 date. + * Converts a <code>boolean</code> value to the literal strings "true" or + * "false" (XML Schema <code>boolean</code> fields also allow "1" or "0"). + */ + public static String formatXsdBoolean(boolean value) + { + return value ? "true" : "false"; + } + + + /** + * Parses an XML Schema <code>boolean</code> value, accepting any of + * the legal formats and trimming whitespace. * - * @throws XmlException if unable to parse. + * @throws XmlException the passed value, after trimming, is not one + * of the 4 legal representations of boolean data under XML + * Schema. */ - public static Date parseXsdDatetime(String value) + public static boolean parseXsdBoolean(String value) { - Calendar cal = GregorianCalendar.getInstance(); - cal.setTimeInMillis(0); - int idx = parserHelper(value, cal, Calendar.YEAR, 0); - idx = parserHelper(value, cal, Calendar.MONTH, idx+1); - idx = parserHelper(value, cal, Calendar.DAY_OF_MONTH, idx+1); - idx = parserHelper(value, cal, Calendar.HOUR_OF_DAY, idx+1); - idx = parserHelper(value, cal, Calendar.MINUTE, idx+1); - idx = parserHelper(value, cal, Calendar.SECOND, idx+1); - if (idx < value.length() && (value.charAt(idx) == '.')) + try { - idx = parserHelper(value, cal, Calendar.MILLISECOND, idx+1); + value = value.trim(); + if (value.equals("true") || value.equals("1")) + return true; + else if (value.equals("false") || value.equals("0")) + return false; + else + throw new XmlException("not an XSD boolean value: " + value); } - parseTimezone(value, cal, idx); - return cal.getTime(); + catch (NullPointerException e) + { + throw new XmlException("null values not allowed"); + } } Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/TestXmlUtil.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/TestXmlUtil.java 2009-07-23 21:36:40 UTC (rev 93) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/TestXmlUtil.java 2009-07-23 22:06:24 UTC (rev 94) @@ -88,6 +88,52 @@ } + public void testFormatXsdBoolean() throws Exception + { + assertEquals("true", XmlUtil.formatXsdBoolean(true)); + assertEquals("false", XmlUtil.formatXsdBoolean(false)); + } + + + public void testParseXsdBoolean() throws Exception + { + assertEquals(true, XmlUtil.parseXsdBoolean("true")); + assertEquals(true, XmlUtil.parseXsdBoolean("1")); + assertEquals(false, XmlUtil.parseXsdBoolean("false")); + assertEquals(false, XmlUtil.parseXsdBoolean("0")); + + try + { + XmlUtil.parseXsdBoolean("TRUE"); + fail("uppercase \"TRUE\" not legal per XML schema"); + } + catch (XmlException e) + { + // success + } + + try + { + XmlUtil.parseXsdBoolean(""); + fail("empty string not legal per XML schema"); + } + catch (XmlException e) + { + // success + } + + try + { + XmlUtil.parseXsdBoolean(null); + fail("null not legal"); + } + catch (XmlException e) + { + // success + } + } + + public void testEscape() throws Exception { assertEquals("", XmlUtil.escape(null)); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-23 21:36:58
|
Revision: 93 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=93&view=rev Author: kdgregory Date: 2009-07-23 21:36:40 +0000 (Thu, 23 Jul 2009) Log Message: ----------- fix copyright/license comment Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlOptions.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlOptions.java 2009-07-23 20:17:49 UTC (rev 92) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/Bean2XmlOptions.java 2009-07-23 21:36:40 UTC (rev 93) @@ -1,6 +1,20 @@ -// Copyright (c) 2009 Keith D Gregory +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + /** * Options used by {@link Bean2XmlHandler} to control the structure of the * DOM tree. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-16 15:15:32
|
Revision: 90 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=90&view=rev Author: kdgregory Date: 2009-07-16 15:15:28 +0000 (Thu, 16 Jul 2009) Log Message: ----------- re-introduce OutputHandler interface Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputOptions.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandlerImpl.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandlerImpl.java Removed Paths: ------------- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandler.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java 2009-07-15 19:31:56 UTC (rev 89) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java 2009-07-16 15:15:28 UTC (rev 90) @@ -62,7 +62,7 @@ BeanOutputOptions... options) { Element root = DomUtil.newDocument(nsUri, "temp"); - BeanOutputDriver.dispatch(rootName, bean, new BeanOutputHandler(root, options)); + BeanOutputDriver.dispatch(rootName, bean, new BeanOutputHandlerImpl(root, options)); return replaceRoot(root); } @@ -81,7 +81,7 @@ BeanOutputOptions... options) { Element root = DomUtil.newDocument("temp"); - BeanOutputDriver.dispatch(rootName, bean, new BeanOutputHandler(root, options)); + BeanOutputDriver.dispatch(rootName, bean, new BeanOutputHandlerImpl(root, options)); return replaceRoot(root); } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java 2009-07-15 19:31:56 UTC (rev 89) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java 2009-07-16 15:15:28 UTC (rev 90) @@ -14,335 +14,35 @@ package net.sf.practicalxml.converter; -import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.XmlUtil; - -import java.util.Arrays; import java.util.Date; -import java.util.EnumSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import javax.xml.XMLConstants; -import org.w3c.dom.Element; - /** - * {@link OutputHandler} that builds a DOM tree. The structure of this - * tree is customized by passing values from {@link BeanOutputOptions}. - * <p> - * Instances of this class are constructed around an <code>Element</code>, - * which represents the object to be introspected by {@link BeanOutputDriver}; - * this simplifies recursive calls. - * <p> - * This class is not threadsafe (but then, neither are the JDK's DOM classes). + * {@link BeanOutputDriver} interacts with instances of this interface. + * There's only one such instance within the Practical XML library: + * {@link BeanOutputHandlerImpl}, but the existence of the interface + * lets you reuse the driver for other purposes. */ -public class BeanOutputHandler +public interface BeanOutputHandler { - private EnumSet<BeanOutputOptions> _options; - private Appender _appender; - - - /** - * Public constructor, which uses default appender and allows individual - * options specifiers. - */ - public BeanOutputHandler(Element appendTo, BeanOutputOptions... options) - { - _appender = new Appender(appendTo); - _options = EnumSet.noneOf(BeanOutputOptions.class); - for (BeanOutputOptions option : options) - _options.add(option); - } - - - /** - * Internal constructor, used by container classes for recursive calls. - */ - protected BeanOutputHandler(BeanOutputHandler parent, Appender appender) - { - _appender = appender; - _options = parent._options; - } - - -//---------------------------------------------------------------------------- -// OutputHandler implementation -//---------------------------------------------------------------------------- - - public void convert(String name, Boolean value) - { - Object formatted = value; - if (shouldFormatAsXsd()) - formatted = value.booleanValue() ? "true" : "false"; - - _appender.append(name, "xsd:boolean", formatted); - } - - - public void convert(String name, Byte value) - { - _appender.append(name, "xsd:byte", value); - } - - - public void convert(String name, Character value) - { - _appender.append(name, "xsd:string", value); - } - - - public void convert(String name, Date value) - { - Object formatted = value; - if (shouldFormatAsXsd()) - formatted = XmlUtil.formatXsdDatetime(value); - - _appender.append(name, "xsd:dateTime", formatted); - } - - - public void convert(String name, Double value) - { - Object formatted = value; - if (shouldFormatAsXsd()) - formatted = XmlUtil.formatXsdDecimal(value); - - _appender.append(name, "xsd:decimal", formatted); - } - - - public void convert(String name, Float value) - { - Object formatted = value; - if (shouldFormatAsXsd()) - formatted = XmlUtil.formatXsdDecimal(value); - - _appender.append(name, "xsd:decimal", formatted); - } - - - public void convert(String name, Integer value) - { - _appender.append(name, "xsd:int", value); - } - - - public void convert(String name, Long value) - { - _appender.append(name, "xsd:long", value); - } - - - public void convert(String name, Number value) - { - _appender.append(name, "xsd:decimal", value); - } - - - public void convert(String name, Short value) - { - _appender.append(name, "xsd:short", value); - } - - - public void convert(String name, String value) - { - _appender.append(name, "xsd:string", value); - } - - - public void convert(String name, List<?> value) - { - Element container = _appender.append(name, javaXsiType(value), null); - appendSequence(container, value.iterator(), true); - } - - - public void convert(String name, Set<?> value) - { - Element container = _appender.append(name, javaXsiType(value), null); - appendSequence(container, value.iterator(), false); - } - - - public void convert(String name, Map<?,?> map) - { - Element container = _appender.append(name, javaXsiType(map), null); - BeanOutputHandler childHandler = new BeanOutputHandler(this, new MapAppender(container)); - BeanOutputDriver.introspect(map, childHandler); - } - - - public void convert(String name, Object value) - { - Element container = _appender.append(name, javaXsiType(value), null); - Appender childAppender = value.getClass().isArray() - ? new IndexedAppender(container) - : new Appender(container); - BeanOutputHandler childHandler = new BeanOutputHandler(this, childAppender); - BeanOutputDriver.introspect(value, childHandler); - } - - - public void convert(String name, Object[] array) - { - Element container = _appender.append(name, javaXsiType(array), null); - appendSequence(container, Arrays.asList(array).iterator(), true); - } - - - public void convertNull(String name) - { - if (!_options.contains(BeanOutputOptions.XSI_NIL)) - return; - - Element child = _appender.append(name, "", null); - child.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); - } - - -//---------------------------------------------------------------------------- -// Internals -//---------------------------------------------------------------------------- - - protected boolean shouldFormatAsXsd() - { - return _options.contains(BeanOutputOptions.XSD_FORMAT) - || _options.contains(BeanOutputOptions.XSI_TYPE); - } - - - /** - * Returns the <code>xsi:type</code> value for a Java object (should not - * be called for primitive wrappers, as these are covered by "xsd" types). - */ - private String javaXsiType(Object obj) - { - return "java:" + obj.getClass().getName(); - } - - - /** - * Common code for sequenced objects -- arrays, lists, and sets. - */ - private void appendSequence(Element parent, Iterator<?> itx, boolean isIndexed) - { - Appender childAppender = isIndexed ? new IndexedAppender(parent) - : new Appender(parent); - BeanOutputHandler childHandler = new BeanOutputHandler(this, childAppender); - while (itx.hasNext()) - BeanOutputDriver.dispatch("data", itx.next(), childHandler); - } - - - /** - * This class is responsible for adding nodes to the DOM tree. Each - * instance of <code>XmlOutputHandler</code> is constructed around one - * of these (or a subclass). Subclasses may override the {@link #append} - * method to add additional information to the appended nodes. - * <p> - * A little bit of weirdness: as an inner class, this is constructed in - * the context of an <code>XmlOutputHandler</code>, and has access to - * members defined by that handler. However, in a recursive call, it is - * actually held and used by a different handler. Since the second handler - * copies members of the first, I don't see this as a problem ... until - * some member doesn't get copied. - */ - private class Appender - { - private Element _appendTo; - - public Appender(Element appendTo) - { - _appendTo = appendTo; - } - - /** - * Appends a child to the element we manage. - * - * @param name Local-name for the new element. As long as you're - * dealing with Java, property names and element names - * are interchangeable. If handling a <code>Map</code>, - * you'll need to ensure that keys are appropriate XML - * element names. - * @param xsiType The value to insert in an <code>xsi:type</code> - * attribute. This is required, even if the output-type - * option isn't turned on. - * @param value If not-null, will be stringified and attached to the - * element as a text node. Apply any fancy formatting - * before coming here. If null, will be ignored. - */ - public Element append(String name, String xsiType, Object value) - { - if ((name == null) || (name.length() == 0)) - name = "data"; - - Element child = DomUtil.appendChildInheritNamespace(_appendTo, name); - - if (_options.contains(BeanOutputOptions.XSI_TYPE)) - child.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:type", xsiType); - - if (value != null) - child.setTextContent(String.valueOf(value)); - - return child; - } - } - - - /** - * An appender for array/list processing, that attaches an index attribute - * to the appended node. - */ - private class IndexedAppender - extends Appender - { - int _index = 0; - - public IndexedAppender(Element appendTo) - { - super(appendTo); - } - - @Override - public Element append(String name, String xsiType, Object value) - { - Element child = super.append(name, xsiType, value); - child.setAttribute("index", String.valueOf(_index++)); - return child; - } - } - - - /** - * An appender for maps, which either uses the passed name as the - * element name, or as the value of a "key" attribute. - */ - private class MapAppender - extends Appender - { - public MapAppender(Element appendTo) - { - super(appendTo); - } - - @Override - public Element append(String name, String xsiType, Object value) - { - Element child = null; - if (_options.contains(BeanOutputOptions.INTROSPECT_MAPS)) - { - child = super.append(name, xsiType, value); - } - else - { - child = super.append("data", xsiType, value); - child.setAttribute("key", name); - } - return child; - } - } + public void convert(String name, Boolean value); + public void convert(String name, Byte value); + public void convert(String name, Character value); + public void convert(String name, Date value); + public void convert(String name, Double value); + public void convert(String name, Float value); + public void convert(String name, Integer value); + public void convert(String name, Long value); + public void convert(String name, Number value); + public void convert(String name, Short value); + public void convert(String name, String value); + public void convert(String name, List<?> value); + public void convert(String name, Set<?> value); + public void convert(String name, Map<?,?> map); + public void convert(String name, Object value); + public void convert(String name, Object[] array); + public void convertNull(String name); } Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandlerImpl.java (from rev 89, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandlerImpl.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandlerImpl.java 2009-07-16 15:15:28 UTC (rev 90) @@ -0,0 +1,351 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.XmlUtil; + +import java.util.Arrays; +import java.util.Date; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.xml.XMLConstants; + +import org.w3c.dom.Element; + + +/** + * Invoked by {@link BeanOutputDriver} to build a DOM tree from introspected + * Java objects. + * <p> + * Instances of this class are constructed around an <code>Element</code>, + * which represents the object to be introspected by {@link BeanOutputDriver}. + * This simplifies recursive calls, at the expense of not being able to attach + * type information at the top level. See {@link BeanConverter} for the rather + * ugly hack to work around this. + * <p> + * This class is not threadsafe (but then, neither are the JDK's DOM classes). + */ +public class BeanOutputHandlerImpl +implements BeanOutputHandler +{ + private EnumSet<BeanOutputOptions> _options; + private Appender _appender; + + + /** + * Public constructor, which uses default appender and allows individual + * options specifiers. + */ + public BeanOutputHandlerImpl(Element appendTo, BeanOutputOptions... options) + { + _appender = new Appender(appendTo); + _options = EnumSet.noneOf(BeanOutputOptions.class); + for (BeanOutputOptions option : options) + _options.add(option); + } + + + /** + * Internal constructor, used by container classes for recursive calls. + */ + protected BeanOutputHandlerImpl(BeanOutputHandlerImpl parent, Appender appender) + { + _appender = appender; + _options = parent._options; + } + + +//---------------------------------------------------------------------------- +// OutputHandler implementation +//---------------------------------------------------------------------------- + + public void convert(String name, Boolean value) + { + Object formatted = value; + if (shouldFormatAsXsd()) + formatted = value.booleanValue() ? "true" : "false"; + + _appender.append(name, "xsd:boolean", formatted); + } + + + public void convert(String name, Byte value) + { + _appender.append(name, "xsd:byte", value); + } + + + public void convert(String name, Character value) + { + _appender.append(name, "xsd:string", value); + } + + + public void convert(String name, Date value) + { + Object formatted = value; + if (shouldFormatAsXsd()) + formatted = XmlUtil.formatXsdDatetime(value); + + _appender.append(name, "xsd:dateTime", formatted); + } + + + public void convert(String name, Double value) + { + Object formatted = value; + if (shouldFormatAsXsd()) + formatted = XmlUtil.formatXsdDecimal(value); + + _appender.append(name, "xsd:decimal", formatted); + } + + + public void convert(String name, Float value) + { + Object formatted = value; + if (shouldFormatAsXsd()) + formatted = XmlUtil.formatXsdDecimal(value); + + _appender.append(name, "xsd:decimal", formatted); + } + + + public void convert(String name, Integer value) + { + _appender.append(name, "xsd:int", value); + } + + + public void convert(String name, Long value) + { + _appender.append(name, "xsd:long", value); + } + + + public void convert(String name, Number value) + { + _appender.append(name, "xsd:decimal", value); + } + + + public void convert(String name, Short value) + { + _appender.append(name, "xsd:short", value); + } + + + public void convert(String name, String value) + { + _appender.append(name, "xsd:string", value); + } + + + public void convert(String name, List<?> value) + { + Element container = _appender.append(name, javaXsiType(value), null); + appendSequence(container, value.iterator(), true); + } + + + public void convert(String name, Set<?> value) + { + Element container = _appender.append(name, javaXsiType(value), null); + appendSequence(container, value.iterator(), false); + } + + + public void convert(String name, Map<?,?> map) + { + Element container = _appender.append(name, javaXsiType(map), null); + BeanOutputHandlerImpl childHandler = new BeanOutputHandlerImpl(this, new MapAppender(container)); + BeanOutputDriver.introspect(map, childHandler); + } + + + public void convert(String name, Object value) + { + Element container = _appender.append(name, javaXsiType(value), null); + Appender childAppender = value.getClass().isArray() + ? new IndexedAppender(container) + : new Appender(container); + BeanOutputHandlerImpl childHandler = new BeanOutputHandlerImpl(this, childAppender); + BeanOutputDriver.introspect(value, childHandler); + } + + + public void convert(String name, Object[] array) + { + Element container = _appender.append(name, javaXsiType(array), null); + appendSequence(container, Arrays.asList(array).iterator(), true); + } + + + public void convertNull(String name) + { + if (!_options.contains(BeanOutputOptions.XSI_NIL)) + return; + + Element child = _appender.append(name, "", null); + child.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + protected boolean shouldFormatAsXsd() + { + return _options.contains(BeanOutputOptions.XSD_FORMAT) + || _options.contains(BeanOutputOptions.XSI_TYPE); + } + + + /** + * Returns the <code>xsi:type</code> value for a Java object (should not + * be called for primitive wrappers, as these are covered by "xsd" types). + */ + private String javaXsiType(Object obj) + { + return "java:" + obj.getClass().getName(); + } + + + /** + * Common code for sequenced objects -- arrays, lists, and sets. + */ + private void appendSequence(Element parent, Iterator<?> itx, boolean isIndexed) + { + Appender childAppender = isIndexed ? new IndexedAppender(parent) + : new Appender(parent); + BeanOutputHandlerImpl childHandler = new BeanOutputHandlerImpl(this, childAppender); + while (itx.hasNext()) + BeanOutputDriver.dispatch("data", itx.next(), childHandler); + } + + + /** + * This class is responsible for adding nodes to the DOM tree. Each + * instance of <code>XmlOutputHandler</code> is constructed around one + * of these (or a subclass). Subclasses may override the {@link #append} + * method to add additional information to the appended nodes. + * <p> + * A little bit of weirdness: as an inner class, this is constructed in + * the context of an <code>XmlOutputHandler</code>, and has access to + * members defined by that handler. However, in a recursive call, it is + * actually held and used by a different handler. Since the second handler + * copies members of the first, I don't see this as a problem ... until + * some member doesn't get copied. + */ + private class Appender + { + private Element _appendTo; + + public Appender(Element appendTo) + { + _appendTo = appendTo; + } + + /** + * Appends a child to the element we manage. + * + * @param name Local-name for the new element. As long as you're + * dealing with Java, property names and element names + * are interchangeable. If handling a <code>Map</code>, + * you'll need to ensure that keys are appropriate XML + * element names. + * @param xsiType The value to insert in an <code>xsi:type</code> + * attribute. This is required, even if the output-type + * option isn't turned on. + * @param value If not-null, will be stringified and attached to the + * element as a text node. Apply any fancy formatting + * before coming here. If null, will be ignored. + */ + public Element append(String name, String xsiType, Object value) + { + if ((name == null) || (name.length() == 0)) + name = "data"; + + Element child = DomUtil.appendChildInheritNamespace(_appendTo, name); + + if (_options.contains(BeanOutputOptions.XSI_TYPE)) + child.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:type", xsiType); + + if (value != null) + child.setTextContent(String.valueOf(value)); + + return child; + } + } + + + /** + * An appender for array/list processing, that attaches an index attribute + * to the appended node. + */ + private class IndexedAppender + extends Appender + { + int _index = 0; + + public IndexedAppender(Element appendTo) + { + super(appendTo); + } + + @Override + public Element append(String name, String xsiType, Object value) + { + Element child = super.append(name, xsiType, value); + child.setAttribute("index", String.valueOf(_index++)); + return child; + } + } + + + /** + * An appender for maps, which either uses the passed name as the + * element name, or as the value of a "key" attribute. + */ + private class MapAppender + extends Appender + { + public MapAppender(Element appendTo) + { + super(appendTo); + } + + @Override + public Element append(String name, String xsiType, Object value) + { + Element child = null; + if (_options.contains(BeanOutputOptions.INTROSPECT_MAPS)) + { + child = super.append(name, xsiType, value); + } + else + { + child = super.append("data", xsiType, value); + child.setAttribute("key", name); + } + return child; + } + } +} Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputOptions.java 2009-07-15 19:31:56 UTC (rev 89) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputOptions.java 2009-07-16 15:15:28 UTC (rev 90) @@ -2,7 +2,7 @@ package net.sf.practicalxml.converter; /** - * Options used by {@link BeanOutputHandler} to control the structure of the + * Options used by {@link BeanOutputHandlerImpl} to control the structure of the * DOM tree. */ public enum BeanOutputOptions Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java 2009-07-15 19:31:56 UTC (rev 89) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java 2009-07-16 15:15:28 UTC (rev 90) @@ -25,7 +25,7 @@ /** * Tests for the top-level <code>BeanConverter</code> methods. These tend to - * be minimal; the detailed testing happens in {@link TestBeanOutputHandler} + * be minimal; the detailed testing happens in {@link TestBeanOutputHandlerImpl} * and {@link TestBeanInputHandler}. */ public class TestBeanConverter Deleted: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandler.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandler.java 2009-07-15 19:31:56 UTC (rev 89) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandler.java 2009-07-16 15:15:28 UTC (rev 90) @@ -1,574 +0,0 @@ -// Copyright 2008-2009 severally by the contributors -// -// 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 net.sf.practicalxml.converter; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import javax.xml.XMLConstants; - -import org.w3c.dom.Element; - -import junit.framework.TestCase; - -import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.OutputUtil; -import net.sf.practicalxml.junit.DomAsserts; -import net.sf.practicalxml.xpath.XPathWrapper; - - -public class TestBeanOutputHandler -extends TestCase -{ -//---------------------------------------------------------------------------- -// Test Data / Classes -//---------------------------------------------------------------------------- - - /** - * An array of simple value types, along with their XSD type name and - * XSD-formatted value. - */ - private final static Object[][] SIMPLE_VALUES = new Object[][] - { - new Object[] { Byte.valueOf((byte)123), "xsd:byte", "123" }, - new Object[] { Short.valueOf((short)4567), "xsd:short", "4567" }, - new Object[] { Integer.valueOf(12345678), "xsd:int", "12345678" }, - new Object[] { Long.valueOf(12345678901234L), "xsd:long", "12345678901234" }, - new Object[] { Float.valueOf((float)1234), "xsd:decimal", "1234.0" }, - new Object[] { Double.valueOf(1234567890.5), "xsd:decimal", "1234567890.5" }, - new Object[] { Boolean.TRUE, "xsd:boolean", "true" }, - new Object[] { Boolean.FALSE, "xsd:boolean", "false" }, - new Object[] { Character.valueOf('A'), "xsd:string", "A" }, - new Object[] { "this is a test", "xsd:string", "this is a test" }, - new Object[] { new Date(1247551703704L), "xsd:dateTime", "2009-07-14T06:08:23" }, - new Object[] { new BigInteger("123456789012345"), "xsd:decimal", "123456789012345" }, - new Object[] { new BigDecimal("123456789012345.123456789012345"), "xsd:decimal", "123456789012345.123456789012345" } - }; - - - // has to be public so that we can introspect it - public static class SimpleBean - { - private String _sval; - private int _ival; - private BigDecimal _dval; - private boolean _bval; - - public SimpleBean() - { - // nothign to see here - } - - public SimpleBean(String sval, int ival, BigDecimal dval, boolean bval) - { - _sval = sval; - _ival = ival; - _dval = dval; - _bval = bval; - } - - public String getSval() { return _sval; } - public void setSval(String sval) { _sval = sval; } - - public int getIval() { return _ival; } - public void setIval(int ival) { _ival = ival; } - - public BigDecimal getDval() { return _dval; } - public void setDval(BigDecimal dval) { _dval = dval; } - - public boolean isBval() { return _bval; } - public void setBval(boolean bval) { _bval = bval; } - } - - - public static class CompoundBean - { - private SimpleBean _simple; - private int[] _primArray; - private List<String> _stringList; - - public CompoundBean() - { - // nothing here - } - - public CompoundBean(SimpleBean simple, int[] primArray, List<String> stringList) - { - super(); - _simple = simple; - _primArray = primArray; - _stringList = stringList; - } - - public SimpleBean getSimple() { return _simple; } - public void setSimple(SimpleBean simple) { _simple = simple; } - - public int[] getPrimArray() { return _primArray; } - public void setPrimArray(int[] primArray) { _primArray = primArray; } - - public List<String> getStringList() { return _stringList; } - public void setStringList(List<String> list) { _stringList = list; } - } - - -//---------------------------------------------------------------------------- -// Support Code -//---------------------------------------------------------------------------- - - /** - * Asserts that an element has the expected number of children (verifies - * that we didn't append to the wrong element). - */ - private void assertChildCount(String message, Element parent, int expected) - { - List<Element> children = DomUtil.getChildren(parent); - assertEquals(message + " child count:", expected, children.size()); - } - - - /** - * Asserts the name, type, and value of an element. If any of the expected - * values are null, the associated test isn't performed. - */ - private void assertNameTypeValue( - String message, Element elem, - String expectedName, String expectedType, String expectedValue) - { - if (expectedName != null) - assertEquals(message + " name:", expectedName, - elem.getNodeName()); - if (expectedType != null) - assertEquals(message + " type:", expectedType, - elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type")); - if (expectedValue != null) - assertEquals(message + " value:", expectedValue, - DomUtil.getText(elem)); - } - - - /** - * Asserts the name and type of a compound element, where the type is a Java classname. - */ - private void assertContainerNameAndType(Element elem, String expectedName, Class<?> expectedType) - { - assertNameTypeValue("container", elem, expectedName, "java:" + expectedType.getName(), null); - } - - - /** - * A variety of assertions about added children. Assumes that we'll add - * a single child node per parent, and that we aren't using namespaces. - */ - private void assertAppend(String message, Element parent, - String expectedName, - String expectedType, - String expectedValue) - { - assertChildCount(message, parent, 1); - assertNameTypeValue(message, DomUtil.getChildren(parent).get(0), - expectedName, expectedType, expectedValue); - } - - -//---------------------------------------------------------------------------- -// Test Cases -//---------------------------------------------------------------------------- - - // this test is essentially repeated in the DispatchSimpleValues tests - // it exists here as an initial verification of appended element structure - public void testDispatch() throws Exception - { - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", "bar", new BeanOutputHandler(root)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - assertAppend("", root, "foo", "", "bar"); - } - - - public void testDispachWithNullName() throws Exception - { - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch(null, "bar", new BeanOutputHandler(root)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - assertAppend("", root, "data", "", "bar"); - } - - - public void testNamespaceInheritance() throws Exception - { - Element root = DomUtil.newDocument("urn:zippy", "argle:bargle"); - BeanOutputDriver.dispatch("foo", "bar", new BeanOutputHandler(root)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - List<Element> children = DomUtil.getChildren(root); - assertEquals("child count", 1, children.size()); - - Element child = children.get(0); - assertEquals("urn:zippy", child.getNamespaceURI()); - assertEquals("argle", child.getPrefix()); - assertEquals("foo", child.getLocalName()); - } - - - public void testDispatchNullValue() throws Exception - { - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", null, new BeanOutputHandler(root)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - List<Element> children1 = DomUtil.getChildren(root); - assertEquals("null element added", 0, children1.size()); - - BeanOutputDriver.dispatch("foo", null, new BeanOutputHandler(root, BeanOutputOptions.XSI_NIL)); - List<Element> children2 = DomUtil.getChildren(root); - assertEquals("null element added", 1, children2.size()); - assertEquals("nil flag", "true", - children2.get(0).getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil")); - } - - - - public void testDispatchSimpleValuesWithDefaults() throws Exception - { - Element root = DomUtil.newDocument("test"); - for (int ii = 0 ; ii < SIMPLE_VALUES.length ; ii++) - { - Object value = SIMPLE_VALUES[ii][0]; - Element parent = DomUtil.appendChild(root, "value"); - BeanOutputDriver.dispatch("foo", value, new BeanOutputHandler(parent)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - assertAppend("value " + ii, parent, "foo", "", String.valueOf(value)); - } - } - - - public void testDispatchSimpleValuesWithXSDFormatting() throws Exception - { - Element root = DomUtil.newDocument("test"); - for (int ii = 0 ; ii < SIMPLE_VALUES.length ; ii++) - { - Element parent = DomUtil.appendChild(root, "value"); - BeanOutputDriver.dispatch("foo", SIMPLE_VALUES[ii][0], - new BeanOutputHandler(parent, BeanOutputOptions.XSD_FORMAT)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - assertAppend("value " + ii, parent, "foo", "", (String)SIMPLE_VALUES[ii][2]); - } - } - - - public void testDispatchSimpleValuesWithXSIType() throws Exception - { - Element root = DomUtil.newDocument("test"); - for (int ii = 0 ; ii < SIMPLE_VALUES.length ; ii++) - { - Element parent = DomUtil.appendChild(root, "value"); - BeanOutputDriver.dispatch("foo", SIMPLE_VALUES[ii][0], - new BeanOutputHandler(parent, BeanOutputOptions.XSI_TYPE)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - assertAppend("value " + ii, parent, "foo", (String)SIMPLE_VALUES[ii][1], (String)SIMPLE_VALUES[ii][2]); - } - } - - - public void testDispatchObjectArray() throws Exception - { - Object[] data = new String[] { "argle", "bargle" }; - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", data, new BeanOutputHandler(root)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - assertAppend("container", root, "foo", "", null); - - Element container = DomUtil.getChild(root, "foo"); - List<Element> children = DomUtil.getChildren(container); - assertEquals("child count", 2, children.size()); - assertNameTypeValue("child 0", children.get(0), "data", "", "argle"); - assertNameTypeValue("child 1", children.get(1), "data", "", "bargle"); - - DomAsserts.assertEquals("child 1 index", "0", root, "/test/foo/data[1]/@index"); - DomAsserts.assertEquals("child 2 index", "1", root, "/test/foo/data[2]/@index"); - } - - - public void testDispatchObjectArrayWithType() throws Exception - { - Object[] data = new String[] { "argle", "bargle" }; - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", data, - new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - Element container = DomUtil.getChild(root, "foo"); - assertContainerNameAndType(container, "foo", data.getClass()); - - List<Element> children = DomUtil.getChildren(container); - assertEquals("child count", 2, children.size()); - assertNameTypeValue("child 0", children.get(0), "data", "xsd:string", "argle"); - assertNameTypeValue("child 1", children.get(1), "data", "xsd:string", "bargle"); - - DomAsserts.assertEquals("child 1 index", "0", root, "/test/foo/data[1]/@index"); - DomAsserts.assertEquals("child 2 index", "1", root, "/test/foo/data[2]/@index"); - } - - - public void testDispatchPrimitiveArrayWithType() throws Exception - { - int[] data = new int[] { 1, 2, 3 }; - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", data, - new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - Element container = DomUtil.getChild(root, "foo"); - assertContainerNameAndType(container, "foo", data.getClass()); - - List<Element> children = DomUtil.getChildren(container); - assertEquals("child count", 3, children.size()); - assertNameTypeValue("child 1", children.get(0), "data", "xsd:int", "1"); - assertNameTypeValue("child 2", children.get(1), "data", "xsd:int", "2"); - assertNameTypeValue("child 3", children.get(2), "data", "xsd:int", "3"); - - DomAsserts.assertEquals("child 1 index", "0", root, "/test/foo/data[1]/@index"); - DomAsserts.assertEquals("child 2 index", "1", root, "/test/foo/data[2]/@index"); - DomAsserts.assertEquals("child 3 index", "2", root, "/test/foo/data[3]/@index"); - } - - - public void testDispatchListWithType() throws Exception - { - List<String> data = new ArrayList<String>(); - data.add("argle"); - data.add("bargle"); - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", data, - new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - Element container = DomUtil.getChild(root, "foo"); - assertContainerNameAndType(container, "foo", data.getClass()); - - List<Element> children = DomUtil.getChildren(container); - assertEquals("child count", 2, children.size()); - assertNameTypeValue("child 0", children.get(0), "data", "xsd:string", "argle"); - assertNameTypeValue("child 1", children.get(1), "data", "xsd:string", "bargle"); - - DomAsserts.assertEquals("child 1 index", "0", root, "/test/foo/data[1]/@index"); - DomAsserts.assertEquals("child 2 index", "1", root, "/test/foo/data[2]/@index"); - } - - - public void testDispatchSetWithType() throws Exception - { - // use TreeSet so that order is guaranteed - Set<String> data = new TreeSet<String>(); - data.add("bargle"); - data.add("argle"); - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", data, - new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - Element container = DomUtil.getChild(root, "foo"); - assertContainerNameAndType(container, "foo", data.getClass()); - - List<Element> children = DomUtil.getChildren(container); - assertEquals("child count", 2, children.size()); - assertNameTypeValue("child 0", children.get(0), "data", "xsd:string", "argle"); - assertNameTypeValue("child 1", children.get(1), "data", "xsd:string", "bargle"); - - DomAsserts.assertEquals("child 1 index", "", root, "/test/foo/data[1]/@index"); - DomAsserts.assertEquals("child 2 index", "", root, "/test/foo/data[2]/@index"); - } - - - public void testDispatchMapWithDefaultFormat() throws Exception - { - // use TreeMap so that order is guaranteed - Map<String,Object> data = new TreeMap<String,Object>(); - data.put("foo", Integer.valueOf(123)); - data.put("argle", "bargle"); - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", data, - new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - Element container = DomUtil.getChild(root, "foo"); - assertContainerNameAndType(container, "foo", data.getClass()); - - List<Element> children = DomUtil.getChildren(container); - assertEquals("child count", 2, children.size()); - assertNameTypeValue("child 0", children.get(0), "data", "xsd:string", "bargle"); - assertNameTypeValue("child 1", children.get(1), "data", "xsd:int", "123"); - - DomAsserts.assertEquals("child 1 key", "argle", root, "/test/foo/data[1]/@key"); - DomAsserts.assertEquals("child 2 key", "foo", root, "/test/foo/data[2]/@key"); - } - - - public void testDispatchMapWithIntrospectFormat() throws Exception - { - // use TreeMap so that order is guaranteed - Map<String,Object> data = new TreeMap<String,Object>(); - data.put("foo", Integer.valueOf(123)); - data.put("argle", "bargle"); - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", data, - new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE, - BeanOutputOptions.INTROSPECT_MAPS)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - Element container = DomUtil.getChild(root, "foo"); - assertContainerNameAndType(container, "foo", data.getClass()); - - List<Element> children = DomUtil.getChildren(container); - assertEquals("child count", 2, children.size()); - assertNameTypeValue("child 0", children.get(0), "argle", "xsd:string", "bargle"); - assertNameTypeValue("child 1", children.get(1), "foo", "xsd:int", "123"); - - DomAsserts.assertEquals("child 1 key", "", root, "/test/foo/argle/@key"); - DomAsserts.assertEquals("child 2 key", "", root, "/test/foo/foo/@key"); - } - - - public void testDispatchSimpleBean() throws Exception - { - SimpleBean bean = new SimpleBean("zippy", 123, new BigDecimal("456.78"), true); - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", bean, - new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - Element container = DomUtil.getChild(root, "foo"); - assertContainerNameAndType(container, "foo", bean.getClass()); - - List<Element> children = DomUtil.getChildren(container); - assertEquals("child count", 4, children.size()); - - Element child1 = (Element)(new XPathWrapper("//sval").evaluate(root).get(0)); - assertNameTypeValue("sval", child1, "sval", "xsd:string", "zippy"); - - Element child2 = (Element)(new XPathWrapper("//ival").evaluate(root).get(0)); - assertNameTypeValue("ival", child2, "ival", "xsd:int", "123"); - - Element child3 = (Element)(new XPathWrapper("//dval").evaluate(root).get(0)); - assertNameTypeValue("dval", child3, "dval", "xsd:decimal", "456.78"); - - Element child4 = (Element)(new XPathWrapper("//bval").evaluate(root).get(0)); - assertNameTypeValue("bval", child4, "bval", "xsd:boolean", "true"); - } - - - public void testDispatchSimpleBeanIncludingNulls() throws Exception - { - SimpleBean bean = new SimpleBean(); - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", bean, - new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE, - BeanOutputOptions.XSI_NIL)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - Element container = DomUtil.getChild(root, "foo"); - assertContainerNameAndType(container, "foo", bean.getClass()); - - List<Element> children = DomUtil.getChildren(container); - assertEquals("child count", 4, children.size()); - - Element child1 = (Element)(new XPathWrapper("//sval").evaluate(root).get(0)); - assertEquals("sval nil", "true", child1.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil")); - - Element child2 = (Element)(new XPathWrapper("//ival").evaluate(root).get(0)); - assertNameTypeValue("ival", child2, "ival", "xsd:int", "0"); - - Element child3 = (Element)(new XPathWrapper("//dval").evaluate(root).get(0)); - assertEquals("dval nil", "true", child3.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil")); - - Element child4 = (Element)(new XPathWrapper("//bval").evaluate(root).get(0)); - assertNameTypeValue("bval", child4, "bval", "xsd:boolean", "false"); - } - - - public void testDispatchSimpleBeanIgnoringNulls() throws Exception - { - SimpleBean bean = new SimpleBean(); - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", bean, - new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - Element container = DomUtil.getChild(root, "foo"); - assertContainerNameAndType(container, "foo", bean.getClass()); - - List<Element> children = DomUtil.getChildren(container); - assertEquals("child count", 2, children.size()); - - Element child1 = (Element)(new XPathWrapper("//ival").evaluate(root).get(0)); - assertNameTypeValue("ival", child1, "ival", "xsd:int", "0"); - - Element child2 = (Element)(new XPathWrapper("//bval").evaluate(root).get(0)); - assertNameTypeValue("bval", child2, "bval", "xsd:boolean", "false"); - } - - - public void testDispatchCompoundBean() throws Exception - { - CompoundBean bean = new CompoundBean( - new SimpleBean("zippy", 123, new BigDecimal("456.78"), true), - new int[] { 1, 2, 3 }, - Arrays.asList("foo", "bar", "baz")); - - // at this point, I'm convinced the type output works, so we'll do default - // output and then use XPath for all assertions - - Element root = DomUtil.newDocument("test"); - BeanOutputDriver.dispatch("foo", bean, new BeanOutputHandler(root)); -// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - - DomAsserts.assertEquals("zippy", root, "/test/foo/simple/sval"); - DomAsserts.assertEquals("123", root, "/test/foo/simple/ival"); - DomAsserts.assertEquals("456.78", root, "/test/foo/simple/dval"); - DomAsserts.assertEquals("true", root, "/test/foo/simple/bval"); - - DomAsserts.assertEquals("1", root, "/test/foo/primArray/data[1]"); - DomAsserts.assertEquals("2", root, "/test/foo/primArray/data[2]"); - DomAsserts.assertEquals("3", root, "/test/foo/primArray/data[3]"); - - DomAsserts.assertEquals("0", root, "/test/foo/primArray/data[1]/@index"); - DomAsserts.assertEquals("1", root, "/test/foo/primArray/data[2]/@index"); - DomAsserts.assertEquals("2", root, "/test/foo/primArray/data[3]/@index"); - - DomAsserts.assertEquals("foo", root, "/test/foo/stringList/data[1]"); - DomAsserts.assertEquals("bar", root, "/test/foo/stringList/data[2]"); - DomAsserts.assertEquals("baz", root, "/test/foo/stringList/data[3]"); - } -} Copied: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandlerImpl.java (from rev 89, branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandler.java) =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandlerImpl.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandlerImpl.java 2009-07-16 15:15:28 UTC (rev 90) @@ -0,0 +1,574 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import javax.xml.XMLConstants; + +import org.w3c.dom.Element; + +import junit.framework.TestCase; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.OutputUtil; +import net.sf.practicalxml.junit.DomAsserts; +import net.sf.practicalxml.xpath.XPathWrapper; + + +public class TestBeanOutputHandlerImpl +extends TestCase +{ +//---------------------------------------------------------------------------- +// Test Data / Classes +//---------------------------------------------------------------------------- + + /** + * An array of simple value types, along with their XSD type name and + * XSD-formatted value. + */ + private final static Object[][] SIMPLE_VALUES = new Object[][] + { + new Object[] { Byte.valueOf((byte)123), "xsd:byte", "123" }, + new Object[] { Short.valueOf((short)4567), "xsd:short", "4567" }, + new Object[] { Integer.valueOf(12345678), "xsd:int", "12345678" }, + new Object[] { Long.valueOf(12345678901234L), "xsd:long", "12345678901234" }, + new Object[] { Float.valueOf((float)1234), "xsd:decimal", "1234.0" }, + new Object[] { Double.valueOf(1234567890.5), "xsd:decimal", "1234567890.5" }, + new Object[] { Boolean.TRUE, "xsd:boolean", "true" }, + new Object[] { Boolean.FALSE, "xsd:boolean", "false" }, + new Object[] { Character.valueOf('A'), "xsd:string", "A" }, + new Object[] { "this is a test", "xsd:string", "this is a test" }, + new Object[] { new Date(1247551703704L), "xsd:dateTime", "2009-07-14T06:08:23" }, + new Object[] { new BigInteger("123456789012345"), "xsd:decimal", "123456789012345" }, + new Object[] { new BigDecimal("123456789012345.123456789012345"), "xsd:decimal", "123456789012345.123456789012345" } + }; + + + // has to be public so that we can introspect it + public static class SimpleBean + { + private String _sval; + private int _ival; + private BigDecimal _dval; + private boolean _bval; + + public SimpleBean() + { + // nothign to see here + } + + public SimpleBean(String sval, int ival, BigDecimal dval, boolean bval) + { + _sval = sval; + _ival = ival; + _dval = dval; + _bval = bval; + } + + public String getSval() { return _sval; } + public void setSval(String sval) { _sval = sval; } + + public int getIval() { return _ival; } + public void setIval(int ival) { _ival = ival; } + + public BigDecimal getDval() { return _dval; } + public void setDval(BigDecimal dval) { _dval = dval; } + + public boolean isBval() { return _bval; } + public void setBval(boolean bval) { _bval = bval; } + } + + + public static class CompoundBean + { + private SimpleBean _simple; + private int[] _primArray; + private List<String> _stringList; + + public CompoundBean() + { + // nothing here + } + + public CompoundBean(SimpleBean simple, int[] primArray, List<String> stringList) + { + super(); + _simple = simple; + _primArray = primArray; + _stringList = stringList; + } + + public SimpleBean getSimple() { return _simple; } + public void setSimple(SimpleBean simple) { _simple = simple; } + + public int[] getPrimArray() { return _primArray; } + public void setPrimArray(int[] primArray) { _primArray = primArray; } + + public List<String> getStringList() { return _stringList; } + public void setStringList(List<String> list) { _stringList = list; } + } + + +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + /** + * Asserts that an element has the expected number of children (verifies + * that we didn't append to the wrong element). + */ + private void assertChildCount(String message, Element parent, int expected) + { + List<Element> children = DomUtil.getChildren(parent); + assertEquals(message + " child count:", expected, children.size()); + } + + + /** + * Asserts the name, type, and value of an element. If any of the expected + * values are null, the associated test isn't performed. + */ + private void assertNameTypeValue( + String message, Element elem, + String expectedName, String expectedType, String expectedValue) + { + if (expectedName != null) + assertEquals(message + " name:", expectedName, + elem.getNodeName()); + if (expectedType != null) + assertEquals(message + " type:", expectedType, + elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type")); + if (expectedValue != null) + assertEquals(message + " value:", expectedValue, + DomUtil.getText(elem)); + } + + + /** + * Asserts the name and type of a compound element, where the type is a Java classname. + */ + private void assertContainerNameAndType(Element elem, String expectedName, Class<?> expectedType) + { + assertNameTypeValue("container", elem, expectedName, "java:" + expectedType.getName(), null); + } + + + /** + * A variety of assertions about added children. Assumes that we'll add + * a single child node per parent, and that we aren't using namespaces. + */ + private void assertAppend(String message, Element parent, + String expectedName, + String expectedType, + String expectedValue) + { + assertChildCount(message, parent, 1); + assertNameTypeValue(message, Do... [truncated message content] |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-15 19:31:59
|
Revision: 89 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=89&view=rev Author: kdgregory Date: 2009-07-15 19:31:56 +0000 (Wed, 15 Jul 2009) Log Message: ----------- remove unused methods Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java 2009-07-15 19:28:12 UTC (rev 88) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java 2009-07-15 19:31:56 UTC (rev 89) @@ -26,7 +26,6 @@ import java.util.Set; import javax.xml.XMLConstants; -import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -70,47 +69,6 @@ //---------------------------------------------------------------------------- -// Public methods -//---------------------------------------------------------------------------- - - /** - * Convenience method to create a new document and invoke conversion. - * This document will not namespace its elements. - * - * @param bean The object to be converted. - * @param rootName The name of the root element of the new document. - * @param options Options to control the output structure. Options - * are additive, and are either on (passed here) or - * off. - */ - public static Document newDocument(Object bean, String rootName, BeanOutputOptions... options) - { - return newDocument(bean, null, rootName, options); - } - - - /** - * Convenience method to create a new document and invoke conversion, - * with all elements belonging to a specified namespace. - * - * @param bean The object to be converted. - * @param nsUri Namespace for this document; all elements will - * have the same namespace. If <code>null</code>, - * the elements will not be namespaced. - * @param rootName The name of the root element of the new document. - * @param options Options to control the output structure. Options - * are additive, and are either on (passed here) or - * off. - */ - public static Document newDocument(Object bean, String nsUri, String rootName, - BeanOutputOptions... options) - { - Element root = DomUtil.newDocument(nsUri, rootName); - return root.getOwnerDocument(); - } - - -//---------------------------------------------------------------------------- // OutputHandler implementation //---------------------------------------------------------------------------- This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-15 19:28:14
|
Revision: 88 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=88&view=rev Author: kdgregory Date: 2009-07-15 19:28:12 +0000 (Wed, 15 Jul 2009) Log Message: ----------- DomAsserts - rename params for clarity Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java Modified: trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java 2009-07-15 19:23:56 UTC (rev 87) +++ trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java 2009-07-15 19:28:12 UTC (rev 88) @@ -62,7 +62,7 @@ /** - * Asserts that an element has the given name and namespace URI. + * Asserts that an element has the given local name and namespace URI. * <p> * If assertion fails, will display message indicating whether name or * namespace was invalid. @@ -70,32 +70,32 @@ * @param expectedNSUri The expected namespace URI. May be <code>null * </code> to assert that the element does not * have a namespace. - * @param expectedName The expected name. + * @param localName The expected name, sans prefix * @param elem The element to assert. */ public static void assertNamespaceAndName( - String expectedNSUri, String expectedName, Element elem) + String expectedNSUri, String localName, Element elem) { Assert.assertEquals("invalid namespace", expectedNSUri, elem.getNamespaceURI()); - Assert.assertEquals("invalid localname", expectedName, DomUtil.getLocalName(elem)); + Assert.assertEquals("invalid localname", localName, DomUtil.getLocalName(elem)); } /** - * Asserts that an element has the given name and namespace URI. + * Asserts that an element has the given local name and namespace URI. * * @param message Message to display if assertion fails. * @param expectedNSUri The expected namespace URI. May be <code>null * </code> to assert that the element does not * have a namespace. - * @param expectedName The expected name. + * @param localName The expected name, sans prefix * @param elem The element to assert. */ public static void assertNamespaceAndName( - String message, String expectedNSUri, String expectedName, Element elem) + String message, String expectedNSUri, String localName, Element elem) { Assert.assertEquals(message, expectedNSUri, elem.getNamespaceURI()); - Assert.assertEquals(message, expectedName, DomUtil.getLocalName(elem)); + Assert.assertEquals(message, localName, DomUtil.getLocalName(elem)); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-15 19:23:58
|
Revision: 87 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=87&view=rev Author: kdgregory Date: 2009-07-15 19:23:56 +0000 (Wed, 15 Jul 2009) Log Message: ----------- Initial Commit - BeanConverter (output only) Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputOptions.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandler.java Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java 2009-07-15 19:23:56 UTC (rev 87) @@ -0,0 +1,112 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import net.sf.practicalxml.DomUtil; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + + +/** + * Converts Java objects (not just beans) to and from an XML representation. + * This was originally developed to support RESTful web services, without the + * class-generation hassle of JAXB. + * <p> + * The XML generated/consumed by this class obeys the following rules, with + * options controlled by {@link BeanOutputOptions}: + * <ul> + * <li> All elements inherit their parent's namespace and namespace prefix. + * <li> Elements of sets are output using the element name "data". + * <li> Elements of lists and arrays are output using the element name "data", + * with an attribute "index" that contains the numeric index. + * <li> Elements of maps are by default output using the name "data", with an + * attribute "name" containing the actual map key. The output option + * <code>INTROSPECT_MAPS</code> will change this behavior, but will throw + * if the map keys are not valid XML element names. + * <li> Values are output using <code>toString()</code>, except as overridden + * by options. + * <li> Null values do not generate output, unless the <code>XSI_NIL</code> + * option is in effect. + * </ul> + */ +public class BeanConverter +{ + /** + * Creates a new DOM document from the passed bean, in which all elements + * are members of the specified namespace. + * + * @param bean The source object. This can be any Java object: + * bean, collection, or simple type. + * @param nsUri The namespace of the root element. This will be + * inherited by all child elements. + * @param rootName The qualified name given to the root element of the + * generated document. If a qualified name, all child + * elements will inherit its prefix. + * @param options Options controlling output. +. + */ + public static Document generateXml(Object bean, String nsUri, String rootName, + BeanOutputOptions... options) + { + Element root = DomUtil.newDocument(nsUri, "temp"); + BeanOutputDriver.dispatch(rootName, bean, new BeanOutputHandler(root, options)); + return replaceRoot(root); + } + + + /** + * Creates a new DOM document from the passed bean, without namespace. + * + * @param bean The source object. This can be any Java object: + * bean, collection, or simple type. + * @param rootName The name given to the root element of the produced + * document. + * @param options Options controlling output. +. + */ + public static Document generateXml(Object bean, String rootName, + BeanOutputOptions... options) + { + Element root = DomUtil.newDocument("temp"); + BeanOutputDriver.dispatch(rootName, bean, new BeanOutputHandler(root, options)); + return replaceRoot(root); + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + /** + * Replaces the passed element as the root of the document by its first + * element child. This is a remarkably ugly hack, but it lets me avoid + * writing a worse hack into the output handler to determine whether or + * not it is handling the root element. + * <p> + * This works for the Sun (Xerces) DOM implementation. It might not work + * for other implementations (indeed, the <code>Node</code> JavaDoc + * indicates that it might not be allowed). + */ + private static Document replaceRoot(Element oldRoot) + { + Element newRoot = DomUtil.getChildren(oldRoot).get(0); + oldRoot.removeChild(newRoot); + + Document dom = oldRoot.getOwnerDocument(); + dom.replaceChild(newRoot, oldRoot); + return dom; + } +} Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputDriver.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputDriver.java 2009-07-15 19:23:56 UTC (rev 87) @@ -0,0 +1,207 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * Driver class for bean conversion. This class uses an {@link OutputHandler} + * to produce actual output, and provides two methods that interact with that + * handler: + * + * <ul> + * <li> {@link #dispatch} examines the type of the passed object, and calls + * the appropriate output handler method. Objects may be dispatched + * with or without a name associated. + * <li> {@link #introspect} examines compound objects that are either beans + * or Java collection objects, and calls <code>dispatch()</code> for + * each member. This method is meant to be called by the handler. + * </ul> + */ +public class BeanOutputDriver +{ + /** + * Dispatches a named object to the output handler. + */ + public static void dispatch(String name, Object obj, BeanOutputHandler handler) + { + if (obj == null) + handler.convertNull(name); + else if (obj instanceof String) + handler.convert(name, (String)obj); + else if (obj instanceof Integer) + handler.convert(name, (Integer)obj); + else if (obj instanceof Long) + handler.convert(name, (Long)obj); + else if (obj instanceof Short) + handler.convert(name, (Short)obj); + else if (obj instanceof Byte) + handler.convert(name, (Byte)obj); + else if (obj instanceof Double) + handler.convert(name, (Double)obj); + else if (obj instanceof Float) + handler.convert(name, (Float)obj); + else if (obj instanceof Boolean) + handler.convert(name, (Boolean)obj); + else if (obj instanceof Character) + handler.convert(name, (Character)obj); + else if (obj instanceof Date) + handler.convert(name, (Date)obj); + else if (obj instanceof Number) + handler.convert(name, (Number)obj); + else if (obj instanceof List) + handler.convert(name, (List<?>)obj); + else if (obj instanceof Set) + handler.convert(name, (Set<?>)obj); + else if (obj instanceof Map) + handler.convert(name, (Map<?,?>)obj); + else if (obj instanceof Object[]) + handler.convert(name, (Object[])obj); + else + handler.convert(name, obj); + } + + + /** + * Dispatches an unnamed object to the output handler (actually + * dispatches with a null name). + */ + public static void dispatch(Object obj, BeanOutputHandler handler) + { + dispatch(null, obj, handler); + } + + + /** + * Introspects the passed compound object, and dispatches each of its + * components. If the object is not a compound object, will dispatch + * the object itself. If the object is <code>null</code>, does nothing. + */ + public static void introspect(Object obj, BeanOutputHandler handler) + { + if (obj == null) + return; + + if (isSimpleObject(obj)) + dispatch(obj, handler); + else if (obj.getClass().isArray()) + introspectPrimitiveArray(obj, handler); + else if (obj instanceof Object[]) + introspectObjectArray((Object[])obj, handler); + else if (obj instanceof Collection<?>) + introspectCollection((Collection<?>)obj, handler); + else if (obj instanceof Map<?,?>) + introspectMap((Map<?,?>)obj, handler); + else + introspectBean(obj, handler); + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + private static boolean isSimpleObject(Object obj) + { + return (obj instanceof String) + || (obj instanceof Integer) + || (obj instanceof Long) + || (obj instanceof Byte) + || (obj instanceof Short) + || (obj instanceof Double) + || (obj instanceof Float) + || (obj instanceof Character) + || (obj instanceof Boolean) + || (obj instanceof Number) + || (obj instanceof Date); + } + + + private static void introspectPrimitiveArray(Object array, BeanOutputHandler handler) + { + int length = Array.getLength(array); + for (int ii = 0 ; ii < length ; ii++) + dispatch(Array.get(array, ii), handler); + } + + + private static void introspectObjectArray(Object[] array, BeanOutputHandler handler) + { + for (int ii = 0 ; ii < array.length ; ii++) + dispatch(array[ii], handler); + } + + + private static void introspectCollection(Collection<?> coll, BeanOutputHandler handler) + { + for (Object member : coll) + dispatch(member, handler); + } + + + private static void introspectMap(Map<?,?> map, BeanOutputHandler handler) + { + for (Map.Entry<?,?> entry : map.entrySet()) + dispatch(String.valueOf(entry.getKey()), entry.getValue(), handler); + } + + + private static void introspectBean(Object bean, BeanOutputHandler handler) + { + try + { + BeanInfo info = Introspector.getBeanInfo(bean.getClass(), Object.class); + PropertyDescriptor[] props = info.getPropertyDescriptors(); + for (int ii = 0 ; ii < props.length ; ii++) + beanIntrospectionHelper(bean, props[ii], handler); + } + catch (IntrospectionException ee) + { + throw new RuntimeException("introspection failure", ee); + } + } + + + private static void beanIntrospectionHelper( + Object bean, PropertyDescriptor propDesc, BeanOutputHandler handler) + { + Method getter = propDesc.getReadMethod(); + if (getter == null) + return; + + Object value = null; + try + { + value = getter.invoke(bean); + } + catch (Exception ee) + { + throw new RuntimeException("unable to retrieve bean value", ee); + } + + dispatch(propDesc.getName(), value, handler); + } +} Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputHandler.java 2009-07-15 19:23:56 UTC (rev 87) @@ -0,0 +1,390 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.XmlUtil; + +import java.util.Arrays; +import java.util.Date; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.xml.XMLConstants; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + + +/** + * {@link OutputHandler} that builds a DOM tree. The structure of this + * tree is customized by passing values from {@link BeanOutputOptions}. + * <p> + * Instances of this class are constructed around an <code>Element</code>, + * which represents the object to be introspected by {@link BeanOutputDriver}; + * this simplifies recursive calls. + * <p> + * This class is not threadsafe (but then, neither are the JDK's DOM classes). + */ +public class BeanOutputHandler +{ + private EnumSet<BeanOutputOptions> _options; + private Appender _appender; + + + /** + * Public constructor, which uses default appender and allows individual + * options specifiers. + */ + public BeanOutputHandler(Element appendTo, BeanOutputOptions... options) + { + _appender = new Appender(appendTo); + _options = EnumSet.noneOf(BeanOutputOptions.class); + for (BeanOutputOptions option : options) + _options.add(option); + } + + + /** + * Internal constructor, used by container classes for recursive calls. + */ + protected BeanOutputHandler(BeanOutputHandler parent, Appender appender) + { + _appender = appender; + _options = parent._options; + } + + +//---------------------------------------------------------------------------- +// Public methods +//---------------------------------------------------------------------------- + + /** + * Convenience method to create a new document and invoke conversion. + * This document will not namespace its elements. + * + * @param bean The object to be converted. + * @param rootName The name of the root element of the new document. + * @param options Options to control the output structure. Options + * are additive, and are either on (passed here) or + * off. + */ + public static Document newDocument(Object bean, String rootName, BeanOutputOptions... options) + { + return newDocument(bean, null, rootName, options); + } + + + /** + * Convenience method to create a new document and invoke conversion, + * with all elements belonging to a specified namespace. + * + * @param bean The object to be converted. + * @param nsUri Namespace for this document; all elements will + * have the same namespace. If <code>null</code>, + * the elements will not be namespaced. + * @param rootName The name of the root element of the new document. + * @param options Options to control the output structure. Options + * are additive, and are either on (passed here) or + * off. + */ + public static Document newDocument(Object bean, String nsUri, String rootName, + BeanOutputOptions... options) + { + Element root = DomUtil.newDocument(nsUri, rootName); + return root.getOwnerDocument(); + } + + +//---------------------------------------------------------------------------- +// OutputHandler implementation +//---------------------------------------------------------------------------- + + public void convert(String name, Boolean value) + { + Object formatted = value; + if (shouldFormatAsXsd()) + formatted = value.booleanValue() ? "true" : "false"; + + _appender.append(name, "xsd:boolean", formatted); + } + + + public void convert(String name, Byte value) + { + _appender.append(name, "xsd:byte", value); + } + + + public void convert(String name, Character value) + { + _appender.append(name, "xsd:string", value); + } + + + public void convert(String name, Date value) + { + Object formatted = value; + if (shouldFormatAsXsd()) + formatted = XmlUtil.formatXsdDatetime(value); + + _appender.append(name, "xsd:dateTime", formatted); + } + + + public void convert(String name, Double value) + { + Object formatted = value; + if (shouldFormatAsXsd()) + formatted = XmlUtil.formatXsdDecimal(value); + + _appender.append(name, "xsd:decimal", formatted); + } + + + public void convert(String name, Float value) + { + Object formatted = value; + if (shouldFormatAsXsd()) + formatted = XmlUtil.formatXsdDecimal(value); + + _appender.append(name, "xsd:decimal", formatted); + } + + + public void convert(String name, Integer value) + { + _appender.append(name, "xsd:int", value); + } + + + public void convert(String name, Long value) + { + _appender.append(name, "xsd:long", value); + } + + + public void convert(String name, Number value) + { + _appender.append(name, "xsd:decimal", value); + } + + + public void convert(String name, Short value) + { + _appender.append(name, "xsd:short", value); + } + + + public void convert(String name, String value) + { + _appender.append(name, "xsd:string", value); + } + + + public void convert(String name, List<?> value) + { + Element container = _appender.append(name, javaXsiType(value), null); + appendSequence(container, value.iterator(), true); + } + + + public void convert(String name, Set<?> value) + { + Element container = _appender.append(name, javaXsiType(value), null); + appendSequence(container, value.iterator(), false); + } + + + public void convert(String name, Map<?,?> map) + { + Element container = _appender.append(name, javaXsiType(map), null); + BeanOutputHandler childHandler = new BeanOutputHandler(this, new MapAppender(container)); + BeanOutputDriver.introspect(map, childHandler); + } + + + public void convert(String name, Object value) + { + Element container = _appender.append(name, javaXsiType(value), null); + Appender childAppender = value.getClass().isArray() + ? new IndexedAppender(container) + : new Appender(container); + BeanOutputHandler childHandler = new BeanOutputHandler(this, childAppender); + BeanOutputDriver.introspect(value, childHandler); + } + + + public void convert(String name, Object[] array) + { + Element container = _appender.append(name, javaXsiType(array), null); + appendSequence(container, Arrays.asList(array).iterator(), true); + } + + + public void convertNull(String name) + { + if (!_options.contains(BeanOutputOptions.XSI_NIL)) + return; + + Element child = _appender.append(name, "", null); + child.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:nil", "true"); + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + protected boolean shouldFormatAsXsd() + { + return _options.contains(BeanOutputOptions.XSD_FORMAT) + || _options.contains(BeanOutputOptions.XSI_TYPE); + } + + + /** + * Returns the <code>xsi:type</code> value for a Java object (should not + * be called for primitive wrappers, as these are covered by "xsd" types). + */ + private String javaXsiType(Object obj) + { + return "java:" + obj.getClass().getName(); + } + + + /** + * Common code for sequenced objects -- arrays, lists, and sets. + */ + private void appendSequence(Element parent, Iterator<?> itx, boolean isIndexed) + { + Appender childAppender = isIndexed ? new IndexedAppender(parent) + : new Appender(parent); + BeanOutputHandler childHandler = new BeanOutputHandler(this, childAppender); + while (itx.hasNext()) + BeanOutputDriver.dispatch("data", itx.next(), childHandler); + } + + + /** + * This class is responsible for adding nodes to the DOM tree. Each + * instance of <code>XmlOutputHandler</code> is constructed around one + * of these (or a subclass). Subclasses may override the {@link #append} + * method to add additional information to the appended nodes. + * <p> + * A little bit of weirdness: as an inner class, this is constructed in + * the context of an <code>XmlOutputHandler</code>, and has access to + * members defined by that handler. However, in a recursive call, it is + * actually held and used by a different handler. Since the second handler + * copies members of the first, I don't see this as a problem ... until + * some member doesn't get copied. + */ + private class Appender + { + private Element _appendTo; + + public Appender(Element appendTo) + { + _appendTo = appendTo; + } + + /** + * Appends a child to the element we manage. + * + * @param name Local-name for the new element. As long as you're + * dealing with Java, property names and element names + * are interchangeable. If handling a <code>Map</code>, + * you'll need to ensure that keys are appropriate XML + * element names. + * @param xsiType The value to insert in an <code>xsi:type</code> + * attribute. This is required, even if the output-type + * option isn't turned on. + * @param value If not-null, will be stringified and attached to the + * element as a text node. Apply any fancy formatting + * before coming here. If null, will be ignored. + */ + public Element append(String name, String xsiType, Object value) + { + if ((name == null) || (name.length() == 0)) + name = "data"; + + Element child = DomUtil.appendChildInheritNamespace(_appendTo, name); + + if (_options.contains(BeanOutputOptions.XSI_TYPE)) + child.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:type", xsiType); + + if (value != null) + child.setTextContent(String.valueOf(value)); + + return child; + } + } + + + /** + * An appender for array/list processing, that attaches an index attribute + * to the appended node. + */ + private class IndexedAppender + extends Appender + { + int _index = 0; + + public IndexedAppender(Element appendTo) + { + super(appendTo); + } + + @Override + public Element append(String name, String xsiType, Object value) + { + Element child = super.append(name, xsiType, value); + child.setAttribute("index", String.valueOf(_index++)); + return child; + } + } + + + /** + * An appender for maps, which either uses the passed name as the + * element name, or as the value of a "key" attribute. + */ + private class MapAppender + extends Appender + { + public MapAppender(Element appendTo) + { + super(appendTo); + } + + @Override + public Element append(String name, String xsiType, Object value) + { + Element child = null; + if (_options.contains(BeanOutputOptions.INTROSPECT_MAPS)) + { + child = super.append(name, xsiType, value); + } + else + { + child = super.append("data", xsiType, value); + child.setAttribute("key", name); + } + return child; + } + } +} Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputOptions.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanOutputOptions.java 2009-07-15 19:23:56 UTC (rev 87) @@ -0,0 +1,41 @@ +// Copyright (c) 2009 Keith D Gregory +package net.sf.practicalxml.converter; + +/** + * Options used by {@link BeanOutputHandler} to control the structure of the + * DOM tree. + */ +public enum BeanOutputOptions +{ + /** + * Outputs values using formats defined by XML Schema, rather than Java's + * <code>String.valueOf()</code> method. Note that these formats are not + * flagged in the element, so sender and receiver will have to agree on + * the format. + */ + XSD_FORMAT, + + /** + * Will add an <code>xsi:type</code> attribute to each element. For values + * covered by the XML Schema simple types, this attribute's value will be + * "<code>xsd:XXX</code>", where XXX is the XSD type name. For complex + * types, this attribute's value will be "<code>java:XXX</code>", where + * XXX is the fully-qualified classname. + * <p> + * <em>This option implies {@link #XSD_FORMAT} for simple types</em>. + */ + XSI_TYPE, + + /** + * Report null values using the <code>xsi:nil="true"</code> attribute. If + * not present, null values are ignored, and not added to DOM tree. + */ + XSI_NIL, + + /** + * Output maps in an "introspected" format, where the name of each item + * is the map key (rather than "data"), and the "key" attribute is omitted. + * If any key is not a valid XML identifier, the converter will throw. + */ + INTROSPECT_MAPS +} Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java 2009-07-15 19:23:56 UTC (rev 87) @@ -0,0 +1,74 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.OutputUtil; + +import javax.xml.XMLConstants; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import junit.framework.TestCase; + +/** + * Tests for the top-level <code>BeanConverter</code> methods. These tend to + * be minimal; the detailed testing happens in {@link TestBeanOutputHandler} + * and {@link TestBeanInputHandler}. + */ +public class TestBeanConverter +extends TestCase +{ +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + public void testGenerateSimpleContentSansNamespace() throws Exception + { + String rootName = "argle"; + String value = "foo"; + + Document dom = BeanConverter.generateXml(value, rootName, BeanOutputOptions.XSI_TYPE); +// System.out.println(OutputUtil.compactString(dom)); + + Element root = dom.getDocumentElement(); + assertEquals(rootName, root.getNodeName()); + assertEquals(value, root.getTextContent()); + assertEquals("xsd:string", root.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type")); + assertEquals(0, DomUtil.getChildren(root).size()); + } + + + public void testGenerateSimpleContentWithNamespace() throws Exception + { + String nsUri = "urn:fibble"; + String rootName = "argle:bargle"; + String value = "foo"; + + Document dom = BeanConverter.generateXml(value, nsUri, rootName, BeanOutputOptions.XSI_TYPE); +// System.out.println(OutputUtil.compactString(dom)); + + Element root = dom.getDocumentElement(); + assertEquals(nsUri, root.getNamespaceURI()); + assertEquals(rootName, root.getNodeName()); + assertEquals(value, root.getTextContent()); + assertEquals("xsd:string", root.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type")); + assertEquals(0, DomUtil.getChildren(root).size()); + } +} \ No newline at end of file Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandler.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandler.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanOutputHandler.java 2009-07-15 19:23:56 UTC (rev 87) @@ -0,0 +1,574 @@ +// Copyright 2008-2009 severally by the contributors +// +// 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 net.sf.practicalxml.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import javax.xml.XMLConstants; + +import org.w3c.dom.Element; + +import junit.framework.TestCase; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.OutputUtil; +import net.sf.practicalxml.junit.DomAsserts; +import net.sf.practicalxml.xpath.XPathWrapper; + + +public class TestBeanOutputHandler +extends TestCase +{ +//---------------------------------------------------------------------------- +// Test Data / Classes +//---------------------------------------------------------------------------- + + /** + * An array of simple value types, along with their XSD type name and + * XSD-formatted value. + */ + private final static Object[][] SIMPLE_VALUES = new Object[][] + { + new Object[] { Byte.valueOf((byte)123), "xsd:byte", "123" }, + new Object[] { Short.valueOf((short)4567), "xsd:short", "4567" }, + new Object[] { Integer.valueOf(12345678), "xsd:int", "12345678" }, + new Object[] { Long.valueOf(12345678901234L), "xsd:long", "12345678901234" }, + new Object[] { Float.valueOf((float)1234), "xsd:decimal", "1234.0" }, + new Object[] { Double.valueOf(1234567890.5), "xsd:decimal", "1234567890.5" }, + new Object[] { Boolean.TRUE, "xsd:boolean", "true" }, + new Object[] { Boolean.FALSE, "xsd:boolean", "false" }, + new Object[] { Character.valueOf('A'), "xsd:string", "A" }, + new Object[] { "this is a test", "xsd:string", "this is a test" }, + new Object[] { new Date(1247551703704L), "xsd:dateTime", "2009-07-14T06:08:23" }, + new Object[] { new BigInteger("123456789012345"), "xsd:decimal", "123456789012345" }, + new Object[] { new BigDecimal("123456789012345.123456789012345"), "xsd:decimal", "123456789012345.123456789012345" } + }; + + + // has to be public so that we can introspect it + public static class SimpleBean + { + private String _sval; + private int _ival; + private BigDecimal _dval; + private boolean _bval; + + public SimpleBean() + { + // nothign to see here + } + + public SimpleBean(String sval, int ival, BigDecimal dval, boolean bval) + { + _sval = sval; + _ival = ival; + _dval = dval; + _bval = bval; + } + + public String getSval() { return _sval; } + public void setSval(String sval) { _sval = sval; } + + public int getIval() { return _ival; } + public void setIval(int ival) { _ival = ival; } + + public BigDecimal getDval() { return _dval; } + public void setDval(BigDecimal dval) { _dval = dval; } + + public boolean isBval() { return _bval; } + public void setBval(boolean bval) { _bval = bval; } + } + + + public static class CompoundBean + { + private SimpleBean _simple; + private int[] _primArray; + private List<String> _stringList; + + public CompoundBean() + { + // nothing here + } + + public CompoundBean(SimpleBean simple, int[] primArray, List<String> stringList) + { + super(); + _simple = simple; + _primArray = primArray; + _stringList = stringList; + } + + public SimpleBean getSimple() { return _simple; } + public void setSimple(SimpleBean simple) { _simple = simple; } + + public int[] getPrimArray() { return _primArray; } + public void setPrimArray(int[] primArray) { _primArray = primArray; } + + public List<String> getStringList() { return _stringList; } + public void setStringList(List<String> list) { _stringList = list; } + } + + +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + /** + * Asserts that an element has the expected number of children (verifies + * that we didn't append to the wrong element). + */ + private void assertChildCount(String message, Element parent, int expected) + { + List<Element> children = DomUtil.getChildren(parent); + assertEquals(message + " child count:", expected, children.size()); + } + + + /** + * Asserts the name, type, and value of an element. If any of the expected + * values are null, the associated test isn't performed. + */ + private void assertNameTypeValue( + String message, Element elem, + String expectedName, String expectedType, String expectedValue) + { + if (expectedName != null) + assertEquals(message + " name:", expectedName, + elem.getNodeName()); + if (expectedType != null) + assertEquals(message + " type:", expectedType, + elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type")); + if (expectedValue != null) + assertEquals(message + " value:", expectedValue, + DomUtil.getText(elem)); + } + + + /** + * Asserts the name and type of a compound element, where the type is a Java classname. + */ + private void assertContainerNameAndType(Element elem, String expectedName, Class<?> expectedType) + { + assertNameTypeValue("container", elem, expectedName, "java:" + expectedType.getName(), null); + } + + + /** + * A variety of assertions about added children. Assumes that we'll add + * a single child node per parent, and that we aren't using namespaces. + */ + private void assertAppend(String message, Element parent, + String expectedName, + String expectedType, + String expectedValue) + { + assertChildCount(message, parent, 1); + assertNameTypeValue(message, DomUtil.getChildren(parent).get(0), + expectedName, expectedType, expectedValue); + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + // this test is essentially repeated in the DispatchSimpleValues tests + // it exists here as an initial verification of appended element structure + public void testDispatch() throws Exception + { + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", "bar", new BeanOutputHandler(root)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + assertAppend("", root, "foo", "", "bar"); + } + + + public void testDispachWithNullName() throws Exception + { + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch(null, "bar", new BeanOutputHandler(root)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + assertAppend("", root, "data", "", "bar"); + } + + + public void testNamespaceInheritance() throws Exception + { + Element root = DomUtil.newDocument("urn:zippy", "argle:bargle"); + BeanOutputDriver.dispatch("foo", "bar", new BeanOutputHandler(root)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + List<Element> children = DomUtil.getChildren(root); + assertEquals("child count", 1, children.size()); + + Element child = children.get(0); + assertEquals("urn:zippy", child.getNamespaceURI()); + assertEquals("argle", child.getPrefix()); + assertEquals("foo", child.getLocalName()); + } + + + public void testDispatchNullValue() throws Exception + { + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", null, new BeanOutputHandler(root)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + List<Element> children1 = DomUtil.getChildren(root); + assertEquals("null element added", 0, children1.size()); + + BeanOutputDriver.dispatch("foo", null, new BeanOutputHandler(root, BeanOutputOptions.XSI_NIL)); + List<Element> children2 = DomUtil.getChildren(root); + assertEquals("null element added", 1, children2.size()); + assertEquals("nil flag", "true", + children2.get(0).getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil")); + } + + + + public void testDispatchSimpleValuesWithDefaults() throws Exception + { + Element root = DomUtil.newDocument("test"); + for (int ii = 0 ; ii < SIMPLE_VALUES.length ; ii++) + { + Object value = SIMPLE_VALUES[ii][0]; + Element parent = DomUtil.appendChild(root, "value"); + BeanOutputDriver.dispatch("foo", value, new BeanOutputHandler(parent)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + assertAppend("value " + ii, parent, "foo", "", String.valueOf(value)); + } + } + + + public void testDispatchSimpleValuesWithXSDFormatting() throws Exception + { + Element root = DomUtil.newDocument("test"); + for (int ii = 0 ; ii < SIMPLE_VALUES.length ; ii++) + { + Element parent = DomUtil.appendChild(root, "value"); + BeanOutputDriver.dispatch("foo", SIMPLE_VALUES[ii][0], + new BeanOutputHandler(parent, BeanOutputOptions.XSD_FORMAT)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + assertAppend("value " + ii, parent, "foo", "", (String)SIMPLE_VALUES[ii][2]); + } + } + + + public void testDispatchSimpleValuesWithXSIType() throws Exception + { + Element root = DomUtil.newDocument("test"); + for (int ii = 0 ; ii < SIMPLE_VALUES.length ; ii++) + { + Element parent = DomUtil.appendChild(root, "value"); + BeanOutputDriver.dispatch("foo", SIMPLE_VALUES[ii][0], + new BeanOutputHandler(parent, BeanOutputOptions.XSI_TYPE)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + assertAppend("value " + ii, parent, "foo", (String)SIMPLE_VALUES[ii][1], (String)SIMPLE_VALUES[ii][2]); + } + } + + + public void testDispatchObjectArray() throws Exception + { + Object[] data = new String[] { "argle", "bargle" }; + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", data, new BeanOutputHandler(root)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + assertAppend("container", root, "foo", "", null); + + Element container = DomUtil.getChild(root, "foo"); + List<Element> children = DomUtil.getChildren(container); + assertEquals("child count", 2, children.size()); + assertNameTypeValue("child 0", children.get(0), "data", "", "argle"); + assertNameTypeValue("child 1", children.get(1), "data", "", "bargle"); + + DomAsserts.assertEquals("child 1 index", "0", root, "/test/foo/data[1]/@index"); + DomAsserts.assertEquals("child 2 index", "1", root, "/test/foo/data[2]/@index"); + } + + + public void testDispatchObjectArrayWithType() throws Exception + { + Object[] data = new String[] { "argle", "bargle" }; + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", data, + new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Element container = DomUtil.getChild(root, "foo"); + assertContainerNameAndType(container, "foo", data.getClass()); + + List<Element> children = DomUtil.getChildren(container); + assertEquals("child count", 2, children.size()); + assertNameTypeValue("child 0", children.get(0), "data", "xsd:string", "argle"); + assertNameTypeValue("child 1", children.get(1), "data", "xsd:string", "bargle"); + + DomAsserts.assertEquals("child 1 index", "0", root, "/test/foo/data[1]/@index"); + DomAsserts.assertEquals("child 2 index", "1", root, "/test/foo/data[2]/@index"); + } + + + public void testDispatchPrimitiveArrayWithType() throws Exception + { + int[] data = new int[] { 1, 2, 3 }; + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", data, + new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Element container = DomUtil.getChild(root, "foo"); + assertContainerNameAndType(container, "foo", data.getClass()); + + List<Element> children = DomUtil.getChildren(container); + assertEquals("child count", 3, children.size()); + assertNameTypeValue("child 1", children.get(0), "data", "xsd:int", "1"); + assertNameTypeValue("child 2", children.get(1), "data", "xsd:int", "2"); + assertNameTypeValue("child 3", children.get(2), "data", "xsd:int", "3"); + + DomAsserts.assertEquals("child 1 index", "0", root, "/test/foo/data[1]/@index"); + DomAsserts.assertEquals("child 2 index", "1", root, "/test/foo/data[2]/@index"); + DomAsserts.assertEquals("child 3 index", "2", root, "/test/foo/data[3]/@index"); + } + + + public void testDispatchListWithType() throws Exception + { + List<String> data = new ArrayList<String>(); + data.add("argle"); + data.add("bargle"); + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", data, + new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Element container = DomUtil.getChild(root, "foo"); + assertContainerNameAndType(container, "foo", data.getClass()); + + List<Element> children = DomUtil.getChildren(container); + assertEquals("child count", 2, children.size()); + assertNameTypeValue("child 0", children.get(0), "data", "xsd:string", "argle"); + assertNameTypeValue("child 1", children.get(1), "data", "xsd:string", "bargle"); + + DomAsserts.assertEquals("child 1 index", "0", root, "/test/foo/data[1]/@index"); + DomAsserts.assertEquals("child 2 index", "1", root, "/test/foo/data[2]/@index"); + } + + + public void testDispatchSetWithType() throws Exception + { + // use TreeSet so that order is guaranteed + Set<String> data = new TreeSet<String>(); + data.add("bargle"); + data.add("argle"); + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", data, + new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Element container = DomUtil.getChild(root, "foo"); + assertContainerNameAndType(container, "foo", data.getClass()); + + List<Element> children = DomUtil.getChildren(container); + assertEquals("child count", 2, children.size()); + assertNameTypeValue("child 0", children.get(0), "data", "xsd:string", "argle"); + assertNameTypeValue("child 1", children.get(1), "data", "xsd:string", "bargle"); + + DomAsserts.assertEquals("child 1 index", "", root, "/test/foo/data[1]/@index"); + DomAsserts.assertEquals("child 2 index", "", root, "/test/foo/data[2]/@index"); + } + + + public void testDispatchMapWithDefaultFormat() throws Exception + { + // use TreeMap so that order is guaranteed + Map<String,Object> data = new TreeMap<String,Object>(); + data.put("foo", Integer.valueOf(123)); + data.put("argle", "bargle"); + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", data, + new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Element container = DomUtil.getChild(root, "foo"); + assertContainerNameAndType(container, "foo", data.getClass()); + + List<Element> children = DomUtil.getChildren(container); + assertEquals("child count", 2, children.size()); + assertNameTypeValue("child 0", children.get(0), "data", "xsd:string", "bargle"); + assertNameTypeValue("child 1", children.get(1), "data", "xsd:int", "123"); + + DomAsserts.assertEquals("child 1 key", "argle", root, "/test/foo/data[1]/@key"); + DomAsserts.assertEquals("child 2 key", "foo", root, "/test/foo/data[2]/@key"); + } + + + public void testDispatchMapWithIntrospectFormat() throws Exception + { + // use TreeMap so that order is guaranteed + Map<String,Object> data = new TreeMap<String,Object>(); + data.put("foo", Integer.valueOf(123)); + data.put("argle", "bargle"); + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", data, + new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE, + BeanOutputOptions.INTROSPECT_MAPS)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Element container = DomUtil.getChild(root, "foo"); + assertContainerNameAndType(container, "foo", data.getClass()); + + List<Element> children = DomUtil.getChildren(container); + assertEquals("child count", 2, children.size()); + assertNameTypeValue("child 0", children.get(0), "argle", "xsd:string", "bargle"); + assertNameTypeValue("child 1", children.get(1), "foo", "xsd:int", "123"); + + DomAsserts.assertEquals("child 1 key", "", root, "/test/foo/argle/@key"); + DomAsserts.assertEquals("child 2 key", "", root, "/test/foo/foo/@key"); + } + + + public void testDispatchSimpleBean() throws Exception + { + SimpleBean bean = new SimpleBean("zippy", 123, new BigDecimal("456.78"), true); + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", bean, + new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Element container = DomUtil.getChild(root, "foo"); + assertContainerNameAndType(container, "foo", bean.getClass()); + + List<Element> children = DomUtil.getChildren(container); + assertEquals("child count", 4, children.size()); + + Element child1 = (Element)(new XPathWrapper("//sval").evaluate(root).get(0)); + assertNameTypeValue("sval", child1, "sval", "xsd:string", "zippy"); + + Element child2 = (Element)(new XPathWrapper("//ival").evaluate(root).get(0)); + assertNameTypeValue("ival", child2, "ival", "xsd:int", "123"); + + Element child3 = (Element)(new XPathWrapper("//dval").evaluate(root).get(0)); + assertNameTypeValue("dval", child3, "dval", "xsd:decimal", "456.78"); + + Element child4 = (Element)(new XPathWrapper("//bval").evaluate(root).get(0)); + assertNameTypeValue("bval", child4, "bval", "xsd:boolean", "true"); + } + + + public void testDispatchSimpleBeanIncludingNulls() throws Exception + { + SimpleBean bean = new SimpleBean(); + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", bean, + new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE, + BeanOutputOptions.XSI_NIL)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Element container = DomUtil.getChild(root, "foo"); + assertContainerNameAndType(container, "foo", bean.getClass()); + + List<Element> children = DomUtil.getChildren(container); + assertEquals("child count", 4, children.size()); + + Element child1 = (Element)(new XPathWrapper("//sval").evaluate(root).get(0)); + assertEquals("sval nil", "true", child1.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil")); + + Element child2 = (Element)(new XPathWrapper("//ival").evaluate(root).get(0)); + assertNameTypeValue("ival", child2, "ival", "xsd:int", "0"); + + Element child3 = (Element)(new XPathWrapper("//dval").evaluate(root).get(0)); + assertEquals("dval nil", "true", child3.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil")); + + Element child4 = (Element)(new XPathWrapper("//bval").evaluate(root).get(0)); + assertNameTypeValue("bval", child4, "bval", "xsd:boolean", "false"); + } + + + public void testDispatchSimpleBeanIgnoringNulls() throws Exception + { + SimpleBean bean = new SimpleBean(); + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", bean, + new BeanOutputHandler(root, BeanOutputOptions.XSI_TYPE)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Element container = DomUtil.getChild(root, "foo"); + assertContainerNameAndType(container, "foo", bean.getClass()); + + List<Element> children = DomUtil.getChildren(container); + assertEquals("child count", 2, children.size()); + + Element child1 = (Element)(new XPathWrapper("//ival").evaluate(root).get(0)); + assertNameTypeValue("ival", child1, "ival", "xsd:int", "0"); + + Element child2 = (Element)(new XPathWrapper("//bval").evaluate(root).get(0)); + assertNameTypeValue("bval", child2, "bval", "xsd:boolean", "false"); + } + + + public void testDispatchCompoundBean() throws Exception + { + CompoundBean bean = new CompoundBean( + new SimpleBean("zippy", 123, new BigDecimal("456.78"), true), + new int[] { 1, 2, 3 }, + Arrays.asList("foo", "bar", "baz")); + + // at this point, I'm convinced the type output works, so we'll do default + // output and then use XPath for all assertions + + Element root = DomUtil.newDocument("test"); + BeanOutputDriver.dispatch("foo", bean, new BeanOutputHandler(root)); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + DomAsserts.assertEquals("zippy", root, "/test/foo/simple/sval"); + DomAsserts.assertEquals("123", root, "/test/foo/simple/ival"); + DomAsserts.assertEquals("456.78", root, "/test/foo/simple/dval"); + DomAsserts.assertEquals("true", root, "/test/foo/simple/bval"); + + DomAsserts.assertEquals("1", root, "/test/foo/primArray/data[1]"); + DomAsserts.assertEquals("2", root, "/test/foo/primArray/data[2]"); + DomAsserts.assertEquals("3", root, "/test/foo/primArray/data[3]"); + + DomAsserts.assertEquals("0", root, "/test/foo/primArray/data[1]/@index"); + DomAsserts.assertEquals("1", root, "/test/foo/primArray/data[2]/@index"); + DomAsserts.assertEquals("2", root, "/test/foo/primArray/data[3]/@index"); + + DomAsserts.assertEquals("foo", root, "/test/foo/stringList/data[1]"); + DomAsserts.assertEquals("bar", root, "/test/foo/stringList/data[2]"); + DomAsserts.assertEquals("baz", root, "/test/foo/stringList/data[3]"); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-15 16:50:31
|
Revision: 86 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=86&view=rev Author: kdgregory Date: 2009-07-15 16:50:25 +0000 (Wed, 15 Jul 2009) Log Message: ----------- update POM Modified Paths: -------------- branches/dev-1.1/pom.xml Modified: branches/dev-1.1/pom.xml =================================================================== --- branches/dev-1.1/pom.xml 2009-07-15 16:47:48 UTC (rev 85) +++ branches/dev-1.1/pom.xml 2009-07-15 16:50:25 UTC (rev 86) @@ -5,7 +5,7 @@ <groupId>net.sf.practicalxml</groupId> <artifactId>practicalxml</artifactId> <packaging>jar</packaging> - <version>1.0.3</version> + <version>1.1-SNAPSHOT</version> <name>practicalxml</name> <url>http://sourceforge.net/projects/practicalxml/</url> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-15 16:47:49
|
Revision: 85 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=85&view=rev Author: kdgregory Date: 2009-07-15 16:47:48 +0000 (Wed, 15 Jul 2009) Log Message: ----------- add 1.1 development branch Added Paths: ----------- branches/dev-1.1/ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-14 14:06:16
|
Revision: 84 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=84&view=rev Author: kdgregory Date: 2009-07-14 14:06:13 +0000 (Tue, 14 Jul 2009) Log Message: ----------- add XmlUtil.formatXsdDecimal() Modified Paths: -------------- trunk/pom.xml trunk/src/main/java/net/sf/practicalxml/XmlUtil.java trunk/src/test/java/net/sf/practicalxml/TestXmlUtil.java Modified: trunk/pom.xml =================================================================== --- trunk/pom.xml 2009-07-14 12:26:58 UTC (rev 83) +++ trunk/pom.xml 2009-07-14 14:06:13 UTC (rev 84) @@ -5,7 +5,7 @@ <groupId>net.sf.practicalxml</groupId> <artifactId>practicalxml</artifactId> <packaging>jar</packaging> - <version>1.0.2</version> + <version>1.0.3</version> <name>practicalxml</name> <url>http://sourceforge.net/projects/practicalxml/</url> Modified: trunk/src/main/java/net/sf/practicalxml/XmlUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/XmlUtil.java 2009-07-14 12:26:58 UTC (rev 83) +++ trunk/src/main/java/net/sf/practicalxml/XmlUtil.java 2009-07-14 14:06:13 UTC (rev 84) @@ -15,6 +15,7 @@ package net.sf.practicalxml; import java.text.DateFormat; +import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; @@ -99,6 +100,31 @@ /** + * Converts a Java <code>double</code> to a string, using the format + * specified by XML Schema for <code>decimal</code> elements. This + * method wraps the value and calls {@link #formatXsdDecimal(Number)}, + * so call that method if you already have an object. + */ + public static String formatXsdDecimal(double value) + { + return formatXsdDecimal(Double.valueOf(value)); + } + + + /** + * Converts a Java <code>double</code> to a string, using the format + * specified by XML Schema for <code>decimal</code> elements. If + * passed <code>null</code>, returns an empty string + */ + public static String formatXsdDecimal(Number value) + { + if (value == null) + return ""; + return getXsdDecimalFormatter().format(value); + } + + + /** * Parses an XML Schema <code>dateTime</code> object, accepting any of * the legal formats. Note that this method can also be used to parse * a generic ISO-8601 date. @@ -231,6 +257,9 @@ private static ThreadLocal<DateFormat> _xsdDatetimeFormatter = new ThreadLocal<DateFormat>(); + // used by getXsdDecimalFormatter() + private static ThreadLocal<DecimalFormat> _xsdDecimalFormatter = new ThreadLocal<DecimalFormat>(); + /** * Does the actual work of {@link isLegal(String)}. */ @@ -247,7 +276,8 @@ /** - * Returns a DateFormat that will output our standard XSD dateTime format. + * Returns a DateFormat that will output the standard XSD dateTime format. + * This is managed as a ThreadLocal because formatters are not threadsafe. */ private static DateFormat getXsdDatetimeFormatter() { @@ -263,6 +293,30 @@ /** + * Returns a DecimalFormat that will output the standard XSD decimalformat. + * This is managed as a ThreadLocal because formatters are not threadsafe. + * <p> + * Note: output is limited to 17 digits to the right of the decimal point, + * because we assume a <code>double</code> input. For that reason, while + * you can use this method for <code>BigDecimal</code> values, that class' + * <code>toString()</code> is a better choice. + * <p> + * Note 2: there is no corresponding parse method; <code>Double.parseDouble() + * </code> will do the job for you. + */ + private static DecimalFormat getXsdDecimalFormatter() + { + DecimalFormat format = _xsdDecimalFormatter.get(); + if (format == null) + { + format = new DecimalFormat("#0.0################;-#"); + _xsdDecimalFormatter.set(format); + } + return format; + } + + + /** * Used by {@link parseXsdDatetime} to process individual fields of the * dateTime string and store them into a calendar object. It expects to * be called with <code>index</code> pointing to the start of the field, Modified: trunk/src/test/java/net/sf/practicalxml/TestXmlUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestXmlUtil.java 2009-07-14 12:26:58 UTC (rev 83) +++ trunk/src/test/java/net/sf/practicalxml/TestXmlUtil.java 2009-07-14 14:06:13 UTC (rev 84) @@ -77,6 +77,17 @@ } + public void testFormatXsdDecimal() throws Exception + { + assertEquals("", XmlUtil.formatXsdDecimal(null)); + assertEquals("0.0", XmlUtil.formatXsdDecimal(0)); + assertEquals("1234.0", XmlUtil.formatXsdDecimal(1234)); + assertEquals("-1234.0", XmlUtil.formatXsdDecimal(-1234)); + assertEquals("1234.5", XmlUtil.formatXsdDecimal(1234.5)); + assertEquals("1234567890.123456", XmlUtil.formatXsdDecimal(1234567890.123456)); + } + + public void testEscape() throws Exception { assertEquals("", XmlUtil.escape(null)); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-07-14 12:27:03
|
Revision: 83 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=83&view=rev Author: kdgregory Date: 2009-07-14 12:26:58 +0000 (Tue, 14 Jul 2009) Log Message: ----------- add "converter" package (empty) Added Paths: ----------- trunk/src/main/java/net/sf/practicalxml/converter/ trunk/src/main/java/net/sf/practicalxml/converter/package.html Added: trunk/src/main/java/net/sf/practicalxml/converter/package.html =================================================================== --- trunk/src/main/java/net/sf/practicalxml/converter/package.html (rev 0) +++ trunk/src/main/java/net/sf/practicalxml/converter/package.html 2009-07-14 12:26:58 UTC (rev 83) @@ -0,0 +1,6 @@ +<html> +<body> + This package contains classes to convert between XML and a variety of + other formats. +</body> +</html> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |