There is a bug in the current marshalling implementation which adds
xsi:type="java.lang.String" to all enumerations. I noticed that this is
already surppressed for instances of the DateFieldHandler. By adding a check
for EnumFieldHandler, the xsi:type attribute is surppressed and unmarshalling
works as expected. I have attached an updated file.
-Steve
/**
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
* statements and notices. Redistributions must also contain a
* copy of this document.
*
* 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. The name "Exolab" must not be used to endorse or promote
* products derived from this Software without prior written
* permission of Intalio, Inc. For written permission,
* please contact [EMAIL PROTECTED]
*
* 4. Products derived from this Software may not be called "Exolab"
* nor may "Exolab" appear in their names without prior written
* permission of Intalio, Inc. Exolab is a registered
* trademark of Intalio, Inc.
*
* 5. Due credit should be given to the Exolab Project
* (http://www.exolab.org/).
*
* THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 1999, 2000 (C) Intalio, Inc. All Rights Reserved.
*
* $Id: Marshaller.java,v 1.82 2001/12/05 22:44:23 blandin Exp $
*/
package org.exolab.castor.xml;
//-- xml related imports
import org.xml.sax.*;
import org.w3c.dom.*;
import org.apache.xml.serialize.Serializer;
import org.apache.xml.serialize.OutputFormat;
import org.exolab.castor.mapping.ClassDescriptor;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.FieldHandler;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.types.AnyNode;
import org.exolab.castor.xml.descriptors.StringClassDescriptor;
import org.exolab.castor.xml.handlers.DateFieldHandler;
import org.exolab.castor.xml.handlers.EnumFieldHandler;
import org.exolab.castor.xml.util.*;
import org.exolab.castor.xml.ClassDescriptorEnumeration;
import org.exolab.castor.util.Configuration;
import org.exolab.castor.util.Messages;
import org.exolab.castor.util.MimeBase64Encoder;
import org.exolab.castor.util.List;
import org.exolab.castor.util.Stack;
import org.xml.sax.helpers.AttributeListImpl;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.Writer;
import java.lang.reflect.*;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
/**
* A Marshaller to allowing serializing Java Object's to XML
* @author <a href="mailto:[EMAIL PROTECTED]">Keith Visco</a>
* @version $Revision: 1.82 $ $Date: 2001/12/05 22:44:23 $
**/
public class Marshaller extends MarshalFramework {
/**
* Message name for a non sax capable serializer error
**/
private static final String SERIALIZER_NOT_SAX_CAPABLE
= "conf.serializerNotSaxCapable";
/**
* The XSI Namespace URI
**/
public static final String XSI_NAMESPACE
= "http://www.w3.org/2001/XMLSchema-instance";
/**
* The namespace declaration String
**/
private static final String XMLNS = "xmlns";
/**
* Namespace declaration for xml schema instance
**/
private static final String XSI_PREFIX = "xsi";
/**
* The xsi:type attribute
**/
private static final String XSI_TYPE = "xsi:type";
/**
* The CDATA type..uses for SAX attributes
**/
private static final String CDATA = "CDATA";
/**
* Constants used for the QName handling
*/
private static final String QNAME_NAME = "QName";
private static final String DEFAULT_PREFIX = "ns";
private static int NAMESPACE_COUNTER = 0;
/**
* A static flag used to enable debugging when using
* the static marshal methods.
**/
public static boolean enableDebug = false;
/**
* A flag indicating whether or not to generate
* debug information
**/
private boolean _debug = false;
/**
* The print writer used for logging
**/
private PrintWriter _logWriter = null;
/**
* Insert NameSpace prefix declarations at the root node
*/
private boolean _nsPrefixAtRoot = false;
/**
* Name of the root element to use
*/
private String _rootElement = null;
/**
* The default namespace
**/
private String _defaultNamespace = null;
/**
* The validation flag
**/
private boolean _validate = false;
/**
* The current namespace scoping
**/
private List _nsScope = null;
/**
* The ClassDescriptorResolver used for resolving XMLClassDescriptors
**/
private ClassDescriptorResolver _cdResolver = null;
/**
* The namespace stack
*/
private Namespaces _namespaces = null;
private DocumentHandler _handler = null;
private Serializer _serializer = null;
private XMLNaming _naming = null;
private Node _node = null;
/**
* A boolean to indicate whether or not we are
* marshalling as a complete document or not.
**/
private boolean _asDocument = true;
/**
* The depth of the sub tree, 0 denotes document level
**/
int depth = 0;
private List _packages = null;
private Stack _parents = null;
private boolean _marshalExtendedType = true;
/**
* An instance of StringClassDescriptor
**/
private static final StringClassDescriptor _StringClassDescriptor
= new StringClassDescriptor();
/**
* Creates a new Marshaller
**/
public Marshaller( DocumentHandler handler ) {
if ( handler == null )
throw new IllegalArgumentException( "Argument 'handler' is null." );
_handler = handler;
// call internal initializer
initialize();
} //-- Marshaller
/**
* Creates a new Marshaller with the given writer
* @param out the Writer to serialize to
**/
public Marshaller( Writer out )
throws IOException
{
if (out == null)
throw new IllegalArgumentException( "Argument 'out' is null.");
// call internal initializer
initialize();
_serializer = Configuration.getSerializer();
if (_serializer == null)
throw new RuntimeException("Unable to obtain serializer");
_serializer.setOutputCharStream( out );
_handler = _serializer.asDocumentHandler();
if ( _handler == null ) {
String err = Messages.format( this.SERIALIZER_NOT_SAX_CAPABLE,
_serializer.getClass().getName() );
throw new RuntimeException( err );
}
} //-- Marshaller
/**
* Creates a new Marshaller
*/
public Marshaller( Node node )
{
if ( node == null )
throw new IllegalArgumentException( "Argument 'node' is null." );
_node = node;
_handler = new SAX2DOMHandler( node );
// call internal initializer
initialize();
} //-- Marshaller
/**
* Initializes this Marshaller. This is common code shared among
* the Constructors
**/
private void initialize() {
_debug = enableDebug;
_namespaces = new Namespaces();
_nsScope = new List(3);
_packages = new List(3);
_cdResolver = new ClassDescriptorResolverImpl();
_parents = new Stack();
_validate = Configuration.marshallingValidation();
_naming = XMLNaming.getInstance();
setNamespaceMapping( XSI_PREFIX, XSI_NAMESPACE );
} //-- initialize();
/**
* Sets whether or not to marshal as a document which includes
* the XML declaration, and if necessary the DOCTYPE declaration.
* By default the Marshaller will marshal as a well formed
* XML fragment (no XML declaration or DOCTYPE).
*
* @param asDocument a boolean, when true, indicating to marshal
* as a complete XML document.
**/
public void setMarshalAsDocument(boolean asDocument) {
_asDocument = asDocument;
if (_serializer != null) {
OutputFormat format = Configuration.getOutputFormat();
format.setOmitXMLDeclaration( ! asDocument );
_serializer.setOutputFormat( format );
try {
_handler = _serializer.asDocumentHandler();
}
catch (java.io.IOException iox) {
//-- we can ignore this exception since it shouldn't
//-- happen. If _serializer is not null, it means
//-- we've already called this method sucessfully
//-- in the Marshaller() constructor
}
}
} //-- setMarshalAsDocument
/**
* Sets the given mapping to be used by the marshalling
* Framework. If a ClassDescriptorResolver exists
* This mapping will be added to the existing Resolver. Otherwise
* a new ClassDescriptorResolver will be created.
*
* @param mapping the mapping to using during marshalling
**/
public void setMapping( Mapping mapping )
throws MappingException
{
if (_cdResolver == null)
_cdResolver = new ClassDescriptorResolverImpl();
_cdResolver.setMappingLoader( (XMLMappingLoader) mapping.getResolver(
Mapping.XML ) );
} //-- setMapping
/**
* Sets the mapping for the given Namespace prefix
* @param nsPrefix the namespace prefix
* @param nsURI the namespace that the prefix resolves to
**/
public void setNamespaceMapping(String nsPrefix, String nsURI) {
if ((nsURI == null) || (nsURI.length() == 0)) {
String err = "namespace URI must not be null.";
throw new IllegalArgumentException(err);
}
if ((nsPrefix == null) || (nsPrefix.length() == 0)) {
_defaultNamespace = nsURI;
return;
}
_namespaces.addNamespace(nsPrefix, nsURI);
} //-- setNamespacePrefix
/**
* Sets the name of the root element to use
* @param The name of the root element to use
*/
public void setRootElement(String rootElement)
{
_rootElement = rootElement;
} //-- setRootElement
/**
* Returns the name of the root element to use
* @return Returns the name of the root element to use
*/
public String getRootElement()
{
return _rootElement;
} //-- getRootElement
/**
* Set to True to declare the given namespace mappings at the root node.
Default is False.
* @param nsPrefixAtRoot
*/
public void setNSPrefixAtRoot(boolean nsPrefixAtRoot)
{
_nsPrefixAtRoot = nsPrefixAtRoot;
}
/**
* Returns True if the given namespace mappings will be declared at the root
node.
* @return Returns True if the given namespace mappings will be declared at
the root node.
*/
public boolean getNSPrefixAtRoot()
{
return _nsPrefixAtRoot;
}
/**
* Sets the ClassDescriptorResolver to use during unmarshalling
* @param cdr the ClassDescriptorResolver to use
* @see #setMapping
* <BR />
* <B>Note:</B> This method will nullify any Mapping
* currently being used by this Marshaller
**/
public void setResolver( ClassDescriptorResolver cdr ) {
if (cdr != null) _cdResolver = cdr;
} //-- setResolver
/**
* Sets whether or not to validate the object model
* before marshalling. By default validation is enabled.
* This method is really for debugging.
* I do not recommend turning off validation, since
* you could marshal a document, which you can then
* not unmarshal. If you know the object model
* is guaranteed to be valid, disabling validation will
* improve performace.
*
* @param validate the boolean indicating whether or not to
* validate the object model before marshalling.
**/
public void setValidation(boolean validate) {
_validate = validate;
} //-- setValidation
/**
* If True the marshaller will use the 'xsi:type' attribute
* to marshall a field value that extended the defined field type.
* Default is True.
*/
public void setMarshalExtendedType(boolean marshalExtendedType)
{
_marshalExtendedType = marshalExtendedType;
} //-- setMarshalExtendedType
/**
* If True the marshaller will use the 'xsi:type' attribute
* to marshall a field value that extended the defined field type.
* Default is True.
* @return If True the marshaller will use the 'xsi:type' attribute
* to marshall a field value that extended the defined field type.
* Default is True.
*/
public boolean getMarshalExtendedType()
{
return _marshalExtendedType;
} //-- setMarshallExtendedType
/**
* Marshals the given Object as XML using the given writer
* @param obj the Object to marshal
* @param out the writer to marshal to
* @exception org.exolab.castor.xml.MarshalException
* @exception org.exolab.castor.xml.ValidationException
**/
public static void marshal(Object object, Writer out)
throws MarshalException, ValidationException
{
if (object == null)
throw new MarshalException("object must not be null");
if (enableDebug) {
System.out.print("- Marshaller called using ");
System.out.println("*static* marshal(Object, Writer)");
}
Marshaller marshaller;
try {
marshaller = new Marshaller(out);
marshaller.marshal(object);
} catch ( IOException except ) {
throw new MarshalException( except );
}
} //-- marshal
/**
* Marshals the given Object as XML using the given DocumentHandler
* to send events to.
* @param obj the Object to marshal
* @param handler the DocumentHandler to marshal to
* @exception org.exolab.castor.xml.MarshalException
* @exception org.exolab.castor.xml.ValidationException
**/
public static void marshal(Object object, DocumentHandler handler)
throws MarshalException, ValidationException
{
if (object == null)
throw new MarshalException("object must not be null");
if (enableDebug) {
System.out.print("- Marshaller called using ");
System.out.println("*static* marshal(Object, DocumentHandler)");
}
Marshaller marshaller;
marshaller = new Marshaller(handler);
marshaller.marshal(object);
} //-- marshal
/**
* Marshals the given Object as XML using the given DOM Node
* to send events to.
* @param obj the Object to marshal
* @param node the DOM Node to marshal to
* @exception org.exolab.castor.xml.MarshalException
* @exception org.exolab.castor.xml.ValidationException
**/
public static void marshal(Object object, Node node)
throws MarshalException, ValidationException
{
if (object == null)
throw new MarshalException("object must not be null");
if (enableDebug) {
System.out.print("- Marshaller called using ");
System.out.println("*static* marshal(Object, Node)");
}
Marshaller marshaller;
marshaller = new Marshaller(node);
marshaller.marshal(object);
} //-- marshal
/**
* Marshals the given Object as XML using the DocumentHandler
* for this Marshaller.
* @param obj the Object to marshal
* @exception org.exolab.castor.xml.MarshalException
* @exception org.exolab.castor.xml.ValidationException
**/
public void marshal(Object object)
throws MarshalException, ValidationException
{
if (object == null)
throw new MarshalException("object must not be null");
if (_debug) {
System.out.println("Marshalling " + object.getClass().getName());
}
if (object instanceof AnyNode) {
try{
AnyNode2SAX.fireEvents((AnyNode)object, _handler);
} catch(SAXException e) {
throw new MarshalException(e);
}
}
else {
validate(object);
if (_asDocument) {
try {
_handler.startDocument();
marshal(object, null, _handler);
_handler.endDocument();
} catch (SAXException sx) {
throw new MarshalException(sx);
}
}
else marshal(object, null, _handler);
}
} //-- marshal
/**
* Marshals the given object, using the given descriptor
* and document handler.
*
* <BR/>
* <B>Note:</B>
* <I>
* It is an error if this method is called with an
* AttributeDescriptor.
* </I>
* @param descriptor the XMLFieldDescriptor for the given object
* @param handler the DocumentHandler to marshal to
* @exception org.exolab.castor.xml.MarshalException
* @exception org.exolab.castor.xml.ValidationException
* during marshaling
**/
private void marshal
(Object object,
XMLFieldDescriptor descriptor,
DocumentHandler handler)
throws MarshalException, ValidationException
{
if (object == null) {
String err = "Marshaller#marshal: null parameter: 'object'";
throw new MarshalException(err);
}
if (descriptor != null && descriptor.isTransient())
return;
if (object instanceof AnyNode) {
try {
AnyNode2SAX.fireEvents((AnyNode) object, handler);
}catch (SAXException e) {
throw new MarshalException(e);
}
return;
}
boolean containerField = false;
if (descriptor != null && descriptor.isContainer())
containerField = true;
//-- add object to stack so we don't potentially get into
//-- an endlessloop
if (_parents.search(object) >= 0) return;
_parents.push(object);
Class _class = object.getClass();
boolean byteArray = false;
if (_class.isArray())
byteArray = (_class.getComponentType() == Byte.TYPE);
boolean atRoot = false;
if (descriptor == null) {
descriptor = new XMLFieldDescriptorImpl(_class, "root", null, null);
atRoot = true;
}
//-- calculate Object's name
String name = descriptor.getXMLName();
if (atRoot && _rootElement!=null)
name = _rootElement;
if (name == null) {
name = _class.getName();
//-- remove package information from name
int idx = name.lastIndexOf('.');
if (idx >= 0) {
name = name.substring(idx+1);
}
//-- remove capitalization
name = _naming.toXMLName(name);
}
//-- obtain the class descriptor
XMLClassDescriptor classDesc = null;
boolean saveType = false; /* flag for xsi:type */
if (_class == descriptor.getFieldType())
classDesc = (XMLClassDescriptor)descriptor.getClassDescriptor();
if (classDesc == null) {
//-- check for primitive or String, we need to use
//-- the special #isPrimitive method of this class
//-- so that we can check for the primitive wrapper
//-- classes
if (isPrimitive(_class) || byteArray) {
classDesc = _StringClassDescriptor;
//-- check to see if we need to save the xsi:type
//-- for this class
if (_class != descriptor.getFieldType()) {
saveType = (!descriptor.getFieldType().isPrimitive());
}
}
else {
//-- save package information for use when searching
//-- for MarshalInfo classes
String className = _class.getName();
int idx = className.lastIndexOf(".");
if (idx > 0) {
String pkgName = className.substring(0,idx+1);
if (!_packages.contains(pkgName))
_packages.add(pkgName);
}
if (_marshalExtendedType) {
// marshall as the actual value
classDesc = getClassDescriptor(_class);
saveType = (_class != descriptor.getFieldType());
} else {
// marshall as the base field type
_class = descriptor.getFieldType();
classDesc = getClassDescriptor(_class);
}
if (atRoot && _rootElement!=null)
name = _rootElement;
else if (descriptor.getXMLName()==null)
name = classDesc.getXMLName();
}
if (classDesc == null) {
//-- make sure we are allowed to marshal Object
if ((_class == Void.class) ||
(_class == Object.class) ||
(_class == Class.class)) {
throw new MarshalException
(MarshalException.BASE_CLASS_OR_VOID_ERR);
}
_parents.pop();
return;
}
}
//-- Suppress 'xsi:type' attributes when Castor is able to infer the
//-- information from the mapping file
//-- XXXX Date fix
if (saveType && (descriptor.getHandler() instanceof DateFieldHandler))
saveType = false;
if (saveType && (descriptor.getHandler() instanceof EnumFieldHandler))
saveType = false;
//-- XXXX end Date fix
//-- handle Attributes
AttributeListImpl atts = new AttributeListImpl();
XMLFieldDescriptor[] descriptors = classDesc.getAttributeDescriptors();
for (int i = 0; i < descriptors.length; i++) {
if (descriptors[i] == null) continue;
XMLFieldDescriptor attDescriptor = descriptors[i];
String xmlName = attDescriptor.getXMLName();
//-- handle attribute namespaces
//-- [need to add this support]
Object value = null;
try {
value = attDescriptor.getHandler().getValue(object);
}
catch(IllegalStateException ise) {
continue;
}
//-- handle IDREF(S)
if (attDescriptor.isReference() && (value != null)) {
if (attDescriptor.isMultivalued()) {
Object[] objects = (Object[])value;
if (objects.length == 0) continue;
StringBuffer sb = new StringBuffer();
for (int v = 0; v < objects.length; v++) {
if (v > 0) sb.append(' ');
sb.append(getObjectID(objects[v]).toString());
}
value = sb;
}
else {
value = getObjectID(value);
}
}
if (value == null) continue;
//check if the value is a QName that needs to
//be resolved ({URI}value -> ns:value).
String valueType = attDescriptor.getSchemaType();
if ((valueType != null) && (valueType.equals(QNAME_NAME)))
value = resolveQName(value, attDescriptor, atts);
atts.addAttribute(xmlName, CDATA, value.toString());
}
//-- Look for attributes in container fields,
//-- (also handle container in container)
/* REMOVED For now (KV)
processContainerAttributes(object, classDesc, atts);
*/
//-- xsi:type
if (saveType) {
saveType = declareNamespace(XSI_PREFIX, XSI_NAMESPACE, atts);
atts.addAttribute(XSI_TYPE, CDATA, "java:"+_class.getName());
}
//------------------/
//- Create element -/
//------------------/
//-- namespace management
String nsPrefix = descriptor.getNameSpacePrefix();
if (nsPrefix == null) nsPrefix = classDesc.getNameSpacePrefix();
String nsURI = descriptor.getNameSpaceURI();
if (nsURI == null) nsURI = classDesc.getNameSpaceURI();
if ((nsURI == null) && (nsPrefix != null)) {
nsURI = _namespaces.getNamespaceURI(nsPrefix);
}
else if ((nsPrefix == null) && (nsURI != null)) {
nsPrefix = (String) _namespaces.getNamespacePrefix(nsURI);
}
boolean declaredNS = false;
//-- declare namespace at this element scope?
if (nsURI != null)
//-- only if prefix not already been declared at root (via
setNSPrefixAtRoot method)
if (!(_nsPrefixAtRoot && nsPrefix!=null))
declaredNS = declareNamespace(nsPrefix, nsURI, atts);
//-- declare all namespace prefix at root (via setNSPrefixAtRoot
method)?
if (_nsPrefixAtRoot && atRoot)
{
//-- insert all namespace declarations at the root level
_namespaces.declareAsAttributes(atts);
}
String qName = null;
if (nsPrefix != null) {
int len = nsPrefix.length();
if (len > 0) {
StringBuffer sb = new StringBuffer(len+name.length()+1);
sb.append(nsPrefix);
sb.append(':');
sb.append(name);
qName = sb.toString();
}
else qName = name;
}
else qName = name;
//check if the value is a QName that needs to
//be resolved ({URI}value -> ns:value)
String valueType = descriptor.getSchemaType();
if ((valueType != null) && (valueType.equals(QNAME_NAME)))
object = resolveQName(object, descriptor, atts);
try {
if (!containerField)
handler.startElement(qName, atts);
}
catch (org.xml.sax.SAXException sx) {
throw new MarshalException(sx);
}
//-- handle text content
XMLFieldDescriptor cdesc = classDesc.getContentDescriptor();
if (cdesc != null) {
Object obj = null;
try {
obj = cdesc.getHandler().getValue(object);
}
catch(IllegalStateException ise) {};
if (obj != null) {
String str = obj.toString();
if ((str != null) && (str.length() > 0)) {
char[] chars = str.toCharArray();
try {
handler.characters(chars, 0, chars.length);
}
catch(org.xml.sax.SAXException sx) {
throw new MarshalException(sx);
}
}
}
}
// special case for byte[]
else if (byteArray) {
//-- Base64Encoding
MimeBase64Encoder encoder = new MimeBase64Encoder();
encoder.translate((byte[])object);
char[] chars = encoder.getCharArray();
try {
handler.characters(chars, 0, chars.length);
}
catch(org.xml.sax.SAXException sx) {
throw new MarshalException(sx);
}
}
/* special case for Strings and primitives */
else if (isPrimitive(_class)) {
char[] chars = object.toString().toCharArray();
try {
handler.characters(chars,0,chars.length);
}
catch(org.xml.sax.SAXException sx) {
throw new MarshalException(sx);
}
}
//-- handle daughter elements
descriptors = classDesc.getElementDescriptors();
++depth;
for (int i = 0; i < descriptors.length; i++) {
XMLFieldDescriptor elemDescriptor = descriptors[i];
Object obj = null;
try {
obj = elemDescriptor.getHandler().getValue(object);
}
catch(IllegalStateException ise) {
continue;
}
if (obj == null) continue;
Class type = obj.getClass();
//-- handle arrays
if (type.isArray()) {
//-- special case for byte[]
if (type.getComponentType() == Byte.TYPE) {
marshal(obj, elemDescriptor, handler);
}
else {
int length = Array.getLength(obj);
for (int j = 0; j < length; j++) {
Object item = Array.get(obj, j);
if (item != null)
marshal(item, elemDescriptor, handler);
}
}
}
//-- handle enumerations
else if (obj instanceof java.util.Enumeration) {
Enumeration enum = (Enumeration)obj;
while (enum.hasMoreElements()) {
Object item = enum.nextElement();
if (item != null)
marshal(item, elemDescriptor, handler);
}
}
else marshal(obj, elemDescriptor, handler);
}
//-- finish element
try {
if (!containerField)
handler.endElement(qName);
}
catch(org.xml.sax.SAXException sx) {
throw new MarshalException(sx);
}
--depth;
_parents.pop();
if (declaredNS) _nsScope.remove(nsURI);
if (saveType) _nsScope.remove(XSI_NAMESPACE);
} //-- void marshal(DocumentHandler)
/**
* Retrieves the ID for the given Object
*
* @param object the Object to retrieve the ID for
* @return the ID for the given Object
**/
private Object getObjectID(Object object)
throws MarshalException
{
if (object == null) return null;
Object id = null;
XMLClassDescriptor cd = getClassDescriptor(object.getClass());
String err = null;
if (cd != null) {
XMLFieldDescriptor fieldDesc
= (XMLFieldDescriptor) cd.getIdentity();
if (fieldDesc != null) {
FieldHandler fieldHandler = fieldDesc.getHandler();
if (fieldHandler != null) {
try {
id = fieldHandler.getValue(object);
}
catch(IllegalStateException ise) {
err = ise.toString();
}
}//fieldHandler != null
else {
err = "FieldHandler for Identity descriptor is null.";
}
}//fieldDesc != null
else err = "No identity descriptor available";
}//cd!=null
else {
err = "Unable to resolve ClassDescriptor.";
}
if (err != null) {
String errMsg = "Unable to resolve ID for instance of class '";
errMsg += object.getClass().getName();
errMsg += "' due to the following error: ";
throw new MarshalException(errMsg + err);
}
return id;
} //-- getID
/**
* Declares the given namespace, if not already in scope
*
* @param nsPrefix the namespace prefix
* @param nsURI the namespace URI to declare
* @param atts the AttributeListImpl to create the namespace
* declaration
* @return true if the namespace was not in scope and was
* sucessfully declared, other false
**/
private boolean declareNamespace
(String nsPrefix, String nsURI, AttributeListImpl atts)
{
boolean declared = false;
if ( (nsURI != null) && (nsURI.length() != 0)) {
if (!_nsScope.contains(nsURI)) {
String attName = XMLNS;
if (nsPrefix != null) {
int len = nsPrefix.length();
if (len > 0) {
StringBuffer buf = new StringBuffer(6+len);
buf.append(XMLNS);
buf.append(':');
buf.append(nsPrefix);
attName = buf.toString();
}
}
_nsScope.add(nsURI);
atts.addAttribute(attName, CDATA, nsURI);
declared = true;
}
}
return declared;
} //-- declareNamespace
/**
* Sets the flag to turn on and off debugging
* @param debug the flag indicating whether or not debug information
* should be generated
**/
public void setDebug(boolean debug) {
this._debug = debug;
} //-- setDebug
/**
* Sets the PrintWriter used for logging
* @param printWriter the PrintWriter to use for logging
**/
public void setLogWriter(PrintWriter printWriter) {
this._logWriter = printWriter;
} //-- setLogWriter
/**
* Finds and returns an XMLClassDescriptor for the given class. If
* a XMLClassDescriptor could not be found, this method will attempt to
* create one automatically using reflection.
* @param _class the Class to get the XMLClassDescriptor for
* @exception MarshalException when there is a problem
* retrieving or creating the XMLClassDescriptor for the given class
**/
private XMLClassDescriptor getClassDescriptor(Class _class)
throws MarshalException
{
XMLClassDescriptor classDesc = null;
if (!isPrimitive(_class))
classDesc = _cdResolver.resolve(_class);
if (_cdResolver.error()) {
throw new MarshalException(_cdResolver.getErrorMessage());
}
return classDesc;
} //-- getClassDescriptor
/**
* Finds and returns an XMLClassDescriptor for the given class. If
* a XMLClassDescriptor could not be found, this method will attempt to
* create one automatically using reflection.
* @param _class the Class to get the XMLClassDescriptor for
* @exception MarshalException when there is a problem
* retrieving or creating the XMLClassDescriptor for the given class
**/
private XMLClassDescriptor getClassDescriptor
(String className, ClassLoader loader)
throws MarshalException
{
XMLClassDescriptor classDesc = _cdResolver.resolve(className, loader);
if (_cdResolver.error()) {
throw new MarshalException(_cdResolver.getErrorMessage());
}
return classDesc;
} //-- getClassDescriptor
private void processContainerAttributes
(Object target, XMLClassDescriptor classDesc, AttributeListImpl atts)
throws MarshalException
{
XMLFieldDescriptor[] elemDescriptors = classDesc.getElementDescriptors();
for (int i = 0; i < elemDescriptors.length; i++) {
if (elemDescriptors[i] == null) continue;
if (!elemDescriptors[i].isContainer()) continue;
XMLFieldDescriptor elemDescriptor = elemDescriptors[i];
Object containerObject = elemDescriptor.getHandler().getValue(target);
if (containerObject == null) continue;
XMLClassDescriptor containerClassDesc =
(XMLClassDescriptor)elemDescriptor.getClassDescriptor();
if (containerClassDesc == null) {
containerClassDesc = getClassDescriptor(elemDescriptor.getFieldType());
if (containerClassDesc == null) continue;
}
// Look for attributes
XMLFieldDescriptor[] attrDescriptors =
containerClassDesc.getAttributeDescriptors();
for (int idx = 0; idx < attrDescriptors.length; idx++) {
if (attrDescriptors[idx] == null) continue;
String xmlName = attrDescriptors[idx].getXMLName();
Object value = null;
try {
value =
attrDescriptors[idx].getHandler().getValue(containerObject);
}
catch(IllegalStateException ise) {
continue;
}
if (value == null) continue;
atts.addAttribute(xmlName, CDATA, value.toString());
}
// recursively process containers
processContainerAttributes(containerObject, containerClassDesc, atts);
}
} //-- processContainerAttributes
/**
* Resolve a QName value ({URI}value) by declaring a namespace after
* having retrieved the prefix.
*/
private Object resolveQName(Object value, XMLFieldDescriptor fieldDesc,
AttributeListImpl atts) {
if ( (value == null) || !(value instanceof String))
return value;
if (!(fieldDesc instanceof XMLFieldDescriptorImpl))
return value;
String result = (String)value;
if (result.indexOf('}') <= 0) {
String err = "Bad QName value :'"+result+"', it should follow the pattern
'{URI}value'";
throw new IllegalArgumentException(err);
}
int idx = result.indexOf('}');
if (idx <= 0){
String err = "Bad QName value :'"+result+"', it should follow the pattern
'{URI}value'";
throw new IllegalArgumentException(err);
}
String nsURI = result.substring(1, idx);
String prefix = ((XMLFieldDescriptorImpl)fieldDesc).getQNamePrefix();
//no prefix provided, check if one has been previously defined
if (prefix == null)
prefix = _namespaces.getNamespacePrefix(nsURI);
//if still no prefix, use a naming algorithm (ns+counter).
if (prefix == null)
prefix = DEFAULT_PREFIX+(++NAMESPACE_COUNTER);
result = (prefix.length() !=
0)?prefix+":"+result.substring(idx+1):result.substring(idx+1);
declareNamespace(prefix, nsURI, atts);
return result;
}
private void validate(Object object)
throws ValidationException
{
if (_validate) {
//-- we must have a valid element before marshalling
Validator validator = new Validator();
validator.validate(object, _cdResolver);
}
}
} //-- Marshaller