package org.dbxml.xml.dom;

/*
 * dbXML License, Version 1.0
 *
 *
 * Copyright (c) 1999-2001 The dbXML Group, L.L.C.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by
 *        The dbXML Group (http://www.dbxml.com/)."
 *    Alternately, this acknowledgment may appear in the software
 *    itself, if and wherever such third-party acknowledgments normally
 *    appear.
 *
 * 4. The names "dbXML" and "The dbXML Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    info@dbxml.com.
 *
 * 5. Products derived from this software may not be called "dbXML",
 *    nor may "dbXML" appear in their name, without prior written
 *    permission of The dbXML Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE DBXML GROUP OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: NodeImpl.java,v 1.25 2001/08/05 07:23:17 tom Exp $
 */

import org.dbxml.core.*;
import org.dbxml.xml.*;
import org.dbxml.server.util.*;

import org.w3c.dom.*;
import java.io.*;
import java.util.*;

/**
 * NodeImpl implements the foundation of the dbXML compressed DOM.
 */

public abstract class NodeImpl implements CompressedNode, DBNode {
   public static final String XMLNS_PREFIX = "xmlns";

   public static final String OBJECT_NS = "http://www.dbxml.org/XMLObject";
   public static final String OBJECT_HREF = "href";
   public static final String OBJECT_TYPE = "type";

   public static final String TYPE_CONTENT = "content";
   public static final String TYPE_REPLACE = "replace";
   public static final String TYPE_INSERT = "insert";
   public static final String TYPE_APPEND = "append";
   
   // Static Exception Instances
   public static final DOMException EX_NO_MODIFICATION_ALLOWED =
      new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "This Node Is Read-Only");

   public static final DOMException EX_INUSE_ATTRIBUTE =
      new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, "This Attribute Belongs To Another Element");

   public static final DOMException EX_WRONG_DOCUMENT =
      new DOMException(DOMException.WRONG_DOCUMENT_ERR, "This Attribute Belongs To Another Document");

   public static final DOMException EX_NOT_FOUND =
      new DOMException(DOMException.NOT_FOUND_ERR, "This Node Does Not Belong To This Element");

   public static final DOMException EX_HIERARCHY_REQUEST =
      new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "This Node Cannot Contain This Child");

   public static final DOMException EX_NO_DATA_ALLOWED =
      new DOMException(DOMException.NO_DATA_ALLOWED_ERR, "This Node Has No Value");

   public static final DOMException EX_INVALID_STATE =
      new DOMException(DOMException.INVALID_STATE_ERR, "NodeIterator Has Been Detached");

   public static final DOMException EX_DOMSTRING_SIZE =
      new DOMException(DOMException.DOMSTRING_SIZE_ERR, "String Too Large For Type");

   public static final DOMException EX_INDEX_SIZE =
      new DOMException(DOMException.INDEX_SIZE_ERR, "Index Out Of Bounds");

   // Serialization Stuff
   protected NodeSource source = null;
   protected byte[] data = null;
   protected int pos = 0;
   protected int len = 0;

   protected boolean loaded = false; // Have the definitions for this node been loaded?
   protected boolean dirty = false;  // Has this node been modified?

   // DOM Level 1 Stuff
   protected NodeImpl parentNode = null;
   protected String nodeName = null;
   protected String nodeValue = null;

   // DOM Level 2 Stuff
   protected String nsURI = null;    // Just for caching so we don't have to walk the tree

   // DOM Level 3 Stuff
   protected HashMap userData = null;
   protected Object key = null;

   public NodeImpl() {
   }
   
   public NodeImpl(byte[] data, int pos, int len) {
      this.data = data;
      this.pos = pos;
      this.len = len;
   }

   public NodeImpl(NodeImpl parentNode, byte[] data, int pos, int len) {
      this(data, pos, len);
      this.parentNode = parentNode;
   }

   public NodeImpl(NodeImpl parentNode, boolean dirty) {
      this.parentNode = parentNode;
      if ( dirty )
         setDirty();
   }

   public final boolean isLoaded() {
      return loaded;
   }

   protected void checkLoaded() {
      if ( loaded )
         return;
      else
         loaded = true;
   }

   public void load() {
      checkLoaded();
   }

   public void unload() {
      if ( loaded )
         loaded = false;
   }

   public short getSymbolID() {
      return -1;
   }

   public final void checkReadOnly() throws DOMException {
      DocumentImpl doc = (DocumentImpl)getOwnerDocument();
      if ( doc != null && doc.isReadOnly() )
         throw EX_NO_MODIFICATION_ALLOWED;
   }

   public final boolean isDefined() {
      return data != null;
   }

   public final boolean isDirty() {
      return dirty;
   }

   public final void setDirty() {
      dirty = true;
      if ( parentNode != null )
         parentNode.setDirty();
   }

   public byte[] getDataBytes() {
      return data;
   }

   public void setDataBytes(byte[] data, int pos, int len) {
      this.data = data;
      this.pos = pos;
      this.len = len;
   }

   public void setDataBytes(byte[] data) {
      this.data = data;
   }

   public int getDataPos() {
      return pos;
   }

   public void setDataPos(int pos) {
      this.pos = pos;
   }

   public int getDataLen() {
      return len;
   }

   public void setDataLen(int len) {
      this.len = len;
   }

   public final void setNodeName(String nodeName) {
      checkLoaded();
      this.nodeName = nodeName;
      setDirty();
   }

   public final void setParentNode(NodeImpl parentNode) {
      this.parentNode = parentNode;
   }

   protected Node getPreviousSibling(Node node) {
      return null;
   }

   protected Node getNextSibling(Node node) {
      return null;
   }

   public final void setSource(NodeSource source) {
      this.source = source;
   }

   public final NodeSource getSource() {
      if ( source == null && parentNode != null )
         return parentNode.getSource();
      else
         return source;
   }
   
   public void expandSource() {
   }

   // Some DOM Level 1 Core Methods

   /**
    * A code representing the type of the underlying object, as defined above.
    */
   public abstract short getNodeType();

   /**
    * The last child of this node. If there is no such node, this returns
    * <code>null</code>.
    */
   public Node getLastChild() {
      return null;
   }

   /**
    * The node immediately preceding this node. If there is no such node, this
    * returns <code>null</code>.
    */
   public final Node getPreviousSibling() {
      return parentNode != null ? parentNode.getPreviousSibling(this)
                                : null;
   }

   /**
    * The node immediately following this node. If there is no such node, this
    * returns <code>null</code>.
    */
   public final Node getNextSibling() {
      return parentNode != null ? parentNode.getNextSibling(this)
                                : null;
   }

   /**
    * 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 null;
   }

   public boolean hasAttributes() {
      return false;
   }

   /**
    * The <code>Document</code> object associated with this node. This is also
    * the <code>Document</code> object used to create new nodes. When this
    * node is a <code>Document</code> this is <code>null</code>.
    */
   public final Document getOwnerDocument() {
      if ( getNodeType() == Node.DOCUMENT_NODE )
         return (Document)this;
      return parentNode != null ? parentNode.getOwnerDocument()
                                : null;
   }

   /**
    * Inserts the node <code>newChild</code> before the existing child node
    * <code>refChild</code>. If <code>refChild</code> is <code>null</code>,
    * insert <code>newChild</code> at the end of the list of children.
    * <br>If <code>newChild</code> is a <code>DocumentFragment</code> object,
    * all of its children are inserted, in the same order, before
    * <code>refChild</code>. If the <code>newChild</code> is already in the
    * tree, it is first removed.
    * @param newChild The node to insert.
    * @param refChild The reference node, i.e., the node before which the new
    *   node must be inserted.
    * @return The node being inserted.
    * @exception DOMException
    *   HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
    *   allow children of the type of the <code>newChild</code> node, or if
    *   the node to insert is one of this node's ancestors.
    *   <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
    *   from a different document than the one that created this node.
    *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
    *   <br>NOT_FOUND_ERR: Raised if <code>refChild</code> is not a child of
    *   this node.
    */
   public Node insertBefore(Node newChild, Node refChild) throws DOMException {
      throw EX_HIERARCHY_REQUEST;
   }

   /**
    * A <code>NodeList</code> that contains all children of this node. If there
    * are no children, this is a <code>NodeList</code> containing no nodes.
    * The content of the returned <code>NodeList</code> is "live" in the sense
    * that, for instance, changes to the children of the node object that
    * it was created from are immediately reflected in the nodes returned by
    * the <code>NodeList</code> accessors; it is not a static snapshot of the
    * content of the node. This is true for every <code>NodeList</code>,
    * including the ones returned by the <code>getElementsByTagName</code>
    * method.
    */
   public NodeList getChildNodes() {
      return new NodeListImpl(this);
   }

    /**
     *  The value of this node, depending on its type; see the table above.
     * When it is defined to be <code>null</code> , setting it has no effect.
     * @exception DOMException
     *    NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
     * @exception DOMException
     *    DOMSTRING_SIZE_ERR: Raised when it would return more characters
     *   than fit in a <code>DOMString</code> variable on the implementation
     *   platform.
     */
   public void setNodeValue(String nodeValue) throws DOMException {
      throw EX_NO_DATA_ALLOWED;
   }

   /**
    * The parent of this node. All nodes, except <code>Document</code>,
    * <code>DocumentFragment</code>, and <code>Attr</code> may have a parent.
    * However, if a node has just been created and not yet added to the tree,
    * or if it has been removed from the tree, this is <code>null</code>.
    */
   public final Node getParentNode() {
      return parentNode;
   }

   /**
    * The first child of this node. If there is no such node, this returns
    * <code>null</code>.
    */
   public Node getFirstChild() {
      return null;
   }

   /**
    * The name of this node, depending on its type; see the table above.
    */
   public String getNodeName() {
      checkLoaded();
      return nodeName;
   }

   /**
    * The value of this node, depending on its type; see the table above.
    * @exception DOMException
    *   NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
    * @exception DOMException
    *   DOMSTRING_SIZE_ERR: Raised when it would return more characters than
    *   fit in a <code>DOMString</code> variable on the implementation
    *   platform.
    */
   public String getNodeValue() throws DOMException {
      checkLoaded();
      return nodeValue;
   }

   /**
    * Replaces the child node <code>oldChild</code> with <code>newChild</code>
    * in the list of children, and returns the <code>oldChild</code> node. If
    * the <code>newChild</code> is already in the tree, it is first removed.
    * @param newChild The new node to put in the child list.
    * @param oldChild The node being replaced in the list.
    * @return The node replaced.
    * @exception DOMException
    *   HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
    *   allow children of the type of the <code>newChild</code> node, or it
    *   the node to put in is one of this node's ancestors.
    *   <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
    *   from a different document than the one that created this node.
    *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
    *   <br>NOT_FOUND_ERR: Raised if <code>oldChild</code> is not a child of
    *   this node.
    */
   public synchronized Node replaceChild(Node newChild, Node oldChild) throws DOMException {
      throw EX_HIERARCHY_REQUEST;
   }

   /**
    * Removes the child node indicated by <code>oldChild</code> from the list
    * of children, and returns it.
    * @param oldChild The node being removed.
    * @return The node removed.
    * @exception DOMException
    *   NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
    *   <br>NOT_FOUND_ERR: Raised if <code>oldChild</code> is not a child of
    *   this node.
    */
   public synchronized Node removeChild(Node oldChild) throws DOMException {
      throw EX_NOT_FOUND;
   }

   /**
    * Adds the node <code>newChild</code> to the end of the list of children of
    * this node. If the <code>newChild</code> is already in the tree, it is
    * first removed.
    * @param newChild The node to add.If it is a  <code>DocumentFragment</code>
    *   object, the entire contents of the document fragment are moved into
    *   the child list of this node
    * @return The node added.
    * @exception DOMException
    *   HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
    *   allow children of the type of the <code>newChild</code> node, or if
    *   the node to append is one of this node's ancestors.
    *   <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
    *   from a different document than the one that created this node.
    *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
    */
   public synchronized Node appendChild(Node newChild) throws DOMException {
      throw EX_HIERARCHY_REQUEST;
   }

   /**
    *  This is a convenience method to allow easy determination of whether a
    * node has any children.
    * @return  <code>true</code> if the node has any children,
    *   <code>false</code> if the node has no children.
    */
   public boolean hasChildNodes() {
      return false;
   }

   /**
    * Returns a duplicate of this node, i.e., serves as a generic copy
    * constructor for nodes. The duplicate node has no parent (
    * <code>parentNode</code> returns <code>null</code>.).
    * <br>Cloning an <code>Element</code> copies all attributes and their
    * values, including those generated by the  XML processor to represent
    * defaulted attributes, but this method does not copy any text it contains
    * unless it is a deep clone, since the text is contained in a child
    * <code>Text</code> node. Cloning any other type of node simply returns a
    * copy of this node.
    * @param deep If <code>true</code>, recursively clone the subtree under the
    *   specified node; if <code>false</code>, clone only the node itself (and
    *   its attributes, if it is an <code>Element</code>).
    * @return The duplicate node.
    */
   public final synchronized Node cloneNode(boolean deep) {
      DocumentImpl doc = (DocumentImpl)getOwnerDocument();
      if ( deep ) {
         byte[] data = this.data;
         int pos = this.pos;
         int len = this.len;

         if ( dirty ) {
            try {
               data = DOMCompressor.Compress(this, doc.getSymbols());
               pos = 0;
               len = data.length;
            }
            catch ( Exception e ) {
               // Will never happen
            }
         }

         NodeImpl newNode = null;
         switch ( getNodeType() ) {

            case Node.ATTRIBUTE_NODE:
               newNode = new AttrImpl(this, true);
               newNode.setNodeName(getNodeName());
               newNode.setNodeValue(getNodeValue());
               break;

            case Node.CDATA_SECTION_NODE:
               newNode = new CDATASectionImpl(this, data, pos, len);
               break;

            case Node.COMMENT_NODE:
               newNode = new CommentImpl(this, data, pos, len);
               break;

            case Node.DOCUMENT_FRAGMENT_NODE:
               DocumentFragmentImpl df = new DocumentFragmentImpl(this);
               if ( deep ) {
                  NodeList nl = getChildNodes();
                  for ( int i = 0; i < nl.getLength(); i++ )
                     df.appendChild(nl.item(i).cloneNode(deep));
               }
               newNode = df;
               break;
               
            case Node.ELEMENT_NODE:
               newNode = new ElementImpl(this, data, pos, len);
               break;

            case Node.ENTITY_REFERENCE_NODE:
               newNode = new EntityReferenceImpl(this, data, pos, len);
               break;

            case Node.NOTATION_NODE:
               newNode = new NotationImpl(this, data, pos, len);
               break;

            case Node.PROCESSING_INSTRUCTION_NODE:
               newNode = new ProcessingInstructionImpl(this, data, pos, len);
               break;

            case Node.TEXT_NODE:
               newNode = new TextImpl(this, data, pos, len);
               break;

         }
         if ( newNode != null )
            return newNode;
      }

      checkLoaded();
      switch ( getNodeType() ) {
         case Node.ATTRIBUTE_NODE:
            Attr attr = doc.createAttribute(nodeName);
            attr.setValue(nodeValue);
            return attr;

         case Node.CDATA_SECTION_NODE:
            return doc.createCDATASection(nodeValue);

         case Node.COMMENT_NODE:
            return doc.createComment(nodeValue);

         case Node.DOCUMENT_FRAGMENT_NODE:
            return doc.createDocumentFragment();
            
         case Node.ELEMENT_NODE:
            Element elem = doc.createElement(nodeName);
            NamedNodeMap attrs = getAttributes();
            int size = attrs.getLength();
            for ( int i = 0; i < size; i++ ) {
               Attr a = (Attr)attrs.item(i);
               elem.setAttribute(a.getName(), a.getValue());
            }
            return elem;

         case Node.ENTITY_REFERENCE_NODE:
            return doc.createEntityReference(nodeValue);

         case Node.PROCESSING_INSTRUCTION_NODE:
            return doc.createProcessingInstruction(nodeName, nodeValue);

         case Node.TEXT_NODE:
            return doc.createTextNode(nodeValue);
      }

      return null;
   }

   // Some DOM Level 2 Core Methods

    /**
     *  Puts all <code>Text</code> nodes in the full depth of the sub-tree
     * underneath this <code>Node</code> , including attribute nodes, into a
     * "normal" form where only markup (e.g., tags, comments, processing
     * instructions, CDATA sections, and entity references) separates
     * <code>Text</code> nodes, i.e., there are neither adjacent
     * <code>Text</code> nodes nor empty <code>Text</code> nodes. This can be
     * used to ensure that the DOM view  of a document is the same as if it
     * were saved and re-loaded, and is useful when operations (such as
     * XPointer lookups) that depend on a particular document tree structure
     * are to be used. In cases where the document contains
     * <code>CDATASections</code> , the normalize operation alone may not be
     * sufficient, since XPointers do not differentiate between
     * <code>Text</code> nodes and <code>CDATASection</code> nodes.
     * @since DOM Level 2
     */
   public void normalize() {
   }

    /**
     * Tests whether the DOM implementation implements a specific feature and
     * that feature is supported by this node.
     * @param feature  The name of the feature to test. This is the same name
     *   which can be passed to the method <code>hasFeature</code> on
     *   <code>DOMImplementation</code> .
     * @param version  This is the version number of the feature to test. In
     *   Level 2, version 1, this is the string "2.0". If the version is not
     *   specified, supporting any version of the feature will cause the
     *   method to return <code>true</code> .
     * @return  Returns <code>true</code> if the specified feature is supported
     *    on this node, <code>false</code> otherwise.
     * @since DOM Level 2
     */
   public final boolean isSupported(String feature, String version) {
      return DOMImplementationImpl.HasFeature(feature, version);
   }

    /**
     *  The  namespace URI of this node, or <code>null</code> if it is
     * unspecified.
     * <br> This is not a computed value that is the result of a namespace
     * lookup based on an examination of the namespace declarations in scope.
     * It is merely the namespace URI given at creation time.
     * <br> For nodes of any type other than <code>ELEMENT_NODE</code> and
     * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1
     * method, such as <code>createElement</code> from the
     * <code>Document</code> interface, this is always <code>null</code> .
     * Per the  Namespaces in XML Specification  an attribute does not
     * inherit its namespace from the element it is attached to. If an
     * attribute is not explicitly given a namespace, it simply has no
     * namespace.
     * @since DOM Level 2
     */
   public final String getNamespaceURI() {
      if ( nsURI != null )
         return nsURI;
      short nodeType = getNodeType();
      if ( !(nodeType == Node.ELEMENT_NODE || nodeType == Node.ATTRIBUTE_NODE) )
         return null;

      String prefix = getPrefix();
      return prefix != null ? lookupNamespaceURI(prefix)
                            : lookupDefaultNamespaceURI();
   }

    /**
     *  The  namespace prefix of this node, or <code>null</code> if it is
     * unspecified.
     * <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.
     * @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 final String getPrefix() {
      short nodeType = getNodeType();
      if ( !(nodeType == Node.ELEMENT_NODE || nodeType == Node.ATTRIBUTE_NODE) )
         return null;
      checkLoaded();
      int idx = nodeName.indexOf(':');
      return idx != -1 ? nodeName.substring(0, idx)
                       : null;
   }

    /**
     *  The  namespace prefix of this node, or <code>null</code> if it is
     * unspecified.
     * <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.
     * @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 final void setPrefix(String prefix) {
      short nodeType = getNodeType();
      if ( !(nodeType == Node.ELEMENT_NODE || nodeType == Node.ATTRIBUTE_NODE) )
         return;
      checkReadOnly();
      String ln = getLocalName();
      if ( prefix != null )
         setNodeName(prefix+':'+ln);
      else
         setNodeName(ln);
   }

    /**
     *  Returns the local part of the  qualified name of this node.
     * <br> For nodes created with a DOM Level 1 method, such as
     * <code>createElement</code> from the <code>Document</code> interface,
     * it is <code>null</code> .
     * @since DOM Level 2
     */
   public final String getLocalName() {
      short nodeType = getNodeType();
      if ( !(nodeType == Node.ELEMENT_NODE || nodeType == Node.ATTRIBUTE_NODE) )
         return null;
      String prefix = getPrefix();
      return prefix != null ? getNodeName().substring(prefix.length()+1)
                            : getNodeName();
   }

   /**
    * getDefaultNamespaceURI returns the default Namespace URI in this
    * scope.
    *
    * @return The URI (or null)
    */
   public final String lookupDefaultNamespaceURI() {
      String uri = null;
      if ( getNodeType() == Node.ELEMENT_NODE )
         uri = ((Element)this).getAttribute(XMLNS_PREFIX);
      if ( uri != null && uri.length() > 0 )
         return uri;
      return parentNode != null ? parentNode.lookupDefaultNamespaceURI()
                                : null;
   }

   // Some DOM Level 3 Core Methods

    /**
     * Returns whether this node is the same node as the given one. Do we
     * really want to make this different from equals?Yes, change name from
     * isIdentical to isSameNode. (Telcon 4 Jul 2000).
     * @param otherThe node to test against.
     * @return Returns <code>true</code> if the nodes are the same,
     *   <code>false</code> otherwise.
     */
   public final boolean isSameNode(Node other) {
      return this == other;
   }

    /**
     * Look up the prefix associated to the given namespace URI, starting from
     * this node.
     * @param namespaceURIThe namespace URI to look for.
     * @return Returns the associated namespace prefix or <code>null</code>
     *   if none is found.
     */
   public final String lookupNamespacePrefix(String namespaceURI) {
      if ( getNodeType() == Node.ELEMENT_NODE ) {
         NamedNodeMap map = getAttributes();
         int size = map.getLength();
         for ( int i = 0; i < size; i++ ) {
            Attr attr = (Attr)map.item(i);
            String name = attr.getName();
            if ( name.startsWith(XMLNS_PREFIX+':')
              && attr.getValue().equals(namespaceURI))
               return name.substring(XMLNS_PREFIX.length()+1);
         }
      }
      return parentNode != null ? parentNode.lookupNamespacePrefix(namespaceURI)
                                : null;
   }

    /**
     * Look up the namespace URI associated to the given prefix, starting from
     * this node.Name? May need to change depending on ending of the
     * relative namespace URI reference nightmare.
     * @param prefixThe prefix to look for.
     * @return Returns the associated namespace URI or <code>null</code> if
     *   none is found.
     */
   public final String lookupNamespaceURI(String prefix) {
      String uri = null;
      if ( getNodeType() == Node.ELEMENT_NODE )
         uri = ((Element)this).getAttribute(XMLNS_PREFIX+':'+prefix);
      if ( uri != null && uri.length() > 0 )
         return uri;
      return parentNode != null ? parentNode.lookupNamespaceURI(prefix)
                                : null;
   }

    /**
     * This method walks down the tree, starting from this node, and adds
     * namespace declarations where needed so that every namespace being
     * used is properly declared. It also changes or assign prefixes when
     * needed. This effectively makes this node subtree is "namespace
     * wellformed".
     * <br>What the generated prefixes are and/or how prefixes are changed to
     * achieve this is implementation dependent.Any other name?How specific
     * should this be? Should we not even specify that this should be done
     * by walking down the tree?What does this do on attribute nodes?Doesn't
     * do anything (F2F 1 Aug 2000).How does it work with entity reference
     * subtree which may be broken?This doesn't affect entity references
     * which are not visited in this operation (F2F 1 Aug 2000).Should this
     * be really be on Node?Yes, but this only works on Document, Element,
     * and DocumentFragment. On other types it is a no-op. (F2F 1 Aug 2000).
     * What happens with read-only nodes?What/how errors should be reported?
     * Are there any?
     */
    public void normalizeNS() throws DOMException {
    }

    /**
     * This method allows to attach a user object to a Node. This object can
     * then be retreived using <code>getUserData</code>.Is this really worth
     * it?Could we live without a key?What happens if the node is cloned,
     * imported, adopted? Should some event mechanism be specified to notify
     * the application?What happens if the node is cloned?What should Object
     * be mapped to in ECMAScript? For IDL we need to define this type
     * somehow.
     * @param dataThe piece of data to attach to this node.
     * @param keyThe key to associate this data to.
     * @return The object previously associated to this node and the given
     *   key or <code>null</code>.
     */
    public final synchronized Object setUserData(Object data, String key) {
       if ( userData == null )
          userData = new HashMap();
       Object oldData = userData.get(key);
       userData.put(key, data);
       return oldData;
    }

    /**
     * This method allows to retreive a user object previously attached to a
     * Node with <code>setUserData</code>.
     * @param keyThe key to look for.
     * @return The object associated to this node and the given key or
     *   <code>null</code>.
     */
    public final synchronized Object getUserData(String key) {
       if ( userData == null )
          return null;
       return userData.get(key);
    }

    /**
     * This attribute returns a unique key identifying this node.What type
     * should this really be?In what space is this key unique (Document,
     * DOMImplementation)?What is the lifetime of the uniqueness of this key
     * (Node, Document, ...)?
     */
   public final synchronized Object getKey() {
      if ( key == null )
         key = new Object();
      return key;
   }
}
