| /* DomNamedNodeMap.java -- | |
| Copyright (C) 1999,2000,2001,2004 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath 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 for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.xml.dom; | |
| import org.w3c.dom.DOMException; | |
| import org.w3c.dom.NamedNodeMap; | |
| import org.w3c.dom.Node; | |
| /** | |
| * <p> "NamedNodeMap" implementation. </p> | |
| * Used mostly to hold element attributes, but sometimes also | |
| * to list notations or entities. | |
| * | |
| * @author David Brownell | |
| * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> | |
| */ | |
| public class DomNamedNodeMap | |
| implements NamedNodeMap | |
| { | |
| final DomNode owner; | |
| final short type; | |
| DomNode first; | |
| int length; | |
| boolean readonly; | |
| // package private | |
| DomNamedNodeMap(DomNode owner, short type) | |
| { | |
| this.owner = owner; | |
| this.type = type; | |
| } | |
| /** | |
| * Exposes the internal "readonly" flag. In DOM, all NamedNodeMap | |
| * objects found in a DocumentType object are read-only (after | |
| * they are fully constructed), and those holding attributes of | |
| * a readonly element will also be readonly. | |
| */ | |
| public final boolean isReadonly() | |
| { | |
| return readonly; | |
| } | |
| /** | |
| * Sets the internal "readonly" flag so the node and its | |
| * children can't be changed. | |
| */ | |
| public void makeReadonly() | |
| { | |
| readonly = true; | |
| for (DomNode ctx = first; ctx != null; ctx = ctx.next) | |
| { | |
| ctx.makeReadonly(); | |
| } | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Returns the named item from the map, or null; names are just | |
| * the nodeName property. | |
| */ | |
| public Node getNamedItem(String name) | |
| { | |
| for (DomNode ctx = first; ctx != null; ctx = ctx.next) | |
| { | |
| if (ctx.getNodeName().equals(name)) | |
| { | |
| return ctx; | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * <b>DOM L2</b> | |
| * Returns the named item from the map, or null; names are the | |
| * localName and namespaceURI properties, ignoring any prefix. | |
| */ | |
| public Node getNamedItemNS(String namespaceURI, String localName) | |
| { | |
| if ("".equals(namespaceURI)) | |
| { | |
| namespaceURI = null; | |
| } | |
| for (DomNode ctx = first; ctx != null; ctx = ctx.next) | |
| { | |
| String name = ctx.getLocalName(); | |
| if ((localName == null && name == null) || | |
| (localName != null && localName.equals(name))) | |
| { | |
| String uri = ctx.getNamespaceURI(); | |
| if ("".equals(uri)) | |
| { | |
| uri = null; | |
| } | |
| if ((namespaceURI == null && uri == null) || | |
| (namespaceURI != null && namespaceURI.equals(uri))) | |
| { | |
| return ctx; | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Stores the named item into the map, optionally overwriting | |
| * any existing node with that name. The name used is just | |
| * the nodeName attribute. | |
| */ | |
| public Node setNamedItem(Node arg) | |
| { | |
| return setNamedItem(arg, false); | |
| } | |
| /** | |
| * <b>DOM L2</b> | |
| * Stores the named item into the map, optionally overwriting | |
| * any existing node with that fully qualified name. The name | |
| * used incorporates the localName and namespaceURI properties, | |
| * and ignores any prefix. | |
| */ | |
| public Node setNamedItemNS(Node arg) | |
| { | |
| return setNamedItem(arg, true); | |
| } | |
| Node setNamedItem(Node arg, boolean ns) | |
| { | |
| if (readonly) | |
| { | |
| throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR); | |
| } | |
| DomNode node = (DomNode) arg; | |
| if (node.owner != owner.owner) | |
| { | |
| throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR); | |
| } | |
| if (node.nodeType != type) | |
| { | |
| throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR); | |
| } | |
| if (node.nodeType == Node.ATTRIBUTE_NODE) | |
| { | |
| DomNode element = node.parent; | |
| if (element != null && element != owner) | |
| { | |
| throw new DomDOMException(DOMException.INUSE_ATTRIBUTE_ERR); | |
| } | |
| node.parent = owner; | |
| node.depth = owner.depth + 1; | |
| } | |
| String nodeName = node.getNodeName(); | |
| String localName = ns ? node.getLocalName() : null; | |
| String namespaceURI = ns ? node.getNamespaceURI() : null; | |
| if ("".equals(namespaceURI)) | |
| { | |
| namespaceURI = null; | |
| } | |
| // maybe attribute ADDITION events (?) | |
| DomNode last = null; | |
| for (DomNode ctx = first; ctx != null; ctx = ctx.next) | |
| { | |
| boolean test = false; | |
| if (ns) | |
| { | |
| String tln = ctx.getLocalName(); | |
| if (tln == null) | |
| { | |
| tln = ctx.getNodeName(); | |
| } | |
| if (tln.equals(localName)) | |
| { | |
| String tu = ctx.getNamespaceURI(); | |
| if ((tu == null && namespaceURI == null) || | |
| (tu != null && tu.equals(namespaceURI))) | |
| { | |
| test = true; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| test = ctx.getNodeName().equals(nodeName); | |
| } | |
| if (test) | |
| { | |
| // replace | |
| node.previous = ctx.previous; | |
| node.next = ctx.next; | |
| if (ctx.previous != null) | |
| { | |
| ctx.previous.next = node; | |
| } | |
| if (ctx.next != null) | |
| { | |
| ctx.next.previous = node; | |
| } | |
| if (first == ctx) | |
| { | |
| first = node; | |
| } | |
| reparent(node, nodeName, ctx.index); | |
| ctx.parent = null; | |
| ctx.next = null; | |
| ctx.previous = null; | |
| ctx.setDepth(0); | |
| ctx.index = 0; | |
| return ctx; | |
| } | |
| last = ctx; | |
| } | |
| // append | |
| if (last != null) | |
| { | |
| last.next = node; | |
| node.previous = last; | |
| } | |
| else | |
| { | |
| first = node; | |
| } | |
| length++; | |
| reparent(node, nodeName, 0); | |
| return null; | |
| } | |
| void reparent(DomNode node, String nodeName, int i) | |
| { | |
| node.parent = owner; | |
| node.setDepth(owner.depth + 1); | |
| // index renumbering | |
| for (DomNode ctx = node; ctx != null; ctx = ctx.next) | |
| { | |
| ctx.index = i++; | |
| } | |
| // cache xml:space | |
| boolean xmlSpace = "xml:space".equals(nodeName); | |
| if (xmlSpace && owner instanceof DomElement) | |
| { | |
| ((DomElement) owner).xmlSpace = node.getNodeValue(); | |
| } | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Removes the named item from the map, or reports an exception; | |
| * names are just the nodeName property. | |
| */ | |
| public Node removeNamedItem(String name) | |
| { | |
| return removeNamedItem(null, name, false); | |
| } | |
| /** | |
| * <b>DOM L2</b> | |
| * Removes the named item from the map, or reports an exception; | |
| * names are the localName and namespaceURI properties. | |
| */ | |
| public Node removeNamedItemNS(String namespaceURI, String localName) | |
| { | |
| return removeNamedItem(namespaceURI, localName, true); | |
| } | |
| Node removeNamedItem(String uri, String name, boolean ns) | |
| { | |
| if (readonly) | |
| { | |
| throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR); | |
| } | |
| // report attribute REMOVAL event? | |
| for (DomNode ctx = first; ctx != null; ctx = ctx.next) | |
| { | |
| boolean test = false; | |
| String nodeName = ctx.getNodeName(); | |
| if (ns) | |
| { | |
| String tln = ctx.getLocalName(); | |
| if (name != null && name.equals(tln)) | |
| { | |
| String tu = ctx.getNamespaceURI(); | |
| if ((tu == null && uri == null) || | |
| (tu != null && tu.equals(uri))) | |
| { | |
| test = true; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| test = nodeName.equals(name); | |
| } | |
| if (test) | |
| { | |
| // uncache xml:space | |
| boolean xmlSpace = "xml:space".equals(nodeName); | |
| if (xmlSpace && owner instanceof DomElement) | |
| { | |
| ((DomElement) owner).xmlSpace = ""; | |
| } | |
| // is this a default attribute? | |
| if (ctx.nodeType == Node.ATTRIBUTE_NODE) | |
| { | |
| String def = getDefaultValue(ctx.getNodeName()); | |
| if (def != null) | |
| { | |
| ctx.setNodeValue(def); | |
| ((DomAttr) ctx).setSpecified(false); | |
| return null; | |
| } | |
| } | |
| // remove | |
| if (ctx == first) | |
| { | |
| first = ctx.next; | |
| } | |
| if (ctx.previous != null) | |
| { | |
| ctx.previous.next = ctx.next; | |
| } | |
| if (ctx.next != null) | |
| { | |
| ctx.next.previous = ctx.previous; | |
| } | |
| length--; | |
| ctx.previous = null; | |
| ctx.next = null; | |
| ctx.parent = null; | |
| ctx.setDepth(0); | |
| ctx.index = 0; | |
| return ctx; | |
| } | |
| } | |
| throw new DomDOMException(DOMException.NOT_FOUND_ERR); | |
| } | |
| String getDefaultValue(String name) | |
| { | |
| DomDoctype doctype = (DomDoctype) owner.owner.getDoctype(); | |
| if (doctype == null) | |
| { | |
| return null; | |
| } | |
| DTDAttributeTypeInfo info = | |
| doctype.getAttributeTypeInfo(owner.getNodeName(), name); | |
| if (info == null) | |
| { | |
| return null; | |
| } | |
| return info.value; | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Returns the indexed item from the map, or null. | |
| */ | |
| public Node item(int index) | |
| { | |
| DomNode ctx = first; | |
| int count = 0; | |
| while (ctx != null && count < index) | |
| { | |
| ctx = ctx.next; | |
| count++; | |
| } | |
| return ctx; | |
| } | |
| /** | |
| * <b>DOM L1</b> | |
| * Returns the length of the map. | |
| */ | |
| public int getLength() | |
| { | |
| return length; | |
| } | |
| } |