Thread: [Practicalxml-commits] SF.net SVN: practicalxml:[87] branches/dev-1.1/src
Brought to you by:
kdgregory
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-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-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 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-08-18 20:25:55
|
Revision: 120 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=120&view=rev Author: kdgregory Date: 2009-08-18 20:25:47 +0000 (Tue, 18 Aug 2009) Log Message: ----------- refactoring - introduce ConversionStrings Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/DomUtilToo.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanDriver.java Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionStrings.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-08-18 19:54:24 UTC (rev 119) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-08-18 20:25:47 UTC (rev 120) @@ -19,6 +19,7 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.converter.ConversionException; +import net.sf.practicalxml.converter.internal.ConversionStrings; import net.sf.practicalxml.converter.internal.DomUtilToo; @@ -168,7 +169,7 @@ { Element child = super.appendValue(name, type, value); if (child != null) - child.setAttribute("index", String.valueOf(_index++)); + child.setAttribute(ConversionStrings.AT_ARRAY_INDEX, String.valueOf(_index++)); return child; } } @@ -198,9 +199,9 @@ } else { - child = super.appendValue("data", type, value); + child = super.appendValue(ConversionStrings.EL_COLLECTION_ITEM, type, value); if (child != null) - child.setAttribute("key", name); + child.setAttribute(ConversionStrings.AT_MAP_KEY, name); } return child; } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-08-18 19:54:24 UTC (rev 119) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-08-18 20:25:47 UTC (rev 120) @@ -29,6 +29,7 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.converter.ConversionException; import net.sf.practicalxml.converter.bean.Bean2XmlAppenders.*; +import net.sf.practicalxml.converter.internal.ConversionStrings; import net.sf.practicalxml.converter.internal.DomUtilToo; import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; @@ -117,12 +118,6 @@ } - private String getJavaXsiType(Object obj) - { - return "java:" + obj.getClass().getName(); - } - - /** * Introduces the XML Schema Instance namespace into the DOM tree using a * meaningless attribute. The Xerces serializer does not attempt to promote @@ -171,13 +166,13 @@ if (!array.getClass().isArray()) return false; - Element parent = appender.appendContainer(name, getJavaXsiType(array)); + Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(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); + convert(value, ConversionStrings.EL_COLLECTION_ITEM, childAppender); } return true; } @@ -188,7 +183,7 @@ if (!(obj instanceof Map)) return false; - Element parent = appender.appendContainer(name, getJavaXsiType(obj)); + Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(obj)); Appender childAppender = new MapAppender(parent, _options); for (Map.Entry<?,?> entry : ((Map<?,?>)obj).entrySet()) { @@ -203,11 +198,11 @@ if (!(obj instanceof Collection)) return false; - Element parent = appender.appendContainer(name, getJavaXsiType(obj)); + Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(obj)); Appender childAppender = new IndexedAppender(parent, _options); for (Object value : (Collection<?>)obj) { - convert(value, "data", childAppender); + convert(value, ConversionStrings.EL_COLLECTION_ITEM, childAppender); } return true; } @@ -215,7 +210,7 @@ private boolean tryToConvertAsBean(Object bean, String name, Appender appender) { - Element parent = appender.appendContainer(name, getJavaXsiType(bean)); + Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(bean)); Appender childAppender = new BasicAppender(parent, _options); try { Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-08-18 19:54:24 UTC (rev 119) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-08-18 20:25:47 UTC (rev 120) @@ -35,6 +35,7 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.converter.ConversionException; +import net.sf.practicalxml.converter.internal.ConversionStrings; import net.sf.practicalxml.converter.internal.DomUtilToo; import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; import net.sf.practicalxml.internal.StringUtils; @@ -84,8 +85,6 @@ // 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, @@ -184,7 +183,7 @@ List<Element> children = DomUtil.getChildren(elem); for (Element child : children) { - String key = child.getAttribute("key"); + String key = child.getAttribute(ConversionStrings.AT_MAP_KEY); if (StringUtils.isEmpty(key)) key = DomUtil.getLocalName(child); Class<?> childClass = getClassFromXsiType(child); @@ -244,9 +243,9 @@ if (xsiType == null) return null; - if (xsiType.startsWith("java:")) + String javaType = DomUtilToo.getJavaClassFromXsiType(xsiType); + if (javaType != null) { - String javaType = xsiType.substring(5); try { return Class.forName(javaType); @@ -257,6 +256,7 @@ "invalid Java type specification: " + javaType, elem, ee); } } + return _helper.getJavaType(xsiType); } Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionStrings.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionStrings.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionStrings.java 2009-08-18 20:25:47 UTC (rev 120) @@ -0,0 +1,47 @@ +// 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.internal; + + +/** + * Contains constants for various string values used by the conversion + * routines. The goal is to have all converters use the same strings + * (without typos) in the same location. + */ +public class ConversionStrings +{ + /** + * Element name used to hold unnamed items from collections and arrays. + */ + public final static String EL_COLLECTION_ITEM = "data"; + + + /** + * Attribute used to hold the element index number for collections and arrays. + */ + public final static String AT_ARRAY_INDEX = "index"; + + + /** + * Attribute used to hold the item key value for maps. + */ + public final static String AT_MAP_KEY = "key"; + + + /** + * Used to prefix Java type names in an <code>xsi:type</code> attribute. + */ + public final static String JAVA_TYPE_PREFIX = "java:"; +} Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/DomUtilToo.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/DomUtilToo.java 2009-08-18 19:54:24 UTC (rev 119) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/DomUtilToo.java 2009-08-18 20:25:47 UTC (rev 120) @@ -27,7 +27,7 @@ * are not generally useful to anyone who isn't writing a converter. But if you * are writing a converter, you'll probably use them a lot ... * <p> - * Note: where methods in this class reference a namespaced element or attribute + * Note: where methods in this class reference a namespaced element or attribute * (eg, <code>xsi:type</code>), they do not use a prefix unless explicitly noted. * This prevents the possibility of collisions, where the same prefix is used * elsewhere in the DOM for elements not managed by the converter. A serializer @@ -36,44 +36,80 @@ public class DomUtilToo { /** + * Sets the <code>xsi:nil</code> attribute to the passed value. + */ + public static void setXsiNil(Element elem, boolean isNil) + { + String value = isNil ? "true" : "false"; + elem.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil", value); + } + + + /** + * Returns the value of the <code>xsi:nil</code> attribute on the passed + * element, <code>false</code> if the attribute is not set. + */ + public static boolean getXsiNil(Element elem) + { + String attr = elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil"); + return attr.equals("true"); + } + + + /** * Sets the <code>xsi:type</code> attribute to the passed value. */ public static void setXsiType(Element elem, String xsiType) { elem.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type", xsiType); } - - + + /** * Returns the value of the <code>xsi:type</code> attribute on the passed * element, <code>null</code> if the attribute is not set. */ public static String getXsiType(Element elem) - { + { String xsiType = elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type"); return StringUtils.isEmpty(xsiType) ? null : xsiType; } - - + + /** - * Sets the <code>xsi:nil</code> attribute to the passed value. + * Returns the Java classname corresponding to an <code>xsi:type</code> + * value, <code>null</code> if the attribute is missing or doesn't follow + * the pattern for Java type names. */ - public static void setXsiNil(Element elem, boolean isNil) + public static String getJavaClassFromXsiType(String xsiType) { - String value = isNil ? "true" : "false"; - elem.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil", value); + if (!xsiType.startsWith(ConversionStrings.JAVA_TYPE_PREFIX)) + return null; + return xsiType.substring(ConversionStrings.JAVA_TYPE_PREFIX.length()); } - - + + /** - * Returns the value of the <code>xsi:nil</code> attribute on the passed - * element, <code>false</code> if the attribute is not set. + * Returns the type name to be stored in <code>xsi:type</code> for a Java + * class (this is a companion to {@link #getJavaClassFromXsiType}). */ - public static boolean getXsiNil(Element elem) + public static String getXsiTypeForJavaObject(Class<?> klass) { - String attr = elem.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil"); - return attr.equals("true"); + return ConversionStrings.JAVA_TYPE_PREFIX + klass.getName(); } + + + /** + * Returns the type name to be stored in <code>xsi:type</code> for a Java + * object (this is actually used more than the alternate version). Will + * return empty string if passed <code>null</code>. + */ + public static String getXsiTypeForJavaObject(Object obj) + { + return (obj == null) + ? "" + : getXsiTypeForJavaObject(obj.getClass()); + } } Modified: 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 2009-08-18 19:54:24 UTC (rev 119) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanDriver.java 2009-08-18 20:25:47 UTC (rev 120) @@ -700,10 +700,9 @@ public void testReadOnlyBean() throws Exception { - Xml2BeanDriver driver = new Xml2BeanDriver(); Element data = createTestData( element("sval", text("foo"))); - + Xml2BeanDriver driver = new Xml2BeanDriver(); assertConversionFailure("converted bean without setter", driver, data, ReadOnlyBean.class); } 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-09-23 21:46:31
|
Revision: 154 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=154&view=rev Author: kdgregory Date: 2009-09-23 21:46:18 +0000 (Wed, 23 Sep 2009) Log Message: ----------- change child naming code for sequences (bugfix) Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlAppenders.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-09-23 21:25:10 UTC (rev 153) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-09-23 21:46:18 UTC (rev 154) @@ -157,24 +157,17 @@ extends BasicAppender { private int _index = 0; - private String _elementName = ConversionStrings.EL_COLLECTION_ITEM; public IndexedAppender(Element parent, EnumSet<Bean2XmlOptions> options) { super(parent, options); - if (options.contains(Bean2XmlOptions.SEQUENCE_NAMED_BY_PARENT)) - { - _elementName = DomUtil.getLocalName(parent); - if (_elementName.endsWith("s") || _elementName.endsWith("S")) - _elementName = _elementName.substring(0, _elementName.length() - 1); - } } @Override public Element appendValue(String name, String type, String value) { - Element child = super.appendValue(_elementName, type, value); + Element child = super.appendValue(name, type, value); if ((child != null) && isOptionSet(Bean2XmlOptions.SEQUENCE_INDEXES)) child.setAttribute(ConversionStrings.AT_ARRAY_INDEX, String.valueOf(_index++)); return child; Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java 2009-09-23 21:25:10 UTC (rev 153) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java 2009-09-23 21:46:18 UTC (rev 154) @@ -32,6 +32,7 @@ import net.sf.practicalxml.converter.internal.ConversionStrings; import net.sf.practicalxml.converter.internal.DomUtilToo; import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; +import net.sf.practicalxml.internal.StringUtils; /** @@ -77,7 +78,7 @@ { Element root = DomUtil.newDocument(nsUri, rootName); doXsiNamespaceHack(root); - convert(obj, "", new DirectAppender(root, _options)); + convert(obj, rootName, new DirectAppender(root, _options)); return root; } @@ -168,11 +169,13 @@ Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(array)); Appender childAppender = new IndexedAppender(parent, _options); + String childName = determineChildNameForSequence(name); int length = Array.getLength(array); + for (int idx = 0 ; idx < length ; idx++) { Object value = Array.get(array, idx); - convert(value, ConversionStrings.EL_COLLECTION_ITEM, childAppender); + convert(value, childName, childAppender); } return true; } @@ -200,9 +203,11 @@ Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(obj)); Appender childAppender = new IndexedAppender(parent, _options); + String childName = determineChildNameForSequence(name); + for (Object value : (Collection<?>)obj) { - convert(value, ConversionStrings.EL_COLLECTION_ITEM, childAppender); + convert(value, childName, childAppender); } return true; } @@ -250,4 +255,18 @@ else convert(value, name, appender); } + + + private String determineChildNameForSequence(String parentName) + { + if (StringUtils.isEmpty(parentName)) + return ConversionStrings.EL_COLLECTION_ITEM; + + if (!_options.contains(Bean2XmlOptions.SEQUENCE_NAMED_BY_PARENT)) + return ConversionStrings.EL_COLLECTION_ITEM; + + if (parentName.endsWith("s") || parentName.endsWith("S")) + return parentName.substring(0, parentName.length() - 1); + return parentName; + } } Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlAppenders.java 2009-09-23 21:25:10 UTC (rev 153) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlAppenders.java 2009-09-23 21:46:18 UTC (rev 154) @@ -160,8 +160,6 @@ { Element root = DomUtil.newDocument("root"); - // note: index shouldn't increment on null value, so put the - // null entry between not-null entries Appender appender = new IndexedAppender(root, useOptions()); Element child0 = appender.appendValue("foo", "bar", "baz"); Element childX = appender.appendValue("bing", "bang", null); @@ -174,10 +172,7 @@ assertNull(childX); assertNameTypeValue(child0, "foo", "", "baz"); - assertEquals("0", child0.getAttribute("index")); - assertNameTypeValue(child1, "argle", "", "wargle"); - assertEquals("1", child1.getAttribute("index")); } @@ -201,10 +196,34 @@ assertXsiNil(child2, true); assertNameTypeValue(child2, "bing", "", null); - assertEquals("2", child2.getAttribute("index")); } + public void testIndexedAppenderWithIndexOption() throws Exception + { + Element root = DomUtil.newDocument("root"); + + // note: index shouldn't increment on null value, so put the + // null entry between not-null entries + Appender appender = new IndexedAppender(root, useOptions(Bean2XmlOptions.SEQUENCE_INDEXES)); + Element child0 = appender.appendValue("foo", "bar", "baz"); + Element childX = appender.appendValue("bing", "bang", null); + Element child1 = appender.appendValue("argle", "bargle", "wargle"); + + List<Element> children = DomUtil.getChildren(root); + assertEquals(2, children.size()); + assertSame(child0, children.get(0)); + assertSame(child1, children.get(1)); + assertNull(childX); + + assertNameTypeValue(child0, "foo", "", "baz"); + assertEquals("0", child0.getAttribute("index")); + + assertNameTypeValue(child1, "argle", "", "wargle"); + assertEquals("1", child1.getAttribute("index")); + } + + public void testMapAppenderDefault() throws Exception { Element root = DomUtil.newDocument("root"); 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-11-23 13:55:14
|
Revision: 178 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=178&view=rev Author: kdgregory Date: 2009-11-23 13:55:04 +0000 (Mon, 23 Nov 2009) Log Message: ----------- Bean2XmlOptions: rename INTROSPECT_MAPS to MAP_KEYS_AS_ELEMENT_NAME Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlAppenders.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBeanConverter.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-10-28 18:29:33 UTC (rev 177) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-11-23 13:55:04 UTC (rev 178) @@ -200,7 +200,7 @@ public Element appendValue(String name, Class<?> klass, String value) { Element child = null; - if (isOptionSet(Bean2XmlOptions.INTROSPECT_MAPS)) + if (isOptionSet(Bean2XmlOptions.MAP_KEYS_AS_ELEMENT_NAME)) { child = super.appendValue(name, klass, value); } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java 2009-10-28 18:29:33 UTC (rev 177) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java 2009-11-23 13:55:04 UTC (rev 178) @@ -134,7 +134,7 @@ // I think it's more clear to express the rules this way, rather than // as an if-condition with nested sub-conditions boolean addCnvNS = _options.contains(Bean2XmlOptions.USE_INDEX_ATTR); - addCnvNS |= !_options.contains(Bean2XmlOptions.INTROSPECT_MAPS); + addCnvNS |= !_options.contains(Bean2XmlOptions.MAP_KEYS_AS_ELEMENT_NAME); addCnvNS &= _options.contains(Bean2XmlOptions.USE_TYPE_ATTR); if (addCnvNS) { Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java 2009-10-28 18:29:33 UTC (rev 177) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java 2009-11-23 13:55:04 UTC (rev 178) @@ -35,7 +35,7 @@ * 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, + MAP_KEYS_AS_ELEMENT_NAME, /** * If the value is <code>null</code>, add an element containing a single Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlAppenders.java 2009-10-28 18:29:33 UTC (rev 177) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlAppenders.java 2009-11-23 13:55:04 UTC (rev 178) @@ -275,7 +275,7 @@ { Element root = DomUtil.newDocument("root"); - Appender appender = new MapAppender(root, useOptions(Bean2XmlOptions.INTROSPECT_MAPS)); + Appender appender = new MapAppender(root, useOptions(Bean2XmlOptions.MAP_KEYS_AS_ELEMENT_NAME)); Element child0 = appender.appendValue("foo", String.class, "baz"); Element child1 = appender.appendValue("argle", String.class, "wargle"); Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlConverter.java 2009-10-28 18:29:33 UTC (rev 177) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlConverter.java 2009-11-23 13:55:04 UTC (rev 178) @@ -525,7 +525,7 @@ public void testConvertMapIntrospectWithXsiType() throws Exception { - Bean2XmlConverter driver = new Bean2XmlConverter(Bean2XmlOptions.INTROSPECT_MAPS, Bean2XmlOptions.USE_TYPE_ATTR); + Bean2XmlConverter driver = new Bean2XmlConverter(Bean2XmlOptions.MAP_KEYS_AS_ELEMENT_NAME, Bean2XmlOptions.USE_TYPE_ATTR); // TreeMap means that the data will be re-ordered Map<String,Integer> data = new TreeMap<String,Integer>(); @@ -550,7 +550,7 @@ public void testFailMapIntrospectWithInvalidKey() throws Exception { - Bean2XmlConverter driver = new Bean2XmlConverter(Bean2XmlOptions.INTROSPECT_MAPS); + Bean2XmlConverter driver = new Bean2XmlConverter(Bean2XmlOptions.MAP_KEYS_AS_ELEMENT_NAME); Map<String,Integer> data = new TreeMap<String,Integer>(); data.put("%key1%", new Integer(123)); Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBeanConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBeanConverter.java 2009-10-28 18:29:33 UTC (rev 177) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBeanConverter.java 2009-11-23 13:55:04 UTC (rev 178) @@ -274,7 +274,7 @@ data.put("bar", "bargle"); data.put("baz", "bazgle"); - Document dom = BeanConverter.convertToXml(data, "test", Bean2XmlOptions.INTROSPECT_MAPS); + Document dom = BeanConverter.convertToXml(data, "test", Bean2XmlOptions.MAP_KEYS_AS_ELEMENT_NAME); // System.out.println(OutputUtil.compactString(dom)); DomAsserts.assertCount(0, dom, "/test/data"); 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-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-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-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-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-17 17:26:48
|
Revision: 110 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=110&view=rev Author: kdgregory Date: 2009-08-17 17:26:40 +0000 (Mon, 17 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/TestXml2BeanDriver.java Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanOptions.java Removed Paths: ------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean2xml/ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/xml2bean/ 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-17 17:13:36 UTC (rev 109) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java 2009-08-17 17:26:40 UTC (rev 110) @@ -16,8 +16,8 @@ import org.w3c.dom.Document; -import net.sf.practicalxml.converter.bean2xml.Bean2XmlDriver; -import net.sf.practicalxml.converter.bean2xml.Bean2XmlOptions; +import net.sf.practicalxml.converter.bean.Bean2XmlDriver; +import net.sf.practicalxml.converter.bean.Bean2XmlOptions; /** Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java (from rev 100, 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/bean/Bean2XmlAppenders.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-08-17 17:26:40 UTC (rev 110) @@ -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.bean; + +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/bean/Bean2XmlDriver.java (from rev 108, 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/bean/Bean2XmlDriver.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-08-17 17:26:40 UTC (rev 110) @@ -0,0 +1,259 @@ +// 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.bean; + +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 javax.xml.XMLConstants; + +import org.w3c.dom.Element; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.converter.ConversionException; +import net.sf.practicalxml.converter.ConversionHelper; +import net.sf.practicalxml.converter.bean.Bean2XmlAppenders.*; + + +/** + * 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); + + public Bean2XmlDriver(Bean2XmlOptions... options) + { + for (Bean2XmlOptions option : options) + _options.add(option); + _helper = new ConversionHelper(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); + doXsiNamespaceHack(root); + 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(); + } + + + /** + * 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) + { + if (obj != null) + klass = obj.getClass(); + + String objType = _helper.getXsdType(klass); + if ((obj == null) || (objType != null)) + { + appender.appendValue(name, objType, _helper.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/bean/Bean2XmlOptions.java (from rev 100, 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/bean/Bean2XmlOptions.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java 2009-08-17 17:26:40 UTC (rev 110) @@ -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.bean; + + +/** + * 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/bean/Xml2BeanDriver.java (from rev 109, 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/bean/Xml2BeanDriver.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-08-17 17:26:40 UTC (rev 110) @@ -0,0 +1,447 @@ +// 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.bean; + +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. 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 +//---------------------------------------------------------------------------- + + /** + * Attempts to convert the passed DOM subtree into an object of the + * specified class. + */ + 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); + } + } +} Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java (from rev 105, 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/bean/Xml2BeanHandler.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java 2009-08-17 17:26:40 UTC (rev 110) @@ -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.bean; + +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.ConversionHelper; +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 ConversionHelper _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 ConversionHelper(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/bean/Xml2BeanOptions.java (from rev 109, 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/bean/Xml2BeanOptions.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanOptions.java 2009-08-17 17:26:40 UTC (rev 110) @@ -0,0 +1,69 @@ +// 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.bean; + + + +/** + * 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, + + + /** + * Expect data (in particular, dates) to be formatted per XML Schema spec. + */ + EXPECT_XSD_FORMAT, + + + /** + * If present, the converter ignores elements that don't correspond to + * settable properties of the bean. + */ + IGNORE_MISSING_PROPERTIES, + + + /** + * 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, + + + /** + * 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 +} Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlAppenders.java 2009-08-17 17:13:36 UTC (rev 109) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlAppenders.java 2009-08-17 17:26:40 UTC (rev 110) @@ -20,9 +20,9 @@ import org.w3c.dom.Element; import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.converter.bean2xml.Bean2XmlOptions; +import net.sf.practicalxml.converter.bean.Bean2XmlOptions; -import static net.sf.practicalxml.converter.bean2xml.Bean2XmlAppenders.*; +import static net.sf.practicalxml.converter.bean.Bean2XmlAppenders.*; public class TestBean2XmlAppenders Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java 2009-08-17 17:13:36 UTC (rev 109) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java 2009-08-17 17:26:40 UTC (rev 110) @@ -27,8 +27,8 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.OutputUtil; -import net.sf.practicalxml.converter.bean2xml.Bean2XmlDriver; -import net.sf.practicalxml.converter.bean2xml.Bean2XmlOptions; +import net.sf.practicalxml.converter.bean.Bean2XmlDriver; +import net.sf.practicalxml.converter.bean.Bean2XmlOptions; import net.sf.practicalxml.junit.DomAsserts; 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-08-17 17:13:36 UTC (rev 109) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java 2009-08-17 17:26:40 UTC (rev 110) @@ -16,7 +16,7 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.OutputUtil; -import net.sf.practicalxml.converter.bean2xml.Bean2XmlOptions; +import net.sf.practicalxml.converter.bean.Bean2XmlOptions; import javax.xml.XMLConstants; import org.w3c.dom.Document; Modified: 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 2009-08-17 17:13:36 UTC (rev 109) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanDriver.java 2009-08-17 17:26:40 UTC (rev 110) @@ -27,8 +27,8 @@ import javax.xml.XMLConstants; import org.w3c.dom.Element; -import net.sf.practicalxml.converter.xml2bean.Xml2BeanDriver; -import net.sf.practicalxml.converter.xml2bean.Xml2BeanOptions; +import net.sf.practicalxml.converter.bean.Xml2BeanDriver; +import net.sf.practicalxml.converter.bean.Xml2BeanOptions; import static net.sf.practicalxml.builder.XmlBuilder.*; 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-18 17:41:55
|
Revision: 112 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=112&view=rev Author: kdgregory Date: 2009-08-18 17:41:38 +0000 (Tue, 18 Aug 2009) Log Message: ----------- change BeanConverter API, write tests for it 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/AbstractBeanConverterTestCase.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.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-08-17 20:24:04 UTC (rev 111) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java 2009-08-18 17:41:38 UTC (rev 112) @@ -19,6 +19,8 @@ import net.sf.practicalxml.converter.bean.Bean2XmlDriver; import net.sf.practicalxml.converter.bean.Bean2XmlOptions; +import net.sf.practicalxml.converter.bean.Xml2BeanDriver; +import net.sf.practicalxml.converter.bean.Xml2BeanOptions; /** @@ -26,11 +28,13 @@ * Originally developed to support simple web services, without the overhead * (schema definitions and/or annotations) required by JAXB. * <p> - * A single instance handles conversions in one direction only. This is a - * consequence of specifying options as varargs, but fits well with "normal" - * application design, in which input and output are distinct operations. - * Note that, depending on options settings, output generated by one converter - * may not be valid input for another. + * A single instance is intended for conversions in one direction only. This + * is a consequence of specifying options as varargs, but fits well with + * "normal" application design, in which input and output are handled by + * different parts of the code. You won't get an error if you try to convert + * the "wrong" way, you'll simply get default behavior. Note that, depending + * on options settings, output generated by one converter may not be valid + * input for another. * <p> * The basic structure of XML produced/consumed by this class is that the * root element represents the object being converted, and its child nodes @@ -140,15 +144,50 @@ * </table> * * <strong>Warning</strong>: - * this class makes use of <code>java.beans.Introspector</code>, which holds - * a cache of introspected objects. If you use this converter in an app-server - * you should call <code>Introspector.flushCaches()</code> during deploy. + * <code>java.beans.Introspector</code> holds a cache of introspected objects. + * If you use this converter in an app-server you should call + * <code>Introspector.flushCaches()</code> during deploy. */ public class BeanConverter { + private Bean2XmlDriver _outputDriver; + private Xml2BeanDriver _inputDriver; + + /** + * Creates an instance with all options disabled. This can be used for + * conversions in either direction, and is meant primarily for testing. + */ + public BeanConverter() + { + _outputDriver = new Bean2XmlDriver(); + _inputDriver = new Xml2BeanDriver(); + } + + + /** + * Creates an instance to be used for Bean -> XML conversion, + */ + public BeanConverter(Bean2XmlOptions... options) + { + _outputDriver = new Bean2XmlDriver(options); + _inputDriver = new Xml2BeanDriver(); + } + + /** + * Creates an instance to be used for XML -> Bean conversion. + */ + public BeanConverter(Xml2BeanOptions options) + { + _outputDriver = new Bean2XmlDriver(); + _inputDriver = new Xml2BeanDriver(options); + } + + + /** * Creates a new DOM document from the passed bean, in which all elements - * are members of the specified namespace. + * are members of the specified namespace and will inherit the root's + * prefix (if any). * * @param bean The source object. This can be any Java object: * bean, collection, or simple type. @@ -157,14 +196,10 @@ * @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, - Bean2XmlOptions... options) + public Element convertToXml(Object bean, String nsUri, String rootName) { - Bean2XmlDriver driver = new Bean2XmlDriver(options); - return driver.convert(bean, nsUri, rootName).getOwnerDocument(); + return _outputDriver.convert(bean, nsUri, rootName); } @@ -175,13 +210,37 @@ * 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, - Bean2XmlOptions... options) + public Element convertToXml(Object bean, String rootName) { - Bean2XmlDriver driver = new Bean2XmlDriver(options); - return driver.convert(bean, rootName).getOwnerDocument(); + return _outputDriver.convert(bean, rootName); } + + + /** + * Creates a new Java object from the root of the passed <code>Document + * </code>. + * + * @param dom The source document. + * @param klass The desired class to instantiate and fill from this + * document. + */ + public <T> T convertToJava(Document dom, Class<T> klass) + { + return convertToJava(dom.getDocumentElement(), klass); + } + + + /** + * Creates a new Java object from the the passed DOM <code>Element</code>. + * + * @param dom The source element -- this may or may not be the + * root element of its document. + * @param klass The desired class to instantiate and fill from this + * document. + */ + public <T> T convertToJava(Element root, Class<T> klass) + { + return _inputDriver.convert(root, klass); + } } 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-17 20:24:04 UTC (rev 111) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/AbstractBeanConverterTestCase.java 2009-08-18 17:41:38 UTC (rev 112) @@ -16,6 +16,7 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Arrays; import java.util.Date; import java.util.List; @@ -23,6 +24,8 @@ import org.w3c.dom.Element; +import junit.framework.Assert; + import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; @@ -120,6 +123,15 @@ public boolean isBval() { return _bval; } public void setBval(boolean bval) { _bval = bval; } + + public void assertEquals(SimpleBean that) + { + assertNotNull(that); + Assert.assertEquals("sval", _sval, that._sval); + Assert.assertEquals("ival", _ival, that._ival); + Assert.assertEquals("dval", _dval, that._dval); + Assert.assertEquals("bval", _bval, that._bval); + } } @@ -150,6 +162,13 @@ public List<String> getStringList() { return _stringList; } public void setStringList(List<String> list) { _stringList = list; } + + public void assertEquals(CompoundBean that) + { + _simple.assertEquals(that._simple); + Assert.assertTrue("primArray", Arrays.equals(_primArray, that._primArray)); + Assert.assertEquals("stringlist", _stringList, that._stringList); + } } 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-08-17 20:24:04 UTC (rev 111) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java 2009-08-18 17:41:38 UTC (rev 112) @@ -14,19 +14,34 @@ package net.sf.practicalxml.converter; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.OutputUtil; import net.sf.practicalxml.converter.bean.Bean2XmlOptions; +import net.sf.practicalxml.converter.bean.Xml2BeanOptions; +import net.sf.practicalxml.junit.DomAsserts; -import javax.xml.XMLConstants; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - /** - * Tests for the top-level <code>BeanConverter</code> methods. These are - * minimal; the detailed testing happens in {@link TestBean2XmlDriver} - * and {@link TestXml2BeanDriver}. + * Tests for the top-level <code>BeanConverter</code> methods. These are all + * "out and back" tests to verify that we can read the XML that we produce + * (and to show cases where we can't). Detailed tests (verifying specific + * output) are in {@link TestBean2XmlDriver} and {@link TestXml2BeanDriver}. + * <p> + * Note that each conversion has a commented-out line that will print the + * generated XML. Uncommenting these lines may help you understand how + * conversion works in particular cases. */ public class TestBeanConverter extends AbstractBeanConverterTestCase @@ -41,41 +56,368 @@ // Support Code //---------------------------------------------------------------------------- + private static void assertConversionError( + String message, BeanConverter converter, Element elem, Class<?> klass) + { + try + { + converter.convertToJava(elem, klass); + fail(message); + } + catch (ConversionException ee) + { /* success */ } + } //---------------------------------------------------------------------------- // Test Cases //---------------------------------------------------------------------------- - public void testGenerateSimpleContentSansNamespace() throws Exception + // an initial test to verify everything works + public void testConvertStringDefault() throws Exception { - String rootName = "argle"; - String value = "foo"; + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); - Document dom = BeanConverter.generateXml(value, rootName, Bean2XmlOptions.XSI_TYPE); -// System.out.println(OutputUtil.compactString(dom)); + String obj = "this is a test"; + Element root = outConverter.convertToXml(obj, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + assertEquals("test", DomUtil.getLocalName(root)); - 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()); + Object result = inConverter.convertToJava(root, String.class); + assertEquals(obj, result); } - public void testGenerateSimpleContentWithNamespace() throws Exception + public void testConvertPrimitiveDefault() throws Exception { - String nsUri = "urn:fibble"; - String rootName = "argle:bargle"; - String value = "foo"; + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); - Document dom = BeanConverter.generateXml(value, nsUri, rootName, Bean2XmlOptions.XSI_TYPE); + for (PrimitiveValue value : PRIMITIVE_VALUES) + { + Object obj = value.getValue(); + Element root = outConverter.convertToXml(obj, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Object result = inConverter.convertToJava(root, value.getKlass()); + assertEquals(obj, result); + } + } + + + public void testConvertPrimitiveFormatXsd() throws Exception + { + BeanConverter outConverter = new BeanConverter(Bean2XmlOptions.XSD_FORMAT); + BeanConverter inConverter = new BeanConverter(Xml2BeanOptions.EXPECT_XSD_FORMAT); + + for (PrimitiveValue value : PRIMITIVE_VALUES) + { + Object obj = value.getValue(); + Element root = outConverter.convertToXml(obj, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Object result = inConverter.convertToJava(root, value.getKlass()); + assertEquals(obj, result); + } + } + + + public void testConvertNullDefault() throws Exception + { + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(null, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Object result = inConverter.convertToJava(root, String.class); + assertNull(result); + } + + + public void testConvertNullUseAndRequireXsiNil() throws Exception + { + BeanConverter outConverter = new BeanConverter(Bean2XmlOptions.XSI_NIL); + BeanConverter inConverter = new BeanConverter(Xml2BeanOptions.REQUIRE_XSI_NIL); + + Element root = outConverter.convertToXml(null, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Object result = inConverter.convertToJava(root, String.class); + assertNull(result); + } + + + public void testConvertNullFailureRequireXsiNil() throws Exception + { + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(Xml2BeanOptions.REQUIRE_XSI_NIL); + + Element root = outConverter.convertToXml(null, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + assertConversionError("accepted DOM with null entry but no xsi:nil", + inConverter, root, String.class); + } + + + public void testPrimitiveArrayDefault() throws Exception + { + int[] data = new int[] { 1, 2, 4, 5 }; + + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + int[] result = inConverter.convertToJava(root, int[].class); + assertTrue(Arrays.equals(data, result)); + } + + + public void testStringListDefault() throws Exception + { + List<String> data = Arrays.asList("foo", "bar", "baz"); + + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + List<String> result = inConverter.convertToJava(root, List.class); + assertEquals(data, result); + } + + + // demonstrates that the list will be read as List<String> even though + // it was written as List<Integer> + public void testIntegerListDefault() throws Exception + { + List<Integer> data = Arrays.asList(1, 2, 3); + assert(data.get(0) instanceof Integer); + + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + List<?> result = inConverter.convertToJava(root, List.class); + assertTrue(result instanceof List); + assertTrue(result.get(0) instanceof String); + } + + + // demonstrates that you don't need to require xsi:type to use it + public void testIntegerListWithXsiType() throws Exception + { + List<Integer> data = Arrays.asList(1, 2, 3); + assert(data.get(0) instanceof Integer); + + BeanConverter outConverter = new BeanConverter(Bean2XmlOptions.XSI_TYPE); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + List<?> result = inConverter.convertToJava(root, List.class); + assertEquals(data, result); + } + + + // demonstrates that the caller drives the inbound conversion + public void testConvertListToSortedSet() throws Exception + { + List<String> data = Arrays.asList("foo", "bar", "baz", "bar"); + + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Set<?> result = inConverter.convertToJava(root, SortedSet.class); + Iterator<?> itx = result.iterator(); + assertEquals("bar", itx.next()); + assertEquals("baz", itx.next()); + assertEquals("foo", itx.next()); + assertFalse(itx.hasNext()); + } + + + public void testMapDefault() throws Exception + { + Map<String,String> data = new HashMap<String,String>(); + data.put("foo", "argle"); + data.put("bar", "bargle"); + data.put("baz", "bazgle"); + + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + DomAsserts.assertCount(3, root, "/test/data"); + DomAsserts.assertCount(1, root, "/test/data[@key='foo']"); + DomAsserts.assertEquals("argle", root, "/test/data[@key='foo']"); + + Map<?,?> result = inConverter.convertToJava(root, Map.class); + assertEquals(data, result); + } + + + // demonstrates that the input converter handles either format by default + public void testMapIntrospected() throws Exception + { + Map<String,String> data = new HashMap<String,String>(); + data.put("foo", "argle"); + data.put("bar", "bargle"); + data.put("baz", "bazgle"); + + BeanConverter outConverter = new BeanConverter(Bean2XmlOptions.INTROSPECT_MAPS); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + DomAsserts.assertCount(0, root, "/test/data"); + DomAsserts.assertCount(1, root, "/test/foo"); + DomAsserts.assertEquals("argle", root, "/test/foo"); + + Map<?,?> result = inConverter.convertToJava(root, Map.class); + assertEquals(data, result); + } + + + public void testSimpleBeanDefault() throws Exception + { + SimpleBean data = new SimpleBean("abc", 123, new BigDecimal("456.789"), true); + + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + SimpleBean result = inConverter.convertToJava(root, SimpleBean.class); + data.assertEquals(result); + } + + + public void testSimpleBeanRequireXsiType() throws Exception + { + SimpleBean data = new SimpleBean("abc", 123, new BigDecimal("456.789"), true); + + BeanConverter outconverter1 = new BeanConverter(Bean2XmlOptions.XSI_TYPE); + BeanConverter outconverter2 = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(Xml2BeanOptions.REQUIRE_XSI_TYPE); + + Element valid = outconverter1.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(valid.getOwnerDocument())); + + SimpleBean result = inConverter.convertToJava(valid, SimpleBean.class); + data.assertEquals(result); + + Element invalid = outconverter2.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(invalid.getOwnerDocument())); + + assertConversionError("converter requiring xsi:type accepted XML without", + inConverter, invalid, SimpleBean.class); + } + + + public void testListOfSimpleBeanWithXsiTypeAndNulls() throws Exception + { + SimpleBean bean1 = new SimpleBean("abc", 123, new BigDecimal("456.789"), true); + SimpleBean bean2 = new SimpleBean("zyx", 987, null, false); + List<SimpleBean> data = Arrays.asList(bean1, bean2); + + BeanConverter outConverter = new BeanConverter(Bean2XmlOptions.XSI_TYPE); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + List<SimpleBean> result = inConverter.convertToJava(root, List.class); + assertEquals(2, result.size()); + bean1.assertEquals(result.get(0)); + bean2.assertEquals(result.get(1)); + } + + + // another demonstration that caller drives input conversion + // ... and note that we don't care about xsi:type in this case + public void testListOfSimpleBeanToArrayOfSame() throws Exception + { + SimpleBean bean1 = new SimpleBean("abc", 123, new BigDecimal("456.789"), true); + SimpleBean bean2 = new SimpleBean("zyx", 987, null, false); + List<SimpleBean> data = Arrays.asList(bean1, bean2); + + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + SimpleBean[] result = inConverter.convertToJava(root, SimpleBean[].class); + assertEquals(2, result.length); + bean1.assertEquals(result[0]); + bean2.assertEquals(result[1]); + } + + + public void testCompoundBeanDefault() throws Exception + { + CompoundBean data = new CompoundBean( + new SimpleBean("abc", 123, new BigDecimal("456.789"), true), + new int[] { 1, 5, 7, 9 }, + Arrays.asList("foo", "bar", "baz")); + + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + CompoundBean result = inConverter.convertToJava(root, CompoundBean.class); + data.assertEquals(result); + } + + + public void testSimpleBeanWithNamespace() throws Exception + { + SimpleBean data = new SimpleBean("abc", 123, new BigDecimal("456.789"), true); + + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Element root = outConverter.convertToXml(data, "urn:foo", "bar:test"); +// System.out.println(OutputUtil.compactString(root.getOwnerDocument())); + + Element child = DomUtil.getChild(root, "sval"); + assertEquals("urn:foo", child.getNamespaceURI()); + assertEquals("bar", child.getPrefix()); + + SimpleBean result = inConverter.convertToJava(root, SimpleBean.class); + data.assertEquals(result); + } + + + // this one is here just for coverage + public void testSimpleBeanFromDocument() throws Exception + { + SimpleBean data = new SimpleBean("abc", 123, new BigDecimal("456.789"), true); + + BeanConverter outConverter = new BeanConverter(); + BeanConverter inConverter = new BeanConverter(); + + Document dom = outConverter.convertToXml(data, "test").getOwnerDocument(); // 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()); + SimpleBean result = inConverter.convertToJava(dom, SimpleBean.class); + data.assertEquals(result); } } \ No newline at end of file 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-18 17:57:56
|
Revision: 113 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=113&view=rev Author: kdgregory Date: 2009-08-18 17:57:48 +0000 (Tue, 18 Aug 2009) Log Message: ----------- create internals package, put ConversionHelper in it Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java Removed Paths: ------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java Deleted: 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-18 17:41:38 UTC (rev 112) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java 2009-08-18 17:57:48 UTC (rev 113) @@ -1,479 +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 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<?>>(); - { - _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()); - - _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; - - - /** - * 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(); - } - - - /** - * 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); - } - - - /** - * 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/bean/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-08-18 17:41:38 UTC (rev 112) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-08-18 17:57:48 UTC (rev 113) @@ -30,8 +30,8 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.converter.ConversionException; -import net.sf.practicalxml.converter.ConversionHelper; import net.sf.practicalxml.converter.bean.Bean2XmlAppenders.*; +import net.sf.practicalxml.converter.internal.ConversionHelper; /** Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-08-18 17:41:38 UTC (rev 112) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-08-18 17:57:48 UTC (rev 113) @@ -37,7 +37,7 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.converter.ConversionException; -import net.sf.practicalxml.converter.ConversionHelper; +import net.sf.practicalxml.converter.internal.ConversionHelper; import net.sf.practicalxml.internal.StringUtils; import org.w3c.dom.Element; Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java 2009-08-18 17:41:38 UTC (rev 112) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java 2009-08-18 17:57:48 UTC (rev 113) @@ -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.ConversionHelper; +import net.sf.practicalxml.converter.internal.ConversionHelper; import net.sf.practicalxml.internal.StringUtils; import java.util.Arrays; Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java (from rev 110, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionHelper.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java 2009-08-18 17:57:48 UTC (rev 113) @@ -0,0 +1,480 @@ +// 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.internal; + +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; +import net.sf.practicalxml.converter.ConversionException; + + +/** + * Responsible for converting "primitive" types -- those with unambiguous + * string representations -- to/from such a representation. + */ +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<?>>(); + { + _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()); + + _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; + + + /** + * 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(); + } + + + /** + * 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); + } + + + /** + * 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/test/java/net/sf/practicalxml/converter/TestConversionHelper.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java 2009-08-18 17:41:38 UTC (rev 112) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java 2009-08-18 17:57:48 UTC (rev 113) @@ -18,7 +18,9 @@ import java.math.BigInteger; import java.util.Date; +import net.sf.practicalxml.converter.internal.ConversionHelper; + public class TestConversionHelper extends AbstractBeanConverterTestCase { 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-18 18:10:51
|
Revision: 114 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=114&view=rev Author: kdgregory Date: 2009-08-18 18:10:39 +0000 (Tue, 18 Aug 2009) Log Message: ----------- rename conversion options; remove unused options Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanOptions.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 Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-08-18 17:57:48 UTC (rev 113) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-08-18 18:10:39 UTC (rev 114) @@ -93,7 +93,7 @@ protected void setType(Element elem, String type) { - if (isOptionSet(Bean2XmlOptions.XSI_TYPE)) + if (isOptionSet(Bean2XmlOptions.ADD_XSI_TYPE)) elem.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type", type); } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-08-18 17:57:48 UTC (rev 113) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-08-18 18:10:39 UTC (rev 114) @@ -114,7 +114,7 @@ private boolean shouldUseXsdFormatting() { return _options.contains(Bean2XmlOptions.XSD_FORMAT) - || _options.contains(Bean2XmlOptions.XSI_TYPE); + || _options.contains(Bean2XmlOptions.ADD_XSI_TYPE); } @@ -143,7 +143,7 @@ private void doXsiNamespaceHack(Element root) { if (_options.contains(Bean2XmlOptions.XSI_NIL) - && !_options.contains(Bean2XmlOptions.XSI_TYPE)) + && !_options.contains(Bean2XmlOptions.ADD_XSI_TYPE)) { root.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil", "false"); } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java 2009-08-18 17:57:48 UTC (rev 113) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java 2009-08-18 18:10:39 UTC (rev 114) @@ -17,39 +17,40 @@ /** * Options used by {@link Bean2XmlHandler} to control the structure of the - * DOM tree. + * generated 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. + * "<code>xsd:TYPE</code>", where TYPE is the XSD type name. For complex + * types, this attribute's value will be "<code>java:TYPE</code>", where + * TYPE is the fully-qualified classname. * <p> * <em>This option implies {@link #XSD_FORMAT} for simple types</em>. */ - XSI_TYPE, + ADD_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. + * 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, + + /** + * Add null values into the tree, as an element without content, with the + * attribute <code>xsi:nil</code> set to "true". 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. + * 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. */ - INTROSPECT_MAPS + XSD_FORMAT } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanOptions.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanOptions.java 2009-08-18 17:57:48 UTC (rev 113) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanOptions.java 2009-08-18 18:10:39 UTC (rev 114) @@ -29,13 +29,11 @@ */ CONVERT_BLANK_AS_NULL, - /** * Expect data (in particular, dates) to be formatted per XML Schema spec. */ EXPECT_XSD_FORMAT, - /** * If present, the converter ignores elements that don't correspond to * settable properties of the bean. @@ -44,21 +42,12 @@ /** - * 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, - - - /** * 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 Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlAppenders.java 2009-08-18 17:57:48 UTC (rev 113) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlAppenders.java 2009-08-18 18:10:39 UTC (rev 114) @@ -72,7 +72,7 @@ { Element root = DomUtil.newDocument("root"); - Appender appender = new BasicAppender(root, useOptions(Bean2XmlOptions.XSI_TYPE)); + Appender appender = new BasicAppender(root, useOptions(Bean2XmlOptions.ADD_XSI_TYPE)); Element child = appender.appendValue("foo", "bar", "baz"); assertNull(child.getNamespaceURI()); @@ -128,7 +128,7 @@ { Element root = DomUtil.newDocument("root"); - Appender appender = new BasicAppender(root, useOptions(Bean2XmlOptions.XSI_TYPE)); + Appender appender = new BasicAppender(root, useOptions(Bean2XmlOptions.ADD_XSI_TYPE)); Element child0 = appender.appendContainer("foo", "bar"); Element child1 = appender.appendContainer("argle", "bargle"); @@ -297,7 +297,7 @@ { Element root = DomUtil.newDocument("root"); - Appender appender = new DirectAppender(root, useOptions(Bean2XmlOptions.XSI_TYPE)); + Appender appender = new DirectAppender(root, useOptions(Bean2XmlOptions.ADD_XSI_TYPE)); Element child = appender.appendValue("foo", "bar", "baz"); assertSame(root, child); @@ -361,7 +361,7 @@ { Element root = DomUtil.newDocument("root"); - Appender appender = new DirectAppender(root, useOptions(Bean2XmlOptions.XSI_TYPE)); + Appender appender = new DirectAppender(root, useOptions(Bean2XmlOptions.ADD_XSI_TYPE)); Element child0 = appender.appendContainer("foo", "bar"); assertSame(root, child0); Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java 2009-08-18 17:57:48 UTC (rev 113) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java 2009-08-18 18:10:39 UTC (rev 114) @@ -128,7 +128,7 @@ public void testConvertPrimitivesWithXsiType() throws Exception { - Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.XSI_TYPE); + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.ADD_XSI_TYPE); for (int idx = 0 ; idx < PRIMITIVE_VALUES.length ; idx++) { PrimitiveValue value = PRIMITIVE_VALUES[idx]; @@ -164,7 +164,7 @@ public void testConvertPrimitiveArrayWithXsiType() throws Exception { - Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.XSI_TYPE); + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.ADD_XSI_TYPE); int[] data = new int[] { 1, 2, 3 }; Element root = driver.convert(data, "test"); @@ -188,7 +188,7 @@ public void testConvertStringArrayWithXsiType() throws Exception { - Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.XSI_TYPE); + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.ADD_XSI_TYPE); String[] data = new String[] { "foo", "bar", "baz" }; Element root = driver.convert(data, "test"); @@ -212,7 +212,7 @@ public void testConvertStringListWithXsiType() throws Exception { - Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.XSI_TYPE); + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.ADD_XSI_TYPE); List<String> data = new ArrayList<String>(); data.add("foo"); @@ -240,7 +240,7 @@ public void testConvertStringSetWithXsiType() throws Exception { - Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.XSI_TYPE); + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.ADD_XSI_TYPE); // TreeSet means that the data will be re-ordered Set<String> data = new TreeSet<String>(); @@ -269,7 +269,7 @@ public void testConvertMapDefaultWithXsiType() throws Exception { - Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.XSI_TYPE); + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.ADD_XSI_TYPE); // TreeMap means that the data will be re-ordered Map<String,Integer> data = new TreeMap<String,Integer>(); @@ -294,7 +294,7 @@ public void testConvertMapIntrospectWithXsiType() throws Exception { - Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.INTROSPECT_MAPS, Bean2XmlOptions.XSI_TYPE); + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.INTROSPECT_MAPS, Bean2XmlOptions.ADD_XSI_TYPE); // TreeMap means that the data will be re-ordered Map<String,Integer> data = new TreeMap<String,Integer>(); @@ -335,7 +335,7 @@ public void testConvertSimpleBeanWithXsiType() throws Exception { - Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.XSI_TYPE); + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.ADD_XSI_TYPE); SimpleBean bean = new SimpleBean("zippy", 123, new BigDecimal("456.78"), true); Element root = driver.convert(bean, "test"); @@ -383,7 +383,7 @@ public void testConvertSimpleBeanXsiNilAndXsiType() throws Exception { - Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.XSI_NIL, Bean2XmlOptions.XSI_TYPE); + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.XSI_NIL, Bean2XmlOptions.ADD_XSI_TYPE); SimpleBean bean = new SimpleBean(null, 123, null, true); Element root = driver.convert(bean, "test"); @@ -399,7 +399,7 @@ public void testConvertBeanArrayWithXsiType() throws Exception { - Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.XSI_TYPE); + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.ADD_XSI_TYPE); SimpleBean bean1 = new SimpleBean("foo", 123, new BigDecimal("456.789"), true); SimpleBean bean2 = new SimpleBean("bar", 456, new BigDecimal("0.0"), false); 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-08-18 17:57:48 UTC (rev 113) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java 2009-08-18 18:10:39 UTC (rev 114) @@ -216,7 +216,7 @@ List<Integer> data = Arrays.asList(1, 2, 3); assert(data.get(0) instanceof Integer); - BeanConverter outConverter = new BeanConverter(Bean2XmlOptions.XSI_TYPE); + BeanConverter outConverter = new BeanConverter(Bean2XmlOptions.ADD_XSI_TYPE); BeanConverter inConverter = new BeanConverter(); Element root = outConverter.convertToXml(data, "test"); @@ -311,7 +311,7 @@ { SimpleBean data = new SimpleBean("abc", 123, new BigDecimal("456.789"), true); - BeanConverter outconverter1 = new BeanConverter(Bean2XmlOptions.XSI_TYPE); + BeanConverter outconverter1 = new BeanConverter(Bean2XmlOptions.ADD_XSI_TYPE); BeanConverter outconverter2 = new BeanConverter(); BeanConverter inConverter = new BeanConverter(Xml2BeanOptions.REQUIRE_XSI_TYPE); @@ -335,7 +335,7 @@ SimpleBean bean2 = new SimpleBean("zyx", 987, null, false); List<SimpleBean> data = Arrays.asList(bean1, bean2); - BeanConverter outConverter = new BeanConverter(Bean2XmlOptions.XSI_TYPE); + BeanConverter outConverter = new BeanConverter(Bean2XmlOptions.ADD_XSI_TYPE); BeanConverter inConverter = new BeanConverter(); Element root = outConverter.convertToXml(data, "test"); 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-18 18:19:58
|
Revision: 115 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=115&view=rev Author: kdgregory Date: 2009-08-18 18:19:50 +0000 (Tue, 18 Aug 2009) Log Message: ----------- bugfix: introspected maps with invalid keys Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-08-18 18:10:39 UTC (rev 114) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-08-18 18:19:50 UTC (rev 115) @@ -21,6 +21,7 @@ import org.w3c.dom.Element; import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.converter.ConversionException; /** @@ -126,10 +127,17 @@ if (shouldSkip(value)) return null; - Element child = DomUtil.appendChildInheritNamespace(_parent, name); - setType(child, type); - setValue(child, value); - return child; + try + { + Element child = DomUtil.appendChildInheritNamespace(_parent, name); + setType(child, type); + setValue(child, value); + return child; + } + catch (Exception ee) + { + throw new ConversionException("unable to append child", ee); + } } public Element appendContainer(String name, String type) Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java 2009-08-18 18:10:39 UTC (rev 114) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java 2009-08-18 18:19:50 UTC (rev 115) @@ -66,6 +66,18 @@ } + private void assertConversionException(String message, Bean2XmlDriver driver, Object data) + { + try + { + driver.convert(data, "test"); + fail(message); + } + catch (ConversionException ee) + { /* success */ } + } + + //---------------------------------------------------------------------------- // Test Cases //---------------------------------------------------------------------------- @@ -317,6 +329,19 @@ } + public void testConvertMapIntrospectWithInvalidKey() throws Exception + { + Bean2XmlDriver driver = new Bean2XmlDriver(Bean2XmlOptions.INTROSPECT_MAPS); + + Map<String,Integer> data = new TreeMap<String,Integer>(); + data.put("%key1%", new Integer(123)); + data.put("%key2%", new Integer(456)); + + assertConversionException("converted map with invalid key under INTROSPECT_MAPS", + driver, data); + } + + public void testConvertSimpleBeanDefault() throws Exception { Bean2XmlDriver driver = new Bean2XmlDriver(); 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-18 18:52:34
|
Revision: 116 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=116&view=rev Author: kdgregory Date: 2009-08-18 18:52:26 +0000 (Tue, 18 Aug 2009) Log Message: ----------- add xpath into ConversionException Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionException.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.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/TestXml2BeanDriver.java Modified: 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 2009-08-18 18:19:50 UTC (rev 115) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/ConversionException.java 2009-08-18 18:52:26 UTC (rev 116) @@ -14,16 +14,38 @@ package net.sf.practicalxml.converter; +import org.w3c.dom.Element; +import net.sf.practicalxml.DomUtil; + + /** * A runtime exception thrown for any conversion error. Will always have a - * message, and typically contains a wrapped exception. + * message, and typically contains a wrapped exception. If thrown during + * a conversion <code>from</code> XML, should also have the absolute XPath + * of the node that caused the problem, and this is appended to the message. */ public class ConversionException extends RuntimeException { private static final long serialVersionUID = 1L; + private String _xpath; + + + public ConversionException(String message, Element elem, Throwable cause) + { + super(message, cause); + _xpath = DomUtil.getAbsolutePath(elem); + } + + public ConversionException(String message, Element elem) + { + super(message); + _xpath = DomUtil.getAbsolutePath(elem); + } + + public ConversionException(String message, Throwable cause) { super(message, cause); @@ -33,4 +55,13 @@ { super(message); } + + + @Override + public String toString() + { + return (_xpath != null) + ? super.getMessage() + " (" + _xpath + ")" + : super.getMessage(); + } } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-08-18 18:19:50 UTC (rev 115) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-08-18 18:52:26 UTC (rev 116) @@ -136,7 +136,7 @@ } catch (Exception ee) { - throw new ConversionException("unable to append child", ee); + throw new ConversionException("unable to append child: " + name, _parent, ee); } } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-08-18 18:19:50 UTC (rev 115) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-08-18 18:52:26 UTC (rev 116) @@ -123,8 +123,7 @@ { 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)); + throw new ConversionException("missing xsi:nil", elem); } return true; @@ -137,8 +136,7 @@ return null; if (hasElementChildren(elem)) - throw new ConversionException( - "expecting primitive; has children: " + DomUtil.getAbsolutePath(elem)); + throw new ConversionException("expecting primitive; has children", elem); return _helper.parse(getText(elem), klass); } @@ -271,9 +269,7 @@ catch (ClassNotFoundException ee) { throw new ConversionException( - "invalid Java type specification (" + javaType + "): " - + DomUtil.getAbsolutePath(elem), - ee); + "invalid Java type specification: " + javaType, elem, ee); } } return _helper.getJavaType(xsiType); @@ -287,8 +283,7 @@ 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)); + throw new ConversionException("missing xsi:type", elem); if (xsiType.equals(_helper.getXsdType(klass))) return; @@ -298,8 +293,8 @@ return; throw new ConversionException( - "invalid xsi:type (\"" + xsiType + "\" for " + klass.getName() + "): " - + DomUtil.getAbsolutePath(elem)); + "invalid xsi:type: \"" + xsiType + "\" for " + klass.getName(), + elem); } @@ -325,9 +320,7 @@ 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)); + throw new ConversionException("can't find property setter", child); } return setter; @@ -347,9 +340,9 @@ methodMap.put(propDesc.getName(), setter); } } - catch (IntrospectionException e) + catch (IntrospectionException ee) { - throw new ConversionException("unable to introspect", e); + throw new ConversionException("unable to introspect", ee); } _introspectedClasses.put(klass, methodMap); @@ -399,9 +392,7 @@ } catch (Exception ee) { - throw new ConversionException( - "unable to instantiate bean: " + DomUtil.getAbsolutePath(elem), - ee); + throw new ConversionException("unable to instantiate bean", elem, ee); } } @@ -414,9 +405,7 @@ } catch (Exception ee) { - throw new ConversionException( - "unable to set property: " + DomUtil.getAbsolutePath(elem), - ee); + throw new ConversionException("unable to set property", elem, ee); } } } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java 2009-08-18 18:19:50 UTC (rev 115) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java 2009-08-18 18:52:26 UTC (rev 116) @@ -14,17 +14,8 @@ package net.sf.practicalxml.converter.bean; -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.internal.ConversionHelper; -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; @@ -32,7 +23,12 @@ import org.w3c.dom.Element; +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.converter.ConversionException; +import net.sf.practicalxml.converter.internal.ConversionHelper; +import net.sf.practicalxml.internal.StringUtils; + /** * Invoked by {@link Xml2BeanDriver} to convert a DOM <code>Element</code> * into the appropriate Java object. Unlike {@link Bean2XmlHandler}, there Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java 2009-08-18 18:19:50 UTC (rev 115) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java 2009-08-18 18:52:26 UTC (rev 116) @@ -33,7 +33,7 @@ */ public class ConversionHelper { - private static Map<String,Class<?>> XSD_TYPE_TO_JAVA_CLASS + private static Map<String,Class<?>> XSD_TYPE_TO_JAVA_CLASS = new HashMap<String,Class<?>>(); static { @@ -46,8 +46,8 @@ 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<?>>(); @@ -64,7 +64,7 @@ _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()); @@ -109,8 +109,8 @@ 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. Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java 2009-08-18 18:19:50 UTC (rev 115) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBean2XmlDriver.java 2009-08-18 18:52:26 UTC (rev 116) @@ -66,7 +66,7 @@ } - private void assertConversionException(String message, Bean2XmlDriver driver, Object data) + private void assertConversionFailure(String message, Bean2XmlDriver driver, Object data) { try { @@ -74,7 +74,9 @@ fail(message); } catch (ConversionException ee) - { /* success */ } + { +// System.out.println(ee); + } } @@ -337,8 +339,8 @@ data.put("%key1%", new Integer(123)); data.put("%key2%", new Integer(456)); - assertConversionException("converted map with invalid key under INTROSPECT_MAPS", - driver, data); + assertConversionFailure("converted map with invalid key under INTROSPECT_MAPS", + driver, data); } 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-08-18 18:19:50 UTC (rev 115) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestBeanConverter.java 2009-08-18 18:52:26 UTC (rev 116) @@ -56,7 +56,7 @@ // Support Code //---------------------------------------------------------------------------- - private static void assertConversionError( + private static void assertConversionFailure( String message, BeanConverter converter, Element elem, Class<?> klass) { try @@ -65,7 +65,9 @@ fail(message); } catch (ConversionException ee) - { /* success */ } + { +// System.out.println(ee); + } } //---------------------------------------------------------------------------- @@ -156,7 +158,7 @@ Element root = outConverter.convertToXml(null, "test"); // System.out.println(OutputUtil.compactString(root.getOwnerDocument())); - assertConversionError("accepted DOM with null entry but no xsi:nil", + assertConversionFailure("accepted DOM with null entry but no xsi:nil", inConverter, root, String.class); } @@ -324,7 +326,7 @@ Element invalid = outconverter2.convertToXml(data, "test"); // System.out.println(OutputUtil.compactString(invalid.getOwnerDocument())); - assertConversionError("converter requiring xsi:type accepted XML without", + assertConversionFailure("converter requiring xsi:type accepted XML without", inConverter, invalid, SimpleBean.class); } Modified: 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 2009-08-18 18:19:50 UTC (rev 115) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestXml2BeanDriver.java 2009-08-18 18:52:26 UTC (rev 116) @@ -70,7 +70,7 @@ } - private static void assertConversionError( + private static void assertConversionFailure( String message, Xml2BeanDriver driver, Element elem, Class<?> klass) { try @@ -79,7 +79,9 @@ fail(message); } catch (ConversionException ee) - { /* success */ } + { +// System.out.println(ee); + } } @@ -133,8 +135,8 @@ assertEquals("foo", dst); Element invalid = createTestData(text("foo")); - assertConversionError("converted element missing xsi:type", - driver, invalid, String.class); + assertConversionFailure("converted element missing xsi:type", + driver, invalid, String.class); } @@ -143,8 +145,8 @@ 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); + assertConversionFailure("converted element with incorrect xsi:type", + driver, invalid, String.class); } @@ -153,8 +155,8 @@ Xml2BeanDriver driver = new Xml2BeanDriver(); Element invalid = createTestData(text("foo"), element("bar")); - assertConversionError("converted primitive with element content", - driver, invalid, String.class); + assertConversionFailure("converted primitive with element content", + driver, invalid, String.class); } @@ -175,8 +177,8 @@ assertNull(driver.convert(valid, String.class)); Element invalid = createTestData(); - assertConversionError("able to convert null data with REQUIRE_XSI_NIL set", - driver, invalid, String.class); + assertConversionFailure("able to convert null data with REQUIRE_XSI_NIL set", + driver, invalid, String.class); } @@ -210,8 +212,8 @@ 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); + assertConversionFailure("able to convert blank data with CONVERT_BLANK_AS_NULL and REQUIRE_XSI_NIL set", + driver, invalid, String.class); } @@ -249,8 +251,8 @@ Element invalid = createTestData( element("foo", text("12"), xsiType("xsd:int"))); - assertConversionError("able to convert with REQUIRE_XSI_TYPE set", - driver, invalid, int[].class); + assertConversionFailure("able to convert with REQUIRE_XSI_TYPE set", + driver, invalid, int[].class); } @@ -319,8 +321,8 @@ Element data = createTestData( element("a", text("foo"), xsiType("java:foo"))); - assertConversionError("converted unknown type", - driver, data, List.class); + assertConversionFailure("converted unknown type", + driver, data, List.class); } @@ -557,8 +559,8 @@ 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); + assertConversionFailure("didn't throw when missing xsi:type on top level", + driver, invalid1, SimpleBean.class); Element invalid2 = createTestData( xsiType("java:" + SimpleBean.class.getName()), @@ -566,8 +568,8 @@ 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); + assertConversionFailure("didn't throw when missing xsi:type on component level", + driver, invalid2, SimpleBean.class); } @@ -580,7 +582,7 @@ Xml2BeanDriver driver1 = new Xml2BeanDriver(); - assertConversionError("converted bean when extra fields present in XML", + assertConversionFailure("converted bean when extra fields present in XML", driver1, data, SimpleBean.class); Xml2BeanDriver driver2 = new Xml2BeanDriver(Xml2BeanOptions.IGNORE_MISSING_PROPERTIES); @@ -702,7 +704,7 @@ Element data = createTestData( element("sval", text("foo"))); - assertConversionError("converted bean without setter", - driver, data, ReadOnlyBean.class); + assertConversionFailure("converted bean without setter", + driver, data, ReadOnlyBean.class); } } 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-18 19:16:56
|
Revision: 117 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=117&view=rev Author: kdgregory Date: 2009-08-18 19:16:50 +0000 (Tue, 18 Aug 2009) Log Message: ----------- another class rename: ConversionHelper back to PrimitiveConversionHelper Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/PrimitiveConversionHelper.java Removed Paths: ------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-08-18 18:52:26 UTC (rev 116) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-08-18 19:16:50 UTC (rev 117) @@ -31,7 +31,7 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.converter.ConversionException; import net.sf.practicalxml.converter.bean.Bean2XmlAppenders.*; -import net.sf.practicalxml.converter.internal.ConversionHelper; +import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; /** @@ -41,14 +41,14 @@ */ public class Bean2XmlDriver { - private ConversionHelper _helper; + private PrimitiveConversionHelper _helper; private EnumSet<Bean2XmlOptions> _options = EnumSet.noneOf(Bean2XmlOptions.class); public Bean2XmlDriver(Bean2XmlOptions... options) { for (Bean2XmlOptions option : options) _options.add(option); - _helper = new ConversionHelper(shouldUseXsdFormatting()); + _helper = new PrimitiveConversionHelper(shouldUseXsdFormatting()); } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-08-18 18:52:26 UTC (rev 116) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-08-18 19:16:50 UTC (rev 117) @@ -37,7 +37,7 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.converter.ConversionException; -import net.sf.practicalxml.converter.internal.ConversionHelper; +import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; import net.sf.practicalxml.internal.StringUtils; import org.w3c.dom.Element; @@ -52,7 +52,7 @@ public class Xml2BeanDriver { private EnumSet<Xml2BeanOptions> _options; - private ConversionHelper _helper; + private PrimitiveConversionHelper _helper; private Map<Class<?>,Map<String,Method>> _introspectedClasses; @@ -62,7 +62,7 @@ for (Xml2BeanOptions option : options) _options.add(option); - _helper = new ConversionHelper(_options.contains(Xml2BeanOptions.EXPECT_XSD_FORMAT)); + _helper = new PrimitiveConversionHelper(_options.contains(Xml2BeanOptions.EXPECT_XSD_FORMAT)); _introspectedClasses = new HashMap<Class<?>,Map<String,Method>>(); } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java 2009-08-18 18:52:26 UTC (rev 116) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanHandler.java 2009-08-18 19:16:50 UTC (rev 117) @@ -25,7 +25,7 @@ import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.converter.ConversionException; -import net.sf.practicalxml.converter.internal.ConversionHelper; +import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; import net.sf.practicalxml.internal.StringUtils; @@ -38,7 +38,7 @@ public class Xml2BeanHandler { private EnumSet<Xml2BeanOptions> _options; - private ConversionHelper _primitiveHelper; + private PrimitiveConversionHelper _primitiveHelper; /** @@ -50,7 +50,7 @@ for (Xml2BeanOptions option : options) _options.add(option); - _primitiveHelper = new ConversionHelper(true); + _primitiveHelper = new PrimitiveConversionHelper(true); } Deleted: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java 2009-08-18 18:52:26 UTC (rev 116) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java 2009-08-18 19:16:50 UTC (rev 117) @@ -1,480 +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.internal; - -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; -import net.sf.practicalxml.converter.ConversionException; - - -/** - * Responsible for converting "primitive" types -- those with unambiguous - * string representations -- to/from such a representation. - */ -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<?>>(); - { - _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()); - - _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; - - - /** - * 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(); - } - - - /** - * 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); - } - - - /** - * 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); - } - } - } - } -} Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/PrimitiveConversionHelper.java (from rev 116, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/ConversionHelper.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/PrimitiveConversionHelper.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/internal/PrimitiveConversionHelper.java 2009-08-18 19:16:50 UTC (rev 117) @@ -0,0 +1,480 @@ +// 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.internal; + +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; +import net.sf.practicalxml.converter.ConversionException; + + +/** + * Responsible for converting "primitive" types -- those with unambiguous + * string representations -- to/from such a representation. + */ +public class PrimitiveConversionHelper +{ + 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<?>>(); + { + _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()); + + _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; + + + /** + * 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(); + } + + + /** + * 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); + } + + + /** + * 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/test/java/net/sf/practicalxml/converter/TestConversionHelper.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java 2009-08-18 18:52:26 UTC (rev 116) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/TestConversionHelper.java 2009-08-18 19:16:50 UTC (rev 117) @@ -18,7 +18,7 @@ import java.math.BigInteger; import java.util.Date; -import net.sf.practicalxml.converter.internal.ConversionHelper; +import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; public class TestConversionHelper @@ -36,7 +36,7 @@ private void assertFailsConversionToObject( String message, - ConversionHelper helper, + PrimitiveConversionHelper helper, String str, Class<?> klass) { @@ -58,7 +58,7 @@ public void testClassToXsdType() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertEquals("xsd:string", helper.getXsdType(String.class)); assertEquals("xsd:string", helper.getXsdType(Character.class)); @@ -72,7 +72,7 @@ public void testUnknownClass() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertNull(helper.getXsdType(Class.class)); } @@ -80,7 +80,7 @@ public void testConvertNull() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertNull(helper.stringify(null)); assertNull(helper.parse(null, Object.class)); @@ -89,7 +89,7 @@ public void testConvertString() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertEquals("xsd:string", helper.getXsdType(String.class)); @@ -103,7 +103,7 @@ public void testConvertCharacter() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertEquals("xsd:string", helper.getXsdType(Character.class)); @@ -123,7 +123,7 @@ public void testConvertBooleanDefault() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); // default boolean conversion is compatible with XSD type def assertEquals("xsd:boolean", helper.getXsdType(Boolean.class)); @@ -143,7 +143,7 @@ public void testConvertBooleanXsd() throws Exception { - ConversionHelper helper = new ConversionHelper(true); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(true); assertEquals("xsd:boolean", helper.getXsdType(Boolean.class)); @@ -167,7 +167,7 @@ public void testConvertByte() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertEquals("xsd:byte", helper.getXsdType(Byte.class)); @@ -205,7 +205,7 @@ public void testConvertShort() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertEquals("xsd:short", helper.getXsdType(Short.class)); @@ -243,7 +243,7 @@ public void testConvertInteger() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertEquals("xsd:int", helper.getXsdType(Integer.class)); @@ -281,7 +281,7 @@ public void testConvertLong() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertEquals("xsd:long", helper.getXsdType(Long.class)); @@ -319,7 +319,7 @@ public void testConvertFloatDefault() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); // default float conversion is compatible with XSD type def assertEquals("xsd:decimal", helper.getXsdType(Float.class)); @@ -351,7 +351,7 @@ public void testConvertFloatXsd() throws Exception { - ConversionHelper helper = new ConversionHelper(true); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(true); assertEquals("xsd:decimal", helper.getXsdType(Float.class)); @@ -381,7 +381,7 @@ public void testConvertDoubleDefault() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); // default double conversion is compatible with XSD type def assertEquals("xsd:decimal", helper.getXsdType(Double.class)); @@ -413,7 +413,7 @@ public void testConvertDoubleXsd() throws Exception { - ConversionHelper helper = new ConversionHelper(true); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(true); assertEquals("xsd:decimal", helper.getXsdType(Double.class)); @@ -444,7 +444,7 @@ public void testConvertBigInteger() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertEquals("xsd:decimal", helper.getXsdType(BigInteger.class)); @@ -474,7 +474,7 @@ public void testConvertBigDecimal() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); assertEquals("xsd:decimal", helper.getXsdType(BigDecimal.class)); @@ -500,7 +500,7 @@ public void testConvertDateDefault() throws Exception { - ConversionHelper helper = new ConversionHelper(); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(); // default date conversion is NOT compatible with XSD type def, // so this is misleading @@ -525,7 +525,7 @@ public void testConvertDateXsd() throws Exception { - ConversionHelper helper = new ConversionHelper(true); + PrimitiveConversionHelper helper = new PrimitiveConversionHelper(true); assertEquals("xsd:dateTime", helper.getXsdType(Date.class)); 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-09-08 16:06:23
|
Revision: 124 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=124&view=rev Author: kdgregory Date: 2009-09-08 16:06:13 +0000 (Tue, 08 Sep 2009) Log Message: ----------- implement Xml2Json conversion Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java 2009-09-08 16:06:13 UTC (rev 124) @@ -0,0 +1,73 @@ +// 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.json; + +import java.util.Iterator; +import java.util.List; + +import net.sf.practicalxml.DomUtil; + +import org.w3c.dom.Element; + + +/** + * Handles the actual work of converting XML to JSON. + */ +public class Xml2JsonConverter +{ + /** + * Appends the contents of the specified element to an existing buffer. + * Returns the buffer as a convenience. + */ + public StringBuffer convert(Element elem, StringBuffer buf) + { + String text = DomUtil.getText(elem); + if (text != null) + return appendText(text, buf); + else + return appendChildren(elem, buf); + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + private StringBuffer appendText(String text, StringBuffer buf) + { + buf.append('"') + .append(text) + .append('"'); + return buf; + } + + + private StringBuffer appendChildren(Element elem, StringBuffer buf) + { + buf.append("{"); + List<Element> children = DomUtil.getChildren(elem); + for (Iterator<Element> childItx = children.iterator() ; childItx.hasNext() ; ) + { + Element child = childItx.next(); + buf.append(DomUtil.getLocalName(child)) + .append(": "); + convert(child, buf); + if (childItx.hasNext()) + buf.append(", "); + } + buf.append("}"); + return buf; + } +} Property changes on: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java ___________________________________________________________________ Added: svn:executable + * Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java 2009-09-08 16:06:13 UTC (rev 124) @@ -0,0 +1,87 @@ +// 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.json; + +import org.w3c.dom.Element; + +import net.sf.practicalxml.builder.ElementNode; +import net.sf.practicalxml.converter.AbstractConversionTestCase; + +import static net.sf.practicalxml.builder.XmlBuilder.*; + + +public class TestXml2JsonConverter +extends AbstractConversionTestCase +{ + public TestXml2JsonConverter(String testName) + { + super(testName); + } + + +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + public void convertAndAssert(String expected, ElementNode rootNode) + { + Element root = rootNode.toDOM().getDocumentElement(); + StringBuffer buf = new Xml2JsonConverter() + .convert(root, new StringBuffer()); + assertEquals(expected, buf.toString()); + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + public void testEmptyConversion() throws Exception + { + convertAndAssert( + "{}", + element("data")); + } + + + public void testSingleChild() throws Exception + { + convertAndAssert( + "{foo: \"bar\"}", + element("data", + element("foo", text("bar")))); + } + + + public void testTwoChildren() throws Exception + { + convertAndAssert( + "{foo: \"bar\", argle: \"bargle\"}", + element("data", + element("foo", text("bar")), + element("argle", text("bargle")))); + } + + + public void testChildAndGrandchildren() throws Exception + { + convertAndAssert( + "{foo: \"bar\", argle: {biz: \"baz\", fizz: \"buzz\"}}", + element("data", + element("foo", text("bar")), + element("argle", + element("biz", text("baz")), + element("fizz", text("buzz"))))); + } +} Property changes on: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java ___________________________________________________________________ Added: svn:executable + * 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-09-11 17:43:40
|
Revision: 126 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=126&view=rev Author: kdgregory Date: 2009-09-11 17:43:24 +0000 (Fri, 11 Sep 2009) Log Message: ----------- implement JSON -> XML conversion Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Json2XmlConverter.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/JsonUtil.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJson2XmlConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJsonUtil.java Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Json2XmlConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Json2XmlConverter.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Json2XmlConverter.java 2009-09-11 17:43:24 UTC (rev 126) @@ -0,0 +1,262 @@ +// 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.json; + +import org.w3c.dom.Element; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.converter.ConversionException; + + +/** + * This class contains a hand-written recursive-descent parser for JSON + * strings. Instances are constructed around a source string, and used + * only once (thus thread-safety is not an issue). + * <p> + * See <a href="http://www.json.org/">json.org</a> for the JSON grammar. + * <p> + * The current implementation creates a child element for each element + * of an array, producing output similar to that from the Bean->XML + * conversion. + */ +public class Json2XmlConverter +{ + private String _src; // we pull substrings from the base string + private int _curPos; // position of current token (start of substring) + private int _nextPos; // position of next token (end of substring) + + + public Json2XmlConverter(String src) + { + _src = src; + } + + + /** + * Creates a new XML <code>Document</code> from the passed JSON string + * (which must contain an object definition and nothing else). The root + * element will be named "data". + */ + public Element convert() + { + Element root = DomUtil.newDocument("data"); + parse(root); + return root; + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + /** + * Top-level parser entry: expects the string to be a single object + * definition, without anything before or after the outer brace pair. + */ + private void parse(Element parent) + { + expect("{"); + parseObject(parent); + if (nextToken().length() > 0) + throw new ConversionException(commonExceptionText( + "unexpected content after closing brace")); + } + + + /** + * Called when the next token is expected to represent a value (of + * any type), to dispatch and append that value to the parent element. + * Returns the subsequent token. + */ + private String valueDispatch(String next, Element parent) + { + if (next.equals("{")) + parseObject(parent); + else if (next.equals("[")) + parseArray(parent); + else if (next.equals("\"")) + DomUtil.setText(parent, parseString()); + else + DomUtil.setText(parent, next); + + return nextToken(); + } + + + private void parseObject(Element parent) + { + String next = nextToken(); + if (atEndOfSequence(next, "}", false)) + return; + + while (true) + { + Element child = appendChild(parent, next); + expect(":"); + + next = valueDispatch(nextToken(), child); + if (atEndOfSequence(next, "}", true)) + return; + next = nextToken(); + } + } + + + private void parseArray(Element parent) + { + String next = nextToken(); + if (atEndOfSequence(next, "]", false)) + return; + + while (true) + { + Element child = DomUtil.appendChild(parent, "data"); + next = valueDispatch(next, child); + if (atEndOfSequence(next, "]", true)) + return; + next = nextToken(); + } + } + + + private String parseString() + { + try + { + for (_curPos = _nextPos ; _nextPos < _src.length() ; _nextPos++) + { + char c = _src.charAt(_nextPos); + if (c == '"') + return JsonUtil.unescape(_src.substring(_curPos, _nextPos++)); + if (c == '\\') + _nextPos++; + } + throw new ConversionException(commonExceptionText("unterminated string")); + } + catch (IllegalArgumentException ee) + { + throw new ConversionException(commonExceptionText("invalid string"), ee); + } + } + + + /** + * Reads the next token and verifies that it contains the expected value. + */ + private String expect(String expected) + { + String next = nextToken(); + if (next.equals(expected)) + return next; + + throw new ConversionException(commonExceptionText("unexpected token")); + } + + + /** + * Checks the next token (passed) to see if it represents the end of a + * sequence, a contination (","), or something unexpected. + */ + private boolean atEndOfSequence(String next, String expectedEnd, boolean throwIfSomethingElse) + { + if (next.equals(expectedEnd)) + return true; + else if (next.equals(",")) + return false; + else if (next.equals("")) + throw new ConversionException(commonExceptionText("unexpected end of input")); + else if (throwIfSomethingElse) + throw new ConversionException(commonExceptionText("unexpected token")); + return false; + } + + + /** + * Extracts the next token from the string, skipping any initial whitespace. + * Tokens consist of a set of specific single-character strings, or any other + * sequence of non-whitespace characters. + */ + private String nextToken() + { + final int len = _src.length(); + + _curPos = _nextPos; + while ((_curPos < len) && Character.isWhitespace(_src.charAt(_curPos))) + _curPos++; + + if (_curPos == len) + return ""; + + _nextPos = _curPos + 1; + if (!isDelimiter(_src.charAt(_curPos))) + { + while ((_nextPos < len) + && !Character.isWhitespace(_src.charAt(_nextPos)) + && !isDelimiter(_src.charAt(_nextPos))) + _nextPos++; + } + + return _src.substring(_curPos, _nextPos); + } + + + private boolean isDelimiter(char c) + { + switch (c) + { + case '{' : + case '}' : + case '[' : + case ']' : + case ':' : + case ',' : + case '"' : + return true; + default : + return false; + } + } + + + private String commonExceptionText(String preamble) + { + String excerpt = (_curPos + 20) > _src.length() + ? _src.substring(_curPos) + : _src.substring(_curPos, _curPos + 20) + "[...]"; + return preamble + " at position " + _curPos + ": \"" + excerpt + "\""; + } + + + /** + * A wrapper around DomUtil.appendChild() that applies some validation + * on the name, and replaces the DOM exception with ConversionException. + */ + private Element appendChild(Element parent, String name) + { + if (name.equals("")) + throw new ConversionException(commonExceptionText("unexpected end of input")); + if (isDelimiter(name.charAt(0))) + throw new ConversionException(commonExceptionText("invalid token")); + try + { + return DomUtil.appendChild(parent, name); + } + catch (Exception e) + { + throw new ConversionException(commonExceptionText("invalid element name"), e); + } + } + +} Property changes on: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Json2XmlConverter.java ___________________________________________________________________ Added: svn:executable + * Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/JsonUtil.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/JsonUtil.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/JsonUtil.java 2009-09-11 17:43:24 UTC (rev 126) @@ -0,0 +1,103 @@ +// 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.json; + +import net.sf.practicalxml.internal.StringUtils; + + +/** + * Static utility methods for working with JSON content. Mostly duplicates + * methods from Jakarta Commons. + */ +public class JsonUtil +{ + /** + * Unescapes a string, replacing "slash-sequences" by actual characters. + * Null is converted to empty string. + * + * @throws IllegalArgumentException on any failure + */ + public static String unescape(String src) + { + if (src == null) + return ""; + + StringBuffer buf = new StringBuffer(src.length()); + for (int ii = 0 ; ii < src.length() ; ) + { + char c = src.charAt(ii++); + if (c == '\\') + { + if (ii == src.length()) + throw new IllegalArgumentException("escape extends past end of string"); + c = src.charAt(ii++); + switch (c) + { + case '"' : + case '\\' : + case '/' : + // do nothing, simple escape + break; + case 'b' : + c = '\b'; + break; + case 'f' : + c = '\f'; + break; + case 'n' : + c = '\n'; + break; + case 'r' : + c = '\r'; + break; + case 't' : + c = '\t'; + break; + case 'U' : + case 'u' : + c = unescapeUnicode(src, ii); + ii += 4; + break; + default : + throw new IllegalArgumentException("invalid escape character: " + c); + } + } + buf.append(c); + } + return buf.toString(); + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + private static char unescapeUnicode(String src, int idx) + { + if (idx + 4 > src.length()) + throw new IllegalArgumentException("unicode escape extends past end of string"); + + int value = 0; + for (int ii = 0 ; ii < 4 ; ii++) + { + int digit = StringUtils.parseDigit(src.charAt(idx + ii), 16); + if (digit < 0) + throw new IllegalArgumentException( + "invalid unicode escape: " + src.substring(idx, idx + 4)); + value = value * 16 + digit; + } + return (char)value; + } +} Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJson2XmlConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJson2XmlConverter.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJson2XmlConverter.java 2009-09-11 17:43:24 UTC (rev 126) @@ -0,0 +1,435 @@ +// 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.json; + +import org.w3c.dom.Element; + +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.converter.AbstractConversionTestCase; +import net.sf.practicalxml.converter.ConversionException; + + +public class TestJson2XmlConverter +extends AbstractConversionTestCase +{ + public TestJson2XmlConverter(String testName) + { + super(testName); + } + + +//---------------------------------------------------------------------------- +// Test Cases +// ---- +// Note that in some place we call Node.getChildNodes(), in others we call +// DomUtil.getChildren. This is intentional: in the former case we want to +// ensure that the converter isn't inserting extraneous text, in the latter +// we want to ensure it isn't inserting extraneous elements (but we don't +// care how many nodes it uses to build the text content). +//---------------------------------------------------------------------------- + + public void testConvertEmpty() throws Exception + { + String src = "{}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(0, root.getChildNodes().getLength()); + } + + + public void testConvertEmptyWithWhitespace() throws Exception + { + String src = " {\t}\n"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(0, root.getChildNodes().getLength()); + } + + + public void testFailContentBeforeInitialBrace() throws Exception + { + String src = "test = {}"; + + try + { + new Json2XmlConverter(src).convert(); + fail(); + } + catch (ConversionException ee) + { + // success + } + } + + + public void testFailContentAfterTerminalBrace() throws Exception + { + String src = "{};"; + + try + { + new Json2XmlConverter(src).convert(); + fail(); + } + catch (ConversionException ee) + { + // success + } + } + + + public void testFailMissingTerminalBrace() throws Exception + { + String src = " { "; + + try + { + new Json2XmlConverter(src).convert(); + fail(); + } + catch (ConversionException ee) + { + // success + } + } + + + public void testConvertSingleElementNumeric() throws Exception + { + String src = "{foo: 123}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(1, root.getChildNodes().getLength()); + + Element child = (Element)root.getFirstChild(); + assertEquals("foo", child.getNodeName()); + assertEquals("123", DomUtil.getText(child)); + assertEquals(0, DomUtil.getChildren(child).size()); + } + + + public void testConvertSingleElementString() throws Exception + { + String src = "{foo: \"bar\"}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(1, root.getChildNodes().getLength()); + + Element child = (Element)root.getFirstChild(); + assertEquals("foo", child.getNodeName()); + assertEquals("bar", DomUtil.getText(child)); + assertEquals(0, DomUtil.getChildren(child).size()); + } + + + public void testConvertEmptyString() throws Exception + { + String src = "{foo: \"\"}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(1, root.getChildNodes().getLength()); + + Element child = (Element)root.getFirstChild(); + assertEquals("foo", child.getNodeName()); + assertEquals("", DomUtil.getText(child)); + assertEquals(0, DomUtil.getChildren(child).size()); + } + + + public void testConvertStringWithEmbeddedEscape() throws Exception + { + String src = "{foo: \"b\\\"\\u0061r\"}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(1, root.getChildNodes().getLength()); + + Element child = (Element)root.getFirstChild(); + assertEquals("foo", child.getNodeName()); + assertEquals("b\"ar", DomUtil.getText(child)); + assertEquals(0, DomUtil.getChildren(child).size()); + } + + + public void testFailUnterminatedString() throws Exception + { + String src = "{foo: \"bar}"; + + try + { + new Json2XmlConverter(src).convert(); + fail(); + } + catch (ConversionException ee) + { + // success + } + } + + + public void testFailInvalidEscapeAtEndOfString() throws Exception + { + String src = "{foo: \"bar\\u123\"}"; + + try + { + new Json2XmlConverter(src).convert(); + fail(); + } + catch (ConversionException ee) + { + // success + } + } + + + public void testConvertTwoElementNumeric() throws Exception + { + String src = "{foo: 123, bar: 456}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(2, root.getChildNodes().getLength()); + + Element child1 = (Element)root.getFirstChild(); + assertEquals("foo", child1.getNodeName()); + assertEquals("123", DomUtil.getText(child1)); + assertEquals(0, DomUtil.getChildren(child1).size()); + + Element child2 = (Element)child1.getNextSibling(); + assertEquals("bar", child2.getNodeName()); + assertEquals("456", DomUtil.getText(child2)); + assertEquals(0, DomUtil.getChildren(child2).size()); + } + + + public void testConvertTwoElementStringWithWhitespace() throws Exception + { + String src = "{foo : \"123\" , bar\t: \"456\" }"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(2, root.getChildNodes().getLength()); + + Element child1 = (Element)root.getFirstChild(); + assertEquals("foo", child1.getNodeName()); + assertEquals("123", DomUtil.getText(child1)); + assertEquals(0, DomUtil.getChildren(child1).size()); + + Element child2 = (Element)child1.getNextSibling(); + assertEquals("bar", child2.getNodeName()); + assertEquals("456", DomUtil.getText(child2)); + assertEquals(0, DomUtil.getChildren(child2).size()); + } + + + public void testFailObjectMissingCommaBetweenTerms() throws Exception + { + String src = "{foo: 123 bar: 456}"; + + try + { + new Json2XmlConverter(src).convert(); + fail(); + } + catch (ConversionException e) + { + // success + } + } + + + public void testFailObjectMissingElement() throws Exception + { + String src = "{foo: 123, , bar: 456}"; + + try + { + new Json2XmlConverter(src).convert(); + fail(); + } + catch (ConversionException ee) + { + // success + } + } + + + public void testConvertNested() throws Exception + { + String src = "{foo: {bar: 123, baz:456}}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(1, root.getChildNodes().getLength()); + + Element child = (Element)root.getFirstChild(); + assertEquals("foo", child.getNodeName()); + assertNull(DomUtil.getText(child)); + assertEquals(2, child.getChildNodes().getLength()); + + Element grandchild1 = (Element)child.getFirstChild(); + assertEquals("bar", grandchild1.getNodeName()); + assertEquals("123", DomUtil.getText(grandchild1)); + assertEquals(0, DomUtil.getChildren(grandchild1).size()); + + Element grandchild2 = (Element)grandchild1.getNextSibling(); + assertEquals("baz", grandchild2.getNodeName()); + assertEquals("456", DomUtil.getText(grandchild2)); + assertEquals(0, DomUtil.getChildren(grandchild2).size()); + } + + + public void testConvertNestedEmpty() throws Exception + { + String src = "{foo: {}}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(1, root.getChildNodes().getLength()); + + Element child = (Element)root.getFirstChild(); + assertEquals("foo", child.getNodeName()); + assertNull(DomUtil.getText(child)); + assertEquals(0, child.getChildNodes().getLength()); + } + + + public void testConvertEmptyArray() throws Exception + { + String src = "{foo: []}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(1, root.getChildNodes().getLength()); + + Element child = (Element)root.getFirstChild(); + assertEquals("foo", child.getNodeName()); + assertNull(DomUtil.getText(child)); + assertEquals(0, child.getChildNodes().getLength()); + } + + + public void testConvertSingleElementNumericArray() throws Exception + { + String src = "{foo: [123]}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(1, root.getChildNodes().getLength()); + + Element child = (Element)root.getFirstChild(); + assertEquals("foo", child.getNodeName()); + assertNull(DomUtil.getText(child)); + assertEquals(1, child.getChildNodes().getLength()); + + Element grandchild = (Element)child.getFirstChild(); + assertEquals("data", grandchild.getNodeName()); + assertEquals("123", DomUtil.getText(grandchild)); + assertEquals(0, DomUtil.getChildren(grandchild).size()); + } + + + public void testConvertMultiElementNumericArray() throws Exception + { + String src = "{foo: [123, 456]}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(1, root.getChildNodes().getLength()); + + Element child = (Element)root.getFirstChild(); + assertEquals("foo", child.getNodeName()); + assertNull(DomUtil.getText(child)); + assertEquals(2, child.getChildNodes().getLength()); + + Element grandchild1 = (Element)child.getFirstChild(); + assertEquals("data", grandchild1.getNodeName()); + assertEquals("123", DomUtil.getText(grandchild1)); + assertEquals(0, DomUtil.getChildren(grandchild1).size()); + + Element grandchild2 = (Element)grandchild1.getNextSibling(); + assertEquals("data", grandchild2.getNodeName()); + assertEquals("456", DomUtil.getText(grandchild2)); + assertEquals(0, DomUtil.getChildren(grandchild2).size()); + } + + + public void testConvertMultiElementMixedArray() throws Exception + { + String src = "{foo: [123, \"bar\", 456]}"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(1, root.getChildNodes().getLength()); + + Element child = (Element)root.getFirstChild(); + assertEquals("foo", child.getNodeName()); + assertNull(DomUtil.getText(child)); + assertEquals(3, child.getChildNodes().getLength()); + + Element grandchild1 = (Element)child.getFirstChild(); + assertEquals("data", grandchild1.getNodeName()); + assertEquals("123", DomUtil.getText(grandchild1)); + assertEquals(0, DomUtil.getChildren(grandchild1).size()); + + Element grandchild2 = (Element)grandchild1.getNextSibling(); + assertEquals("data", grandchild2.getNodeName()); + assertEquals("bar", DomUtil.getText(grandchild2)); + assertEquals(0, DomUtil.getChildren(grandchild2).size()); + + Element grandchild3 = (Element)grandchild2.getNextSibling(); + assertEquals("data", grandchild3.getNodeName()); + assertEquals("456", DomUtil.getText(grandchild3)); + assertEquals(0, DomUtil.getChildren(grandchild3).size()); + } + + + public void testFailConvertUnterminatedArray() throws Exception + { + String src = "{foo: [123, 456"; + + try + { + new Json2XmlConverter(src).convert(); + fail(); + } + catch (ConversionException ee) + { + // success + } + } + + + public void testFailConvertArrayMissingElement() throws Exception + { + String src = "{foo: [123 , , 456]}"; + + try + { + new Json2XmlConverter(src).convert(); + fail(); + } + catch (ConversionException ee) + { + // success + } + } +} Property changes on: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJson2XmlConverter.java ___________________________________________________________________ Added: svn:executable + * Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJsonUtil.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJsonUtil.java (rev 0) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJsonUtil.java 2009-09-11 17:43:24 UTC (rev 126) @@ -0,0 +1,118 @@ +// 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.json; + +import junit.framework.TestCase; + +public class TestJsonUtil extends TestCase +{ + public void testUnescapeNullAndEmpty() throws Exception + { + assertEquals("", JsonUtil.unescape(null)); + assertEquals("", JsonUtil.unescape("")); + } + + + public void testUnescapeNormalString() throws Exception + { + assertEquals("f", JsonUtil.unescape("f")); + assertEquals("fo", JsonUtil.unescape("fo")); + assertEquals("foo", JsonUtil.unescape("foo")); + } + + + public void testUnescapeSingleCharSlashes() throws Exception + { + assertEquals("\"", JsonUtil.unescape("\\\"")); + assertEquals("\\", JsonUtil.unescape("\\\\")); + assertEquals("/", JsonUtil.unescape("\\/")); + assertEquals("\b", JsonUtil.unescape("\\b")); + assertEquals("\f", JsonUtil.unescape("\\f")); + assertEquals("\n", JsonUtil.unescape("\\n")); + assertEquals("\r", JsonUtil.unescape("\\r")); + assertEquals("\t", JsonUtil.unescape("\\t")); + + // and a couple of tests to ensure that we don't overstep + assertEquals("ba\rbaz", JsonUtil.unescape("ba\\rbaz")); + assertEquals("\r\n", JsonUtil.unescape("\\r\\n")); + } + + + public void testUnescapeUnicode() throws Exception + { + assertEquals("A", JsonUtil.unescape("\\u0041")); + assertEquals("A", JsonUtil.unescape("\\U0041")); + + // verify that we correctly index subsequent chars + assertEquals("BAR", JsonUtil.unescape("B\\U0041R")); + } + + + public void testUnescapeFailEndOfString() throws Exception + { + try + { + JsonUtil.unescape("foo\\"); + fail("completed for escape at end of string"); + } + catch (IllegalArgumentException e) + { + // success + } + } + + + public void testUnescapeFailInvalidChar() throws Exception + { + try + { + JsonUtil.unescape("foo\\q"); + fail("completed for invalid escape sequence"); + } + catch (IllegalArgumentException e) + { + // success + } + } + + + public void testUnescapeFailIncompleteUnicodeEscape() throws Exception + { + try + { + JsonUtil.unescape("foo\\u12"); + fail("completed for invalid escape sequence"); + } + catch (IllegalArgumentException e) + { + // success + } + } + + + public void testUnescapeFailInvalidUnicodeEscape() throws Exception + { + try + { + JsonUtil.unescape("\\u0foo"); + fail("completed for invalid escape sequence"); + } + catch (IllegalArgumentException e) + { + // success + } + } + +} 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-09-11 18:30:46
|
Revision: 127 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=127&view=rev Author: kdgregory Date: 2009-09-11 18:30:39 +0000 (Fri, 11 Sep 2009) Log Message: ----------- Xml2JsonConverter now escapes strings Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/JsonUtil.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJsonUtil.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/JsonUtil.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/JsonUtil.java 2009-09-11 17:43:24 UTC (rev 126) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/JsonUtil.java 2009-09-11 18:30:39 UTC (rev 127) @@ -24,6 +24,53 @@ public class JsonUtil { /** + * Escapes a string, replacing quotes, backslashes, non-printable and + * non-ASCII characters by a defined set of single-character or 4-digit + * unicode escapes. + */ + public static String escape(String src) + { + if (src == null) + return ""; + + StringBuffer buf = new StringBuffer(src.length() + 20); + for (int ii = 0 ; ii < src.length() ; ii++) + { + char c = src.charAt(ii); + switch (c) + { + case '"' : + case '\\' : + case '/' : + buf.append('\\').append(c); + break; + case '\b' : + buf.append("\\b"); + break; + case '\f' : + buf.append("\\f"); + break; + case '\n' : + buf.append("\\n"); + break; + case '\r' : + buf.append("\\r"); + break; + case '\t' : + buf.append("\\t"); + break; + default : + if ((c >= 32) && (c <= 127)) + buf.append(c); + else + buf.append(escapeUnicode(c)); + } + } + return buf.toString(); + } + + + /** * Unescapes a string, replacing "slash-sequences" by actual characters. * Null is converted to empty string. * @@ -100,4 +147,18 @@ } return (char)value; } + + + private static String escapeUnicode(char c) + { + char[] buf = new char[] { '\\', 'u', '0', '0', '0', '0' }; + int value = c & 0xFFFF; + for (int ii = 5 ; ii > 1 ; ii--) + { + int digit = value % 16; + value /= 16; + buf[ii] = Character.forDigit(digit, 16); + } + return new String(buf); + } } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java 2009-09-11 17:43:24 UTC (rev 126) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java 2009-09-11 18:30:39 UTC (rev 127) @@ -48,7 +48,7 @@ private StringBuffer appendText(String text, StringBuffer buf) { buf.append('"') - .append(text) + .append(JsonUtil.escape(text)) .append('"'); return buf; } Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJsonUtil.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJsonUtil.java 2009-09-11 17:43:24 UTC (rev 126) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJsonUtil.java 2009-09-11 18:30:39 UTC (rev 127) @@ -18,6 +18,45 @@ public class TestJsonUtil extends TestCase { + public void testEscapeNullAndEmpty() throws Exception + { + assertEquals("", JsonUtil.escape(null)); + assertEquals("", JsonUtil.escape("")); + } + + + public void testEscapeNormalString() throws Exception + { + assertEquals("f", JsonUtil.unescape("f")); + assertEquals("fo", JsonUtil.unescape("fo")); + assertEquals("foo", JsonUtil.unescape("foo")); + } + + + public void testEescapeSingleChar() throws Exception + { + assertEquals("\\\"", JsonUtil.escape("\"")); + assertEquals("\\\\", JsonUtil.escape("\\")); + assertEquals("\\/", JsonUtil.escape("/")); + assertEquals("\\b", JsonUtil.escape("\b")); + assertEquals("\\f", JsonUtil.escape("\f")); + assertEquals("\\n", JsonUtil.escape("\n")); + assertEquals("\\r", JsonUtil.escape("\r")); + assertEquals("\\t", JsonUtil.escape("\t")); + + // and a couple of tests to ensure that we don't overstep + assertEquals("ba\\rbaz", JsonUtil.escape("ba\rbaz")); + assertEquals("\\r\\n", JsonUtil.escape("\r\n")); + } + + + public void testEscapeUnicode() throws Exception + { + assertEquals("\\u0019", JsonUtil.escape("\u0019")); + assertEquals("\\u1bcd", JsonUtil.escape("\u1bcd")); + } + + public void testUnescapeNullAndEmpty() throws Exception { assertEquals("", JsonUtil.unescape(null)); @@ -33,7 +72,7 @@ } - public void testUnescapeSingleCharSlashes() throws Exception + public void testUnescapeSingleChar() throws Exception { assertEquals("\"", JsonUtil.unescape("\\\"")); assertEquals("\\", JsonUtil.unescape("\\\\")); Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java 2009-09-11 17:43:24 UTC (rev 126) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java 2009-09-11 18:30:39 UTC (rev 127) @@ -84,4 +84,19 @@ element("biz", text("baz")), element("fizz", text("buzz"))))); } + + + public void testStringEscaping() throws Exception + { + convertAndAssert( + "{backslash: \"\\\\\", " + + "quote: \"\\\"\", " + + "nonprint: \"\\b\\f\\n\\r\\t\", " + + "unicode: \"b\\u00e4r\"}", + element("data", + element("backslash", text("\\")), + element("quote", text("\"")), + element("nonprint", text("\b\f\n\r\t")), + element("unicode", text("b\u00e4r")))); + } } 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-09-18 18:46:45
|
Revision: 140 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=140&view=rev Author: kdgregory Date: 2009-09-18 18:45:53 +0000 (Fri, 18 Sep 2009) Log Message: ----------- BeanConverter now contains only static methods, is a facade Renamed Bean2XmlDriver and Xml2BeanDriver to XXXConverter Moved conversion docs into package 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/bean/Bean2XmlAppenders.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBeanConverter.java Added Paths: ----------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanConverter.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/package.html branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestXml2BeanConverter.java Removed Paths: ------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestBean2XmlDriver.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestXml2BeanDriver.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-09-18 15:51:32 UTC (rev 139) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java 2009-09-18 18:45:53 UTC (rev 140) @@ -17,175 +17,32 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; -import net.sf.practicalxml.converter.bean.Bean2XmlDriver; +import net.sf.practicalxml.converter.bean.Bean2XmlConverter; import net.sf.practicalxml.converter.bean.Bean2XmlOptions; -import net.sf.practicalxml.converter.bean.Xml2BeanDriver; +import net.sf.practicalxml.converter.bean.Xml2BeanConverter; import net.sf.practicalxml.converter.bean.Xml2BeanOptions; /** * Converts Java objects (not just beans) to or from an XML representation. * Originally developed to support simple web services, without the overhead - * (schema definitions and/or annotations) required by JAXB. + * (schema definitions and/or annotations) required by JAXB. See the {@link + * net.sf.practicalxml.converter.bean package docs} for specifics. * <p> - * A single instance is intended for conversions in one direction only. This - * is a consequence of specifying options as varargs, but fits well with - * "normal" application design, in which input and output are handled by - * different parts of the code. You won't get an error if you try to convert - * the "wrong" way, you'll simply get default behavior. Note that, depending - * on options settings, output generated by one converter may not be valid - * input for another. + * This class provides static facade methods for + * {@link net.sf.practicalxml.converter.bean.Bean2XmlConverter} and + * {@link net.sf.practicalxml.converter.bean.Xml2BeanConverter}. If static + * methods and throwaway objects offend you then use those classes directly. * <p> - * The basic structure of XML produced/consumed by this class is that the - * root element represents the object being converted, and its child nodes - * represent the elements/properties of the object. Nodes representing complex - * objects have child elements, those representing simple (primitive) objects - * have a single text child. Objects are processed recursively, and cycles are - * <em>not</em> detected (although this may change). - * <p> - * A namespace and qualified name may be provided to {@link #convertToXml}, - * and all elements will inherit the namespace and prefix. Namespaces are - * ignored on input; all property matches uses the element's local name. - * <p> - * Each element may have an <code>xsi:type</code> attribute (where <code>xsi - * </code> references to the XML Schema instance namespace). This attribute - * is optional for both output and input; if used for output, it is merely - * informational, but for input will be examined to validate that the XML is - * appropriate for the desired object type. For primitive types, wrappers, - * and strings, it takes the form "<code>xsd:TYPE</code>", where <code>TYPE - * </code> is one of the simple types defined by XML schema. For other Java - * types, it takes the form "<code>java:TYPE</code>", where <code>TYPE</code> - * is the fully qualified Java classname. - * <p> - * On input, the desired type is specified by the caller or by introspection - * (except in the case of collection elements; see below). The <code>xsi:type - * </code> value, if any, is ignored except for validation. - * <p> - * Additional conversion rules are as follows: - * - * <table border="1"> - * <tr><th>Java Object Type - * <th>{@link #convertToXml} - * <th>{@link #convertToJava} - * <tr><td>Primitives, Wrapper objects, and String - * <td> - * <td> - * <tr><td>Arrays - * <td>Elements of the array are written in sequence to the DOM, using the - * element name "<code>data</code>". Elements are given an attribute - * named "<code>index</code>", which contains the element's position - * within the array. - * <td>Elements of the array are processed in sequence, ignoring both - * element name and "index" attribute. - * <tr><td>Lists and Sets - * <td>The collection is iterated, and elements are written in sequence to - * the DOM, using the element name "<code>data</code>". Elements are - * given an attribute named "<code>index</code>", which contains the - * element's position within the iteration (meaningful only for lists). - * <td>Elements of the are processed in sequence, ignoring both element - * name and "index" attribute. - * <p> - * If an <code>xsi:type</code> attribute is present, it will be used - * to drive conversion of the element. Otherwise, the element will be - * converted as a <code>String</code> (which will fail for complex - * types, because string conversion assumes a single text node). - * <p> - * Where the caller specifies an interface as the conversion class, the - * converter will choose an appropriate implementation class: - * <ul> - * <li> <code>ArrayList</code> for <code>List</code> or <code>Collection</code> - * <li> <code>TreeSet</code> for <code>SortedSet</code> - * <li> <code>HashSet</code> for <code>Set</code> - * </ul> - * <tr><td>Maps - * <td>The collection is iterated, and elements are written in sequence to - * the DOM. Depending on the output options, either the entry key will - * be used as the element name, or the name will be "<code>data</code>" - * and the key stored in an attribute named "<code>key</code>". The - * former option is only permitted if all keys in the map are valid - * XML element names; otherwise the converter will throw. - * <td>Elements are processed in sequence. The converter first looks for - * a "<code>key</code>" attribute, and will use it as the entry key - * if found. Otherwise, it will use the element name. If your maps - * are being reduced to a single entry, look for a missing attribute. - * <p> - * If an <code>xsi:type</code> attribute is present, it will be used - * to drive conversion of the element. Otherwise, the element will be - * converted as a <code>String</code> (which will fail for complex - * types, because string conversion assumes a single text node). - * <p> - * Where the caller specifies an interface as the conversion class, - * the converter will choose an appropriate implementation class: - * <code>TreeMap</code> for <code>SortedMap</code>, and <code>HashMap - * </code> for <code>Map</code>. - * <tr><td>Bean-structured Objects - * <td>The object is introspected, and properties are written in the order - * provided by the <code>Introspector</code>. - * <td>The bean class must provide a no-argument constructor (otherwise it - * doesn't follow the bean spec, and we can't use it). - * <p> - * The converter relies on <code>java.beans.Introspector</code> to find - * property setter methods for an object. If the object provides - * multiple methods for the property, the converter will use whichever - * one the introspector provides. - * <p> - * Elements are processed in order, and the element's localname is used - * to find the associated object property. If the XML does not contain - * an element corresponding to a bean property, that property is left - * with its default value (ie, we don't try to find an element based - * on property name). - * <p> - * If the XML contains an element that does not correspond to any bean - * property, the converter will either throw or ignore the element, - * depending on options settings. - * <tr><td>Other Objects - * <td>not supported - * <td>not supported - * </table> - * * <strong>Warning</strong>: - * <code>java.beans.Introspector</code> holds a cache of introspected objects. - * If you use this converter in an app-server you should call - * <code>Introspector.flushCaches()</code> during deploy. + * Bean-to-XML conversion uses <code>java.beans.Introspector</code>, which + * holds a cache of introspected objects. If you use this conversion in an + * app-server you should call <code>Introspector.flushCaches()</code> during + * deploy. */ public class BeanConverter { - private Bean2XmlDriver _outputDriver; - private Xml2BeanDriver _inputDriver; - - /** - * Creates an instance with all options disabled. This can be used for - * conversions in either direction, and is meant primarily for testing. - */ - public BeanConverter() - { - _outputDriver = new Bean2XmlDriver(); - _inputDriver = new Xml2BeanDriver(); - } - - - /** - * Creates an instance to be used for Bean -> XML conversion, - */ - public BeanConverter(Bean2XmlOptions... options) - { - _outputDriver = new Bean2XmlDriver(options); - _inputDriver = new Xml2BeanDriver(); - } - - - /** - * Creates an instance to be used for XML -> Bean conversion. - */ - public BeanConverter(Xml2BeanOptions options) - { - _outputDriver = new Bean2XmlDriver(); - _inputDriver = new Xml2BeanDriver(options); - } - - - /** * Creates a new DOM document from the passed bean, in which all elements * are members of the specified namespace and will inherit the root's * prefix (if any). @@ -197,10 +54,13 @@ * @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 Conversion options. */ - public Document convertToXml(Object bean, String nsUri, String rootName) + public static Document convertToXml( + Object bean, String nsUri, String rootName, Bean2XmlOptions... options) { - return _outputDriver.convert(bean, nsUri, rootName) + return new Bean2XmlConverter(options) + .convert(bean, nsUri, rootName) .getOwnerDocument(); } @@ -212,10 +72,13 @@ * bean, collection, or simple type. * @param rootName The name given to the root element of the produced * document. + * @param options Conversion options. */ - public Document convertToXml(Object bean, String rootName) + public static Document convertToXml( + Object bean, String rootName, Bean2XmlOptions... options) { - return _outputDriver.convert(bean, rootName) + return new Bean2XmlConverter(options) + .convert(bean, rootName) .getOwnerDocument(); } @@ -227,10 +90,12 @@ * @param dom The source document. * @param klass The desired class to instantiate and fill from this * document. + * @param options Conversion options. */ - public <T> T convertToJava(Document dom, Class<T> klass) + public static <T> T convertToJava( + Document dom, Class<T> klass, Xml2BeanOptions... options) { - return convertToJava(dom.getDocumentElement(), klass); + return convertToJava(dom.getDocumentElement(), klass, options); } @@ -243,9 +108,11 @@ * root element of its document. * @param klass The desired class to instantiate and fill from this * document. + * @param options Conversion options. */ - public <T> T convertToJava(Element root, Class<T> klass) + public static <T> T convertToJava( + Element root, Class<T> klass, Xml2BeanOptions... options) { - return _inputDriver.convert(root, klass); + return new Xml2BeanConverter(options).convert(root, klass); } } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-09-18 15:51:32 UTC (rev 139) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlAppenders.java 2009-09-18 18:45:53 UTC (rev 140) @@ -25,7 +25,7 @@ /** * Packaging class used for XML output appenders. This class is a temporary - * hack, as I move intelligence into {@link Bean2XmlDriver}; the contained + * hack, as I move intelligence into {@link Bean2XmlConverter}; the contained * classes will end up in a new package, once I figure out what the package * structure should be. */ Copied: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java (from rev 130, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java 2009-09-18 18:45:53 UTC (rev 140) @@ -0,0 +1,253 @@ +// 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.bean; + +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.bean.Bean2XmlAppenders.*; +import net.sf.practicalxml.converter.internal.ConversionStrings; +import net.sf.practicalxml.converter.internal.DomUtilToo; +import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; + + +/** + * 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 Bean2XmlConverter +{ + private PrimitiveConversionHelper _helper; + private EnumSet<Bean2XmlOptions> _options = EnumSet.noneOf(Bean2XmlOptions.class); + + public Bean2XmlConverter(Bean2XmlOptions... options) + { + for (Bean2XmlOptions option : options) + _options.add(option); + _helper = 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); + doXsiNamespaceHack(root); + 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.ADD_XSI_TYPE); + } + + + /** + * 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.ADD_XSI_TYPE)) + { + DomUtilToo.setXsiNil(root, false); + } + } + + + private boolean tryToConvertAsPrimitiveOrNull( + Object obj, Class<?> klass, String name, Appender appender) + { + if (obj != null) + klass = obj.getClass(); + + String objType = _helper.getXsdType(klass); + if ((obj == null) || (objType != null)) + { + appender.appendValue(name, objType, _helper.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, DomUtilToo.getXsiTypeForJavaObject(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, ConversionStrings.EL_COLLECTION_ITEM, childAppender); + } + return true; + } + + + private boolean tryToConvertAsMap(Object obj, String name, Appender appender) + { + if (!(obj instanceof Map)) + return false; + + Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(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, DomUtilToo.getXsiTypeForJavaObject(obj)); + Appender childAppender = new IndexedAppender(parent, _options); + for (Object value : (Collection<?>)obj) + { + convert(value, ConversionStrings.EL_COLLECTION_ITEM, childAppender); + } + return true; + } + + + private boolean tryToConvertAsBean(Object bean, String name, Appender appender) + { + Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(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/bean/Bean2XmlDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-09-18 15:51:32 UTC (rev 139) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlDriver.java 2009-09-18 18:45:53 UTC (rev 140) @@ -1,253 +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.bean; - -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.bean.Bean2XmlAppenders.*; -import net.sf.practicalxml.converter.internal.ConversionStrings; -import net.sf.practicalxml.converter.internal.DomUtilToo; -import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; - - -/** - * 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 PrimitiveConversionHelper _helper; - private EnumSet<Bean2XmlOptions> _options = EnumSet.noneOf(Bean2XmlOptions.class); - - public Bean2XmlDriver(Bean2XmlOptions... options) - { - for (Bean2XmlOptions option : options) - _options.add(option); - _helper = 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); - doXsiNamespaceHack(root); - 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.ADD_XSI_TYPE); - } - - - /** - * 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.ADD_XSI_TYPE)) - { - DomUtilToo.setXsiNil(root, false); - } - } - - - private boolean tryToConvertAsPrimitiveOrNull( - Object obj, Class<?> klass, String name, Appender appender) - { - if (obj != null) - klass = obj.getClass(); - - String objType = _helper.getXsdType(klass); - if ((obj == null) || (objType != null)) - { - appender.appendValue(name, objType, _helper.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, DomUtilToo.getXsiTypeForJavaObject(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, ConversionStrings.EL_COLLECTION_ITEM, childAppender); - } - return true; - } - - - private boolean tryToConvertAsMap(Object obj, String name, Appender appender) - { - if (!(obj instanceof Map)) - return false; - - Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(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, DomUtilToo.getXsiTypeForJavaObject(obj)); - Appender childAppender = new IndexedAppender(parent, _options); - for (Object value : (Collection<?>)obj) - { - convert(value, ConversionStrings.EL_COLLECTION_ITEM, childAppender); - } - return true; - } - - - private boolean tryToConvertAsBean(Object bean, String name, Appender appender) - { - Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(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/bean/Xml2BeanConverter.java (from rev 130, branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java) =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanConverter.java (rev 0) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanConverter.java 2009-09-18 18:45:53 UTC (rev 140) @@ -0,0 +1,396 @@ +// 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.bean; + +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 net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.converter.ConversionException; +import net.sf.practicalxml.converter.internal.ConversionStrings; +import net.sf.practicalxml.converter.internal.DomUtilToo; +import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; +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. 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 Xml2BeanConverter +{ + private EnumSet<Xml2BeanOptions> _options; + private PrimitiveConversionHelper _helper; + private Map<Class<?>,Map<String,Method>> _introspectedClasses; + + + public Xml2BeanConverter(Xml2BeanOptions... options) + { + _options = EnumSet.noneOf(Xml2BeanOptions.class); + for (Xml2BeanOptions option : options) + _options.add(option); + + _helper = new PrimitiveConversionHelper(_options.contains(Xml2BeanOptions.EXPECT_XSD_FORMAT)); + _introspectedClasses = new HashMap<Class<?>,Map<String,Method>>(); + } + + +//---------------------------------------------------------------------------- +// Public Methods +//---------------------------------------------------------------------------- + + /** + * Attempts to convert the passed DOM subtree into an object of the + * specified class. + */ + 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)) + { + if (!DomUtilToo.getXsiNil(elem)) + throw new ConversionException("missing/false xsi:nil", 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", 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(ConversionStrings.AT_MAP_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; + } + + + /** + * 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 = DomUtilToo.getXsiType(elem); + if (xsiType == null) + return null; + + String javaType = DomUtilToo.getJavaClassFromXsiType(xsiType); + if (javaType != null) + { + try + { + return Class.forName(javaType); + } + catch (ClassNotFoundException ee) + { + throw new ConversionException( + "invalid Java type specification: " + javaType, elem, ee); + } + } + + return _helper.getJavaType(xsiType); + } + + + private void validateXsiType(Element elem, Class<?> klass) + { + if (!_options.contains(Xml2BeanOptions.REQUIRE_XSI_TYPE)) + return; + + String xsiType = DomUtilToo.getXsiType(elem); + if (xsiType == null) + throw new ConversionException("missing xsi:type", 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(), + 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", 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 ee) + { + throw new ConversionException("unable to introspect", ee); + } + + _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", 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", elem, ee); + } + } +} Deleted: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-09-18 15:51:32 UTC (rev 139) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanDriver.java 2009-09-18 18:45:53 UTC (rev 140) @@ -1,396 +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.bean; - -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 net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.converter.ConversionException; -import net.sf.practicalxml.converter.internal.ConversionStrings; -import net.sf.practicalxml.converter.internal.DomUtilToo; -import net.sf.practicalxml.converter.internal.PrimitiveConversionHelper; -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. 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 Xml2BeanDriver -{ - private EnumSet<Xml2BeanOptions> _options; - private PrimitiveConversionHelper _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 PrimitiveConversionHelper(_options.contains(Xml2BeanOptions.EXPECT_XSD_FORMAT)); - _introspectedClasses = new HashMap<Class<?>,Map<String,Method>>(); - } - - -//---------------------------------------------------------------------------- -// Public Methods -//---------------------------------------------------------------------------- - - /** - * Attempts to convert the passed DOM subtree into an object of the - * specified class. - */ - 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)) - { - if (!DomUtilToo.getXsiNil(elem)) - throw new ConversionException("missing/false xsi:nil", 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", 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(ConversionStrings.AT_MAP_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; - } - - - /** - * 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 = DomUtilToo.getXsiType(elem); - if (xsiType == null) - return null; - - String javaType = DomUtilToo.getJavaClassFromXsiType(xsiType); - if (javaType != null) - { - try - { - return Class.forName(javaType); - } - catch (ClassNotFoundException ee) - { - throw new ConversionException( - "invalid Java type specification: " + javaType, elem, ee); - } - } - - return _helper.getJavaType(xsiType); - } - - - private void validateXsiType(Element elem, Class<?> klass) - { - if (!_options.contains(Xml2BeanOptions.REQUIRE_XSI_TYPE)) - return; - - String xsiType = DomUtilToo.getXsiType(elem); - if (xsiType == null) - throw new ConversionException("missing xsi:type", 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(), - 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", 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 ee) - { - throw new ConversionException("unable to introspect", ee); - } - - _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(); - } - ... [truncated message content] |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-09-22 15:04:54
|
Revision: 141 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=141&view=rev Author: kdgregory Date: 2009-09-22 15:04:28 +0000 (Tue, 22 Sep 2009) Log Message: ----------- replace StringBuffer by StringBuilder Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java 2009-09-18 18:45:53 UTC (rev 140) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java 2009-09-22 15:04:28 UTC (rev 141) @@ -31,7 +31,7 @@ * Appends the contents of the specified element to an existing buffer. * Returns the buffer as a convenience. */ - public StringBuffer convert(Element elem, StringBuffer buf) + public StringBuilder convert(Element elem, StringBuilder buf) { String text = DomUtil.getText(elem); if (text != null) @@ -45,7 +45,7 @@ // Internals //---------------------------------------------------------------------------- - private StringBuffer appendText(String text, StringBuffer buf) + private StringBuilder appendText(String text, StringBuilder buf) { buf.append('"') .append(JsonUtil.escape(text)) @@ -54,7 +54,7 @@ } - private StringBuffer appendChildren(Element elem, StringBuffer buf) + private StringBuilder appendChildren(Element elem, StringBuilder buf) { buf.append("{"); List<Element> children = DomUtil.getChildren(elem); Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java 2009-09-18 18:45:53 UTC (rev 140) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java 2009-09-22 15:04:28 UTC (rev 141) @@ -37,8 +37,8 @@ public void convertAndAssert(String expected, ElementNode rootNode) { Element root = rootNode.toDOM().getDocumentElement(); - StringBuffer buf = new Xml2JsonConverter() - .convert(root, new StringBuffer()); + StringBuilder buf = new Xml2JsonConverter() + .convert(root, new StringBuilder(256)); assertEquals(expected, buf.toString()); } 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-09-22 18:05:30
|
Revision: 144 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=144&view=rev Author: kdgregory Date: 2009-09-22 17:32:02 +0000 (Tue, 22 Sep 2009) Log Message: ----------- xml->json: support arrays Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/package.html branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java 2009-09-22 15:27:06 UTC (rev 143) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Xml2JsonConverter.java 2009-09-22 17:32:02 UTC (rev 144) @@ -14,9 +14,12 @@ package net.sf.practicalxml.converter.json; +import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import net.sf.practicalxml.DomUtil; @@ -43,16 +46,24 @@ //---------------------------------------------------------------------------- /** - * Appends the contents of the specified element to an existing buffer. + * Converts the subtree rooted at <code>elem</code> to a JSON string. + */ + public String convert(Element elem) + { + return convert(elem, new StringBuilder(256)).toString(); + } + + + /** + * Converts the subtree rooted at <code>elem</code> to a JSON string, + * appending to an existing buffer. This is useful when building a + * JSON assignment statment (eg: "var x = OBJECT"). + * <p> * Returns the buffer as a convenience. */ public StringBuilder convert(Element elem, StringBuilder buf) { - String text = DomUtil.getText(elem); - if (text != null) - return appendText(text, buf); - else - return appendChildren(elem, buf); + return append(buf, elem); } @@ -60,8 +71,22 @@ // Internals //---------------------------------------------------------------------------- - private StringBuilder appendText(String text, StringBuilder buf) + // yes, this method is just a restatement of convert(Element,StringBuilder) + // I want all the internal appenders named "append". + private StringBuilder append(StringBuilder buf, Element elem) { + List<Element> children = DomUtil.getChildren(elem); + String text = DomUtil.getText(elem); + + if ((children.size() > 0) || (text == null)) + return appendObject(buf, children); + else + return appendText(buf, text); + } + + + private StringBuilder appendText(StringBuilder buf, String text) + { buf.append('"') .append(JsonUtil.escape(text)) .append('"'); @@ -69,20 +94,74 @@ } - private StringBuilder appendChildren(Element elem, StringBuilder buf) + private StringBuilder appendObject(StringBuilder buf, List<Element> children) { + List<String> names = new ArrayList<String>(); + Map<String,List<Element>> arrays = new HashMap<String,List<Element>>(); + Map<String,Element> nonArrays = new HashMap<String,Element>(); + categorizeChildren(children, names, arrays, nonArrays); + buf.append("{"); - List<Element> children = DomUtil.getChildren(elem); - for (Iterator<Element> childItx = children.iterator() ; childItx.hasNext() ; ) + for (Iterator<String> itx = names.iterator() ; itx.hasNext() ; ) { - Element child = childItx.next(); - buf.append(DomUtil.getLocalName(child)) - .append(": "); - convert(child, buf); - if (childItx.hasNext()) + String name = itx.next(); + buf.append(name).append(": "); + if (arrays.containsKey(name)) + appendArray(buf, arrays.get(name)); + else + append(buf, nonArrays.get(name)); + if (itx.hasNext()) buf.append(", "); } buf.append("}"); return buf; } + + + private StringBuilder appendArray(StringBuilder buf, List<Element> values) + { + buf.append("["); + for (Iterator<Element> itx = values.iterator() ; itx.hasNext() ; ) + { + Element child = itx.next(); + append(buf, child); + if (itx.hasNext()) + buf.append(", "); + } + buf.append("]"); + return buf; + } + + + /** + * Examines the children of the passed element and categorizes them as + * "array" or "not array", while tracking the first appearance of the + * element name in document order. + */ + private void categorizeChildren( + List<Element> children, List<String> names, + Map<String,List<Element>> arrays, Map<String,Element> nonArrays) + { + for (Element child : children) + { + String name = DomUtil.getLocalName(child); + if (arrays.containsKey(name)) + { + arrays.get(name).add(child); + } + else if (nonArrays.containsKey(name)) + { + Element prev = nonArrays.remove(name); + List<Element> list = new ArrayList<Element>(2); + list.add(prev); + list.add(child); + arrays.put(name, list); + } + else + { + nonArrays.put(name, child); + names.add(name); + } + } + } } Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/package.html =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/package.html 2009-09-22 15:27:06 UTC (rev 143) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/package.html 2009-09-22 17:32:02 UTC (rev 144) @@ -2,36 +2,39 @@ <body> This package contains classes to convert JSON (JavaScript Object Notation) -strings (as defined <a href="http://www.json.org/">json.org</a> ) to and +strings (as defined <a href="http://www.json.org/">json.org</a> ) to and from an XML DOM. Although both XML and JSON are textual representations of hierarchical data, there are some peculiarities in the conversion: <dl> <dt> Container Element <dd> Although elements within a JSON object are named, the object itself is - not. XML requires a named root element, so the JSON->XML conversion - creates a root element named "data", and the XML->JSON conversion ignores - the passed root element. + not. XML requires a named root element, so the JSON to XML conversion + creates a root element named "data", and the XML to JSON conversion + ignores the passed root element. <dt> Document Order <dd> In XML, document order is important and may be validated; in JSON, the order of elements within an object is not important. To support arrays - (qv), the XML->JSON conversion intentionally breaks document order. And + the XML to JSON conversion intentionally breaks document order (qv). And as JSON has no intrinsic order, you'll need to explicitly re-arrange the - result of a JSON->XML conversion prior to any validation. + result of a JSON to XML conversion prior to any validation. <dt> Non-String Data <dd> JSON supports numbers and boolean literals in addition to quote-delimited - strings. The JSON->XML conversion will handle these values transparently. - The default XML->JSON conversion writes all content as quote-delimited + strings. The JSON to XML conversion will handle these values transparently. + The default XML to JSON conversion writes all content as quote-delimited strings, but there is a conversion option to recognize a limited set of - non-string elements based on the <code>xsi:type</code> attribute. -<dt> Arrays + non-string elements based on the <code>xsi:type</code> attribute. +<dt> Arrays, XML to JSON <dd> XML does not have a defined array construct, but may repeat elements; JSON has a defined array construct, and repeated elements will overwrite the - former definition. To avoid this problem, the XML->JSON conversion will - identify all repeated elements and create an array construct. The default - JSON->XML conversion creates a parent-children construct (which is more - palatable to subsequent XML->Java conversion), but a conversion option - allows conversion to a repeated element. + former definition. To avoid this problem, the XML to JSON converter will + create a JSON array construct from all siblings with the same localname. +<dt> Arrays, JSON to XML +<dd> By default, arrays are converted into a single parent element, with one + child named "data" for each element of the array; this is similar to the + Java to XML conversion. Optionally, you may convert an array into a series + of repeated elements (siblings), all with the same name. When using this + option, conversion will fail if presented with a top-level JSON array. </dl> </body> Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java 2009-09-22 15:27:06 UTC (rev 143) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestXml2JsonConverter.java 2009-09-22 17:32:02 UTC (rev 144) @@ -37,9 +37,8 @@ public void convertAndAssert(String expected, ElementNode rootNode) { Element root = rootNode.toDOM().getDocumentElement(); - StringBuilder buf = new Xml2JsonConverter() - .convert(root, new StringBuilder(256)); - assertEquals(expected, buf.toString()); + String json = new Xml2JsonConverter().convert(root); + assertEquals(expected, json); } @@ -86,6 +85,53 @@ } + public void testArray() throws Exception + { + // note that "argle" elements are not adjacent, must become adjacent + convertAndAssert( + "{foo: \"bar\", argle: [\"bargle\", \"wargle\"], baz: \"bar\"}", + element("data", + element("foo", text("bar")), + element("argle", text("bargle")), + element("baz", text("bar")), + element("argle", text("wargle")))); + } + + + public void testArrayWithNestedObject() throws Exception + { + convertAndAssert( + "{foo: \"bar\", argle: [\"bargle\", {foo: \"bar\", bar: \"baz\"}]}", + element("data", + element("foo", text("bar")), + element("argle", text("bargle")), + element("argle", + element("foo", text("bar")), + element("bar", text("baz"))))); + } + + + // this covers documents parsed with "ignorable whitespace" + public void testMixedContentWithWhitespace() throws Exception + { + convertAndAssert( + "{foo: \"bar\"}", + element("data", + text(" "), + element("foo", text("bar")), + text("\n"))); + } + + + public void testWhitespace() throws Exception + { + convertAndAssert( + "{foo: \" \"}", + element("data", + element("foo", text(" ")))); + } + + public void testStringEscaping() throws Exception { convertAndAssert( 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-09-22 19:07:02
|
Revision: 145 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=145&view=rev Author: kdgregory Date: 2009-09-22 19:06:46 +0000 (Tue, 22 Sep 2009) Log Message: ----------- implement Json2XmlOptions.ARRAYS_AS_REPEATED_ELEMENTS Modified Paths: -------------- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Json2XmlConverter.java branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJson2XmlConverter.java Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Json2XmlConverter.java =================================================================== --- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Json2XmlConverter.java 2009-09-22 17:32:02 UTC (rev 144) +++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/json/Json2XmlConverter.java 2009-09-22 19:06:46 UTC (rev 145) @@ -17,6 +17,7 @@ import java.util.EnumSet; import org.w3c.dom.Element; +import org.w3c.dom.Node; import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.converter.ConversionException; @@ -72,15 +73,22 @@ /** * Top-level parser entry: expects the string to be a single object - * definition, without anything before or after the outer brace pair. + * or array definition, without anything before or after the outer + * brace/bracket pair. */ private void parse(Element parent) { - expect("{"); - parseObject(parent); + String first = nextToken(); + if (first.equals("{")) + parseObject(parent); + else if (first.equals("[")) + parseArray(parent); + else + throw new ConversionException(commonExceptionText( + "unexpected content start of line")); if (nextToken().length() > 0) throw new ConversionException(commonExceptionText( - "unexpected content after closing brace")); + "unexpected content at end of line")); } @@ -125,13 +133,29 @@ private void parseArray(Element parent) { + String childName = "data"; + if (_options.contains(Json2XmlOptions.ARRAYS_AS_REPEATED_ELEMENTS)) + { + // we come in here with the assumption that array elements will + // be created as children of "parent" ... but now we learn that + // they're actually siblings, and the passed parent will disappear + // ... so here's an ugly little hack to make that happen + Node realParent = parent.getParentNode(); + if (!(realParent instanceof Element)) + throw new ConversionException(commonExceptionText( + "cannot convert top-level array as repeated elements")); + childName = DomUtil.getLocalName(parent); + realParent.removeChild(parent); + parent = (Element)realParent; + } + String next = nextToken(); if (atEndOfSequence(next, "]", false)) return; while (true) { - Element child = DomUtil.appendChild(parent, "data"); + Element child = DomUtil.appendChild(parent, childName); next = valueDispatch(next, child); if (atEndOfSequence(next, "]", true)) return; Modified: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJson2XmlConverter.java =================================================================== --- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJson2XmlConverter.java 2009-09-22 17:32:02 UTC (rev 144) +++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/json/TestJson2XmlConverter.java 2009-09-22 19:06:46 UTC (rev 145) @@ -402,6 +402,58 @@ } + public void testConvertTopLevelArray() throws Exception + { + String src = "[123, 456]"; + + Element root = new Json2XmlConverter(src).convert(); + assertEquals("data", root.getNodeName()); + assertEquals(2, root.getChildNodes().getLength()); + + Element child1 = (Element)root.getFirstChild(); + assertEquals("data", child1.getNodeName()); + assertEquals("123", DomUtil.getText(child1)); + assertEquals(0, DomUtil.getChildren(child1).size()); + + Element child2 = (Element)child1.getNextSibling(); + assertEquals("data", child2.getNodeName()); + assertEquals("456", DomUtil.getText(child2)); + assertEquals(0, DomUtil.getChildren(child2).size()); + } + + + public void testConvertArrayAsRepeatedElements() throws Exception + { + // leading and trailing elements to ensure sibling order + String src = "{foo: \"abc\", bar: [123, 456], baz: \"def\"}"; + + Element root = new Json2XmlConverter(src, Json2XmlOptions.ARRAYS_AS_REPEATED_ELEMENTS) + .convert(); + assertEquals("data", root.getNodeName()); + assertEquals(4, root.getChildNodes().getLength()); + + Element child1 = (Element)root.getFirstChild(); + assertEquals("foo", child1.getNodeName()); + assertEquals("abc", DomUtil.getText(child1)); + assertEquals(0, DomUtil.getChildren(child1).size()); + + Element child2 = (Element)child1.getNextSibling(); + assertEquals("bar", child2.getNodeName()); + assertEquals("123", DomUtil.getText(child2)); + assertEquals(0, DomUtil.getChildren(child2).size()); + + Element child3 = (Element)child2.getNextSibling(); + assertEquals("bar", child3.getNodeName()); + assertEquals("456", DomUtil.getText(child3)); + assertEquals(0, DomUtil.getChildren(child3).size()); + + Element child4 = (Element)child3.getNextSibling(); + assertEquals("baz", child4.getNodeName()); + assertEquals("def", DomUtil.getText(child4)); + assertEquals(0, DomUtil.getChildren(child4).size()); + } + + public void testFailConvertUnterminatedArray() throws Exception { String src = "{foo: [123, 456"; @@ -432,4 +484,22 @@ // success } } + + + public void testFailConvertArrayAsRootUsingRepeatedElements() throws Exception + { + String src = "[123, 456]"; + + try + { + new Json2XmlConverter(src, Json2XmlOptions.ARRAYS_AS_REPEATED_ELEMENTS) + .convert(); + fail("able to create XML with multiple root elements"); + } + catch (ConversionException ee) + { + // success + } + } + } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |