/* Copyright (c) 2001-2017, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 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.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS 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 HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR 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.
*/
package org.hsqldb.jdbc;
import java.io.
ByteArrayInputStream;
import java.io.
ByteArrayOutputStream;
import java.io.
CharArrayReader;
import java.io.
Closeable;
import java.io.
IOException;
import java.io.
InputStream;
import java.io.
InputStreamReader;
import java.io.
OutputStream;
import java.io.
OutputStreamWriter;
import java.io.
Reader;
import java.io.
StringReader;
import java.io.
Writer;
import java.lang.reflect.
Constructor;
import java.lang.reflect.
InvocationTargetException;
import java.nio.charset.
Charset;
import java.sql.
SQLException;
import java.sql.
SQLFeatureNotSupportedException;
import java.sql.
SQLXML;
import java.util.
ArrayList;
import java.util.
List;
import java.util.concurrent.
ArrayBlockingQueue;
import java.util.concurrent.
ExecutorService;
import java.util.concurrent.
ThreadPoolExecutor;
import java.util.concurrent.
TimeUnit;
import java.util.zip.
GZIPInputStream;
import java.util.zip.
GZIPOutputStream;
import javax.xml.bind.util.
JAXBResult;
import javax.xml.bind.util.
JAXBSource;
import javax.xml.parsers.
DocumentBuilder;
import javax.xml.parsers.
DocumentBuilderFactory;
import javax.xml.parsers.
FactoryConfigurationError;
import javax.xml.parsers.
ParserConfigurationException;
import javax.xml.stream.
XMLEventReader;
import javax.xml.stream.
XMLInputFactory;
import javax.xml.stream.
XMLOutputFactory;
import javax.xml.stream.
XMLStreamException;
import javax.xml.stream.
XMLStreamWriter;
import javax.xml.transform.
Result;
import javax.xml.transform.
Source;
import javax.xml.transform.
Transformer;
import javax.xml.transform.
TransformerConfigurationException;
import javax.xml.transform.
TransformerException;
import javax.xml.transform.
TransformerFactory;
import javax.xml.transform.
TransformerFactoryConfigurationError;
import javax.xml.transform.dom.
DOMResult;
import javax.xml.transform.dom.
DOMSource;
import javax.xml.transform.sax.
SAXResult;
import javax.xml.transform.sax.
SAXSource;
import javax.xml.transform.stax.
StAXResult;
import javax.xml.transform.stax.
StAXSource;
import javax.xml.transform.stream.
StreamResult;
import javax.xml.transform.stream.
StreamSource;
import org.hsqldb.error.
ErrorCode;
import org.hsqldb.lib.
ClosableByteArrayOutputStream;
import org.hsqldb.lib.
StringConverter;
import org.w3c.dom.
DOMException;
import org.w3c.dom.
DOMImplementation;
import org.w3c.dom.
Document;
import org.w3c.dom.
DocumentType;
import org.w3c.dom.
Element;
import org.w3c.dom.
EntityReference;
import org.w3c.dom.
Node;
import org.w3c.dom.
ProcessingInstruction;
import org.w3c.dom.
Text;
import org.w3c.dom.bootstrap.
DOMImplementationRegistry;
import org.xml.sax.
Attributes;
import org.xml.sax.
ContentHandler;
import org.xml.sax.
InputSource;
import org.xml.sax.
Locator;
import org.xml.sax.
SAXException;
/* $Id: JDBCSQLXML.java 5743 2017-04-09 14:22:33Z fredt $ */
/**
* <!-- start generic documentation -->
* The mapping in the JavaTM programming language for the SQL XML type.
* XML is a built-in type that stores an XML value
* as a column value in a row of a database table.
* By default drivers implement an SQLXML object as
* a logical pointer to the XML data
* rather than the data itself.
* An SQLXML object is valid for the duration of the transaction in which it was created.
* <p>
* The SQLXML interface provides methods for accessing the XML value
* as a String, a Reader or Writer, or as a Stream. The XML value
* may also be accessed through a Source or set as a Result, which
* are used with XML Parser APIs such as DOM, SAX, and StAX, as
* well as with XSLT transforms and XPath evaluations.
* <p>
* Methods in the interfaces ResultSet, CallableStatement, and PreparedStatement,
* such as getSQLXML allow a programmer to access an XML value.
* In addition, this interface has methods for updating an XML value.
* <p>
* The XML value of the SQLXML instance may be obtained as a BinaryStream using
* <pre>
* SQLXML sqlxml = resultSet.getSQLXML(column);
* InputStream binaryStream = sqlxml.getBinaryStream();
* </pre>
* For example, to parse an XML value with a DOM parser:
* <pre>
* DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
* Document result = parser.parse(binaryStream);
* </pre>
* or to parse an XML value with a SAX parser to your handler:
* <pre>
* SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
* parser.parse(binaryStream, myHandler);
* </pre>
* or to parse an XML value with a StAX parser:
* <pre>
* XMLInputFactory factory = XMLInputFactory.newInstance();
* XMLStreamReader streamReader = factory.createXMLStreamReader(binaryStream);
* </pre>
* <p>
* Because databases may use an optimized representation for the XML,
* accessing the value through getSource() and
* setResult() can lead to improved processing performance
* without serializing to a stream representation and parsing the XML.
* <p>
* For example, to obtain a DOM Document Node:
* <pre>
* DOMSource domSource = sqlxml.getSource(DOMSource.class);
* Document document = (Document) domSource.getNode();
* </pre>
* or to set the value to a DOM Document Node to myNode:
* <pre>
* DOMResult domResult = sqlxml.setResult(DOMResult.class);
* domResult.setNode(myNode);
* </pre>
* or, to send SAX events to your handler:
* <pre>
* SAXSource saxSource = sqlxml.getSource(SAXSource.class);
* XMLReader xmlReader = saxSource.getXMLReader();
* xmlReader.setContentHandler(myHandler);
* xmlReader.parse(saxSource.getInputSource());
* </pre>
* or, to set the result value from SAX events:
* <pre>
* SAXResult saxResult = sqlxml.setResult(SAXResult.class);
* ContentHandler contentHandler = saxResult.getXMLReader().getContentHandler();
* contentHandler.startDocument();
* // set the XML elements and attributes into the result
* contentHandler.endDocument();
* </pre>
* or, to obtain StAX events:
* <pre>
* StAXSource staxSource = sqlxml.getSource(StAXSource.class);
* XMLStreamReader streamReader = staxSource.getXMLStreamReader();
* </pre>
* or, to set the result value from StAX events:
* <pre>
* StAXResult staxResult = sqlxml.getResult(StAXResult.class);
* XMLStreamWriter streamWriter = staxResult.getXMLStreamWriter();
* </pre>
* or, to perform XSLT transformations on the XML value using the XSLT in xsltFile
* output to file resultFile:
* <pre>
* File xsltFile = new File("a.xslt");
* File myFile = new File("result.xml");
* Transformer xslt = TransformerFactory.newInstance().newTransformer(new StreamSource(xsltFile));
* Source source = sqlxml.getSource(null);
* Result result = new StreamResult(myFile);
* xslt.transform(source, result);
* </pre>
* or, to evaluate an XPath expression on the XML value:
* <pre>
* XPath xpath = XPathFactory.newInstance().newXPath();
* DOMSource domSource = sqlxml.getSource(DOMSource.class);
* Document document = (Document) domSource.getNode();
* String expression = "/foo/@bar";
* String barValue = xpath.evaluate(expression, document);
* </pre>
* To set the XML value to be the result of an XSLT transform:
* <pre>
* File sourceFile = new File("source.xml");
* Transformer xslt = TransformerFactory.newInstance().newTransformer(new StreamSource(xsltFile));
* Source streamSource = new StreamSource(sourceFile);
* Result result = sqlxml.setResult(null);
* xslt.transform(streamSource, result);
* </pre>
* Any Source can be transformed to a Result using the identity transform
* specified by calling newTransformer():
* <pre>
* Transformer identity = TransformerFactory.newInstance().newTransformer();
* Source source = sqlxml.getSource(null);
* File myFile = new File("result.xml");
* Result result = new StreamResult(myFile);
* identity.transform(source, result);
* </pre>
* To write the contents of a Source to standard output:
* <pre>
* Transformer identity = TransformerFactory.newInstance().newTransformer();
* Source source = sqlxml.getSource(null);
* Result result = new StreamResult(System.out);
* identity.transform(source, result);
* </pre>
* To create a DOMSource from a DOMResult:
* <pre>
* DOMSource domSource = new DOMSource(domResult.getNode());
* </pre>
* <p>
* Incomplete or invalid XML values may cause an SQLException when
* set or the exception may occur when execute() occurs. All streams
* must be closed before execute() occurs or an SQLException will be thrown.
* <p>
* Reading and writing XML values to or from an SQLXML object can happen at most once.
* The conceptual states of readable and not readable determine if one
* of the reading APIs will return a value or throw an exception.
* The conceptual states of writable and not writable determine if one
* of the writing APIs will set a value or throw an exception.
* <p>
* The state moves from readable to not readable once free() or any of the
* reading APIs are called: getBinaryStream(), getCharacterStream(), getSource(), and getString().
* Implementations may also change the state to not writable when this occurs.
* <p>
* The state moves from writable to not writeable once free() or any of the
* writing APIs are called: setBinaryStream(), setCharacterStream(), setResult(), and setString().
* Implementations may also change the state to not readable when this occurs.
* <p>
* All methods on the <code>SQLXML</code> interface must be fully implemented if the
* JDBC driver supports the data type.
* <!-- end generic documentation -->
*
* <!-- start release-specific documentation -->
* <div class="ReleaseSpecificDocumentation">
* <h3>HSQLDB-Specific Information:</h3> <p>
*
* Starting with HSQLDB 2.0, a rudimentary client-side SQLXML interface
* implementation (this class) is supported for local use when the product is
* built and run under JDK 1.6+ and the SQLXML instance is constructed as the
* result of calling JDBCConnection.createSQLXML(). <p>
*
* SQLXML instances retrieved in such a fashion are initially write-only, with
* the lifecycle of read and write availability constrained in accordance with
* the documentation of the interface methods. <p>
*
* When build and run under JDK 1.6+, it is also possible to retrieve read-only
* SQLXML instances from JDBCResultSet.getSQLXML(...), given that the underlying
* data can be converted to an XML Document Object Model (DOM). <p>
*
* However, at the time of this writing (2007-06-12) it is not yet possible to
* store SQLXML objects directly into an HSQLDB database or to use them directly
* for HSQLDB statement parameterization purposes. This is because the SQLXML
* data type is not yet natively supported by the HSQLDB engine. Instead, a
* JDBCSQLXML instance must first be read as a string, binary input stream,
* character input stream and so on, which can then be used for such purposes. <p>
*
* Here is the current read/write availability lifecycle for JDBCSQLXML:
*
* <TABLE summary="">
* <THEAD valign="bottom">
* <TR align="center">
* <TH>
* Origin
* </TH>
* <TH>
* Initially
* </TH>
* <TH>
* After 1<SUP>st</SUP> Write
* </TH>
* <TH>
* After 1<SUP>st</SUP> Read
* </TH>
* <TH>
* After 1<SUP>st</SUP> Free
* </TH>
* </TR>
* </THEAD>
* <TBODY>
* <TR >
* <TH>
* <tt>org.hsqldb.jdbc.JDBCConnection.createSQLXML()</tt>
* </TH>
* <TD >
* Write-only
* </TD>
* <TD>
* Read-only
* </TD>
* <TD>
* Not readable or writable
* </TD>
* <TD>
* Not readable or writable
* </TD>
* </TR>
* <TR>
* <TH>
* <tt>org.hsqldb.jdbc.JDBCResultSet.getSQLXML(...)</tt>
* </TH>
* <TD >
* Read-only
* </TD>
* <TD>
* N/A
* </TD>
* <TD>
* Not readable or writable
* </TD>
* <TD>
* Not readable or writable
* </TD>
* </TR>
* </TBODY>
* </TABLE>
* </div>
* <!-- end release-specific documentation -->
*
* @author Campbell Burnet (campbell-burnet@users dot sourceforge.net)
* @see javax.xml.parsers
* @see javax.xml.stream
* @see javax.xml.transform
* @see javax.xml.xpath
* @since JDK 1.6, HSQLDB 2.0
*/
public class
JDBCSQLXML implements
SQLXML {
private static
String domFeatures = "XML 3.0 Traversal +Events 2.0";
private static
DOMImplementation domImplementation;
private static
DOMImplementationRegistry domImplementationRegistry;
private static
ThreadPoolExecutor executorService;
private static
Transformer identityTransformer;
private static
TransformerFactory transformerFactory;
/**
* Precomputed Charset to reduce octet to character sequence conversion
* charset lookup overhead.
*/
private static final
Charset utf8Charset;
private static
ArrayBlockingQueue<
Runnable>
workQueue;
static {
Charset charset = null;
try {
charset =
Charset.
forName("UTF8");
} catch (
Exception e) {
}
utf8Charset =
charset;
}
/**
* When non-null, the SAX ContentHandler currently in use to build this
* object's SQLXML value from a SAX event sequence.
*/
private
SAX2DOMBuilder builder;
/**
* Whether this object is closed. When closed, no further reading
* or writing is possible.
*/
private boolean
closed;
// ------------------------- Internal Implementation -----------------------
/**
* This object's SQLXML value as a GZIPed byte array
*/
private volatile byte[]
gzdata;
/**
* When non-null, the stream currently in use to read this object's
* SQLXML value as an octet sequence.
*/
private
InputStream inputStream;
/**
* When non-null, the stream currently in use to write this object's
* SQLXML value from an octet sequence.
*/
private
ClosableByteArrayOutputStream outputStream;
/**
* When non-null, the DOMResult currently in use to build this object's
* SQLXML value.
*/
private
DOMResult domResult;
/**
* This object's public id
*/
private
String publicId;
/**
* Whether it is possible to read this object's SQLXML value.
*/
private boolean
readable;
/**
* This object's system id
*/
private
String systemId;
/**
* Whether it is possible to write this object's SQLXML value.
*/
private boolean
writable;
/**
* Constructs a new, initially write-only JDBCSQLXML object. <p>
*/
protected
JDBCSQLXML() {
setReadable(false);
setWritable(true);
}
/**
* Constructs a new read-only JDBCSQLXML object from the given octet
* sequence. <p>
*
* @param bytes the octet sequence representing the SQLXML value
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(byte[]
bytes) throws
SQLException {
this(
bytes, null);
}
/**
* Constructs a new read-only JDBCSQLXML object from the given character
* sequence. <p>
*
* @param chars the character sequence representing the SQLXML value
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(char[]
chars) throws
SQLException {
this(
chars, 0,
chars.length, null);
}
/**
* Constructs a new JDBCSQLXML object from the given Document. <p>
*
* @param document the Document representing the SQLXML value
* @throws java.sql.SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(
Document document) throws
SQLException {
this(new
DOMSource(
document));
}
/**
* Constructs a new read-only JDBCSQLXML object from the given octet
* sequence. <p>
*
* Relative URI references will be resolved against the present working
* directory reported by the Java virtual machine. <p>
*
* @param inputStream an octet stream representing an SQLXML value
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(
InputStream inputStream) throws
SQLException {
this(
inputStream, null);
}
/**
* Constructs a new read-only JDBCSQLXML object from the given character
* sequence. <p>
*
* Relative URI references will be resolved against the present working
* directory reported by the Java virtual machine. <p>
*
* <b>Note:</b>Normally, a byte stream should be used rather than a reader,
* so that the XML parser can resolve character encoding specified by the
* XML declaration. However, in many cases the encoding of the input stream
* is already resolved, as in the case of reading XML from a StringReader.
*
* @param reader a character stream representing an SQLXML value
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(
Reader reader) throws
SQLException {
this(
reader, null);
}
/**
* Constructs a new read-only JDBCSQLXML object from the given Source
* object. <p>
*
* @param source a Source representing an SQLXML value
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
public
JDBCSQLXML(
Source source) throws
SQLException {
init(
source);
}
/**
* Constructs a new read-only JDBCSQLXML object from the given character
* sequence. <p>
*
* Relative URI references will be resolved against the present working
* directory reported by the Java virtual machine. <p>
*
* @param string a character sequence representing an SQLXML value
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(
String string) throws
SQLException {
this(new
StreamSource(new
StringReader(
string)));
}
/**
* Constructs a new read-only JDBCSQLXML object from the given octet
* sequence. <p>
*
* @param bytes the octet sequence representing the SQLXML value
* @param systemId must be a String that conforms to the URI syntax;
* allows relative URIs to be processed.
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(byte[]
bytes,
String systemId) throws
SQLException {
this(new
StreamSource(new
ByteArrayInputStream(
bytes),
systemId));
}
protected
JDBCSQLXML(char[]
chars,
String systemId) throws
SQLException {
this(
chars, 0,
chars.length,
systemId);
}
/**
* Constructs a new read-only JDBCSQLXML object from the given octet
* sequence. <p>
*
* Relative URI references will be resolved against the given systemId. <p>
*
* @param inputStream an octet stream representing an SQLXML value
* @param systemId a String that conforms to the URI syntax, indicating
* the URI from which the XML data is being read, so that relative URI
* references can be resolved
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(
InputStream inputStream,
String systemId) throws
SQLException {
this(new
StreamSource(
inputStream,
systemId));
}
/**
* Constructs a new read-only JDBCSQLXML object from the given character
* sequence. <p>
*
* Relative URI references will be resolved against the given systemId. <p>
*
* <b>Note:</b>Normally, a byte stream should be used rather than a reader,
* so that the XML parser can resolve character encoding specified by the
* XML declaration. However, in many cases the encoding of the input stream
* is already resolved, as in the case of reading XML from a StringReader.
*
* @param reader a character stream representing an SQLXML value;
* @param systemId a String that conforms to the URI syntax, indicating
* the URI from which the XML data is being read, so that relative URI
* references can be resolved
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(
Reader reader,
String systemId) throws
SQLException {
this(new
StreamSource(
reader,
systemId));
}
/**
* Constructs a new read-only JDBCSQLXML object from the given character
* sequence. <p>
*
* Relative URI references will be resolved against the given systemId. <p>
*
* @param string a character sequence representing an SQLXML value
* @param systemId a String that conforms to the URI syntax, indicating
* the URI from which the XML data is being read, so that relative URI
* references can be resolved
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(
String string,
String systemId) throws
SQLException {
this(new
StreamSource(new
StringReader(
string),
systemId));
}
/**
* Constructs a new read-only JDBCSQLXML object from the given gzipped octet
* sequence. <p>
*
* @param bytes the gzipped octet sequence representing the SQLXML value
* @param clone whether to clone the given gzipped octet sequence
* @param systemId
* @param publicId
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected
JDBCSQLXML(byte[]
bytes, boolean
clone,
String systemId,
String publicId) throws
SQLException {
this.
setGZipData(
clone ?
bytes.
clone()
:
bytes);
this.
systemId =
systemId;
this.
publicId =
publicId;
}
protected
JDBCSQLXML(char[]
chars, int
offset, int
length,
String systemId) throws
SQLException {
this(new
StreamSource(new
CharArrayReader(
chars,
offset,
length),
systemId));
}
/**
* This method closes this object and releases the resources that it held.
* The SQL XML object becomes invalid and neither readable or writable
* when this method is called.
*
* After <code>free</code> has been called, any attempt to invoke a
* method other than <code>free</code> will result in a <code>SQLException</code>
* being thrown. If <code>free</code> is called multiple times, the subsequent
* calls to <code>free</code> are treated as a no-op.
* @throws SQLException if there is an error freeing the XML value.
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6
*/
public void
free() throws
SQLException {
close();
}
/**
* Retrieves the XML value designated by this SQLXML instance as a stream.
* The bytes of the input stream are interpreted according to appendix F of the XML 1.0 specification.
* The behavior of this method is the same as ResultSet.getBinaryStream()
* when the designated column of the ResultSet has a type java.sql.Types of SQLXML.
* <p>
* The SQL XML object becomes not readable when this method is called and
* may also become not writable depending on implementation.
*
* @return a stream containing the XML data.
* @throws SQLException if there is an error processing the XML value.
* An exception is thrown if the state is not readable.
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6
*/
public synchronized
InputStream getBinaryStream() throws
SQLException {
checkClosed();
checkReadable();
InputStream rval =
getBinaryStreamImpl();
setReadable(false);
setWritable(false);
return
rval;
}
/**
* Retrieves a stream that can be used to write the XML value that this SQLXML instance represents.
* The stream begins at position 0.
* The bytes of the stream are interpreted according to appendix F of the XML 1.0 specification
* The behavior of this method is the same as ResultSet.updateBinaryStream()
* when the designated column of the ResultSet has a type java.sql.Types of SQLXML.
* <p>
* The SQL XML object becomes not writeable when this method is called and
* may also become not readable depending on implementation.
*
* @return a stream to which data can be written.
* @throws SQLException if there is an error processing the XML value.
* An exception is thrown if the state is not writable.
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6
*/
public synchronized
OutputStream setBinaryStream() throws
SQLException {
checkClosed();
checkWritable();
OutputStream rval =
setBinaryStreamImpl();
setWritable(false);
setReadable(true);
return
rval;
}
/**
* Retrieves the XML value designated by this SQLXML instance as a java.io.Reader object.
* The format of this stream is defined by org.xml.sax.InputSource,
* where the characters in the stream represent the unicode code points for
* XML according to section 2 and appendix B of the XML 1.0 specification.
* Although an encoding declaration other than unicode may be present,
* the encoding of the stream is unicode.
* The behavior of this method is the same as ResultSet.getCharacterStream()
* when the designated column of the ResultSet has a type java.sql.Types of SQLXML.
* <p>
* The SQL XML object becomes not readable when this method is called and
* may also become not writable depending on implementation.
*
* @return a stream containing the XML data.
* @throws SQLException if there is an error processing the XML value.
* The getCause() method of the exception may provide a more detailed exception, for example,
* if the stream does not contain valid characters.
* An exception is thrown if the state is not readable.
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6
*/
public synchronized
Reader getCharacterStream() throws
SQLException {
checkClosed();
checkReadable();
Reader reader =
getCharacterStreamImpl();
setReadable(false);
setWritable(false);
return
reader;
}
/**
* Retrieves a stream to be used to write the XML value that this SQLXML instance represents.
* The format of this stream is defined by org.xml.sax.InputSource,
* where the characters in the stream represent the unicode code points for
* XML according to section 2 and appendix B of the XML 1.0 specification.
* Although an encoding declaration other than unicode may be present,
* the encoding of the stream is unicode.
* The behavior of this method is the same as ResultSet.updateCharacterStream()
* when the designated column of the ResultSet has a type java.sql.Types of SQLXML.
* <p>
* The SQL XML object becomes not writable when this method is called and
* may also become not readable depending on implementation.
*
* @return a stream to which data can be written.
* @throws SQLException if there is an error processing the XML value.
* The getCause() method of the exception may provide a more detailed exception, for example,
* if the stream does not contain valid characters.
* An exception is thrown if the state is not writable.
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6 Build 79
*/
public synchronized
Writer setCharacterStream() throws
SQLException {
checkClosed();
checkWritable();
Writer writer =
setCharacterStreamImpl();
setReadable(true);
setWritable(false);
return
writer;
}
/**
* Returns a string representation of the XML value designated by this SQLXML instance.
* The format of this String is defined by org.xml.sax.InputSource,
* where the characters in the stream represent the unicode code points for
* XML according to section 2 and appendix B of the XML 1.0 specification.
* Although an encoding declaration other than unicode may be present,
* the encoding of the String is unicode.
* The behavior of this method is the same as ResultSet.getString()
* when the designated column of the ResultSet has a type java.sql.Types of SQLXML.
* <p>
* The SQL XML object becomes not readable when this method is called and
* may also become not writable depending on implementation.
*
* @return a string representation of the XML value designated by this SQLXML instance.
* @throws SQLException if there is an error processing the XML value.
* The getCause() method of the exception may provide a more detailed exception, for example,
* if the stream does not contain valid characters.
* An exception is thrown if the state is not readable.
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6
*/
public synchronized
String getString() throws
SQLException {
checkClosed();
checkReadable();
String value =
getStringImpl();
setReadable(false);
setWritable(false);
return
value;
}
/**
* Sets the XML value designated by this SQLXML instance to the given String representation.
* The format of this String is defined by org.xml.sax.InputSource,
* where the characters in the stream represent the unicode code points for
* XML according to section 2 and appendix B of the XML 1.0 specification.
* Although an encoding declaration other than unicode may be present,
* the encoding of the String is unicode.
* The behavior of this method is the same as ResultSet.updateString()
* when the designated column of the ResultSet has a type java.sql.Types of SQLXML.
* <p>
* The SQL XML object becomes not writeable when this method is called and
* may also become not readable depending on implementation.
*
* @param value the XML value
* @throws SQLException if there is an error processing the XML value.
* The getCause() method of the exception may provide a more detailed exception, for example,
* if the stream does not contain valid characters.
* An exception is thrown if the state is not writable.
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6
*/
public synchronized void
setString(
String value) throws
SQLException {
if (
value == null) {
throw
JDBCUtil.
nullArgument("value");
}
checkWritable();
setStringImpl(
value);
setReadable(true);
setWritable(false);
}
/**
* Returns a Source for reading the XML value designated by this SQLXML instance.
* Sources are used as inputs to XML parsers and XSLT transformers.
* <p>
* Sources for XML parsers will have namespace processing on by default.
* The systemID of the Source is implementation dependent.
* <p>
* The SQL XML object becomes not readable when this method is called and
* may also become not writable depending on implementation.
* <p>
* Note that SAX is a callback architecture, so a returned
* SAXSource should then be set with a content handler that will
* receive the SAX events from parsing. The content handler
* will receive callbacks based on the contents of the XML.
* <pre>
* SAXSource saxSource = sqlxml.getSource(SAXSource.class);
* XMLReader xmlReader = saxSource.getXMLReader();
* xmlReader.setContentHandler(myHandler);
* xmlReader.parse(saxSource.getInputSource());
* </pre>
*
* @param sourceClass The class of the source, or null.
* If the class is null, a vendor specific Source implementation will be returned.
* The following classes are supported at a minimum:
* <pre>
* javax.xml.transform.dom.DOMSource - returns a DOMSource
* javax.xml.transform.sax.SAXSource - returns a SAXSource
* javax.xml.transform.stax.StAXSource - returns a StAXSource
* javax.xml.transform.stream.StreamSource - returns a StreamSource
* </pre>
* @return a Source for reading the XML value.
* @throws SQLException if there is an error processing the XML value
* or if this feature is not supported.
* The getCause() method of the exception may provide a more detailed exception, for example,
* if an XML parser exception occurs.
* An exception is thrown if the state is not readable.
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6 Build 79
*/
@
SuppressWarnings("unchecked")
public synchronized <T extends
Source>T
getSource(
Class<T>
sourceClass) throws
SQLException {
checkClosed();
checkReadable();
final
Source source =
getSourceImpl(
sourceClass);
setReadable(false);
setWritable(false);
return (T)
source;
}
/**
* Returns a Result for setting the XML value designated by this SQLXML instance.
* <p>
* The systemID of the Result is implementation dependent.
* <p>
* The SQL XML object becomes not writable when this method is called and
* may also become not readable depending on implementation.
* <p>
* Note that SAX is a callback architecture and the returned
* SAXResult has a content handler assigned that will receive the
* SAX events based on the contents of the XML. Call the content
* handler with the contents of the XML document to assign the values.
* <pre>
* SAXResult saxResult = sqlxml.getResult(SAXResult.class);
* ContentHandler contentHandler = saxResult.getXMLReader().getContentHandler();
* contentHandler.startDocument();
* // set the XML elements and attributes into the result
* contentHandler.endDocument();
* </pre>
*
* @param resultClass The class of the result, or null.
* If resultClass is null, a vendor specific Result implementation will be returned.
* The following classes are supported at a minimum:
* <pre>
* javax.xml.transform.dom.DOMResult - returns a DOMResult
* javax.xml.transform.sax.SAXResult - returns a SAXResult
* javax.xml.transform.stax.StAXResult - returns a StAXResult
* javax.xml.transform.stream.StreamResult - returns a StreamResult
* </pre>
* @return Returns a Result for setting the XML value.
* @throws SQLException if there is an error processing the XML value
* or if this feature is not supported.
* The getCause() method of the exception may provide a more detailed exception, for example,
* if an XML parser exception occurs.
* An exception is thrown if the state is not writable.
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6 Build 79
*/
public synchronized <T extends
Result>T
setResult(
Class<T>
resultClass) throws
SQLException {
checkClosed();
checkWritable();
final T
result =
createResult(
resultClass);
setReadable(true);
setWritable(false);
return
result;
}
/**
* @return that may be used to perform processing asynchronously.
*/
protected static
ExecutorService getExecutorService() {
if (
JDBCSQLXML.
executorService == null) {
int
corePoolSize = 1;
int
maximumPoolSize = 10;
long
keepAliveTime = 1;
TimeUnit unit =
TimeUnit.
SECONDS;
JDBCSQLXML.
workQueue = new
ArrayBlockingQueue<
Runnable>(10);
JDBCSQLXML.
executorService = new
ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue);
}
return
executorService;
}
/**
* @return with which to obtain xml transformer instances.
* @throws java.sql.SQLException when unable to obtain a factory instance.
*/
protected static
TransformerFactory getTransformerFactory() throws
SQLException {
if (
JDBCSQLXML.
transformerFactory == null) {
try {
JDBCSQLXML.
transformerFactory =
TransformerFactory.
newInstance();
} catch (
TransformerFactoryConfigurationError ex) {
throw
Exceptions.
transformFailed(
ex);
}
}
return
JDBCSQLXML.
transformerFactory;
}
/**
* @return used to perform identity transforms
* @throws java.sql.SQLException when unable to obtain the instance.
*/
protected static
Transformer getIdentityTransformer() throws
SQLException {
if (
JDBCSQLXML.
identityTransformer == null) {
try {
JDBCSQLXML.
identityTransformer =
getTransformerFactory().
newTransformer();
} catch (
TransformerConfigurationException ex) {
throw
Exceptions.
transformFailed(
ex);
}
}
return
JDBCSQLXML.
identityTransformer;
}
/**
* @return with which to construct DOM implementation instances.
* @throws java.sql.SQLException when unable to obtain a factory instance.
*/
protected static
DOMImplementationRegistry getDOMImplementationRegistry() throws
SQLException {
if (
domImplementationRegistry == null) {
try {
domImplementationRegistry =
DOMImplementationRegistry.
newInstance();
} catch (
ClassCastException ex) {
throw
Exceptions.
domInstantiation(
ex);
} catch (
InstantiationException ex) {
throw
Exceptions.
domInstantiation(
ex);
} catch (
ClassNotFoundException ex) {
throw
Exceptions.
domInstantiation(
ex);
} catch (
IllegalAccessException ex) {
throw
Exceptions.
domInstantiation(
ex);
}
}
return
domImplementationRegistry;
}
/**
* @return with which to create document instances.
* @throws java.sql.SQLException when unable to obtain the DOM
* implementation instance.
*/
protected static
DOMImplementation getDOMImplementation() throws
SQLException {
if (
domImplementation == null) {
domImplementation =
getDOMImplementationRegistry().
getDOMImplementation(
domFeatures);
}
if (
domImplementation == null) {
Exception ex = new
RuntimeException("Not supported: "
+
domFeatures);
throw
Exceptions.
domInstantiation(
ex);
}
return
domImplementation;
}
/**
* @param namespaceURI of the document element to create or <code>null</code>.
* @param qualifiedName of the document element to be created or <code>null</code>.
* @param docType of document to be created or <code>null</code>.
* When <code>doctype</code> is not <code>null</code>, its
* <code>Node.ownerDocument</code> attribute is set to the document
* being created.
* @return with its document element.
* If the <code>NamespaceURI</code>, <code>qualifiedName</code>, and
* <code>doctype</code> are <code>null</code>, the returned
* <code>Document</code> is empty with no document element.
* @throws java.sql.SQLException wrapping any internal exception that occurs.
* @see org.w3c.dom.DOMImplementation#createDocument(String,String,DocumentType)
*/
protected static
Document createDocument(
String namespaceURI,
String qualifiedName,
DocumentType docType) throws
SQLException {
try {
return
getDOMImplementation().
createDocument(
namespaceURI,
qualifiedName,
docType);
} catch (
DOMException ex) {
throw
Exceptions.
domInstantiation(
ex);
}
}
/**
* @return that is empty with no document element.
* @throws java.sql.SQLException wrapping any internal exception that occurs.
*/
protected static
Document createDocument() throws
SQLException {
return
createDocument(null, null, null);
}
/**
* Initializes this object's SQLXML value from the given Source
* object. <p>
*
* @param source the Source representing the SQLXML value
* @throws SQLException if the argument does not represent a
* valid SQLXML value
*/
protected void
init(
Source source) throws
SQLException {
if (
source == null) {
throw
JDBCUtil.
nullArgument("source");
}
Transformer transformer =
JDBCSQLXML.
getIdentityTransformer();
StreamResult result = new
StreamResult();
ByteArrayOutputStream baos = new
ByteArrayOutputStream();
GZIPOutputStream gzos;
try {
gzos = new
GZIPOutputStream(
baos);
} catch (
IOException ex) {
throw
Exceptions.
transformFailed(
ex);
}
result.
setOutputStream(
gzos);
try {
transformer.
transform(
source,
result);
} catch (
TransformerException ex) {
throw
Exceptions.
transformFailed(
ex);
}
try {
gzos.
close();
} catch (
IOException ex) {
throw
Exceptions.
transformFailed(
ex);
}
byte[]
data =
baos.
toByteArray();
setGZipData(
data);
setReadable(true);
setWritable(false);
}
/**
* Assigns this object's SQLXML value from the designated gzipped array
* of bytes.
*
* @param data the SQLXML value
* @throws java.sql.SQLException if the argument does not represent a
* valid SQLXML value
*/
protected void
setGZipData(byte[]
data) throws
SQLException {
if (
data == null) {
throw
JDBCUtil.
nullArgument("data");
}
this.
gzdata =
data;
}
/**
* Directly retrieves this object's present SQLMXL value as a gzipped
* array of bytes. <p>
*
* May be null, empty or invalid.
*
* @return this object's SQLMXL value as a gzipped byte array
*/
protected byte[]
gZipData() {
return this.
gzdata;
}
/**
* Retrieves this object's SQLXML value as a gzipped array of bytes,
* possibly by terminating any in-progress write operations and converting
* accumulated intermediate data.
*
* @throws java.sql.SQLException if an underlying I/O or transform
* error occurs
* @return this object's SQLXML value
*/
protected byte[]
getGZipData() throws
SQLException {
byte[]
bytes =
gZipData();
if (
bytes != null) {
return
bytes;
}
if (this.
domResult != null) {
DOMSource source = new
DOMSource(
domResult.
getNode(),
domResult.
getSystemId());
OutputStream os =
setBinaryStreamImpl();
StreamResult result = new
StreamResult(
os);
try {
JDBCSQLXML.
identityTransformer.
transform(
source,
result);
} catch (
TransformerException ex) {
throw
Exceptions.
transformFailed(
ex);
}
try {
os.
close();
} catch (
IOException ex) {
throw
Exceptions.
transformFailed(
ex);
}
}
if (this.
outputStream == null) {
throw
Exceptions.
notReadable("No Data.");
} else if (!this.
outputStream.
isClosed()) {
throw
Exceptions.
notReadable(
"Stream used for writing must be closed but is still open.");
} else if (this.
outputStream.
isFreed()) {
throw
Exceptions.
notReadable(
"Stream used for writing was freed and is no longer valid.");
}
try {
setGZipData(this.
outputStream.
toByteArray());
return
gZipData();
} catch (
IOException ex) {
throw
Exceptions.
notReadable();
} finally {
this.
freeOutputStream();
}
}
/**
* closes this object and releases the resources that it holds.
*/
protected synchronized void
close() {
this.
closed = true;
setReadable(false);
setWritable(false);
freeOutputStream();
freeInputStream();
freeDomResult();
this.
gzdata = null;
}
/**
* Closes the input stream, if any, currently in use to read this object's
* SQLXML value and nullifies the stream reference to make it eligible
* for subsequent garbage collection.
*/
protected void
freeInputStream() {
if (this.
inputStream != null) {
try {
this.
inputStream.
close();
} catch (
IOException ex) {
// ex.printStackTrace();
} finally {
this.
inputStream = null;
}
}
}
/**
* Closes the output stream, if any, currently in use to write this object's
* SQLXML value and nullifies the stream reference to make it eligible for
* subsequent garbage collection. The stream's data buffer reference may
* also be nullified, in order to make it eligible for garbage collection
* immediately, just in case an external client still holds a reference to
* the output stream.
*/
protected void
freeOutputStream() {
if (this.
outputStream != null) {
try {
this.
outputStream.
free();
} catch (
IOException ex) {
// ex.printStackTrace();
}
this.
outputStream = null;
}
}
/**
* Checks whether this object is closed (has been freed).
*
* @throws java.sql.SQLException if this object is closed.
*/
protected synchronized void
checkClosed() throws
SQLException {
if (this.
closed) {
throw
Exceptions.
inFreedState();
}
}
/**
* Checks whether this object is readable.
*
* @throws java.sql.SQLException if this object is not readable.
*/
protected synchronized void
checkReadable() throws
SQLException {
if (!this.
isReadable()) {
throw
Exceptions.
notReadable();
}
}
/**
* Assigns this object's readability status.
*
* @param readable if <tt>true</tt>, then readable; else not readable
*/
protected synchronized void
setReadable(boolean
readable) {
this.
readable =
readable;
}
/**
* Checks whether this object is writable.
*
* @throws java.sql.SQLException if this object is not writable.
*/
protected synchronized void
checkWritable() throws
SQLException {
if (!this.
isWritable()) {
throw
Exceptions.
notWritable();
}
}
/**
* Assigns this object's writability status.
*
* @param writable if <tt>true</tt>, then writable; else not writable
*/
protected synchronized void
setWritable(boolean
writable) {
this.
writable =
writable;
}
/**
* Retrieves the object's readability status.
*
* @return if <tt>true</tt>, then readable; else not readable
*/
public synchronized boolean
isReadable() {
return this.
readable;
}
/**
* Retrieves the object's readability status.
*
* @return if <tt>true</tt>, then writable; else not writable
*/
public synchronized boolean
isWritable() {
return this.
writable;
}
/**
* Retrieves a stream representing the XML value designated by this
* SQLXML instance. <p>
*
* @return a stream containing the XML data.
* @throws SQLException if there is an error processing the XML value.
*/
protected
InputStream getBinaryStreamImpl() throws
SQLException {
try {
byte[]
data =
getGZipData();
ByteArrayInputStream bais = new
ByteArrayInputStream(
data);
return new
GZIPInputStream(
bais);
} catch (
IOException ex) {
throw
Exceptions.
transformFailed(
ex);
}
}
/**
* Retrieves a reader representing the XML value designated by this
* SQLXML instance. <p>
*
* @return a reader containing the XML data.
* @throws SQLException if there is an error processing the XML value.
*/
protected
Reader getCharacterStreamImpl() throws
SQLException {
return new
InputStreamReader(
getBinaryStreamImpl());
}
/**
* Retrieves a string representing the XML value designated by this
* SQLXML instance. <p>
*
* @return a string containing the XML data.
* @throws SQLException if there is an error processing the XML value.
*/
protected
String getStringImpl() throws
SQLException {
try {
return
StringConverter.
inputStreamToString(
getBinaryStreamImpl(),
"US-ASCII");
} catch (
IOException ex) {
throw
Exceptions.
transformFailed(
ex);
}
}
/**
* Retrieves a stream to completely write the XML value this SQLXML
* instance represents. <p>
*
* @return a stream to which the data can be written.
* @throws SQLException if there is an error processing the XML value.
*/
protected
OutputStream setBinaryStreamImpl() throws
SQLException {
this.
outputStream = new
ClosableByteArrayOutputStream();
try {
return new
GZIPOutputStream(this.
outputStream);
} catch (
IOException ex) {
this.
outputStream = null;
throw
Exceptions.
resultInstantiation(
ex);
}
}
/**
* Retrieves a writer to completely write the XML value this SQLXML
* instance represents. <p>
*
* @return a writer to which the data can be written.
* @throws SQLException if there is an error processing the XML value.
* The getCause() method of the exception may provide a more detailed exception, for example,
* if the stream does not contain valid characters.
* An exception is thrown if the state is not writable.
* @since JDK 1.6 Build 79
*/
protected
Writer setCharacterStreamImpl() throws
SQLException {
return new
OutputStreamWriter(
setBinaryStreamImpl());
}
/**
* Sets the XML value designated by this SQLXML instance using the given
* String representation. <p>
*
* @param value the XML value
* @throws SQLException if there is an error processing the XML value.
*/
protected void
setStringImpl(
String value) throws
SQLException {
init(new
StreamSource(new
StringReader(
value)));
}
/**
* Returns a Source for reading the XML value designated by this SQLXML
* instance. <p>
*
* @param sourceClass The class of the source, or null. If null, then a
* DOMSource is returned.
* @return a Source for reading the XML value.
* @throws SQLException if there is an error processing the XML value
* or if the given <tt>sourceClass</tt> is not supported.
*/
protected <T extends
Source>T
getSourceImpl(
Class<T>
sourceClass) throws
SQLException {
if (
JAXBSource.class.
isAssignableFrom(
sourceClass)) {
// Must go first presently, since JAXBSource extends SAXSource
// (purely as an implementation detail) and it's not possible
// to instantiate a valid JAXBSource with a Zero-Args
// constructor(or any subclass thereof, due to the finality of
// its private marshaller and context object attributes)
// FALL THROUGH... will throw an exception
} else if (
StreamSource.class.
isAssignableFrom(
sourceClass)) {
return
createStreamSource(
sourceClass);
} else if ((
sourceClass == null)
||
DOMSource.class.
isAssignableFrom(
sourceClass)) {
return
createDOMSource(
sourceClass);
} else if (
SAXSource.class.
isAssignableFrom(
sourceClass)) {
return
createSAXSource(
sourceClass);
} else if (
StAXSource.class.
isAssignableFrom(
sourceClass)) {
return
createStAXSource(
sourceClass);
}
throw
JDBCUtil.
invalidArgument("sourceClass: " +
sourceClass);
}
/**
* Retrieves a new StreamSource for reading the XML value designated by this
* SQLXML instance. <p>
*
* @param sourceClass The class of the source
* @throws java.sql.SQLException if there is an error processing the XML
* value or if the given <tt>sourceClass</tt> is not supported.
* @return a new StreamSource for reading the XML value designated by this
* SQLXML instance
*/
@
SuppressWarnings("unchecked")
protected <T extends
Source>T
createStreamSource(
Class<T>
sourceClass) throws
SQLException {
StreamSource source = null;
try {
source = (
sourceClass == null) ? new
StreamSource()
: (
StreamSource)
sourceClass.
newInstance();
} catch (
SecurityException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
InstantiationException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
IllegalAccessException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
ClassCastException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
}
Reader reader =
getCharacterStreamImpl();
source.
setReader(
reader);
return (T)
source;
}
/**
* Retrieves a new DOMSource for reading the XML value designated by this
* SQLXML instance. <p>
*
* @param sourceClass The class of the source
* @throws java.sql.SQLException if there is an error processing the XML
* value or if the given <tt>sourceClass</tt> is not supported.
* @return a new DOMSource for reading the XML value designated by this
* SQLXML instance
*/
@
SuppressWarnings("unchecked")
protected <T extends
Source>T
createDOMSource(
Class<T>
sourceClass) throws
SQLException {
DOMSource source = null;
try {
source = (
sourceClass == null) ? new
DOMSource()
: (
DOMSource)
sourceClass.
newInstance();
} catch (
SecurityException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
IllegalAccessException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
InstantiationException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
ClassCastException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
}
Transformer transformer =
JDBCSQLXML.
getIdentityTransformer();
InputStream stream = this.
getBinaryStreamImpl();
StreamSource streamSource = new
StreamSource();
DOMResult result = new
DOMResult();
streamSource.
setInputStream(
stream);
try {
transformer.
transform(
streamSource,
result);
} catch (
TransformerException ex) {
throw
Exceptions.
transformFailed(
ex);
}
source.
setNode(
result.
getNode());
source.
setSystemId(
result.
getSystemId());
return (T)
source;
}
/**
* Retrieves a new SAXSource for reading the XML value designated by this
* SQLXML instance. <p>
*
* @param sourceClass The class of the source
* @throws java.sql.SQLException if there is an error processing the XML
* value or if the given <tt>sourceClass</tt> is not supported.
* @return a new SAXSource for reading the XML value designated by this
* SQLXML instance
*/
@
SuppressWarnings("unchecked")
protected <T extends
Source>T
createSAXSource(
Class<T>
sourceClass) throws
SQLException {
SAXSource source = null;
try {
source = (
sourceClass == null) ? new
SAXSource()
: (
SAXSource)
sourceClass.
newInstance();
} catch (
SecurityException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
InstantiationException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
IllegalAccessException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
ClassCastException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
}
Reader reader =
getCharacterStreamImpl();
InputSource inputSource = new
InputSource(
reader);
source.
setInputSource(
inputSource);
return (T)
source;
}
/**
* Retrieves a new StAXSource for reading the XML value designated by this
* SQLXML instance. <p>
*
* @param sourceClass The class of the source
* @throws java.sql.SQLException if there is an error processing the XML
* value or if the given <tt>sourceClass</tt> is not supported.
* @return a new StAXSource for reading the XML value designated by this
* SQLXML instance
*/
@
SuppressWarnings("unchecked")
protected <T extends
Source>T
createStAXSource(
Class<T>
sourceClass) throws
SQLException {
StAXSource source = null;
Constructor sourceCtor = null;
Reader reader = null;
XMLInputFactory factory = null;
XMLEventReader eventReader = null;
try {
factory =
XMLInputFactory.
newInstance();
} catch (
FactoryConfigurationError ex) {
throw
Exceptions.
sourceInstantiation(
ex);
}
try {
sourceCtor =
(
sourceClass == null)
?
StAXSource.class.
getConstructor(
XMLEventReader.class)
:
sourceClass.
getConstructor(
XMLEventReader.class);
} catch (
SecurityException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
NoSuchMethodException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
}
reader =
getCharacterStreamImpl();
try {
eventReader =
factory.
createXMLEventReader(
reader);
} catch (
XMLStreamException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
}
try {
source = (
StAXSource)
sourceCtor.
newInstance(
eventReader);
} catch (
SecurityException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
IllegalArgumentException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
IllegalAccessException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
InstantiationException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
} catch (
InvocationTargetException ex) {
throw
Exceptions.
sourceInstantiation(
ex.
getTargetException());
} catch (
ClassCastException ex) {
throw
Exceptions.
sourceInstantiation(
ex);
}
return (T)
source;
}
/**
* Retrieves a new Result for setting the XML value designated by this
* SQLXML instance.
*
* @param resultClass The class of the result, or null.
* @throws java.sql.SQLException if there is an error processing the XML
* value or the state is not writable
* @return for setting the XML value designated by this SQLXML instance.
*/
protected <T extends
Result>T
createResult(
Class<T>
resultClass) throws
SQLException {
checkWritable();
setWritable(false);
setReadable(true);
if (
JAXBResult.class.
isAssignableFrom(
resultClass)) {
// Must go first presently, since JAXBResult extends SAXResult
// (purely as an implementation detail) and it's not possible
// to instantiate a valid JAXBResult with a Zero-Args
// constructor(or any subclass thereof, due to the finality of
// its private UnmarshallerHandler)
// FALL THROUGH... will throw an exception
} else if ((
resultClass == null)
||
StreamResult.class.
isAssignableFrom(
resultClass)) {
return
createStreamResult(
resultClass);
} else if (
DOMResult.class.
isAssignableFrom(
resultClass)) {
return
createDOMResult(
resultClass);
} else if (
SAXResult.class.
isAssignableFrom(
resultClass)) {
return
createSAXResult(
resultClass);
} else if (
StAXResult.class.
isAssignableFrom(
resultClass)) {
return
createStAXResult(
resultClass);
}
throw
JDBCUtil.
invalidArgument("resultClass: " +
resultClass);
}
/**
* Retrieves a new StreamResult for setting the XML value designated by this
* SQLXML instance.
*
* @param resultClass The class of the result, or null.
* @throws java.sql.SQLException if there is an error processing the XML
* value
* @return for setting the XML value designated by this SQLXML instance.
*/
// @SuppressWarnings("unchecked")
protected <T extends
Result>T
createStreamResult(
Class<T>
resultClass) throws
SQLException {
StreamResult result = null;
try {
result = (
resultClass == null) ? new
StreamResult()
: (
StreamResult)
resultClass.
newInstance();
} catch (
SecurityException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
InstantiationException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
IllegalAccessException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
ClassCastException ex) {
throw
Exceptions.
resultInstantiation(
ex);
}
OutputStream stream =
setBinaryStreamImpl();
result.
setOutputStream(
stream);
return (T)
result;
}
/**
* Retrieves a new DOMResult for setting the XML value designated by this
* SQLXML instance.
*
* @param resultClass The class of the result, or null.
* @throws java.sql.SQLException if there is an error processing the XML
* value
* @return for setting the XML value designated by this SQLXML instance.
*/
@
SuppressWarnings("unchecked")
protected <T extends
Result>T
createDOMResult(
Class<T>
resultClass) throws
SQLException {
try {
T
result = (
resultClass == null) ? ((T) new
DOMResult())
:
resultClass.
newInstance();
this.
domResult = (
DOMResult)
result;
return
result;
} catch (
SecurityException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
InstantiationException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
IllegalAccessException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
ClassCastException ex) {
throw
Exceptions.
resultInstantiation(
ex);
}
}
/**
* Retrieves a new SAXResult for setting the XML value designated by this
* SQLXML instance.
*
* @param resultClass The class of the result, or null.
* @throws java.sql.SQLException if there is an error processing the XML
* value
* @return for setting the XML value designated by this SQLXML instance.
*/
@
SuppressWarnings("unchecked")
protected <T extends
Result>T
createSAXResult(
Class<T>
resultClass) throws
SQLException {
SAXResult result = null;
try {
result = (
resultClass == null) ? new
SAXResult()
: (
SAXResult)
resultClass.
newInstance();
} catch (
SecurityException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
InstantiationException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
IllegalAccessException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
ClassCastException ex) {
throw
Exceptions.
resultInstantiation(
ex);
}
SAX2DOMBuilder handler = null;
try {
handler = new
SAX2DOMBuilder();
} catch (
ParserConfigurationException ex) {
throw
Exceptions.
resultInstantiation(
ex);
}
this.
domResult = new
DOMResult();
result.
setHandler(
handler);
this.
domResult.
setNode(
handler.
getDocument());
return (T)
result;
}
/**
* Retrieves a new DOMResult for setting the XML value designated by this
* SQLXML instance.
*
* @param resultClass The class of the result, or null.
* @throws java.sql.SQLException if there is an error processing the XML
* value
* @return for setting the XML value designated by this SQLXML instance.
*/
@
SuppressWarnings("unchecked")
protected <T extends
Result>T
createStAXResult(
Class<T>
resultClass) throws
SQLException {
StAXResult result = null;
try {
this.
domResult =
new
DOMResult((new
SAX2DOMBuilder()).
getDocument());
XMLOutputFactory factory =
XMLOutputFactory.
newInstance();
XMLStreamWriter xmlStreamWriter =
factory.
createXMLStreamWriter(this.
domResult);
if (
resultClass == null ||
resultClass ==
StAXResult.class) {
result = new
StAXResult(
xmlStreamWriter);
} else {
Constructor ctor =
resultClass.
getConstructor(
XMLStreamWriter.class);
result = (
StAXResult)
ctor.
newInstance(
xmlStreamWriter);
}
} catch (
ParserConfigurationException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
SecurityException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
IllegalArgumentException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
IllegalAccessException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
InvocationTargetException ex) {
throw
Exceptions.
resultInstantiation(
ex.
getTargetException());
} catch (
FactoryConfigurationError ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
InstantiationException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
NoSuchMethodException ex) {
throw
Exceptions.
resultInstantiation(
ex);
} catch (
XMLStreamException ex) {
throw
Exceptions.
resultInstantiation(
ex);
}
return (T)
result;
}
protected void
freeDomResult() {
this.
domResult = null;
}
/**
* Basically just a namespace to isolate SQLXML exception generation
*/
protected static class
Exceptions {
/**
* Construction Disabled.
*/
private
Exceptions() {
}
/**
* Retrieves a new SQLXML DOM instantiation exception.
*
* @param cause of the exception
*/
static
SQLException domInstantiation(
Throwable cause) {
Exception ex = (
cause instanceof
Exception) ? (
Exception)
cause
: new
Exception(
cause);
return
JDBCUtil.
sqlException(
ErrorCode.
GENERAL_ERROR,
"SQLXML DOM instantiation failed: "
+
cause,
ex);
}
/**
* Retrieves a new SQLXML source instantiation exception.
*
* @param cause of the exception.
* @return a new SQLXML source instantiation exception
*/
static
SQLException sourceInstantiation(
Throwable cause) {
Exception ex = (
cause instanceof
Exception) ? (
Exception)
cause
: new
Exception(
cause);
return
JDBCUtil.
sqlException(
ErrorCode.
GENERAL_ERROR,
"SQLXML Source instantiation failed: "
+
cause,
ex);
}
/**
* Retrieves a new SQLXML result instantiation exception.
*
* @param cause of the exception.
* @return a new SQLXML result instantiation exception
*/
static
SQLException resultInstantiation(
Throwable cause) {
Exception ex = (
cause instanceof
Exception) ? (
Exception)
cause
: new
Exception(
cause);
return
JDBCUtil.
sqlException(
ErrorCode.
GENERAL_ERROR,
"SQLXML Result instantiation failed: "
+
cause,
ex);
}
/**
* Retrieves a new SQLXML parse failed exception.
*
* @param cause of the exception.
* @return a new SQLXML parse failed exception
*/
static
SQLException parseFailed(
Throwable cause) {
Exception ex = (
cause instanceof
Exception) ? (
Exception)
cause
: new
Exception(
cause);
return
JDBCUtil.
sqlException(
ErrorCode.
GENERAL_ERROR,
"parse failed: " +
cause,
ex);
}
/**
* Retrieves a new SQLXML transform failed exception.
*
* @param cause of the exception.
* @return a new SQLXML parse failed exception
*/
static
SQLException transformFailed(
Throwable cause) {
Exception ex = (
cause instanceof
Exception) ? (
Exception)
cause
: new
Exception(
cause);
return
JDBCUtil.
sqlException(
ErrorCode.
GENERAL_ERROR,
"transform failed: " +
cause,
ex);
}
/**
* Retrieves a new SQLXML not readable exception.
*
* @return a new SQLXML not readable exception
*/
static
SQLException notReadable() {
return
JDBCUtil.
sqlException(
ErrorCode.
GENERAL_IO_ERROR,
"SQLXML in not readable state");
}
/**
* Retrieves a new SQLXML not readable exception.
*
* @return a new SQLXML not readable exception
*/
static
SQLException notReadable(
String reason) {
return
JDBCUtil.
sqlException(
ErrorCode.
GENERAL_IO_ERROR,
"SQLXML in not readable state: "
+
reason);
}
/**
* Retrieves a new SQLXML not writable exception.
*
* @return a new SQLXML not writable exception
*/
static
SQLException notWritable() {
return
JDBCUtil.
sqlException(
ErrorCode.
GENERAL_IO_ERROR,
"SQLXML in not writable state");
}
/**
* Currently unused.
*
* @return never
*/
static
SQLException directUpdateByLocatorNotSupported() {
return
JDBCUtil.
sqlException(
ErrorCode.
X_0A000,
"SQLXML direct update by locator");
}
/**
* Retrieves a new SQLXML in freed state exception.
*
* @return a new SQLXML in freed state exception
*/
static
SQLException inFreedState() {
return
JDBCUtil.
sqlException(
ErrorCode.
GENERAL_ERROR,
"SQLXML in freed state");
}
}
// -------------------------------------------------------------------------
/**
* Builds a DOM from SAX events.
*/
protected static class
SAX2DOMBuilder implements
ContentHandler,
Closeable {
/**
*
*/
private boolean
closed;
/**
*
*/
private
Element currentElement;
// --------------------- internal implementation -----------------------
/**
*
*/
private
Node currentNode;
/**
*
*/
private
Document document;
/**
*
*/
private
Locator locator;
/**
* <p>Creates a new instance of SAX2DOMBuilder, which creates
* a new document. The document is available via
* {@link #getDocument()}.</p>
* @throws javax.xml.parsers.ParserConfigurationException
*/
public
SAX2DOMBuilder() throws
ParserConfigurationException {
DocumentBuilderFactory documentBuilderFactory;
DocumentBuilder documentBuilder;
documentBuilderFactory =
DocumentBuilderFactory.
newInstance();
documentBuilderFactory.
setValidating(false);
documentBuilderFactory.
setNamespaceAware(true);
documentBuilder =
documentBuilderFactory.
newDocumentBuilder();
this.
document =
documentBuilder.
newDocument();
this.
currentNode = this.
document;
}
/**
* Receive an object for locating the origin of SAX document events.
*
* <p>SAX parsers are strongly encouraged (though not absolutely
* required) to supply a locator: if it does so, it must supply
* the locator to the application by invoking this method before
* invoking any of the other methods in the ContentHandler
* interface.</p>
*
* <p>The locator allows the application to determine the end
* position of any document-related event, even if the parser is
* not reporting an error. Typically, the application will
* use this information for reporting its own errors (such as
* character content that does not match an application's
* business rules). The information returned by the locator
* is probably not sufficient for use with a search engine.</p>
*
* <p>Note that the locator will return correct information only
* during the invocation SAX event callbacks after
* {@link #startDocument startDocument} returns and before
* {@link #endDocument endDocument} is called. The
* application should not attempt to use it at any other time.</p>
*
* @param locator an object that can return the location of
* any SAX document event
* @see org.xml.sax.Locator
*/
public void
setDocumentLocator(
Locator locator) {
this.
locator =
locator;
}
/**
* Retrieves the Locator. <p>
* @return the Locator
*/
public
Locator getDocumentLocator() {
return this.
locator;
}
/**
* Receive notification of the beginning of a document.
*
* <p>The SAX parser will invoke this method only once, before any
* other event callbacks (except for {@link #setDocumentLocator
* setDocumentLocator}).</p>
*
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
* @see #endDocument
*/
public void
startDocument() throws
SAXException {
checkClosed();
}
/**
* Receive notification of the end of a document.
*
* <p><strong>There is an apparent contradiction between the
* documentation for this method and the documentation for {@link
* org.xml.sax.ErrorHandler#fatalError}. Until this ambiguity is
* resolved in a future major release, clients should make no
* assumptions about whether endDocument() will or will not be
* invoked when the parser has reported a fatalError() or thrown
* an exception.</strong></p>
*
* <p>The SAX parser will invoke this method only once, and it will
* be the last method invoked during the parse. The parser shall
* not invoke this method until it has either abandoned parsing
* (because of an unrecoverable error) or reached the end of
* input.</p>
*
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
* @see #startDocument
*/
public void
endDocument() throws
SAXException {
checkClosed();
close();
}
/**
* Begin the scope of a prefix-URI Namespace mapping.
*
* <p>The information from this event is not necessary for
* normal Namespace processing: the SAX XML reader will
* automatically replace prefixes for element and attribute
* names when the <code>http://xml.org/sax/features/namespaces</code>
* feature is <var>true</var> (the default).</p>
*
* <p>There are cases, however, when applications need to
* use prefixes in character data or in attribute values,
* where they cannot safely be expanded automatically; the
* start/endPrefixMapping event supplies the information
* to the application to expand prefixes in those contexts
* itself, if necessary.</p>
*
* <p>Note that start/endPrefixMapping events are not
* guaranteed to be properly nested relative to each other:
* all startPrefixMapping events will occur immediately before the
* corresponding {@link #startElement startElement} event,
* and all {@link #endPrefixMapping endPrefixMapping}
* events will occur immediately after the corresponding
* {@link #endElement endElement} event,
* but their order is not otherwise
* guaranteed.</p>
*
* <p>There should never be start/endPrefixMapping events for the
* "xml" prefix, since it is predeclared and immutable.</p>
*
* @param prefix the Namespace prefix being declared.
* An empty string is used for the default element namespace,
* which has no prefix.
* @param uri the Namespace URI the prefix is mapped to
* @throws org.xml.sax.SAXException the client may throw
* an exception during processing
* @see #endPrefixMapping
* @see #startElement
*/
public void
startPrefixMapping(
String prefix,
String uri) throws
SAXException {
checkClosed();
}
/**
* End the scope of a prefix-URI mapping.
*
* <p>See {@link #startPrefixMapping startPrefixMapping} for
* details. These events will always occur immediately after the
* corresponding {@link #endElement endElement} event, but the order of
* {@link #endPrefixMapping endPrefixMapping} events is not otherwise
* guaranteed.</p>
*
* @param prefix the prefix that was being mapped.
* This is the empty string when a default mapping scope ends.
* @throws org.xml.sax.SAXException the client may throw
* an exception during processing
* @see #startPrefixMapping
* @see #endElement
*/
public void
endPrefixMapping(
String prefix) throws
SAXException {
checkClosed();
}
/**
* Receive notification of the beginning of an element.
*
* <p>The Parser will invoke this method at the beginning of every
* element in the XML document; there will be a corresponding
* {@link #endElement endElement} event for every startElement event
* (even when the element is empty). All of the element's content will be
* reported, in order, before the corresponding endElement
* event.</p>
*
* <p>This event allows up to three name components for each
* element:</p>
*
* <ol>
* <li>the Namespace URI;</li>
* <li>the local name; and</li>
* <li>the qualified (prefixed) name.</li>
* </ol>
*
* <p>Any or all of these may be provided, depending on the
* values of the <var>http://xml.org/sax/features/namespaces</var>
* and the <var>http://xml.org/sax/features/namespace-prefixes</var>
* properties:</p>
*
* <ul>
* <li>the Namespace URI and local name are required when
* the namespaces property is <var>true</var> (the default), and are
* optional when the namespaces property is <var>false</var> (if one is
* specified, both must be);</li>
* <li>the qualified name is required when the namespace-prefixes property
* is <var>true</var>, and is optional when the namespace-prefixes property
* is <var>false</var> (the default).</li>
* </ul>
*
* <p>Note that the attribute list provided will contain only
* attributes with explicit values (specified or defaulted):
* #IMPLIED attributes will be omitted. The attribute list
* will contain attributes used for Namespace declarations
* (xmlns* attributes) only if the
* <code>http://xml.org/sax/features/namespace-prefixes</code>
* property is true (it is false by default, and support for a
* true value is optional).</p>
*
* <p>Like {@link #characters characters()}, attribute values may have
* characters that need more than one <code>char</code> value. </p>
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace
* processing is not being performed
* @param localName the local name (without prefix), or the
* empty string if Namespace processing is not being
* performed
* @param qName the qualified name (with prefix), or the
* empty string if qualified names are not available
* @param atts the attributes attached to the element. If
* there are no attributes, it shall be an empty
* Attributes object. The value of this object after
* startElement returns is undefined
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
* @see #endElement
* @see org.xml.sax.Attributes
* @see org.xml.sax.helpers.AttributesImpl
*/
public void
startElement(
String uri,
String localName,
String qName,
Attributes atts) throws
SAXException {
checkClosed();
Element element;
if ((
uri == null) || (
uri.
length() == 0)) {
element =
getDocument().
createElement(
qName);
} else {
element =
getDocument().
createElementNS(
uri,
qName);
}
if (
atts != null) {
for (int
i = 0;
i <
atts.
getLength();
i++) {
String attrURI =
atts.
getURI(
i);
String attrQName =
atts.
getQName(
i);
String attrValue =
atts.
getValue(
i);
if ((
attrURI == null) || (
attrURI.
length() == 0)) {
element.
setAttribute(
attrQName,
attrValue);
} else {
element.
setAttributeNS(
attrURI,
attrQName,
attrValue);
}
}
}
getCurrentNode().
appendChild(
element);
setCurrentNode(
element);
if (
getCurrentElement() == null) {
setCurrentElement(
element);
}
}
/**
* Receive notification of the end of an element.
*
* <p>The SAX parser will invoke this method at the end of every
* element in the XML document; there will be a corresponding
* {@link #startElement startElement} event for every endElement
* event (even when the element is empty).</p>
*
* <p>For information on the names, see startElement.</p>
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace
* processing is not being performed
* @param localName the local name (without prefix), or the
* empty string if Namespace processing is not being
* performed
* @param qName the qualified XML name (with prefix), or the
* empty string if qualified names are not available
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
*/
public void
endElement(
String uri,
String localName,
String qName) throws
SAXException {
checkClosed();
setCurrentNode(
getCurrentNode().
getParentNode());
}
/**
* Receive notification of character data.
*
* <p>The Parser will call this method to report each chunk of
* character data. SAX parsers may return all contiguous character
* data in a single chunk, or they may split it into several
* chunks; however, all of the characters in any single event
* must come from the same external entity so that the Locator
* provides useful information.</p>
*
* <p>The application must not attempt to read from the array
* outside of the specified range.</p>
*
* <p>Individual characters may consist of more than one Java
* <code>char</code> value. There are two important cases where this
* happens, because characters can't be represented in just sixteen bits.
* In one case, characters are represented in a <em>Surrogate Pair</em>,
* using two special Unicode values. Such characters are in the so-called
* "Astral Planes", with a code point above U+FFFF. A second case involves
* composite characters, such as a base character combining with one or
* more accent characters. </p>
*
* <p> Your code should not assume that algorithms using
* <code>char</code>-at-a-time idioms will be working in character
* units; in some cases they will split characters. This is relevant
* wherever XML permits arbitrary characters, such as attribute values,
* processing instruction data, and comments as well as in data reported
* from this method. It's also generally relevant whenever Java code
* manipulates internationalized text; the issue isn't unique to XML.</p>
*
* <p>Note that some parsers will report whitespace in element
* content using the {@link #ignorableWhitespace ignorableWhitespace}
* method rather than this one (validating parsers <em>must</em>
* do so).</p>
*
* @param ch the characters from the XML document
* @param start the start position in the array
* @param length the number of characters to read from the array
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
* @see #ignorableWhitespace
* @see org.xml.sax.Locator
*/
public void
characters(char[]
ch, int
start,
int
length) throws
SAXException {
checkClosed();
Node node =
getCurrentNode().
getLastChild();
String s = new
String(
ch,
start,
length);
if ((
node != null) && (
node.
getNodeType() ==
Node.
TEXT_NODE)) {
((
Text)
node).
appendData(
s);
} else {
Text text =
getDocument().
createTextNode(
s);
getCurrentNode().
appendChild(
text);
}
}
/**
* Receive notification of ignorable whitespace in element content.
*
* <p>Validating Parsers must use this method to report each chunk
* of whitespace in element content (see the W3C XML 1.0
* recommendation, section 2.10): non-validating parsers may also
* use this method if they are capable of parsing and using
* content models.</p>
*
* <p>SAX parsers may return all contiguous whitespace in a single
* chunk, or they may split it into several chunks; however, all of
* the characters in any single event must come from the same
* external entity, so that the Locator provides useful
* information.</p>
*
* <p>The application must not attempt to read from the array
* outside of the specified range.</p>
*
* @param ch the characters from the XML document
* @param start the start position in the array
* @param length the number of characters to read from the array
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
* @see #characters
*/
public void
ignorableWhitespace(char[]
ch, int
start,
int
length) throws
SAXException {
characters(
ch,
start,
length);
}
/**
* Receive notification of a processing instruction.
*
* <p>The Parser will invoke this method once for each processing
* instruction found: note that processing instructions may occur
* before or after the main document element.</p>
*
* <p>A SAX parser must never report an XML declaration (XML 1.0,
* section 2.8) or a text declaration (XML 1.0, section 4.3.1)
* using this method.</p>
*
* <p>Like {@link #characters characters()}, processing instruction
* data may have characters that need more than one <code>char</code>
* value. </p>
*
* @param target the processing instruction target
* @param data the processing instruction data, or null if
* none was supplied. The data does not include any
* whitespace separating it from the target
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
*/
public void
processingInstruction(
String target,
String data) throws
SAXException {
checkClosed();
ProcessingInstruction processingInstruction;
processingInstruction =
getDocument().
createProcessingInstruction(
target,
data);
getCurrentNode().
appendChild(
processingInstruction);
}
/**
* Receive notification of a skipped entity.
* This is not called for entity references within markup constructs
* such as element start tags or markup declarations. (The XML
* recommendation requires reporting skipped external entities.
* SAX also reports internal entity expansion/non-expansion, except
* within markup constructs.)
*
* <p>The Parser will invoke this method each time the entity is
* skipped. Non-validating processors may skip entities if they
* have not seen the declarations (because, for example, the
* entity was declared in an external DTD subset). All processors
* may skip external entities, depending on the values of the
* <code>http://xml.org/sax/features/external-general-entities</code>
* and the
* <code>http://xml.org/sax/features/external-parameter-entities</code>
* properties.</p>
*
* @param name the name of the skipped entity. If it is a
* parameter entity, the name will begin with '%', and if
* it is the external DTD subset, it will be the string
* "[dtd]"
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
*/
public void
skippedEntity(
String name) throws
SAXException {
checkClosed();
EntityReference entityReference =
getDocument().
createEntityReference(
name);
getCurrentNode().
appendChild(
entityReference);
}
/**
* Closes this DOMBuilder.
*/
public void
close() {
this.
closed = true;
}
/**
* Frees the DOMBuilder.
*/
public void
free() {
close();
this.
document = null;
this.
currentElement = null;
this.
currentNode = null;
this.
locator = null;
}
/**
* Retrieves whether this DOMBuilder is closed.
*
* @return boolean
*/
public boolean
isClosed() {
return this.
closed;
}
/**
* Checks whether this DOMBuilder is closed.
*
* @throws SAXException if this DOMBuilder is closed.
*/
protected void
checkClosed() throws
SAXException {
if (
isClosed()) {
throw new
SAXException("content handler is closed."); // NOI18N
}
}
/**
* Retrieves the document.
*
* @return Document
*/
public
Document getDocument() {
return this.
document;
}
/**
* Retrieves the current element. <p>
*/
protected
Element getCurrentElement() {
return this.
currentElement;
}
/**
* Assigns the current element.
* @param element
*/
protected void
setCurrentElement(
Element element) {
this.
currentElement =
element;
}
/**
* Retrieves the current node. <p>
*/
protected
Node getCurrentNode() {
return this.
currentNode;
}
/**
* Assigns the current node. <p>
* @param node
*/
protected void
setCurrentNode(
Node node) {
this.
currentNode =
node;
}
}
/**
* Writes to a {@link javax.xml.stream.XMLStreamWriter XMLStreamWriter}
* from SAX events.
*/
public static class
SAX2XMLStreamWriter implements
ContentHandler,
Closeable {
/**
* Namespace declarations for an upcoming element.
*/
private
List<
QualifiedName>
namespaces =
new
ArrayList<
QualifiedName>();
/**
* Whether this object is closed.
*/
private boolean
closed;
/**
* This object's SAX locator.
*/
private
Locator locator;
/**
* XML stream writer where events are pushed.
*/
private
XMLStreamWriter writer;
/**
* Constructs a new SAX2XMLStreamWriter that writes SAX events to the
* designated XMLStreamWriter.
*
* @param writer the writer to which to write SAX events
*/
public
SAX2XMLStreamWriter(
XMLStreamWriter writer) {
if (
writer == null) {
throw new
NullPointerException("writer");
}
this.
writer =
writer;
}
/**
* Receive notification of the beginning of a document.
*
* <p>The SAX parser will invoke this method only once, before any
* other event callbacks (except for {@link #setDocumentLocator
* setDocumentLocator}).</p>
*
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
* @see #endDocument
*/
public void
startDocument() throws
SAXException {
checkClosed();
try {
this.
writer.
writeStartDocument();
} catch (
XMLStreamException e) {
throw new
SAXException(
e);
}
}
/**
* Receive notification of the end of a document.
*
* <p><strong>There is an apparent contradiction between the
* documentation for this method and the documentation for {@link
* org.xml.sax.ErrorHandler#fatalError}. Until this ambiguity is
* resolved in a future major release, clients should make no
* assumptions about whether endDocument() will or will not be
* invoked when the parser has reported a fatalError() or thrown
* an exception.</strong></p>
*
* <p>The SAX parser will invoke this method only once, and it will
* be the last method invoked during the parse. The parser shall
* not invoke this method until it has either abandoned parsing
* (because of an unrecoverable error) or reached the end of
* input.</p>
*
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
* @see #startDocument
*/
public void
endDocument() throws
SAXException {
checkClosed();
try {
this.
writer.
writeEndDocument();
this.
writer.
flush();
} catch (
XMLStreamException e) {
throw new
SAXException(
e);
}
}
/**
* Receive notification of character data.
*
* <p>The Parser will call this method to report each chunk of
* character data. SAX parsers may return all contiguous character
* data in a single chunk, or they may split it into several
* chunks; however, all of the characters in any single event
* must come from the same external entity so that the Locator
* provides useful information.</p>
*
* <p>The application must not attempt to read from the array
* outside of the specified range.</p>
*
* <p>Individual characters may consist of more than one Java
* <code>char</code> value. There are two important cases where this
* happens, because characters can't be represented in just sixteen bits.
* In one case, characters are represented in a <em>Surrogate Pair</em>,
* using two special Unicode values. Such characters are in the so-called
* "Astral Planes", with a code point above U+FFFF. A second case involves
* composite characters, such as a base character combining with one or
* more accent characters. </p>
*
* <p> Your code should not assume that algorithms using
* <code>char</code>-at-a-time idioms will be working in character
* units; in some cases they will split characters. This is relevant
* wherever XML permits arbitrary characters, such as attribute values,
* processing instruction data, and comments as well as in data reported
* from this method. It's also generally relevant whenever Java code
* manipulates internationalized text; the issue isn't unique to XML.</p>
*
* <p>Note that some parsers will report whitespace in element
* content using the {@link #ignorableWhitespace ignorableWhitespace}
* method rather than this one (validating parsers <em>must</em>
* do so).</p>
*
* @param ch the characters from the XML document
* @param start the start position in the array
* @param length the number of characters to read from the array
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
* @see #ignorableWhitespace
* @see org.xml.sax.Locator
*/
public void
characters(char[]
ch, int
start,
int
length) throws
SAXException {
checkClosed();
try {
this.
writer.
writeCharacters(
ch,
start,
length);
} catch (
XMLStreamException e) {
throw new
SAXException(
e);
}
}
/**
* Receive notification of the beginning of an element.
*
* <p>The Parser will invoke this method at the beginning of every
* element in the XML document; there will be a corresponding
* {@link #endElement endElement} event for every startElement event
* (even when the element is empty). All of the element's content will be
* reported, in order, before the corresponding endElement
* event.</p>
*
* <p>This event allows up to three name components for each
* element:</p>
*
* <ol>
* <li>the Namespace URI;</li>
* <li>the local name; and</li>
* <li>the qualified (prefixed) name.</li>
* </ol>
*
* <p>Any or all of these may be provided, depending on the
* values of the <var>http://xml.org/sax/features/namespaces</var>
* and the <var>http://xml.org/sax/features/namespace-prefixes</var>
* properties:</p>
*
* <ul>
* <li>the Namespace URI and local name are required when
* the namespaces property is <var>true</var> (the default), and are
* optional when the namespaces property is <var>false</var> (if one is
* specified, both must be);</li>
* <li>the qualified name is required when the namespace-prefixes property
* is <var>true</var>, and is optional when the namespace-prefixes property
* is <var>false</var> (the default).</li>
* </ul>
*
* <p>Note that the attribute list provided will contain only
* attributes with explicit values (specified or defaulted):
* #IMPLIED attributes will be omitted. The attribute list
* will contain attributes used for Namespace declarations
* (xmlns* attributes) only if the
* <code>http://xml.org/sax/features/namespace-prefixes</code>
* property is true (it is false by default, and support for a
* true value is optional).</p>
*
* <p>Like {@link #characters characters()}, attribute values may have
* characters that need more than one <code>char</code> value. </p>
*
* @param namespaceURI the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace
* processing is not being performed
* @param localName the local name (without prefix), or the
* empty string if Namespace processing is not being
* performed
* @param qName the qualified name (with prefix), or the
* empty string if qualified names are not available
* @param atts the attributes attached to the element. If
* there are no attributes, it shall be an empty
* Attributes object. The value of this object after
* startElement returns is undefined
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
* @see #endElement
* @see org.xml.sax.Attributes
* @see org.xml.sax.helpers.AttributesImpl
*/
public void
startElement(
String namespaceURI,
String localName,
String qName,
Attributes atts) throws
SAXException {
checkClosed();
try {
int
qi =
qName.
indexOf(':');
String prefix = (
qi > 0) ?
qName.
substring(0,
qi)
: "";
this.
writer.
writeStartElement(
prefix,
localName,
namespaceURI);
int
length =
namespaces.
size();
for (int
i = 0;
i <
length;
i++) {
QualifiedName ns =
namespaces.
get(
i);
this.
writer.
writeNamespace(
ns.
prefix,
ns.
namespaceName);
}
namespaces.
clear();
length =
atts.
getLength();
for (int
i = 0;
i <
length;
i++) {
this.
writer.
writeAttribute(
atts.
getURI(
i),
atts.
getLocalName(
i),
atts.
getValue(
i));
}
} catch (
XMLStreamException e) {
throw new
SAXException(
e);
}
}
/**
* Receive notification of the end of an element.
*
* <p>The SAX parser will invoke this method at the end of every
* element in the XML document; there will be a corresponding
* {@link #startElement startElement} event for every endElement
* event (even when the element is empty).</p>
*
* <p>For information on the names, see startElement.</p>
*
* @param namespaceURI the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace
* processing is not being performed
* @param localName the local name (without prefix), or the
* empty string if Namespace processing is not being
* performed
* @param qName the qualified XML name (with prefix), or the
* empty string if qualified names are not available
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
*/
public void
endElement(
String namespaceURI,
String localName,
String qName) throws
SAXException {
checkClosed();
try {
this.
writer.
writeEndElement();
} catch (
XMLStreamException e) {
throw new
SAXException(
e);
}
}
/**
* Begin the scope of a prefix-URI Namespace mapping.
*
* <p>The information from this event is not necessary for
* normal Namespace processing: the SAX XML reader will
* automatically replace prefixes for element and attribute
* names when the <code>http://xml.org/sax/features/namespaces</code>
* feature is <var>true</var> (the default).</p>
*
* <p>There are cases, however, when applications need to
* use prefixes in character data or in attribute values,
* where they cannot safely be expanded automatically; the
* start/endPrefixMapping event supplies the information
* to the application to expand prefixes in those contexts
* itself, if necessary.</p>
*
* <p>Note that start/endPrefixMapping events are not
* guaranteed to be properly nested relative to each other:
* all startPrefixMapping events will occur immediately before the
* corresponding {@link #startElement startElement} event,
* and all {@link #endPrefixMapping endPrefixMapping}
* events will occur immediately after the corresponding
* {@link #endElement endElement} event,
* but their order is not otherwise
* guaranteed.</p>
*
* <p>There should never be start/endPrefixMapping events for the
* "xml" prefix, since it is predeclared and immutable.</p>
*
* @param prefix the Namespace prefix being declared.
* An empty string is used for the default element namespace,
* which has no prefix.
* @param uri the Namespace URI the prefix is mapped to
* @throws org.xml.sax.SAXException the client may throw
* an exception during processing
* @see #endPrefixMapping
* @see #startElement
*/
public void
startPrefixMapping(
String prefix,
String uri) throws
SAXException {
checkClosed();
try {
this.
writer.
setPrefix(
prefix,
uri);
namespaces.
add(new
QualifiedName(
prefix,
uri));
} catch (
XMLStreamException e) {
throw new
SAXException(
e);
}
}
/**
* End the scope of a prefix-URI mapping.
*
* <p>See {@link #startPrefixMapping startPrefixMapping} for
* details. These events will always occur immediately after the
* corresponding {@link #endElement endElement} event, but the order of
* {@link #endPrefixMapping endPrefixMapping} events is not otherwise
* guaranteed.</p>
*
* @param prefix the prefix that was being mapped.
* This is the empty string when a default mapping scope ends.
* @throws org.xml.sax.SAXException the client may throw
* an exception during processing
* @see #startPrefixMapping
* @see #endElement
*/
public void
endPrefixMapping(
String prefix) throws
SAXException {
checkClosed();
//
}
/**
* Receive notification of ignorable whitespace in element content.
*
* <p>Validating Parsers must use this method to report each chunk
* of whitespace in element content (see the W3C XML 1.0
* recommendation, section 2.10): non-validating parsers may also
* use this method if they are capable of parsing and using
* content models.</p>
*
* <p>SAX parsers may return all contiguous whitespace in a single
* chunk, or they may split it into several chunks; however, all of
* the characters in any single event must come from the same
* external entity, so that the Locator provides useful
* information.</p>
*
* <p>The application must not attempt to read from the array
* outside of the specified range.</p>
*
* @param ch the characters from the XML document
* @param start the start position in the array
* @param length the number of characters to read from the array
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
* @see #characters
*/
public void
ignorableWhitespace(char[]
ch, int
start,
int
length) throws
SAXException {
characters(
ch,
start,
length);
}
/**
* Receive notification of a processing instruction.
*
* <p>The Parser will invoke this method once for each processing
* instruction found: note that processing instructions may occur
* before or after the main document element.</p>
*
* <p>A SAX parser must never report an XML declaration (XML 1.0,
* section 2.8) or a text declaration (XML 1.0, section 4.3.1)
* using this method.</p>
*
* <p>Like {@link #characters characters()}, processing instruction
* data may have characters that need more than one <code>char</code>
* value. </p>
*
* @param target the processing instruction target
* @param data the processing instruction data, or null if
* none was supplied. The data does not include any
* whitespace separating it from the target
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
*/
public void
processingInstruction(
String target,
String data) throws
SAXException {
checkClosed();
try {
this.
writer.
writeProcessingInstruction(
target,
data);
} catch (
XMLStreamException e) {
throw new
SAXException(
e);
}
}
/**
* Receive an object for locating the origin of SAX document events.
*
* <p>SAX parsers are strongly encouraged (though not absolutely
* required) to supply a locator: if it does so, it must supply
* the locator to the application by invoking this method before
* invoking any of the other methods in the ContentHandler
* interface.</p>
*
* <p>The locator allows the application to determine the end
* position of any document-related event, even if the parser is
* not reporting an error. Typically, the application will
* use this information for reporting its own errors (such as
* character content that does not match an application's
* business rules). The information returned by the locator
* is probably not sufficient for use with a search engine.</p>
*
* <p>Note that the locator will return correct information only
* during the invocation SAX event callbacks after
* {@link #startDocument startDocument} returns and before
* {@link #endDocument endDocument} is called. The
* application should not attempt to use it at any other time.</p>
*
* @param locator an object that can return the location of
* any SAX document event
* @see org.xml.sax.Locator
*/
public void
setDocumentLocator(
Locator locator) {
this.
locator =
locator;
}
/**
* Retrieves the Locator. <p>
* @return the Locator
*/
public
Locator getDocumentLocator() {
return this.
locator;
}
/**
* Receive notification of a skipped entity.
* This is not called for entity references within markup constructs
* such as element start tags or markup declarations. (The XML
* recommendation requires reporting skipped external entities.
* SAX also reports internal entity expansion/non-expansion, except
* within markup constructs.)
*
* <p>The Parser will invoke this method each time the entity is
* skipped. Non-validating processors may skip entities if they
* have not seen the declarations (because, for example, the
* entity was declared in an external DTD subset). All processors
* may skip external entities, depending on the values of the
* <code>http://xml.org/sax/features/external-general-entities</code>
* and the
* <code>http://xml.org/sax/features/external-parameter-entities</code>
* properties.</p>
*
* @param name the name of the skipped entity. If it is a
* parameter entity, the name will begin with '%', and if
* it is the external DTD subset, it will be the string
* "[dtd]"
* @throws org.xml.sax.SAXException any SAX exception, possibly
* wrapping another exception
*/
public void
skippedEntity(
String name) throws
SAXException {
checkClosed();
//
}
public void
comment(char[]
ch, int
start,
int
length) throws
SAXException {
checkClosed();
try {
this.
writer.
writeComment(new
String(
ch,
start,
length));
} catch (
XMLStreamException e) {
throw new
SAXException(
e);
}
}
public
XMLStreamWriter getWriter() {
return this.
writer;
}
protected
List<
QualifiedName>
getNamespaces() {
return this.
namespaces;
}
/**
* Closes this object.
*/
public void
close() throws
IOException {
if (!this.
closed) {
this.
closed = true;
try {
this.
writer.
close();
} catch (
XMLStreamException e) {
throw new
IOException(
e);
} finally {
this.
writer = null;
this.
locator = null;
this.
namespaces = null;
}
}
}
/**
* Retrieves whether this object is closed.
*
* @return boolean
*/
public boolean
isClosed() {
return this.
closed;
}
/**
* Checks whether this object is closed.
*
* @throws SAXException if this DOMBuilder is closed.
*/
protected void
checkClosed() throws
SAXException {
if (
isClosed()) {
throw new
SAXException("content handler is closed."); // NOI18N
}
}
// --------------------- internal implementation -----------------------
protected static class
QualifiedName {
public final
String namespaceName;
public final
String prefix;
public
QualifiedName(final
String prefix,
final
String namespaceName) {
this.
prefix =
prefix;
this.
namespaceName =
namespaceName;
}
}
}
}