/*
* Copyright (c) 2007 Henri Sivonen
* Copyright (c) 2008-2010 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 nu.validator.htmlparser.common.DocumentMode;
import nu.validator.htmlparser.impl.CoalescingTreeBuilder;
import nu.validator.htmlparser.impl.HtmlAttributes;
import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
/**
* The tree builder glue for building a tree through the public DOM APIs.
*
* @version $Id$
* @author hsivonen
*/
class DOMTreeBuilder extends CoalescingTreeBuilder<Element> {
/**
* The DOM impl.
*/
private DOMImplementation implementation;
/**
* The current doc.
*/
private Document document;
/**
* The constructor.
*
* @param implementation
* the DOM impl.
*/
protected DOMTreeBuilder(DOMImplementation implementation) {
super();
this.implementation = implementation;
}
/**
*
* @see nu.validator.htmlparser.impl.TreeBuilder#addAttributesToElement(java.lang.Object,
* nu.validator.htmlparser.impl.HtmlAttributes)
*/
@Override protected void addAttributesToElement(Element element,
HtmlAttributes attributes) throws SAXException {
try {
for (int i = 0; i < attributes.getLength(); i++) {
String localName = attributes.getLocalNameNoBoundsCheck(i);
String uri = attributes.getURINoBoundsCheck(i);
if (!element.hasAttributeNS(uri, localName)) {
element.setAttributeNS(uri, localName,
attributes.getValueNoBoundsCheck(i));
}
}
} catch (DOMException e) {
fatal(e);
}
}
/**
*
* @see nu.validator.htmlparser.impl.CoalescingTreeBuilder#appendCharacters(java.lang.Object,
* java.lang.String)
*/
@Override protected void appendCharacters(Element parent, String text)
throws SAXException {
try {
Node lastChild = parent.getLastChild();
if (lastChild != null && lastChild.getNodeType() == Node.TEXT_NODE) {
Text lastAsText = (Text) lastChild;
lastAsText.setData(lastAsText.getData() + text);
return;
}
parent.appendChild(document.createTextNode(text));
} catch (DOMException e) {
fatal(e);
}
}
/**
*
* @see nu.validator.htmlparser.impl.TreeBuilder#appendChildrenToNewParent(java.lang.Object,
* java.lang.Object)
*/
@Override protected void appendChildrenToNewParent(Element oldParent,
Element newParent) throws SAXException {
try {
while (oldParent.hasChildNodes()) {
newParent.appendChild(oldParent.getFirstChild());
}
} catch (DOMException e) {
fatal(e);
}
}
/**
*
* @see nu.validator.htmlparser.impl.CoalescingTreeBuilder#appendComment(java.lang.Object,
* java.lang.String)
*/
@Override protected void appendComment(Element parent, String comment)
throws SAXException {
try {
parent.appendChild(document.createComment(comment));
} catch (DOMException e) {
fatal(e);
}
}
/**
*
* @see nu.validator.htmlparser.impl.CoalescingTreeBuilder#appendCommentToDocument(java.lang.String)
*/
@Override protected void appendCommentToDocument(String comment)
throws SAXException {
try {
document.appendChild(document.createComment(comment));
} catch (DOMException e) {
fatal(e);
}
}
/**
*
* @see nu.validator.htmlparser.impl.TreeBuilder#createElement(String, String, nu.validator.htmlparser.impl.HtmlAttributes, Object)
*/
@Override protected Element createElement(String ns, String name,
HtmlAttributes attributes, Element intendedParent) throws SAXException {
try {
Element rv = document.createElementNS(ns, name);
for (int i = 0; i < attributes.getLength(); i++) {
rv.setAttributeNS(attributes.getURINoBoundsCheck(i),
attributes.getLocalNameNoBoundsCheck(i),
attributes.getValueNoBoundsCheck(i));
if (attributes.getTypeNoBoundsCheck(i) == "ID") {
rv.setIdAttributeNS(null, attributes.getLocalName(i), true);
}
}
return rv;
} catch (DOMException e) {
fatal(e);
throw new RuntimeException("Unreachable");
}
}
/**
*
* @see nu.validator.htmlparser.impl.TreeBuilder#createHtmlElementSetAsRoot(nu.validator.htmlparser.impl.HtmlAttributes)
*/
@Override protected Element createHtmlElementSetAsRoot(
HtmlAttributes attributes) throws SAXException {
try {
Element rv = document.createElementNS(
"http://www.w3.org/1999/xhtml", "html");
for (int i = 0; i < attributes.getLength(); i++) {
rv.setAttributeNS(attributes.getURINoBoundsCheck(i),
attributes.getLocalNameNoBoundsCheck(i),
attributes.getValueNoBoundsCheck(i));
}
document.appendChild(rv);
return rv;
} catch (DOMException e) {
fatal(e);
throw new RuntimeException("Unreachable");
}
}
/**
*
* @see nu.validator.htmlparser.impl.TreeBuilder#appendElement(java.lang.Object,
* java.lang.Object)
*/
@Override protected void appendElement(Element child, Element newParent)
throws SAXException {
try {
newParent.appendChild(child);
} catch (DOMException e) {
fatal(e);
}
}
/**
*
* @see nu.validator.htmlparser.impl.TreeBuilder#hasChildren(java.lang.Object)
*/
@Override protected boolean hasChildren(Element element)
throws SAXException {
try {
return element.hasChildNodes();
} catch (DOMException e) {
fatal(e);
throw new RuntimeException("Unreachable");
}
}
/**
* @see nu.validator.htmlparser.impl.TreeBuilder#createElement(String,
* java.lang.String, org.xml.sax.Attributes, java.lang.Object)
*/
@Override protected Element createElement(String ns, String name,
HtmlAttributes attributes, Element form, Element intendedParent) throws SAXException {
try {
Element rv = createElement(ns, name, attributes, intendedParent);
rv.setUserData("nu.validator.form-pointer", form, null);
return rv;
} catch (DOMException e) {
fatal(e);
return null;
}
}
/**
* @see nu.validator.htmlparser.impl.TreeBuilder#start()
*/
@Override protected void start(boolean fragment) throws SAXException {
document = implementation.createDocument(null, null, null);
}
/**
*
* @see nu.validator.htmlparser.impl.TreeBuilder#documentMode(nu.validator.htmlparser.common.DocumentMode,
* java.lang.String, java.lang.String, boolean)
*/
protected void documentMode(DocumentMode mode, String publicIdentifier,
String systemIdentifier, boolean html4SpecificAdditionalErrorChecks)
throws SAXException {
document.setUserData("nu.validator.document-mode", mode, null);
}
/**
* Returns the document.
*
* @return the document
*/
Document getDocument() {
Document rv = document;
document = null;
return rv;
}
/**
* Return the document fragment.
*
* @return the document fragment
*/
DocumentFragment getDocumentFragment() {
DocumentFragment rv = document.createDocumentFragment();
Node rootElt = document.getFirstChild();
while (rootElt.hasChildNodes()) {
rv.appendChild(rootElt.getFirstChild());
}
document = null;
return rv;
}
@Override
protected Element createAndInsertFosterParentedElement(String ns, String name,
HtmlAttributes attributes, Element table, Element stackParent) throws SAXException {
try {
Node parent = table.getParentNode();
Element child = createElement(ns, name, attributes, parent != null ? (Element) parent : stackParent);
if (parent != null) { // always an element if not null
parent.insertBefore(child, table);
} else {
stackParent.appendChild(child);
}
return child;
} catch (DOMException e) {
fatal(e);
throw new RuntimeException("Unreachable");
}
}
@Override protected void insertFosterParentedCharacters(String text,
Element table, Element stackParent) throws SAXException {
try {
Node parent = table.getParentNode();
if (parent != null) { // always an element if not null
Node previousSibling = table.getPreviousSibling();
if (previousSibling != null
&& previousSibling.getNodeType() == Node.TEXT_NODE) {
Text lastAsText = (Text) previousSibling;
lastAsText.setData(lastAsText.getData() + text);
return;
}
parent.insertBefore(document.createTextNode(text), table);
return;
}
Node lastChild = stackParent.getLastChild();
if (lastChild != null && lastChild.getNodeType() == Node.TEXT_NODE) {
Text lastAsText = (Text) lastChild;
lastAsText.setData(lastAsText.getData() + text);
return;
}
stackParent.appendChild(document.createTextNode(text));
} catch (DOMException e) {
fatal(e);
}
}
@Override protected void insertFosterParentedChild(Element child,
Element table, Element stackParent) throws SAXException {
try {
Node parent = table.getParentNode();
if (parent != null) { // always an element if not null
parent.insertBefore(child, table);
} else {
stackParent.appendChild(child);
}
} catch (DOMException e) {
fatal(e);
}
}
@Override protected void detachFromParent(Element element)
throws SAXException {
try {
Node parent = element.getParentNode();
if (parent != null) {
parent.removeChild(element);
}
} catch (DOMException e) {
fatal(e);
}
}
}