/**
* 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 info@exolab.org.
*
* 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-2004 (C) Intalio, Inc. All Rights Reserved.
*
* $Id$
*/
package org.exolab.castor.xml;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamReader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.castor.mapping.BindingType;
import org.castor.mapping.MappingUnmarshaller;
import org.castor.xml.BackwardCompatibilityContext;
import org.castor.xml.InternalContext;
import org.castor.xml.UnmarshalListenerAdapter;
import org.castor.xml.XMLProperties;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.mapping.MappingLoader;
import org.exolab.castor.types.AnyNode;
import org.exolab.castor.util.ObjectFactory;
import org.exolab.castor.xml.location.FileLocation;
import org.exolab.castor.xml.util.AnyNode2SAX2;
import org.exolab.castor.xml.util.DOMEventProducer;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.Parser;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/**
* An unmarshaller to allowing unmarshalling of XML documents to
* Java Objects. The Class must specify
* the proper access methods (setters/getters) in order for instances
* of the Class to be properly unmarshalled.
*
* @author <a href="mailto:kvisco-at-intalio.com">Keith Visco</a>
* @version $Revision$ $Date: 2006-02-23 14:16:51 -0700 (Thu, 23 Feb 2006) $
*/
public class Unmarshaller {
/**
* Logger from commons-logging.
*/
private static final Log LOG = LogFactory.getLog(Unmarshaller.class);
//----------------------------/
//- Private Member Variables -/
//----------------------------/
/**
* The Class that this Unmarshaller was created with.
*/
private Class _class = null;
/**
* A boolean indicating whether or not collections (including
* arrays) should be cleared upon initial use by Castor.
* False by default for backward compatibility.
*/
private boolean _clearCollections = false;
/**
* A user specified IDResolver for resolving IDREFs.
*/
private IDResolver _idResolver = null;
/**
* A boolean that specifies whether or not
* non-matched attributes should be ignored upon
* unmarshalling.
*/
private boolean _ignoreExtraAtts = true;
/**
* A boolean that specifies whether or not
* non-matched elements should be ignored upon
* unmarshalling.
*/
private boolean _ignoreExtraElements = false;
/**
* The instance of _class to Unmarshal into (optional)
*/
private Object _instanceObj = null;
/**
* The EntityResolver used for resolving entities
*/
EntityResolver entityResolver = null;
/**
* The class loader to use
*/
private ClassLoader _loader = null;
/**
* A boolean to indicate that objects should
* be re-used where appropriate
*/
private boolean _reuseObjects = false;
/**
* The unmarshaller listener that listens to unmarshalling event
*/
private org.castor.xml.UnmarshalListener _unmarshalListener = null;
/**
* The flag indicating whether or not to validate during
* unmarshalling
*/
private boolean _validate = false;
/**
* A flag indicating the unmarshaller should preserve
* "ignorable" whitespace. The XML instance can
* control it's own behavior using the xml:space
* attribute. This sets the "default" behavior
* when xml:space="default".
*/
private boolean _wsPreserve = false;
/**
* A list of namespace To Package Mappings
*/
private HashMap _namespaceToPackage = null;
/**
* An optional factory for unmarshalling objects
*/
private ObjectFactory _objectFactory;
/**
* The Castor XML context to use at unmarshalling.
*/
private InternalContext _internalContext;
//----------------/
//- Constructors -/
//----------------/
/**
* An empty default constructor which:
* - sets the internal context to the backward compatibility context
* - all other flags to defaults
* Internally the Unmarshaller(Class) constructor is called.
*/
public Unmarshaller() {
this((Class) null);
}
/**
* A constructor which sets the root class.
*
* Internally calls constructor Unmarshaller(InternalContext, Class) with
* an instance of BackwardCompatibilityContext as context.
*
* @param clazz root class for unmarshalling
*/
public Unmarshaller(final Class clazz) {
this(new BackwardCompatibilityContext(), clazz);
}
/**
* Creates a new basic Unmarshaller.
*
* When using this constructor it will most likely be
* necessary to use a mapping file or ClassDescriptorResolver
* So that the Unmarshaller can find the classes during the
* unmarshalling process.
*
* @param internalContext the {@link InternalContext} to use
*/
public Unmarshaller(final InternalContext internalContext) {
this(internalContext, (Class) null, (ClassLoader) null);
}
/**
* Creates a new Unmarshaller with the given Class.
*
* @param internalContext the {@link InternalContext} to use
* @param c the Class to create the Unmarshaller for, this
* may be null, if the Unmarshaller#setMapping is called
* to load a mapping for the root element of xml document.
*/
public Unmarshaller(final InternalContext internalContext, final Class c) {
this(internalContext, c, null);
} //-- Unmarshaller(Class)
/**
* Creates a new {@link Unmarshaller} with the given Class.
*
* @param internalContext the {@link InternalContext} to be used, for config, and such...
* @param c the {@link Class} to create the {@link Unmarshaller} for, this
* may be null, if the Unmarshaller#setMapping is called
* to load a mapping for the root element of xml document.
* @param loader The {@link ClassLoader} to use.
*/
public Unmarshaller(
final InternalContext internalContext,
final Class c, final ClassLoader loader) {
super();
if (internalContext == null) {
String message = "InternalContext must not be null";
LOG.warn(message);
throw new IllegalArgumentException(message);
}
setInternalContext(internalContext);
setClass(c);
_loader = loader;
if ((loader == null) && (c != null)) {
_loader = c.getClassLoader();
}
_internalContext.setClassLoader(_loader);
}
/**
* Creates a new Unmarshaller with the given Mapping.
* An instance of BackwardsCompatibilityContext is used as InternalContext.
*
* @param mapping The Mapping to use.
* @throws MappingException in case that Unmarshaller fails to be instantiated
*/
public Unmarshaller(final Mapping mapping) throws MappingException {
this(new BackwardCompatibilityContext(), mapping);
}
/**
* Creates a new Unmarshaller with the given Mapping.
*
* @param internalContext the internal context to use
* @param mapping The Mapping to use.
* @throws MappingException in case that Unmarshaller fails to be instantiated
*/
public Unmarshaller(final InternalContext internalContext, final Mapping mapping)
throws MappingException {
this(internalContext, null, null);
if (mapping != null) {
setMapping(mapping);
this._loader = mapping.getClassLoader();
}
}
/**
* Creates a new Unmarshaller with the given Object.
*
* @param root the instance to unmarshal into. This
* may be null, if the Unmarshaller#setMapping is called
* to load a mapping for the root element of xml document.
*/
public Unmarshaller(final Object root) {
this(new BackwardCompatibilityContext(), root);
}
/**
* Creates a new Unmarshaller with the given Object.
*
* @param internalContext the internal context to use
* @param root the instance to unmarshal into. This
* may be null, if the Unmarshaller#setMapping is called
* to load a mapping for the root element of xml document.
*/
public Unmarshaller(final InternalContext internalContext, final Object root) {
this(internalContext, null, null);
if (root != null) {
final Class clazz = root.getClass();
setClass(clazz);
_loader = clazz.getClassLoader();
}
_instanceObj = root;
}
/**
* Adds a mapping from the given namespace URI to the given
* package name.
*
* @param nsURI the namespace URI to map from
* @param packageName the package name to map to
*/
public void addNamespaceToPackageMapping(final String nsURI, final String packageName) {
if (_namespaceToPackage == null) {
_namespaceToPackage = new HashMap();
}
String iNsUri = (nsURI == null) ? "" : nsURI;
String iPackageName = (packageName == null) ? "" : packageName;
_namespaceToPackage.put(iNsUri, iPackageName);
} //-- addNamespaceToPackageMapping
/**
* Creates and initalizes an UnmarshalHandler
* @return the new UnmarshalHandler
**/
public UnmarshalHandler createHandler() {
UnmarshalHandler handler = new UnmarshalHandler(_internalContext, _class);
handler.setClearCollections(_clearCollections);
handler.setReuseObjects(_reuseObjects);
handler.setValidation(_validate);
handler.setIgnoreExtraAttributes(_ignoreExtraAtts);
handler.setIgnoreExtraElements(_ignoreExtraElements);
handler.setInternalContext(_internalContext);
handler.setWhitespacePreserve(_wsPreserve);
// If the object factory has been set, set it on the handler
if (this._objectFactory != null) {
handler.setObjectFactory(this._objectFactory);
}
//-- copy namespaceToPackageMappings
if (_namespaceToPackage != null) {
Iterator keys = _namespaceToPackage.keySet().iterator();
while (keys.hasNext()) {
String nsURI = (String)keys.next();
String pkgName = (String) _namespaceToPackage.get(nsURI);
handler.addNamespaceToPackageMapping(nsURI, pkgName);
}
}
if (_instanceObj != null) {
handler.setRootObject(_instanceObj);
}
if (_idResolver != null)
handler.setIDResolver(_idResolver);
if (_loader != null)
handler.setClassLoader(_loader);
if (_unmarshalListener != null)
handler.setUnmarshalListener(_unmarshalListener);
return handler;
} //-- createHandler
/**
* Indicates whether or not validation should be performed during umarshalling.
* @return True if validation is performed during umarshalling.
*/
public boolean isValidating() {
return _validate;
}
/**
* Sets the 'expected' {@link Class} instance on the Unmarshaller.
*
* @param clazz the Class to create the Unmarshaller for, this
* may be null, if the Unmarshaller#setMapping is called
* to load a mapping for the root element of xml document.
*/
public void setClass(Class clazz) {
_class = clazz;
} //-- setClass(Class)
/**
* Sets the 'expected' {@link Object} instance on the Unmarshaller, into
* which will be unmarshalled.
*
* @param root the instance to unmarshal into. This
* may be null, if the Unmarshaller#setMapping is called
* to load a mapping for the root element of xml document.
*/
public void setObject(Object root) {
_instanceObj = root;
} //-- setObject(Object)
/**
* Sets the ClassLoader to use when loading new classes.
* <br />
* <b>Note:</b>This ClassLoader is used for classes
* loaded by the unmarshaller only. If a Mapping has
* been set, the Mapping has it's own ClassLoader and
* may also need to be set propertly.
* <br />
*
* @param loader the ClassLoader to use
**/
public void setClassLoader(ClassLoader loader) {
this._loader = loader;
} //-- setClassLoader
/**
* Sets whether or not to clear collections (including arrays)
* upon first use to remove default values. By default, and
* for backward compatibility with previous versions of Castor
* this value is false, indicating that collections are not
* cleared before initial use by Castor.
*
* @param clear the boolean value that when true indicates
* collections should be cleared upon first use.
*/
public void setClearCollections(boolean clear) {
_clearCollections = clear;
} //-- setClearCollections
/**
* Custom debugging is replaced with commons-logging
* @deprecated
**/
public void setDebug(boolean debug) {
// no-op
} //-- setDebug
/**
* Sets the EntityResolver to use when resolving system and
* public ids with respect to entites and Document Type.
* @param entityResolver the EntityResolver to use when
* resolving System and Public ids.
**/
public void setEntityResolver(EntityResolver entityResolver) {
this.entityResolver = entityResolver;
} //-- entityResolver
/**
* Sets the IDResolver to use when resolving IDREFs for
* which no associated element may exist in XML document.
*
* @param idResolver the IDResolver to use when resolving
* IDREFs for which no associated element may exist in the
* XML document.
**/
public void setIDResolver(IDResolver idResolver) {
_idResolver = idResolver;
} //-- idResolver
/**
* Sets whether or not attributes that do not match
* a specific field should simply be ignored or
* reported as an error. By default, extra attributes
* are ignored.
*
* @param ignoreExtraAtts a boolean that when true will
* allow non-matched attributes to simply be ignored.
*/
public void setIgnoreExtraAttributes(boolean ignoreExtraAtts) {
_ignoreExtraAtts = ignoreExtraAtts;
} //-- setIgnoreExtraAttributes
/**
* Sets whether or not elements that do not match
* a specific field should simply be ignored or
* reported as an error. By default, extra elements
* are flagged as an error.
*
* @param ignoreExtraElements a boolean that when true will
* allow non-matched elements to simply be ignored.
*/
public void setIgnoreExtraElements(boolean ignoreExtraElements) {
_ignoreExtraElements = ignoreExtraElements;
} //-- setIgnoreExtraElements
/**
* Logging is replaced with commons-logging.
* @param printWriter the PrintWriter to use for logging
* @deprecated
**/
public void setLogWriter(PrintWriter printWriter) {
// no-op
} //-- setLogWriter
/**
* Sets the Mapping to use during unmarshalling. If the Mapping has a ClassLoader it
* will be used during unmarshalling.
*
* @param mapping Mapping to use during unmarshalling.
* @see #setResolver
*/
public void setMapping(final Mapping mapping) throws MappingException {
if (_loader == null) {
_loader = mapping.getClassLoader();
}
MappingUnmarshaller mum = new MappingUnmarshaller();
MappingLoader resolver = mum.getMappingLoader(mapping, BindingType.XML);
_internalContext.getXMLClassDescriptorResolver().setMappingLoader(resolver);
}
/**
* Sets a boolean that when true indicates that objects
* contained within the object model should be re-used
* where appropriate. This is only valid when unmarshalling
* to an existing object.
*
* @param reuse the boolean indicating whether or not
* to re-use existing objects in the object model.
**/
public void setReuseObjects(boolean reuse) {
_reuseObjects = reuse;
} //-- setReuseObjects
/**
* Sets an optional {@link org.exolab.castor.xml.UnmarshalListener} to receive pre and
* post unmarshal notification for each Object in the tree.
* An UnmarshalListener is often used to allow objects to
* appropriately initialize themselves by taking application
* specific behavior as they are unloaded.
* Current only one (1) listener is allowed. If you need
* register multiple listeners, you will have to create
* your own master listener that will forward the
* event notifications and manage the multiple
* listeners.<br/>
* The deprecated listener set with this method will be wrapped by an
* adapter.
*
* @param listener the {@link org.exolab.castor.xml.UnmarshalListener} to set.
* @deprecated replaced by {@link org.castor.xml.UnmarshalListener}
*/
public void setUnmarshalListener(org.exolab.castor.xml.UnmarshalListener listener) {
if (listener == null) {
_unmarshalListener = null;
} else {
UnmarshalListenerAdapter adapter = new UnmarshalListenerAdapter();
adapter.setOldListener(listener);
_unmarshalListener = adapter;
}
}
/**
* Sets an optional {@link org.castor.xml.UnmarshalListener} to receive pre and
* post unmarshal notification for each Object in the tree.
* An UnmarshalListener is often used to allow objects to
* appropriately initialize themselves by taking application
* specific behavior as they are unloaded.
* Current only one (1) listener is allowed. If you need
* register multiple listeners, you will have to create
* your own master listener that will forward the
* event notifications and manage the multiple
* listeners.
*
* @param listener the {@link org.castor.xml.UnmarshalListener} to set.
*/
public void setUnmarshalListener(org.castor.xml.UnmarshalListener listener) {
_unmarshalListener = listener;
}
/**
* Sets the flag for validation.
*
* @param validate A boolean to indicate whether or not validation should be done
* during umarshalling.
* <br/>
* By default validation will be performed.
*/
public void setValidation(boolean validate) {
_validate = validate;
} //-- setValidation
/**
* Sets the top-level whitespace (xml:space) to either
* preserving or non preserving. The XML document
* can override this value using xml:space on specific
* elements.This sets the "default" behavior
* when xml:space="default".
*
* @param preserve a boolean that when true enables
* whitespace preserving by default.
*/
public void setWhitespacePreserve(boolean preserve) {
_wsPreserve = preserve;
} //-- setWhitespacePreserve
/**
* Unmarshals Objects of this Unmarshaller's Class type.
* The Class must specify the proper access methods
* (setters/getters) in order for instances of the Class
* to be properly unmarshalled.
* @param reader the Reader to read the XML from
* @exception MarshalException when there is an error during
* the unmarshalling process
* @exception ValidationException when there is a validation error
**/
public Object unmarshal(Reader reader)
throws MarshalException, ValidationException
{
return unmarshal(new InputSource(reader));
} //-- unmarshal(Reader reader)
/**
* Unmarshals Objects of this Unmarshaller's Class type.
* The Class must specify the proper access methods
* (setters/getters) in order for instances of the Class
* to be properly unmarshalled.
* @param eventProducer the EventProducer which produces
* the SAX events
* @exception MarshalException when there is an error during
* the unmarshalling process
* @exception ValidationException when there is a validation error
* @deprecated please use @see #unmarshal(SAX2EventProducer) instead.
**/
public Object unmarshal(EventProducer eventProducer)
throws MarshalException, ValidationException
{
UnmarshalHandler handler = createHandler();
eventProducer.setDocumentHandler(handler);
try {
eventProducer.start();
}
catch(org.xml.sax.SAXException sx) {
convertSAXExceptionToMarshalException(handler, sx);
}
return handler.getObject();
} //-- unmarshal(EventProducer)
/**
* Unmarshals Objects of this Unmarshaller's Class type.
* The Class must specify the proper access methods
* (setters/getters) in order for instances of the Class
* to be properly unmarshalled.
* @param eventProducer the SAX2EventProducer instance which produces
* the SAX 2 events
* @exception MarshalException when there is an error during
* the unmarshalling process
* @exception ValidationException when there is a validation error
* @since 1.0M3
**/
public Object unmarshal(SAX2EventProducer eventProducer)
throws MarshalException, ValidationException
{
UnmarshalHandler handler = createHandler();
eventProducer.setContentHandler(handler);
try {
eventProducer.start();
}
catch(org.xml.sax.SAXException sx) {
convertSAXExceptionToMarshalException(handler, sx);
}
return handler.getObject();
} //-- unmarshal(SAX2EventProducer)
/**
* Unmarshals objects of this {@link Unmarshaller}'s Class type
* from an {@link AnyNode} instance.
*
* The Class must specify the proper access methods
* (setters/getters) in order for instances of the Class
* to be properly unmarshalled.
*
* @param anyNode {@link AnyNode} instance to be unmarshalled from
* @exception MarshalException when there is an error during
* the unmarshalling process
* @return The {@link Object} instance that is a result of unmarshalling.
**/
public Object unmarshal(final AnyNode anyNode)
throws MarshalException {
UnmarshalHandler handler = createHandler();
try {
AnyNode2SAX2.fireEvents(anyNode, handler);
} catch (SAXException sex) {
convertSAXExceptionToMarshalException(handler, sex);
}
return handler.getObject();
}
/**
* Unmarshals Objects of this Unmarshaller's Class type.
* The Class must specify the proper access methods
* (setters/getters) in order for instances of the Class
* to be properly unmarshalled.
* @param source the InputSource to read the XML from
* @exception MarshalException when there is an error during
* the unmarshalling process
* @exception ValidationException when there is a validation error
**/
public Object unmarshal(InputSource source)
throws MarshalException, ValidationException
{
XMLReader reader = null;
Parser parser = null;
//-- First try XMLReader
try {
reader = _internalContext.getXMLReader();
if (entityResolver != null)
reader.setEntityResolver(entityResolver);
} catch (RuntimeException rx) {
LOG.debug("Unable to create SAX XMLReader, attempting SAX Parser.");
}
if (reader == null) {
parser = _internalContext.getParser();
if (parser == null)
throw new MarshalException("Unable to create SAX Parser.");
if (entityResolver != null)
parser.setEntityResolver(entityResolver);
}
UnmarshalHandler handler = createHandler();
try {
if (reader != null) {
reader.setContentHandler(handler);
reader.setErrorHandler(handler);
reader.parse(source);
}
else {
parser.setDocumentHandler(handler);
parser.setErrorHandler(handler);
parser.parse(source);
}
}
catch (java.io.IOException ioe) {
throw new MarshalException(ioe);
}
catch (org.xml.sax.SAXException sx) {
convertSAXExceptionToMarshalException(handler, sx);
}
return handler.getObject();
} //-- unmarshal(InputSource)
/**
* Unmarshals Objects of this Unmarshaller's Class type.
* The Class must specify the proper access methods
* (setters/getters) in order for instances of the Class
* to be properly unmarshalled.
* @param node the DOM node to read the XML from
* @exception MarshalException when there is an error during
* the unmarshalling process
* @exception ValidationException when there is a validation error
**/
public Object unmarshal(Node node)
throws MarshalException, ValidationException
{
return unmarshal(new DOMEventProducer(node));
} //-- unmarshal(EventProducer)
/**
* Unmarshals objects of this {@link Unmarshaller}'s class type. The class must
* specify the proper access methods (setters/getters) in order for
* instances of the class to be properly unmarshalled.
*
* @param eventReader
* the StaX {@link XMLEventReader} to read XML from.
* @exception MarshalException
* indicates a general problem during the unmarshalling process.
* @throws ValidationException
* indicates a problem related to validation.
*
* @since 1.3.2
**/
public Object unmarshal(XMLEventReader eventReader)
throws MarshalException, ValidationException {
return unmarshal(BaseSax2EventFromStaxProducer.createSax2EventFromStax(eventReader));
}
/**
* Unmarshals objects of this {@link Unmarshaller}'s class type. The class must
* specify the proper access methods (setters/getters) in order for
* instances of the class to be properly unmarshalled.
*
* @param streamReader
* the STaX {@link XMLStreamReader} to read XML from.
* @exception MarshalException
* indicates a general problem during the unmarshalling process.
* @throws ValidationException
* indicates a problem related to validation.
*
* @since 1.3.2
**/
public Object unmarshal(XMLStreamReader streamReader)
throws MarshalException, ValidationException {
return unmarshal(BaseSax2EventFromStaxProducer.createSax2EventFromStax(streamReader));
}
/**
* Unmarshals objects of this {@link Unmarshaller}'s class type. <br/>
* The class must specify the proper access methods (setters/getters) in
* order for instances of the class to be properly unmarshalled. </br/>
*
* @param eventProducer
* the {@link SAX2EventAndErrorProducer} instance which produces
* the SAX 2 events and handles SAX 2 errors.
* @exception MarshalException
* indiactes a general error during the unmarshalling
* process.
* @exception ValidationException
* indicates a validation error.
* @since 1.3.2
**/
public Object unmarshal(SAX2EventAndErrorProducer eventProducer)
throws MarshalException, ValidationException
{
UnmarshalHandler handler = createHandler();
eventProducer.setContentHandler(handler);
eventProducer.setErrorHandler(handler);
try {
eventProducer.start();
}
catch(org.xml.sax.SAXException sx) {
convertSAXExceptionToMarshalException(handler, sx);
}
return handler.getObject();
}
/**
* Converts a SAXException to a (localised) MarshalException.
* @param handler The {@link UnmarshalHandler} required to obtain DocumentLocator instance.
* @param sex The {@link SAXException} instance
* @throws MarshalException The {@link MarshalException} instance derived from the SAX exception.
*/
private void convertSAXExceptionToMarshalException(UnmarshalHandler handler, SAXException sex) throws MarshalException {
Exception except = sex.getException();
if (except == null) {
except = sex;
}
MarshalException marshalEx = new MarshalException(except);
if (handler.getDocumentLocator() != null) {
FileLocation location = new FileLocation();
location.setFilename(handler.getDocumentLocator().getSystemId());
location.setLineNumber(handler.getDocumentLocator().getLineNumber());
location.setColumnNumber(handler.getDocumentLocator().getColumnNumber());
marshalEx.setLocation(location);
}
throw marshalEx;
}
//-------------------------/
//- Public Static Methods -/
//-------------------------/
/**
* Returns a ContentHandler for the given UnmarshalHandler
*
* @return the ContentHandler
*/
public static ContentHandler getContentHandler(UnmarshalHandler handler)
throws SAXException
{
return handler;
} //-- getContentHandler
/**
* Unmarshals Objects of the given Class type. The Class must specify
* the proper access methods (setters/getters) in order for instances
* of the Class to be properly unmarshalled.
*
* <p><b>Note:</b>This is a *static* method, any mapping files set
* on a particular Unmarshaller instance, and any changes made
* via setters will be unavailable to this method.</p>
*
* @param c the Class to create a new instance of
* @param reader the Reader to read the XML from
* @exception MarshalException when there is an error during
* the unmarshalling process
* @exception ValidationException when there is a validation error
**/
public static Object unmarshal(Class c, Reader reader)
throws MarshalException, ValidationException
{
Unmarshaller unmarshaller = createUnmarshaller(c);
return unmarshaller.unmarshal(reader);
} //-- void unmarshal(Writer)
/**
* Helper method for static #unmarshal methods to create
* an {@link Unmarshaller} instance.
*
* @param clazz The root class to be used during unmarshalling.
* @return An {@link Unmarshaller} instance.
*/
private static Unmarshaller createUnmarshaller(final Class clazz) {
XMLContext xmlContext = new XMLContext();
Unmarshaller unmarshaller = xmlContext.createUnmarshaller();
unmarshaller.setClass(clazz);
// TODO: Should this be at level INFO?
if (LOG.isDebugEnabled()) {
LOG.debug("*static* unmarshal method called, this will ignore any "
+ "mapping files or changes made to an Unmarshaller instance.");
}
//-- for backward compatibility with Castor versions
//-- prior to version 0.9.5.3
unmarshaller.setWhitespacePreserve(true);
return unmarshaller;
}
/**
* Unmarshals Objects of the given Class type. The Class must specify
* the proper access methods (setters/getters) in order for instances
* of the Class to be properly unmarshalled.
*
* <p><b>Note:</b>This is a *static* method, any mapping files set
* on a particular Unmarshaller instance, and any changes made
* via setters will be unavailable to this method.</p>
*
* @param c the Class to create a new instance of
* @param source the InputSource to read the XML from
* @exception MarshalException when there is an error during
* the unmarshalling process
* @exception ValidationException when there is a validation error
*/
public static Object unmarshal(Class c, InputSource source)
throws MarshalException, ValidationException
{
Unmarshaller unmarshaller = createUnmarshaller(c);
return unmarshaller.unmarshal(source);
} //-- void unmarshal(Writer)
/**
* Unmarshals Objects of the given Class type. The Class must specify
* the proper access methods (setters/getters) in order for instances
* of the Class to be properly unmarshalled.
*
* <p><b>Note:</b>This is a *static* method, any mapping files set
* on a particular Unmarshaller instance, and any changes made
* via setters will be unavailable to this method.</p>
*
* @param c The Class to create a new instance of.
* @param node The DOM Node to read the XML from.
* @exception MarshalException When there is an error during the unmarshalling
* process.
* @exception ValidationException When there is a validation error.
*/
public static Object unmarshal(Class c, Node node)
throws MarshalException, ValidationException {
Unmarshaller unmarshaller = createUnmarshaller(c);
return unmarshaller.unmarshal(node);
} //-- void unmarshal(Writer)
/**
* Set an object factory for the unmarshaller. This factory will be used to
* construct the objects being unmarshalled.
*
* @param objectFactory
* Factory used for constructing objects during unmarshalling.
*/
public void setObjectFactory(final ObjectFactory objectFactory) {
this._objectFactory = objectFactory;
} // -- setObjectFactory
/**
* Returns the value of the given Castor XML-specific property.
*
* @param name
* Qualified name of the CASTOR XML-specific property.
* @return The current value of the given property.
* @since 1.1.2
*/
public String getProperty(final String name) {
Object propertyValue = _internalContext.getProperty(name);
if ((propertyValue != null) && !(propertyValue instanceof String)) {
String message =
"Requested property: " + name
+ " is not of type String, but: " + propertyValue.getClass()
+ " throwing IllegalStateException.";
LOG.warn(message);
throw new IllegalStateException(message);
}
return (String) propertyValue;
}
/**
* Sets a custom value of a given Castor XML-specific property.
*
* @param name
* Name of the Castor XML property
* @param value
* Custom value to set.
* @since 1.1.2
*/
public void setProperty(final String name, final String value) {
_internalContext.setProperty(name, value);
}
/**
* To set the internal XML Context to be used.
* @param internalContext the context to be used
*/
public void setInternalContext(final InternalContext internalContext) {
_internalContext = internalContext;
deriveProperties();
}
/**
* Derive class-level properties from {@link XMLProperties} as defined
* {@link InternalContext}. This method will be called after a new {@link InternalContext}
* has been set.
* @link #setInternalContext(InternalContext)
*/
private void deriveProperties() {
_validate = _internalContext.marshallingValidation();
_ignoreExtraElements = (!_internalContext.strictElements());
//-- process namespace to package mappings
String mappings =
_internalContext.getStringProperty(XMLProperties.NAMESPACE_PACKAGE_MAPPINGS);
if (mappings != null && mappings.length() > 0) {
StringTokenizer tokens = new StringTokenizer(mappings, ",");
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
int sepIdx = token.indexOf('=');
if (sepIdx < 0) {
continue;
}
String ns = token.substring(0, sepIdx).trim();
String javaPackage = token.substring(sepIdx + 1).trim();
addNamespaceToPackageMapping(ns, javaPackage);
}
}
}
/**
* To get the internal XML Context that is in use.
* @return the {@link InternalContext} in use
*/
public InternalContext getInternalContext() {
return _internalContext;
}
/**
* Sets the XMLClassDescriptorResolver to use during unmarshalling
* @param xmlClassDescriptorResolver the XMLClassDescriptorResolver to use
* @see #setMapping
* <BR />
* <B>Note:</B> This method will nullify any Mapping
* currently being used by this Unmarshaller
*/
public void setResolver(XMLClassDescriptorResolver xmlClassDescriptorResolver) {
_internalContext.setResolver(xmlClassDescriptorResolver);
}
} //-- Unmarshaller