blob: 1f9bd20980f5cb11e08cb298c49f3d8fc4a20e71 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 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 end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache
* XMLBeans", nor may "Apache" appear in their name, without prior
* written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``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 THE APACHE SOFTWARE FOUNDATION 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000-2003 BEA Systems
* Inc., <http://www.bea.com/>. For more information on the Apache Software
* Foundation, please see <http://www.apache.org/>.
*/
package org.apache.xmlbeans.impl.marshal;
import org.apache.xmlbeans.Marshaller;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlRuntimeException;
import org.apache.xmlbeans.impl.binding.bts.BindingLoader;
import org.apache.xmlbeans.impl.binding.bts.BindingType;
import org.apache.xmlbeans.impl.binding.bts.BindingTypeName;
import org.apache.xmlbeans.impl.binding.bts.JavaTypeName;
import org.apache.xmlbeans.impl.binding.bts.XmlTypeName;
import org.apache.xmlbeans.impl.common.XmlReaderToWriter;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import java.io.OutputStream;
import java.util.Collection;
final class MarshallerImpl
implements Marshaller
{
private final BindingLoader loader;
private final RuntimeBindingTypeTable typeTable;
private final ScopedNamespaceContext namespaceContext;
private final RuntimeTypeFactory runtimeTypeFactory =
new RuntimeTypeFactory();
private final Collection errors;
private final XmlOptions options;
private int prefixCnt = 0;
private static final String NSPREFIX = "n";
private static final String XML_VERSION = "1.0";
MarshallerImpl(NamespaceContext root_nsctx,
BindingLoader loader,
RuntimeBindingTypeTable typeTable,
XmlOptions options)
{
this.namespaceContext = new ScopedNamespaceContext(root_nsctx);
this.loader = loader;
this.typeTable = typeTable;
this.errors = BindingContextImpl.extractErrorHandler(options);
this.options = options;
namespaceContext.openScope();
}
public XMLStreamReader marshal(Object obj,
NamespaceContext nscontext)
throws XmlException
{
JavaTypeName jname = determineJavaType(obj);
BindingTypeName root_elem_btype = loader.lookupElementFor(jname);
if (root_elem_btype == null) {
final String msg = "failed to find root " +
"element corresponding to " + jname;
throw new XmlRuntimeException(msg);
}
final XmlTypeName elem = root_elem_btype.getXmlName();
assert elem.getComponentType() == XmlTypeName.ELEMENT;
final QName elem_qn = elem.getQName();
//get type for this element/object pair
final BindingTypeName type_name = loader.lookupTypeFor(jname);
if (type_name == null) {
String msg = "failed to lookup type for " + jname;
throw new XmlException(msg);
}
final BindingType btype = loader.getBindingType(type_name);
if (btype == null) {
String msg = "failed to load type " + type_name;
throw new XmlException(msg);
}
RuntimeGlobalProperty prop = new RuntimeGlobalProperty(btype, elem_qn);
return new MarshalResult(prop, obj, this);
}
private static JavaTypeName determineJavaType(Object obj)
{
return JavaTypeName.forString(obj.getClass().getName());
}
public void marshal(XMLStreamWriter writer, Object obj)
throws XmlException
{
XMLStreamReader rdr = marshal(obj, writer.getNamespaceContext());
try {
XmlReaderToWriter.writeAll(rdr, writer);
}
catch (XMLStreamException e) {
throw new XmlException(e);
}
}
public void marshal(OutputStream out, Object obj)
throws XmlException
{
String encoding = (String)options.get(XmlOptions.CHARACTER_ENCODING);
if (encoding != null) {
marshal(out, obj, encoding);
} else {
try {
final XMLOutputFactory xof = XMLOutputFactory.newInstance();
final XMLStreamWriter writer = xof.createXMLStreamWriter(out);
marshal(writer, obj);
}
catch (XMLStreamException e) {
throw new XmlException(e);
}
}
}
public void marshal(OutputStream out, Object obj, String encoding)
throws XmlException
{
if (encoding == null) {
throw new IllegalArgumentException("null encoding");
}
try {
XMLOutputFactory output_factory = XMLOutputFactory.newInstance();
XMLStreamWriter writer =
output_factory.createXMLStreamWriter(out, encoding);
writer.writeStartDocument(encoding, XML_VERSION);
marshal(writer, obj);
writer.writeEndDocument();
writer.close();
}
catch (XMLStreamException e) {
throw new XmlException(e);
}
}
public XMLStreamReader marshalType(Object obj,
QName elementName,
QName schemaType,
String javaType,
NamespaceContext namespaceContext)
throws XmlException
{
BindingType type = lookupBindingType(obj, schemaType, loader);
RuntimeGlobalProperty prop = new RuntimeGlobalProperty(type, elementName);
return new MarshalResult(prop, obj, this);
}
public void marshalType(XMLStreamWriter writer,
Object obj,
QName elementName,
QName schemaType,
String javaType)
throws XmlException
{
XMLStreamReader rdr = marshalType(obj, elementName, schemaType,
javaType,
writer.getNamespaceContext());
try {
XmlReaderToWriter.writeAll(rdr, writer);
}
catch (XMLStreamException e) {
throw new XmlException(e);
}
}
private static BindingType lookupBindingType(Object obj,
QName schemaType,
BindingLoader loader)
{
JavaTypeName jname = determineJavaType(obj);
XmlTypeName xname = XmlTypeName.forTypeNamed(schemaType);
BindingTypeName btname = BindingTypeName.forPair(jname, xname);
if (btname == null) {
final String msg = "failed to find type corresponding to " + btname;
throw new XmlRuntimeException(msg);
}
final BindingType binding_type = loader.getBindingType(btname);
if (binding_type == null) {
final String msg = "failed to load type " + btname;
throw new XmlRuntimeException(msg);
}
return binding_type;
}
Collection getErrorCollection()
{
return errors;
}
BindingLoader getLoader()
{
return loader;
}
RuntimeBindingTypeTable getTypeTable()
{
return typeTable;
}
ScopedNamespaceContext getNamespaceContext()
{
return namespaceContext;
}
RuntimeBindingType createRuntimeBindingType(BindingType type, Object instance)
{
String expectedJavaClass = type.getName().getJavaName().toString();
String actualJavaClass = instance.getClass().getName();
if (!actualJavaClass.equals(expectedJavaClass)) {
//redefine type
JavaTypeName tn = JavaTypeName.forString(actualJavaClass);
BindingTypeName bindingTypeName = loader.lookupTypeFor(tn);
if (bindingTypeName != null) {
final BindingType bindingType =
loader.getBindingType(bindingTypeName);
if (bindingType != null) {
type = bindingType;
} else {
//fallthrough to use the expected type...
//TODO: consider adding a warning to the errors?
}
} else {
//fallthrough to use the expected type...
//TODO: consider adding a warning to the errors?
}
}
return runtimeTypeFactory.createRuntimeType(type, typeTable, loader);
}
String ensurePrefix(String uri)
{
String prefix = namespaceContext.getPrefix(uri);
if (prefix == null) {
prefix = bindNextPrefix(uri);
}
assert prefix != null;
return prefix;
}
private String bindNextPrefix(final String uri)
{
assert uri != null;
String testuri;
String prefix;
do {
prefix = NSPREFIX + (++prefixCnt);
testuri = namespaceContext.getNamespaceURI(prefix);
}
while (testuri != null);
assert prefix != null;
namespaceContext.bindNamespace(prefix, uri);
return prefix;
}
}