8135283: DOM API update: Element Traversal Specification
authorjoehw
Fri, 25 Sep 2015 16:42:19 -0700
changeset 32791 ed81078b4e7f
parent 32712 f61a63b7d1e5
child 32792 5625643929aa
8135283: DOM API update: Element Traversal Specification Reviewed-by: mchung, lancea
jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/dom/ElementImpl.java
jaxp/src/java.xml/share/classes/org/w3c/dom/ElementTraversal.java
jaxp/test/javax/xml/jaxp/unittest/dom/ElementTraversal.java
jaxp/test/javax/xml/jaxp/unittest/dom/ElementTraversal.xml
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/dom/ElementImpl.java	Wed Jul 05 20:50:41 2017 +0200
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/dom/ElementImpl.java	Fri Sep 25 16:42:19 2015 -0700
@@ -3,11 +3,12 @@
  * DO NOT REMOVE OR ALTER!
  */
 /*
- * Copyright 1999-2002,2004 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
@@ -17,70 +18,75 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.sun.org.apache.xerces.internal.dom;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.DOMException;
 import org.w3c.dom.Element;
+import org.w3c.dom.ElementTraversal;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.w3c.dom.Text;
-
 import org.w3c.dom.TypeInfo;
 import com.sun.org.apache.xerces.internal.util.URI;
 
 /**
- * Elements represent most of the "markup" and structure of the
- * document.  They contain both the data for the element itself
- * (element name and attributes), and any contained nodes, including
- * document text (as children).
+ * Elements represent most of the "markup" and structure of the document. They
+ * contain both the data for the element itself (element name and attributes),
+ * and any contained nodes, including document text (as children).
  * <P>
  * Elements may have Attributes associated with them; the API for this is
  * defined in Node, but the function is implemented here. In general, XML
  * applications should retrive Attributes as Nodes, since they may contain
- * entity references and hence be a fairly complex sub-tree. HTML users will
- * be dealing with simple string values, and convenience methods are provided
- * to work in terms of Strings.
+ * entity references and hence be a fairly complex sub-tree. HTML users will be
+ * dealing with simple string values, and convenience methods are provided to
+ * work in terms of Strings.
  * <P>
  * ElementImpl does not support Namespaces. ElementNSImpl, which inherits from
  * it, does.
+ *
  * @see ElementNSImpl
  *
  * @xerces.internal
  *
- * @author Arnaud  Le Hors, IBM
+ * @author Arnaud Le Hors, IBM
  * @author Joe Kesselman, IBM
  * @author Andy Clark, IBM
  * @author Ralf Pfeiffer, IBM
- * @since  PR-DOM-Level-1-19980818.
+ * @since PR-DOM-Level-1-19980818.
  */
 public class ElementImpl
-    extends ParentNode
-    implements Element, TypeInfo {
+        extends ParentNode
+        implements Element, ElementTraversal, TypeInfo {
 
     //
     // Constants
     //
-
-    /** Serialization version. */
+    /**
+     * Serialization version.
+     */
     static final long serialVersionUID = 3717253516652722278L;
     //
     // Data
     //
 
-    /** Element name. */
+    /**
+     * Element name.
+     */
     protected String name;
 
-    /** Attributes. */
+    /**
+     * Attributes.
+     */
     protected AttributeMap attributes;
 
     //
     // Constructors
     //
-
-    /** Factory constructor. */
+    /**
+     * Factory constructor.
+     */
     public ElementImpl(CoreDocumentImpl ownerDoc, String name) {
         super(ownerDoc);
         this.name = name;
@@ -88,7 +94,8 @@
     }
 
     // for ElementNSImpl
-    protected ElementImpl() {}
+    protected ElementImpl() {
+    }
 
     // Support for DOM Level 3 renameNode method.
     // Note: This only deals with part of the pb. CoreDocumentImpl
@@ -97,26 +104,45 @@
         if (needsSyncData()) {
             synchronizeData();
         }
-            this.name = name;
+        if (ownerDocument.errorChecking) {
+            int colon1 = name.indexOf(':');
+            if (colon1 != -1) {
+                String msg
+                        = DOMMessageFormatter.formatMessage(
+                                DOMMessageFormatter.DOM_DOMAIN,
+                                "NAMESPACE_ERR",
+                                null);
+                throw new DOMException(DOMException.NAMESPACE_ERR, msg);
+            }
+            if (!CoreDocumentImpl.isXMLName(name, ownerDocument.isXML11Version())) {
+                String msg = DOMMessageFormatter.formatMessage(
+                        DOMMessageFormatter.DOM_DOMAIN,
+                        "INVALID_CHARACTER_ERR", null);
+                throw new DOMException(DOMException.INVALID_CHARACTER_ERR,
+                        msg);
+            }
+        }
+        this.name = name;
         reconcileDefaultAttributes();
     }
 
     //
     // Node methods
     //
-
-
     /**
-     * A short integer indicating what type of node this is. The named
-     * constants for this value are defined in the org.w3c.dom.Node interface.
+     * A short integer indicating what type of node this is. The named constants
+     * for this value are defined in the org.w3c.dom.Node interface.
      */
     public short getNodeType() {
         return Node.ELEMENT_NODE;
     }
 
     /**
-     * Returns the element name
+     * Returns the node name
+     *
+     * @return the node name
      */
+    @Override
     public String getNodeName() {
         if (needsSyncData()) {
             synchronizeData();
@@ -129,7 +155,10 @@
      * from Node rather than specified on Element; in fact only Elements will
      * ever have Attributes, but they want to allow folks to "blindly" operate
      * on the tree as a set of Nodes.
+     *
+     * @return all Attributes
      */
+    @Override
     public NamedNodeMap getAttributes() {
 
         if (needsSyncData()) {
@@ -143,12 +172,13 @@
     } // getAttributes():NamedNodeMap
 
     /**
-     * Return a duplicate copy of this Element. Note that its children
-     * will not be copied unless the "deep" flag is true, but Attributes
-     * are <i>always</i> replicated.
+     * Return a duplicate copy of this Element. Note that its children will not
+     * be copied unless the "deep" flag is true, but Attributes are
+     * {@code always} replicated.
      *
      * @see org.w3c.dom.Node#cloneNode(boolean)
      */
+    @Override
     public Node cloneNode(boolean deep) {
 
         ElementImpl newnode = (ElementImpl) super.cloneNode(deep);
@@ -160,10 +190,12 @@
 
     } // cloneNode(boolean):Node
 
-   /**
-     * DOM Level 3 WD - Experimental.
-     * Retrieve baseURI
+    /**
+     * DOM Level 3 WD - Experimental. Retrieve baseURI
+     *
+     * @return the baseURI
      */
+    @Override
     public String getBaseURI() {
 
         if (needsSyncData()) {
@@ -174,62 +206,61 @@
         // 1. The base URI specified by an xml:base attribute on the element,
         // if one exists
         if (attributes != null) {
-            Attr attrNode = (Attr)attributes.getNamedItem("xml:base");
+            final Attr attrNode = getXMLBaseAttribute();
             if (attrNode != null) {
-                String uri =  attrNode.getNodeValue();
-                if (uri.length() != 0 ) {// attribute value is always empty string
+                final String uri = attrNode.getNodeValue();
+                if (uri.length() != 0) {// attribute value is always empty string
                     try {
-                       uri = new URI(uri).toString();
-                    }
-                    catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e) {
-                        // This may be a relative URI.
+                        URI _uri = new URI(uri, true);
+                        // If the URI is already absolute return it; otherwise it's relative and we need to resolve it.
+                        if (_uri.isAbsoluteURI()) {
+                            return _uri.toString();
+                        }
 
                         // Make any parentURI into a URI object to use with the URI(URI, String) constructor
                         String parentBaseURI = (this.ownerNode != null) ? this.ownerNode.getBaseURI() : null;
-                        if (parentBaseURI != null){
-                            try{
-                                uri = new URI(new URI(parentBaseURI), uri).toString();
-                            }
-                            catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException ex){
+                        if (parentBaseURI != null) {
+                            try {
+                                URI _parentBaseURI = new URI(parentBaseURI);
+                                _uri.absolutize(_parentBaseURI);
+                                return _uri.toString();
+                            } catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException ex) {
                                 // This should never happen: parent should have checked the URI and returned null if invalid.
                                 return null;
                             }
-                            return uri;
                         }
+                        // REVISIT: what should happen in this case?
+                        return null;
+                    } catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException ex) {
                         return null;
                     }
-                    return uri;
                 }
             }
         }
 
         // 2.the base URI of the element's parent element within the
         // document or external entity, if one exists
-                // 3. the base URI of the document entity or external entity
-                // containing the element
-
-                // ownerNode serves as a parent or as document
-                String baseURI = (this.ownerNode != null) ? this.ownerNode.getBaseURI() : null ;
-        //base URI of parent element is not null
-        if(baseURI != null){
-            try {
-                //return valid absolute base URI
-               return new URI(baseURI).toString();
-            }
-            catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){
-                return null;
-            }
-        }
-        return null;
+        // 3. the base URI of the document entity or external entity
+        // containing the element
+        // ownerNode serves as a parent or as document
+        return (this.ownerNode != null) ? this.ownerNode.getBaseURI() : null;
     } //getBaseURI
 
-
+    /**
+     * NON-DOM Returns the xml:base attribute.
+     *
+     * @return the xml:base attribute
+     */
+    protected Attr getXMLBaseAttribute() {
+        return (Attr) attributes.getNamedItem("xml:base");
+    } // getXMLBaseAttribute():Attr
 
     /**
-     * NON-DOM
-     * set the ownerDocument of this node, its children, and its attributes
+     * NON-DOM set the ownerDocument of this node, its children, and its
+     * attributes
      */
-    void setOwnerDocument(CoreDocumentImpl doc) {
+    @Override
+    protected void setOwnerDocument(CoreDocumentImpl doc) {
         super.setOwnerDocument(doc);
         if (attributes != null) {
             attributes.setOwnerDocument(doc);
@@ -239,15 +270,14 @@
     //
     // Element methods
     //
-
     /**
-     * Look up a single Attribute by name. Returns the Attribute's
-     * string value, or an empty string (NOT null!) to indicate that the
-     * name did not map to a currently defined attribute.
+     * Look up a single Attribute by name. Returns the Attribute's string value,
+     * or an empty string (NOT null!) to indicate that the name did not map to a
+     * currently defined attribute.
      * <p>
-     * Note: Attributes may contain complex node trees. This method
-     * returns the "flattened" string obtained from Attribute.getValue().
-     * If you need the structure information, see getAttributeNode().
+     * Note: Attributes may contain complex node trees. This method returns the
+     * "flattened" string obtained from Attribute.getValue(). If you need the
+     * structure information, see getAttributeNode().
      */
     public String getAttribute(String name) {
 
@@ -257,16 +287,15 @@
         if (attributes == null) {
             return "";
         }
-        Attr attr = (Attr)(attributes.getNamedItem(name));
+        Attr attr = (Attr) (attributes.getNamedItem(name));
         return (attr == null) ? "" : attr.getValue();
 
     } // getAttribute(String):String
 
-
     /**
-     * Look up a single Attribute by name. Returns the Attribute Node,
-     * so its complete child tree is available. This could be important in
-     * XML, where the string rendering may not be sufficient information.
+     * Look up a single Attribute by name. Returns the Attribute Node, so its
+     * complete child tree is available. This could be important in XML, where
+     * the string rendering may not be sufficient information.
      * <p>
      * If no matching attribute is available, returns null.
      */
@@ -278,36 +307,32 @@
         if (attributes == null) {
             return null;
         }
-        return (Attr)attributes.getNamedItem(name);
+        return (Attr) attributes.getNamedItem(name);
 
     } // getAttributeNode(String):Attr
 
-
     /**
-     * Returns a NodeList of all descendent nodes (children,
-     * grandchildren, and so on) which are Elements and which have the
-     * specified tag name.
+     * Returns a NodeList of all descendent nodes (children, grandchildren, and
+     * so on) which are Elements and which have the specified tag name.
      * <p>
-     * Note: NodeList is a "live" view of the DOM. Its contents will
-     * change as the DOM changes, and alterations made to the NodeList
-     * will be reflected in the DOM.
+     * Note: NodeList is a "live" view of the DOM. Its contents will change as
+     * the DOM changes, and alterations made to the NodeList will be reflected
+     * in the DOM.
      *
-     * @param tagname The type of element to gather. To obtain a list of
-     * all elements no matter what their names, use the wild-card tag
-     * name "*".
+     * @param tagname The type of element to gather. To obtain a list of all
+     * elements no matter what their names, use the wild-card tag name "*".
      *
      * @see DeepNodeListImpl
      */
     public NodeList getElementsByTagName(String tagname) {
-        return new DeepNodeListImpl(this,tagname);
+        return new DeepNodeListImpl(this, tagname);
     }
 
     /**
-     * Returns the name of the Element. Note that Element.nodeName() is
-     * defined to also return the tag name.
+     * Returns the name of the Element. Note that Element.nodeName() is defined
+     * to also return the tag name.
      * <p>
-     * This is case-preserving in XML. HTML should uppercasify it on the
-     * way in.
+     * This is case-preserving in XML. HTML should uppercasify it on the way in.
      */
     public String getTagName() {
         if (needsSyncData()) {
@@ -326,9 +351,9 @@
      * <p>
      * To normalize a Document, normalize its top-level Element child.
      * <p>
-     * As of PR-DOM-Level-1-19980818, CDATA -- despite being a subclass of
-     * Text -- is considered "markup" and will _not_ be merged either with
-     * normal Text or with other CDATASections.
+     * As of PR-DOM-Level-1-19980818, CDATA -- despite being a subclass of Text
+     * -- is considered "markup" and will _not_ be merged either with normal
+     * Text or with other CDATASections.
      */
     public void normalize() {
         // No need to normalize if already normalized.
@@ -347,35 +372,27 @@
             //   1) There is an adjacent text node
             //   2) There is no adjacent text node, but kid is
             //      an empty text node.
-            if ( kid.getNodeType() == Node.TEXT_NODE )
-            {
+            if (kid.getNodeType() == Node.TEXT_NODE) {
                 // If an adjacent text node, merge it with kid
-                if ( next!=null && next.getNodeType() == Node.TEXT_NODE )
-                {
-                    ((Text)kid).appendData(next.getNodeValue());
-                    removeChild( next );
+                if (next != null && next.getNodeType() == Node.TEXT_NODE) {
+                    ((Text) kid).appendData(next.getNodeValue());
+                    removeChild(next);
                     next = kid; // Don't advance; there might be another.
-                }
-                else
-                {
+                } else {
                     // If kid is empty, remove it
-                    if ( kid.getNodeValue() == null || kid.getNodeValue().length() == 0 ) {
-                        removeChild( kid );
+                    if (kid.getNodeValue() == null || kid.getNodeValue().length() == 0) {
+                        removeChild(kid);
                     }
                 }
-            }
-
-            // Otherwise it might be an Element, which is handled recursively
+            } // Otherwise it might be an Element, which is handled recursively
             else if (kid.getNodeType() == Node.ELEMENT_NODE) {
                 kid.normalize();
             }
         }
 
         // We must also normalize all of the attributes
-        if ( attributes!=null )
-        {
-            for( int i=0; i<attributes.getLength(); ++i )
-            {
+        if (attributes != null) {
+            for (int i = 0; i < attributes.getLength(); ++i) {
                 Node attr = attributes.item(i);
                 attr.normalize();
             }
@@ -383,21 +400,20 @@
 
         // changed() will have occurred when the removeChild() was done,
         // so does not have to be reissued.
-
         isNormalized(true);
     } // normalize()
 
     /**
-     * Remove the named attribute from this Element. If the removed
-     * Attribute has a default value, it is immediately replaced thereby.
+     * Remove the named attribute from this Element. If the removed Attribute
+     * has a default value, it is immediately replaced thereby.
      * <P>
      * The default logic is actually implemented in NamedNodeMapImpl.
-     * PR-DOM-Level-1-19980818 doesn't fully address the DTD, so some
-     * of this behavior is likely to change in future versions. ?????
+     * PR-DOM-Level-1-19980818 doesn't fully address the DTD, so some of this
+     * behavior is likely to change in future versions. ?????
      * <P>
-     * Note that this call "succeeds" even if no attribute by this name
-     * existed -- unlike removeAttributeNode, which will throw a not-found
-     * exception in that case.
+     * Note that this call "succeeds" even if no attribute by this name existed
+     * -- unlike removeAttributeNode, which will throw a not-found exception in
+     * that case.
      *
      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
      * readonly.
@@ -421,16 +437,14 @@
 
     } // removeAttribute(String)
 
-
     /**
-     * Remove the specified attribute/value pair. If the removed
-     * Attribute has a default value, it is immediately replaced.
+     * Remove the specified attribute/value pair. If the removed Attribute has a
+     * default value, it is immediately replaced.
      * <p>
-     * NOTE: Specifically removes THIS NODE -- not the node with this
-     * name, nor the node with these contents. If the specific Attribute
-     * object passed in is not stored in this Element, we throw a
-     * DOMException.  If you really want to remove an attribute by name,
-     * use removeAttribute().
+     * NOTE: Specifically removes THIS NODE -- not the node with this name, nor
+     * the node with these contents. If the specific Attribute object passed in
+     * is not stored in this Element, we throw a DOMException. If you really
+     * want to remove an attribute by name, use removeAttribute().
      *
      * @return the Attribute object that was removed.
      * @throws DOMException(NOT_FOUND_ERR) if oldattr is not an attribute of
@@ -439,7 +453,7 @@
      * readonly.
      */
     public Attr removeAttributeNode(Attr oldAttr)
-        throws DOMException {
+            throws DOMException {
 
         if (ownerDocument.errorChecking && isReadOnly()) {
             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
@@ -458,19 +472,18 @@
 
     } // removeAttributeNode(Attr):Attr
 
-
     /**
-     * Add a new name/value pair, or replace the value of the existing
-     * attribute having that name.
+     * Add a new name/value pair, or replace the value of the existing attribute
+     * having that name.
      *
-     * Note: this method supports only the simplest kind of Attribute,
-     * one whose value is a string contained in a single Text node.
-     * If you want to assert a more complex value (which XML permits,
-     * though HTML doesn't), see setAttributeNode().
+     * Note: this method supports only the simplest kind of Attribute, one whose
+     * value is a string contained in a single Text node. If you want to assert
+     * a more complex value (which XML permits, though HTML doesn't), see
+     * setAttributeNode().
      *
-     * The attribute is created with specified=true, meaning it's an
-     * explicit value rather than inherited from the DTD as a default.
-     * Again, setAttributeNode can be used to achieve other results.
+     * The attribute is created with specified=true, meaning it's an explicit
+     * value rather than inherited from the DTD as a default. Again,
+     * setAttributeNode can be used to achieve other results.
      *
      * @throws DOMException(INVALID_NAME_ERR) if the name is not acceptable.
      * (Attribute factory will do that test for us.)
@@ -478,54 +491,52 @@
      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
      * readonly.
      */
-        public void setAttribute(String name, String value) {
+    public void setAttribute(String name, String value) {
 
-                if (ownerDocument.errorChecking && isReadOnly()) {
-                        String msg =
-                                DOMMessageFormatter.formatMessage(
-                                        DOMMessageFormatter.DOM_DOMAIN,
-                                        "NO_MODIFICATION_ALLOWED_ERR",
-                                        null);
-                        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
-                }
+        if (ownerDocument.errorChecking && isReadOnly()) {
+            String msg
+                    = DOMMessageFormatter.formatMessage(
+                            DOMMessageFormatter.DOM_DOMAIN,
+                            "NO_MODIFICATION_ALLOWED_ERR",
+                            null);
+            throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
+        }
 
-                if (needsSyncData()) {
-                        synchronizeData();
-                }
+        if (needsSyncData()) {
+            synchronizeData();
+        }
 
-                Attr newAttr = getAttributeNode(name);
-                if (newAttr == null) {
-                        newAttr = getOwnerDocument().createAttribute(name);
+        Attr newAttr = getAttributeNode(name);
+        if (newAttr == null) {
+            newAttr = getOwnerDocument().createAttribute(name);
 
-                        if (attributes == null) {
-                                attributes = new AttributeMap(this, null);
-                        }
+            if (attributes == null) {
+                attributes = new AttributeMap(this, null);
+            }
 
-                        newAttr.setNodeValue(value);
-                        attributes.setNamedItem(newAttr);
-                }
-                else {
-                        newAttr.setNodeValue(value);
-                }
+            newAttr.setNodeValue(value);
+            attributes.setNamedItem(newAttr);
+        } else {
+            newAttr.setNodeValue(value);
+        }
 
-        } // setAttribute(String,String)
+    } // setAttribute(String,String)
 
     /**
-     * Add a new attribute/value pair, or replace the value of the
-     * existing attribute with that name.
+     * Add a new attribute/value pair, or replace the value of the existing
+     * attribute with that name.
      * <P>
      * This method allows you to add an Attribute that has already been
      * constructed, and hence avoids the limitations of the simple
-     * setAttribute() call. It can handle attribute values that have
-     * arbitrarily complex tree structure -- in particular, those which
-     * had entity references mixed into their text.
+     * setAttribute() call. It can handle attribute values that have arbitrarily
+     * complex tree structure -- in particular, those which had entity
+     * references mixed into their text.
      *
-     * @throws DOMException(INUSE_ATTRIBUTE_ERR) if the Attribute object
-     * has already been assigned to another Element.
+     * @throws DOMException(INUSE_ATTRIBUTE_ERR) if the Attribute object has
+     * already been assigned to another Element.
      */
     public Attr setAttributeNode(Attr newAttr)
-        throws DOMException
-        {
+            throws DOMException {
 
         if (needsSyncData()) {
             synchronizeData();
@@ -535,13 +546,13 @@
             if (isReadOnly()) {
                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
                 throw new DOMException(
-                                     DOMException.NO_MODIFICATION_ALLOWED_ERR,
-                                     msg);
+                        DOMException.NO_MODIFICATION_ALLOWED_ERR,
+                        msg);
             }
 
             if (newAttr.getOwnerDocument() != ownerDocument) {
                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
-                    throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
+                throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
             }
         }
 
@@ -556,19 +567,16 @@
     //
     // DOM2: Namespace methods
     //
-
     /**
-     * Introduced in DOM Level 2. <p>
+     * Introduced in DOM Level 2.
+     * <p>
      *
      * Retrieves an attribute value by local name and namespace URI.
      *
-     * @param namespaceURI
-     *                      The namespace URI of the attribute to
-     *                      retrieve.
-     * @param localName     The local name of the attribute to retrieve.
-     * @return String       The Attr value as a string, or empty string
-     *                      if that attribute
-     *                      does not have a specified or default value.
+     * @param namespaceURI The namespace URI of the attribute to retrieve.
+     * @param localName The local name of the attribute to retrieve.
+     * @return String The Attr value as a string, or empty string if that
+     * attribute does not have a specified or default value.
      * @since WD-DOM-Level-2-19990923
      */
     public String getAttributeNS(String namespaceURI, String localName) {
@@ -581,89 +589,85 @@
             return "";
         }
 
-        Attr attr = (Attr)(attributes.getNamedItemNS(namespaceURI, localName));
+        Attr attr = (Attr) (attributes.getNamedItemNS(namespaceURI, localName));
         return (attr == null) ? "" : attr.getValue();
 
     } // getAttributeNS(String,String):String
 
     /**
-     * Introduced in DOM Level 2. <p>
-     *
-     *  Adds a new attribute.
-     *  If the given namespaceURI is null or an empty string and the
-     *  qualifiedName has a prefix that is "xml", the new attribute is bound to
-     *  the predefined namespace "http://www.w3.org/XML/1998/namespace"
-     *  [Namespaces].  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 qualifiedName, and its value is changed to be the
-     *  value 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 Attr node plus any Text and EntityReference nodes,
-     *  build the appropriate subtree, and use setAttributeNodeNS or
-     *  setAttributeNode to assign it as the value of an attribute.
+     * Introduced in DOM Level 2.
+     * <p>
      *
-     * @param namespaceURI      The namespace URI of the attribute to create
-     *                          or alter.
-     * @param qualifiedName     The qualified name of the attribute to create or
-     *                          alter.
-     * @param value             The value to set in string form.
-     * @throws                  INVALID_CHARACTER_ERR: Raised if the specified
-     *                          name contains an invalid character.
+     * Adds a new attribute. If the given namespaceURI is null or an empty
+     * string and the qualifiedName has a prefix that is "xml", the new
+     * attribute is bound to the predefined namespace
+     * "http://www.w3.org/XML/1998/namespace" [Namespaces]. 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 qualifiedName, and its
+     * value is changed to be the value 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 Attr node plus any Text and
+     * EntityReference nodes, build the appropriate subtree, and use
+     * setAttributeNodeNS or setAttributeNode to assign it as the value of an
+     * attribute.
      *
-     * @throws                  NO_MODIFICATION_ALLOWED_ERR: Raised if this
-     *                          node is readonly.
+     * @param namespaceURI The namespace URI of the attribute to create or
+     * alter.
+     * @param qualifiedName The qualified name of the attribute to create or
+     * alter.
+     * @param value The value to set in string form.
+     * @throws INVALID_CHARACTER_ERR: Raised if the specified name contains an
+     * invalid character.
      *
-     * @throws                  NAMESPACE_ERR: Raised if the qualifiedName
-     *                          has a prefix that is "xml" and the namespaceURI
-     *                          is neither null nor an empty string nor
-     *                          "http://www.w3.org/XML/1998/namespace", or if
-     *                          the qualifiedName has a prefix that is "xmlns"
-     *                          but the namespaceURI is neither null nor an
-     *                          empty string, or if if the qualifiedName has a
-     *                          prefix different from "xml" and "xmlns" and the
-     *                          namespaceURI is null or an empty string.
+     * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+     *
+     * @throws NAMESPACE_ERR: Raised if the qualifiedName has a prefix that is
+     * "xml" and the namespaceURI is neither null nor an empty string nor
+     * "http://www.w3.org/XML/1998/namespace", or if the qualifiedName has a
+     * prefix that is "xmlns" but the namespaceURI is neither null nor an empty
+     * string, or if if the qualifiedName has a prefix different from "xml" and
+     * "xmlns" and the namespaceURI is null or an empty string.
      * @since WD-DOM-Level-2-19990923
      */
-     public void setAttributeNS(String namespaceURI,String qualifiedName,
-                                          String value) {
-                if (ownerDocument.errorChecking && isReadOnly()) {
-                        String msg =
-                                DOMMessageFormatter.formatMessage(
-                                        DOMMessageFormatter.DOM_DOMAIN,
-                                        "NO_MODIFICATION_ALLOWED_ERR",
-                                        null);
-                        throw new DOMException(
-                                DOMException.NO_MODIFICATION_ALLOWED_ERR,
-                                msg);
-                }
-                if (needsSyncData()) {
-                        synchronizeData();
-                }
-                int index = qualifiedName.indexOf(':');
-                String prefix, localName;
-                if (index < 0) {
-                        prefix = null;
-                        localName = qualifiedName;
-                }
-                else {
-                        prefix = qualifiedName.substring(0, index);
-                        localName = qualifiedName.substring(index + 1);
-                }
-                Attr newAttr = getAttributeNodeNS(namespaceURI, localName);
-                if (newAttr == null) {
+    public void setAttributeNS(String namespaceURI, String qualifiedName,
+            String value) {
+        if (ownerDocument.errorChecking && isReadOnly()) {
+            String msg
+                    = DOMMessageFormatter.formatMessage(
+                            DOMMessageFormatter.DOM_DOMAIN,
+                            "NO_MODIFICATION_ALLOWED_ERR",
+                            null);
+            throw new DOMException(
+                    DOMException.NO_MODIFICATION_ALLOWED_ERR,
+                    msg);
+        }
+        if (needsSyncData()) {
+            synchronizeData();
+        }
+        int index = qualifiedName.indexOf(':');
+        String prefix, localName;
+        if (index < 0) {
+            prefix = null;
+            localName = qualifiedName;
+        } else {
+            prefix = qualifiedName.substring(0, index);
+            localName = qualifiedName.substring(index + 1);
+        }
+        Attr newAttr = getAttributeNodeNS(namespaceURI, localName);
+        if (newAttr == null) {
             // REVISIT: this is not efficient, we are creating twice the same
             //          strings for prefix and localName.
-                        newAttr = getOwnerDocument().createAttributeNS(
-                                        namespaceURI,
-                                        qualifiedName);
-                        if (attributes == null) {
-                                attributes = new AttributeMap(this, null);
-                        }
-                        newAttr.setNodeValue(value);
-                        attributes.setNamedItemNS(newAttr);
+            newAttr = getOwnerDocument().createAttributeNS(
+                    namespaceURI,
+                    qualifiedName);
+            if (attributes == null) {
+                attributes = new AttributeMap(this, null);
+            }
+            newAttr.setNodeValue(value);
+            attributes.setNamedItemNS(newAttr);
                 }
                 else {
             if (newAttr instanceof AttrNSImpl){
@@ -694,25 +698,24 @@
                 attributes.setNamedItemNS(newAttr);
             }
 
-                        newAttr.setNodeValue(value);
-                }
+            newAttr.setNodeValue(value);
+        }
 
     } // setAttributeNS(String,String,String)
 
-
     /**
-     * Introduced in DOM Level 2. <p>
+     * Introduced in DOM Level 2.
+     * <p>
      *
      * Removes an attribute by local name and namespace URI. If the removed
-     * attribute has a default value it is immediately replaced.
-     * The replacing attribute has the same namespace URI and local name,
-     * as well as the original prefix.<p>
+     * attribute has a default value it is immediately replaced. The replacing
+     * attribute has the same namespace URI and local name, as well as the
+     * original prefix.<p>
      *
-     * @param namespaceURI  The namespace URI of the attribute to remove.
+     * @param namespaceURI The namespace URI of the attribute to remove.
      *
-     * @param localName     The local name of the attribute to remove.
-     * @throws                  NO_MODIFICATION_ALLOWED_ERR: Raised if this
-     *                          node is readonly.
+     * @param localName The local name of the attribute to remove.
+     * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
      * @since WD-DOM-Level-2-19990923
      */
     public void removeAttributeNS(String namespaceURI, String localName) {
@@ -737,15 +740,13 @@
     /**
      * Retrieves an Attr node by local name and namespace URI.
      *
-     * @param namespaceURI  The namespace URI of the attribute to
-     *                      retrieve.
-     * @param localName     The local name of the attribute to retrieve.
-     * @return Attr         The Attr node with the specified attribute
-     *                      local name and namespace
-     *                      URI or null if there is no such attribute.
+     * @param namespaceURI The namespace URI of the attribute to retrieve.
+     * @param localName The local name of the attribute to retrieve.
+     * @return Attr The Attr node with the specified attribute local name and
+     * namespace URI or null if there is no such attribute.
      * @since WD-DOM-Level-2-19990923
      */
-    public Attr getAttributeNodeNS(String namespaceURI, String localName){
+    public Attr getAttributeNodeNS(String namespaceURI, String localName) {
 
         if (needsSyncData()) {
             synchronizeData();
@@ -753,40 +754,34 @@
         if (attributes == null) {
             return null;
         }
-        return (Attr)attributes.getNamedItemNS(namespaceURI, localName);
+        return (Attr) attributes.getNamedItemNS(namespaceURI, localName);
 
     } // getAttributeNodeNS(String,String):Attr
 
     /**
-     * Introduced in DOM Level 2. <p>
+     * Introduced in DOM Level 2.
+     * <p>
      *
-     * Adds a new attribute. If an attribute with that local name and
-     * namespace URI is already present in the element, it is replaced
-     * by the new one.
+     * Adds a new attribute. If an attribute with that local name and namespace
+     * URI is already present in the element, it is replaced by the new one.
      *
-     * @param Attr      The Attr node to add to the attribute list. When
-     *                  the Node has no namespaceURI, this method behaves
-     *                  like setAttributeNode.
-     * @return Attr     If the newAttr attribute replaces an existing attribute
-     *                  with the same local name and namespace URI, the *
-     *                  previously existing Attr node is returned, otherwise
-     *                  null is returned.
-     * @throws          WRONG_DOCUMENT_ERR: Raised if newAttr
-     *                  was created from a different document than the one that
-     *                  created the element.
+     * @param newAttr The Attr node to add to the attribute list. When the Node
+     * has no namespaceURI, this method behaves like setAttributeNode.
+     * @return Attr If the newAttr attribute replaces an existing attribute with
+     * the same local name and namespace URI, the * previously existing Attr
+     * node is returned, otherwise null is returned.
+     * @throws WRONG_DOCUMENT_ERR: Raised if newAttr was created from a
+     * different document than the one that created the element.
      *
-     * @throws          NO_MODIFICATION_ALLOWED_ERR: Raised if
-     *                  this node is readonly.
+     * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
      *
-     * @throws          INUSE_ATTRIBUTE_ERR: Raised if newAttr is
-     *                  already an attribute of another Element object. The
-     *                  DOM user must explicitly clone Attr nodes to re-use
-     *                  them in other elements.
+     * @throws INUSE_ATTRIBUTE_ERR: Raised if newAttr is already an attribute of
+     * another Element object. The DOM user must explicitly clone Attr nodes to
+     * re-use them in other elements.
      * @since WD-DOM-Level-2-19990923
      */
     public Attr setAttributeNodeNS(Attr newAttr)
-        throws DOMException
-        {
+            throws DOMException {
 
         if (needsSyncData()) {
             synchronizeData();
@@ -794,9 +789,9 @@
         if (ownerDocument.errorChecking) {
             if (isReadOnly()) {
                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
-                    throw new DOMException(
-                                     DOMException.NO_MODIFICATION_ALLOWED_ERR,
-                                     msg);
+                throw new DOMException(
+                        DOMException.NO_MODIFICATION_ALLOWED_ERR,
+                        msg);
             }
             if (newAttr.getOwnerDocument() != ownerDocument) {
                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
@@ -813,9 +808,9 @@
     } // setAttributeNodeNS(Attr):Attr
 
     /**
-      * NON-DOM: sets attribute node for this element
-      */
-    protected int setXercesAttributeNode (Attr attr){
+     * NON-DOM: sets attribute node for this element
+     */
+    protected int setXercesAttributeNode(Attr attr) {
 
         if (needsSyncData()) {
             synchronizeData();
@@ -829,9 +824,9 @@
     }
 
     /**
-      * NON-DOM: get inded of an attribute
-      */
-    protected int getXercesAttribute(String namespaceURI, String localName){
+     * NON-DOM: get inded of an attribute
+     */
+    protected int getXercesAttribute(String namespaceURI, String localName) {
 
         if (needsSyncData()) {
             synchronizeData();
@@ -868,32 +863,30 @@
     }
 
     /**
-     * Introduced in DOM Level 2. <p>
+     * Introduced in DOM Level 2.
+     * <p>
      *
      * Returns a NodeList of all the Elements with a given local name and
      * namespace URI in the order in which they would be encountered in a
      * preorder traversal of the Document tree, starting from this node.
      *
-     * @param namespaceURI The namespace URI of the elements to match
-     *                     on. The special value "*" matches all
-     *                     namespaces. When it is null or an empty
-     *                     string, this method behaves like
-     *                     getElementsByTagName.
-     * @param localName    The local name of the elements to match on.
-     *                     The special value "*" matches all local names.
-     * @return NodeList    A new NodeList object containing all the matched
-     *                     Elements.
+     * @param namespaceURI The namespace URI of the elements to match on. The
+     * special value "*" matches all namespaces. When it is null or an empty
+     * string, this method behaves like getElementsByTagName.
+     * @param localName The local name of the elements to match on. The special
+     * value "*" matches all local names.
+     * @return NodeList A new NodeList object containing all the matched
+     * Elements.
      * @since WD-DOM-Level-2-19990923
      */
     public NodeList getElementsByTagNameNS(String namespaceURI,
-                                           String localName) {
+            String localName) {
         return new DeepNodeListImpl(this, namespaceURI, localName);
     }
 
     /**
-     * DOM Level 3 WD- Experimental.
-     * Override inherited behavior from NodeImpl and ParentNode to check on
-     * attributes
+     * DOM Level 3 WD- Experimental. Override inherited behavior from NodeImpl
+     * and ParentNode to check on attributes
      */
     public boolean isEqualNode(Node arg) {
         if (!super.isEqualNode(arg)) {
@@ -917,10 +910,9 @@
                     if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) {
                         return false;
                     }
-                }
-                else {
+                } else {
                     Node n2 = map2.getNamedItemNS(n1.getNamespaceURI(),
-                                                  n1.getLocalName());
+                            n1.getLocalName());
                     if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) {
                         return false;
                     }
@@ -941,8 +933,8 @@
             if (isReadOnly()) {
                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
                 throw new DOMException(
-                                     DOMException.NO_MODIFICATION_ALLOWED_ERR,
-                                     msg);
+                        DOMException.NO_MODIFICATION_ALLOWED_ERR,
+                        msg);
             }
 
             if (at.getOwnerElement() != this) {
@@ -953,8 +945,7 @@
         ((AttrImpl) at).isIdAttribute(makeId);
         if (!makeId) {
             ownerDocument.removeIdentifier(at.getValue());
-        }
-        else {
+        } else {
             ownerDocument.putIdentifier(at.getValue(), this);
         }
     }
@@ -968,19 +959,19 @@
         }
         Attr at = getAttributeNode(name);
 
-                if( at == null){
-                String msg = DOMMessageFormatter.formatMessage(
-                                                                        DOMMessageFormatter.DOM_DOMAIN,
-                                                                        "NOT_FOUND_ERR", null);
+        if (at == null) {
+            String msg = DOMMessageFormatter.formatMessage(
+                    DOMMessageFormatter.DOM_DOMAIN,
+                    "NOT_FOUND_ERR", null);
             throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
-                }
+        }
 
-                if (ownerDocument.errorChecking) {
+        if (ownerDocument.errorChecking) {
             if (isReadOnly()) {
                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
                 throw new DOMException(
-                                     DOMException.NO_MODIFICATION_ALLOWED_ERR,
-                                     msg);
+                        DOMException.NO_MODIFICATION_ALLOWED_ERR,
+                        msg);
             }
 
             if (at.getOwnerElement() != this) {
@@ -992,8 +983,7 @@
         ((AttrImpl) at).isIdAttribute(makeId);
         if (!makeId) {
             ownerDocument.removeIdentifier(at.getValue());
-        }
-        else {
+        } else {
             ownerDocument.putIdentifier(at.getValue(), this);
         }
     }
@@ -1002,51 +992,52 @@
      * DOM Level 3: register the given attribute node as an ID attribute
      */
     public void setIdAttributeNS(String namespaceURI, String localName,
-                                    boolean makeId) {
+            boolean makeId) {
         if (needsSyncData()) {
             synchronizeData();
         }
         //if namespace uri is empty string, set it to 'null'
         if (namespaceURI != null) {
-            namespaceURI = (namespaceURI.length() == 0)? null : namespaceURI;
+            namespaceURI = (namespaceURI.length() == 0) ? null : namespaceURI;
         }
         Attr at = getAttributeNodeNS(namespaceURI, localName);
 
-                if( at == null){
-                String msg = DOMMessageFormatter.formatMessage(
-                                                                        DOMMessageFormatter.DOM_DOMAIN,
-                                                                        "NOT_FOUND_ERR", null);
+        if (at == null) {
+            String msg = DOMMessageFormatter.formatMessage(
+                    DOMMessageFormatter.DOM_DOMAIN,
+                    "NOT_FOUND_ERR", null);
             throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
-                }
+        }
 
-                if (ownerDocument.errorChecking) {
+        if (ownerDocument.errorChecking) {
             if (isReadOnly()) {
-                String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
+                String msg = DOMMessageFormatter.formatMessage(
+                        DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
                 throw new DOMException(
-                                     DOMException.NO_MODIFICATION_ALLOWED_ERR,
-                                     msg);
+                        DOMException.NO_MODIFICATION_ALLOWED_ERR,
+                        msg);
             }
 
             if (at.getOwnerElement() != this) {
-                String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
+                String msg = DOMMessageFormatter.formatMessage(
+                        DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
                 throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
             }
         }
         ((AttrImpl) at).isIdAttribute(makeId);
         if (!makeId) {
             ownerDocument.removeIdentifier(at.getValue());
-        }
-        else {
+        } else {
             ownerDocument.putIdentifier(at.getValue(), this);
         }
-   }
+    }
 
     /**
      * @see org.w3c.dom.TypeInfo#getTypeName()
      */
-     public String getTypeName() {
+    public String getTypeName() {
         return null;
-     }
+    }
 
     /**
      * @see org.w3c.dom.TypeInfo#getTypeNamespace()
@@ -1056,33 +1047,32 @@
     }
 
     /**
-     * Introduced in DOM Level 3. <p>
+     * Introduced in DOM Level 3.
+     * <p>
      * Checks if a type is derived from another by restriction. See:
      * http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-isDerivedFrom
      *
-     * @param ancestorNS
-     *        The namspace of the ancestor type declaration
-     * @param ancestorName
-     *        The name of the ancestor type declaration
-     * @param type
-     *        The reference type definition
+     * @param typeNamespaceArg The namspace of the ancestor type declaration
+     * @param typeNameArg The name of the ancestor type declaration
+     * @param derivationMethod The derivation method
      *
-     * @return boolean True if the type is derived by restriciton for the
-     *         reference type
+     * @return boolean True if the type is derived by restriction for the
+     * reference type
      */
     public boolean isDerivedFrom(String typeNamespaceArg,
-                                 String typeNameArg,
-                                 int derivationMethod) {
+            String typeNameArg,
+            int derivationMethod) {
 
         return false;
     }
 
-        /**
-         * Method getSchemaTypeInfo.
-         * @return TypeInfo
-         */
-    public TypeInfo getSchemaTypeInfo(){
-        if(needsSyncData()) {
+    /**
+     * Method getSchemaTypeInfo.
+     *
+     * @return TypeInfo
+     */
+    public TypeInfo getSchemaTypeInfo() {
+        if (needsSyncData()) {
             synchronizeData();
         }
         return this;
@@ -1091,25 +1081,24 @@
     //
     // Public methods
     //
-
     /**
      * NON-DOM: Subclassed to flip the attributes' readonly switch as well.
+     *
      * @see NodeImpl#setReadOnly
      */
     public void setReadOnly(boolean readOnly, boolean deep) {
-        super.setReadOnly(readOnly,deep);
+        super.setReadOnly(readOnly, deep);
         if (attributes != null) {
-            attributes.setReadOnly(readOnly,true);
+            attributes.setReadOnly(readOnly, true);
         }
     }
 
-
-
     //
     // Protected methods
     //
-
-    /** Synchronizes the data (name and value) for fast nodes. */
+    /**
+     * Synchronizes the data (name and value) for fast nodes.
+     */
     protected void synchronizeData() {
 
         // no need to sync in the future
@@ -1141,7 +1130,9 @@
         }
     }
 
-    /** Setup the default attributes. */
+    /**
+     * Setup the default attributes.
+     */
     protected void setupDefaultAttributes() {
         NamedNodeMapImpl defaults = getDefaultAttributes();
         if (defaults != null) {
@@ -1149,7 +1140,9 @@
         }
     }
 
-    /** Reconcile default attributes. */
+    /**
+     * Reconcile default attributes.
+     */
     protected void reconcileDefaultAttributes() {
         if (attributes != null) {
             NamedNodeMapImpl defaults = getDefaultAttributes();
@@ -1157,17 +1150,19 @@
         }
     }
 
-    /** Get the default attributes. */
+    /**
+     * Get the default attributes.
+     */
     protected NamedNodeMapImpl getDefaultAttributes() {
 
-        DocumentTypeImpl doctype =
-            (DocumentTypeImpl) ownerDocument.getDoctype();
+        DocumentTypeImpl doctype
+                = (DocumentTypeImpl) ownerDocument.getDoctype();
         if (doctype == null) {
             return null;
         }
-        ElementDefinitionImpl eldef =
-            (ElementDefinitionImpl)doctype.getElements()
-                                               .getNamedItem(getNodeName());
+        ElementDefinitionImpl eldef
+                = (ElementDefinitionImpl) doctype.getElements()
+                .getNamedItem(getNodeName());
         if (eldef == null) {
             return null;
         }
@@ -1175,4 +1170,208 @@
 
     } // getDefaultAttributes()
 
+    //
+    // ElementTraversal methods
+    //
+    /**
+     * @see <a
+     * href="http://www.w3.org/TR/2008/REC-ElementTraversal-20081222/#attribute-childElementCount">
+     * Element Traversal Specification</a>
+     */
+    @Override
+    public final int getChildElementCount() {
+        int count = 0;
+        Element child = getFirstElementChild();
+        while (child != null) {
+            ++count;
+            child = ((ElementImpl) child).getNextElementSibling();
+        }
+        return count;
+    } // getChildElementCount():int
+
+    /**
+     * @see <a
+     * href="http://www.w3.org/TR/2008/REC-ElementTraversal-20081222/#attribute-firstElementChild">
+     * Element Traversal Specification</a>
+     */
+    @Override
+    public final Element getFirstElementChild() {
+        Node n = getFirstChild();
+        while (n != null) {
+            switch (n.getNodeType()) {
+                case Node.ELEMENT_NODE:
+                    return (Element) n;
+                case Node.ENTITY_REFERENCE_NODE:
+                    final Element e = getFirstElementChild(n);
+                    if (e != null) {
+                        return e;
+                    }
+                    break;
+            }
+            n = n.getNextSibling();
+        }
+        return null;
+    } // getFirstElementChild():Element
+
+    /**
+     * @see <a
+     * href="http://www.w3.org/TR/2008/REC-ElementTraversal-20081222/#attribute-lastElementChild">
+     * Element Traversal Specification</a>
+     */
+    @Override
+    public final Element getLastElementChild() {
+        Node n = getLastChild();
+        while (n != null) {
+            switch (n.getNodeType()) {
+                case Node.ELEMENT_NODE:
+                    return (Element) n;
+                case Node.ENTITY_REFERENCE_NODE:
+                    final Element e = getLastElementChild(n);
+                    if (e != null) {
+                        return e;
+                    }
+                    break;
+            }
+            n = n.getPreviousSibling();
+        }
+        return null;
+    } // getLastElementChild():Element
+
+    /**
+     * @see <a
+     * href="http://www.w3.org/TR/2008/REC-ElementTraversal-20081222/#attribute-nextElementSibling">
+     * Element Traversal Specification</a>
+     */
+    @Override
+    public final Element getNextElementSibling() {
+        Node n = getNextLogicalSibling(this);
+        while (n != null) {
+            switch (n.getNodeType()) {
+                case Node.ELEMENT_NODE:
+                    return (Element) n;
+                case Node.ENTITY_REFERENCE_NODE:
+                    final Element e = getFirstElementChild(n);
+                    if (e != null) {
+                        return e;
+                    }
+                    break;
+            }
+            n = getNextLogicalSibling(n);
+        }
+        return null;
+    } // getNextElementSibling():Element
+
+    /**
+     * @see <a
+     * href="http://www.w3.org/TR/2008/REC-ElementTraversal-20081222/#attribute-previousElementSibling">
+     * Element Traversal Specification</a>
+     */
+    @Override
+    public final Element getPreviousElementSibling() {
+        Node n = getPreviousLogicalSibling(this);
+        while (n != null) {
+            switch (n.getNodeType()) {
+                case Node.ELEMENT_NODE:
+                    return (Element) n;
+                case Node.ENTITY_REFERENCE_NODE:
+                    final Element e = getLastElementChild(n);
+                    if (e != null) {
+                        return e;
+                    }
+                    break;
+            }
+            n = getPreviousLogicalSibling(n);
+        }
+        return null;
+    } // getPreviousElementSibling():Element
+
+    // Returns the first element node found from a
+    // non-recursive in order traversal of the given node.
+    private Element getFirstElementChild(Node n) {
+        final Node top = n;
+        while (n != null) {
+            if (n.getNodeType() == Node.ELEMENT_NODE) {
+                return (Element) n;
+            }
+            Node next = n.getFirstChild();
+            while (next == null) {
+                if (top == n) {
+                    break;
+                }
+                next = n.getNextSibling();
+                if (next == null) {
+                    n = n.getParentNode();
+                    if (n == null || top == n) {
+                        return null;
+                    }
+                }
+            }
+            n = next;
+        }
+        return null;
+    } // getFirstElementChild(Node):Element
+
+    // Returns the first element node found from a
+    // non-recursive reverse order traversal of the given node.
+    private Element getLastElementChild(Node n) {
+        final Node top = n;
+        while (n != null) {
+            if (n.getNodeType() == Node.ELEMENT_NODE) {
+                return (Element) n;
+            }
+            Node next = n.getLastChild();
+            while (next == null) {
+                if (top == n) {
+                    break;
+                }
+                next = n.getPreviousSibling();
+                if (next == null) {
+                    n = n.getParentNode();
+                    if (n == null || top == n) {
+                        return null;
+                    }
+                }
+            }
+            n = next;
+        }
+        return null;
+    } // getLastElementChild(Node):Element
+
+    // Returns the next logical sibling with respect to the given node.
+    private Node getNextLogicalSibling(Node n) {
+        Node next = n.getNextSibling();
+        // If "n" has no following sibling and its parent is an entity reference node we
+        // need to continue the search through the following siblings of the entity
+        // reference as these are logically siblings of the given node.
+        if (next == null) {
+            Node parent = n.getParentNode();
+            while (parent != null && parent.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
+                next = parent.getNextSibling();
+                if (next != null) {
+                    break;
+                }
+                parent = parent.getParentNode();
+            }
+        }
+        return next;
+    } // getNextLogicalSibling(Node):Node
+
+    // Returns the previous logical sibling with respect to the given node.
+    private Node getPreviousLogicalSibling(Node n) {
+        Node prev = n.getPreviousSibling();
+        // If "n" has no previous sibling and its parent is an entity reference node we
+        // need to continue the search through the previous siblings of the entity
+        // reference as these are logically siblings of the given node.
+        if (prev == null) {
+            Node parent = n.getParentNode();
+            while (parent != null && parent.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
+                prev = parent.getPreviousSibling();
+                if (prev != null) {
+                    break;
+                }
+                parent = parent.getParentNode();
+            }
+        }
+        return prev;
+    } // getPreviousLogicalSibling(Node):Node
 } // class ElementImpl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxp/src/java.xml/share/classes/org/w3c/dom/ElementTraversal.java	Fri Sep 25 16:42:19 2015 -0700
@@ -0,0 +1,103 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * Copyright (c) 2015 World Wide Web Consortium,
+ *
+ * (Massachusetts Institute of Technology, European Research Consortium for
+ * Informatics and Mathematics, Keio University, Beihang). 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/copyright-software
+ */
+
+package org.w3c.dom;
+
+/**
+ * The {@code ElementTraversal} interface is a set of read-only attributes
+ * which allow an author to easily navigate between elements in a document.
+ * <p>
+ * In conforming implementations of Element Traversal, all objects that
+ * implement {@link Element} must also implement the {@code ElementTraversal}
+ * interface. Four of the methods,
+ * {@link #getFirstElementChild}, {@link #getLastElementChild},
+ * {@link #getPreviousElementSibling}, and {@link #getNextElementSibling},
+ * each provides a live reference to another element with the defined
+ * relationship to the current element, if the related element exists. The
+ * fifth method, {@link #getChildElementCount}, exposes the number of child
+ * elements of an element, for preprocessing before navigation.
+ *
+ * @see
+ * <a href='http://www.w3.org/TR/ElementTraversal/'><cite>Element Traversal Specification</cite></a>.
+ *
+ * @since 9
+ */
+public interface ElementTraversal {
+
+    /**
+     * Returns a reference to the first child node of the element which is of
+     * the {@link Element} type.
+     *
+     * @return a reference to an element child, {@code null} if the element has
+     * no child of the {@link Element} type.
+     */
+    Element getFirstElementChild();
+
+    /**
+     * Returns a reference to the last child node of the element which is of
+     * the {@link Element} type.
+     *
+     * @return a reference to an element child, {@code null} if the element has
+     * no child of the {@link Element} type.
+     */
+    Element getLastElementChild();
+
+    /**
+     * Returns a reference to the sibling node of the element which most immediately
+     * precedes the element in document order, and which is of the {@link Element} type.
+     *
+     * @return a reference to an element child, {@code null} if the element has
+     * no sibling node of the {@link Element} type that comes before this one.
+     */
+    Element getPreviousElementSibling();
+
+    /**
+     * Returns a reference to the sibling node of the element which most immediately
+     * follows the element in document order, and which is of the {@link Element} type.
+     *
+     * @return a reference to an element child, {@code null} if the element has
+     * no sibling node of the {@link Element} type that comes after this one.
+     */
+    Element getNextElementSibling();
+
+    /**
+     * Returns the current number of child nodes of the element which are of
+     * the {@link Element} type.
+     *
+     * @return the number of element children, or {@code 0} if the element has
+     * no element children.
+     */
+    int getChildElementCount();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxp/test/javax/xml/jaxp/unittest/dom/ElementTraversal.java	Fri Sep 25 16:42:19 2015 -0700
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package dom;
+
+import java.io.IOException;
+import java.io.InputStream;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+/*
+ * @bug 8135283
+ * @summary Tests for the Element Traversal interface.
+ */
+
+public class ElementTraversal {
+    /*
+       Verifies the ElementTraversal interface by exercising all of its five
+       methods while reading through the xml document.
+     */
+    @Test(dataProvider = "doc")
+    public void test(Document doc) {
+        org.w3c.dom.ElementTraversal et = (org.w3c.dom.ElementTraversal)doc.getDocumentElement();
+        //4 toys are listed
+        Assert.assertEquals(et.getChildElementCount(), 4);
+
+        //The 1st is the Martian
+        Element toy1 = et.getFirstElementChild();
+        verify(toy1, "1", "The Martian");
+
+        //toy1 has no previous element
+        Element noE = ((org.w3c.dom.ElementTraversal)toy1).getPreviousElementSibling();
+        Assert.assertEquals(noE, null);
+
+        //The 1st toy's next element is toy2, the Doll
+        Element toy2 = ((org.w3c.dom.ElementTraversal)toy1).getNextElementSibling();
+        verify(toy2, "2", "The Doll");
+
+        //The last toy is toy4, the Spaceship
+        Element toy4 = et.getLastElementChild();
+        verify(toy4, "4", "The Spaceship");
+
+        //toy4 has no next element
+        noE = ((org.w3c.dom.ElementTraversal)toy4).getNextElementSibling();
+        Assert.assertEquals(noE, null);
+
+        //toy4's previous element is toy3, Transformer X
+        //toy3 is also an EntityReference
+        Element toy3 = ((org.w3c.dom.ElementTraversal)toy4).getPreviousElementSibling();
+        verify(toy3, "3", "Transformer X");
+    }
+
+    /**
+     * Verifies that the values matches the specified element.
+     * @param id the value of the id attribute
+     * @param name the value of its name element
+     */
+    void verify(Element e, String id, String name) {
+        Assert.assertEquals(e.getAttribute("id"), id);
+        Element toyName = ((org.w3c.dom.ElementTraversal)e).getFirstElementChild();
+        Assert.assertEquals(toyName.getTextContent(), name);
+    }
+
+
+    /*
+     * DataProvider: a Document object
+     */
+    @DataProvider(name = "doc")
+    Object[][] getXPath() {
+        return new Object[][]{{getDoc()}};
+    }
+    Document getDoc() {
+        InputStream xmlFile = getClass().getResourceAsStream("ElementTraversal.xml");
+        Document doc = null;
+        try {
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            dbf.setExpandEntityReferences(false);
+            DocumentBuilder db = dbf.newDocumentBuilder();
+            doc = db.parse(xmlFile);
+        } catch (ParserConfigurationException | SAXException | IOException e) {
+            System.out.println("fail: " + e.getMessage());
+        }
+
+        return doc;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxp/test/javax/xml/jaxp/unittest/dom/ElementTraversal.xml	Fri Sep 25 16:42:19 2015 -0700
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE toys [
+<!ENTITY toy3 "<name>Transformer X</name><price>519</price>">
+]>
+
+<toys>
+
+    <toy id="1">	
+        <name>The Martian</name>
+        <price>98470</price>
+    </toy>
+    <toy id="2">
+        <name>The Doll</name>
+        <price>345</price>
+    </toy>         
+    <toy id="3">
+        &toy3;
+    </toy>    
+    <toy id="4">
+        <name>The Spaceship</name>
+        <price>725</price>
+    </toy>         
+     
+</toys>
\ No newline at end of file