/* eXist Native XML Database
* Copyright (C) 2000-03, Wolfgang M. Meier (wolfgang@exist-db.org)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Id$
*/
package org.exist.util.serializer;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerException;
import org.exist.memtree.NodeImpl;
import org.exist.util.serializer.json.JSONWriter;
import org.w3c.dom.Attr;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Comment;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import org.xml.sax.helpers.NamespaceSupport;
public class DOMSerializer {
private final static Properties defaultProperties = new Properties();
static {
defaultProperties.setProperty(OutputKeys.ENCODING, "UTF-8");
defaultProperties.setProperty(OutputKeys.INDENT, "false");
}
private final static int XML_WRITER = 0;
private final static int XHTML_WRITER = 1;
private final static int TEXT_WRITER = 2;
private final static int JSON_WRITER = 3;
private XMLWriter writers[] = {
new IndentingXMLWriter(),
new XHTMLWriter(),
new TEXTWriter(),
new JSONWriter()
};
protected XMLWriter receiver;
protected NamespaceSupport nsSupport = new NamespaceSupport();
protected HashMap<String, String> namespaceDecls = new HashMap<String, String>();
protected Properties outputProperties;
public DOMSerializer() {
super();
this.receiver = writers[XML_WRITER];
}
public DOMSerializer(Writer writer, Properties outputProperties) {
super();
setOutput(writer, outputProperties);
}
public void setOutput(Writer writer, Properties properties) {
if (properties == null)
outputProperties = defaultProperties;
else
outputProperties = properties;
String method = outputProperties.getProperty("method", "xml");
if ("xhtml".equalsIgnoreCase(method))
receiver = writers[XHTML_WRITER];
else if("text".equalsIgnoreCase(method))
receiver = writers[TEXT_WRITER];
else if ("json".equalsIgnoreCase(method))
receiver = writers[JSON_WRITER];
else
receiver = writers[XML_WRITER];
receiver.setWriter(writer);
receiver.setOutputProperties(outputProperties);
}
public void setWriter(Writer writer) {
receiver.setWriter(writer);
}
public void reset() {
nsSupport.reset();
namespaceDecls.clear();
for (int i = 0; i < writers.length; i++)
writers[i].reset();
}
public void serialize(Node node) throws TransformerException {
receiver.startDocument();
Node top = node;
while (node != null) {
startNode(node);
Node nextNode = node.getNodeType() == NodeImpl.REFERENCE_NODE ? null : node.getFirstChild();
while (nextNode == null) {
endNode(node);
if (top != null && top.equals(node))
break;
nextNode = node.getNextSibling();
if (nextNode == null) {
node = node.getParentNode();
if (node == null || (top != null && top.equals(node))) {
endNode(node);
//nextNode = null;
break;
}
}
}
node = nextNode;
}
receiver.endDocument();
}
protected void startNode(Node node) throws TransformerException {
switch (node.getNodeType()) {
case Node.DOCUMENT_NODE :
case Node.DOCUMENT_FRAGMENT_NODE :
break;
case Node.ELEMENT_NODE :
namespaceDecls.clear();
nsSupport.pushContext();
receiver.startElement(node.getNodeName());
String uri = node.getNamespaceURI();
String prefix = node.getPrefix();
if (uri == null)
uri = "";
if (prefix == null)
prefix = "";
if (nsSupport.getURI(prefix) == null) {
namespaceDecls.put(prefix, uri);
nsSupport.declarePrefix(prefix, uri);
}
// check attributes for required namespace declarations
NamedNodeMap attrs = node.getAttributes();
Attr nextAttr;
String attrName;
for (int i = 0; i < attrs.getLength(); i++) {
nextAttr = (Attr) attrs.item(i);
attrName = nextAttr.getName();
if (attrName.equals("xmlns")) {
String oldURI = nsSupport.getURI("");
uri = nextAttr.getValue();
if (oldURI == null || (!oldURI.equals(uri))) {
namespaceDecls.put("", uri);
nsSupport.declarePrefix("", uri);
}
} else if (attrName.startsWith("xmlns:")) {
prefix = attrName.substring(6);
if (nsSupport.getURI(prefix) == null) {
uri = nextAttr.getValue();
namespaceDecls.put(prefix, uri);
nsSupport.declarePrefix(prefix, uri);
}
} else if (attrName.indexOf(':') > 0) {
prefix = nextAttr.getPrefix();
uri = nextAttr.getNamespaceURI();
if (prefix == null){
prefix = attrName.split(":")[0];
}
if (nsSupport.getURI(prefix) == null) {
namespaceDecls.put(prefix, uri);
nsSupport.declarePrefix(prefix, uri);
}
}
}
// output all namespace declarations
for (Map.Entry<String, String> nsEntry : namespaceDecls.entrySet()) {
receiver.namespace( nsEntry.getKey(), nsEntry.getValue());
}
// output attributes
String name;
for (int i = 0; i < attrs.getLength(); i++) {
nextAttr = (Attr) attrs.item(i);
name = nextAttr.getName();
if(name.startsWith("xmlns"))
continue;
receiver.attribute(nextAttr.getName(), nextAttr.getValue());
}
break;
case Node.TEXT_NODE :
case Node.CDATA_SECTION_NODE :
receiver.characters(((CharacterData) node).getData());
break;
case Node.ATTRIBUTE_NODE :
break;
case Node.PROCESSING_INSTRUCTION_NODE :
receiver.processingInstruction(
((ProcessingInstruction) node).getTarget(),
((ProcessingInstruction) node).getData());
break;
case Node.COMMENT_NODE :
receiver.comment(((Comment) node).getData());
break;
default :
//TODO : what kind of defaut here ?!! -pb
break;
}
}
protected void endNode(Node node) throws TransformerException {
if (node == null)
return;
if (node.getNodeType() == Node.ELEMENT_NODE) {
nsSupport.popContext();
receiver.endElement(node.getNodeName());
}
}
}