/*
 Copyright (C) 2000-2004

 Code contributed by Greg Collecutt, Joseph Hope and Paul Cochrane

 This file is part of xmds.
 
 This program 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
 of the License, or (at your option) any later version.

 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 for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

/*
  $Id: kissdom.cc,v 1.12 2004/07/13 05:29:37 paultcochrane Exp $
*/

/*! @file kissdom.cc
  @brief Greg Collecutt's implementation of DOM3

  More detailed explanation...
*/

// My implementation of DOM3

#include<xmlbasics.h>
#include<dom3.h>
#include<kissdom.h>

#define DEBUGKISSDOM 0  //!< Whether or not to debug the KISS DOM

// ******************************************************************************
// ******************************************************************************
//	DOMException
// ******************************************************************************
// ******************************************************************************

long nDOMExceptions=0;  //!< The number of DOM exception objects

DOMException::DOMException() {
  if(DEBUGKISSDOM) {
    nDOMExceptions++;
    printf("DOMException::DOMException();\n");
    printf("nDOMExceptions=%li\n",nDOMExceptions);
  }
  code = 0;
};

DOMException::DOMException(
			   unsigned long err) {
  if(DEBUGKISSDOM) {
    nDOMExceptions++;
    printf("DOMException::DOMException(unsigned long err);\n");
    printf("nDOMExceptions=%li\n",nDOMExceptions);
  }
  code = err;
};

DOMException::~DOMException() {
  if(DEBUGKISSDOM) {
    nDOMExceptions--;
    printf("DOMException::~DOMException();\n");
    printf("nDOMExceptions=%li\n",nDOMExceptions);
  }
  code = 0;
};

const char* DOMException::getError() const {
  switch (code) {
  case INDEX_SIZE_ERR :
    return "INDEX_SIZE_ERR\n";
    break;
  case DOMSTRING_SIZE_ERR :
    return "DOMSTRING_SIZE_ERR\n";
    break;
  case HIERARCHY_REQUEST_ERR :
    return "HIERARCHY_REQUEST_ERR\n";
    break;
  case WRONG_DOCUMENT_ERR :
    return "WRONG_DOCUMENT_ERR\n";
    break;
  case INVALID_CHARACTER_ERR :
    return "INVALID_CHARACTER_ERR\n";
    break;
  case NO_DATA_ALLOWED_ERR :
    return "NO_DATA_ALLOWED_ERR\n";
    break;
  case NO_MODIFICATION_ALLOWED_ERR :
    return "NO_MODIFICATION_ALLOWED_ERR\n";
    break;
  case NOT_FOUND_ERR :
    return "NOT_FOUND_ERR\n";
    break;
  case NOT_SUPPORTED_ERR :
    return "NOT_SUPPORTED_ERR\n";
    break;
  case INUSE_ATTRIBUTE_ERR :
    return "INUSE_ATTRIBUTE_ERR\n";
    break;
  case INVALID_STATE_ERR :
    return "INVALID_STATE_ERR\n";
    break;
  case SYNTAX_ERR :
    return "SYNTAX_ERR\n";
    break;
  case INVALID_MODIFICATION_ERR :
    return "INVALID_MODIFICATION_ERR\n";
    break;
  case NAMESPACE_ERR :
    return "NAMESPACE_ERR\n";
    break;
  case INVALID_ACCESS_ERR :
    return "INVALID_ACCESS_ERR\n";
    break;
  default :
    return "UNKOWN_ERR\n";
    break;
  }
};

// ******************************************************************************
// ******************************************************************************
//	KissDOMImplementation
// ******************************************************************************
// ******************************************************************************

long nKissDOMImplementations=0;  //!< Number of KISS DOM implementation objects

// ******************************************************************************
KissDOMImplementation::KissDOMImplementation() {
  if(DEBUGKISSDOM) {
    nKissDOMImplementations++;
    printf("KissDOMImplementation::KissDOMImplementation();\n");
    printf("nKissDOMImplementations=%li\n",nKissDOMImplementations);
  }
};

// ******************************************************************************
KissDOMImplementation::~KissDOMImplementation() {
  if(DEBUGKISSDOM) {
    printf("KissDOMImplementation::~KissDOMImplementation();\n");
  }

  for(list<Node*>::const_iterator ppNode = myNodeList.begin(); ppNode != myNodeList.end(); ppNode++) {
    if(DEBUGKISSDOM) {
      printf("deleting *ppNode=%li\n",(long)*ppNode);
    }
    delete (*ppNode);
  }
  if(DEBUGKISSDOM) {
    nKissDOMImplementations--;
    printf("	... KissDOMImplementation deleted\n");
    printf("nKissDOMImplementations=%li\n",nKissDOMImplementations);
  }
};

// ******************************************************************************
bool KissDOMImplementation::hasFeature(
				       DOMString& feature,
				       DOMString& version) const {
  return 0;
};

// ******************************************************************************
DocumentType* KissDOMImplementation::createDocumentType(
							const DOMString& qualifiedName,
							const DOMString& publicId,
							const DOMString& systemId) {

  if(qualifiedName.hasIllegalCharacters()) {
    throw DOMException(DOMException::INVALID_CHARACTER_ERR);
  }

  if(!qualifiedName.isNSWellFormed()) {
    throw DOMException(DOMException::NAMESPACE_ERR);
  }

  DOMString namePrefix;
  DOMString nameLocalPart;

  if(qualifiedName.splitNSName(namePrefix,nameLocalPart)) {

    // was a namespace name

    if(namePrefix.beginsWithxml()|nameLocalPart.beginsWithxml()) {
      throw DOMException(DOMException::INVALID_CHARACTER_ERR);
    }
  }
  else {

    // was not a namespace name, in which case the name may be
    // anything not begining with xml,

    if(qualifiedName.beginsWithxml()) {
      throw DOMException(DOMException::INVALID_CHARACTER_ERR);
    }
  }
	
  DocumentType* newDocumentType = new KissDocumentType(qualifiedName,publicId,systemId);

  myNodeList.push_back(newDocumentType);
  return newDocumentType;
};

// ******************************************************************************
Document* KissDOMImplementation::createDocument(
						const DOMString& namespaceURI,
						const DOMString& qualifiedName,
						DocumentType* doctype) {

  if(qualifiedName.hasIllegalCharacters()) {
    throw DOMException(DOMException::INVALID_CHARACTER_ERR);
  }

  if(!qualifiedName.isNSWellFormed()) {
    throw DOMException(DOMException::NAMESPACE_ERR);
  }

  DOMString namePrefix;
  DOMString nameLocalPart;

  if(namespaceURI.length()>0) {

    // We are making a document with a namespace root element.

    if(qualifiedName.splitNSName(namePrefix,nameLocalPart)) {

      // was a namespace name

      if(namePrefix.eqxml()) {
		
	// if the prefix is xml
	// the namespaceURI must be exactly XML_NAMESPACEURI

	if(namespaceURI != XML_NAMESPACEURI) {
	  throw DOMException(DOMException::NAMESPACE_ERR);
	}
      }
      else if(namePrefix.beginsWithxml() |nameLocalPart.beginsWithxml()) {
	throw DOMException(DOMException::INVALID_CHARACTER_ERR);
      }
    }
    else {

      // was not a namespace name, in which case the name may be
      // anything not begining with xml,

      if(qualifiedName.beginsWithxml()) {
	throw DOMException(DOMException::INVALID_CHARACTER_ERR);
      }
    }
  }
	
  if(doctype != 0) {
    if((doctype->ownerDocument() != 0)|doctype->readOnly()) {
      throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
    }
  }

  // No further exceptions will occur beyond this point,
  // therefore we may begin creating the document and element nodes.

  Document* newDocument = new KissDocument((DOMImplementation*)this);

  myNodeList.push_back(newDocument);

  if(doctype != 0) {

    doctype->setOwnerDocument(newDocument);

    newDocument->appendChild(doctype);

    // must look for doctype amongst nodes and remove it if there
    // since it now belongs to the document

    list<Node*>::iterator ppNode;
    ppNode = myNodeList.begin();
    while(((*ppNode) != doctype) && (ppNode != myNodeList.end())) {
      ppNode++;
    }
    if((*ppNode) == doctype) {
      myNodeList.erase(ppNode);
    }
  }

  Element* documentElement = new KissElement(newDocument,0,namespaceURI,qualifiedName);

  newDocument->appendChild(documentElement);

  return newDocument;
};

// ******************************************************************************
DOMImplementation* KissDOMImplementation::getAs(
						DOMString& feature) {
  return this;
};

// ******************************************************************************
void KissDOMImplementation::printAll() {

  if(DEBUGKISSDOM) {
    printf("KissDOMImplementation::printAll()\n");
  }

  for(list<Node*>::const_iterator ppNode = myNodeList.begin(); ppNode != myNodeList.end(); ppNode++) {
    printNode(0,*ppNode);
  }
};

// ******************************************************************************
void KissDOMImplementation::printNode(
				      const unsigned long& indent,
				      const Node* node2Print) {

  if(DEBUGKISSDOM) {
    printf("KissDOMImplementation::printNode()\n");
  }

  unsigned long i;

  for(i = 0; i < indent; i++) {
    printf("	");
  }
  printf("[%li] %s",node2Print->key(),node2Print->nodeName()->c_str());

  const DOMString* s = node2Print->nodeValue();
  if(s != 0) {
    printf("='%s'",s->c_str());
  }
  printf("\n");

  if(node2Print->namespaceURI() != 0) {
    for(i = 0; i < indent; i++) {
      printf("	");
    }
    printf("	(URI='%s')\n",node2Print->namespaceURI()->c_str());
  }
	
  if(node2Print->attributes() != 0) {
    if(node2Print->attributes()->length() != 0) {

      for(i = 0; i < indent; i++) {
	printf("	");
      }
      printf("Has Attributes (%li):\n",node2Print->attributes()->length());

      for(i = 0; i < node2Print->attributes()->length(); i++) {
	printNode(indent+1,node2Print->attributes()->item(i));
      }
    }
  }

  if(node2Print->childNodes()->length() != 0) {

    for(i = 0; i < indent; i++) {
      printf("	");
    }
    printf("Has Children (%li):\n",node2Print->childNodes()->length());

    for(i = 0; i < node2Print->childNodes()->length(); i++) {
      printNode(indent+1,node2Print->childNodes()->item(i));
    }
  }
};

// ******************************************************************************
// ******************************************************************************
//	KissNodeList
// ******************************************************************************
// ******************************************************************************

long nKissNodeLists=0;  //!< Number of KISS node list objects

// ******************************************************************************
KissNodeList::KissNodeList(
			   const list<Node*>* yourPNodeList) :
  myPNodeList(yourPNodeList) {
  if(DEBUGKISSDOM) {
    nKissNodeLists++;
    printf("KissNodeList::KissNodeList();\n");
    printf("nKissNodeLists=%li\n",nKissNodeLists);
  }
};

// ******************************************************************************
KissNodeList::~KissNodeList() {
  if(DEBUGKISSDOM) {
    nKissNodeLists--;
    printf("KissNodeList::~KissNodeList();\n");
    printf("nKissNodeLists=%li\n",nKissNodeLists);
  }
};

// ******************************************************************************
Node* KissNodeList::item(
			 unsigned long	index) const {
  if(index >= myPNodeList->size()) {
    return 0;
  }

  list<Node*>::const_iterator ppNode = myPNodeList->begin();
  for(unsigned long i = 0; i<index; i++) {
    ppNode++;
  }

  return (*ppNode);
};

// ******************************************************************************
unsigned long KissNodeList::length() const {
  return myPNodeList->size();
};

// ******************************************************************************
// ******************************************************************************
//	KissNamedNodeMap
// ******************************************************************************
// ******************************************************************************

long nKissNamedNodeMaps=0;  //!< Number of KISS named node map objects

// ******************************************************************************
KissNamedNodeMap::KissNamedNodeMap(
				   const unsigned long& yourNodeType) :
  myNodeType(yourNodeType) {
  if(DEBUGKISSDOM) {
    nKissNamedNodeMaps++;
    printf("KissNamedNodeMap::KissNamedNodeMap();\n");
    printf("nKissNamedNodeMaps=%li\n",nKissNamedNodeMaps);
  }

  readOnly=0;
};

// ******************************************************************************
KissNamedNodeMap::~KissNamedNodeMap() {
  if(DEBUGKISSDOM) {
    nKissNamedNodeMaps--;
    printf("KissNamedNodeMap::~KissNamedNodeMap();\n");
    printf("nKissNamedNodeMaps=%li\n",nKissNamedNodeMaps);
  }

  for(list<Node*>::const_iterator ppNode = myNodeList.begin(); ppNode != myNodeList.end(); ppNode++) {
    delete (*ppNode);
  }
};

// ******************************************************************************
Node* KissNamedNodeMap::getNamedItem(
				     const DOMString& name) const {

  if(DEBUGKISSDOM) {
    printf("KissNamedNodeMap::getNamedItem()\n");
  }


  if (DEBUGKISSDOM) {
    printf("KissNamedNodeMap::getNamedItem() myNodeList.size() = %d\n",myNodeList.size());
  }

  if(myNodeList.size() == 0) {
    return 0;
  }

  list<Node*>::const_iterator ppNode = myNodeList.begin();
  list<const DOMString*>::const_iterator ppName = myNameList.begin();

  bool found=0;

  while((ppNode != myNodeList.end())&!found) {
    found = (**ppName==name);
    if(!found) {
      ppNode++;
      ppName++;
    }
  }

  if(ppNode != myNodeList.end()) {
    return *ppNode;
  }
	
  return 0;
};

// ******************************************************************************
Node* KissNamedNodeMap::setNamedItem(
				     Node& arg) {

  if(DEBUGKISSDOM) {
    printf("KissNamedNodeMap::setNamedItem()\n");
  }

  canAddThisNode(arg);

  if (DEBUGKISSDOM) {
    printf("arg.nodeName = %s\n",arg.nodeName()->c_str());
  }
 
  Node* oldNode;
  try {
    oldNode = removeNamedItem(*(arg.nodeName()));
  }
  catch (DOMException DOMExc) {
    if (DEBUGKISSDOM) {
      printf("KissNamedNodeMap::setNamedItem DOMException %s\n", DOMExc.getError());
    }
    oldNode = 0;
  }

  myNodeList.push_back(&arg);
  myNameList.push_back(arg.nodeName());
  myNamespaceURIList.push_back(&EMPTY_STRING);

  return oldNode;
};

// ******************************************************************************
Node* KissNamedNodeMap::removeNamedItem(
					const DOMString& name) {

  if(DEBUGKISSDOM) {
    printf("KissNamedNodeMap::removeNamedItem()\n");
  }

  if(DEBUGKISSDOM) {
    printf("name is %s\n", name.c_str());
  }

  if(readOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(myNodeList.size() == 0) {
    throw DOMException(DOMException::NOT_FOUND_ERR);
  }

  list<Node*>::iterator ppNode = myNodeList.begin();
  list<const DOMString*>::iterator ppName = myNameList.begin();
  list<const DOMString*>::iterator ppNamespaceURI = myNamespaceURIList.begin();
  bool found=0;

  while((ppNode != myNodeList.end())&!found) {
    found = (**ppName==name);
    if(!found) {
      ppNode++;
      ppName++;
      ppNamespaceURI++;
    }
  }

  if(ppNode != myNodeList.end()) {
    Node* removedNode = *ppNode;
    myNodeList.erase(ppNode);
    myNameList.erase(ppName);
    myNamespaceURIList.erase(ppNamespaceURI);
    return removedNode;
  }

  // if not found by now
  throw DOMException(DOMException::NOT_FOUND_ERR);
};

// ******************************************************************************
unsigned long KissNamedNodeMap::length() const {
  return myNodeList.size();
};

// ******************************************************************************
Node* KissNamedNodeMap::item(
			     const unsigned long index) const {

  if(index >= myNodeList.size()) {
    return 0;
  }
	
  list<Node*>::const_iterator ppNode = myNodeList.begin();
  for(unsigned long i = 0; i < index; i++) {
    ppNode++;
  }

  return *ppNode;
};

// ******************************************************************************
Node* KissNamedNodeMap::getNamedItemNS(
				       const DOMString& namespaceURI,
				       const DOMString& localname) const {

  if(DEBUGKISSDOM) {
    printf("KissNamedNodeMap::getNamedItemNS()\n");
  }

  if(myNodeList.size() == 0) {
    return 0;
  }

  list<Node*>::const_iterator ppNode = myNodeList.begin();
  list<const DOMString*>::const_iterator ppName = myNameList.begin();
  list<const DOMString*>::const_iterator ppNamespaceURI = myNamespaceURIList.begin();
  bool found=0;

  while((ppNode != myNodeList.end())&!found) {
    found = (**ppName==localname)&(**ppNamespaceURI==namespaceURI);
    if(!found) {
      ppNode++;
      ppName++;
      ppNamespaceURI++;
    }
  }

  if(ppNode != myNodeList.end()) {
    return *ppNode;
  }

  return 0;
};

// ******************************************************************************
Node* KissNamedNodeMap::setNamedItemNS(
				       Node& arg) {

  if(DEBUGKISSDOM) {
    printf("KissNamedNodeMap::setNamedItemNS()\n");
  }

  canAddThisNode(arg);

  Node* oldNode;

  try {
    oldNode = removeNamedItemNS(*(arg.namespaceURI()),*(arg.localName()));
  }
  catch (DOMException DOMExc) {
    oldNode = 0;
  }

  myNodeList.push_back(&arg);
  myNameList.push_back(arg.localName());
  myNamespaceURIList.push_back(arg.namespaceURI());

  return oldNode;
};

// ******************************************************************************
Node* KissNamedNodeMap::removeNamedItemNS(
					  const DOMString& namespaceURI,
					  const DOMString& localname) {

  if(DEBUGKISSDOM) {
    printf("KissNamedNodeMap::removeNamedItemNS()\n");
  }

  if(readOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(myNodeList.size() == 0) {
    throw DOMException(DOMException::NOT_FOUND_ERR);
  }

  list<Node*>::iterator ppNode = myNodeList.begin();
  list<const DOMString*>::iterator ppName = myNameList.begin();
  list<const DOMString*>::iterator ppNamespaceURI = myNamespaceURIList.begin();

  bool found=0;

  while((ppNode != myNodeList.end())&!found) {
    found = (**ppName==localname)&(**ppNamespaceURI==namespaceURI);
    if(!found) {
      ppNode++;
      ppName++;
      ppNamespaceURI++;
    }
  }


  if(ppNode != myNodeList.end()) {
    Node* removedNode = *ppNode;
    myNodeList.erase(ppNode);
    myNameList.erase(ppName);
    myNamespaceURIList.erase(ppNamespaceURI);
    return removedNode;
  }

  // if not found by now
  throw DOMException(DOMException::NOT_FOUND_ERR);
};

// ******************************************************************************
void KissNamedNodeMap::setReadOnly(
				   const bool& newReadOnly) {
  readOnly = newReadOnly;
};

// ******************************************************************************
const Document* KissNamedNodeMap::ownerDocument() const {
  if(DEBUGKISSDOM) {
    printf("KissNamedNodeMap::ownerDocument()\n");
  }

  return myOwnerDocument;
};

// ******************************************************************************
void KissNamedNodeMap::setOwnerDocument(
					const Document* yourOwnerDocument) {
  if(DEBUGKISSDOM) {
    printf("KissNamedNodeMap::setOwnerDocument()\n");
  }

  myOwnerDocument=yourOwnerDocument;
};

// ******************************************************************************
void KissNamedNodeMap::canAddThisNode(
				      const Node& arg) const {

  if(DEBUGKISSDOM) {
    printf("KissNamedNodeMap::canAddThisNode()\n");
  }

  if(readOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(arg.ownerDocument() != ownerDocument()) {
    throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
  }

  if(arg.nodeType() != myNodeType) {
    throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
  }
};

// ******************************************************************************
// ******************************************************************************
//	KissNode
// ******************************************************************************
// ******************************************************************************

long nKissNodes=0;  //!< Number of KISS node objects

// ******************************************************************************
KissNode::KissNode(
		   const Document *const yourOwnerDocument,
		   Node *const yourParentNode,
		   const DOMString& yourNodeName) :
  myKissNodeList(&myNodeList) {
  if(DEBUGKISSDOM) {
    nKissNodes++;
    printf("KissNode::KissNode(); [%s]\n",yourNodeName.c_str());
    printf("nKissNodes=%li\n",nKissNodes);
  }

  myOwnerDocument = yourOwnerDocument;
  myParentNode = yourParentNode;
  myNodeName = yourNodeName;
  myReadOnly = 0;

  if(myOwnerDocument != 0) {
    myDOMKey = myOwnerDocument->getDOMKey();
  }
  else {
    myDOMKey = 0;
  }
};

// ******************************************************************************
KissNode::~KissNode() {
  if(DEBUGKISSDOM) {
    printf("KissNode::~KissNode(%li); [%s]\n",myDOMKey,myNodeName.c_str());
  }

  for(list<Node*>::const_iterator ppNode = myNodeList.begin(); ppNode != myNodeList.end(); ppNode++) {
    if(DEBUGKISSDOM) {
      printf("deleting *ppNode=%li\n",(long)*ppNode); 
    }
    delete (*ppNode);
  }
  if(DEBUGKISSDOM) {
    nKissNodes--;
    printf("	...(%li); [%s] deleted\n",myDOMKey,myNodeName.c_str());
    printf("nKissNodes=%li\n",nKissNodes);
  }
};

// ******************************************************************************
const DOMString* KissNode::nodeName() const {
  return &myNodeName;
};

// ******************************************************************************
const DOMString* KissNode::nodeValue() const {
  return 0;
};

// ******************************************************************************
void KissNode::setNodeValue(
			    const DOMString& newNodeValue) {
};

// ******************************************************************************
Node* KissNode::parentNode() const {
  return myParentNode;
};

// ******************************************************************************
void KissNode::setParentNode(
			     Node* newParentNode) {

  if(DEBUGKISSDOM) {
    printf("KissNode::setParentNode() [%s]\n",nodeName()->c_str());
  }

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }
  myParentNode = newParentNode;
};

// ******************************************************************************
const NodeList* KissNode::childNodes() const {
  return &myKissNodeList;
};

// ******************************************************************************
Node* KissNode::firstChild() const {
  return *myNodeList.begin();
};

// ******************************************************************************
Node* KissNode::lastChild() const {
  return *myNodeList.end();
};

// ******************************************************************************
Node* KissNode::previousSibling() const {

  if(myParentNode == 0) {
    return 0;
  }

  for(unsigned long i = 0; i < myParentNode->childNodes()->length(); i++) {
    if((const Node*) myParentNode->childNodes()->item(i) == this) {
      return myParentNode->childNodes()->item(i-1);
    }
  }

  return 0;
};

// ******************************************************************************
Node* KissNode::nextSibling() const {

  if(myParentNode == 0) {
    return 0;
  }

  for(unsigned long i = 0; i < myParentNode->childNodes()->length(); i++) {
    if((const Node*) myParentNode->childNodes()->item(i) == this) {
      return myParentNode->childNodes()->item(i+1);
    }
  }

  return 0;
};

// ******************************************************************************
const NamedNodeMap* KissNode::attributes() const {
  return 0;
};

// ******************************************************************************
const Document* KissNode::ownerDocument() const {
  return myOwnerDocument;
};

// ******************************************************************************
void KissNode::setOwnerDocument(
				const Document *const newOwnerDocument) {

  if(DEBUGKISSDOM) {
    printf("KissNode::setOwnerDocument() [%s]\n",nodeName()->c_str());
  }

  myOwnerDocument = newOwnerDocument;
  myDOMKey = newOwnerDocument->getDOMKey();

  for(list<Node*>::const_iterator ppNode = myNodeList.begin(); ppNode != myNodeList.end(); ppNode++) {
    (*ppNode)->setOwnerDocument(newOwnerDocument);
  }
};

// ******************************************************************************
Node* KissNode::insertBefore(
			     Node* newChild,
			     Node* refChild) {

  if(newChild == 0) {
    return 0;
  }

  if(refChild == 0) {
    return(appendChild(newChild));
  }

  checkChildAddingConstraints1(newChild);

  checkChildAddingConstraints2(newChild);

  list<Node*>::iterator ppNode = myNodeList.begin();
  while((ppNode != myNodeList.end()) &(*ppNode != refChild)) {
    ppNode++;
  }
  if(*ppNode != refChild) {
    throw DOMException(DOMException::NOT_FOUND_ERR);
  }

  if(newChild->parentNode() != 0) {
    newChild->parentNode()->removeChild(newChild);
  }

  if(newChild->nodeType() == DOCUMENT_FRAGMENT_NODE) {
    while(newChild->hasChildNodes()) {
      Node* nextChild = newChild->removeChild(newChild->firstChild());
      nextChild->setParentNode(this);
      myNodeList.insert(ppNode,nextChild);
    }
  }
  else {
    newChild->setParentNode(this);
    myNodeList.insert(ppNode,newChild);
  }

  return newChild;
};

// ******************************************************************************
Node* KissNode::replaceChild(
			     Node* newChild,
			     Node* oldChild) {

  if(newChild == 0) {
    return 0;
  }

  checkChildAddingConstraints1(newChild);

  checkChildAddingConstraints2(newChild);

  list<Node*>::iterator ppNode = myNodeList.begin();
  while((ppNode != myNodeList.end())&(*ppNode != oldChild)) {
    ppNode++;
  }
  if(*ppNode != oldChild) {
    throw DOMException(DOMException::NOT_FOUND_ERR);
  }

  if(newChild->parentNode() != 0) {
    newChild->parentNode()->removeChild(newChild);
  }

  if(newChild->nodeType() == DOCUMENT_FRAGMENT_NODE) {
    while(newChild->hasChildNodes()) {
      Node* nextChild = newChild->removeChild(newChild->firstChild());
      nextChild->setParentNode(this);
      myNodeList.insert(ppNode,nextChild);
    }
  }
  else {
    newChild->setParentNode(this);
    myNodeList.insert(ppNode,newChild);
  }
	
  oldChild->setParentNode(0);
  myNodeList.erase(ppNode);

  return oldChild;
};

// ******************************************************************************
Node* KissNode::removeChild(
			    Node* oldChild) {

  if(oldChild == 0) {
    return 0;
  }

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  list<Node*>::iterator ppNode = myNodeList.begin();
  while((ppNode != myNodeList.end())&(*ppNode != oldChild)) {
    ppNode++;
  }
	
  if(*ppNode != oldChild) {
    throw DOMException(DOMException::NOT_FOUND_ERR);
  }

  myNodeList.erase(ppNode);
  oldChild->setParentNode(0);

  return oldChild;
};

// ******************************************************************************
Node* KissNode::appendChild(
			    Node* newChild) {

  if(newChild == 0) {
    return 0;
  }

  checkChildAddingConstraints1(newChild);

  checkChildAddingConstraints2(newChild);

  if(newChild->parentNode() != 0) {
    newChild->parentNode()->removeChild(newChild);
  }

  if(newChild->nodeType() == DOCUMENT_FRAGMENT_NODE) {
    while(newChild->hasChildNodes()) {
      Node* nextChild = newChild->removeChild(newChild->firstChild());
      nextChild->setParentNode(this);
      myNodeList.push_back(nextChild);
    }
  }
  else {
    newChild->setParentNode(this);
    myNodeList.push_back(newChild);
  }

  return newChild;
};

// ******************************************************************************
bool KissNode::hasChildNodes() const {
  return (myNodeList.size() > 0);
};

// ******************************************************************************
void KissNode::normalize() {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  unsigned long i;

  // firstly remove empty text nodes
  i = 0;
  while(i < myNodeList.size()) {
    Node* testNode = myKissNodeList.item(i);
    if(testNode->nodeType() == TEXT_NODE) {
      if(testNode->nodeValue()->length() == 0) {
	removeChild(testNode);
	delete testNode;
	i--;
      }
    i++;	
    }
  }

  i = 0;
  while(i + 1 < myNodeList.size()) {
    Node* testNode1 = myKissNodeList.item(i);
    Node* testNode2 = myKissNodeList.item(i + 1);
    if((testNode1->nodeType() == TEXT_NODE) &(testNode2->nodeType() == TEXT_NODE)) {
      DOMString s = *(testNode1->nodeValue());
      s += *(testNode2->nodeValue());
      testNode1->setNodeValue(s);
      removeChild(testNode2);
      delete testNode2;
      i--;
    }
    i++;
  }
};

// ******************************************************************************
bool KissNode::isSupported(
			   DOMString& feature,
			   DOMString& version) const {
  return 0;
};

// ******************************************************************************
const DOMString* KissNode::namespaceURI() const {
  return 0;
};

// ******************************************************************************
const DOMString* KissNode::prefix() const {
  return 0;
};

// ******************************************************************************
void KissNode::setPrefix(
			 const DOMString& newPrefix) {
};

// ******************************************************************************
const DOMString* KissNode::localName() const {
  return 0;
};

// ******************************************************************************
bool KissNode::hasAttributes() const {
  return 0;
};

// ******************************************************************************
const DOMString* KissNode::baseURI() const {
  return 0;
};

// ******************************************************************************
Node::DocumentOrder KissNode::compareDocumentOrder(
						   const Node* other) const {

  if(other == this) {
    return DOCUMENT_ORDER_SAME;
  }
 
  if(other->ownerDocument() != 0) {
    if(other->ownerDocument() != myOwnerDocument); {
    throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
    }
  }
  else {
    if(other != myOwnerDocument) {
      throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
    }
  }

  if(other->nodeType() == ATTRIBUTE_NODE) {
    return DOCUMENT_ORDER_UNORDERED;
  }
	
  bool passedThisNode = 0;

  // work forwards from top node looking for other node
  const Node* nextNode = myOwnerDocument;

  while (nextNode != 0) {
    if(nextNode == other) {
      if(passedThisNode) {
	return DOCUMENT_ORDER_FOLLOWING;
      }
      else {
	return DOCUMENT_ORDER_PRECEDING;
      }
    }
    passedThisNode = passedThisNode | (nextNode == this);

    if(nextNode->hasChildNodes()) {
      nextNode = nextNode->firstChild();
    }
    else {
      while((nextNode->nextSibling() == 0) &(nextNode->parentNode() != 0)) {
	nextNode = nextNode->parentNode();
      }
    }

    if(nextNode != 0) {
      nextNode = nextNode->nextSibling();
    }
  }

  return DOCUMENT_ORDER_UNORDERED;
};

// ******************************************************************************
Node::TreePosition KissNode::compareTreePosition(
						 const Node* other) const {

  if(other == this) {
    return TREE_POSITION_SAME;
  }

  if(other->ownerDocument() != 0) {
    if(other->ownerDocument() != myOwnerDocument) {
      throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
    }
  }
  else {
    if(other != myOwnerDocument) {
      throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
    }
  }

  const Node* nextNode;

  // look for other as ancestor
  nextNode = myParentNode;
  while(nextNode != 0) {
    if(nextNode == other) {
      return TREE_POSITION_ANCESTOR;
    }
    nextNode = nextNode->parentNode();
  }

  // look for other as child
  nextNode = other->parentNode();
  while(nextNode != 0) {
    if(nextNode == this) {
      return TREE_POSITION_DESCENDANT;
    }
    nextNode = nextNode->parentNode();
  }

  // else use document order
  switch(compareDocumentOrder(other)) {
  case DOCUMENT_ORDER_PRECEDING :
    return TREE_POSITION_PRECEDING;
    break;
  case DOCUMENT_ORDER_FOLLOWING :
    return TREE_POSITION_FOLLOWING;
    break;
  default :
    return TREE_POSITION_UNORDERED;
  }
};

// ******************************************************************************
const DOMString* KissNode::textContent(
				       const bool& deep) const {

  const DOMString* pS = nodeValue();

  if(pS != 0) {
    myTextContent = *pS;
  }
  else {
    myTextContent = EMPTY_STRING;
  }

  if(deep) {
    for(list<Node*>::const_iterator ppNode = myNodeList.begin(); ppNode != myNodeList.end(); ppNode ++) {
      if(((*ppNode)->nodeType() != COMMENT_NODE) &((*ppNode)->nodeType() != PROCESSING_INSTRUCTION_NODE)) {
	myTextContent += *(*ppNode)->textContent(deep);
      }
    }
  }
  else {
    for(list<Node*>::const_iterator ppNode = myNodeList.begin(); ppNode != myNodeList.end(); ppNode ++) {
      if(	((*ppNode)->nodeType() == TEXT_NODE) |((*ppNode)->nodeType() == CDATA_SECTION_NODE)) {
	myTextContent += *(*ppNode)->textContent(deep);
      }
    }
  }

  return &myTextContent;
};

// ******************************************************************************
void KissNode::setTextContent(
			      const DOMString& newTextContent) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  // delete all children
  for(list<Node*>::const_iterator ppNode = myNodeList.begin(); ppNode != myNodeList.end(); ppNode++) {
    delete (*ppNode);
  }

  Node* newTextNode = new KissText(myOwnerDocument,myParentNode,newTextContent);

  appendChild(newTextNode);
};

// ******************************************************************************
bool KissNode::isSameNode(
			  const Node* other) const {
  return (other == this);
};

// ******************************************************************************
const DOMString* KissNode::lookupNamespacePrefix(
						 const DOMString& namespaceURI) const {

  if(DEBUGKISSDOM) {
    printf("KissNode::lookupNamespacePrefix() [%s]\n",nodeName()->c_str());
  }

  if(namespaceURI.length() == 0) {
    return 0;
  }

  const Node* nextAncestor = this;

  while(nextAncestor != 0) {
    if(nextAncestor->hasAttributes()) {
      for(unsigned long i = 0; i < nextAncestor->attributes()->length(); i++) {
	if(*nextAncestor->attributes()->item(i)->nodeValue() == namespaceURI) {
	  if(nextAncestor->attributes()->item(i)->namespaceURI() != 0) {
	    if(nextAncestor->attributes()->item(i)->prefix()->eqxmlns()) {
	      return nextAncestor->attributes()->item(i)->localName();
	    }
	  }
	}
      }
    }

    nextAncestor = nextAncestor->parentNode();
  }

  return 0;
};

// ******************************************************************************
const DOMString* KissNode::lookupNamespaceURI(
					      const DOMString& prefix) const {

  if(DEBUGKISSDOM) {
    printf("KissNode::lookupNamespaceURI() [%s]\n",nodeName()->c_str());
  }

  if(prefix.eqxml()) {
    return &XML_NAMESPACEURI;
  }

  if(prefix.eqxmlns()) {
    return &XMLNS_NAMESPACEURI;
  }

  const Node* nextAncestor = this;

  while(nextAncestor != 0) {

    if(nextAncestor->hasAttributes()) {
      if(prefix.length() > 0) {
	// look for explicitly defined namespace
	for(unsigned long i = 0; i < nextAncestor->attributes()->length(); i++)  {
	  if(nextAncestor->attributes()->item(i)->namespaceURI() != 0) {
	    if(nextAncestor->attributes()->item(i)->prefix()->eqxmlns()
	       &(*nextAncestor->attributes()->item(i)->localName() == prefix)) {
	      return nextAncestor->attributes()->item(i)->nodeValue();
	    }
	  }
	}
      }
      else	{
	// look for first default namespace
	for(unsigned long i = 0; i < nextAncestor->attributes()->length(); i++)  {
	  if(nextAncestor->attributes()->item(i)->namespaceURI() != 0) {
	    if(nextAncestor->attributes()->item(i)->nodeName()->eqxmlns()) {
	      return(nextAncestor->attributes()->item(i)->nodeValue());
	    }
	  }
	}
      }
    }
		
    nextAncestor = nextAncestor->parentNode();
  }

  return 0;

};

// ******************************************************************************
void KissNode::normalizeNS() {
};

// ******************************************************************************
DOMKey KissNode::key() const {
  return myDOMKey;
};

// ******************************************************************************
bool KissNode::equalsNode(
			  const Node* arg,
			  bool deep) const {

  // check for the obvious first!

  if(arg == 0) {
    return 0;
  }

  if(arg == this) {
    return 1;
  }

  const DOMString* s1;
  const DOMString* s2; 
  const NamedNodeMap* a1 = arg->attributes();
  const NamedNodeMap* a2 = attributes();
  unsigned long i;

  // compare nodeTypes

  if (arg->nodeType() != nodeType()) {
    return 0;
  }

  // compare nodeNames

  if (arg->nodeName() != nodeName()) {
    return 0;
  }

  // compare nodeValues

  s1 = arg->nodeValue();
  s2 = nodeValue();

  if((s1 == 0) != (s1 == 0)) {
    return 0;
  }

  if((s1 != 0) && (s2 != 0)) {
    if(*s1 != *s2) {
      return 0;
    }
  }

  // compare namespaceURIs;

  s1 = arg->namespaceURI();
  s2 = namespaceURI();

  if((s1 == 0) != (s1 == 0)) {
    return 0;
  }

  if((s1 != 0) && (s2 != 0)) {
    if(*s1 != *s2) {
      return 0;
    }
  }

  // compare attributes (if any)

  if((a1 == 0) != (a1 == 0)) {
    return 0;
  }

  if(a1 != 0) {
    if(a1->length() != a2->length()) {
      return 0;
    }
    for(i = 0; i < a1->length(); i++) {
      if(!a1->item(i)->equalsNode(a2->item(i),deep)) {
	return 0;
      }
    }
  }

  if(deep) {

    // compare children

    if(arg->childNodes()->length() != childNodes()->length()) {
      return 0;
    }

    for(i = 0; i < childNodes()->length(); i++) {
      if(!childNodes()->item(i)->equalsNode(arg->childNodes()->item(i),deep)) {
	return 0;
      }
    }
  }

  // well, if we have gotten this far then these nodes must be,
  // to all intents, equal!

  return 1;
};

// ******************************************************************************
Node* KissNode::getAs(
		      DOMString& feature) {
  return this;
};

// ******************************************************************************
bool KissNode::readOnly() const {
  return myReadOnly;
};

// ******************************************************************************
void KissNode::setReadOnly(
			   const bool& newReadOnly,
			   const bool& deep) {
  myReadOnly = newReadOnly;
  if(deep) {
    for(list<Node*>::const_iterator ppNode = myNodeList.begin(); ppNode != myNodeList.end(); ppNode ++ ) {
      (*ppNode)->setReadOnly(newReadOnly,deep);
    }
  }
};

// ******************************************************************************
void KissNode::checkChildAddingConstraints1(
					    const Node* newChild) const {

  // this node is not read only
  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  // that new node's parent is not read only -
  // i.e. it will allow this node to be removed from it
  if(newChild->parentNode() != 0) {
    if(newChild->parentNode()->readOnly()) {
      throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
    }
  }

  // if the new node is a document fragment node that is itself
  // not read only - i.e. it will allow its children to be removed
  if(newChild->nodeType() == DOCUMENT_FRAGMENT_NODE) {
    if(newChild->readOnly()) {
      throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
    }
  }

  // that the node to add is not this node or one of its ancestors
  const Node* ancestorNode = this;
  while(ancestorNode != 0) {
    if(newChild == ancestorNode) {
      throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
    }
    ancestorNode = ancestorNode->parentNode();
  }

  // new node has same owner document
  if(myOwnerDocument == 0) {
    if(nodeType() == DOCUMENT_NODE) { // I am a document! 
      if((const Node*) newChild->ownerDocument() != this) {
	throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
      }
    }
    else if(newChild->ownerDocument() != myOwnerDocument) {
      throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
    }
  }
};

// ******************************************************************************
void KissNode::checkChildAddingConstraints2(
					    const Node* newChild) const {

  // this base class routine only checks that the new child is not
  // an illegal type to add to the majority of other classes.
  // the Document and Attr classes have more stringent constraints
  // and so these classes override this function

  switch(newChild->nodeType()) {
  case DOCUMENT_NODE :
  case DOCUMENT_TYPE_NODE :
  case ATTRIBUTE_NODE :
  case ENTITY_NODE :
  case NOTATION_NODE :
    throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
    break;
  }
};

// ******************************************************************************
// ******************************************************************************
//	KissDocument
// ******************************************************************************
// ******************************************************************************

long nKissDocuments=0;  //!< Number of KISS document objects

// ******************************************************************************
unsigned long KissDocument::nodeType() const {
  return DOCUMENT_NODE;
};

// ******************************************************************************
void KissDocument::setParentNode(
				 Node* newParentNode) {
  throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
};

// ******************************************************************************
void KissDocument::setOwnerDocument(
				    const Document *const newOwnerDocument) {
  throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
};

// ******************************************************************************
Node* KissDocument::insertBefore(
				 Node* newChild,
				 Node* refChild) {

  if(refChild == 0) {
    return appendChild(newChild);
  }

  if(newChild == 0) {
    return 0;
  }

  // may only have one DOCUMENT_TYPE_NODE

  if((newChild->nodeType() == DOCUMENT_TYPE_NODE) &(myDoctype != 0)) {
    throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
  }

  // may only have one ELEMENT_NODE

  if((newChild->nodeType() == ELEMENT_NODE) &(myDocumentElement != 0)) {
    throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
  }

  KissNode::insertBefore(newChild,refChild);

  if(newChild->nodeType() == DOCUMENT_TYPE_NODE) {
    myDoctype = newChild;
  }

  if(newChild->nodeType() == ELEMENT_NODE) {
    myDocumentElement = newChild;
  }

  return newChild;
};

// ******************************************************************************
Node* KissDocument::replaceChild(
				 Node* newChild,
				 Node* oldChild) {

  if(newChild == 0) {
    return 0;
  }

  KissNode::replaceChild(newChild,oldChild);

  if(newChild->nodeType() == DOCUMENT_TYPE_NODE) {
    myDoctype = newChild;
  }

  if(newChild->nodeType() == ELEMENT_NODE) {
    myDocumentElement = newChild;
  }

  return oldChild;
};

// ******************************************************************************
Node* KissDocument::removeChild(
				Node* oldChild) {

  KissNode::removeChild(oldChild);

  if(oldChild->nodeType() == DOCUMENT_TYPE_NODE) {
    myDoctype = 0;
  }

  if(oldChild->nodeType() == ELEMENT_NODE) {
    myDocumentElement = 0;
  }

  return oldChild;
};

// ******************************************************************************
Node* KissDocument::appendChild(
				Node* newChild) {

  if(newChild == 0) {
    return 0;
  }

  // may only have one DOCUMENT_TYPE_NODE

  if((newChild->nodeType() == DOCUMENT_TYPE_NODE) &(myDoctype != 0)) {
    throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
  }

  // may only have one ELEMENT_NODE

  if((newChild->nodeType() == ELEMENT_NODE) &(myDocumentElement != 0)) {
    throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
  }

  KissNode::appendChild(newChild);

  if(newChild->nodeType() == DOCUMENT_TYPE_NODE) {
    myDoctype = newChild;
  }

  if(newChild->nodeType() == ELEMENT_NODE) {
    myDocumentElement = newChild;
  }

  return newChild;
};

// ******************************************************************************
Node* KissDocument::cloneNode(
			      const bool& deep) const {

  Document* newDocument = new KissDocument(myDOMImplementation);

  newDocument->setActualEncoding(myActualEncoding);
  newDocument->setEncoding(myEncoding);
  newDocument->setVersion(myVersion);
  newDocument->setStandalone(myStandalone);
  newDocument->setStrictErrorChecking(myStrictErrorChecking);

  if(deep) {
    for(unsigned long i = 0; i < childNodes()->length(); i++) {
      Node* newChild = childNodes()->item(i)->cloneNode(deep);
      newChild->setOwnerDocument(this);
      newDocument->appendChild(newChild);
    }
  }

  return newDocument;
};

// ******************************************************************************
const DOMString* KissDocument::namespaceURI() const {
  return &myNamespaceURI;
};

// ******************************************************************************
Node::DocumentOrder KissDocument::compareDocumentOrder(
						       const Node* other) const {

  if(other == this) {
    return DOCUMENT_ORDER_SAME;
  }
 
  if(other->ownerDocument() != this) {
    throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
  }

  if(other->nodeType() == ATTRIBUTE_NODE) {
    return DOCUMENT_ORDER_UNORDERED;
  }
	
  return DOCUMENT_ORDER_FOLLOWING;
};

// ******************************************************************************
Node::TreePosition KissDocument::compareTreePosition(
						     const Node* other) const {

  if(other == this) {
    return TREE_POSITION_SAME;
  }

  if(other->ownerDocument() != this) {
    throw DOMException(DOMException::WRONG_DOCUMENT_ERR);
  }

  return TREE_POSITION_DESCENDANT;
};

// ******************************************************************************
void KissDocument::setTextContent(
				  const DOMString& newTextContent) {
  throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
};

// ******************************************************************************
void KissDocument::normalizeNS() {

  if(myDocumentElement != 0) {
    myDocumentElement->normalizeNS();
  }
};

// ******************************************************************************
void KissDocument::checkChildAddingConstraints2(
						const Node* newChild) const {

  switch(newChild->nodeType()) {
  case DOCUMENT_TYPE_NODE :
  case ELEMENT_NODE :
  case PROCESSING_INSTRUCTION_NODE :
  case COMMENT_NODE :
    break;
  case DOCUMENT_FRAGMENT_NODE :
    for(unsigned long i = 0; i < newChild->childNodes()->length(); i++) {
      unsigned long nextType = newChild->childNodes()->item(i)->nodeType();
      if(!((nextType == PROCESSING_INSTRUCTION_NODE) |(nextType == COMMENT_NODE))) {
	throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
      }
    }
    break;
  default :
    throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
  }
};

// ******************************************************************************
KissDocument::KissDocument(
			   const DOMImplementation* yourDOMImplementation) :
  KissNode(0,0,"#document"),
  myElementNodeList(&myElementList) {
  if(DEBUGKISSDOM) {
    nKissDocuments++;
    printf("KissDocument::KissDocument(); [%s]\n",nodeName()->c_str());
    printf("nKissDocuments=%li\n",nKissDocuments);
  }

  myDoctype = 0;
  myDocumentElement = 0;
  nextKey=0;
  myDOMImplementation = yourDOMImplementation;
  myStandalone = 0;
  myStrictErrorChecking = 1;
};

// ******************************************************************************
KissDocument::~KissDocument() {
  if(DEBUGKISSDOM) {
    nKissDocuments--;
    printf("KissDocument::~KissDocument(); [%s]\n",nodeName()->c_str());
    printf("nKissDocuments=%li\n",nKissDocuments);
  }
};

// ******************************************************************************
DOMKey KissDocument::getDOMKey() const {
  nextKey++;
  return nextKey;
};

// ******************************************************************************
const DocumentType* KissDocument::doctype() const {
  return dynamic_cast<DocumentType*>(myDoctype);
};

// ******************************************************************************
const DOMImplementation* KissDocument::implementation() const {
  return myDOMImplementation;
};

// ******************************************************************************
Element* KissDocument::documentElement() const {
  return dynamic_cast<Element*>(myDocumentElement);
};

// ******************************************************************************
Element* KissDocument::createElement(
				     const DOMString& tagName) {

  if(DEBUGKISSDOM) {
    printf("KissDocument::createElement(); [%s]\n",nodeName()->c_str());
  }

  if((!tagName.isName()) |tagName.beginsWithxml()) {
    throw DOMException(DOMException::INVALID_CHARACTER_ERR);
  }

  return new KissElement(this,0,"",tagName);
};

// ******************************************************************************
DocumentFragment* KissDocument::createDocumentFragment() {

  return new KissDocumentFragment(this);
};

// ******************************************************************************
Text* KissDocument::createTextNode(
				   const DOMString& data) {
  return new KissText(this,0,data);
};

// ******************************************************************************
Comment* KissDocument::createComment(
				     const DOMString& data) {
  return new KissComment(this,0,data);
};

// ******************************************************************************
CDATASection* KissDocument::createCDATASection(
					       const DOMString& data) {
  return new KissCDATASection(this,0,data);
};

// ******************************************************************************
ProcessingInstruction* KissDocument::createProcessingInstruction(
								 const DOMString& target,
								 const DOMString& data) {

  if(target.hasIllegalCharacters()) {
    throw DOMException(DOMException::INVALID_CHARACTER_ERR);
  }

  return new KissProcessingInstruction(this,0,target,data);
};

// ******************************************************************************
Attr* KissDocument::createAttribute(
				    const DOMString& name) {

  if(DEBUGKISSDOM) {
    printf("KissDocument::createAttribute(); [%s]\n",nodeName()->c_str());
  }

  if((!name.isName())|name.beginsWithxml()) {
    throw DOMException(DOMException::INVALID_CHARACTER_ERR);
  }

  return new KissAttr(ownerDocument(),0,"",name,1);
};

// ******************************************************************************
EntityReference* KissDocument::createEntityReference(
						     const DOMString& name) {

  if(name.hasIllegalCharacters()) {
    throw DOMException(DOMException::INVALID_CHARACTER_ERR);
  }

  return new KissEntityReference(this,0,name);
};

// ******************************************************************************
NodeList* KissDocument::getElementsByTagName(
					     const DOMString& tagName) {

  myElementList.clear();

  Node* nextNode = myDocumentElement;

  while (nextNode != 0) {
    if(nextNode->nodeType() == ELEMENT_NODE) {
      if(tagName == "*") {
	myElementList.push_back(nextNode);
      }
      else if(*nextNode->nodeName() == tagName) {
	myElementList.push_back(nextNode);
      }
    }
    if(nextNode->hasChildNodes()) {
      nextNode = nextNode->firstChild();
    }
    else {
      while((nextNode->nextSibling() == 0) &(nextNode->parentNode() != 0)) {
	nextNode = nextNode->parentNode();
      }
    }

    if(nextNode != 0) {
      nextNode = nextNode->nextSibling();
    }
  }

  return &myElementNodeList;
};

// ******************************************************************************
Node* KissDocument::importNode(
			       const Node* importNode,
			       const bool& deep) {

  if((importNode->nodeType() == DOCUMENT_NODE) |(importNode->nodeType() == DOCUMENT_TYPE_NODE)) {
    throw DOMException(DOMException::NOT_SUPPORTED_ERR);
  }

  Node* newNode;

  if(importNode->nodeType() == ATTRIBUTE_NODE) {
    newNode = importNode->cloneNode(1);
  }
  else {
    newNode = importNode->cloneNode(deep);
  }
	
  return adoptNode(newNode);
};

// ******************************************************************************
Element* KissDocument::createElementNS(
				       const DOMString& namespaceURI,
				       const DOMString& qualifiedName) {

  if(DEBUGKISSDOM) {
    printf("KissDocument::createElementNS(); [%s]\n",nodeName()->c_str());
  }

  if(!qualifiedName.isNSWellFormed()) {
    throw DOMException(DOMException::NAMESPACE_ERR);
  }

  DOMString namePrefix;
  DOMString nameLocalPart;

  // We are definicately making a namespace element.

  if(qualifiedName.splitNSName(namePrefix,nameLocalPart)) {

    // was a namespace name

    // if the prefix is xml the namespaceURI must be exactly XML_NAMESPACEURI

    if(namePrefix.eqxml()) {
      if(namespaceURI != XML_NAMESPACEURI) {
	throw DOMException(DOMException::NAMESPACE_ERR);
      }
    }
    else if((!namePrefix.isNCName()) |namePrefix.beginsWithxml()) {
      throw DOMException(DOMException::INVALID_CHARACTER_ERR);
    }

    if((!nameLocalPart.isNCName()) |nameLocalPart.beginsWithxml()) {
      throw DOMException(DOMException::INVALID_CHARACTER_ERR);
    }
  }
  else {

    // was not a namespace name, in which case the usual xml criterior apply.

    if((!qualifiedName.isName()) |qualifiedName.beginsWithxml()) {
      throw DOMException(DOMException::INVALID_CHARACTER_ERR);
    }
  }

  return new KissElement(this,0,namespaceURI,qualifiedName);
};

// ******************************************************************************
Attr* KissDocument::createAttributeNS(
				      const DOMString& namespaceURI,
				      const DOMString& qualifiedName) {

  if(DEBUGKISSDOM) {
    printf("KissDocument::createAttributeNS(); [%s]\n",nodeName()->c_str());
  }

  if(!qualifiedName.isNSWellFormed()) {
    throw DOMException(DOMException::NAMESPACE_ERR);
  }

  DOMString namePrefix;
  DOMString nameLocalPart;

  if(qualifiedName.splitNSName(namePrefix,nameLocalPart)) {

    // was a namespace name

    // if the prefix is xml the namespaceURI must be exactly XML_NAMESPACEURI

    if(namePrefix.eqxml()) {

      // if the prefix is xml
      // the namespaceURI must be exactly XML_NAMESPACEURI

      if(namespaceURI != XML_NAMESPACEURI) {
	throw DOMException(DOMException::NAMESPACE_ERR);
      }
    }
    else if(namePrefix.eqxmlns()) {

      // or if the prefix is xmlns
      // the namespaceURI must be exactly XMLNS_NAMESPACEURI

      if(namespaceURI != XMLNS_NAMESPACEURI) {
	throw DOMException(DOMException::NAMESPACE_ERR);
      }
    }
    else if((!namePrefix.isNCName()) |namePrefix.beginsWithxml()) {
      throw DOMException(DOMException::INVALID_CHARACTER_ERR);
    }

    if((!nameLocalPart.isNCName()) |nameLocalPart.beginsWithxml()) {
      throw DOMException(DOMException::INVALID_CHARACTER_ERR);
    }
  }
  else {

    // was not a namespace name, in which case the name may be 'xmlns'
    // or any other normal xml name,

    if(!qualifiedName.eqxmlns()) {
      if((qualifiedName.isName()) |qualifiedName.beginsWithxml()) {
	throw DOMException(DOMException::INVALID_CHARACTER_ERR);
      }
    }
  }

  return new KissAttr(ownerDocument(),0,namespaceURI,qualifiedName,1);
};

// ******************************************************************************
NodeList* KissDocument::getElementsByTagNameNS(
					       const DOMString& namespaceURI,
					       const DOMString& localname) {

  if(namespaceURI.length()==0) {
    return getElementsByTagName(localname);
  }

  myElementList.clear();

  Node* nextNode = myDocumentElement;

  while (nextNode != 0) {
    if(nextNode->nodeType() == ELEMENT_NODE) {
      if(nextNode->namespaceURI() != 0) {
	if(localname == "*") {
	  myElementList.push_back(nextNode);
	}
	else if((*nextNode->namespaceURI() == namespaceURI) &(*nextNode->localName() == localname)) {
	  myElementList.push_back(nextNode);
	}
      }
    }
    if(nextNode->hasChildNodes()) {
      nextNode = nextNode->firstChild();
    }
    else {
      while((nextNode->nextSibling() == 0) &(nextNode->parentNode() != 0)) {
	nextNode = nextNode->parentNode();
      }
    }

    if(nextNode != 0) {
      nextNode = nextNode->nextSibling();
    }
  }

  return &myElementNodeList;
};

// ******************************************************************************
Element* KissDocument::getElementById(
				      const DOMString& elementId) {
  return 0;
};

// ******************************************************************************
const DOMString* KissDocument::actualEncoding() const {
  return &myActualEncoding;
};

// ******************************************************************************
void KissDocument::setActualEncoding(
				     const DOMString& newActualEncoding) {
  myActualEncoding = newActualEncoding;
};

// ******************************************************************************
const DOMString* KissDocument::encoding() const {
  return &myEncoding;
};

// ******************************************************************************
void KissDocument::setEncoding(
			       const DOMString& newEncoding) {
  myEncoding = newEncoding;
};

// ******************************************************************************
bool KissDocument::standalone() const {
  return myStandalone;
};

// ******************************************************************************
void KissDocument::setStandalone(
				 const bool& newStandalone) {
  myStandalone = newStandalone;
};

// ******************************************************************************
bool KissDocument::strictErrorChecking() const {
  return myStrictErrorChecking;
};

// ******************************************************************************
void KissDocument::setStrictErrorChecking(
					  const bool& newStrictErrorChecking) {
  myStrictErrorChecking = newStrictErrorChecking;
};

// ******************************************************************************
const DOMString* KissDocument::version() const {
  return &myVersion;
};

// ******************************************************************************
void KissDocument::setVersion(
			      const DOMString& newVersion) {
  myVersion = newVersion;
};

// ******************************************************************************
Node* KissDocument::adoptNode(
			      Node* source) {

  if(source->readOnly()) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(source->parentNode() != 0)	{
    if(source->parentNode()->readOnly()) {
      throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
    }
    source->parentNode()->removeChild(source);
  }

  if(source->ownerDocument() != 0) {
    if(source->ownerDocument()->implementation() != myDOMImplementation) {
      return 0;
    }
  }

  if((source->nodeType() == DOCUMENT_NODE) |(source->nodeType() == DOCUMENT_TYPE_NODE)) {
    throw DOMException(DOMException::NOT_SUPPORTED_ERR);
  }

  Node* nextNode;

  if((source->nodeType() == ATTRIBUTE_NODE)
     |(source->nodeType() == DOCUMENT_FRAGMENT_NODE)
     |(source->nodeType() == ELEMENT_NODE)
     |(source->nodeType() == ENTITY_REFERENCE_NODE)) {

    // need to check that there are no read only nodes in source's subtree

    nextNode = source->firstChild();

    while (nextNode != 0) {

      if(nextNode->readOnly()) {
	throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
      }

      if(nextNode->hasChildNodes()) {
	nextNode = nextNode->firstChild();
      }
      else {
	while((nextNode->nextSibling() == 0) &(nextNode->parentNode() != 0) &(nextNode != source)) {
	  nextNode = nextNode->parentNode();
	}
      }

      if(nextNode == source) {
	nextNode = 0;
      }

      if(nextNode != 0) {
	nextNode = nextNode->nextSibling();
      }
    }
  }

  Attr* attrNode;

  nextNode = source;

  while (nextNode != 0) {

    switch(nextNode->nodeType()) {

    case ATTRIBUTE_NODE:

      attrNode = dynamic_cast<Attr*>(nextNode);

      attrNode->setOwnerElement(0);
      attrNode->setSpecified(1);
      break;

    case ELEMENT_NODE:

      // we are supposed to remove unspecified attributes here
      // how do we do this?

      break;

    case ENTITY_REFERENCE_NODE:
      while(nextNode->hasChildNodes()) {
	delete nextNode->removeChild(source->firstChild());
      }
      break;

    case DOCUMENT_FRAGMENT_NODE:
    case PROCESSING_INSTRUCTION_NODE:
    case TEXT_NODE:
    case COMMENT_NODE:
    case CDATA_SECTION_NODE:
      break;

    default :
      return 0;
    }

    if(nextNode->hasChildNodes()) {
      nextNode = nextNode->firstChild();
    }
    else {
      while((nextNode->nextSibling() == 0) &(nextNode->parentNode() != 0) &(nextNode != source)) {
	nextNode = nextNode->parentNode();
      }
    }

    if(nextNode == source) {
      nextNode = 0;
    }

    if(nextNode != 0) {
      nextNode = nextNode->nextSibling();
    }
  }

  source->setOwnerDocument(this);

  return source;
};

// ******************************************************************************
void KissDocument::setBaseURI(
			      const DOMString *const baseURI) {
};

// ******************************************************************************
// ******************************************************************************
//	KissElement
// ******************************************************************************
// ******************************************************************************

long nKissElements=0;  //!< Number of KISS element objects

// ******************************************************************************
unsigned long KissElement::nodeType() const {
  return Node::ELEMENT_NODE;
};

// ******************************************************************************
const NamedNodeMap* KissElement::attributes() const {
  return &myAttributesMap;
};

// ******************************************************************************
void KissElement::setOwnerDocument(
				   const Document *const newOwnerDocument) {

  if(DEBUGKISSDOM) {
    printf("KissElement::setOwnerDocument() [%s]\n",nodeName()->c_str());
  }

  KissNode::setOwnerDocument(newOwnerDocument);

  myAttributesMap.setOwnerDocument(newOwnerDocument);

  for(unsigned long i = 0; i < myAttributesMap.length(); i++) {
    myAttributesMap.item(i)->setOwnerDocument(newOwnerDocument);
  }
};

// ******************************************************************************
Node* KissElement::cloneNode(
			     const bool& deep) const {

  Element* newElement = new KissElement(ownerDocument(),0,myNamespaceURI,myNodeName);

  for(unsigned long i = 0; i < myAttributesMap.length(); i++) {
    Attr* nextAttr = dynamic_cast<Attr*>(myAttributesMap.item(i));
    Attr* newAttr = dynamic_cast<Attr*>(myAttributesMap.item(i)->cloneNode(deep));
    newAttr->setSpecified(nextAttr->specified());
    newElement->setAttributeNode(*newAttr);
  }

  if(deep) {
    for(unsigned long i = 0; i < childNodes()->length(); i++) {
      newElement->appendChild(childNodes()->item(i)->cloneNode(deep));
    }
  }

  return newElement;
};

// ******************************************************************************
const DOMString* KissElement::namespaceURI() const {
  return &myNamespaceURI;
};

// ******************************************************************************
const DOMString* KissElement::prefix() const {
  return &myPrefix;
};

// ******************************************************************************
void KissElement::setPrefix(
			    const DOMString& newPrefix) {

  // if I am not a namespace element do nothing

  if(myNamespaceURI.length()==0) {
    return;
  }

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(newPrefix == 0) {
    throw DOMException(DOMException::NAMESPACE_ERR);
  }

  if(!newPrefix.isNCName()) {
    throw DOMException(DOMException::INVALID_CHARACTER_ERR);
  }
	
  if(newPrefix.eqxml() &(myNamespaceURI != XML_NAMESPACEURI)) {
    throw DOMException(DOMException::NAMESPACE_ERR);
  }

  myPrefix = newPrefix;
  if(myPrefix.length() != 0) {
    myNodeName = myPrefix;
    myNodeName += ":";
    myNodeName += myLocalname;
  }
  else
    myNodeName = myLocalname;
};

// ******************************************************************************
const DOMString* KissElement::localName() const {
  return &myLocalname;
};

// ******************************************************************************
bool KissElement::hasAttributes() const {
  return (myAttributesMap.length() != 0);
};

// ******************************************************************************
void KissElement::normalizeNS() {

  enum {
    ACTION_ARBITRARY_PREFIX = 1,
    ACTION_CHANGE_PREFIX = 2,
    ACTION_NEW_BINDING = 4
  };
	

  unsigned long i;
  unsigned long action;

  const DOMString* boundPrefix = 0;
  const DOMString* boundURI = 0;
  DOMString newAttrName;
  const DOMString* newPrefix =0;

  if(DEBUGKISSDOM) {
    printf("KissElement::normalizeNS() [%s]\n",nodeName()->c_str());
  }

  if(myReadOnly) {
    return;
  }

  for(i = 0; i < myAttributesMap.length(); i++) {
    if(myAttributesMap.item(i)->readOnly()) {
      return;
    }
  }

  // examine element's namespace

  action = 0;

  if(myNamespaceURI.length() > 0) {

    if(DEBUGKISSDOM) {
      printf("	I have a namespaceURI:\n");
      printf("		myPrefix	= '%s'\n",myPrefix.c_str());
      printf("		myLocalname	= '%s'\n",myNamespaceURI.c_str());
      printf("		myNamespaceURI	= '%s'\n",myNamespaceURI.c_str());
    }

    if(myPrefix.eqxml()) {

      if(DEBUGKISSDOM) {
	printf("	Element tag is already NS well formed.\n");
      }
    }
    else {

      boundPrefix = lookupNamespacePrefix(myNamespaceURI);

      bool needNewBinding = 0;

      if(boundPrefix == 0) {
	needNewBinding = 1;
      }
      else if(*boundPrefix != myPrefix) {
	needNewBinding = 1;
      }

      if(needNewBinding)	{

	if(DEBUGKISSDOM) {
	  printf("	There is either no prefix bound to myNamespaceURI\n");
	  printf("		or else there is one that is different to mine\n");
	  printf("		I need a new binding.\n");		
	}

	newPrefix = &myPrefix;
	action |= ACTION_NEW_BINDING;
      }
      else {

	if(DEBUGKISSDOM) {
	  printf("	Prefix binding found, and is same as mine.\n");
	}
      }
    }
  }
  else {

    if(DEBUGKISSDOM) {
      printf("	I am not a namespace element.\n");
      printf("		myNodeName = '%s'\n",myNodeName.c_str());
    }

    DOMString namePrefix;
    DOMString nameLocalPart;
	
    if(myNodeName.splitNSName(namePrefix,nameLocalPart)) {

      if(DEBUGKISSDOM) {
	printf("	I have a prefix in my name.\n");
	printf("	Looking for bound URI.\n");
      }

      boundURI = lookupNamespaceURI(namePrefix);

      if(boundURI != 0) {

	printf("Namespace error in Element '%s'\n",nodeName()->c_str());
	printf("	Element has no declared namespace but has the prefix '%s'\n",namePrefix.c_str());
	printf("	which is otherwise bound to '%s'\n",boundURI->c_str());

	return;
      }
      else {

	if(DEBUGKISSDOM) {
	  printf("	There is no URI bound to my prefix. I may be skipped.\n");
	}
      }
    }
    else {

      if(DEBUGKISSDOM) {
	printf("	I do not have a prefix in my name.\n");
	printf("	Looking for default URI binding.\n");
      }
      boundURI = lookupNamespaceURI("");

      if(boundURI != 0) {

	if(DEBUGKISSDOM) {
	  printf("	There is a default namespaceURI binding\n");
	  printf("	 for my empty prefix and so I must override\n");
	  printf("	 it by becoming a namespace element\n");
	  printf("	 and creating a new empty default namespace.\n");
	}

	newPrefix = &myPrefix;
	action |= ACTION_NEW_BINDING;
      }
      else {

	if(DEBUGKISSDOM) {
	  printf("	There is no default URI binding.\n");
	}
      }
    }
  }

  // now process actions
	
  if(action && ACTION_NEW_BINDING) {

    newAttrName = "xmlns";

    if (newPrefix->length() != 0) {
      newAttrName += ":";
      newAttrName += *newPrefix;
    }

    if(DEBUGKISSDOM) {
      printf("	Creating binding %s='%s'.\n",newAttrName.c_str(),myNamespaceURI.c_str());
    }

    setAttributeNS(XMLNS_NAMESPACEURI,newAttrName,myNamespaceURI);
  }

  // examine attributes' namespaces

  for(i = 0; i < myAttributesMap.length(); i++) {

    action = 0;

    const DOMString* attributePrefix = myAttributesMap.item(i)->prefix();
    const DOMString* attributeLocalname = myAttributesMap.item(i)->localName();
    const DOMString* attributeNodeName = myAttributesMap.item(i)->nodeName();
    const DOMString* attributeURI = myAttributesMap.item(i)->namespaceURI();

    if(DEBUGKISSDOM) {
      printf("		Examining attribute %li:\n",i);
    }

    if(attributeURI->length() != 0) {
		
      if(DEBUGKISSDOM) {
	printf("		Attribute has a namespaceURI:\n");
	printf("		attributePrefix		= '%s'\n",attributePrefix->c_str());
	printf("		attributeLocalname 	= '%s'\n",attributeLocalname->c_str());
	printf("		attributeURI		= '%s'\n",attributeURI->c_str());
      }

      if(attributePrefix->eqxml() |attributePrefix->eqxmlns() |attributeNodeName->eqxmlns()) {

	if(DEBUGKISSDOM) {
	  printf("		Attribute is already NS well formed.\n");
	}
      }
      else {

	bool needNewPrefix = 0;

	if(attributePrefix->length() == 0) {

	  if(DEBUGKISSDOM) {
	    printf("		Attribute has zero length prefix. Will need a new one.\n");
	  }

	  needNewPrefix = 1;
	}
	else {

	  boundURI = lookupNamespaceURI(*attributePrefix);

	  if(boundURI != 0) {
	    if(*boundURI != *attributeURI) {

	      if(DEBUGKISSDOM) {
		printf("		Attribute has a prefix which is already bound to a different URI. Will need a new one.\n");
	      }

	      needNewPrefix = 1;
	    }
	  }
	}

	if(needNewPrefix) {

	  if(DEBUGKISSDOM) {
	    printf("		Looking for non-default binding\n");
	  }

	  boundPrefix = lookupNamespacePrefixNonDefault(*attributeURI);

	  if(boundPrefix == 0) {

	    if(DEBUGKISSDOM) {
	      printf("		No non-default binding found.\n");
	      printf("		Will need to create arbitrary prefix.\n");
	    }

	    action |= ACTION_ARBITRARY_PREFIX|ACTION_NEW_BINDING;
	  }

	  else {

	    if(DEBUGKISSDOM) {
	      printf("		Non-default binding found (%s).\n",boundPrefix->c_str());
	      printf("			Will change prefix to this.\n");
	    }

	    newPrefix = boundPrefix;
	    action |= ACTION_CHANGE_PREFIX;
	  }
	}
	else {

	  if(DEBUGKISSDOM){
	    printf("		Attribute has a prefix which is not already bound to\n");
	    printf("			a different URI. Looking for non-default binding.\n");
	  }

	  boundPrefix = lookupNamespacePrefixNonDefault(*attributeURI);

	  if(boundPrefix == 0) {

	    if(DEBUGKISSDOM) {
	      printf("		No non-default binding found.\n");
	      printf("			Will need to create new binding.\n");
	    }

	    action |= ACTION_NEW_BINDING;
	  }

	  else {

	    if(DEBUGKISSDOM) {
	      printf("		Non-default binding found (%s).\n",boundPrefix->c_str());
	      printf("			Will change prefix to this.\n");
	    }

	    newPrefix = boundPrefix;
	    action |= ACTION_CHANGE_PREFIX;
	  }
	}
      }
    } 
    else {
		
      if(DEBUGKISSDOM) {
	printf("		Attribute is not a namespace attribute:\n");
	printf("		attributeNodeName = '%s'\n",attributeNodeName->c_str());
      }

      if(attributeNodeName->eqxmlns()) {

	if(DEBUGKISSDOM) {
	  printf("		Attribute is already NS well formed.\n");
	}
      }
      else {
		
	DOMString namePrefix;
	DOMString nameLocalPart;

	if(attributeNodeName->splitNSName(namePrefix,nameLocalPart)) {

	  if(DEBUGKISSDOM) {
	    printf("	Attribute has a prefix in its name.\n");
	    printf("	Looking for bound URI.\n");
	  }

	  boundURI = lookupNamespaceURI(namePrefix);

	  if(boundURI != 0) {

	    printf("Namespace error in attribute '%s'\n",nodeName()->c_str());
	    printf("	Attribute has no declared namespace but has the prefix '%s'\n",namePrefix.c_str());
	    printf("	which is otherwise bound to '%s'\n",boundURI->c_str());

	    return;
	  }
	  else {

	    if(DEBUGKISSDOM) {
	      printf("	There is no URI bound to my prefix. I may be skipped.\n");
	    }
	  }

	}
	else {

	  if(DEBUGKISSDOM) {
	    printf("	I do not have a prefix in my name.\n");
	    printf("	I am fine as I am.\n");
	  }
	}
      }
    }

    // process actions
	
    if(action && ACTION_ARBITRARY_PREFIX) {

      if(DEBUGKISSDOM) {
	printf("		Creating arbitrary prefix.\n");
      }

      char s[100];

      sprintf(s,"DOMImplied%li",ownerDocument()->getDOMKey());

      myAttributesMap.item(i)->setPrefix(s);
    }

    if(action && ACTION_CHANGE_PREFIX) {

      if(DEBUGKISSDOM) {
	printf("		Changing attribute prefix to '%s'.\n",newPrefix->c_str());
      }

      myAttributesMap.item(i)->setPrefix(*newPrefix);		
    }

    if(action && ACTION_NEW_BINDING) {

      newAttrName = "xmlns";

      if (newPrefix->length() != 0) {
	newAttrName += ":";
	newAttrName += *newPrefix;
      }

      if(DEBUGKISSDOM) {
	printf("	Creating binding %s='%s'.\n",newAttrName.c_str(),attributeURI->c_str());
      }

      setAttributeNS(XMLNS_NAMESPACEURI,newAttrName,myNamespaceURI);
    }
  }
		
  // move on to children

  for(i = 0; i < childNodes()->length(); i++) {
    childNodes()->item(i)->normalizeNS();
  }
};

// ******************************************************************************
KissElement::KissElement(
			 const Document *const yourOwnerDocument,
			 Node *const yourParentNode,
			 const DOMString& yourNamespaceURI,
			 const DOMString& yourTagName) :
  KissNode(yourOwnerDocument,yourParentNode,yourTagName),
  myAttributesMap(ATTRIBUTE_NODE),
  myElementNodeList(&myElementList),
  myNamespaceURI(yourNamespaceURI) {
  if(DEBUGKISSDOM) {
    nKissElements++;
    printf("KissElement::KissElement(); [%s]\n",nodeName()->c_str());
    printf("nKissElements=%li\n",nKissElements);
  }

  yourTagName.splitNSName(myPrefix,myLocalname);

  myAttributesMap.setOwnerDocument(yourOwnerDocument);
};

// ******************************************************************************
KissElement::~KissElement() {
  if(DEBUGKISSDOM) {
    nKissElements--;
    printf("KissElement::~KissElement(); [%s]\n", myNodeName.c_str());
    printf("nKissElements=%li\n",nKissElements);
  }

};

// ******************************************************************************
const DOMString* KissElement::tagName() const {
  return &myNodeName;
};

// ******************************************************************************
const DOMString* KissElement::getAttribute(
					   const DOMString& name) const {

  if (DEBUGKISSDOM) {
    printf("KissElement::getAttribute() myAttributesMap.length() = %ld\n",myAttributesMap.length());
  }

  const Node* oldNode = myAttributesMap.getNamedItem(name);

  if(oldNode == 0) {
    return &EMPTY_STRING;
  }
  else {
    return oldNode->nodeValue();
  }
};

// ******************************************************************************
void KissElement::setAttribute(
			       const DOMString& name,
			       const DOMString& value) {

  if(DEBUGKISSDOM) {
    printf("KissElement::setAttribute(); [%s]\n",nodeName()->c_str());
  }

  if(DEBUGKISSDOM) {
    printf("KissElement::setAttribute(); name = %s, value = %s\n",name.c_str(),value.c_str());
  }

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if((!name.isName()) |name.beginsWithxml()) {
    throw DOMException(DOMException::INVALID_CHARACTER_ERR);
  }

  Node* oldNode = myAttributesMap.getNamedItem(name);

  if(oldNode != 0) {
    oldNode->setNodeValue(value);
  }
  else {
    Node* newAttr = new KissAttr(ownerDocument(),this,"",name,1);
    newAttr->setNodeValue(value);
    myAttributesMap.setNamedItem(*newAttr);
  }
};

// ******************************************************************************
void KissElement::removeAttribute(
				  const DOMString& name) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  try {
    Node* oldNode = myAttributesMap.removeNamedItem(name);
    if(oldNode != 0) {
      delete oldNode;
    }
  }
  catch (DOMException) {
  };
};

// ******************************************************************************
Attr* KissElement::getAttributeNode(
				    const DOMString& name) {

  return dynamic_cast<Attr*>(myAttributesMap.getNamedItem(name));
};

// ******************************************************************************
Attr* KissElement::setAttributeNode(
				    Attr& newAttr) {

  if(myReadOnly |newAttr.readOnly()) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(newAttr.ownerElement() != 0) {
    throw DOMException(DOMException::INUSE_ATTRIBUTE_ERR);
  }

  newAttr.setOwnerElement(this);
  return dynamic_cast<Attr*>(myAttributesMap.setNamedItem(newAttr)); 
};

// ******************************************************************************
Attr* KissElement::removeAttributeNode(
				       Attr& oldAttr) {
  return dynamic_cast<Attr*> (myAttributesMap.removeNamedItem(*oldAttr.nodeName())); 
};

// ******************************************************************************
const NodeList* KissElement::getElementsByTagName(
						  const DOMString& tagName,
						  const bool& deep) const {

  myElementList.clear();

  Node* nextNode = firstChild();

  while ((const Node*) nextNode != this) {
    if(nextNode->nodeType() == ELEMENT_NODE) {
      if(tagName == "*") {
	myElementList.push_back(nextNode); 
      }
      else if(*nextNode->nodeName() == tagName) {
	myElementList.push_back(nextNode);
      }
    }
    if(nextNode->hasChildNodes()&deep) {
      nextNode = nextNode->firstChild();
    }
    else {
      while((nextNode->nextSibling() == 0)&((const Node*) nextNode != this)) {
	nextNode = nextNode->parentNode();
      }
      if((const Node*) nextNode != this) {
	nextNode = nextNode->nextSibling();
      }
    }
  }

  return &myElementNodeList;
};

// ******************************************************************************
const DOMString* KissElement::getAttributeNS(
					     const DOMString& namespaceURI,
					     const DOMString& localname) const {

  if(namespaceURI.length()>0) {
    return getAttribute(localname);
  }

  Node* oldNode = myAttributesMap.getNamedItemNS(namespaceURI,localname);

  if(oldNode != 0) {
    return oldNode->nodeValue();
  }
  else {
    return &EMPTY_STRING;
  }
};

// ******************************************************************************
void KissElement::setAttributeNS(
				 const DOMString& namespaceURI,
				 const DOMString& qualifiedName,
				 const DOMString& value) {
		
  if(DEBUGKISSDOM) {
    printf("KissElement::setAttributeNS()\n");
  }

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(!qualifiedName.isNSWellFormed()) {
    throw DOMException(DOMException::NAMESPACE_ERR);
  }

  if(namespaceURI.length()==0) {
    return setAttribute(qualifiedName,value);
  }

  DOMString namePrefix;
  DOMString nameLocalPart;

  Node* oldNode;

  // We are definitely making a namespace attribute.

  if(qualifiedName.splitNSName(namePrefix,nameLocalPart)) {

    // was a namespace name

    // if the prefix is xml the namespaceURI must be exactly XML_NAMESPACEURI

    if(namePrefix.eqxml()) {

      // if the prefix is xml
      // the namespaceURI must be exactly XML_NAMESPACEURI

      if(namespaceURI != XML_NAMESPACEURI) {
	throw DOMException(DOMException::NAMESPACE_ERR);
      }
    }
    else if(namePrefix.eqxmlns()) {

      // or if the prefix is xmlns
      // the namespaceURI must be exactly XMLNS_NAMESPACEURI

      if(namespaceURI != XMLNS_NAMESPACEURI) {
	throw DOMException(DOMException::NAMESPACE_ERR);
      }
    }
    else if((!namePrefix.isNCName()) |namePrefix.beginsWithxml()) {
      throw DOMException(DOMException::INVALID_CHARACTER_ERR);
    }

    if((!nameLocalPart.isNCName()) |nameLocalPart.beginsWithxml()) {
      throw DOMException(DOMException::INVALID_CHARACTER_ERR);
    }

    oldNode = myAttributesMap.getNamedItemNS(namespaceURI,nameLocalPart);
  }
  else {

    // was not a namespace name, in which case the name may be 'xmlns'
    // or anything not begining with xml,

    if(!qualifiedName.eqxmlns()) {
      if((!qualifiedName.isName()) |qualifiedName.beginsWithxml()) {
	throw DOMException(DOMException::INVALID_CHARACTER_ERR);
      }
    }

    oldNode = myAttributesMap.getNamedItemNS(namespaceURI,qualifiedName);
  }
 
  if(oldNode != 0) {
    oldNode->setPrefix(namePrefix);
    oldNode->setNodeValue(value);
  }
  else {
    Node* newAttr =	new KissAttr(ownerDocument(),this,namespaceURI,qualifiedName,1);
    newAttr->setNodeValue(value);

    myAttributesMap.setNamedItemNS(*newAttr);
  }
};

// ******************************************************************************
void KissElement::removeAttributeNS(
				    const DOMString& namespaceURI,
				    const DOMString& localname) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(namespaceURI.length()==0) {
    removeAttribute(localname);
  }

  try {
    Node* oldNode = myAttributesMap.removeNamedItemNS(namespaceURI,localname);
    if(oldNode != 0) {
      delete oldNode;
    }
  }
  catch (DOMException) {
  };
};

// ******************************************************************************
Attr* KissElement::getAttributeNodeNS(
				      const DOMString& namespaceURI,
				      const DOMString& localname) {

  if(namespaceURI.length()==0) {
    return getAttributeNode(localname);
  }

  return dynamic_cast<Attr*> (myAttributesMap.getNamedItemNS(namespaceURI,localname));
};

// ******************************************************************************
Attr* KissElement::setAttributeNodeNS(
				      Attr& newAttr) {

  if(myReadOnly |newAttr.readOnly()) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(newAttr.ownerElement() != 0) {
    throw DOMException(DOMException::INUSE_ATTRIBUTE_ERR);
  }

  newAttr.setOwnerElement(this);
  return dynamic_cast<Attr*>(myAttributesMap.setNamedItemNS(newAttr)); 
};

// ******************************************************************************
NodeList* KissElement::getElementsByTagNameNS(
					      const DOMString& namespaceURI,
					      const DOMString& localname) {

  myElementList.clear();

  Node* nextNode = firstChild();

  while (nextNode != 0) {
    if(nextNode->nodeType() == ELEMENT_NODE) {
      if(nextNode->namespaceURI()->length() > 0) {
	if(localname == "*") {
	  myElementList.push_back(nextNode);
	}
	else if((*nextNode->namespaceURI() == namespaceURI) &(*nextNode->localName() == localname)) {
	  myElementList.push_back(nextNode);
	}
      }
    }
    if(nextNode->hasChildNodes()) {
      nextNode = nextNode->firstChild();
    }
    else {
      while((nextNode->nextSibling() == 0) &(nextNode->parentNode() != 0) &(nextNode != this)) {
	nextNode = nextNode->parentNode();
      }
    }

    if(nextNode == this) {
      nextNode = 0;
    }

    if(nextNode != 0) {
      nextNode = nextNode->nextSibling();
    }
  }

  return &myElementNodeList;
};

// ******************************************************************************
bool KissElement::hasAttribute(
			       const DOMString& name) const {

  const Node* oldNode = myAttributesMap.getNamedItem(name);

  return (oldNode != 0);
};

// ******************************************************************************
bool KissElement::hasAttributeNS(
				 const DOMString& namespaceURI,
				 const DOMString& localname) const {

  if(namespaceURI.length()==0) {
    return hasAttribute(localname);
  }

  const Node* oldNode = myAttributesMap.getNamedItemNS(namespaceURI,localname);

  return (oldNode != 0);
};	

// ******************************************************************************
const DOMString* KissElement::lookupNamespacePrefixNonDefault(
							      const DOMString& namespaceURI) const {

  if(DEBUGKISSDOM) {
    printf("KissElement::lookupNamespacePrefixNonDefault() [%s]\n",nodeName()->c_str());
  }
			
  if(namespaceURI.length() == 0) {
    return 0;
  }

  const Node* nextAncestor = this;

  while(nextAncestor != 0) {

    if(nextAncestor->hasAttributes()) {
      for(unsigned long i = 0; i < nextAncestor->attributes()->length(); i++) {
	if(*nextAncestor->attributes()->item(i)->nodeValue() == namespaceURI) {
	  if(nextAncestor->attributes()->item(i)->prefix()->eqxmlns()) {
	    return nextAncestor->attributes()->item(i)->localName();
	  }
	}
      }
    }

    nextAncestor = nextAncestor->parentNode();
  }

  return 0;
};

// ******************************************************************************
// ******************************************************************************
//	KissAttr
// ******************************************************************************
// ******************************************************************************

long nKissAttrs=0;  //!< Number of KISS attribute objects

// ******************************************************************************
const DOMString* KissAttr::nodeValue() const {
  return &myAttributeValue;
};

// ******************************************************************************
void KissAttr::setNodeValue(
			    const DOMString& newNodeValue) {
  if (DEBUGKISSDOM) {
    printf("KissAttr::setNodeValue()\n");
  }
  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }
  myAttributeValue = newNodeValue;
};

// ******************************************************************************
unsigned long KissAttr::nodeType() const {
  return ATTRIBUTE_NODE;
};

// ******************************************************************************
void KissAttr::setParentNode(
			     Node* newParentNode) {
  throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
};

// ******************************************************************************
Node* KissAttr::cloneNode(
			  const bool& deep) const {

  Node* newAttr = new KissAttr(ownerDocument(),0,myNamespaceURI,myNodeName,1);

  newAttr->setNodeValue(myAttributeValue);

  if(deep) {
    for(unsigned long i = 0; i < childNodes()->length(); i++) {
      newAttr->appendChild(childNodes()->item(i)->cloneNode(deep));
    }
  }

  return newAttr;
};

// ******************************************************************************
const DOMString* KissAttr::namespaceURI() const {
  return &myNamespaceURI;
};

// ******************************************************************************
const DOMString* KissAttr::prefix() const {
  return &myPrefix;
};

// ******************************************************************************
void KissAttr::setPrefix(
			 const DOMString& newPrefix) {

  // if I am not a namespace element do nothing

  if(myNamespaceURI.length() == 0) {
    return;
  }

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(newPrefix == 0) {
    throw DOMException(DOMException::NAMESPACE_ERR);
  }

  if(!newPrefix.isNCName()) {
    throw DOMException(DOMException::INVALID_CHARACTER_ERR);
  }
	
  if(newPrefix.eqxml() &(myNamespaceURI != XML_NAMESPACEURI)) {
    throw DOMException(DOMException::NAMESPACE_ERR);
  }

  if(newPrefix.eqxmlns() &((myNamespaceURI != XMLNS_NAMESPACEURI) |(myNodeName.eqxmlns()))) {
    throw DOMException(DOMException::NAMESPACE_ERR);
  }

  myPrefix = newPrefix;
  if(myPrefix.length() != 0) {
    myNodeName = myPrefix;
    myNodeName += ":";
    myNodeName += myLocalname;
  }
  else
    myNodeName = myLocalname;
};

// ******************************************************************************
const DOMString* KissAttr::localName() const {
  return &myLocalname;
};

// ******************************************************************************
Node::DocumentOrder KissAttr::compareDocumentOrder(
						   const Node* other) const {

  return DOCUMENT_ORDER_UNORDERED;
};

// ******************************************************************************
Node::TreePosition KissAttr::compareTreePosition(
						 const Node* other) const {

  return TREE_POSITION_UNORDERED;
};

// ******************************************************************************
void KissAttr::checkChildAddingConstraints2(const Node* newChild) const {

  switch(newChild->nodeType()) {
  case TEXT_NODE :
  case ENTITY_REFERENCE_NODE :
    break;
  default :
    throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
  }
};

// ******************************************************************************
KissAttr::KissAttr(
		   const Document *const yourOwnerDocument,
		   const Element *const yourOwnerElement,			
		   const DOMString& yourNamespaceURI,
		   const DOMString& yourAttributeName,
		   const bool& isSpecified) :
  KissNode(yourOwnerDocument,0,yourAttributeName),
  myNamespaceURI(yourNamespaceURI) {
  if(DEBUGKISSDOM) {
    nKissAttrs++;
    printf("KissAttr::KissAttr(); [%s]\n",yourAttributeName.c_str());
    printf("nKissAttrs=%li\n",nKissAttrs);
  }

  myOwnerElement = yourOwnerElement;
  yourAttributeName.splitNSName(myPrefix,myLocalname);
  amSpecified = isSpecified;
};

// ******************************************************************************
KissAttr::~KissAttr() {
  if(DEBUGKISSDOM) {
    nKissAttrs--;
    printf("KissAttr::~KissAttr(); [%s]\n", myNodeName.c_str());
    printf("nKissAttrs=%li\n",nKissAttrs);
  }

};

// ******************************************************************************
const DOMString* KissAttr::name() const {
  return nodeName();
};

// ******************************************************************************
bool KissAttr::specified() const {
  return amSpecified;
};

// ******************************************************************************
void KissAttr::setSpecified(
			    const bool& newSpecified) {
  amSpecified = newSpecified;
};

// ******************************************************************************
const DOMString* KissAttr::value() const {
  return nodeValue();
};

// ******************************************************************************
void KissAttr::setValue(
			const DOMString& newValue) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  myAttributeValue = newValue;
};

// ******************************************************************************
const Element* KissAttr::ownerElement() const {
  return myOwnerElement;
};

// ******************************************************************************
void KissAttr::setOwnerElement(
			       const Element* newOwnerElement) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  myOwnerElement = newOwnerElement;
};

// ******************************************************************************
// ******************************************************************************
//	KissCharacterData
// ******************************************************************************
// ******************************************************************************

long nKissCharacterDatas=0;  //!< Number of KISS character data objects

// ******************************************************************************
const DOMString* KissCharacterData::nodeValue() const {
  return &myData;
};

// ******************************************************************************
void KissCharacterData::setNodeValue(
				     const DOMString& newNodeValue) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  myData = newNodeValue;
};

// ******************************************************************************
Node* KissCharacterData::insertBefore(
				      Node* newChild,
				      Node* refChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissCharacterData::replaceChild(
				      Node* newChild,
				      Node* oldChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissCharacterData::removeChild(
				     Node* oldChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissCharacterData::appendChild(
				     Node* newChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
void KissCharacterData::setTextContent(
				       const DOMString& newTextContent) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  myData = newTextContent;
};

// ******************************************************************************
KissCharacterData::KissCharacterData(
				     const Document *const yourOwnerDocument,
				     Node *const yourParentNode,
				     const DOMString& yourNodeName,
				     const DOMString& yourData) :
  KissNode(yourOwnerDocument,yourParentNode,yourNodeName) {
  if(DEBUGKISSDOM) {
    nKissCharacterDatas++;
    printf("KissCharacterData::KissCharacterData(); [%s]\n",yourNodeName.c_str());
    printf("nKissCharacterDatas=%li\n",nKissCharacterDatas);
  }

  myData = yourData;
};

// ******************************************************************************
KissCharacterData::~KissCharacterData() {
  if(DEBUGKISSDOM) {
    nKissCharacterDatas--;
    printf("KissCharacterData::~KissCharacterData(); [%s]\n",myNodeName.c_str());
    printf("nKissCharacterDatas=%li\n",nKissCharacterDatas);
  }
};

// ******************************************************************************
const DOMString* KissCharacterData::data() const {
  return &myData;
};

// ******************************************************************************
unsigned long KissCharacterData::length() const {
  return myData.length();
};

// ******************************************************************************
const DOMString* KissCharacterData::substringData(
						  unsigned long offset,
						  unsigned long count) const {

  if(offset > myData.length()) {
    throw DOMException(DOMException::INDEX_SIZE_ERR);
  }

  unsigned long end = offset + count;
  if(end > myData.length()) {
    end = myData.length();
  }

  myData.subString(mySubStringData,offset,end);

  return &mySubStringData;
};

// ******************************************************************************
void KissCharacterData::appendData(
				   const DOMString& arg) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  myData += arg;
};

// ******************************************************************************
void KissCharacterData::insertData(
				   unsigned long offset,
				   const DOMString& arg) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(offset > myData.length()) {
    throw DOMException(DOMException::INDEX_SIZE_ERR);
  }

  myData.insertString(offset,arg);
};

// ******************************************************************************
void KissCharacterData::deleteData(
				   unsigned long offset,
				   unsigned long count) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(offset > myData.length()) {
    throw DOMException(DOMException::INDEX_SIZE_ERR);
  }

  myData.deleteData(offset,count);
};

// ******************************************************************************
void KissCharacterData::replaceData(
				    unsigned long offset,
				    unsigned long count,
				    const DOMString& arg) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(offset > myData.length()) {
    throw DOMException(DOMException::INDEX_SIZE_ERR);
  }

  myData.replaceData(offset,count,arg);
};

// ******************************************************************************
void KissCharacterData::setData(
				const DOMString& newData) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  myData = newData;
};

// ******************************************************************************
// ******************************************************************************
//	KissTextInt
// ******************************************************************************
// ******************************************************************************

long nKissTextInts=0;  //!< Number of KISS text int objects

// ******************************************************************************
KissTextInt::KissTextInt(
			 const Document *const yourOwnerDocument,
			 Node *const yourParentNode,
			 const DOMString& yourNodeName,
			 const DOMString& yourTextContent) :
  KissCharacterData(yourOwnerDocument,yourParentNode,yourNodeName,yourTextContent) {
  if(DEBUGKISSDOM) {
    nKissTextInts++;
    printf("KissTextInt::KissTextInt(); [%s]\n",yourNodeName.c_str());
    printf("nKissTextInts=%li\n",nKissTextInts);
  }
};

// ******************************************************************************
KissTextInt::~KissTextInt() {
  if(DEBUGKISSDOM) {
    nKissTextInts--;
    printf("KissTextInt::~KissTextInt(); [%s]\n", myNodeName.c_str());
    printf("nKissTextInts=%li\n",nKissTextInts);
  }
};

// ******************************************************************************
Text* KissTextInt::splitText(
			     unsigned long offset) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  if(parentNode() != 0) {
    if(parentNode()->readOnly()) {
      throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
    }
  }

  if(offset > length()) {
    throw DOMException(DOMException::INDEX_SIZE_ERR);
  }

  const DOMString* data2 = substringData(offset,length()-offset);

  Text* newKissText = new KissText(ownerDocument(),0,*data2);

  deleteData(offset,length()-offset);

  if(parentNode() != 0) {
    parentNode()->insertBefore(newKissText,nextSibling());
  }

  return newKissText;
};

// ******************************************************************************
bool KissTextInt::isWhiteSpaceInElementContent() const {
  return 0;
};

// ******************************************************************************
const DOMString* KissTextInt::wholeText() const {

  const Node* nextNode;

  myWholeTextString = *data();

  // add text nodes before this one
  nextNode = previousSibling();
  while(nextNode != 0) {
    if(nextNode->nodeType() == TEXT_NODE) {
      myWholeTextString.insertString(0,*nextNode->nodeValue());
      nextNode = previousSibling();
    }
    else
      nextNode = 0;
  }

  // add text nodes after this one
  nextNode = nextSibling();
  while(nextNode != 0) {
    if(nextNode->nodeType() == TEXT_NODE) {
      myWholeTextString += *nextNode->nodeValue();
      nextNode = nextSibling();
    }
    else
      nextNode = 0;
  }

  return &myWholeTextString;
};

// ******************************************************************************
Text* KissTextInt::replaceWholeText(
				    const DOMString& content) {

  if(parentNode() == 0) {
    setData(content);
    return this;
  }

  if(parentNode()->readOnly() |myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  Node* nextNode;

  // check readOnly for text nodes before this one
  nextNode = previousSibling();
  while(nextNode != 0) {
    if(nextNode->nodeType() == TEXT_NODE) {
      if(nextNode->readOnly()) {
	throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
      }
      nextNode = previousSibling();
    }
    else
      nextNode = 0;
  }

  // check readOnly for text nodes after this one
  nextNode = nextSibling();
  while(nextNode != 0) {
    if(nextNode->nodeType() == TEXT_NODE) {
      if(nextNode->readOnly()) {
	throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
      }
      nextNode = nextSibling();
    }
    else
      nextNode = 0;
  }

  // remove text nodes before this one
  nextNode = previousSibling();
  while(nextNode != 0) {
    if(nextNode->nodeType() == TEXT_NODE) {
      parentNode()->removeChild(nextNode);
      delete nextNode;
      nextNode = previousSibling();
    }
    else
      nextNode = 0;
  }

  // remove text nodes after this one
  nextNode = nextSibling();
  while(nextNode != 0) {
    if(nextNode->nodeType() == TEXT_NODE) {
      parentNode()->removeChild(nextNode);
      delete nextNode;
      nextNode = nextSibling();
    }
    else
      nextNode = 0;
  }

  setData(content);
  return this;
};

// ******************************************************************************
// ******************************************************************************
//	KissText
// ******************************************************************************
// ******************************************************************************

long nKissTexts=0;  //!< Number of KISS text objects

// ******************************************************************************
unsigned long KissText::nodeType() const {
  return TEXT_NODE;
};

// ******************************************************************************
Node* KissText::cloneNode(
			  const bool& deep) const {
  return new KissText(ownerDocument(),0,*data());
};

// ******************************************************************************
KissText::KissText(
		   const Document *const yourOwnerDocument,
		   Node *const yourParentNode,
		   const DOMString& yourTextContent) :
  KissTextInt(yourOwnerDocument,yourParentNode,"#text",yourTextContent) {
  if(DEBUGKISSDOM) {
    nKissTexts++;
    printf("KissText::KissText();\n");
    printf("nKissTexts=%li\n",nKissTexts);
  }
};

// ******************************************************************************
KissText::~KissText() {
  if(DEBUGKISSDOM) {
    nKissTexts--;
    printf("KissText::~KissText();\n");
    printf("nKissTexts=%li\n",nKissTexts);
  }
};

// ******************************************************************************
// ******************************************************************************
//	KissDocumentFragment
// ******************************************************************************
// ******************************************************************************

long nKissDocumentFragments=0;  //!< Number of KISS document fragment objects

// ******************************************************************************
unsigned long KissDocumentFragment::nodeType() const {
  return DOCUMENT_FRAGMENT_NODE;
};

// ******************************************************************************
Node* KissDocumentFragment::cloneNode(
				      const bool& deep) const {

  Node* newDocumentFragment = new KissDocumentFragment(ownerDocument());

  if(deep) {
    for(unsigned long i = 0; i < childNodes()->length(); i++) {
      newDocumentFragment->appendChild(childNodes()->item(i)->cloneNode(deep));
    }
  }

  return newDocumentFragment;
};

// ******************************************************************************
void KissDocumentFragment::normalizeNS() {

  for(unsigned long i = 0; i < childNodes()->length(); i++) {
    childNodes()->item(i)->normalizeNS();
  }
};

// ******************************************************************************
KissDocumentFragment::KissDocumentFragment(
					   const Document *const yourOwnerDocument) :
  KissNode(yourOwnerDocument,0,"#document-fragment") {
  if(DEBUGKISSDOM) {
    nKissDocumentFragments++;
    printf("KissDocumentFragment::KissDocumentFragment();\n");
    printf("nKissDocumentFragments=%li\n",nKissDocumentFragments);
  }
};

// ******************************************************************************
KissDocumentFragment::~KissDocumentFragment() {
  if(DEBUGKISSDOM) {
    nKissDocumentFragments--;
    printf("KissDocumentFragment::~KissDocumentFragment();\n");
    printf("nKissDocumentFragments=%li\n",nKissDocumentFragments);
  }
};

// ******************************************************************************
// ******************************************************************************
//	KissComment
// ******************************************************************************
// ******************************************************************************

long nKissComments=0;  //!< Number of KISS comment objects

// ******************************************************************************
unsigned long KissComment::nodeType() const {
  return COMMENT_NODE;
};

// ******************************************************************************
Node* KissComment::cloneNode(
			     const bool& deep) const {
  return new KissComment(ownerDocument(),0,*data());
};

// ******************************************************************************
KissComment::KissComment(
			 const Document *const yourOwnerDocument,
			 Node *const yourParentNode,
			 const DOMString& yourTextContent) :
  KissCharacterData(yourOwnerDocument,yourParentNode,"#comment",yourTextContent) {
  if(DEBUGKISSDOM) {
    nKissComments++;
    printf("KissComment::KissComment();\n");
    printf("nKissComments=%li\n",nKissComments);
  }
};

// ******************************************************************************
KissComment::~KissComment() {
  if(DEBUGKISSDOM) {
    nKissComments--;
    printf("KissComment::~KissComment();\n");
    printf("nKissComments=%li\n",nKissComments);
  }
};

// ******************************************************************************
// ******************************************************************************
// ******************************************************************************
//	The Extended Interfaces
// ******************************************************************************
// ******************************************************************************
// ******************************************************************************


// ******************************************************************************
// ******************************************************************************
//	KissEntity
// ******************************************************************************
// ******************************************************************************

long nKissEntitys=0;  //!< Number of KISS entity objects

// ******************************************************************************
unsigned long KissEntity::nodeType() const {
  return ENTITY_NODE;
};

// ******************************************************************************
const DOMString* KissEntity::nodeValue() const {
  return &myEntityValue;
};

// ******************************************************************************
void KissEntity::setParentNode(
			       Node* newParentNode) {
  throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
};

// ******************************************************************************
Node* KissEntity::cloneNode(
			    const bool& deep) const {

  Node* newEntity = new KissEntity(ownerDocument(),myNodeName,myPublicId,mySystemId,myNotationName,myEntityValue);

  if(deep) {
    for(unsigned long i = 0; i < childNodes()->length(); i++) {
      newEntity->appendChild(childNodes()->item(i)->cloneNode(deep));
    }
  }

  return newEntity;
};

// ******************************************************************************
Node::DocumentOrder KissEntity::compareDocumentOrder(
						     const Node* other) const {

  return DOCUMENT_ORDER_UNORDERED;
};

// ******************************************************************************
Node::TreePosition KissEntity::compareTreePosition(
						   const Node* other) const {

  return TREE_POSITION_UNORDERED;
};

// ******************************************************************************
KissEntity::KissEntity(
		       const Document *const yourOwnerDocument,
		       const DOMString& yourEntityName,
		       const DOMString& yourPublicId,
		       const DOMString& yourSystemId,
		       const DOMString& yourNotationName,
		       const DOMString& yourEntityValue) :
  KissNode(yourOwnerDocument,0,yourEntityName),
  myPublicId(yourPublicId),
  mySystemId(yourSystemId),
  myNotationName(yourNotationName),
  myEntityValue(yourEntityValue) {
  if(DEBUGKISSDOM) {
    nKissEntitys++;
    printf("KissEntity::KissEntity(); [%s]\n", myNodeName.c_str());
    printf("nKissEntitys=%li\n",nKissEntitys);
  }

  myReadOnly = 1;
};

// ******************************************************************************
KissEntity::~KissEntity() {
  if(DEBUGKISSDOM) {
    nKissEntitys--;
    printf("KissEntity::~KissEntity(); [%s]\n", myNodeName.c_str());
    printf("nKissEntitys=%li\n",nKissEntitys);
  }
}

// ******************************************************************************
const DOMString* KissEntity::publicId() const {
  return &myPublicId;
};

// ******************************************************************************
const DOMString* KissEntity::systemId() const {
  return &mySystemId;
};

// ******************************************************************************
const DOMString* KissEntity::notationName() const {
  return &myNotationName;
};

// ******************************************************************************
const DOMString* KissEntity::actualEncoding() const {
  return &myActualEncoding;
};

// ******************************************************************************
void KissEntity::setActualEncoding(
				   const DOMString& newActualEncoding) {

  myActualEncoding = newActualEncoding;
};

// ******************************************************************************
const DOMString* KissEntity::encoding() const {
  return &myEncoding;
};

// ******************************************************************************
void KissEntity::setEncoding(
			     const DOMString& newEncoding) {

  myEncoding = newEncoding;
};

// ******************************************************************************
const DOMString* KissEntity::version() const {
  return &myVersion;
};

// ******************************************************************************
void KissEntity::setVersion(
			    const DOMString& newVersion) {

  myVersion = newVersion;
};

// ******************************************************************************
// ******************************************************************************
//	KissDocumentType
// ******************************************************************************
// ******************************************************************************

long nKissDocumentTypes=0;  //!< Number of KISS document type objects

// ******************************************************************************
unsigned long KissDocumentType::nodeType() const {
  return DOCUMENT_TYPE_NODE;
};

// ******************************************************************************
Node* KissDocumentType::insertBefore(
				     Node* newChild,
				     Node* refChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissDocumentType::replaceChild(
				     Node* newChild,
				     Node* oldChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissDocumentType::removeChild(
				    Node* oldChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissDocumentType::appendChild(
				    Node* newChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissDocumentType::cloneNode(
				  const bool& deep) const {
  return new KissDocumentType(myNodeName,myPublicId,mySystemId);
};

// ******************************************************************************
void KissDocumentType::setTextContent(
				      const DOMString& newTextContent) {
  throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
};

// ******************************************************************************
KissDocumentType::KissDocumentType(
				   const DOMString& yourDocumentTypeName,
				   const DOMString& yourPublicId,
				   const DOMString& yourSystemId) :
  KissNode(0,0,yourDocumentTypeName),
  myEntitiesMap(ENTITY_NODE),
  myNotationsMap(NOTATION_NODE) {
  if(DEBUGKISSDOM) {
    nKissDocumentTypes++;
    printf("KissDocumentType::KissDocumentType(); [%s]\n",yourDocumentTypeName.c_str());
    printf("nKissDocumentTypes=%li\n",nKissDocumentTypes);
  }

  myPublicId = yourPublicId;
  mySystemId = yourSystemId;
};

// ******************************************************************************
KissDocumentType::~KissDocumentType() {
  if(DEBUGKISSDOM) {
    nKissDocumentTypes--;
    printf("KissDocumentType::~KissDocumentType(); [%s]\n",myNodeName.c_str());
    printf("nKissDocumentTypes=%li\n",nKissDocumentTypes);
  }
};

// ******************************************************************************
const DOMString* KissDocumentType::name() const {
  return nodeName();
};

// ******************************************************************************
const NamedNodeMap* KissDocumentType::entities() const {
  return &myEntitiesMap;
};

// ******************************************************************************
const NamedNodeMap* KissDocumentType::notations() const {
  return &myNotationsMap;
};

// ******************************************************************************
const DOMString* KissDocumentType::publicId() const {
  return &myPublicId;
};

// ******************************************************************************
const DOMString* KissDocumentType::systemId() const {
  return &mySystemId;
};

// ******************************************************************************
const DOMString* KissDocumentType::internalSubset() const {
  return 0;
};

// ******************************************************************************
// ******************************************************************************
//	KissProcessingInstruction
// ******************************************************************************
// ******************************************************************************

long nKissProcessingInstructions=0;  //!< Number of KISS processing instruction objects

// ******************************************************************************
const DOMString* KissProcessingInstruction::nodeValue() const {
  return &myContent;
};

// ******************************************************************************
void KissProcessingInstruction::setNodeValue(
					     const DOMString& newNodeValue) {
  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }
  myContent = newNodeValue;
};

// ******************************************************************************
unsigned long KissProcessingInstruction::nodeType() const {
  return PROCESSING_INSTRUCTION_NODE;
};

// ******************************************************************************
Node* KissProcessingInstruction::insertBefore(
					      Node* newChild,
					      Node* refChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissProcessingInstruction::replaceChild(
					      Node* newChild,
					      Node* oldChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissProcessingInstruction::removeChild(
					     Node* oldChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissProcessingInstruction::appendChild(
					     Node* newChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissProcessingInstruction::cloneNode(
					   const bool& deep) const {
  return new KissProcessingInstruction(ownerDocument(),0,myNodeName,myContent);
};

// ******************************************************************************
void KissProcessingInstruction::setTextContent(
					       const DOMString& newTextContent) {

  if(myReadOnly) {
    throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
  }

  myContent = newTextContent;
};

// ******************************************************************************
KissProcessingInstruction::KissProcessingInstruction(
						     const Document *const yourOwnerDocument,
						     Node *const yourParentNode,
						     const DOMString& yourTarget,
						     const DOMString& yourContent) :
  KissNode(yourOwnerDocument,yourParentNode,yourTarget) {
  if(DEBUGKISSDOM) {
    nKissProcessingInstructions++;
    printf("KissProcessingInstruction::KissProcessingInstruction(); [%s]\n",yourTarget.c_str());
    printf("nKissProcessingInstructions=%li\n",nKissProcessingInstructions);
  }
  myContent = yourContent;
};

// ******************************************************************************
KissProcessingInstruction::~KissProcessingInstruction() {
  if(DEBUGKISSDOM) {
    nKissProcessingInstructions--;
    printf("KissProcessingInstruction::~KissProcessingInstruction(); [%s]\n",myNodeName.c_str());
    printf("nKissProcessingInstructions=%li\n",nKissProcessingInstructions);
  }
};

// ******************************************************************************
const DOMString* KissProcessingInstruction::target() const {
  if(DEBUGKISSDOM) {
    printf("KissProcessingInstruction::target(); [%s]\n",myNodeName.c_str());
  }

  return nodeName();
};

// ******************************************************************************
const DOMString* KissProcessingInstruction::data() const {
  if(DEBUGKISSDOM) {
    printf("KissProcessingInstruction::data(); [%s]\n",myNodeName.c_str());
  }

  return nodeValue();
};

// ******************************************************************************
void KissProcessingInstruction::setData(
					const DOMString& newData) {
  if(DEBUGKISSDOM) {
    printf("KissProcessingInstruction::setData(); [%s]\n",myNodeName.c_str());
  }

  setNodeValue(newData);
};

// ******************************************************************************
// ******************************************************************************
//	KissNotation
// ******************************************************************************
// ******************************************************************************

long nKissNotations=0;  //!< Number of KISS notation objects

// ******************************************************************************
unsigned long KissNotation::nodeType() const {
  return NOTATION_NODE;
};

// ******************************************************************************
void KissNotation::setParentNode(
				 Node* newParentNode) {
  throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
};

// ******************************************************************************
Node* KissNotation::insertBefore(
				 Node* newChild,
				 Node* refChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissNotation::replaceChild(
				 Node* newChild,
				 Node* oldChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissNotation::removeChild(
				Node* oldChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissNotation::appendChild(
				Node* newChild) {
  throw DOMException(DOMException::HIERARCHY_REQUEST_ERR);
};

// ******************************************************************************
Node* KissNotation::cloneNode(
			      const bool& deep) const {

  return new KissNotation(myNodeName,myPublicId,mySystemId);
};

// ******************************************************************************
Node::DocumentOrder KissNotation::compareDocumentOrder(
						       const Node* other) const {

  return DOCUMENT_ORDER_UNORDERED;
};

// ******************************************************************************
Node::TreePosition KissNotation::compareTreePosition(
						     const Node* other) const {

  return TREE_POSITION_UNORDERED;
};

// ******************************************************************************
void KissNotation::setTextContent(
				  const DOMString& newTextContent) {
  throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR);
};

// ******************************************************************************
KissNotation::KissNotation(
			   const DOMString& yourNotationName,
			   const DOMString& yourPublicId,
			   const DOMString& yourSystemId) :
  KissNode(0,0,yourNotationName),
  myNotationName(yourNotationName),
  myPublicId(yourPublicId),
  mySystemId(yourSystemId) {
  if(DEBUGKISSDOM) {
    nKissNotations++;
    printf("KissNotation::KissNotation(); [%s]\n", myNodeName.c_str());
    printf("nKissNotations=%li\n",nKissNotations);
  }

};

// ******************************************************************************
KissNotation::~KissNotation() {
  if(DEBUGKISSDOM) {
    nKissNotations--;
    printf("KissNotation::~KissNotation(); [%s]\n", myNodeName.c_str());
    printf("nKissNotations=%li\n",nKissNotations);
  }

};

// ******************************************************************************
const DOMString* KissNotation::publicId() const {
  return &myPublicId;
};

// ******************************************************************************
const DOMString* KissNotation::systemId() const {
  return &mySystemId;
};

// ******************************************************************************
// ******************************************************************************
//	KissEntityReference
// ******************************************************************************
// ******************************************************************************

long nKissEntityReferences=0;  //!< Number of KISS entity reference objects

// ******************************************************************************
unsigned long KissEntityReference::nodeType() const {
  return ENTITY_REFERENCE_NODE;
};

// ******************************************************************************
Node* KissEntityReference::cloneNode(
				     const bool& deep) const {

  Node* newEntityReference = new KissEntityReference(ownerDocument(),0,myNodeName);

  if(deep) {
    for(unsigned long i = 0; i < childNodes()->length(); i++) {
      newEntityReference->appendChild(childNodes()->item(i)->cloneNode(deep));
    }
  }

  return newEntityReference;
};

// ******************************************************************************
KissEntityReference::KissEntityReference(
					 const Document *const yourOwnerDocument,
					 Node *const yourParentNode,
					 const DOMString& yourEntityNameReference) :
  KissNode(yourOwnerDocument,yourParentNode,yourEntityNameReference) {
  if(DEBUGKISSDOM) {
    nKissEntityReferences++;
    printf("KissEntityReference::KissEntityReference(); [%s]\n",yourEntityNameReference.c_str());
    printf("nKissEntityReferences=%li\n",nKissEntityReferences);
  }
};

// ******************************************************************************
KissEntityReference::~KissEntityReference() {
  if(DEBUGKISSDOM) {
    nKissEntityReferences--;
    printf("KissEntityReference::~KissEntityReference(); [%s]\n",myNodeName.c_str());
    printf("nKissEntityReferences=%li\n",nKissEntityReferences);
  }
};

// ******************************************************************************
// ******************************************************************************
//	KissCDATASection
// ******************************************************************************
// ******************************************************************************

long nKissCDATASections=0;  //!< Number of KISS CDATA section objects

// ******************************************************************************
unsigned long KissCDATASection::nodeType() const {
  return CDATA_SECTION_NODE;
};

// ******************************************************************************
Node* KissCDATASection::cloneNode(
				  const bool& deep) const {
  return new KissCDATASection(ownerDocument(),0,*data());
};

// ******************************************************************************
KissCDATASection::KissCDATASection(
				   const Document *const yourOwnerDocument,
				   Node *const yourParentNode,
				   const DOMString& yourCDATASectionContent) :
  KissTextInt(yourOwnerDocument,yourParentNode,"#cdata-section",yourCDATASectionContent) {
  if(DEBUGKISSDOM) {
    nKissCDATASections++;
    printf("KissCDATASection::KissCDATASection();\n");
    printf("nKissCDATASections=%li\n",nKissCDATASections);
  }
};

// ******************************************************************************
KissCDATASection::~KissCDATASection() {
  if(DEBUGKISSDOM) {
    nKissCDATASections--;
    printf("KissCDATASection::~KissCDATASection();\n");
    printf("nKissCDATASections=%li\n",nKissCDATASections);
  }
};



syntax highlighted by Code2HTML, v. 0.9.1