/*
* Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.ukit.dom;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
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 com.sun.ukit.xml.Pair;
import com.sun.ukit.xml.Parser;
/**
* DOM element node implementation.
*
* @see org.w3c.dom.Node
*/
public abstract class XElm
extends XParent
implements Element, ElementTraversal
{
/** Default attribute list capacity. */
private final static int ATTR_ALLOC_UNIT = 4;
/** List of attributes assigned to this element. Four strings represent
* single attribute: 0 - qname, 1 - localname, 2 - namespace, 3 - value */
private String[] attlst;
/** Number of attributes assigned to this element. */
private int attnum;
/** Index of id attribute or negative value if element has no id. */
private int attid;
/** Number of default attributes. First <code>defnum</code> attributes in
* <code>attlst</code> are default with values copied from list of default
* attributes for this element or assigned values. */
private int defnum;
/** Attr objects created to represent attributes of this element. */
private Reference[] attobj;
/**
* Constructs element object from other element.
*/
protected XElm(XElm element, boolean deep)
{
super(element, deep);
attlst = new String[element.attlst.length];
System.arraycopy(element.attlst, 0, attlst, 0, element.attnum << 2);
attnum = element.attnum;
attid = element.attid;
defnum = element.defnum;
}
/**
* Constructs element object from its qualified name and namespace URI and
* its owner document.
*/
protected XElm(String namespaceURI, String tagName, XDoc ownerDocument)
{
super(namespaceURI, tagName, ownerDocument);
// Copy default attributes from document
String[] dattrs = _getDefAttrs();
defnum = (dattrs != null)? dattrs.length >> 2: 0;
attlst = new String[(defnum + ATTR_ALLOC_UNIT) << 2];
attid = -1;
for (int idx = 0; idx < defnum; idx++) {
int base = idx << 2;
attlst[base] = dattrs[base];
attlst[base + 1] = dattrs[base + 1];
attlst[base + 2] = dattrs[base + 2];
attlst[base + 3] = dattrs[base + 3];
if ("xml:id".equals(attlst[base])) {
attid = idx;
}
}
attnum = defnum;
}
/**
* Constructs element object from parser element.
*/
protected XElm(Pair element, int defcnt, XDoc ownerDocument)
{
super(
(element.ns == null && (element.id & Parser.FLAG_NSAWARE) != 0)?
"": element.ns,
element.qname(),
ownerDocument);
attlst = new String[element.num << 2];
attid = -1;
// Set attributes from last to first. This order ensures the
// default attributes will be in the beginning of the list.
boolean nsaware = ((element.id & Parser.FLAG_NSAWARE) != 0);
Pair attr = element.list;
for (int idx = element.num - 1; idx >= 0; idx--) {
int base = idx << 2;
attlst[base] = ownerDocument._intern(attr.qname());
attlst[base + 1] = ownerDocument._intern(attr.local());
attlst[base + 2] = ownerDocument._intern(
(attr.ns == null && nsaware == true)? "": attr.ns);
attlst[base + 3] = attr.value;
if (attr.id == 'i')
attid = idx;
attr = attr.next;
}
defnum = defcnt;
attnum = element.num;
}
/**
* A code representing the type of the underlying object, as defined above.
*/
public abstract short getNodeType();
/**
* The name of the element. For example, in:
* <pre> <elementExample
* id="demo"> ... </elementExample> , </pre>
* <code>tagName</code> has
* the value <code>"elementExample"</code>. Note that this is
* case-preserving in XML, as are all of the operations of the DOM. The
* HTML DOM returns the <code>tagName</code> of an HTML element in the
* canonical uppercase form, regardless of the case in the source HTML
* document.
*/
public String getTagName()
{
return getNodeName();
}
/**
* Sets the namespace prefix of this node.
* <br>Note that setting this attribute, when permitted, changes the
* <code>nodeName</code> attribute, which holds the qualified name, as
* well as the <code>tagName</code> and <code>name</code> attributes of
* the <code>Element</code> and <code>Attr</code> interfaces, when
* applicable.
* <br>Note also that changing the prefix of an attribute that is known to
* have a default value, does not make a new attribute with the default
* value and the original prefix appear, since the
* <code>namespaceURI</code> and <code>localName</code> do not change.
*
* @param prefix This node namespace prefix.
* @exception DOMException
* INVALID_CHARACTER_ERR: Raised if the specified prefix contains an
* illegal character.
* <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
* <br>NAMESPACE_ERR: Raised if the specified <code>prefix</code> is
* malformed, if the <code>namespaceURI</code> of this node is
* <code>null</code>, if the specified prefix is "xml" and the
* <code>namespaceURI</code> of this node is different from "
* http://www.w3.org/XML/1998/namespace", if this node is an attribute
* and the specified prefix is "xmlns" and the
* <code>namespaceURI</code> of this node is different from "
* http://www.w3.org/2000/xmlns/", or if this node is an attribute and
* the <code>qualifiedName</code> of this node is "xmlns" .
* @since DOM Level 2
*/
public void setPrefix(String prefix)
throws DOMException
{
if (_isNS() == false)
return; // created with non-NS methods have null prefix
_setPrefix(prefix);
}
/**
* Returns whether this node (if it is an element) has any attributes.
*
* @return <code>true</code> if this node has any attributes,
* <code>false</code> otherwise.
*
* @since DOM Level 2
*/
public boolean hasAttributes()
{
return (attnum > 0);
}
/**
* Returns <code>true</code> when an attribute with a given name is
* specified on this element or has a default value, <code>false</code>
* otherwise.
*
* @param name The name of the attribute to look for.
* @return <code>true</code> if an attribute with the given name is
* specified on this element or has a default value, <code>false</code>
* otherwise.
*
* @since DOM Level 2
*/
public boolean hasAttribute(String name)
{
return (_getAttrIdx(null, name) >= 0);
}
/**
* Returns <code>true</code> when an attribute with a given local name and
* namespace URI is specified on this element or has a default value,
* <code>false</code> otherwise. HTML-only DOM implementations do not
* need to implement this method.
*
* @param namespaceURI The namespace URI of the attribute to look for.
* @param localName The local name of the attribute to look for.
* @return <code>true</code> if an attribute with the given local name
* and namespace URI is specified or has a default value on this
* element, <code>false</code> otherwise.
*
* @since DOM Level 2
*/
public boolean hasAttributeNS(String namespaceURI, String localName)
{
return (_getAttrIdx(namespaceURI, localName) >= 0);
}
/**
* A <code>NamedNodeMap</code> containing the attributes of this node (if
* it is an <code>Element</code>) or <code>null</code> otherwise.
*/
public NamedNodeMap getAttributes()
{
return new AttrMapImp(this);
}
/**
* Retrieves an attribute value by name.
*
* @param name The name of the attribute to retrieve.
* @return The <code>Attr</code> value as a string, or the empty string
* if that attribute does not have a specified or default value.
*/
public String getAttribute(String name)
{
int idx = _getAttrIdx(null, name);
// uDOM #text attribute support
if (idx < 0 && name.equals("#text"))
return getTextContent();
return (idx >= 0)? attlst[(idx << 2) + 3]: "";
}
/**
* Retrieves an attribute value by local name and namespace URI. HTML-only
* DOM implementations do not need to implement this method.
*
* @param namespaceURI The namespace URI of the attribute to retrieve.
* @param localName The local name of the attribute to retrieve.
* @return The <code>Attr</code> value as a string, or the empty string
* if that attribute does not have a specified or default value.
*
* @since DOM Level 2
*/
public String getAttributeNS(String namespaceURI, String localName)
{
int idx = _getAttrIdx(namespaceURI, localName);
return (idx >= 0)? attlst[(idx << 2) + 3]: "";
}
/**
* Retrieves an attribute node by name.
* <br>To retrieve an attribute node by qualified name and namespace URI,
* use the <code>getAttributeNodeNS</code> method.
*
* @param name The name (<code>nodeName</code>) of the attribute to
* retrieve.
* @return The <code>Attr</code> node with the specified name (
* <code>nodeName</code>) or <code>null</code> if there is no such
* attribute.
*/
public Attr getAttributeNode(String name)
{
return (Attr)_getAttrObj(_getAttrIdx(null, name));
}
/**
* Retrieves an <code>Attr</code> node by local name and namespace URI.
* HTML-only DOM implementations do not need to implement this method.
*
* @param namespaceURI The namespace URI of the attribute to retrieve.
* @param localName The local name of the attribute to retrieve.
* @return The <code>Attr</code> node with the specified attribute local
* name and namespace URI or <code>null</code> if there is no such
* attribute.
*
* @since DOM Level 2
*/
public Attr getAttributeNodeNS(String namespaceURI, String localName)
{
return (Attr)_getAttrObj(_getAttrIdx(namespaceURI, localName));
}
/**
* Adds a new attribute. If an attribute with that name is already present
* in the element, its value is changed to be that of 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 <code>Attr</code> node plus any
* <code>Text</code> and <code>EntityReference</code> nodes, build the
* appropriate subtree, and use <code>setAttributeNode</code> to assign
* it as the value of an attribute.
* <br>To set an attribute with a qualified name and namespace URI, use
* the <code>setAttributeNS</code> method.
*
* @param name The name of the attribute to create or alter.
* @param value Value to set in string form.
* @exception DOMException
* INVALID_CHARACTER_ERR: Raised if the specified name contains an
* illegal character.
* <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
*/
public void setAttribute(String name, String value)
throws DOMException
{
if (value == null)
throw new NullPointerException("");
if (name.equals("#text")) {
setTextContent(value); // uDOM #text attribute support
} else {
XNode._checkName(name, false);
_updateAttr(_setAttr(null, name, value));
}
}
/**
* Adds a new attribute. If an attribute with the same local name and
* namespace URI is already present on the element, its prefix is
* changed to be the prefix part of the <code>qualifiedName</code>, and
* its value is changed to be the <code>value</code> parameter. This
* value is a simple string; it is not parsed as it is being set. So any
* markup (such as syntax to be recognized as an entity reference) is
* treated as literal text, and needs to be appropriately escaped by the
* implementation when it is written out. In order to assign an
* attribute value that contains entity references, the user must create
* an <code>Attr</code> node plus any <code>Text</code> and
* <code>EntityReference</code> nodes, build the appropriate subtree,
* and use <code>setAttributeNodeNS</code> or
* <code>setAttributeNode</code> to assign it as the value of an
* attribute.
* <br>HTML-only DOM implementations do not need to implement this method.
*
* @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.
* @exception DOMException
* INVALID_CHARACTER_ERR: Raised if the specified qualified name
* contains an illegal character.
* <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
* <br>NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is
* malformed, if the <code>qualifiedName</code> has a prefix and the
* <code>namespaceURI</code> is <code>null</code>, if the
* <code>qualifiedName</code> has a prefix that is "xml" and the
* <code>namespaceURI</code> is different from "
* http://www.w3.org/XML/1998/namespace", or if the
* <code>qualifiedName</code> is "xmlns" and the
* <code>namespaceURI</code> is different from "
* http://www.w3.org/2000/xmlns/".
*
* @since DOM Level 2
*/
public void setAttributeNS(
String namespaceURI, String qualifiedName, String value)
throws DOMException
{
if (value == null)
throw new NullPointerException("");
XNode._checkNameNS(namespaceURI, qualifiedName);
_updateAttr(_setAttr(namespaceURI, qualifiedName, value));
}
/**
* Adds a new attribute node. If an attribute with that name (
* <code>nodeName</code>) is already present in the element, it is
* replaced by the new one.
* <br>To add a new attribute node with a qualified name and namespace
* URI, use the <code>setAttributeNodeNS</code> method.
*
* @param newAttr The <code>Attr</code> node to add to the attribute list.
* @return If the <code>newAttr</code> attribute replaces an existing
* attribute, the replaced <code>Attr</code> node is returned,
* otherwise <code>null</code> is returned.
* @exception DOMException
* WRONG_DOCUMENT_ERR: Raised if <code>newAttr</code> was created from a
* different document than the one that created the element.
* <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
* <br>INUSE_ATTRIBUTE_ERR: Raised if <code>newAttr</code> is already an
* attribute of another <code>Element</code> object. The DOM user must
* explicitly clone <code>Attr</code> nodes to re-use them in other
* elements.
*/
public Attr setAttributeNode(Attr newAttr)
throws DOMException
{
XNode attr = (XNode)newAttr;
if (attr._getParent() == this)
return (Attr)attr;
if (getOwnerDocument() != attr.getOwnerDocument())
throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, "");
if (attr._getParent() != null)
throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, "");
if (attobj == null)
attobj = new WeakReference[attlst.length >> 2];
XNode ex = null;
int idx = _getAttrIdx(null, attr.getNodeName());
if (idx < 0) {
// Create attribute
idx = _setAttr(null, attr.getNodeName(), attr.getNodeValue());
} else {
// Release old object
ex = _getAttrObj(idx);
attobj[idx].clear();
ex._setParent(null);
// Replace attribute
_setAttr(idx, attr.getNodeValue());
}
// Set new object
attobj[idx] = new WeakReference(attr);
attr._setParent(this);
return (Attr)ex;
}
/**
* Adds a new attribute. If an attribute with that local name and that
* namespace URI is already present in the element, it is replaced by
* the new one.
* <br>HTML-only DOM implementations do not need to implement this method.
*
* @param newAttr The <code>Attr</code> node to add to the attribute list.
* @return If the <code>newAttr</code> attribute replaces an existing
* attribute with the same local name and namespace URI, the replaced
* <code>Attr</code> node is returned, otherwise <code>null</code> is
* returned.
* @exception DOMException
* WRONG_DOCUMENT_ERR: Raised if <code>newAttr</code> was created from a
* different document than the one that created the element.
* <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
* <br>INUSE_ATTRIBUTE_ERR: Raised if <code>newAttr</code> is already an
* attribute of another <code>Element</code> object. The DOM user must
* explicitly clone <code>Attr</code> nodes to re-use them in other
* elements.
*
* @since DOM Level 2
*/
public Attr setAttributeNodeNS(Attr newAttr)
throws DOMException
{
XNode attr = (XNode)newAttr;
if (attr._getParent() == this)
return null;
if (getOwnerDocument() != attr.getOwnerDocument())
throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, "");
if (attr._getParent() != null)
throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, "");
XNode ex = null;
int idx = _getAttrIdx(attr.getNamespaceURI(), attr.getLocalName());
if (idx >= 0) {
ex = _getAttrObj(idx);
// Release old object
attobj[idx].clear();
ex._setParent(null);
}
idx = _setAttr(
attr.getNamespaceURI(), attr.getNodeName(), attr.getNodeValue());
if (_isDefAttr(idx)) { // preserve default attribute qname
int base = idx << 2;
attr._set(attlst[base + 2], attlst[base], attlst[base + 1]);
}
if (attobj == null)
attobj = new WeakReference[attlst.length >> 2];
// Set new object
attobj[idx] = new WeakReference(attr);
attr._setParent(this);
return (Attr)ex;
}
/**
* If the parameter <code>isId</code> is <code>true</code>, this method
* declares the specified attribute to be a user-determined ID attribute
* . This affects the value of <code>Attr.isId</code> and the behavior
* of <code>Document.getElementById</code>.
* Use the value <code>false</code> for the parameter
* <code>isId</code> to undeclare an attribute for being a
* user-determined ID attribute.
* <br> To specify an attribute by local name and namespace URI, use the
* <code>setIdAttributeNS</code> method.
*
* @param name The name of the attribute.
* @param isId Whether the attribute is a of type ID.
* @exception DOMException
* NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
* <br>NOT_FOUND_ERR: Raised if the specified node is not an attribute
* of this element.
*
* @since DOM Level 3
*/
public void setIdAttribute(String name, boolean isId)
throws DOMException
{
_setIdAttr(_getAttrIdx(null, name), isId);
}
/**
* If the parameter <code>isId</code> is <code>true</code>, this method
* declares the specified attribute to be a user-determined ID attribute
* . This affects the value of <code>Attr.isId</code> and the behavior
* of <code>Document.getElementById</code>.
* Use the value <code>false</code> for the parameter
* <code>isId</code> to undeclare an attribute for being a
* user-determined ID attribute.
*
* @param namespaceURI The namespace URI of the attribute.
* @param localName The local name of the attribute.
* @param isId Whether the attribute is a of type ID.
* @exception DOMException
* NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
* <br>NOT_FOUND_ERR: Raised if the specified node is not an attribute
* of this element.
*
* @since DOM Level 3
*/
public void setIdAttributeNS(
String namespaceURI, String localName, boolean isId)
throws DOMException
{
_setIdAttr(_getAttrIdx(namespaceURI, localName), isId);
}
/**
* If the parameter <code>isId</code> is <code>true</code>, this method
* declares the specified attribute to be a user-determined ID attribute
* . This affects the value of <code>Attr.isId</code> and the behavior
* of <code>Document.getElementById</code>.
* Use the value <code>false</code> for the parameter
* <code>isId</code> to undeclare an attribute for being a
* user-determined ID attribute.
*
* @param idAttr The attribute node.
* @param isId Whether the attribute is a of type ID.
* @exception DOMException
* NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
* <br>NOT_FOUND_ERR: Raised if the specified node is not an attribute
* of this element.
*
* @since DOM Level 3
*/
public void setIdAttributeNode(Attr idAttr, boolean isId)
throws DOMException
{
XNode attr = (XNode)idAttr;
if (attr._getParent() != this)
throw new DOMException(DOMException.NOT_FOUND_ERR, "");
_setIdAttr(_getAttrIdx(attr), isId);
}
/**
* Removes an attribute by name. If the removed attribute is known to have
* a default value, an attribute immediately appears containing the
* default value as well as the corresponding namespace URI, local name,
* and prefix when applicable.
* <br>To remove an attribute by local name and namespace URI, use the
* <code>removeAttributeNS</code> method.
*
* @param name The name of the attribute to remove.
* @exception DOMException
* NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
*/
public void removeAttribute(String name)
throws DOMException
{
_rmAttr(_getAttrIdx(null, name));
}
/**
* 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.
* <br>HTML-only DOM implementations do not need to implement this method.
*
* @param namespaceURI The namespace URI of the attribute to remove.
* @param localName The local name of the attribute to remove.
* @exception DOMException
* NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
*
* @since DOM Level 2
*/
public void removeAttributeNS(String namespaceURI, String localName)
throws DOMException
{
_rmAttr(_getAttrIdx(namespaceURI, localName));
}
/**
* Removes the specified attribute node. If the removed <code>Attr</code>
* 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, when applicable.
*
* @param oldAttr The <code>Attr</code> node to remove from the attribute
* list.
* @return The <code>Attr</code> node that was removed.
* @exception DOMException
* NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
* <br>NOT_FOUND_ERR: Raised if <code>oldAttr</code> is not an attribute
* of the element.
*/
public Attr removeAttributeNode(Attr oldAttr)
throws DOMException
{
XNode attr = (XNode)oldAttr;
int idx = _getAttrIdx(attr);
if (attr._getParent() != this || idx < 0)
throw new DOMException(DOMException.NOT_FOUND_ERR, "");
_rmAttr(idx);
return (Attr)attr;
}
/**
* Returns a <code>NodeList</code> of all descendant <code>Elements</code>
* with a given tag name, in the order in which they are encountered in
* a preorder traversal of this <code>Element</code> tree.
*
* @param name The name of the tag to match on. The special value "*"
* matches all tags.
* @return A list of matching <code>Element</code> nodes.
*/
public NodeList getElementsByTagName(String name)
{
ElmFilter filter = new ElmFilter(name);
super._procEachChild(filter);
return filter;
}
/**
* Returns a <code>NodeList</code> of all the descendant
* <code>Elements</code> with a given local name and namespace URI in
* the order in which they are encountered in a preorder traversal of
* this <code>Element</code> tree.
* <br>HTML-only DOM implementations do not need to implement this method.
*
* @param namespaceURI The namespace URI of the elements to match on. The
* special value "*" matches all namespaces.
* @param localName The local name of the elements to match on. The
* special value "*" matches all local names.
* @return A new <code>NodeList</code> object containing all the matched
* <code>Elements</code>.
*
* @since DOM Level 2
*/
public NodeList getElementsByTagNameNS(String namespaceURI, String localName)
{
ElmFilter filter = new ElmFilter(namespaceURI, localName);
super._procEachChild(filter);
return filter;
}
/**
* Retrieves the number of child elements.
*
* @return the current number of element nodes that are children
* of this element. <code>0</code> if this element has no child
* elements.
*/
public int getChildElementCount() {
return _countChildElm();
}
/**
* Retrieves the first child element.
*
* @return the first child element or null
*/
public Element getFirstElementChild() {
return _nextElm(null);
}
/**
* Retrieves the last child element.
*
* @return the last child element or null
*/
public Element getLastElementChild() {
return _prevElm(null);
}
/**
* Retrieves the next sibling element.
*
* @return the next sibling element or null
*/
public Element getNextElementSibling() {
XParent parent = _getParent();
return (parent != null) ? parent._nextElm(this) : null;
}
/**
* Retrieves the previous sibling element.
*
* @return the previous sibling element or null
*/
public Element getPreviousElementSibling() {
XParent parent = _getParent();
return (parent != null) ? parent._prevElm(this) : null;
}
/**
* Extends capacity of list of attributes.
*/
private final void _extAttlst()
{
String list[] = new String[attlst.length + (ATTR_ALLOC_UNIT << 2)];
System.arraycopy(attlst, 0, list, 0, attnum << 2);
attlst = list;
if (attobj != null) {
Reference objs[] = new WeakReference[attobj.length + ATTR_ALLOC_UNIT];
System.arraycopy(attobj, 0, objs, 0, attnum);
attobj = objs;
}
}
/**
* Returns total number of attributes.
*/
/* pkg */ final int _getAttrNum()
{
return attnum;
}
/**
* Returns attribute index. If namespace is not <code>null</code> or the
* empty string the name is local name otherwise the name is qualified name.
*
* @param namespace the namespace associated with attribute; <code>null</code>
* is used for non-namespace aware mode; the empty string is used to indicate
* that the attribute has no associated namespace.
* @param name attribute's local or qualified name.
*/
/* pkg */ final int _getAttrIdx(String namespace, String name)
{
if (namespace == null || namespace.length() == 0) {
for (int i = 0; i < attnum; i++) {
if (name.equals(attlst[i << 2]))
return i;
}
} else {
for (int i = 0; i < attnum; i++) {
if (name.equals(attlst[(i << 2) + 1]) == true &&
namespace.equals(attlst[(i << 2) + 2]) == true)
return i;
}
}
return -1;
}
/**
* Returns attribute index for an attribute node.
*/
/* pkg */ final int _getAttrIdx(XNode attr)
{
if (attr != null && attobj != null) {
for (int i = 0; i < attnum; i++) {
Reference ref = attobj[i];
if (ref != null && ref.get() == attr)
return i;
}
}
return -1;
}
/**
* Returns attribute object or <code>null</code> if index is negative.
*/
/* pkg */ final XNode _getAttrObj(int idx)
{
if (idx >= 0 && idx < attnum) {
if (attobj == null)
attobj = new WeakReference[attlst.length >> 2];
if (attobj[idx] == null || attobj[idx].get() == null) {
int base = idx << 2;
XNode attr = (XNode)((attlst[base + 2] != null)?
_getDoc().createAttributeNS(attlst[base + 2], attlst[base]):
_getDoc().createAttribute(attlst[base]));
attr._setValue(attlst[base + 3]);
attr._setParent(this);
attobj[idx] = new WeakReference(attr);
}
return (XNode)attobj[idx].get();
}
return null;
}
/**
* Removes attribute.
*/
private final XNode _rmAttr(int idx)
throws DOMException
{
if (_isRO())
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "");
if (idx < 0 || idx >= attnum)
return null;
String attName = attlst[idx << 2];
String attValue = attlst[(idx << 2) + 3];
if (idx == attid)
attid = -1;
XNode attr = null;
// Release attribute object
if (attobj != null && attobj[idx] != null) {
attr = (XNode)attobj[idx].get();
if (attr != null) {
attr._setParent(null);
attobj[idx].clear();
attobj[idx] = null;
}
}
if (_isDefAttr(idx)) {
// Restore default attribute value
String[] attrs = _getDefAttrs();
attlst[(idx << 2) + 3] = attrs[(idx << 2) + 3];
if ("xml:id".equals(attlst[idx << 2]))
attid = idx;
_attrChanged(attName, attValue, attlst[(idx << 2) + 3]);
} else {
if (idx < (attnum - 1)) {
System.arraycopy(
attlst, (idx + 1) << 2, attlst, idx << 2, (attnum - idx) << 2);
if (attobj != null)
System.arraycopy(attobj, idx + 1, attobj, idx, attnum - idx);
}
attnum -= 1;
_attrRemoved(attName, attValue);
}
return attr;
}
/**
* Sets attribute value or add an attribute and returns its index.
*
* @param namespace the namespace associated with attribute; <code>null</code>
* is used for non-namespace aware mode; the empty string is used to indicate
* that the attribute has no associated namespace.
* @param qname attribute's qualified name.
* @param value attribute's value.
*/
/* pkg */ final int _setAttr(String namespace, String qname, String value)
throws DOMException
{
if (_isRO())
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "");
XDoc doc = _getDoc();
// Check attribute name before add it.
String name = (namespace != null)? XNode._getLocalName(qname): qname;
int idx = _getAttrIdx(namespace, name);
if (idx < 0) {
if ("xml:id".equals(qname)) {
attid = attnum;
}
// Set new attribute.
if (attnum >= (attlst.length >> 2))
_extAttlst();
int base = attnum << 2;
attlst[base] = doc._intern(qname);
attlst[base + 1] = doc._intern(name);
attlst[base + 2] = doc._intern(namespace);
attlst[base + 3] = value;
idx = attnum;
attnum++;
_attrAdded(attlst[base], value);
} else {
int base = idx << 2;
if (!qname.equals(attlst[base]) || value != attlst[base + 3]) {
String oldValue = attlst[base + 3];
attlst[base + 3] = value;
if (!_isDefAttr(idx)) // preserve default attributes qname
attlst[base] = doc._intern(qname);
_attrChanged(attlst[base], oldValue, value);
}
}
return idx;
}
/**
* Sets attribute value and returns its index.
*/
/* pkg */ final int _setAttr(int idx, String value)
throws DOMException
{
if (_isRO())
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "");
if (idx < 0 || idx >= attnum)
return -1;
if (value != attlst[(idx << 2) + 3]) {
String oldValue = attlst[(idx << 2) + 3];
attlst[(idx << 2) + 3] = value;
_attrChanged(attlst[idx << 2], oldValue, value);
}
return idx;
}
/**
* Sets attribute value by the attribute index. Note, this method
* is here to allow parser to assign values to default attributes.
*/
/* pkg */ final void __setAttrV(int idx, String value)
{
if (idx < 0 || idx >= attnum)
throw new IndexOutOfBoundsException();
attlst[(idx << 2) + 3] = value;
}
/**
* Sets attribute namespace URI by the attribute index. Note, this method
* is here to allow parser to assign namespace URIs to default attributes.
*/
/* pkg */ final void __setAttrNS(int idx, String namespace)
{
if (idx < 0 || idx >= attnum)
throw new IndexOutOfBoundsException();
attlst[(idx << 2) + 2] = _getDoc()._intern(namespace);
}
/**
* Sets attribute qualified name. Note, this method is here to allow an
* attribute node to update its qualified name after setPrefix call.
*/
/* pkg */ final void __setAttrQN(int idx, String qname)
{
if (idx < 0 || idx >= attnum)
throw new IndexOutOfBoundsException();
attlst[idx << 2] = qname;
}
/**
* Updates attribute object if it exists.
*/
/* pkg */ final void _updateAttr(int idx)
{
if (idx >= 0 && idx < attnum && attobj != null && attobj[idx] != null) {
XNode attr = (XNode)attobj[idx].get();
if (attr != null) {
int base = idx << 2;
attr._set(attlst[base + 2], attlst[base], attlst[base + 1]);
attr._setValue(attlst[base + 3]);
}
}
}
/**
* Sets attribute to be identifier.
*/
private final void _setIdAttr(int idx, boolean isId)
{
if (_isRO())
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "");
if (idx < 0)
throw new DOMException(DOMException.NOT_FOUND_ERR, "");
// String value = attlst[(idx << 2) + 3];
if (isId == true) {
if (attid >= 0 && "xml:id".equals(attlst[attid << 2]))
return; // cannot remove id from 'xml:id' attribute
attid = idx;
} else {
if (idx != attid || "xml:id".equals(attlst[attid << 2]))
return; // cannot remove id from 'xml:id' attribute
attid = -1;
}
}
/**
* Returns <code>true</code> if attribute is identifier.
*/
/* pkg */ final boolean _isIdAttr(int idx)
{
return (idx == attid);
}
/**
* Returns <code>true</code> if attribute is default.
*/
/* pkg */ final boolean _isDefAttr(int idx)
{
return (idx >= 0 && idx < defnum);
}
/**
* Returns number of default attributes.
*/
/* pkg */ final int _getDefAttrsCount()
{
return defnum;
}
/**
* Returns default attributes for this element or <code>null</code>.
*/
/* pkg */ final String[] _getDefAttrs()
{
return _getDoc()._getDefAttrs(getNodeName());
}
/**
* Returns element with specified identifier or <code>null</code>.
*/
/* pkg */ final Element _getElmById(String elmId)
{
if (attid >= 0 && attlst[(attid << 2) + 3].equals(elmId))
return this;
Element elm = null;
for (int idx = 0; idx < getLength(); idx++) {
Node node = item(idx);
if (node.getNodeType() == ELEMENT_NODE) {
if ((elm = ((XElm)node)._getElmById(elmId)) != null)
break;
}
}
return elm;
}
/**
* Sets node's owner document.
*
* @param ownerDoc New owner document.
*/
protected void _setDoc(XDoc ownerDoc)
{
String[] old_attlst = attlst;
int old_attnum = attnum;
String[] old_deflst = _getDefAttrs();
int old_defnum = defnum;
Reference[] old_attobj = attobj;
// Detach all default attribute objects if any
if (old_attobj != null) {
for (int idx = 0; idx < old_defnum; idx++) {
if (old_attobj[idx] != null) {
XNode attr = (XNode)old_attobj[idx].get();
if (attr != null) {
if (attr.getNodeValue() != old_deflst[(idx << 2) + 3])
continue; // default value had been replaced
attr._setParent(null);
attr._clear();
old_attobj[idx].clear();
old_attobj[idx] = null;
}
}
}
}
// Release id if any
attid = -1; // ids from old doc are invalid
// Get default attributes for this element in the new document
String dattrs[] = ownerDoc._getDefAttrs(getNodeName());
attnum = (dattrs != null)? (dattrs.length >> 2): 0;
defnum = attnum;
// Allocate arrays for new set of attributes
attlst = new String[(defnum + ATTR_ALLOC_UNIT) << 2];
attobj = (old_attobj != null)?
new WeakReference[attlst.length >> 2]: null;
// Copy new default attributes
for (int idx = 0; idx < defnum; idx++) {
int base = idx << 2;
attlst[base] = dattrs[base];
attlst[base + 1] = dattrs[base + 1];
attlst[base + 2] = dattrs[base + 2];
attlst[base + 3] = dattrs[base + 3];
if ("xml:id".equals(attlst[base])) {
attid = idx;
}
}
// Copy attributes defined on this element
super._setDoc(ownerDoc); // new owner is used by _setAttr
for (int idx = 0; idx < old_attnum; idx++) {
int base = idx << 2;
// Skip default attributes with original value
if (idx < old_defnum && old_attlst[base + 3] == old_deflst[base + 3])
continue;
int nidx = _setAttr(
old_attlst[base + 2], old_attlst[base], old_attlst[base + 3]);
// Copy attribute objects
if (old_attobj != null && old_attobj[idx] != null) {
XNode attr = (XNode)old_attobj[idx].get();
if (attr != null) {
// Copy only defined attribute objects
attobj[nidx] = new WeakReference(attr);
attr._setDoc(ownerDoc);
}
}
}
}
/**
* Lets filter to process each child node.
*/
protected void _procEachChild(XList filter)
{
filter._proc(this);
super._procEachChild(filter);
}
/**
* Returns string representation of the element.
*/
public String toString()
{
StringBuffer out = new StringBuffer();
out.append("<");
out.append(getNodeName());
for (int idx = 0; idx < attnum; idx++) {
out.append(" ");
out.append(attlst[idx << 2]);
out.append("=\"");
out.append(attlst[(idx << 2) + 3]);
out.append("\"");
}
if (getLength() > 0) {
out.append(">");
out.append(super.toString());
out.append("</");
out.append(getNodeName());
out.append(">");
} else {
out.append("/>");
}
return out.toString();
}
}