blob: 93e9edcf2891e9e6526e38967f8bc67c7c2ec781 [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.axiom.util.stax;
import java.io.IOException;
import javax.activation.DataHandler;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.axiom.ext.stax.datahandler.DataHandlerProvider;
import org.apache.axiom.ext.stax.datahandler.DataHandlerWriter;
import org.apache.axiom.util.base64.Base64EncodingWriterOutputStream;
/**
* Contains utility methods to work with {@link XMLStreamWriter} objects.
*/
public class XMLStreamWriterUtils {
/**
* Write base64 encoded data to a stream writer. This will result in one or more
* {@link javax.xml.stream.XMLStreamConstants#CHARACTERS} events to be written
* to the stream (or zero events if the data handler produces an empty byte sequence),
* i.e. the data is streamed from the data handler directly to the stream writer.
* Since no in-memory base64 representation of the entire binary data is built, this
* method is suitable for very large amounts of data.
* <p>
* Note that this method will always serialize the data as base64 encoded character data.
* Serialization code should prefer using
* {@link #writeDataHandler(XMLStreamWriter, DataHandler, String, boolean)} or
* {@link #writeDataHandler(XMLStreamWriter, DataHandlerProvider, String, boolean)} to
* enable optimization (if supported by the {@link XMLStreamWriter}).
*
* @param writer the stream writer to write the data to
* @param dh the data handler containing the data to encode
* @throws IOException if an error occurs when reading the data from the data handler
* @throws XMLStreamException if an error occurs when writing the base64 encoded data to
* the stream
*/
public static void writeBase64(XMLStreamWriter writer, DataHandler dh)
throws IOException, XMLStreamException {
Base64EncodingWriterOutputStream out = new Base64EncodingWriterOutputStream(
new XMLStreamWriterWriter(writer), 4096, true);
try {
dh.writeTo(out);
out.close();
} catch (XMLStreamIOException ex) {
throw ex.getXMLStreamException();
}
}
private static DataHandlerWriter internalGetDataHandlerWriter(XMLStreamWriter writer) {
try {
return (DataHandlerWriter)writer.getProperty(DataHandlerWriter.PROPERTY);
} catch (IllegalArgumentException ex) {
return null;
}
}
/**
* Get the {@link DataHandlerWriter} extension for a given {@link XMLStreamWriter}. If the
* writer exposes the extension, a reference to the extension interface implementation is
* returned. If the writer doesn't expose the extension, this method returns an instance of the
* extension interface that emulates the extension (by writing the binary data as base64
* character data to the stream).
*
* @param writer
* the stream for which the method should return the {@link DataHandlerWriter}
* extension
* @return a reference to the extension interface exposed by the writer or an implementation the
* emulates the extension; the return value is never <code>null</code>
*/
public static DataHandlerWriter getDataHandlerWriter(final XMLStreamWriter writer) {
DataHandlerWriter dataHandlerWriter = internalGetDataHandlerWriter(writer);
if (dataHandlerWriter == null) {
return new DataHandlerWriter() {
@Override
public void writeDataHandler(DataHandler dataHandler, String contentID,
boolean optimize) throws IOException, XMLStreamException {
writeBase64(writer, dataHandler);
}
@Override
public void writeDataHandler(DataHandlerProvider dataHandlerProvider,
String contentID, boolean optimize) throws IOException, XMLStreamException {
writeBase64(writer, dataHandlerProvider.getDataHandler());
}
};
} else {
return dataHandlerWriter;
}
}
/**
* Write binary content to the stream. Depending on the supplied {@link XMLStreamWriter},
* the content will be written as base64 encoded character data or using an optimization
* scheme such as XOP/MTOM. The method attempts to submit the binary content using the
* {@link DataHandlerWriter} extension. If the writer doesn't expose this extension,
* the method will fall back to {@link #writeBase64(XMLStreamWriter, DataHandler)}.
* <p>
* Please refer to the documentation of {@link DataHandlerWriter} for a more
* detailed description of the semantics of the different arguments.
*
* @param writer
* the stream writer to write the data to
* @param dataHandler
* the binary content to write
* @param contentID
* an existing content ID for the binary data
* @param optimize
* indicates whether the content is eligible for optimization
* @throws IOException
* if an error occurs while reading from the data handler
* @throws XMLStreamException
* if an error occurs while writing to the underlying stream
*/
public static void writeDataHandler(XMLStreamWriter writer, DataHandler dataHandler,
String contentID, boolean optimize) throws IOException, XMLStreamException {
DataHandlerWriter dataHandlerWriter = internalGetDataHandlerWriter(writer);
if (dataHandlerWriter != null) {
dataHandlerWriter.writeDataHandler(dataHandler, contentID, optimize);
} else {
writeBase64(writer, dataHandler);
}
}
/**
* Write binary content to the stream. This method is similar to
* {@link #writeDataHandler(XMLStreamWriter, DataHandler, String, boolean)},
* but supports deferred loading of the data handler.
*
* @param writer
* the stream writer to write the data to
* @param dataHandlerProvider
* the binary content to write
* @param contentID
* an existing content ID for the binary data
* @param optimize
* indicates whether the content is eligible for optimization
* @throws IOException
* if an error occurs while reading from the data handler
* @throws XMLStreamException
* if an error occurs while writing to the underlying stream
*/
public static void writeDataHandler(XMLStreamWriter writer, DataHandlerProvider dataHandlerProvider,
String contentID, boolean optimize) throws IOException, XMLStreamException {
DataHandlerWriter dataHandlerWriter = internalGetDataHandlerWriter(writer);
if (dataHandlerWriter != null) {
dataHandlerWriter.writeDataHandler(dataHandlerProvider, contentID, optimize);
} else {
writeBase64(writer, dataHandlerProvider.getDataHandler());
}
}
/**
* Prepare the {@code DOCTYPE} declaration using the provided information and output it using
* {@link XMLStreamWriter#writeDTD(String)}.
*
* @param writer
* the stream writer to write the {@code DOCTYPE} declaration to
* @param rootName
* the root name, i.e. the name immediately following the {@code DOCTYPE} keyword
* @param publicId
* the public ID of the external subset, or <code>null</code> if there is no external
* subset or no public ID has been specified for the external subset
* @param systemId
* the system ID of the external subset, or <code>null</code> if there is no external
* subset
* @param internalSubset
* the internal subset, or <code>null</code> if there is none
* @throws XMLStreamException
* if an error occurs while writing to the stream
*/
public static void writeDTD(XMLStreamWriter writer, String rootName, String publicId,
String systemId, String internalSubset) throws XMLStreamException {
StringBuilder buffer = new StringBuilder("<!DOCTYPE ");
buffer.append(rootName);
if (publicId != null) {
buffer.append(" PUBLIC \"");
buffer.append(publicId);
buffer.append("\" \"");
buffer.append(systemId);
buffer.append("\"");
} else if (systemId != null) {
buffer.append(" SYSTEM \"");
buffer.append(systemId);
buffer.append("\"");
}
if (internalSubset != null) {
buffer.append(" [");
buffer.append(internalSubset);
buffer.append("]");
}
buffer.append(">");
writer.writeDTD(buffer.toString());
}
}