src/java.xml/share/classes/com/sun/org/apache/xerces/internal/dom/ElementImpl.java

Print this page

        

@@ -1,53 +1,53 @@
 /*
  * reserved comment block
  * 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
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * 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

@@ -56,69 +56,95 @@
  * @author Ralf Pfeiffer, IBM
  * @since  PR-DOM-Level-1-19980818.
  */
 public class ElementImpl
     extends ParentNode
-    implements Element, TypeInfo {
+        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;
         needsSyncData(true);    // synchronizeData will initialize attributes
     }
 
     // for ElementNSImpl
-    protected ElementImpl() {}
+    protected ElementImpl() {
+    }
 
     // Support for DOM Level 3 renameNode method.
     // Note: This only deals with part of the pb. CoreDocumentImpl
     // does all the work.
     void rename(String name) {
         if (needsSyncData()) {
             synchronizeData();
         }
+        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();
         }
         return name;

@@ -127,11 +153,14 @@
     /**
      * Retrieve all the Attributes as a set. Note that this API is inherited
      * 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()) {
             synchronizeData();
         }

@@ -141,16 +170,17 @@
         return attributes;
 
     } // 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);
         // Replicate NamedNodeMap rather than sharing it.
         if (attributes != null) {

@@ -159,116 +189,115 @@
         return newnode;
 
     } // 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()) {
             synchronizeData();
         }
         // Absolute base URI is computed according to
         // XML Base (http://www.w3.org/TR/xmlbase/#granularity)
         // 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();
+                        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();
                     }
-                    catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e) {
-                        // This may be a relative URI.
 
                         // 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;
+        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);
         }
     }
 
     //
     // 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) {
 
         if (needsSyncData()) {
             synchronizeData();
         }
         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.
      */
     public Attr getAttributeNode(String name) {
 

@@ -276,40 +305,36 @@
             synchronizeData();
         }
         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.
-     *
-     * @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 "*".
+     * 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 "*".
      *
      * @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()) {
             synchronizeData();
         }

@@ -324,13 +349,13 @@
      * and read back in again. This simplifies implementation of higher-level
      * functions that may want to assume that the document is in standard form.
      * <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.
         if (isNormalized()) {
             return;

@@ -345,61 +370,52 @@
             // If kid is a text node, we need to check for one of two
             // conditions:
             //   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();
             }
         }
 
         // 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.
      */
     public void removeAttribute(String name) {

@@ -419,20 +435,18 @@
 
         attributes.safeRemoveNamedItem(name);
 
     } // 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
      * this Element.
      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is

@@ -456,35 +470,34 @@
         }
         return (Attr) attributes.removeItem(oldAttr, true);
 
     } // 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().
-     *
-     * 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.
+     * 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.
      *
      * @throws DOMException(INVALID_NAME_ERR) if the name is not acceptable.
      * (Attribute factory will do that test for us.)
      *
      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
      * readonly.
      */
         public void setAttribute(String name, String value) {
 
                 if (ownerDocument.errorChecking && isReadOnly()) {
-                        String msg =
-                                DOMMessageFormatter.formatMessage(
+            String msg
+                    = DOMMessageFormatter.formatMessage(
                                         DOMMessageFormatter.DOM_DOMAIN,
                                         "NO_MODIFICATION_ALLOWED_ERR",
                                         null);
                         throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
                 }

@@ -501,33 +514,31 @@
                                 attributes = new AttributeMap(this, null);
                         }
 
                         newAttr.setNodeValue(value);
                         attributes.setNamedItem(newAttr);
-                }
-                else {
+        } else {
                         newAttr.setNodeValue(value);
                 }
 
         } // 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();
         }
 

@@ -554,23 +565,20 @@
     } // setAttributeNode(Attr):Attr
 
     //
     // 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 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.
+     * @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) {
 
         if (needsSyncData()) {

@@ -579,61 +587,58 @@
 
         if (attributes == null) {
             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>
+     * 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.
+     * 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.
      *
-     * @param namespaceURI      The namespace URI of the attribute to create
-     *                          or alter.
+     * @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 INVALID_CHARACTER_ERR: Raised if the specified name contains an
+     * invalid character.
      *
-     * @throws                  NO_MODIFICATION_ALLOWED_ERR: Raised if this
-     *                          node is readonly.
+     * @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.
+     * @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,
+    public void setAttributeNS(String namespaceURI, String qualifiedName,
                                           String value) {
                 if (ownerDocument.errorChecking && isReadOnly()) {
-                        String msg =
-                                DOMMessageFormatter.formatMessage(
+            String msg
+                    = DOMMessageFormatter.formatMessage(
                                         DOMMessageFormatter.DOM_DOMAIN,
                                         "NO_MODIFICATION_ALLOWED_ERR",
                                         null);
                         throw new DOMException(
                                 DOMException.NO_MODIFICATION_ALLOWED_ERR,

@@ -645,12 +650,11 @@
                 int index = qualifiedName.indexOf(':');
                 String prefix, localName;
                 if (index < 0) {
                         prefix = null;
                         localName = qualifiedName;
-                }
-                else {
+        } else {
                         prefix = qualifiedName.substring(0, index);
                         localName = qualifiedName.substring(index + 1);
                 }
                 Attr newAttr = getAttributeNodeNS(namespaceURI, localName);
                 if (newAttr == null) {

@@ -697,24 +701,23 @@
                         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 localName     The local name of the attribute to remove.
-     * @throws                  NO_MODIFICATION_ALLOWED_ERR: Raised if this
-     *                          node is readonly.
+     * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
      * @since WD-DOM-Level-2-19990923
      */
     public void removeAttributeNS(String namespaceURI, String localName) {
 
         if (ownerDocument.errorChecking && isReadOnly()) {

@@ -735,60 +738,52 @@
     } // removeAttributeNS(String,String)
 
     /**
      * Retrieves an Attr node by local name and namespace URI.
      *
-     * @param namespaceURI  The namespace URI of the attribute to
-     *                      retrieve.
+     * @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.
+     * @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();
         }
         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.
-     *
-     * @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.
+     * @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 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();
         }
         if (ownerDocument.errorChecking) {

@@ -813,11 +808,11 @@
     } // setAttributeNodeNS(Attr):Attr
 
     /**
       * NON-DOM: sets attribute node for this element
       */
-    protected int setXercesAttributeNode (Attr attr){
+    protected int setXercesAttributeNode(Attr attr) {
 
         if (needsSyncData()) {
             synchronizeData();
         }
 

@@ -829,11 +824,11 @@
     }
 
     /**
       * NON-DOM: get inded of an attribute
       */
-    protected int getXercesAttribute(String namespaceURI, String localName){
+    protected int getXercesAttribute(String namespaceURI, String localName) {
 
         if (needsSyncData()) {
             synchronizeData();
         }
         if (attributes == null) {

@@ -866,36 +861,34 @@
     public boolean hasAttributeNS(String namespaceURI, String localName) {
         return getAttributeNodeNS(namespaceURI, localName) != null;
     }
 
     /**
-     * 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.
+     * @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) {
         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)) {
             return false;
         }

@@ -915,12 +908,11 @@
                 if (n1.getLocalName() == null) { // DOM Level 1 Node
                     Node n2 = map2.getNamedItem(n1.getNodeName());
                     if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) {
                         return false;
                     }
-                }
-                else {
+                } else {
                     Node n2 = map2.getNamedItemNS(n1.getNamespaceURI(),
                                                   n1.getLocalName());
                     if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) {
                         return false;
                     }

@@ -951,12 +943,11 @@
             }
         }
         ((AttrImpl) at).isIdAttribute(makeId);
         if (!makeId) {
             ownerDocument.removeIdentifier(at.getValue());
-        }
-        else {
+        } else {
             ownerDocument.putIdentifier(at.getValue(), this);
         }
     }
 
     /**

@@ -966,11 +957,11 @@
         if (needsSyncData()) {
             synchronizeData();
         }
         Attr at = getAttributeNode(name);
 
-                if( at == null){
+        if (at == null) {
                 String msg = DOMMessageFormatter.formatMessage(
                                                                         DOMMessageFormatter.DOM_DOMAIN,
                                                                         "NOT_FOUND_ERR", null);
             throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
                 }

@@ -990,12 +981,11 @@
         }
 
         ((AttrImpl) at).isIdAttribute(makeId);
         if (!makeId) {
             ownerDocument.removeIdentifier(at.getValue());
-        }
-        else {
+        } else {
             ownerDocument.putIdentifier(at.getValue(), this);
         }
     }
 
     /**

@@ -1006,39 +996,40 @@
         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){
+        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 (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);
             }
 
             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);
         }
    }
 
     /**

@@ -1054,22 +1045,20 @@
     public String getTypeNamespace() {
         return null;
     }
 
     /**
-     * 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
+     * @return boolean True if the type is derived by restriction for the
      *         reference type
      */
     public boolean isDerivedFrom(String typeNamespaceArg,
                                  String typeNameArg,
                                  int derivationMethod) {

@@ -1077,41 +1066,41 @@
         return false;
     }
 
         /**
          * Method getSchemaTypeInfo.
+     *
          * @return TypeInfo
          */
-    public TypeInfo getSchemaTypeInfo(){
-        if(needsSyncData()) {
+    public TypeInfo getSchemaTypeInfo() {
+        if (needsSyncData()) {
             synchronizeData();
         }
         return this;
     }
 
     //
     // 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
         needsSyncData(false);
 

@@ -1139,40 +1128,250 @@
             }
             attributes.moveSpecifiedAttributes(el.attributes);
         }
     }
 
-    /** Setup the default attributes. */
+    /**
+     * Setup the default attributes.
+     */
     protected void setupDefaultAttributes() {
         NamedNodeMapImpl defaults = getDefaultAttributes();
         if (defaults != null) {
             attributes = new AttributeMap(this, defaults);
         }
     }
 
-    /** Reconcile default attributes. */
+    /**
+     * Reconcile default attributes.
+     */
     protected void reconcileDefaultAttributes() {
         if (attributes != null) {
             NamedNodeMapImpl defaults = getDefaultAttributes();
             attributes.reconcileDefaults(defaults);
         }
     }
 
-    /** 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()
+        ElementDefinitionImpl eldef
+                = (ElementDefinitionImpl) doctype.getElements()
                                                .getNamedItem(getNodeName());
         if (eldef == null) {
             return null;
         }
         return (NamedNodeMapImpl) eldef.getAttributes();
 
     } // 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