[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.
|