/*
* Copyright (c) 2004, PostgreSQL Global Development Group
* See the LICENSE file in the project root for more information.
*/
package org.postgresql.jdbc;
import org.postgresql.core.
BaseConnection;
import org.postgresql.util.
GT;
import org.postgresql.util.
PSQLException;
import org.postgresql.util.
PSQLState;
import org.xml.sax.
ErrorHandler;
import org.xml.sax.
InputSource;
import org.xml.sax.
SAXParseException;
import java.io.
ByteArrayInputStream;
import java.io.
ByteArrayOutputStream;
import java.io.
IOException;
import java.io.
InputStream;
import java.io.
OutputStream;
import java.io.
Reader;
import java.io.
StringReader;
import java.io.
StringWriter;
import java.io.
Writer;
import java.sql.
SQLException;
import java.sql.
SQLXML;
import javax.xml.parsers.
DocumentBuilder;
import javax.xml.parsers.
DocumentBuilderFactory;
import javax.xml.stream.
XMLInputFactory;
import javax.xml.stream.
XMLOutputFactory;
import javax.xml.stream.
XMLStreamException;
import javax.xml.stream.
XMLStreamReader;
import javax.xml.stream.
XMLStreamWriter;
import javax.xml.transform.
Result;
import javax.xml.transform.
Source;
import javax.xml.transform.
Transformer;
import javax.xml.transform.
TransformerException;
import javax.xml.transform.
TransformerFactory;
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.sax.
SAXTransformerFactory;
import javax.xml.transform.sax.
TransformerHandler;
import javax.xml.transform.stax.
StAXResult;
import javax.xml.transform.stax.
StAXSource;
import javax.xml.transform.stream.
StreamResult;
import javax.xml.transform.stream.
StreamSource;
public class
PgSQLXML implements
SQLXML {
private final
BaseConnection _conn;
private
String _data; // The actual data contained.
private boolean
_initialized; // Has someone assigned the data for this object?
private boolean
_active; // Is anyone in the process of loading data into us?
private boolean
_freed;
private
ByteArrayOutputStream _byteArrayOutputStream;
private
StringWriter _stringWriter;
private
DOMResult _domResult;
public
PgSQLXML(
BaseConnection conn) {
this(
conn, null, false);
}
public
PgSQLXML(
BaseConnection conn,
String data) {
this(
conn,
data, true);
}
private
PgSQLXML(
BaseConnection conn,
String data, boolean
initialized) {
_conn =
conn;
_data =
data;
_initialized =
initialized;
_active = false;
_freed = false;
}
public synchronized void
free() {
_freed = true;
_data = null;
}
public synchronized
InputStream getBinaryStream() throws
SQLException {
checkFreed();
ensureInitialized();
if (
_data == null) {
return null;
}
try {
return new
ByteArrayInputStream(
_conn.
getEncoding().
encode(
_data));
} catch (
IOException ioe) {
// This should be a can't happen exception. We just
// decoded this data, so it would be surprising that
// we couldn't encode it.
// For this reason don't make it translatable.
throw new
PSQLException("Failed to re-encode xml data.",
PSQLState.
DATA_ERROR,
ioe);
}
}
public synchronized
Reader getCharacterStream() throws
SQLException {
checkFreed();
ensureInitialized();
if (
_data == null) {
return null;
}
return new
StringReader(
_data);
}
// We must implement this unsafely because that's what the
// interface requires. Because it says we're returning T
// which is unknown, none of the return values can satisfy it
// as Java isn't going to understand the if statements that
// ensure they are the same.
//
public synchronized <T extends
Source> T
getSource(
Class<T>
sourceClass) throws
SQLException {
checkFreed();
ensureInitialized();
if (
_data == null) {
return null;
}
try {
if (
sourceClass == null ||
DOMSource.class.
equals(
sourceClass)) {
DocumentBuilderFactory factory =
DocumentBuilderFactory.
newInstance();
DocumentBuilder builder =
factory.
newDocumentBuilder();
builder.
setErrorHandler(new
NonPrintingErrorHandler());
InputSource input = new
InputSource(new
StringReader(
_data));
return (T) new
DOMSource(
builder.
parse(
input));
} else if (
SAXSource.class.
equals(
sourceClass)) {
InputSource is = new
InputSource(new
StringReader(
_data));
return (T) new
SAXSource(
is);
} else if (
StreamSource.class.
equals(
sourceClass)) {
return (T) new
StreamSource(new
StringReader(
_data));
} else if (
StAXSource.class.
equals(
sourceClass)) {
XMLInputFactory xif =
XMLInputFactory.
newInstance();
XMLStreamReader xsr =
xif.
createXMLStreamReader(new
StringReader(
_data));
return (T) new
StAXSource(
xsr);
}
} catch (
Exception e) {
throw new
PSQLException(
GT.
tr("Unable to decode xml data."),
PSQLState.
DATA_ERROR,
e);
}
throw new
PSQLException(
GT.
tr("Unknown XML Source class: {0}",
sourceClass),
PSQLState.
INVALID_PARAMETER_TYPE);
}
public synchronized
String getString() throws
SQLException {
checkFreed();
ensureInitialized();
return
_data;
}
public synchronized
OutputStream setBinaryStream() throws
SQLException {
checkFreed();
initialize();
_active = true;
_byteArrayOutputStream = new
ByteArrayOutputStream();
return
_byteArrayOutputStream;
}
public synchronized
Writer setCharacterStream() throws
SQLException {
checkFreed();
initialize();
_stringWriter = new
StringWriter();
return
_stringWriter;
}
public synchronized <T extends
Result> T
setResult(
Class<T>
resultClass) throws
SQLException {
checkFreed();
initialize();
if (
resultClass == null ||
DOMResult.class.
equals(
resultClass)) {
_domResult = new
DOMResult();
_active = true;
return (T)
_domResult;
} else if (
SAXResult.class.
equals(
resultClass)) {
try {
SAXTransformerFactory transformerFactory =
(
SAXTransformerFactory)
SAXTransformerFactory.
newInstance();
TransformerHandler transformerHandler =
transformerFactory.
newTransformerHandler();
_stringWriter = new
StringWriter();
transformerHandler.
setResult(new
StreamResult(
_stringWriter));
_active = true;
return (T) new
SAXResult(
transformerHandler);
} catch (
TransformerException te) {
throw new
PSQLException(
GT.
tr("Unable to create SAXResult for SQLXML."),
PSQLState.
UNEXPECTED_ERROR,
te);
}
} else if (
StreamResult.class.
equals(
resultClass)) {
_stringWriter = new
StringWriter();
_active = true;
return (T) new
StreamResult(
_stringWriter);
} else if (
StAXResult.class.
equals(
resultClass)) {
_stringWriter = new
StringWriter();
try {
XMLOutputFactory xof =
XMLOutputFactory.
newInstance();
XMLStreamWriter xsw =
xof.
createXMLStreamWriter(
_stringWriter);
_active = true;
return (T) new
StAXResult(
xsw);
} catch (
XMLStreamException xse) {
throw new
PSQLException(
GT.
tr("Unable to create StAXResult for SQLXML"),
PSQLState.
UNEXPECTED_ERROR,
xse);
}
}
throw new
PSQLException(
GT.
tr("Unknown XML Result class: {0}",
resultClass),
PSQLState.
INVALID_PARAMETER_TYPE);
}
public synchronized void
setString(
String value) throws
SQLException {
checkFreed();
initialize();
_data =
value;
}
private void
checkFreed() throws
SQLException {
if (
_freed) {
throw new
PSQLException(
GT.
tr("This SQLXML object has already been freed."),
PSQLState.
OBJECT_NOT_IN_STATE);
}
}
private void
ensureInitialized() throws
SQLException {
if (!
_initialized) {
throw new
PSQLException(
GT.
tr(
"This SQLXML object has not been initialized, so you cannot retrieve data from it."),
PSQLState.
OBJECT_NOT_IN_STATE);
}
// Is anyone loading data into us at the moment?
if (!
_active) {
return;
}
if (
_byteArrayOutputStream != null) {
try {
_data =
_conn.
getEncoding().
decode(
_byteArrayOutputStream.
toByteArray());
} catch (
IOException ioe) {
throw new
PSQLException(
GT.
tr("Failed to convert binary xml data to encoding: {0}.",
_conn.
getEncoding().
name()),
PSQLState.
DATA_ERROR,
ioe);
} finally {
_byteArrayOutputStream = null;
_active = false;
}
} else if (
_stringWriter != null) {
// This is also handling the work for Stream, SAX, and StAX Results
// as they will use the same underlying stringwriter variable.
//
_data =
_stringWriter.
toString();
_stringWriter = null;
_active = false;
} else if (
_domResult != null) {
// Copy the content from the result to a source
// and use the identify transform to get it into a
// friendlier result format.
try {
TransformerFactory factory =
TransformerFactory.
newInstance();
Transformer transformer =
factory.
newTransformer();
DOMSource domSource = new
DOMSource(
_domResult.
getNode());
StringWriter stringWriter = new
StringWriter();
StreamResult streamResult = new
StreamResult(
stringWriter);
transformer.
transform(
domSource,
streamResult);
_data =
stringWriter.
toString();
} catch (
TransformerException te) {
throw new
PSQLException(
GT.
tr("Unable to convert DOMResult SQLXML data to a string."),
PSQLState.
DATA_ERROR,
te);
} finally {
_domResult = null;
_active = false;
}
}
}
private void
initialize() throws
SQLException {
if (
_initialized) {
throw new
PSQLException(
GT.
tr(
"This SQLXML object has already been initialized, so you cannot manipulate it further."),
PSQLState.
OBJECT_NOT_IN_STATE);
}
_initialized = true;
}
// Don't clutter System.err with errors the user can't silence.
// If something bad really happens an exception will be thrown.
static class
NonPrintingErrorHandler implements
ErrorHandler {
public void
error(
SAXParseException e) {
}
public void
fatalError(
SAXParseException e) {
}
public void
warning(
SAXParseException e) {
}
}
}