286N/A/*
286N/A * reserved comment block
286N/A * DO NOT REMOVE OR ALTER!
286N/A */
286N/A/*
286N/A * Copyright 2000-2002,2004 The Apache Software Foundation.
286N/A *
286N/A * Licensed under the Apache License, Version 2.0 (the "License");
286N/A * you may not use this file except in compliance with the License.
286N/A * You may obtain a copy of the License at
286N/A *
286N/A * http://www.apache.org/licenses/LICENSE-2.0
286N/A *
286N/A * Unless required by applicable law or agreed to in writing, software
286N/A * distributed under the License is distributed on an "AS IS" BASIS,
286N/A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
286N/A * See the License for the specific language governing permissions and
286N/A * limitations under the License.
286N/A */
286N/A
286N/Apackage com.sun.org.apache.xerces.internal.dom;
286N/A
286N/Aimport java.util.ArrayList;
286N/Aimport java.util.List;
286N/A
286N/Aimport org.w3c.dom.DOMException;
286N/Aimport org.w3c.dom.Node;
286N/A
286N/A/**
286N/A * AttributeMap inherits from NamedNodeMapImpl and extends it to deal with the
286N/A * specifics of storing attributes. These are:
286N/A * <ul>
286N/A * <li>managing ownership of attribute nodes
286N/A * <li>managing default attributes
286N/A * <li>firing mutation events
286N/A * </ul>
286N/A * <p>
286N/A * This class doesn't directly support mutation events, however, it notifies
286N/A * the document when mutations are performed so that the document class do so.
286N/A *
286N/A * @xerces.internal
286N/A *
286N/A * @version $Id: AttributeMap.java,v 1.7 2010-11-01 04:39:37 joehw Exp $
286N/A */
286N/Apublic class AttributeMap extends NamedNodeMapImpl {
286N/A
286N/A /** Serialization version. */
286N/A static final long serialVersionUID = 8872606282138665383L;
286N/A
286N/A //
286N/A // Constructors
286N/A //
286N/A
286N/A /** Constructs a named node map. */
286N/A protected AttributeMap(ElementImpl ownerNode, NamedNodeMapImpl defaults) {
286N/A super(ownerNode);
286N/A if (defaults != null) {
286N/A // initialize map with the defaults
286N/A cloneContent(defaults);
286N/A if (nodes != null) {
286N/A hasDefaults(true);
286N/A }
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * Adds an attribute using its nodeName attribute.
286N/A * @see org.w3c.dom.NamedNodeMap#setNamedItem
286N/A * @return If the new Node replaces an existing node the replaced Node is
286N/A * returned, otherwise null is returned.
286N/A * @param arg
286N/A * An Attr node to store in this map.
286N/A * @exception org.w3c.dom.DOMException The exception description.
286N/A */
286N/A public Node setNamedItem(Node arg)
286N/A throws DOMException {
286N/A
286N/A boolean errCheck = ownerNode.ownerDocument().errorChecking;
286N/A if (errCheck) {
286N/A if (isReadOnly()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
286N/A throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
286N/A }
286N/A if (arg.getOwnerDocument() != ownerNode.ownerDocument()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
286N/A throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
286N/A }
286N/A if (arg.getNodeType() != Node.ATTRIBUTE_NODE) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
286N/A throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
286N/A }
286N/A }
286N/A AttrImpl argn = (AttrImpl)arg;
286N/A
286N/A if (argn.isOwned()){
286N/A if (errCheck && argn.getOwnerElement() != ownerNode) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INUSE_ATTRIBUTE_ERR", null);
286N/A throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, msg);
286N/A }
286N/A // replacing an Attribute with itself does nothing
286N/A return arg;
286N/A }
286N/A
286N/A
286N/A // set owner
286N/A argn.ownerNode = ownerNode;
286N/A argn.isOwned(true);
286N/A
286N/A int i = findNamePoint(argn.getNodeName(),0);
286N/A AttrImpl previous = null;
286N/A if (i >= 0) {
286N/A previous = (AttrImpl) nodes.get(i);
286N/A nodes.set(i, arg);
286N/A previous.ownerNode = ownerNode.ownerDocument();
286N/A previous.isOwned(false);
286N/A // make sure it won't be mistaken with defaults in case it's reused
286N/A previous.isSpecified(true);
286N/A } else {
286N/A i = -1 - i; // Insert point (may be end of list)
286N/A if (null == nodes) {
286N/A nodes = new ArrayList(5);
286N/A }
286N/A nodes.add(i, arg);
286N/A }
286N/A
286N/A // notify document
286N/A ownerNode.ownerDocument().setAttrNode(argn, previous);
286N/A
286N/A // If the new attribute is not normalized,
286N/A // the owning element is inherently not normalized.
286N/A if (!argn.isNormalized()) {
286N/A ownerNode.isNormalized(false);
286N/A }
286N/A return previous;
286N/A
286N/A } // setNamedItem(Node):Node
286N/A
286N/A /**
286N/A * Adds an attribute using its namespaceURI and localName.
286N/A * @see org.w3c.dom.NamedNodeMap#setNamedItem
286N/A * @return If the new Node replaces an existing node the replaced Node is
286N/A * returned, otherwise null is returned.
286N/A * @param arg A node to store in a named node map.
286N/A */
286N/A public Node setNamedItemNS(Node arg)
286N/A throws DOMException {
286N/A
286N/A boolean errCheck = ownerNode.ownerDocument().errorChecking;
286N/A if (errCheck) {
286N/A if (isReadOnly()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
286N/A throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
286N/A }
286N/A if(arg.getOwnerDocument() != ownerNode.ownerDocument()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
286N/A throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
286N/A }
286N/A if (arg.getNodeType() != Node.ATTRIBUTE_NODE) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
286N/A throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
286N/A }
286N/A }
286N/A AttrImpl argn = (AttrImpl)arg;
286N/A
286N/A if (argn.isOwned()){
286N/A if (errCheck && argn.getOwnerElement() != ownerNode) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INUSE_ATTRIBUTE_ERR", null);
286N/A throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, msg);
286N/A }
286N/A // replacing an Attribute with itself does nothing
286N/A return arg;
286N/A }
286N/A
286N/A // set owner
286N/A argn.ownerNode = ownerNode;
286N/A argn.isOwned(true);
286N/A
286N/A int i = findNamePoint(argn.getNamespaceURI(), argn.getLocalName());
286N/A AttrImpl previous = null;
286N/A if (i >= 0) {
286N/A previous = (AttrImpl) nodes.get(i);
286N/A nodes.set(i, arg);
286N/A previous.ownerNode = ownerNode.ownerDocument();
286N/A previous.isOwned(false);
286N/A // make sure it won't be mistaken with defaults in case it's reused
286N/A previous.isSpecified(true);
286N/A } else {
286N/A // If we can't find by namespaceURI, localName, then we find by
286N/A // nodeName so we know where to insert.
286N/A i = findNamePoint(arg.getNodeName(),0);
286N/A if (i >=0) {
286N/A previous = (AttrImpl) nodes.get(i);
286N/A nodes.add(i, arg);
286N/A } else {
286N/A i = -1 - i; // Insert point (may be end of list)
286N/A if (null == nodes) {
286N/A nodes = new ArrayList(5);
286N/A }
286N/A nodes.add(i, arg);
286N/A }
286N/A }
286N/A // changed(true);
286N/A
286N/A // notify document
286N/A ownerNode.ownerDocument().setAttrNode(argn, previous);
286N/A
286N/A // If the new attribute is not normalized,
286N/A // the owning element is inherently not normalized.
286N/A if (!argn.isNormalized()) {
286N/A ownerNode.isNormalized(false);
286N/A }
286N/A return previous;
286N/A
286N/A } // setNamedItemNS(Node):Node
286N/A
286N/A /**
286N/A * Removes an attribute specified by name.
286N/A * @param name
286N/A * The name of a node to remove. If the
286N/A * removed attribute is known to have a default value, an
286N/A * attribute immediately appears containing the default value
286N/A * as well as the corresponding namespace URI, local name,
286N/A * and prefix when applicable.
286N/A * @return The node removed from the map if a node with such a name exists.
286N/A * @throws NOT_FOUND_ERR: Raised if there is no node named
286N/A * name in the map.
286N/A */
286N/A /***/
286N/A public Node removeNamedItem(String name)
286N/A throws DOMException {
286N/A return internalRemoveNamedItem(name, true);
286N/A }
286N/A
286N/A /**
286N/A * Same as removeNamedItem except that it simply returns null if the
286N/A * specified name is not found.
286N/A */
286N/A Node safeRemoveNamedItem(String name) {
286N/A return internalRemoveNamedItem(name, false);
286N/A }
286N/A
286N/A
286N/A /**
286N/A * NON-DOM: Remove the node object
286N/A *
286N/A * NOTE: Specifically removes THIS NODE -- not the node with this
286N/A * name, nor the node with these contents. If node does not belong to
286N/A * this named node map, we throw a DOMException.
286N/A *
286N/A * @param item The node to remove
286N/A * @param addDefault true -- magically add default attribute
286N/A * @return Removed node
286N/A * @exception DOMException
286N/A */
286N/A protected Node removeItem(Node item, boolean addDefault)
286N/A throws DOMException {
286N/A
286N/A int index = -1;
286N/A if (nodes != null) {
286N/A final int size = nodes.size();
286N/A for (int i = 0; i < size; ++i) {
286N/A if (nodes.get(i) == item) {
286N/A index = i;
286N/A break;
286N/A }
286N/A }
286N/A }
286N/A if (index < 0) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
286N/A throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
286N/A }
286N/A
286N/A return remove((AttrImpl)item, index, addDefault);
286N/A }
286N/A
286N/A /**
286N/A * Internal removeNamedItem method allowing to specify whether an exception
286N/A * must be thrown if the specified name is not found.
286N/A */
286N/A final protected Node internalRemoveNamedItem(String name, boolean raiseEx){
286N/A if (isReadOnly()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
286N/A throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
286N/A }
286N/A int i = findNamePoint(name,0);
286N/A if (i < 0) {
286N/A if (raiseEx) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
286N/A throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
286N/A } else {
286N/A return null;
286N/A }
286N/A }
286N/A
286N/A return remove((AttrImpl)nodes.get(i), i, true);
286N/A
286N/A } // internalRemoveNamedItem(String,boolean):Node
286N/A
286N/A private final Node remove(AttrImpl attr, int index,
286N/A boolean addDefault) {
286N/A
286N/A CoreDocumentImpl ownerDocument = ownerNode.ownerDocument();
286N/A String name = attr.getNodeName();
286N/A if (attr.isIdAttribute()) {
286N/A ownerDocument.removeIdentifier(attr.getValue());
286N/A }
286N/A
286N/A if (hasDefaults() && addDefault) {
286N/A // If there's a default, add it instead
286N/A NamedNodeMapImpl defaults =
286N/A ((ElementImpl) ownerNode).getDefaultAttributes();
286N/A
286N/A Node d;
286N/A if (defaults != null &&
286N/A (d = defaults.getNamedItem(name)) != null &&
286N/A findNamePoint(name, index+1) < 0) {
286N/A NodeImpl clone = (NodeImpl)d.cloneNode(true);
286N/A if (d.getLocalName() !=null){
286N/A // we must rely on the name to find a default attribute
286N/A // ("test:attr"), but while copying it from the DOCTYPE
286N/A // we should not loose namespace URI that was assigned
286N/A // to the attribute in the instance document.
286N/A ((AttrNSImpl)clone).namespaceURI = attr.getNamespaceURI();
286N/A }
286N/A clone.ownerNode = ownerNode;
286N/A clone.isOwned(true);
286N/A clone.isSpecified(false);
286N/A
286N/A nodes.set(index, clone);
286N/A if (attr.isIdAttribute()) {
286N/A ownerDocument.putIdentifier(clone.getNodeValue(),
286N/A (ElementImpl)ownerNode);
286N/A }
286N/A } else {
286N/A nodes.remove(index);
286N/A }
286N/A } else {
286N/A nodes.remove(index);
286N/A }
286N/A
286N/A // changed(true);
286N/A
286N/A // remove reference to owner
286N/A attr.ownerNode = ownerDocument;
286N/A attr.isOwned(false);
286N/A
286N/A // make sure it won't be mistaken with defaults in case it's
286N/A // reused
286N/A attr.isSpecified(true);
286N/A attr.isIdAttribute(false);
286N/A
286N/A // notify document
286N/A ownerDocument.removedAttrNode(attr, ownerNode, name);
286N/A
286N/A return attr;
286N/A }
286N/A
286N/A /**
286N/A * Introduced in DOM Level 2. <p>
286N/A * Removes an attribute specified by local name and namespace URI.
286N/A * @param namespaceURI
286N/A * The namespace URI of the node to remove.
286N/A * When it is null or an empty string, this
286N/A * method behaves like removeNamedItem.
286N/A * @param name The local name of the node to remove. If the
286N/A * removed attribute is known to have a default
286N/A * value, an attribute immediately appears
286N/A * containing the default value.
286N/A * @return Node The node removed from the map if a node with such
286N/A * a local name and namespace URI exists.
286N/A * @throws NOT_FOUND_ERR: Raised if there is no node named
286N/A * name in the map.
286N/A */
286N/A public Node removeNamedItemNS(String namespaceURI, String name)
286N/A throws DOMException {
286N/A return internalRemoveNamedItemNS(namespaceURI, name, true);
286N/A }
286N/A
286N/A /**
286N/A * Same as removeNamedItem except that it simply returns null if the
286N/A * specified local name and namespace URI is not found.
286N/A */
286N/A Node safeRemoveNamedItemNS(String namespaceURI, String name) {
286N/A return internalRemoveNamedItemNS(namespaceURI, name, false);
286N/A }
286N/A
286N/A /**
286N/A * Internal removeNamedItemNS method allowing to specify whether an
286N/A * exception must be thrown if the specified local name and namespace URI
286N/A * is not found.
286N/A */
286N/A final protected Node internalRemoveNamedItemNS(String namespaceURI,
286N/A String name,
286N/A boolean raiseEx) {
286N/A
286N/A CoreDocumentImpl ownerDocument = ownerNode.ownerDocument();
286N/A if (ownerDocument.errorChecking && isReadOnly()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
286N/A throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
286N/A }
286N/A int i = findNamePoint(namespaceURI, name);
286N/A if (i < 0) {
286N/A if (raiseEx) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
286N/A throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
286N/A } else {
286N/A return null;
286N/A }
286N/A }
286N/A
286N/A AttrImpl n = (AttrImpl)nodes.get(i);
286N/A
286N/A if (n.isIdAttribute()) {
286N/A ownerDocument.removeIdentifier(n.getValue());
286N/A }
286N/A // If there's a default, add it instead
286N/A String nodeName = n.getNodeName();
286N/A if (hasDefaults()) {
286N/A NamedNodeMapImpl defaults = ((ElementImpl) ownerNode).getDefaultAttributes();
286N/A Node d;
286N/A if (defaults != null
286N/A && (d = defaults.getNamedItem(nodeName)) != null)
286N/A {
286N/A int j = findNamePoint(nodeName,0);
286N/A if (j>=0 && findNamePoint(nodeName, j+1) < 0) {
286N/A NodeImpl clone = (NodeImpl)d.cloneNode(true);
286N/A clone.ownerNode = ownerNode;
286N/A if (d.getLocalName() != null) {
286N/A // we must rely on the name to find a default attribute
286N/A // ("test:attr"), but while copying it from the DOCTYPE
286N/A // we should not loose namespace URI that was assigned
286N/A // to the attribute in the instance document.
286N/A ((AttrNSImpl)clone).namespaceURI = namespaceURI;
286N/A }
286N/A clone.isOwned(true);
286N/A clone.isSpecified(false);
286N/A nodes.set(i, clone);
286N/A if (clone.isIdAttribute()) {
286N/A ownerDocument.putIdentifier(clone.getNodeValue(),
286N/A (ElementImpl)ownerNode);
286N/A }
286N/A } else {
286N/A nodes.remove(i);
286N/A }
286N/A } else {
286N/A nodes.remove(i);
286N/A }
286N/A } else {
286N/A nodes.remove(i);
286N/A }
286N/A
286N/A // changed(true);
286N/A
286N/A // remove reference to owner
286N/A n.ownerNode = ownerDocument;
286N/A n.isOwned(false);
286N/A // make sure it won't be mistaken with defaults in case it's
286N/A // reused
286N/A n.isSpecified(true);
286N/A // update id table if needed
286N/A n.isIdAttribute(false);
286N/A
286N/A // notify document
286N/A ownerDocument.removedAttrNode(n, ownerNode, name);
286N/A
286N/A return n;
286N/A
286N/A } // internalRemoveNamedItemNS(String,String,boolean):Node
286N/A
286N/A //
286N/A // Public methods
286N/A //
286N/A
286N/A /**
286N/A * Cloning a NamedNodeMap is a DEEP OPERATION; it always clones
286N/A * all the nodes contained in the map.
286N/A */
286N/A
286N/A public NamedNodeMapImpl cloneMap(NodeImpl ownerNode) {
286N/A AttributeMap newmap =
286N/A new AttributeMap((ElementImpl) ownerNode, null);
286N/A newmap.hasDefaults(hasDefaults());
286N/A newmap.cloneContent(this);
286N/A return newmap;
286N/A } // cloneMap():AttributeMap
286N/A
286N/A /**
286N/A * Override parent's method to set the ownerNode correctly
286N/A */
286N/A protected void cloneContent(NamedNodeMapImpl srcmap) {
286N/A List srcnodes = srcmap.nodes;
286N/A if (srcnodes != null) {
286N/A int size = srcnodes.size();
286N/A if (size != 0) {
286N/A if (nodes == null) {
286N/A nodes = new ArrayList(size);
286N/A }
286N/A else {
286N/A nodes.clear();
286N/A }
286N/A for (int i = 0; i < size; ++i) {
286N/A NodeImpl n = (NodeImpl) srcnodes.get(i);
286N/A NodeImpl clone = (NodeImpl) n.cloneNode(true);
286N/A clone.isSpecified(n.isSpecified());
286N/A nodes.add(clone);
286N/A clone.ownerNode = ownerNode;
286N/A clone.isOwned(true);
286N/A }
286N/A }
286N/A }
286N/A } // cloneContent():AttributeMap
286N/A
286N/A
286N/A /**
286N/A * Move specified attributes from the given map to this one
286N/A */
286N/A void moveSpecifiedAttributes(AttributeMap srcmap) {
286N/A int nsize = (srcmap.nodes != null) ? srcmap.nodes.size() : 0;
286N/A for (int i = nsize - 1; i >= 0; i--) {
286N/A AttrImpl attr = (AttrImpl) srcmap.nodes.get(i);
286N/A if (attr.isSpecified()) {
286N/A srcmap.remove(attr, i, false);
286N/A if (attr.getLocalName() != null) {
286N/A setNamedItem(attr);
286N/A }
286N/A else {
286N/A setNamedItemNS(attr);
286N/A }
286N/A }
286N/A }
286N/A } // moveSpecifiedAttributes(AttributeMap):void
286N/A
286N/A
286N/A /**
286N/A * Get this AttributeMap in sync with the given "defaults" map.
286N/A * @param defaults The default attributes map to sync with.
286N/A */
286N/A protected void reconcileDefaults(NamedNodeMapImpl defaults) {
286N/A
286N/A // remove any existing default
286N/A int nsize = (nodes != null) ? nodes.size() : 0;
286N/A for (int i = nsize - 1; i >= 0; --i) {
286N/A AttrImpl attr = (AttrImpl) nodes.get(i);
286N/A if (!attr.isSpecified()) {
286N/A remove(attr, i, false);
286N/A }
286N/A }
286N/A // add the new defaults
286N/A if (defaults == null) {
286N/A return;
286N/A }
286N/A if (nodes == null || nodes.size() == 0) {
286N/A cloneContent(defaults);
286N/A }
286N/A else {
286N/A int dsize = defaults.nodes.size();
286N/A for (int n = 0; n < dsize; ++n) {
286N/A AttrImpl d = (AttrImpl) defaults.nodes.get(n);
286N/A int i = findNamePoint(d.getNodeName(), 0);
286N/A if (i < 0) {
286N/A i = -1 - i;
286N/A NodeImpl clone = (NodeImpl) d.cloneNode(true);
286N/A clone.ownerNode = ownerNode;
286N/A clone.isOwned(true);
286N/A clone.isSpecified(false);
286N/A nodes.add(i, clone);
286N/A }
286N/A }
286N/A }
286N/A
286N/A } // reconcileDefaults()
286N/A
286N/A protected final int addItem (Node arg) {
286N/A
286N/A final AttrImpl argn = (AttrImpl) arg;
286N/A
286N/A // set owner
286N/A argn.ownerNode = ownerNode;
286N/A argn.isOwned(true);
286N/A
286N/A int i = findNamePoint(argn.getNamespaceURI(), argn.getLocalName());
286N/A if (i >= 0) {
286N/A nodes.set(i, arg);
286N/A }
286N/A else {
286N/A // If we can't find by namespaceURI, localName, then we find by
286N/A // nodeName so we know where to insert.
286N/A i = findNamePoint(argn.getNodeName(),0);
286N/A if (i >= 0) {
286N/A nodes.add(i, arg);
286N/A }
286N/A else {
286N/A i = -1 - i; // Insert point (may be end of list)
286N/A if (null == nodes) {
286N/A nodes = new ArrayList(5);
286N/A }
286N/A nodes.add(i, arg);
286N/A }
286N/A }
286N/A
286N/A // notify document
286N/A ownerNode.ownerDocument().setAttrNode(argn, null);
286N/A return i;
286N/A }
286N/A
286N/A} // class AttributeMap