You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1179 lines
42 KiB
1179 lines
42 KiB
/*
|
|
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*/
|
|
/*
|
|
* 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
|
|
*
|
|
* 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.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).
|
|
* <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.
|
|
* @see ElementNSImpl
|
|
*
|
|
* @xerces.internal
|
|
*
|
|
* @author Arnaud Le Hors, IBM
|
|
* @author Joe Kesselman, IBM
|
|
* @author Andy Clark, IBM
|
|
* @author Ralf Pfeiffer, IBM
|
|
* @since PR-DOM-Level-1-19980818.
|
|
*/
|
|
public class ElementImpl
|
|
extends ParentNode
|
|
implements Element, 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();
|
|
}
|
|
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) {
|
|
Attr attrNode = (Attr)attributes.getNamedItem("xml:base");
|
|
if (attrNode != null) {
|
|
String uri = attrNode.getNodeValue();
|
|
if (uri.length() != 0 ) {// attribute value is always empty string
|
|
try {
|
|
uri = new URI(uri).toString();
|
|
}
|
|
catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e) {
|
|
// This may be a relative URI.
|
|
|
|
// 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){
|
|
// This should never happen: parent should have checked the URI and returned null if invalid.
|
|
return null;
|
|
}
|
|
return uri;
|
|
}
|
|
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;
|
|
} //getBaseURI
|
|
|
|
|
|
|
|
/**
|
|
* NON-DOM
|
|
* set the ownerDocument of this node, its children, and its attributes
|
|
*/
|
|
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.
|
|
*/
|
|
public void normalize() {
|
|
// No need to normalize if already normalized.
|
|
if (isNormalized()) {
|
|
return;
|
|
}
|
|
if (needsSyncChildren()) {
|
|
synchronizeChildren();
|
|
}
|
|
ChildNode kid, next;
|
|
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){
|
|
String origNodeName = ((AttrNSImpl) newAttr).name;
|
|
String newName = (prefix!=null) ? (prefix+":"+localName) : localName;
|
|
|
|
((AttrNSImpl) newAttr).name = newName;
|
|
|
|
if (!newName.equals(origNodeName)) {
|
|
// Note: we can't just change the name of the attribute. Names have to be in sorted
|
|
// order in the attributes vector because a binary search is used to locate them.
|
|
// If the new name has a different prefix, the list may become unsorted.
|
|
// Maybe it would be better to resort the list, but the simplest
|
|
// fix seems to be to remove the old attribute and re-insert it.
|
|
// -- Norman.Walsh@Sun.COM, 2 Feb 2007
|
|
newAttr = (Attr) attributes.removeItem(newAttr, false);
|
|
attributes.addItem(newAttr);
|
|
}
|
|
}
|
|
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 = new AttrNSImpl((CoreDocumentImpl)getOwnerDocument(), 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 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.
|
|
* @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();
|
|
}
|
|
//if namespace uri is empty string, set it to 'null'
|
|
if (namespaceURI != null) {
|
|
namespaceURI = (namespaceURI.length() == 0)? null : namespaceURI;
|
|
}
|
|
Attr at = getAttributeNodeNS(namespaceURI, localName);
|
|
|
|
if( at == null){
|
|
String msg = DOMMessageFormatter.formatMessage(
|
|
DOMMessageFormatter.DOM_DOMAIN,
|
|
"NOT_FOUND_ERR", null);
|
|
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 ancestorNS
|
|
* The namspace of the ancestor type declaration
|
|
* @param ancestorName
|
|
* The name of the ancestor type declaration
|
|
* @param type
|
|
* The reference type definition
|
|
*
|
|
* @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()
|
|
|
|
} // class ElementImpl
|