001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.jibx;
020:
021: import org.apache.axiom.om.OMDataSource;
022: import org.apache.axiom.om.OMOutputFormat;
023: import org.apache.axiom.om.util.StAXUtils;
024: import org.jibx.runtime.IBindingFactory;
025: import org.jibx.runtime.IMarshallable;
026: import org.jibx.runtime.IMarshaller;
027: import org.jibx.runtime.IMarshallingContext;
028: import org.jibx.runtime.IXMLWriter;
029: import org.jibx.runtime.JiBXException;
030: import org.jibx.runtime.impl.StAXWriter;
031:
032: import javax.xml.stream.XMLStreamException;
033: import javax.xml.stream.XMLStreamReader;
034: import javax.xml.stream.XMLStreamWriter;
035: import java.io.ByteArrayInputStream;
036: import java.io.ByteArrayOutputStream;
037: import java.io.IOException;
038: import java.io.OutputStream;
039: import java.io.Writer;
040:
041: /** Data source for OM element backed by JiBX data bound object. */
042: public class JiBXDataSource implements OMDataSource {
043: /**
044: * Marshaller index (only needed if object does not have a top-level mapping definition in the
045: * binding, <code>-1</code> if not used).
046: */
047: private final int marshallerIndex;
048:
049: /** Element name (only used with {@link #marshallerIndex}). */
050: private final String elementName;
051:
052: /** Element namespace URI (only used with {@link #marshallerIndex}). */
053: private final String elementNamespace;
054:
055: /** Element namespace prefix (only used with {@link #marshallerIndex}). */
056: private final String elementNamespacePrefix;
057:
058: /** Element namespace index (only used with {@link #marshallerIndex}). */
059: private final int elementNamespaceIndex;
060:
061: /** Indexes of namespaces to be opened (only used with {@link #marshallerIndex}). */
062: private final int[] openNamespaceIndexes;
063:
064: /** Prefixes of namespaces to be opened (only used with {@link #marshallerIndex}). */
065: private final String[] openNamespacePrefixes;
066:
067: /** Data object for output. */
068: private final Object dataObject;
069:
070: /** Binding factory for creating marshaller. */
071: private final IBindingFactory bindingFactory;
072:
073: /**
074: * Constructor from marshallable object and binding factory.
075: *
076: * @param obj
077: * @param factory
078: */
079: public JiBXDataSource(IMarshallable obj, IBindingFactory factory) {
080: marshallerIndex = -1;
081: dataObject = obj;
082: bindingFactory = factory;
083: elementName = elementNamespace = elementNamespacePrefix = null;
084: elementNamespaceIndex = -1;
085: openNamespaceIndexes = null;
086: openNamespacePrefixes = null;
087: }
088:
089: /**
090: * Constructor from object with mapping index and binding factory.
091: *
092: * @param obj
093: * @param index
094: * @param name
095: * @param uri
096: * @param prefix
097: * @param nsindexes
098: * @param nsprefixes
099: * @param factory
100: */
101: public JiBXDataSource(Object obj, int index, String name,
102: String uri, String prefix, int[] nsindexes,
103: String[] nsprefixes, IBindingFactory factory) {
104: if (index < 0) {
105: throw new IllegalArgumentException(
106: "index value must be non-negative");
107: }
108: marshallerIndex = index;
109: dataObject = obj;
110: bindingFactory = factory;
111: boolean found = false;
112: String[] nss = factory.getNamespaces();
113: int nsidx = -1;
114: for (int i = 0; i < nsindexes.length; i++) {
115: if (uri.equals(nss[nsindexes[i]])) {
116: nsidx = nsindexes[i];
117: prefix = nsprefixes[i];
118: found = true;
119: break;
120: }
121: }
122: elementName = name;
123: elementNamespace = uri;
124: elementNamespacePrefix = prefix;
125: if (!found) {
126: for (int i = 0; i < nss.length; i++) {
127: if (uri.equals(nss[i])) {
128: nsidx = i;
129: break;
130: }
131: }
132: if (nsidx >= 0) {
133: int[] icpy = new int[nsindexes.length + 1];
134: icpy[0] = nsidx;
135: System.arraycopy(nsindexes, 0, icpy, 1,
136: nsindexes.length);
137: nsindexes = icpy;
138: String[] scpy = new String[nsprefixes.length + 1];
139: scpy[0] = prefix;
140: System.arraycopy(nsprefixes, 0, scpy, 1,
141: nsprefixes.length);
142: nsprefixes = scpy;
143: } else {
144: throw new IllegalStateException("Namespace not found");
145: }
146: }
147: elementNamespaceIndex = nsidx;
148: openNamespaceIndexes = nsindexes;
149: openNamespacePrefixes = nsprefixes;
150: }
151:
152: /**
153: * Internal method to handle the actual marshalling. If the source object is marshallable it's
154: * it's just marshalled directly, without worrying about redundant namespace declarations and
155: * such. If it needs to be handled with an abstract mapping, the handling is determined by the
156: * 'full' flag. When this is <code>true</code> all namespaces are declared directly, while if
157: * <code>false</code> the namespaces must have previously been declared on some enclosing
158: * element of the output. This allows the normal case of marshalling within the context of a
159: * SOAP message to be handled efficiently, while still generating correct XML if the element is
160: * marshalled directly (as when building the AXIOM representation for use by WS-Security).
161: *
162: * @param full
163: * @param ctx
164: * @throws JiBXException
165: */
166: private void marshal(boolean full, IMarshallingContext ctx)
167: throws JiBXException {
168: try {
169:
170: if (marshallerIndex < 0) {
171: if (dataObject instanceof IMarshallable) {
172: ((IMarshallable) dataObject).marshal(ctx);
173: } else {
174: throw new IllegalStateException(
175: "Object of class "
176: + dataObject.getClass().getName()
177: + " needs a JiBX <mapping> to be marshalled");
178: }
179: } else {
180: IXMLWriter wrtr = ctx.getXmlWriter();
181: String name = elementName;
182: int nsidx = 0;
183: if (full) {
184:
185: // declare all namespaces on start tag
186: nsidx = elementNamespaceIndex;
187: wrtr
188: .startTagNamespaces(nsidx, name,
189: openNamespaceIndexes,
190: openNamespacePrefixes);
191:
192: } else {
193:
194: // configure writer with namespace declared in enclosing scope
195: wrtr.openNamespaces(openNamespaceIndexes,
196: openNamespacePrefixes);
197: if (!"".equals(elementNamespacePrefix)) {
198: name = elementNamespacePrefix + ':' + name;
199: }
200: wrtr.startTagOpen(0, name);
201: }
202:
203: // marshal object representation (may include attributes) into element
204: IMarshaller mrsh = ctx
205: .getMarshaller(marshallerIndex, bindingFactory
206: .getMappedClasses()[marshallerIndex]);
207: mrsh.marshal(dataObject, ctx);
208: wrtr.endTag(nsidx, name);
209: }
210: ctx.getXmlWriter().flush();
211:
212: } catch (IOException e) {
213: throw new JiBXException(
214: "Error marshalling XML representation: "
215: + e.getMessage(), e);
216: }
217: }
218:
219: /* (non-Javadoc)
220: * @see org.apache.axiom.om.OMDataSource#serialize(java.io.OutputStream, org.apache.axiom.om.OMOutputFormat)
221: */
222: public void serialize(OutputStream output, OMOutputFormat format)
223: throws XMLStreamException {
224: try {
225:
226: // marshal with all namespace declarations, since external state unknown
227: IMarshallingContext ctx = bindingFactory
228: .createMarshallingContext();
229: ctx.setOutput(output, format == null ? null : format
230: .getCharSetEncoding());
231: marshal(true, ctx);
232:
233: } catch (JiBXException e) {
234: throw new XMLStreamException("Error in JiBX marshalling: "
235: + e.getMessage(), e);
236: }
237: }
238:
239: /* (non-Javadoc)
240: * @see org.apache.axiom.om.OMDataSource#serialize(java.io.Writer, org.apache.axiom.om.OMOutputFormat)
241: */
242: public void serialize(Writer writer, OMOutputFormat format)
243: throws XMLStreamException {
244: try {
245:
246: // marshal with all namespace declarations, since external state unknown
247: IMarshallingContext ctx = bindingFactory
248: .createMarshallingContext();
249: ctx.setOutput(writer);
250: marshal(true, ctx);
251:
252: } catch (JiBXException e) {
253: throw new XMLStreamException("Error in JiBX marshalling: "
254: + e.getMessage(), e);
255: }
256: }
257:
258: /* (non-Javadoc)
259: * @see org.apache.axiom.om.OMDataSource#serialize(javax.xml.stream.XMLStreamWriter)
260: */
261: public void serialize(XMLStreamWriter xmlWriter)
262: throws XMLStreamException {
263: try {
264:
265: // check if namespaces already declared for abstract mapping
266: boolean full = true;
267: String[] nss = bindingFactory.getNamespaces();
268: if (marshallerIndex >= 0) {
269: String prefix = xmlWriter.getPrefix(elementNamespace);
270: if (elementNamespacePrefix.equals(prefix)) {
271: full = false;
272: for (int i = 0; i < openNamespaceIndexes.length; i++) {
273: String uri = nss[i];
274: prefix = xmlWriter.getPrefix(uri);
275: if (!openNamespacePrefixes[i].equals(prefix)) {
276: full = true;
277: break;
278: }
279: }
280: }
281: }
282:
283: // marshal with all namespace declarations, since external state unknown
284: IXMLWriter writer = new StAXWriter(nss, xmlWriter);
285: IMarshallingContext ctx = bindingFactory
286: .createMarshallingContext();
287: ctx.setXmlWriter(writer);
288: marshal(full, ctx);
289:
290: } catch (JiBXException e) {
291: throw new XMLStreamException("Error in JiBX marshalling: "
292: + e.getMessage(), e);
293: }
294: }
295:
296: /* (non-Javadoc)
297: * @see org.apache.axiom.om.OMDataSource#getReader()
298: */
299: public XMLStreamReader getReader() throws XMLStreamException {
300: ByteArrayOutputStream bos = new ByteArrayOutputStream();
301: serialize(bos, null);
302: return StAXUtils
303: .createXMLStreamReader(new ByteArrayInputStream(bos
304: .toByteArray()));
305: }
306: }
|