org.vectomatic.dom.svg.utils.DOMHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.vectomatic.dom.svg.utils.DOMHelper.java

Source

/**********************************************
 * Copyright (C) 2010 Lukas Laag
 * This file is part of lib-gwt-svg.
 * 
 * libgwtsvg 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 3 of the License, or
 * (at your option) any later version.
 * 
 * libgwtsvg 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 libgwtsvg.  If not, see http://www.gnu.org/licenses/
 **********************************************/
/*
 * Copyright (c) 2004 World Wide Web Consortium,
 *
 * (Massachusetts Institute of Technology, European Research Consortium for
 * Informatics and Mathematics, Keio University). All Rights Reserved. This
 * work is distributed under the W3C(r) Software License [1] 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.
 *
 * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
 */
package org.vectomatic.dom.svg.utils;

import java.util.Iterator;
import java.util.NoSuchElementException;

import org.vectomatic.dom.svg.OMElement;
import org.vectomatic.dom.svg.OMNode;
import org.vectomatic.dom.svg.OMSVGElement;
import org.vectomatic.dom.svg.impl.Attr;
import org.vectomatic.dom.svg.impl.DOMHelperImpl;
import org.vectomatic.dom.svg.impl.NamedNodeMap;
import org.w3c.dom.DOMException;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Text;
import com.google.gwt.event.dom.client.LoseCaptureHandler;
import com.google.gwt.event.shared.HandlerRegistration;

/**
 * Class to group miscellaneous DOM level 2 methods. These
 * methods are missing from the GWT DOM overlay types (such as 
 * {@link com.google.gwt.dom.client.Element} or {@link com.google.gwt.dom.client.Node})
 * but exist in almost all browsers and are sometimes required
 * to develop an SVG application.
 * @author laaglu
 */
public class DOMHelper {
    private static final DOMHelperImpl impl = GWT.create(DOMHelperImpl.class);

    /**
     * Creates an element of the given qualified name and namespace URI.
     * <br>Per [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>]
     * , applications must use the value <code>null</code> as the 
     * namespaceURI parameter for methods if they wish to have no namespace.
     * @param document The document in which the element is to be created.
     * @param namespaceURI The namespace URI of the element to create.
     * @param qualifiedName The qualified name of the element type to 
     *   instantiate.
     * @return A new <code>Element</code> object with the following 
     *   attributes:
     * <table border='1' cellpadding='3'>
     * <tr>
     * <th>Attribute</th>
     * <th>Value</th>
     * </tr>
     * <tr>
     * <td valign='top' rowspan='1' colspan='1'><code>Node.nodeName</code></td>
     * <td valign='top' rowspan='1' colspan='1'>
     *   <code>qualifiedName</code></td>
     * </tr>
     * <tr>
     * <td valign='top' rowspan='1' colspan='1'><code>Node.namespaceURI</code></td>
     * <td valign='top' rowspan='1' colspan='1'>
     *   <code>namespaceURI</code></td>
     * </tr>
     * <tr>
     * <td valign='top' rowspan='1' colspan='1'><code>Node.prefix</code></td>
     * <td valign='top' rowspan='1' colspan='1'>prefix, extracted 
     *   from <code>qualifiedName</code>, or <code>null</code> if there is 
     *   no prefix</td>
     * </tr>
     * <tr>
     * <td valign='top' rowspan='1' colspan='1'><code>Node.localName</code></td>
     * <td valign='top' rowspan='1' colspan='1'>local name, extracted from 
     *   <code>qualifiedName</code></td>
     * </tr>
     * <tr>
     * <td valign='top' rowspan='1' colspan='1'><code>Element.tagName</code></td>
     * <td valign='top' rowspan='1' colspan='1'>
     *   <code>qualifiedName</code></td>
     * </tr>
     * </table>
     * @exception DOMException
     *   INVALID_CHARACTER_ERR: Raised if the specified 
     *   <code>qualifiedName</code> is not an XML name according to the XML 
     *   version in use specified in the <code>Document.xmlVersion</code> 
     *   attribute.
     *   <br>NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is a 
     *   malformed qualified name, if the <code>qualifiedName</code> has a 
     *   prefix and the <code>namespaceURI</code> is <code>null</code>, or 
     *   if the <code>qualifiedName</code> has a prefix that is "xml" and 
     *   the <code>namespaceURI</code> is different from "<a href='http://www.w3.org/XML/1998/namespace'>
     *   http://www.w3.org/XML/1998/namespace</a>" [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>]
     *   , or if the <code>qualifiedName</code> or its prefix is "xmlns" and 
     *   the <code>namespaceURI</code> is different from "<a href='http://www.w3.org/2000/xmlns/'>http://www.w3.org/2000/xmlns/</a>", or if the <code>namespaceURI</code> is "<a href='http://www.w3.org/2000/xmlns/'>http://www.w3.org/2000/xmlns/</a>" and neither the <code>qualifiedName</code> nor its prefix is "xmlns".
     *   <br>NOT_SUPPORTED_ERR: Always thrown if the current document does not 
     *   support the <code>"XML"</code> feature, since namespaces were 
     *   defined by XML.
     */
    public static final native Element createElementNS(Document document, String namespaceURI, String qualifiedName)
            throws JavaScriptException /*-{
                                       return document.createElementNS(namespaceURI, qualifiedName);
                                       }-*/;

    /**
     * Creates a new empty SVG document
     * @return
     * a new empty SVG document
     */
    public static final native Document createDocument(String uri, String qname) /*-{
                                                                                 return $doc.implementation.createDocument(uri, qname, null);
                                                                                 }-*/;

    /**
     * Imports a node from another document to this document, without altering 
     * or removing the source node from the original document; this method 
     * creates a new copy of the source node. The returned node has no 
     * parent; (<code>parentNode</code> is <code>null</code>).
     * <br>For all nodes, importing a node creates a node object owned by the 
     * importing document, with attribute values identical to the source 
     * node's <code>nodeName</code> and <code>nodeType</code>, plus the 
     * attributes related to namespaces (<code>prefix</code>, 
     * <code>localName</code>, and <code>namespaceURI</code>). As in the 
     * <code>cloneNode</code> operation, the source node is not altered. 
     * User data associated to the imported node is not carried over. 
     * However, if any <code>UserDataHandlers</code> has been specified 
     * along with the associated data these handlers will be called with the 
     * appropriate parameters before this method returns.
     * <br>Additional information is copied as appropriate to the 
     * <code>nodeType</code>, attempting to mirror the behavior expected if 
     * a fragment of XML or HTML source was copied from one document to 
     * another, recognizing that the two documents may have different DTDs 
     * in the XML case. The following list describes the specifics for each 
     * type of node. 
     * <dl>
     * <dt>ATTRIBUTE_NODE</dt>
     * <dd>The <code>ownerElement</code> attribute 
     * is set to <code>null</code> and the <code>specified</code> flag is 
     * set to <code>true</code> on the generated <code>Attr</code>. The 
     * descendants of the source <code>Attr</code> are recursively imported 
     * and the resulting nodes reassembled to form the corresponding subtree.
     * Note that the <code>deep</code> parameter has no effect on 
     * <code>Attr</code> nodes; they always carry their children with them 
     * when imported.</dd>
     * <dt>DOCUMENT_FRAGMENT_NODE</dt>
     * <dd>If the <code>deep</code> option 
     * was set to <code>true</code>, the descendants of the source 
     * <code>DocumentFragment</code> are recursively imported and the 
     * resulting nodes reassembled under the imported 
     * <code>DocumentFragment</code> to form the corresponding subtree. 
     * Otherwise, this simply generates an empty 
     * <code>DocumentFragment</code>.</dd>
     * <dt>DOCUMENT_NODE</dt>
     * <dd><code>Document</code> 
     * nodes cannot be imported.</dd>
     * <dt>DOCUMENT_TYPE_NODE</dt>
     * <dd><code>DocumentType</code> 
     * nodes cannot be imported.</dd>
     * <dt>ELEMENT_NODE</dt>
     * <dd><em>Specified</em> attribute nodes of the source element are imported, and the generated 
     * <code>Attr</code> nodes are attached to the generated 
     * <code>Element</code>. Default attributes are <em>not</em> copied, though if the document being imported into defines default 
     * attributes for this element name, those are assigned. If the 
     * <code>importNode</code> <code>deep</code> parameter was set to 
     * <code>true</code>, the descendants of the source element are 
     * recursively imported and the resulting nodes reassembled to form the 
     * corresponding subtree.</dd>
     * <dt>ENTITY_NODE</dt>
     * <dd><code>Entity</code> nodes can be 
     * imported, however in the current release of the DOM the 
     * <code>DocumentType</code> is readonly. Ability to add these imported 
     * nodes to a <code>DocumentType</code> will be considered for addition 
     * to a future release of the DOM.On import, the <code>publicId</code>, 
     * <code>systemId</code>, and <code>notationName</code> attributes are 
     * copied. If a <code>deep</code> import is requested, the descendants 
     * of the the source <code>Entity</code> are recursively imported and 
     * the resulting nodes reassembled to form the corresponding subtree.</dd>
     * <dt>
     * ENTITY_REFERENCE_NODE</dt>
     * <dd>Only the <code>EntityReference</code> itself is 
     * copied, even if a <code>deep</code> import is requested, since the 
     * source and destination documents might have defined the entity 
     * differently. If the document being imported into provides a 
     * definition for this entity name, its value is assigned.</dd>
     * <dt>NOTATION_NODE</dt>
     * <dd>
     * <code>Notation</code> nodes can be imported, however in the current 
     * release of the DOM the <code>DocumentType</code> is readonly. Ability 
     * to add these imported nodes to a <code>DocumentType</code> will be 
     * considered for addition to a future release of the DOM.On import, the 
     * <code>publicId</code> and <code>systemId</code> attributes are copied.
     * Note that the <code>deep</code> parameter has no effect on this type 
     * of nodes since they cannot have any children.</dd>
     * <dt>
     * PROCESSING_INSTRUCTION_NODE</dt>
     * <dd>The imported node copies its 
     * <code>target</code> and <code>data</code> values from those of the 
     * source node.Note that the <code>deep</code> parameter has no effect 
     * on this type of nodes since they cannot have any children.</dd>
     * <dt>TEXT_NODE, 
     * CDATA_SECTION_NODE, COMMENT_NODE</dt>
     * <dd>These three types of nodes inheriting 
     * from <code>CharacterData</code> copy their <code>data</code> and 
     * <code>length</code> attributes from those of the source node.Note 
     * that the <code>deep</code> parameter has no effect on these types of 
     * nodes since they cannot have any children.</dd>
     * </dl> 
     * @param document The document in which to import the node.
     * @param importedNode The node to import.
     * @param deep If <code>true</code>, recursively import the subtree under 
     *   the specified node; if <code>false</code>, import only the node 
     *   itself, as explained above. This has no effect on nodes that cannot 
     *   have any children, and on <code>Attr</code>, and 
     *   <code>EntityReference</code> nodes.
     * @return The imported node that belongs to this <code>Document</code>.
     * @exception DOMException
     *   NOT_SUPPORTED_ERR: Raised if the type of node being imported is not 
     *   supported.
     *   <br>INVALID_CHARACTER_ERR: Raised if one of the imported names is not 
     *   an XML name according to the XML version in use specified in the 
     *   <code>Document.xmlVersion</code> attribute. This may happen when 
     *   importing an XML 1.1 [<a href='http://www.w3.org/TR/2004/REC-xml11-20040204/'>XML 1.1</a>] element 
     *   into an XML 1.0 document, for instance.
     */
    public static final native Node importNode(Document document, Node importedNode, boolean deep) /*-{
                                                                                                   return document.importNode(importedNode, deep);
                                                                                                   }-*/;

    /**
     *  Puts all <code>Text</code> nodes in the full depth of the sub-tree 
     * underneath this <code>Node</code>, including attribute nodes, into a 
     * "normal" form where only structure (e.g., elements, comments, 
     * processing instructions, CDATA sections, and entity references) 
     * separates <code>Text</code> nodes, i.e., there are neither adjacent 
     * <code>Text</code> nodes nor empty <code>Text</code> nodes. This can 
     * be used to ensure that the DOM view of a document is the same as if 
     * it were saved and re-loaded, and is useful when operations (such as 
     * XPointer [<a href='http://www.w3.org/TR/2003/REC-xptr-framework-20030325/'>XPointer</a>]
     *  lookups) that depend on a particular document tree structure are to 
     * be used. If the parameter "normalize-characters" of the 
     * <code>DOMConfiguration</code> object attached to the 
     * <code>Node.ownerDocument</code> is <code>true</code>, this method 
     * will also fully normalize the characters of the <code>Text</code> 
     * nodes. 
     * <p ><b>Note:</b> In cases where the document contains 
     * <code>CDATASections</code>, the normalize operation alone may not be 
     * sufficient, since XPointers do not differentiate between 
     * <code>Text</code> nodes and <code>CDATASection</code> nodes.
     * @param node the node to normalize
     */
    public static final native void normalize(Node node) /*-{
                                                         return node.normalize();
                                                         }-*/;

    /**
     * Extracts a range of data from the node.
     * @param text the text node
     * @param offset Start offset of substring to extract.
     * @param count The number of 16-bit units to extract.
     * @return The specified substring. If the sum of <code>offset</code> and 
     *   <code>count</code> exceeds the <code>length</code>, then all 16-bit 
     *   units to the end of the data are returned.
     * @exception DOMException
     *   INDEX_SIZE_ERR: Raised if the specified <code>offset</code> is 
     *   negative or greater than the number of 16-bit units in 
     *   <code>data</code>, or if the specified <code>count</code> is 
     *   negative.
     *   <br>DOMSTRING_SIZE_ERR: Raised if the specified range of text does 
     *   not fit into a <code>String</code>.
     */
    public static final native String substringData(Text text, int offset, int count)
            throws JavaScriptException /*-{
                                       return text.substringData(offset, count);
                                       }-*/;

    /**
     * Append the string to the end of the character data of the node. Upon 
     * success, <code>data</code> provides access to the concatenation of 
     * <code>data</code> and the <code>String</code> specified.
     * @param text the text node
     * @param arg The <code>String</code> to append.
     * @exception DOMException
     *   NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     */
    public static final native void appendData(Text text, String arg) throws JavaScriptException /*-{
                                                                                                 text.appendData(offset, arg);
                                                                                                 }-*/;

    /**
     * Retrieves an attribute value by local name and namespace URI on
     * the specified element.
     * <br>Per [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>]
     * , applications must use the value <code>null</code> as the 
     * <code>namespaceURI</code> parameter for methods if they wish to have 
     * no namespace.
     * @param elem the element
     * @param namespaceURI The namespace URI of the attribute to retrieve.
     * @param localName The local name of the attribute to retrieve.
     * @return The <code>Attr</code> value as a string, or the empty string 
     *   if that attribute does not have a specified or default value.
     * @exception DOMException
     *   NOT_SUPPORTED_ERR: May be raised if the implementation does not 
     *   support the feature <code>"XML"</code> and the language exposed 
     *   through the Document does not support XML Namespaces (such as [<a href='http://www.w3.org/TR/1999/REC-html401-19991224/'>HTML 4.01</a>]). 
     * @since DOM Level 2
     */
    public static final native String getAttributeNS(Element elem, String namespaceURI, String localName)
            throws JavaScriptException /*-{
                                       return elem.getAttributeNS(namespaceURI, localName);
                                       }-*/;

    /**
     * Returns a <code>NodeList</code> of all the descendant 
     * <code>Elements</code> of the specified element
     * with a given local name and namespace URI in 
     * document order.
     * @param elem The element
     * @param namespaceURI The namespace URI of the elements to match on. The 
     *   special value "*" matches all namespaces.
     * @param localName The local name of the elements to match on. The 
     *   special value "*" matches all local names.
     * @return A new <code>NodeList</code> object containing all the matched 
     *   <code>Elements</code>.
     * @exception DOMException
     *   NOT_SUPPORTED_ERR: May be raised if the implementation does not 
     *   support the feature <code>"XML"</code> and the language exposed 
     *   through the Document does not support XML Namespaces (such as [<a href='http://www.w3.org/TR/1999/REC-html401-19991224/'>HTML 4.01</a>]). 
     * @since DOM Level 2
     */
    public static final native NodeList<? extends Node> getElementsByTagNameNS(Element elem, String namespaceURI,
            String localName) throws JavaScriptException /*-{
                                                         return elem.getElementsByTagNameNS(namespaceURI, localName);
                                                         }-*/;

    /**
     * Returns a <code>NodeList</code> of all the <code>Elements</code> 
     * in the specified document with a 
     * given local name and namespace URI in document order.
     * @param doc The document
     * @param namespaceURI The namespace URI of the elements to match on. The 
     *   special value <code>"*"</code> matches all namespaces.
     * @param localName The local name of the elements to match on. The 
     *   special value "*" matches all local names.
     * @return A new <code>NodeList</code> object containing all the matched 
     *   <code>Elements</code>.
     * @since DOM Level 2
     */
    public static final native NodeList<? extends Node> getElementsByTagNameNS(Document doc, String namespaceURI,
            String localName) throws JavaScriptException /*-{
                                                         return doc.getElementsByTagNameNS(namespaceURI, localName);
                                                         }-*/;

    /**
     * Returns the current document
     * @return the current document
     */
    public static final native Document getCurrentDocument() /*-{
                                                             return $doc;
                                                             }-*/;

    /**
     * Returns a <code>NamedNodeMap</code> containing the attributes of the
     * specified element.
     * @param elem The element
     * @return a <code>NamedNodeMap</code> containing the attributes of the
     * specified element
     */
    public static final native NamedNodeMap<Attr> getAttributes(Element elem) /*-{
                                                                              return elem.attributes;
                                                                              }-*/;

    /**
     * Retrieves an attribute node by name on the specified element.
     * <br>To retrieve an attribute node by qualified name and namespace URI, 
     * use the <code>getAttributeNodeNS</code> method.
     * @param elt The element
     * @param attrName The name (<code>nodeName</code>) of the attribute to 
     *   retrieve.
     * @return The <code>Attr</code> node with the specified name (
     *   <code>nodeName</code>) or <code>null</code> if there is no such 
     *   attribute.
     */
    public static final native Attr getAttributeNode(Element elt, String attrName) /*-{
                                                                                   return elt.getAttributeNode(attrName);
                                                                                   }-*/;

    /**
     * Adds a new attribute node to the specified element. If an attribute with that name (
     * <code>nodeName</code>) is already present in the element, it is 
     * replaced by the new one. Replacing an attribute node by itself has no 
     * effect.
     * <br>To add a new attribute node with a qualified name and namespace 
     * URI, use the <code>setAttributeNodeNS</code> method.
     * @param elt The element
     * @param attr The <code>Attr</code> node to add to the attribute list.
     * @return If the <code>attr</code> attribute replaces an existing 
     *   attribute, the replaced <code>Attr</code> node is returned, 
     *   otherwise <code>null</code> is returned.
     * @exception DOMException
     *   WRONG_DOCUMENT_ERR: Raised if <code>attr</code> was created from a 
     *   different document than the one that created the element.
     *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     *   <br>INUSE_ATTRIBUTE_ERR: Raised if <code>attr</code> is already an 
     *   attribute of another <code>Element</code> object. The DOM user must 
     *   explicitly clone <code>Attr</code> nodes to re-use them in other 
     *   elements.
     */
    public static final native Attr setAttributeNode(Element elt, Attr attr) throws JavaScriptException /*-{
                                                                                                        return elt.setAttributeNode(attr);
                                                                                                        }-*/;

    /**
     * Returns <code>true</code> when an attribute with a given local name and 
     * namespace URI is specified on the specified element or has a default value, 
     * <code>false</code> otherwise.
     * <br>Per [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>]
     * , applications must use the value <code>null</code> as the 
     * <code>namespaceURI</code> parameter for methods if they wish to have 
     * no namespace.
     * @param elem The element
     * @param namespaceURI The namespace URI of the attribute to look for.
     * @param localName The local name of the attribute to look for.
     * @return <code>true</code> if an attribute with the given local name 
     *   and namespace URI is specified or has a default value on this 
     *   element, <code>false</code> otherwise.
     * @exception DOMException
     *   NOT_SUPPORTED_ERR: May be raised if the implementation does not 
     *   support the feature <code>"XML"</code> and the language exposed 
     *   through the Document does not support XML Namespaces (such as [<a href='http://www.w3.org/TR/1999/REC-html401-19991224/'>HTML 4.01</a>]). 
     */
    public static final native boolean hasAttributeNS(Element elem, String namespaceURI, String localName)
            throws JavaScriptException /*-{
                                       return elem.hasAttributeNS(namespaceURI, localName);
                                       }-*/;

    /**
     * Adds a new attribute to the specified element. If an attribute with the same local name and 
     * namespace URI is already present on the element, its prefix is 
     * changed to be the prefix part of the <code>qualifiedName</code>, and 
     * its value is changed to be the <code>value</code> parameter. This 
     * value is a simple string; it is not parsed as it is being set. So any 
     * markup (such as syntax to be recognized as an entity reference) is 
     * treated as literal text, and needs to be appropriately escaped by the 
     * implementation when it is written out. In order to assign an 
     * attribute value that contains entity references, the user must create 
     * an <code>Attr</code> node plus any <code>Text</code> and 
     * <code>EntityReference</code> nodes, build the appropriate subtree, 
     * and use <code>setAttributeNodeNS</code> or 
     * <code>setAttributeNode</code> to assign it as the value of an 
     * attribute.
     * <br>Per [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>]
     * , applications must use the value <code>null</code> as the 
     * <code>namespaceURI</code> parameter for methods if they wish to have 
     * no namespace.
     * @param elem The element
     * @param namespaceURI The namespace URI of the attribute to create or 
     *   alter.
     * @param localName The local name of the attribute to create or 
     *   alter.
     * @param value The value to set in string form.
     * @exception DOMException
     *   INVALID_CHARACTER_ERR: Raised if the specified qualified name is not 
     *   an XML name according to the XML version in use specified in the 
     *   <code>Document.xmlVersion</code> attribute.
     *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     *   <br>NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is 
     *   malformed per the Namespaces in XML specification, if the 
     *   <code>qualifiedName</code> has a prefix and the 
     *   <code>namespaceURI</code> is <code>null</code>, if the 
     *   <code>qualifiedName</code> has a prefix that is "xml" and the 
     *   <code>namespaceURI</code> is different from "<a href='http://www.w3.org/XML/1998/namespace'>
     *   http://www.w3.org/XML/1998/namespace</a>", if the <code>qualifiedName</code> or its prefix is "xmlns" and the 
     *   <code>namespaceURI</code> is different from "<a href='http://www.w3.org/2000/xmlns/'>http://www.w3.org/2000/xmlns/</a>", or if the <code>namespaceURI</code> is "<a href='http://www.w3.org/2000/xmlns/'>http://www.w3.org/2000/xmlns/</a>" and neither the <code>qualifiedName</code> nor its prefix is "xmlns".
     *   <br>NOT_SUPPORTED_ERR: May be raised if the implementation does not 
     *   support the feature <code>"XML"</code> and the language exposed 
     *   through the Document does not support XML Namespaces (such as [<a href='http://www.w3.org/TR/1999/REC-html401-19991224/'>HTML 4.01</a>]). 
     */
    public static final native void setAttributeNS(Element elem, String namespaceURI, String localName,
            String value) throws JavaScriptException /*-{
                                                     elem.setAttributeNS(namespaceURI, localName, value);
                                                     }-*/;

    /**
     * The namespace URI of the specified node, or <code>null</code> if it is 
     * unspecified (see ).
     * <br>This is not a computed value that is the result of a namespace 
     * lookup based on an examination of the namespace declarations in 
     * scope. It is merely the namespace URI given at creation time.
     * <br>For nodes of any type other than <code>ELEMENT_NODE</code> and 
     * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1 
     * method, such as <code>Document.createElement()</code>, this is always 
     * <code>null</code>.
     * <p ><b>Note:</b> Per the <em>Namespaces in XML</em> Specification [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>]
     *  an attribute does not inherit its namespace from the element it is 
     * attached to. If an attribute is not explicitly given a namespace, it 
     * simply has no namespace.
     * @param node a DOM node
     * @return The namespace URI of this node
     */
    public static native String getNamespaceURI(Node node) /*-{
                                                           return node.namespaceURI;
                                                           }-*/;

    /**
     * Returns the local part of the qualified name of this node.
     * <br>For nodes of any type other than <code>ELEMENT_NODE</code> and 
     * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1 
     * method, such as <code>Document.createElement()</code>, this is always 
     * <code>null</code>.
     * @param node a DOM node
     * @return The local part of the qualified name of this node
     */
    public static native String getLocalName(Node node) /*-{
                                                        return node.localName;
                                                        }-*/;

    /**
     * Makes a node sink the events emitted by the specified element
     * @param elem The element emitting the events
     * @param eventName The event name
     */
    public static void bindEventListener(Element elem, String eventName) {
        impl.bindEventListener(elem, eventName);
    }

    /**
     * Returns the element which currently captures all the
     * events after a call to {@link org.vectomatic.dom.svg.utils.DOMHelper#setCaptureElement(OMSVGElement, LoseCaptureHandler)}
     * or null if element is set to capture events
     * @return The event capturing element
     */
    public static OMSVGElement getCaptureElement() {
        return impl.getCaptureElement();
    }

    /**
     * Makes the specified element capture all the events, until
     * a call to {@link org.vectomatic.dom.svg.utils.DOMHelper#releaseCaptureElement()}
     * terminates the capture
     * @param element The capturing element
     * @param loseCaptureHandler A handler which will be invoked
     * if the element loses capture
     * @return  {@link HandlerRegistration} used to remove this handler
     */
    public static HandlerRegistration setCaptureElement(OMSVGElement element,
            LoseCaptureHandler loseCaptureHandler) {
        return impl.setCaptureElement(element, loseCaptureHandler);
    }

    /**
     * Stops the forwarding of all events to the capturing element
     * specified by {@link org.vectomatic.dom.svg.utils.DOMHelper#setCaptureElement(OMSVGElement, LoseCaptureHandler)}
     */
    public static void releaseCaptureElement() {
        impl.releaseCaptureElement();
    }

    /**
     * Returns the JavaScript type of an object. 
     * The function getType is borrowed from: 
     * JavaScript: The Definitive Guide, 5th Edition
     * By David Flanagan
     */
    public static final native String getType(JavaScriptObject x) /*-{
                                                                  // If x is null, return "null"
                                                                  if (x == null) {
                                                                  return "null";
                                                                  }
                                                                  // Next try the typeof operator
                                                                  var t = typeof x;
                                                                  // If the result is not vague, return it
                                                                  if (t != "object")  {
                                                                  return t;
                                                                  }
                                                                  // Otherwise, x is an object. Use the default toString( ) method to
                                                                  // get the class value of the object.
                                                                  var c = Object.prototype.toString.apply(x);  // Returns "[object class]"
                                                                  c = c.substring(8, c.length-1);              // Strip off "[object" and "]"
                                                                      
                                                                  // If the class is not a vague one, return it.
                                                                  if (c != "Object") {
                                                                  return c;
                                                                  }
                                                                  // If we get here, c is "Object".  Check to see if
                                                                  // the value x is really just a generic object.
                                                                  if (x.constructor == Object) {
                                                                  return c;  // Okay the type really is "Object"
                                                                  }
                                                                  // For user-defined classes, look for a string-valued property named
                                                                  // classname, that is inherited from the object's prototype
                                                                  if ("classname" in x.constructor.prototype &&  // inherits classname
                                                                  typeof x.constructor.prototype.classname == "string") // its a string
                                                                  return x.constructor.prototype.classname;
                                                                      
                                                                  // If we really can't figure it out, say so.
                                                                  return "<unknown type>";
                                                                  }-*/;

    /**
     * Creates a value of the formed expected by SVG
     * href attribtues.
     * @param s the identifier of the data pointed by the href.
     * @return a value of the formed expected by SVG
     * href attribtues.
     */
    public static final String toUrl(String s) {
        return "url(#" + s + ")";
    }

    /**
     * Tests if the underlying DOM implementation supports the specified feature
     * @param featureName
     * The name of the feature to test
     * @return
     * true if the feature is supported, false otherwise 
     */
    public static native boolean hasFeature(String featureName) /*-{
                                                                return $doc.implementation.hasFeature(featureName, 1.1);
                                                                }-*/;

    /**
     * Evaluates the specified XPath expression and returns
     * a iterator for the selected {@link org.vectomatic.dom.svg.OMNode} node list.
     * The expression must evaluate to a node list.
     * @param root
     * The element the expression is rooted at
     * @param expr
     * The XPath expression
     * @param resolver
     * A prefix resolver if the expression has prefix
     * @return
     * A node iterator for the selected nodes.
     */
    public static <T extends OMNode> Iterator<T> evaluateXPath(OMElement root, String expr,
            XPathPrefixResolver resolver) {
        final JavaScriptObject result = impl.evaluateNodeListXPath_(root.getElement(), expr, resolver);
        return new Iterator<T>() {
            boolean fetched;
            Node next;

            @Override
            public boolean hasNext() {
                if (!fetched) {
                    next = iterateNext(result);
                    fetched = true;
                }
                return next != null;
            }

            @Override
            public T next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
                fetched = false;
                return OMNode.<T>convert(next);
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }

            private native Node iterateNext(JavaScriptObject result) /*-{
                                                                     return result.iterateNext();
                                                                     }-*/;
        };

    }

    /**
     * Evaluates the specified XPath expression and returns
     * a iterator for the selected {@link com.google.gwt.dom.client.Node} node list.
     * The expression must evaluate to a node list.
     * @param root
     * The element the expression is rooted at
     * @param expr
     * The XPath expression
     * @param resolver
     * A prefix resolver if the expression has prefix
     * @return
     * A node iterator for the selected nodes.
     */
    public static <T extends Node> Iterator<T> evaluateNodeListXPath(Element root, String expr,
            XPathPrefixResolver resolver) {
        final JavaScriptObject result = impl.evaluateNodeListXPath_(root, expr, resolver);
        return new Iterator<T>() {
            boolean fetched;
            T next;

            @Override
            public boolean hasNext() {
                if (!fetched) {
                    next = iterateNext(result);
                    fetched = true;
                }
                return next != null;
            }

            @Override
            public T next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
                fetched = false;
                return next;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }

            private native T iterateNext(JavaScriptObject result) /*-{
                                                                  return result.iterateNext();
                                                                  }-*/;
        };
    }

    /**
     * Evaluates the specified XPath expression and returns
     * the matching {@link com.google.gwt.dom.client.Node} node.
     * The expression must evaluate to a single node.
     * @param root
     * The element the expression is rooted at
     * @param expr
     * The XPath expression
     * @param resolver
     * A prefix resolver if the expression has prefix
     * @return
     * The selected node, or null if no such node exists.
     */
    public static <T extends Node> T evaluateNodeXPath(Element root, String expr, XPathPrefixResolver resolver) {
        return (T) (impl.evaluateNodeXPath_(root, expr, resolver));
    }

    /**
     * Evaluates the specified XPath expression and returns
     * the matching {@link java.lang.String}.
     * The expression must evaluate to a string.
     * @param root
     * The element the expression is rooted at
     * @param expr
     * The XPath expression
     * @param resolver
     * A prefix resolver if the expression has prefix
     * @return
     * The matching string, or null if no such string exists.
     */
    public static String evaluateStringXPath(Element root, String expr, XPathPrefixResolver resolver) {
        return impl.evaluateStringXPath_(root, expr, resolver);
    }

    /**
     * Evaluates the specified XPath expression and returns
     * the matching float.
     * The expression must evaluate to a number.
     * @param root
     * The element the expression is rooted at
     * @param expr
     * The XPath expression
     * @param resolver
     * A prefix resolver if the expression has prefix
     * @return
     * The matching float, or NaN if no such number exists.
     */
    public static float evaluateNumberXPath(Element root, String expr, XPathPrefixResolver resolver) {
        return impl.evaluateNumberXPath_(root, expr, resolver);
    }

    /**
     * Evaluates the specified XPath expression and returns
     * the matching boolean.
     * The expression must evaluate to a boolean.
     * @param root
     * The element the expression is rooted at
     * @param expr
     * The XPath expression
     * @param resolver
     * A prefix resolver if the expression has prefix
     * @return
     * The matching boolean.
     */
    public static boolean evaluateBooleanXPath(Element root, String expr, XPathPrefixResolver resolver) {
        return impl.evaluateBooleanXPath_(root, expr, resolver);
    }

    /**
     * Returns an XPath expression which enables reaching the specified node
     * from the root node
     * @param node
     * The node to reach
     * @param root
     * The root node, or null to specify the document root
     * @return
     * An XPath expression which enables reaching the specified node
     * from the root node
     */
    public static String getXPath(Node node, Node root) {
        StringBuilder builder = new StringBuilder();
        Node parentNode;
        while ((node != root) && (parentNode = node.getParentNode()) != null) {
            NodeList<Node> siblings = parentNode.getChildNodes();
            int index = 0;
            for (int i = 0, length = siblings.getLength(); i < length; i++) {
                Node sibling = siblings.getItem(i);
                if (sibling.getNodeType() == Node.ELEMENT_NODE) {
                    index++;
                    if (node == sibling) {
                        builder.insert(0, "/*[" + index + "]");
                        break;
                    }
                }
            }
            node = parentNode;
        }
        return builder.toString();
    }

    /***
     * Returns whether touch events on SVG elements are supported
     * on the current platform.
     * @return true if touch events on SVG elements are supported
     * on the current platform, false otherwise
     */
    public static native boolean supportsSvgTouchEvents() /*-{
                                                          var elem = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
                                                          elem.setAttribute('ontouchstart', 'return;');
                                                          return (typeof elem.ontouchstart) == "function";
                                                          }-*/;
}