blob: 7c9a226ece1fe04e10d63ba777af4db0baa2529e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.axis2.jibx;
import org.apache.axiom.om.OMDataSource;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axiom.om.util.StAXUtils;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IMarshallable;
import org.jibx.runtime.IMarshaller;
import org.jibx.runtime.IMarshallingContext;
import org.jibx.runtime.IXMLWriter;
import org.jibx.runtime.JiBXException;
import org.jibx.runtime.impl.StAXWriter;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
/** Data source for OM element backed by JiBX data bound object. */
public class JiBXDataSource implements OMDataSource {
/**
* Marshaller index (only needed if object does not have a top-level mapping definition in the
* binding, <code>-1</code> if not used).
*/
private final int marshallerIndex;
/** Element name (only used with {@link #marshallerIndex}). */
private final String elementName;
/** Element namespace URI (only used with {@link #marshallerIndex}). */
private final String elementNamespace;
/** Element namespace prefix (only used with {@link #marshallerIndex}). */
private final String elementNamespacePrefix;
/** Element namespace index (only used with {@link #marshallerIndex}). */
private final int elementNamespaceIndex;
/** Indexes of namespaces to be opened (only used with {@link #marshallerIndex}). */
private final int[] openNamespaceIndexes;
/** Prefixes of namespaces to be opened (only used with {@link #marshallerIndex}). */
private final String[] openNamespacePrefixes;
/** Data object for output. */
private final Object dataObject;
/** Binding factory for creating marshaller. */
private final IBindingFactory bindingFactory;
/**
* Constructor from marshallable object and binding factory.
*
* @param obj
* @param factory
*/
public JiBXDataSource(IMarshallable obj, IBindingFactory factory) {
marshallerIndex = -1;
dataObject = obj;
bindingFactory = factory;
elementName = elementNamespace = elementNamespacePrefix = null;
elementNamespaceIndex = -1;
openNamespaceIndexes = null;
openNamespacePrefixes = null;
}
/**
* Constructor from object with mapping index and binding factory.
*
* @param obj
* @param index
* @param name
* @param uri
* @param prefix
* @param nsindexes
* @param nsprefixes
* @param factory
*/
public JiBXDataSource(Object obj, int index, String name, String uri, String prefix,
int[] nsindexes, String[] nsprefixes, IBindingFactory factory) {
if (index < 0) {
throw new
IllegalArgumentException("index value must be non-negative");
}
marshallerIndex = index;
dataObject = obj;
bindingFactory = factory;
boolean found = false;
String[] nss = factory.getNamespaces();
int nsidx = -1;
for (int i = 0; i < nsindexes.length; i++) {
if (uri.equals(nss[nsindexes[i]])) {
nsidx = nsindexes[i];
prefix = nsprefixes[i];
found = true;
break;
}
}
elementName = name;
elementNamespace = uri;
elementNamespacePrefix = prefix;
if (!found) {
for (int i = 0; i < nss.length; i++) {
if (uri.equals(nss[i])) {
nsidx = i;
break;
}
}
if (nsidx >= 0) {
int[] icpy = new int[nsindexes.length+1];
icpy[0] = nsidx;
System.arraycopy(nsindexes, 0, icpy, 1, nsindexes.length);
nsindexes = icpy;
String[] scpy = new String[nsprefixes.length+1];
scpy[0] = prefix;
System.arraycopy(nsprefixes, 0, scpy, 1, nsprefixes.length);
nsprefixes = scpy;
} else {
throw new IllegalStateException("Namespace not found");
}
}
elementNamespaceIndex = nsidx;
openNamespaceIndexes = nsindexes;
openNamespacePrefixes = nsprefixes;
}
/**
* Internal method to handle the actual marshalling. If the source object is marshallable it's
* it's just marshalled directly, without worrying about redundant namespace declarations and
* such. If it needs to be handled with an abstract mapping, the handling is determined by the
* 'full' flag. When this is <code>true</code> all namespaces are declared directly, while if
* <code>false</code> the namespaces must have previously been declared on some enclosing
* element of the output. This allows the normal case of marshalling within the context of a
* SOAP message to be handled efficiently, while still generating correct XML if the element is
* marshalled directly (as when building the AXIOM representation for use by WS-Security).
*
* @param full
* @param ctx
* @throws JiBXException
*/
private void marshal(boolean full, IMarshallingContext ctx) throws JiBXException {
try {
if (marshallerIndex < 0) {
if (dataObject instanceof IMarshallable) {
((IMarshallable)dataObject).marshal(ctx);
} else {
throw new IllegalStateException("Object of class " + dataObject.getClass().getName() +
" needs a JiBX <mapping> to be marshalled");
}
} else {
IXMLWriter wrtr = ctx.getXmlWriter();
String name = elementName;
int nsidx = 0;
if (full) {
// declare all namespaces on start tag
nsidx = elementNamespaceIndex;
wrtr.startTagNamespaces(nsidx, name, openNamespaceIndexes, openNamespacePrefixes);
} else {
// configure writer with namespace declared in enclosing scope
wrtr.openNamespaces(openNamespaceIndexes, openNamespacePrefixes);
if (!"".equals(elementNamespacePrefix)) {
name = elementNamespacePrefix + ':' + name;
}
wrtr.startTagOpen(0, name);
}
// marshal object representation (may include attributes) into element
IMarshaller mrsh = ctx.getMarshaller(marshallerIndex,
bindingFactory
.getMappedClasses()[marshallerIndex]);
mrsh.marshal(dataObject, ctx);
wrtr.endTag(nsidx, name);
}
ctx.getXmlWriter().flush();
} catch (IOException e) {
throw new JiBXException("Error marshalling XML representation: " + e.getMessage(), e);
}
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMDataSource#serialize(java.io.OutputStream, org.apache.axiom.om.OMOutputFormat)
*/
public void serialize(OutputStream output, OMOutputFormat format) throws XMLStreamException {
try {
// marshal with all namespace declarations, since external state unknown
IMarshallingContext ctx = bindingFactory.createMarshallingContext();
ctx.setOutput(output, format == null ? null : format.getCharSetEncoding());
marshal(true, ctx);
} catch (JiBXException e) {
throw new XMLStreamException("Error in JiBX marshalling: " + e.getMessage(), e);
}
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMDataSource#serialize(java.io.Writer, org.apache.axiom.om.OMOutputFormat)
*/
public void serialize(Writer writer, OMOutputFormat format) throws XMLStreamException {
try {
// marshal with all namespace declarations, since external state unknown
IMarshallingContext ctx = bindingFactory.createMarshallingContext();
ctx.setOutput(writer);
marshal(true, ctx);
} catch (JiBXException e) {
throw new XMLStreamException("Error in JiBX marshalling: " + e.getMessage(), e);
}
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMDataSource#serialize(javax.xml.stream.XMLStreamWriter)
*/
public void serialize(XMLStreamWriter xmlWriter) throws XMLStreamException {
try {
// check if namespaces already declared for abstract mapping
boolean full = true;
String[] nss = bindingFactory.getNamespaces();
if (marshallerIndex >= 0) {
String prefix = xmlWriter.getPrefix(elementNamespace);
if (elementNamespacePrefix.equals(prefix)) {
full = false;
for (int i = 0; i < openNamespaceIndexes.length; i++) {
String uri = nss[i];
prefix = xmlWriter.getPrefix(uri);
if (!openNamespacePrefixes[i].equals(prefix)) {
full = true;
break;
}
}
}
}
// marshal with all namespace declarations, since external state unknown
IXMLWriter writer = new StAXWriter(nss, xmlWriter);
IMarshallingContext ctx = bindingFactory.createMarshallingContext();
ctx.setXmlWriter(writer);
marshal(full, ctx);
} catch (JiBXException e) {
throw new XMLStreamException("Error in JiBX marshalling: " + e.getMessage(), e);
}
}
/* (non-Javadoc)
* @see org.apache.axiom.om.OMDataSource#getReader()
*/
public XMLStreamReader getReader() throws XMLStreamException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
serialize(bos, null);
return StAXUtils.createXMLStreamReader(new ByteArrayInputStream(bos.toByteArray()));
}
}