/*
* Copyright (c) 2008 Mozilla Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package nu.validator.htmlparser.dom;
import org.w3c.dom.DocumentType;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
public class Dom2Sax {
private static String emptyIfNull(String namespaceURI) {
return namespaceURI == null ? "" : namespaceURI;
}
private final NamedNodeMapAttributes attributes = new NamedNodeMapAttributes();
private final ContentHandler contentHandler;
private final LexicalHandler lexicalHandler;
/**
* @param contentHandler
* @param lexicalHandler
*/
public Dom2Sax(ContentHandler contentHandler, LexicalHandler lexicalHandler) {
if (contentHandler == null) {
throw new IllegalArgumentException("ContentHandler must not be null.");
}
this.contentHandler = contentHandler;
this.lexicalHandler = lexicalHandler;
}
public void parse(Node node) throws SAXException {
Node current = node;
Node next;
char[] buf;
for (;;) {
switch (current.getNodeType()) {
case Node.ELEMENT_NODE:
attributes.setNamedNodeMap(current.getAttributes());
// To work around severe bogosity in the default DOM
// impl, use the node name if local name is null.
String localName = current.getLocalName();
contentHandler.startElement(
emptyIfNull(current.getNamespaceURI()),
localName == null ? current.getNodeName()
: localName, null, attributes);
attributes.clear();
break;
case Node.TEXT_NODE:
buf = current.getNodeValue().toCharArray();
contentHandler.characters(buf, 0, buf.length);
break;
case Node.CDATA_SECTION_NODE:
if (lexicalHandler != null) {
lexicalHandler.startCDATA();
}
buf = current.getNodeValue().toCharArray();
contentHandler.characters(buf, 0, buf.length);
if (lexicalHandler != null) {
lexicalHandler.endCDATA();
}
break;
case Node.COMMENT_NODE:
if (lexicalHandler != null) {
buf = current.getNodeValue().toCharArray();
lexicalHandler.comment(buf, 0, buf.length);
}
break;
case Node.DOCUMENT_NODE:
contentHandler.startDocument();
break;
case Node.DOCUMENT_TYPE_NODE:
if (lexicalHandler != null) {
DocumentType doctype = (DocumentType) current;
lexicalHandler.startDTD(doctype.getName(),
doctype.getPublicId(), doctype.getSystemId());
lexicalHandler.endDTD();
}
break;
case Node.PROCESSING_INSTRUCTION_NODE:
contentHandler.processingInstruction(current.getNodeName(), current.getNodeValue());
break;
case Node.ENTITY_REFERENCE_NODE:
contentHandler.skippedEntity(current.getNodeName());
break;
}
if ((next = current.getFirstChild()) != null) {
current = next;
continue;
}
for (;;) {
switch (current.getNodeType()) {
case Node.ELEMENT_NODE:
// To work around severe bogosity in the default DOM
// impl, use the node name if local name is null.
String localName = current.getLocalName();
contentHandler.endElement(
emptyIfNull(current.getNamespaceURI()),
localName == null ? current.getNodeName()
: localName, null);
break;
case Node.DOCUMENT_NODE:
contentHandler.endDocument();
break;
}
if (current == node) {
return;
}
if ((next = current.getNextSibling()) != null) {
current = next;
break;
}
current = current.getParentNode();
}
}
}
private class NamedNodeMapAttributes implements Attributes {
private NamedNodeMap map;
private int length;
public void setNamedNodeMap(NamedNodeMap attributes) {
this.map = attributes;
this.length = attributes.getLength();
}
public void clear() {
this.map = null;
}
public int getIndex(String qName) {
for (int i = 0; i < length; i++) {
Node n = map.item(i);
if (n.getNodeName().equals(qName)) {
return i;
}
}
return -1;
}
public int getIndex(String uri, String localName) {
for (int i = 0; i < length; i++) {
Node n = map.item(i);
if (n.getLocalName().equals(localName) && emptyIfNull(n.getNamespaceURI()).equals(uri)) {
return i;
}
}
return -1;
}
public int getLength() {
return length;
}
public String getLocalName(int index) {
if (index < length && index >= 0) {
return map.item(index).getLocalName();
} else {
return null;
}
}
public String getQName(int index) {
if (index < length && index >= 0) {
return map.item(index).getNodeName();
} else {
return null;
}
}
public String getType(int index) {
if (index < length && index >= 0) {
return "id".equals(map.item(index).getLocalName()) ? "ID" : "CDATA";
} else {
return null;
}
}
public String getType(String qName) {
int index = getIndex(qName);
if (index == -1) {
return null;
} else {
return getType(index);
}
}
public String getType(String uri, String localName) {
int index = getIndex(uri, localName);
if (index == -1) {
return null;
} else {
return getType(index);
}
}
public String getURI(int index) {
if (index < length && index >= 0) {
return emptyIfNull(map.item(index).getNamespaceURI());
} else {
return null;
}
}
public String getValue(int index) {
if (index < length && index >= 0) {
return map.item(index).getNodeValue();
} else {
return null;
}
}
public String getValue(String qName) {
int index = getIndex(qName);
if (index == -1) {
return null;
} else {
return getValue(index);
}
}
public String getValue(String uri, String localName) {
int index = getIndex(uri, localName);
if (index == -1) {
return null;
} else {
return getValue(index);
}
}
}
}