/*
* eXist Open Source Native XML Database
* Copyright (C) 2003-2007 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Id$
*/
package org.exist.xmldb;
import org.apache.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException;
import org.exist.Namespaces;
import org.exist.dom.DocumentTypeImpl;
import org.exist.util.MimeType;
import org.exist.util.serializer.DOMSerializer;
import org.exist.util.serializer.SAXSerializer;
import org.exist.util.VirtualTempFile;
import org.exist.xquery.value.StringValue;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
import org.xmldb.api.base.ErrorCodes;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.XMLResource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.TransformerException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class RemoteXMLResource
extends AbstractRemoteResource
implements XMLResource
{
private final static Properties emptyProperties = new Properties();
/**
* Use external XMLReader to parse XML.
*/
private XMLReader xmlReader = null;
protected String id;
protected int handle = -1;
protected int pos = -1;
private String content = null;
protected Properties outputProperties = null;
protected LexicalHandler lexicalHandler = null;
private static Logger LOG = Logger.getLogger(RemoteXMLResource.class.getName());
public RemoteXMLResource(RemoteCollection parent, XmldbURI docId, String id)
throws XMLDBException {
this(parent, -1, -1, docId, id);
}
public RemoteXMLResource(
RemoteCollection parent,
int handle,
int pos,
XmldbURI docId,
String id)
throws XMLDBException {
super(parent,docId);
this.handle = handle;
this.pos = pos;
this.id = id;
this.mimeType=MimeType.XML_TYPE.getName();
}
public Object getContent() throws XMLDBException {
if (content != null) {
return new StringValue(content).getStringValue(true);
}
Object res=super.getContent();
if(res!=null) {
if(res instanceof byte[]) {
try {
return new String((byte[])res,"UTF-8");
} catch(UnsupportedEncodingException uee) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, uee.getMessage(), uee);
}
} else {
return res;
}
}
return null;
// Backward compatible code (perhaps it is not needed?)
/*
if (properties.getProperty(EXistOutputKeys.COMPRESS_OUTPUT, "no").equals("yes")) {
try {
data = Compressor.uncompress(data);
} catch (IOException e) {
}
}
try {
content = new String(data, properties.getProperty(OutputKeys.ENCODING, "UTF-8"));
// fixme! - this should probably be earlier in the chain before serialisation. /ljo
content = new StringValue(content).getStringValue(true);
} catch (UnsupportedEncodingException ue) {
LOG.warn(ue);
content = new String(data);
content = new StringValue(content).getStringValue(true);
}
return content;
*/
}
public Node getContentAsDOM()
throws XMLDBException
{
InputSource is=null;
InputStream cis=null;
if(content!=null) {
is=new InputSource(new StringReader(content));
} else {
cis = getStreamContent();
is=new InputSource(cis);
}
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(is);
// <frederic.glorieux@ajlsm.com> return a full DOM doc, with root PI and comments
return doc;
} catch (SAXException saxe) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, saxe.getMessage(), saxe);
} catch (ParserConfigurationException pce) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, pce.getMessage(), pce);
} catch(IOException ioe) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ioe.getMessage(), ioe);
} finally {
if(cis != null) {
try {
cis.close();
} catch(IOException ioe) {
// IgnoreIT(R)
}
}
}
}
public void getContentAsSAX(ContentHandler handler)
throws XMLDBException
{
InputSource is=null;
InputStream cis = null;
if(content!=null) {
is=new InputSource(new StringReader(content));
} else {
cis = getStreamContent();
is=new InputSource(cis);
}
XMLReader reader = xmlReader;
if (reader == null) {
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
saxFactory.setNamespaceAware(true);
saxFactory.setValidating(false);
try {
SAXParser sax = saxFactory.newSAXParser();
reader = sax.getXMLReader();
} catch (ParserConfigurationException pce) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, pce.getMessage(), pce);
} catch (SAXException saxe) {
saxe.printStackTrace();
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, saxe.getMessage(), saxe);
}
}
try {
reader.setContentHandler(handler);
if(lexicalHandler != null) {
reader.setProperty(Namespaces.SAX_LEXICAL_HANDLER, lexicalHandler);
}
reader.parse(is);
} catch (SAXException saxe) {
saxe.printStackTrace();
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, saxe.getMessage(), saxe);
} catch (IOException ioe) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ioe.getMessage(), ioe);
} finally {
if(cis!=null) {
try {
cis.close();
} catch(IOException ioe) {
// IgnoreIT(R)
}
}
}
}
public String getNodeId() {
return id == null ? "1" : id;
}
public String getDocumentId() throws XMLDBException {
return path.lastSegment().toString();
}
public String getId() throws XMLDBException {
if (id == null || id.equals("1"))
return getDocumentId();
return getDocumentId() + '_' + id;
}
public String getResourceType() throws XMLDBException {
return "XMLResource";
}
/**
* Sets the external XMLReader to use.
*
* @param xmlReader the XMLReader
*/
public void setXMLReader(XMLReader xmlReader) {
this.xmlReader = xmlReader;
}
public void setContent(Object value) throws XMLDBException {
content = null;
if(!super.setContentInternal(value)) {
if(value instanceof String) {
content = new String((String)value);
} else if(value instanceof byte[]) {
try {
content = new String((byte[])value,"UTF-8");
} catch(UnsupportedEncodingException uee) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, uee.getMessage(), uee);
}
} else {
content = value.toString();
}
}
}
public void setContentAsDOM(Node root) throws XMLDBException {
try {
VirtualTempFile vtmpfile = new VirtualTempFile();
vtmpfile.setTempPrefix("eXistRXR");
vtmpfile.setTempPostfix(".xml");
OutputStreamWriter osw=new OutputStreamWriter(vtmpfile,"UTF-8");
DOMSerializer xmlout = new DOMSerializer(osw, getProperties());
try {
switch (root.getNodeType()) {
case Node.ELEMENT_NODE :
xmlout.serialize((Element) root);
break;
case Node.DOCUMENT_FRAGMENT_NODE :
xmlout.serialize((DocumentFragment) root);
break;
case Node.DOCUMENT_NODE :
xmlout.serialize((Document) root);
break;
default :
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, "invalid node type");
}
} catch (TransformerException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage(), e);
} finally {
try {
osw.close();
} catch(IOException ioe) {
// IgnoreIT(R)
}
try {
vtmpfile.close();
} catch(IOException ioe) {
// IgnoreIT(R)
}
}
setContent(vtmpfile);
} catch(IOException ioe) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ioe.getMessage(), ioe);
}
}
public ContentHandler setContentAsSAX()
throws XMLDBException
{
freeResources();
content = null;
return new InternalXMLSerializer();
}
private class InternalXMLSerializer
extends SAXSerializer
{
VirtualTempFile vtmpfile = null;
OutputStreamWriter writer = null;
public InternalXMLSerializer() {
super();
}
public void startDocument() throws SAXException {
try {
vtmpfile = new VirtualTempFile();
vtmpfile.setTempPrefix("eXistRXR");
vtmpfile.setTempPostfix(".xml");
writer=new OutputStreamWriter(vtmpfile,"UTF-8");
setOutput(writer, emptyProperties);
} catch(IOException ioe) {
throw new SAXException("Unable to create temp file for serialization data",ioe);
}
super.startDocument();
}
/**
* @see org.xml.sax.DocumentHandler#endDocument()
*/
public void endDocument()
throws SAXException
{
super.endDocument();
try {
if (writer != null)
writer.close();
} catch (IOException e) {
throw new SAXException("Unable to close temp file containing serialized data",e);
}
try {
if (vtmpfile != null)
vtmpfile.close();
} catch (IOException e) {
throw new SAXException("Unable to close temp file containing serialized data",e);
}
try {
setContent(vtmpfile);
} catch(XMLDBException xe) {
throw new SAXException("Unable to set file content containing serialized data",xe);
}
}
}
/* (non-Javadoc)
* @see org.xmldb.api.modules.XMLResource#getSAXFeature(java.lang.String)
*/
public boolean getSAXFeature(String arg0)
throws SAXNotRecognizedException, SAXNotSupportedException {
return false;
}
/* (non-Javadoc)
* @see org.xmldb.api.modules.XMLResource#setSAXFeature(java.lang.String, boolean)
*/
public void setSAXFeature(String arg0, boolean arg1)
throws SAXNotRecognizedException, SAXNotSupportedException {
}
public void setLexicalHandler(LexicalHandler handler) {
lexicalHandler = handler;
}
protected void setProperties(Properties properties) {
this.outputProperties = properties;
}
protected Properties getProperties() {
return outputProperties == null ? parent.properties : outputProperties;
}
public DocumentType getDocType() throws XMLDBException {
DocumentType result = null;
List params = new ArrayList(1);
Object[] request = null;
params.add(path.toString());
try {
request = (Object[]) parent.getClient().execute("getDocType", params);
if (!request[0].equals("")) {
result = new DocumentTypeImpl((String)request[0],(String)request[1],(String)request[2]);
}
return result;
} catch (XmlRpcException e) {
throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e.getMessage(), e);
}
}
public void setDocType(DocumentType doctype) throws XMLDBException {
if (doctype != null ) {
List params = new ArrayList(4);
params.add(path.toString());
params.add(doctype.getName());
params.add(doctype.getPublicId() == null ? "" : doctype.getPublicId());
params.add(doctype.getSystemId() == null ? "" : doctype.getSystemId());
try {
parent.getClient().execute("setDocType", params);
} catch (XmlRpcException e) {
throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e.getMessage(), e);
}
}
}
public void getContentIntoAStream(OutputStream os)
throws XMLDBException
{
getContentIntoAStreamInternal(os,content,id!=null,handle,pos);
}
public Object getExtendedContent()
throws XMLDBException
{
return getExtendedContentInternal(content,id!=null,handle,pos);
}
public InputStream getStreamContent() throws XMLDBException {
return getStreamContentInternal(content,id!=null,handle,pos);
}
public long getStreamLength()
throws XMLDBException
{
return getStreamLengthInternal(content);
}
}