/* * 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 org.apache.xerces.dom; import org.apache.xerces.util.URI; 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; /** * 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. * <P> * ElementImpl does not support Namespaces. ElementNSImpl, which inherits from it, does. * * @xerces.internal * * @see ElementNSImpl * * @author Arnaud Le Hors, IBM * @author Joe Kesselman, IBM * @author Andy Clark, IBM * @author Ralf Pfeiffer, IBM * @author Michael Glavassevich, IBM * @version $Id: ElementImpl.java 938635 2010-04-27 20:23:42Z mrglavas $ * @since PR-DOM-Level-1-19980818. */ public class ElementImpl extends ParentNode implements Element, ElementTraversal, TypeInfo { // // Constants // /** Serialization version. */ static final long serialVersionUID = 3717253516652722278L; // // Data // /** Element name. */ protected String name; /** Attributes. */ protected AttributeMap attributes; // // Constructors // /** Factory constructor. */ public ElementImpl(CoreDocumentImpl ownerDoc, String name) { super(ownerDoc); this.name = name; needsSyncData(true); // synchronizeData will initialize attributes } // for ElementNSImpl 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. */ public short getNodeType() { return Node.ELEMENT_NODE; } /** * Returns the element name */ public String getNodeName() { if (needsSyncData()) { synchronizeData(); } return name; } /** * 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. */ public NamedNodeMap getAttributes() { if (needsSyncData()) { synchronizeData(); } if (attributes == null) { attributes = new AttributeMap(this, null); } 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. * * @see org.w3c.dom.Node#cloneNode(boolean) */ public Node cloneNode(boolean deep) { ElementImpl newnode = (ElementImpl) super.cloneNode(deep); // Replicate NamedNodeMap rather than sharing it. if (attributes != null) { newnode.attributes = (AttributeMap) attributes.cloneMap(newnode); } return newnode; } // cloneNode(boolean):Node /** * DOM Level 3 WD - Experimental. Retrieve baseURI */ 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) { final Attr attrNode = getXMLBaseAttribute(); if (attrNode != null) { final String uri = attrNode.getNodeValue(); if (uri.length() != 0) {// attribute value is always empty string try { 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 _parentBaseURI = new URI(parentBaseURI); _uri.absolutize(_parentBaseURI); return _uri.toString(); } catch (org.apache.xerces.util.URI.MalformedURIException ex) { // This should never happen: parent should have checked the URI and returned null if // invalid. return null; } } // REVISIT: what should happen in this case? return null; } catch (org.apache.xerces.util.URI.MalformedURIException ex) { return null; } } } } // 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 return (this.ownerNode != null) ? this.ownerNode.getBaseURI() : null; } // getBaseURI /** * NON-DOM Returns 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 */ 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. * <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(). */ public String getAttribute(String name) { if (needsSyncData()) { synchronizeData(); } if (attributes == null) { return ""; } 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. * <p> * If no matching attribute is available, returns null. */ public Attr getAttributeNode(String name) { if (needsSyncData()) { synchronizeData(); } if (attributes == null) { return null; } 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. * <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 "*". * * @see DeepNodeListImpl */ public NodeList getElementsByTagName(String tagname) { return new DeepNodeListImpl(this, tagname); } /** * 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. */ public String getTagName() { if (needsSyncData()) { synchronizeData(); } return name; } /** * In "normal form" (as read from a source file), there will never be two Text children in succession. But DOM users * may create successive Text nodes in the course of manipulating the document. Normalize walks the sub-tree and * merges adjacent Texts, as if the DOM had been written out 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. * * <ScaleDOM> Note: Only loaded child nodes are normalized. </ScaleDOM> */ public void normalize() { // No need to normalize if already normalized. if (isNormalized()) { return; } if (needsSyncChildren()) { synchronizeChildren(); } ChildNode kid, next; // <ScaleDOM> ChildNode firstChild = null; if(isScaleDomEnabled()) { firstChild = getFirstLoadedChildNode(); } else { firstChild = this.firstChild; } // </ScaleDOM> for (kid = firstChild; kid != null; kid = next) { next = kid.nextSibling; // 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 an adjacent text node, merge it with kid 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 { // If kid is empty, remove it if (kid.getNodeValue() == null || kid.getNodeValue().length() == 0) { removeChild(kid); } } } // 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) { 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. * <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. ????? * <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. * * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is readonly. */ public void removeAttribute(String name) { 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 (attributes == null) { return; } attributes.safeRemoveNamedItem(name); } // removeAttribute(String) /** * 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(). * * @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 readonly. */ public Attr removeAttributeNode(Attr oldAttr) throws DOMException { 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 (attributes == null) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null); throw new DOMException(DOMException.NOT_FOUND_ERR, msg); } 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. * * 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(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); } if (needsSyncData()) { synchronizeData(); } Attr newAttr = getAttributeNode(name); if (newAttr == null) { newAttr = getOwnerDocument().createAttribute(name); if (attributes == null) { attributes = new AttributeMap(this, null); } newAttr.setNodeValue(value); attributes.setNamedItem(newAttr); } else { newAttr.setNodeValue(value); } } // setAttribute(String,String) /** * 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. * * @throws DOMException(INUSE_ATTRIBUTE_ERR) if the Attribute object has already been assigned to another Element. */ public Attr setAttributeNode(Attr newAttr) throws DOMException { if (needsSyncData()) { synchronizeData(); } 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); } if (newAttr.getOwnerDocument() != ownerDocument) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null); throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg); } } if (attributes == null) { attributes = new AttributeMap(this, null); } // This will throw INUSE if necessary return (Attr) attributes.setNamedItem(newAttr); } // setAttributeNode(Attr):Attr // // DOM2: Namespace methods // /** * 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. * @since WD-DOM-Level-2-19990923 */ public String getAttributeNS(String namespaceURI, String localName) { if (needsSyncData()) { synchronizeData(); } if (attributes == null) { return ""; } 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. * * @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 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) { // 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); } else { if (newAttr instanceof AttrNSImpl) { // change prefix and value ((AttrNSImpl) newAttr).name = (prefix != null) ? (prefix + ":" + localName) : localName; } else { // This case may happen if user calls: // elem.setAttribute("name", "value"); // elem.setAttributeNS(null, "name", "value"); // This case is not defined by the DOM spec, we choose // to create a new attribute in this case and remove an old one from the tree // note this might cause events to be propagated or user data to be lost newAttr = ((CoreDocumentImpl) getOwnerDocument()).createAttributeNS(namespaceURI, qualifiedName, localName); attributes.setNamedItemNS(newAttr); } newAttr.setNodeValue(value); } } // setAttributeNS(String,String,String) /** * 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> * * @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. * @since WD-DOM-Level-2-19990923 */ public void removeAttributeNS(String namespaceURI, String localName) { 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 (attributes == null) { return; } attributes.safeRemoveNamedItemNS(namespaceURI, localName); } // removeAttributeNS(String,String) /** * 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. * @since WD-DOM-Level-2-19990923 */ public Attr getAttributeNodeNS(String namespaceURI, String localName) { if (needsSyncData()) { synchronizeData(); } if (attributes == null) { return null; } return (Attr) attributes.getNamedItemNS(namespaceURI, localName); } // getAttributeNodeNS(String,String):Attr /** * 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. * * @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 { if (needsSyncData()) { synchronizeData(); } 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); } if (newAttr.getOwnerDocument() != ownerDocument) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null); throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg); } } if (attributes == null) { attributes = new AttributeMap(this, null); } // This will throw INUSE if necessary return (Attr) attributes.setNamedItemNS(newAttr); } // setAttributeNodeNS(Attr):Attr /** * NON-DOM: sets attribute node for this element */ protected int setXercesAttributeNode(Attr attr) { if (needsSyncData()) { synchronizeData(); } if (attributes == null) { attributes = new AttributeMap(this, null); } return attributes.addItem(attr); } /** * NON-DOM: get inded of an attribute */ protected int getXercesAttribute(String namespaceURI, String localName) { if (needsSyncData()) { synchronizeData(); } if (attributes == null) { return -1; } return attributes.getNamedItemIndex(namespaceURI, localName); } /** * Introduced in DOM Level 2. */ public boolean hasAttributes() { if (needsSyncData()) { synchronizeData(); } return (attributes != null && attributes.getLength() != 0); } /** * Introduced in DOM Level 2. */ public boolean hasAttribute(String name) { return getAttributeNode(name) != null; } /** * Introduced in DOM Level 2. */ public boolean hasAttributeNS(String namespaceURI, String localName) { return getAttributeNodeNS(namespaceURI, localName) != null; } /** * 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. * @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 */ public boolean isEqualNode(Node arg) { if (!super.isEqualNode(arg)) { return false; } boolean hasAttrs = hasAttributes(); if (hasAttrs != ((Element) arg).hasAttributes()) { return false; } if (hasAttrs) { NamedNodeMap map1 = getAttributes(); NamedNodeMap map2 = ((Element) arg).getAttributes(); int len = map1.getLength(); if (len != map2.getLength()) { return false; } for (int i = 0; i < len; i++) { Node n1 = map1.item(i); if (n1.getLocalName() == null) { // DOM Level 1 Node Node n2 = map2.getNamedItem(n1.getNodeName()); if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) { return false; } } else { Node n2 = map2.getNamedItemNS(n1.getNamespaceURI(), n1.getLocalName()); if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) { return false; } } } } return true; } /** * DOM Level 3: register the given attribute node as an ID attribute */ public void setIdAttributeNode(Attr at, boolean makeId) { if (needsSyncData()) { synchronizeData(); } 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); } if (at.getOwnerElement() != this) { 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 { ownerDocument.putIdentifier(at.getValue(), this); } } /** * DOM Level 3: register the given attribute node as an ID attribute */ public void setIdAttribute(String name, boolean makeId) { if (needsSyncData()) { synchronizeData(); } Attr at = getAttributeNode(name); 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); throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); } if (at.getOwnerElement() != this) { 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 { ownerDocument.putIdentifier(at.getValue(), this); } } /** * DOM Level 3: register the given attribute node as an ID attribute */ public void setIdAttributeNS(String namespaceURI, String localName, boolean makeId) { if (needsSyncData()) { synchronizeData(); } Attr at = getAttributeNodeNS(namespaceURI, localName); 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); throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); } if (at.getOwnerElement() != this) { 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 { ownerDocument.putIdentifier(at.getValue(), this); } } /** * @see org.w3c.dom.TypeInfo#getTypeName() */ public String getTypeName() { return null; } /** * @see org.w3c.dom.TypeInfo#getTypeNamespace() */ public String getTypeNamespace() { return null; } /** * 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 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 */ public boolean isDerivedFrom(String typeNamespaceArg, String typeNameArg, int derivationMethod) { return false; } /** * Method getSchemaTypeInfo. * * @return TypeInfo */ 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); if (attributes != null) { attributes.setReadOnly(readOnly, true); } } // // Protected methods // /** Synchronizes the data (name and value) for fast nodes. */ protected void synchronizeData() { // no need to sync in the future needsSyncData(false); // we don't want to generate any event for this so turn them off boolean orig = ownerDocument.getMutationEvents(); ownerDocument.setMutationEvents(false); // attributes setupDefaultAttributes(); // set mutation events flag back to its original value ownerDocument.setMutationEvents(orig); } // synchronizeData() // support for DOM Level 3 renameNode method // @param el The element from which to take the attributes void moveSpecifiedAttributes(ElementImpl el) { if (needsSyncData()) { synchronizeData(); } if (el.hasAttributes()) { if (attributes == null) { attributes = new AttributeMap(this, null); } attributes.moveSpecifiedAttributes(el.attributes); } } /** Setup the default attributes. */ protected void setupDefaultAttributes() { NamedNodeMapImpl defaults = getDefaultAttributes(); if (defaults != null) { attributes = new AttributeMap(this, defaults); } } /** Reconcile default attributes. */ protected void reconcileDefaultAttributes() { if (attributes != null) { NamedNodeMapImpl defaults = getDefaultAttributes(); attributes.reconcileDefaults(defaults); } } /** Get the default attributes. */ protected NamedNodeMapImpl getDefaultAttributes() { DocumentTypeImpl doctype = (DocumentTypeImpl) ownerDocument.getDoctype(); if (doctype == null) { return null; } 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> */ 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> */ 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> */ 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> */ 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> */ 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