2005/5/9

     
 

LibXMLNode.cpp

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*- 
//
// Copyright(C) 2000-2003 by Roger Rene Kommer / artefaktur, Kassel, Germany.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
// 
// This library 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 
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
// 
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
// 
// $Header: /cvsroot/acdk/acdk/acdk_xml/src/acdk/xml/libxmldom/LibXMLNode.cpp,v 1.11 2005/02/13 03:25:57 kommer Exp $
//#include "LibXMLNode.h"
#include "LibXMLNodeList.h"
#include "LibXMLElement.h"
#include "LibXMLComment.h"
#include "LibXMLText.h"
#include "LibXMLNamedNodeMap.h"
#include "LibXMLCDATASection.h"
#include "LibXMLAttr.h"
#include "LibXMLDocument.h"
#include "LibXMLNotation.h"
#include "LibXMLEntity.h"
#include "LibXMLEntityReference.h"
#include "LibXMLDOMInternals.h"
#include "LibXMLDocumentType.h"
#include "LibXMLProcessingInstruction.h"
#include "LibXMLDocumentFragment.h"
#include "LibXMLXPathResult.h"

#include <acdk/lang/NullPointerException.h>
#include <acdk/util/logging/Log.h>

#include <org/w3c/dom/xpath/XPathResult.h>
#include "../dom/NodeUtil.h"
#include "../../../libxml/HTMLtree.h"

#include <map>

namespace acdk {
namespace xml {
namespace libxmldom {

namespace { 

typedef std::map<xmlDocPtr, int> DocReferenceMap;

DocReferenceMap& 
getDocReferenceMap()
{
  static DocReferenceMap _map;
  return _map;
}


} // anon namespace
//static 
void 
XmlNodePtrHolder::registerNode(xmlNodePtr nodePtr)
{
  xmlDocPtr doc = nodePtr->doc;
  if (doc == 0)
    return ;
  DocReferenceMap& refmap = getDocReferenceMap();
  // ### @todo mt safe
  DocReferenceMap::iterator it = refmap.find(doc);
  if (it == refmap.end())
    refmap[doc] =  1;
  else
    ++it->second;

}

//static 
void 
XmlNodePtrHolder::unregisterNode(xmlNodePtr nodePtr)
{
   xmlDocPtr doc = nodePtr->doc;
  if (doc == 0)
    return ;
  DocReferenceMap& refmap = getDocReferenceMap();
  DocReferenceMap::iterator it = refmap.find(doc);
  if (it == refmap.end())
  {
    ACDK_NLOG("acdk.xml.libxmldom", Error, "freeing Unbound notePtr");
    return;
  }
  if ((--it->second) == 0)
  {
    refmap.erase(it);
    xmlFreeDoc(doc);
  }
}
// static 
void 
XmlNodePtrHolder::xmlFreeNode(xmlNodePtr cur)
{
  ::xmlFreeNode(cur);
}

using namespace org::w3c::dom;


LibXMLNode::LibXMLNode(xmlNodePtr np, FreeXmlNodePtrFuncPtr freeFuncPtr)
: _nodePtr(np, freeFuncPtr)
{
  _nodePtr->_private = (void*)this;
}

LibXMLNode::~LibXMLNode()
{
  if (_nodePtr->_private != 0)
  {
    if ((void*)_nodePtr->_private != this)
      ; // ooops
    _nodePtr->_private = 0;
  }
}

//foreign static 
RLibXMLNode 
LibXMLNode::newInstance(xmlDocPtr docPtr, xmlNodePtr nodePtr, int nodeType, bool ownsPtr)
{
  if (nodePtr == 0)
    return Nil;
  if (nodePtr->_private != 0)
  {
    LibXMLNode* np = (LibXMLNode*)nodePtr->_private;
    return np;
  }
  RLibXMLNode newInstance = Nil;
  switch(nodeType)
  {
  case XML_ELEMENT_NODE:
    newInstance = new LibXMLElement(nodePtr, ownsPtr);
    break;
  case XML_COMMENT_NODE:
    newInstance = new LibXMLComment(nodePtr, ownsPtr);
    break;
  case XML_TEXT_NODE:
    newInstance = new LibXMLText(nodePtr, ownsPtr);
    break;
  case XML_CDATA_SECTION_NODE:
    newInstance = new LibXMLCDATASection(nodePtr, ownsPtr);
    break;
  case XML_ATTRIBUTE_NODE:
    newInstance = new LibXMLAttr(nodePtr, ownsPtr);
    break;
  case XML_DOCUMENT_NODE:
    newInstance = new LibXMLDocument(nodePtr, 0);
    break;
  case XML_DOCUMENT_TYPE_NODE:
    newInstance = new LibXMLDocumentType(nodePtr, ownsPtr);
    break;
  case XML_NOTATION_NODE: //XML_DOCUMENT_NODE
    newInstance = new LibXMLNotation(nodePtr, ownsPtr);
    break;
  case XML_ENTITY_NODE:
    newInstance = new LibXMLEntity(nodePtr, ownsPtr);
    break;
  case XML_ENTITY_REF_NODE:
    newInstance = new LibXMLEntityReference(nodePtr, ownsPtr);
    break;
  case XML_PI_NODE: 
    newInstance = new LibXMLProcessingInstruction(nodePtr);
    break;
  case XML_DOCUMENT_FRAG_NODE:
    newInstance = new LibXMLDocumentFragment(nodePtr, ownsPtr);
    break;
  case XML_DTD_NODE:
    newInstance = new LibXMLDocumentType(nodePtr, ownsPtr); // ### @todo not sure, if this is correct
    break;
  
  default:
    newInstance = new LibXMLNode(nodePtr);
    break;
  }
  return newInstance;
}

//foreign static 
RLibXMLNode 
LibXMLNode::newInstance(xmlNodePtr nodePtr, bool ownsPtr)
{
  if (nodePtr == 0)
    return Nil;
  return newInstance(nodePtr->doc, nodePtr, nodePtr->type, ownsPtr);
}

RString 
LibXMLNode::getNodeName()
{
  return XML2STR(_nodePtr->name);
}





RString 
LibXMLNode::getBaseURI()
{
  return XML2STR(xmlNodeGetBase(_nodePtr->doc, _nodePtr));
}



const xmlChar* _getNodeValue(xmlNodePtr node)
{
  switch(node->type)
  {
  case XML_TEXT_NODE:
  case XML_CDATA_SECTION_NODE:
  case XML_COMMENT_NODE:
  case XML_ATTRIBUTE_NODE:
    return xmlNodeGetContent(node);
  default:
      return 0;
  }
}

RString 
LibXMLNode::getNodeValue() THROWS1(RDOMException)
{
  return XML2STR(_getNodeValue(_nodePtr));
}

void 
LibXMLNode::setNodeValue(IN(RString) nodeValue) THROWS1(RDOMException)
{
  switch(_nodePtr->type)
  {
  case XML_TEXT_NODE:
  case XML_CDATA_SECTION_NODE:
  case XML_COMMENT_NODE:
    xmlNodeSetContent(_nodePtr,(const unsigned char*)STR2XMLDUP(nodeValue));
    break;
  case XML_ATTRIBUTE_NODE:
  default:
      break;
  }
}

short 
LibXMLNode::getNodeType()
{
  return _mapLibXMLTypeToDomType(_nodePtr->type);
  /*
  if(_nodePtr->type == XML_DTD_NODE)
    return XML_DOCUMENT_TYPE_NODE;
  return _nodePtr->type;
  */
}


RNode 
LibXMLNode::getParentNode()
{
  return &newInstance(_nodePtr->parent);
}

RNodeList 
LibXMLNode::getChildNodes()
{
  return new LibXMLNodeList(_nodePtr);
}

RNode 
LibXMLNode::getFirstChild()
{
  return &newInstance(_nodePtr->children);
}

RNode 
LibXMLNode::getLastChild()
{
  return &newInstance(_nodePtr->last);
}

RNode 
LibXMLNode::getPreviousSibling()
{
  return &newInstance(_nodePtr->prev);
}

RNode 
LibXMLNode::getNextSibling()
{
  return &newInstance(_nodePtr->next);
}

RNamedNodeMap 
LibXMLNode::getAttributes()
{
  return new LibXMLNamedNodeMap(_nodePtr, NNMTAttributes);
}

RDocument 
LibXMLNode::getOwnerDocument()
{
  return(RDocument)newInstance((xmlNodePtr)_nodePtr->doc);
}

void
checkChildNode(xmlNodePtr parent, xmlNodePtr child, bool isNewNode)
{
  if(child == 0 || parent == 0)
    THROW2(DOMException, NOT_FOUND_ERR, "");
  if (child->doc != 0 || isNewNode == false)
  {
    if (child->doc != parent->doc)
      THROW2(DOMException, WRONG_DOCUMENT_ERR, "Child is not part of document");
  }
  switch(parent->type)
  {
  case XML_CDATA_SECTION_NODE:
  case XML_COMMENT_NODE:
  case XML_TEXT_NODE:
  case XML_ENTITY_NODE:
  case XML_ENTITY_REF_NODE:
  case XML_NOTATION_NODE:
  case XML_PI_NODE:
    THROW2(DOMException, HIERARCHY_REQUEST_ERR, "Parent does not allow to contains children");
  case XML_ATTRIBUTE_NODE:
    if(child->type != XML_TEXT_NODE && child->type != XML_ENTITY_REF_NODE)
      THROW2(DOMException, HIERARCHY_REQUEST_ERR, "Attributes can only contain text or entity references");
    break;
  case XML_DOCUMENT_FRAG_NODE:
  case XML_ELEMENT_NODE:
    if(child->type == XML_DTD_NODE || child->type == XML_DOCUMENT_TYPE_NODE ||
      child->type == XML_ENTITY_NODE || child->type == XML_NOTATION_NODE ||
      child->type == XML_PI_NODE)
      THROW2(DOMException, HIERARCHY_REQUEST_ERR, "Parent node does not allow child of this type");
    // no break
  default:
    if(child->type == XML_ATTRIBUTE_NODE ||
      child->type == XML_DOCUMENT_NODE ||
      child->type == XML_DOCUMENT_FRAG_NODE)
      THROW2(DOMException, HIERARCHY_REQUEST_ERR, "Node type is not allowed to be a child");
    break;
  }
  xmlNodePtr cur;
 for (cur = parent; cur != 0; cur = cur->parent)
  {
    if (cur == child)
      THROW2(DOMException, HIERARCHY_REQUEST_ERR, "child cannot be ancestor of itself");
  }
  if (parent->type == XML_DOCUMENT_NODE)
  {
    xmlNodePtr cur = parent->children;
    while(cur != 0)
    {
      if(cur->type == XML_DTD_NODE ||
          cur->type == XML_DOCUMENT_TYPE_NODE ||
         (cur->type == XML_ELEMENT_NODE && parent->type == XML_DOCUMENT_NODE))
      {
        if(child->type == cur->type && child != cur)
          THROW2(DOMException, HIERARCHY_REQUEST_ERR, "Adding second doctype or root element");
      }
      cur = cur->next;
        
    }
  }
  
}

void
ensureChildNode(xmlNodePtr parent, xmlNodePtr child)
{
  if (child->parent != 0 && child->parent == parent)
    return;
  /*
  for (xmlNodePtr cur = parent->children; cur != 0; cur = cur->next)
  {
    if (cur == child)
      return;
  }*/
  THROW2(DOMException, NOT_FOUND_ERR, "Is not child");
}

RNode 
LibXMLNode::insertBefore(IN(RNode) newChild, IN(RNode) refChild) THROWS1(RDOMException)
{
  if(instanceof(newChild, LibXMLNode) == false || instanceof(refChild, LibXMLNode) == false)
    THROW2(DOMException, NOT_FOUND_ERR, "Node is not a LibXMLNode");
  
  RLibXMLNode nc =(RLibXMLNode)newChild;
  RLibXMLNode rc =(RLibXMLNode)refChild;
  if(rc->_nodePtr->parent != _nodePtr)
    THROW2(DOMException, NOT_FOUND_ERR, "RefNode is not child of this Node");
  checkChildNode(_nodePtr, nc->_nodePtr, true);
  ensureChildNode(_nodePtr, rc->_nodePtr);
  xmlNodePtr newChildNode = xmlAddPrevSibling(rc->_nodePtr, nc->_nodePtr);
  nc->_nodePtr.share();
  return &newInstance(newChildNode);
}

RNode 
LibXMLNode::replaceChild(IN(RNode) newChild, IN(RNode) oldChild) THROWS1(RDOMException)
{
  if(instanceof(newChild, LibXMLNode) == false || instanceof(oldChild, LibXMLNode) == false)
    THROW2(DOMException, NOT_FOUND_ERR, "Node is not a LibXMLNode");
  RLibXMLNode nc =(RLibXMLNode)newChild;
  RLibXMLNode oc =(RLibXMLNode)oldChild;

  checkChildNode(_nodePtr, nc->_nodePtr, true);
  checkChildNode(_nodePtr, oc->_nodePtr, false);
  ensureChildNode(_nodePtr, oc->_nodePtr);
  xmlNodePtr newChildNode = xmlReplaceNode(oc->_nodePtr, nc->_nodePtr);
  // TODO 
  return &newInstance(newChildNode, true);
}

RNode 
LibXMLNode::removeChild(IN(RNode) oldChild) THROWS1(RDOMException)
{
  if(instanceof(oldChild, LibXMLNode) == false)
    THROW2(DOMException, NOT_FOUND_ERR, "Node is not a LibXMLNode");
  RLibXMLNode oc =(RLibXMLNode)oldChild;
  
  ensureChildNode(_nodePtr, oc->_nodePtr);
  xmlUnlinkNode(oc->_nodePtr);
  oc->_unshare(xmlFreeNode);
  return oldChild;
}

RNode 
LibXMLNode::appendChild(IN(RNode) newChild) THROWS1(RDOMException)
{
  if(instanceof(newChild, LibXMLNode) == false)
    THROW2(DOMException, NOT_FOUND_ERR, "Node is not a LibXMLNode");
  
  RLibXMLNode nc =(RLibXMLNode)newChild;
  checkChildNode(_nodePtr, nc->_nodePtr, true);
  xmlNodePtr sch = nc->_nodePtr;
  xmlNodePtr newChildNode = xmlAddChild(_nodePtr, sch);
  nc->_nodePtr.share();
  return &newInstance(newChildNode);
}

bool 
LibXMLNode::hasChildNodes()
{
  return _nodePtr->children != 0;
}

int 
LibXMLNode::getChildCount()
{
  if (_nodePtr->children == 0)
    return 0;
  xmlNodePtr node = _nodePtr->children;
  int count = 0;
  for(count = 0; node != 0; count++)
  {
    node = node->next;
  }
  return count;
}

// from http://search.cpan.org/src/PHISH/XML-LibXML-1.50/dom.c
/*
xmlNodePtr
domAppendChild( xmlNodePtr self,
                xmlNodePtr newChild ){
    if ( self == NULL ) {
        return newChild;
    }
   
    if ( !(domTestHierarchy(self, newChild)
           && domTestDocument(self, newChild))){
        xs_warn("HIERARCHIY_REQUEST_ERR\n"); 
        return NULL;
    }
 
    if ( newChild->doc == self->doc ){
        xmlUnlinkNode( newChild );
    }
    else {
        //xs_warn("WRONG_DOCUMENT_ERR - non conform implementation\n"); 
        newChild= domImportNode( self->doc, newChild, 1 );
    }
 
    if ( self->children != NULL ) {
        domAddNodeToList( newChild, self->last, NULL );
    }
    else if (newChild->type == XML_DOCUMENT_FRAG_NODE ) {
	    newChild->children->parent = self;
        self->children = newChild->children;
        self->last     = newChild->last;
        domAddNodeToList( newChild, self->last, NULL );
    }
    else {
        self->children = newChild;
        self->last     = newChild;
        newChild->parent= self;
    }
    
    return newChild;
}
*/

/*
xmlNodePtr node = xmlDocCopyNode(
> 		xpath_obj->nodesetval->nodeTab[i],
>                  dest_doc, 1 with children );
> 	// no don't xmlUnlinkNode(node);
> 	xmlAddChild(insert_point->nodesetval->nodeNr[0],
> 		node);
*/


RNode 
LibXMLNode::cloneNode(bool deep)
{
  xmlNodePtr newNode = xmlCopyNode(_nodePtr, deep);
  return &newInstance(newNode, true);
}

void normalizeNode(xmlNodePtr _nodePtr)
{
  xmlNodePtr cNode = _nodePtr->children;
  xmlNodePtr lNode = 0;
  while(cNode != 0)
  {
    switch(cNode->type)
    {
    case XML_CDATA_SECTION_NODE:
    case XML_TEXT_NODE:
      if(xmlIsBlankNode(cNode))
      {
        xmlNodePtr next = cNode->next;
        xmlUnlinkNode(cNode);
        xmlFreeNode(cNode);
        cNode = next;
        continue;
      }
      if(lNode != 0)
      {
        lNode = xmlTextMerge(lNode, cNode);
        xmlUnlinkNode(cNode);
        xmlFreeNode(cNode);
        cNode = lNode;
      }
      else
      {
        lNode = cNode;
      }
      break;
    default:
      lNode = 0;
      normalizeNode(cNode);
    }
    cNode = cNode->next;
  }
}

void 
LibXMLNode::normalize()
{
  normalizeNode(_nodePtr);
}

bool 
LibXMLNode::isSupported(IN(RString) feature, IN(RString) version)
{
  // ### @todo implement me
  return false;
}

RString 
LibXMLNode::getNamespaceURI()
{
  if(_nodePtr->type != XML_ELEMENT_NODE && _nodePtr->type != XML_ATTRIBUTE_NODE)
    return Nil;
  if(_nodePtr->ns == 0)
    return Nil;
  return XML2STR(_nodePtr->ns->href);
}

RString 
LibXMLNode::getPrefix()
{
  if(_nodePtr->type != XML_ELEMENT_NODE && _nodePtr->type != XML_ATTRIBUTE_NODE)
    return Nil;
  if(_nodePtr->ns == 0)
    return Nil;
  return XML2STR(_nodePtr->ns->prefix);
}

void 
LibXMLNode::setPrefix(IN(RString) prefix) THROWS1(RDOMException)
{
  const xmlChar* xmlPrefix = STR2XMLDUP(prefix);
  if(xmlValidateName(xmlPrefix, 0) != 0)
    THROW2(DOMException, INVALID_CHARACTER_ERR, "Prefix is not valid");
    
  if(_nodePtr->type != XML_ELEMENT_NODE && _nodePtr->type != XML_ATTRIBUTE_NODE)
    THROW2(DOMException, HIERARCHY_REQUEST_ERR, "Wrong node type to set Prefix");
  if(_nodePtr->ns == 0)
    THROW2(DOMException, NAMESPACE_ERR, "node has no namespace");
  if(_nodePtr->ns->prefix != 0)
    xmlFree((void*)_nodePtr->ns->prefix);
  _nodePtr->ns->prefix = xmlPrefix;
}

RString 
LibXMLNode::getLocalName()
{
  int qnameLen = 0;
  if(xmlSplitQName3(_nodePtr->name, &qnameLen) != 0)
    return XML2STR(_nodePtr->name + qnameLen);
  return XML2STR(_nodePtr->name);
}

bool 
LibXMLNode::hasAttributes()
{
  return _nodePtr->properties != 0;
}

RString 
LibXMLNode::lookupPrefix(IN(RString) namespaceURI)
{
  xmlNodePtr cnode = _nodePtr;
  xmlDocPtr doc = cnode->doc;
  if(cnode->type == XML_DOCUMENT_NODE)
  {
    doc =(xmlDocPtr)cnode;
    cnode = xmlDocGetRootElement(doc);
  }
  xmlNsPtr ns = xmlSearchNsByHref(doc, cnode, STR2XML(namespaceURI));
  if(ns == 0)
    return Nil;
  return XML2STR(ns->prefix);
}

  
bool 
LibXMLNode::isDefaultNamespace(IN(RString) namespaceURI)
{
  xmlNsPtr ns = xmlSearchNsByHref(_nodePtr->doc, _nodePtr, STR2XML(namespaceURI));
  if(ns == 0)
    return false;
  return ns->prefix == 0 || xmlStrlen(ns->prefix) == 0;
}
  
RString 
LibXMLNode::lookupNamespaceURI(IN(RString) prefix)
{
  xmlNodePtr cnode = _nodePtr;
  xmlDocPtr doc = cnode->doc;
  if(cnode->type == XML_DOCUMENT_NODE)
  {
    doc = (xmlDocPtr)cnode;
    cnode = xmlDocGetRootElement(doc);
  }
  xmlNsPtr ns = xmlSearchNs(doc, cnode, STR2XML(prefix));
  if(ns == 0)
    return Nil;
  return XML2STR(ns->href);
}

RString 
LibXMLNode::getTextContent() THROWS1(RDOMException)
{
  switch(getNodeType())
  {
  case ELEMENT_NODE:
  case ATTRIBUTE_NODE:
  case ENTITY_NODE:
  case ENTITY_REFERENCE_NODE:
  case DOCUMENT_FRAGMENT_NODE:
  {
    StringBuffer sb;
    RNodeList children = getChildNodes();
    int len = children->getLength();
    for(int i = 0; i < len; i++)
    {
      RNode child = children->item(i);
      RString textContent = child->getTextContent();
      if(textContent != Nil)
      {
        sb.append(textContent);
      }
    }
    return sb.toString();
  }
  case TEXT_NODE:
  case CDATA_SECTION_NODE:
  case COMMENT_NODE:
  case PROCESSING_INSTRUCTION_NODE:
    return getNodeValue();
  default:
    return Nil;
  }
}

void 
LibXMLNode::setTextContent(IN(RString) textContent) THROWS1(org::w3c::dom::RDOMException)
{
  switch (getNodeType ())
  {
  case ENTITY_REFERENCE_NODE:
    THROW2(DOMException, NO_MODIFICATION_ALLOWED_ERR, "");
  case ELEMENT_NODE:
  case ATTRIBUTE_NODE:
  case ENTITY_NODE:
  case DOCUMENT_FRAGMENT_NODE:
  {
    RNodeList children = getChildNodes();
    int len = children->getLength();
    for (int i = 0; i < len; i++)
    {
      RNode child = children->item(i);
      removeChild(child);
    }
    if (textContent != Nil)
    {
      RText text = getOwnerDocument()->createTextNode(textContent);
      appendChild(&text);
    }
    break;
  }
  case TEXT_NODE:
  case CDATA_SECTION_NODE:
  case COMMENT_NODE:
  case PROCESSING_INSTRUCTION_NODE:
    setNodeValue(textContent);
    break;
  }
}

bool _isEqualNode(xmlNodePtr node1, xmlNodePtr node2);

bool
_isEqualNodeList(xmlNodePtr node1, xmlNodePtr node2)
{
  while(node1 != 0)
  {
    if(_isEqualNode(node1, node2) == false)
      return false;
    node1 = node1->next;
    node2 = node2->next;
  }
  return true;
}
  
bool 
_isEqualNode(xmlNodePtr node1, xmlNodePtr node2)
{
  if(node1 == node2)
    return true;
  if(node1 == 0 || node2 == 0)
    return false;
  if(node1->type != node2->type)
    return false;
  if(xmlStrEqual(node1->name, node2->name) == 0)
    return false;
  if(node1->type == XML_ELEMENT_NODE || node1->type == XML_ATTRIBUTE_NODE)
  {
    xmlNsPtr ns1 = node1->ns;
    if(ns1 != 0)
    {
      xmlNsPtr ns2 = node2->ns;
      if(ns2 == NULL)
        return false;
      if(xmlStrEqual(ns1->href, ns2->href) == 0)
        return false;
      
    }
  }
  const xmlChar* val1 = _getNodeValue(node1);
  const xmlChar* val2 = _getNodeValue(node2);
  if(xmlStrEqual(val1, val2) == 0)
    return false;
  
  if(node1->type == XML_ELEMENT_NODE)
  {
    if(_isEqualNodeList((xmlNodePtr) node1->properties,(xmlNodePtr) node2->properties) == false)
      return false;
  }
  if(node1->type == XML_DOCUMENT_NODE)
  {
    xmlDocPtr doc1 =(xmlDocPtr) node1;
    xmlDocPtr doc2 =(xmlDocPtr) node2;

    if(_isEqualNode((xmlNodePtr)doc1->intSubset,(xmlNodePtr)doc2->intSubset) == false)
      return false;
    if(_isEqualNode((xmlNodePtr) doc1->extSubset,(xmlNodePtr) doc2->extSubset) == false)
      return false;
  }
  if(_isEqualNodeList(node1->children, node2->children) == false)
    return false;
  return true;
}

bool 
LibXMLNode::isEqualNode(IN(RNode) arg)
{
  if(instanceof(arg, LibXMLNode) == false)
    return false;
  return _isEqualNode(_nodePtr, RLibXMLNode(arg)->_nodePtr);
}

RNode 
LibXMLNode::selectNode(IN(RString) xpath)
{
  RLibXMLDocument doc = (RLibXMLDocument)getOwnerDocument();
  acdk::lang::Object obj = doc->evaluate(xpath, this, Nil, org::w3c::dom::xpath::ANY_UNORDERED_NODE_TYPE, Nil);
  if (obj == Nil)
    return Nil;
  if (instanceof(obj, LibXMLXPathResult) == true)
  {
    RLibXMLXPathResult lres = (RLibXMLXPathResult)obj;
    short resType = lres->getResultType();
    return lres->getSingleNodeValue();
  }
  return Nil;
}

RNodeList 
LibXMLNode::selectNodes(IN(RString) xpath)
{
  RLibXMLDocument doc = (RLibXMLDocument)getOwnerDocument();
  acdk::lang::Object obj = doc->evaluate(xpath, this, Nil, org::w3c::dom::xpath::ANY_UNORDERED_NODE_TYPE, Nil);
  if (instanceof(obj, LibXMLXPathResult) == true)
  {
    RLibXMLXPathResult lres = (RLibXMLXPathResult)obj;
    short resType = lres->getResultType();
    return lres->getSnapshotNodeList();
  }
  return Nil;
}

acdk::lang::RString 
LibXMLNode::selectText(IN(acdk::lang::RString) xpath)
{
  RLibXMLDocument doc = (RLibXMLDocument)getOwnerDocument();
  acdk::lang::Object obj = doc->evaluate(xpath, this, Nil, org::w3c::dom::xpath::ANY_UNORDERED_NODE_TYPE, Nil);
  if (instanceof(obj, LibXMLXPathResult) == true)
  {
    RLibXMLXPathResult lres = (RLibXMLXPathResult)obj;
    short resType = lres->getResultType();
    return lres->getStringValue();
  }
  return Nil;
}

acdk::lang::RObject 
LibXMLNode::selectObject(IN(acdk::lang::RString) xpath)
{
  RLibXMLDocument doc = (RLibXMLDocument)getOwnerDocument();
  acdk::lang::Object obj = doc->evaluate(xpath, this, Nil, org::w3c::dom::xpath::ANY_UNORDERED_NODE_TYPE, Nil);
  if (instanceof(obj, LibXMLXPathResult) == false)
    return Nil;

  RLibXMLXPathResult lres = (RLibXMLXPathResult)obj;
  return lres->getObjectValue();
}

acdk::lang::RBoolean 
LibXMLNode::selectBoolean(IN(acdk::lang::RString) xpath)
{
  RLibXMLDocument doc = (RLibXMLDocument)getOwnerDocument();
  acdk::lang::Object obj = doc->evaluate(xpath, this, Nil, org::w3c::dom::xpath::ANY_UNORDERED_NODE_TYPE, Nil);
  if (instanceof(obj, LibXMLXPathResult) == false)
    return Nil;

  RLibXMLXPathResult lres = (RLibXMLXPathResult)obj;
  return Boolean::valueOf(lres->getBooleanValue());
}

int 
LibXMLNode::_mapLibXMLTypeToDomType(int tp)
{
  switch(tp)
  {
  case XML_ELEMENT_NODE: return ELEMENT_NODE;
  case XML_ATTRIBUTE_NODE: return ATTRIBUTE_NODE;
  case XML_TEXT_NODE: return TEXT_NODE;
  case XML_CDATA_SECTION_NODE: return CDATA_SECTION_NODE;
  case XML_ENTITY_REF_NODE: return ENTITY_REFERENCE_NODE;
  case XML_ENTITY_NODE: return XML_ENTITY_NODE;
  case XML_PI_NODE: return PROCESSING_INSTRUCTION_NODE;
  case XML_COMMENT_NODE: return COMMENT_NODE;
  case XML_DOCUMENT_NODE: return DOCUMENT_NODE;
  case XML_DOCUMENT_TYPE_NODE: return DOCUMENT_TYPE_NODE;
  case XML_DOCUMENT_FRAG_NODE: return DOCUMENT_FRAGMENT_NODE;
  case XML_NOTATION_NODE: return NOTATION_NODE;
  case XML_HTML_DOCUMENT_NODE: 
    break;
  case XML_DTD_NODE: return DOCUMENT_TYPE_NODE;
  case XML_ELEMENT_DECL: 
    break;
  case XML_ATTRIBUTE_DECL:
    break;
  case XML_ENTITY_DECL:
    break;
  case XML_NAMESPACE_DECL:
    break;
  case XML_XINCLUDE_START:
    break;
  case XML_XINCLUDE_END:
    break;
  default:
    THROW2(DOMException, NOT_SUPPORTED_ERR, SBSTR("Unknown LibXML node type " << tp));
  }
  THROW2(DOMException, NOT_SUPPORTED_ERR, SBSTR("Cannot map LibXML node type " << tp));
  return 0;
}

int 
LibXMLNode::_mapDomTypeToLibXMLType(int tp)
{
  switch(tp)
  {
  case ATTRIBUTE_NODE: return XML_ATTRIBUTE_NODE;
  case CDATA_SECTION_NODE: return XML_CDATA_SECTION_NODE;
  case COMMENT_NODE: return XML_COMMENT_NODE;
  case DOCUMENT_FRAGMENT_NODE: return XML_DOCUMENT_FRAG_NODE;
  case DOCUMENT_NODE: return XML_DOCUMENT_NODE;
  case DOCUMENT_TYPE_NODE: return XML_DOCUMENT_TYPE_NODE;
  case ELEMENT_NODE: return XML_ELEMENT_NODE;
  case ENTITY_NODE: return XML_ENTITY_DECL;
  case ENTITY_REFERENCE_NODE: return XML_ENTITY_REF_NODE;
  case NOTATION_NODE: return XML_NOTATION_NODE;
  case PROCESSING_INSTRUCTION_NODE: return XML_PI_NODE;
  case TEXT_NODE: return XML_TEXT_NODE;
  default:
    THROW2(DOMException, NOT_SUPPORTED_ERR, SBSTR("Unknown DOM node type " << tp));
  }
  return 0;
}

RString 
LibXMLNode::toXML()
{
  return acdk::xml::dom::NodeUtil::toXml(this);
}

void 
LibXMLNode::write(IN(acdk::io::RWriter) out, int writeFlags, int indentLevel, IN(RString) encoding)
{
  xmlDocPtr doc = _nodePtr->doc;
  xmlOutputBufferPtr buffer = LibXMLDocument::createWriterOutputBuffer(out);
  if (encoding != Nil)
   
  {
    buffer->encoder = xmlFindCharEncodingHandler((const char*)STR2XML(encoding));
    // ### TODO encding check if acdk own 
  }
  int format = 0;
  if (writeFlags & NWFWithIndent)
    format = 1;
  if (doc->type == XML_HTML_DOCUMENT_NODE && ((writeFlags & NWFWHtmlAsXml) != NWFWHtmlAsXml))
  {
    if (((xmlNodePtr)doc) == _nodePtr)
      htmlDocContentDumpFormatOutput(buffer, _nodePtr->doc, (const char*)STR2XML(encoding), format);
    else
      htmlNodeDumpFormatOutput(buffer, _nodePtr->doc, _nodePtr, (const char*)STR2XML(encoding), format);
    //void	
  }
  else
  {
    xmlNodeDumpOutput(buffer, _nodePtr->doc, _nodePtr, indentLevel, format, (const char*)STR2XML(encoding));

  }
  xmlOutputBufferClose(buffer);
  out->flush();
}



RNode
LibXMLNodeList::item(int index)
{
  xmlNodePtr node = _nodePtr->children;
  int count = 0;
  for(count = 0; node != 0 && count < index; count++)
  {
    node = node->next;
  }
  return &LibXMLNode::newInstance(node);
}

int 
LibXMLNodeList::getLength()
{
  xmlNodePtr node = _nodePtr->children;
  int count = 0;
  for(count = 0; node != 0; count++)
  {
    node = node->next;
  }
  return count;
}

xmlAttrPtr
_getNamedItem(xmlNodePtr node, IN(RString) name)
{
  RString rname = name->convert(CCUtf8);
  xmlAttrPtr attr = node->properties;
  while (attr != 0)
  {
    if (_match((const xmlChar*)rname->c_str(), (xmlNodePtr)attr) == true)
      break;
    attr = attr->next;
  }
  return attr;
}

org::w3c::dom::RNode 
LibXMLNamedNodeMap::getNamedItem(IN(RString) name)
{
  if (_type == NNMTAttributes)
  {
    xmlAttrPtr  attr = _getNamedItem(_nodePtr, name);
    return &LibXMLNode::newInstance((xmlNodePtr)attr);
  }
  
  xmlDtdPtr dtd = (xmlDtdPtr) _nodePtr;
  xmlHashTablePtr hash = (xmlHashTablePtr)((_type == NNMTEntities) ? dtd->entities : dtd->notations);
  if (hash == 0)
    return Nil;
    
  xmlNodePtr ret = (xmlNodePtr)xmlHashLookup(hash, STR2XML(name));
  return &LibXMLNode::newInstance(ret);
}

org::w3c::dom::RNode 
LibXMLNamedNodeMap::setNamedItem(IN(org::w3c::dom::RNode) arg) THROWS1(org::w3c::dom::RDOMException)
{
  xmlNodePtr argNode = _getNodePtr(arg);
  
  if (argNode->doc != _nodePtr->doc)
    THROW2(DOMException, WRONG_DOCUMENT_ERR, "");
  checkChildNode(_nodePtr, argNode, false);  
  
  if (_type == NNMTAttributes)
  {
    if (argNode->parent != 0)
      THROW2(DOMException, INUSE_ATTRIBUTE_ERR, "");
    xmlAddChild(_nodePtr, argNode);
  }
  else
  {
    xmlDtdPtr dtd = (xmlDtdPtr)_nodePtr;
    xmlHashTablePtr hash = (xmlHashTablePtr) ((_type == NNMTEntities) ? dtd->entities : dtd->notations);
    if (hash == 0)
    {
      hash = xmlHashCreate(10);
      if (_type == NNMTEntities)
        dtd->entities = hash;
      else
        dtd->notations = hash;
    }
    xmlHashAddEntry(hash, argNode->name, argNode);
  }
  return arg;
}

org::w3c::dom::RNode 
LibXMLNamedNodeMap::removeNamedItem(IN(RString) name) THROWS1(org::w3c::dom::RDOMException)
{
  if (_type == NNMTAttributes)
  {
    xmlAttrPtr attr = _getNamedItem(_nodePtr, name);
    if (attr == 0)
      THROW2(DOMException, NOT_FOUND_ERR, "");
    xmlUnlinkNode((xmlNodePtr)attr);
    return &LibXMLNode::newInstance((xmlNodePtr)attr, true); 
  }
  
  xmlDtdPtr dtd = (xmlDtdPtr) _nodePtr;
  xmlHashTablePtr hash = (xmlHashTablePtr) ((_type == NNMTEntities) ? dtd->entities : dtd->notations);
  if (hash == 0)
    return Nil;
  xmlNodePtr ret = (xmlNodePtr)xmlHashLookup(hash, STR2XML(name));
  if (ret != 0)
  {
    xmlHashRemoveEntry(hash, STR2XML(name), NULL);
  }
  return &LibXMLNode::newInstance((xmlNodePtr)ret, true);
}

struct NodeData
{
  int index;
  int count;
  xmlNodePtr nodePtr;
  NodeData(int idx = 0)
    : index(idx)
    , count(0)
    , nodePtr(0)
  {
  }
};

void
_hashScanner(void* payload, void* vdata, xmlChar* name)
{
  NodeData* data = (NodeData*)vdata;
  if (data->count <= data->index)
    data->nodePtr = (xmlNodePtr)payload;
  data->count++;
}

org::w3c::dom::RNode 
LibXMLNamedNodeMap::item(int index)
{
  if (_type == NNMTAttributes)
  {
    xmlAttrPtr attr;
    int count = 0;
    if (_nodePtr->type == XML_ELEMENT_NODE)
    {
      xmlAttrPtr attr = _nodePtr->properties;
      for (count = 0; attr != 0 && count < index; count++)
      {
        attr = attr->next;
      }
      if (attr == 0)
        THROW1(NullPointerException, SBSTR("No attribute at indexs " << index));
      return &LibXMLNode::newInstance((xmlNodePtr)attr);
    }
    return Nil;
  }
  
  xmlDtdPtr dtd = (xmlDtdPtr)_nodePtr;
  xmlHashTablePtr hash = (xmlHashTablePtr) ((_type == NNMTEntities) ? dtd->entities : dtd->notations);
  if (hash == 0)
    return Nil;
  
  NodeData nodeData(index);
  xmlHashScan(hash, _hashScanner, &nodeData);
  return &LibXMLNode::newInstance(nodeData.nodePtr);
}

int 
LibXMLNamedNodeMap::getLength()
{
  if (_type == NNMTAttributes)
  {
    if (_nodePtr->type == XML_ELEMENT_NODE)
    {
      int count = 0;
      xmlAttrPtr attr = _nodePtr->properties;
      while (attr != NULL)
      {
        count++;
        attr = attr->next;
      }
      return count;
    }
    return -1;
  }
 
  xmlDtdPtr dtd = (xmlDtdPtr)_nodePtr;
  xmlHashTablePtr hash = (xmlHashTablePtr) ((_type == NNMTEntities) ? dtd->entities : dtd->notations);
  if (hash == 0)
    return 0;
  NodeData nodeData(-1);
  xmlHashScan(hash, _hashScanner, &nodeData);
  return nodeData.count;
}


xmlAttrPtr
_getNamedItemNS(xmlNodePtr node, IN(RString) uri, IN(RString) localName)
{
  RString ruri = uri->convert(CCUtf8);
  RString rlocalName = localName->convert(CCUtf8);

  const xmlChar* uric = (const xmlChar*)ruri->c_str();
  const xmlChar* lnc = (const xmlChar*)rlocalName->c_str();
 
  xmlAttrPtr attr = node->properties;
  while (attr != 0)
  {
    if (_matchNS(uric, lnc, (xmlNodePtr)attr))
      break;
    attr = attr->next;
  }
  return attr;
}

org::w3c::dom::RNode 
LibXMLNamedNodeMap::getNamedItemNS(IN(RString) namespaceURI, IN(RString) localName)
{
  if (_type == NNMTAttributes)
  {
    xmlAttrPtr attr = _getNamedItemNS (_nodePtr, namespaceURI, localName);
    return &LibXMLNode::newInstance((xmlNodePtr)attr);
  }
  return Nil;
}

org::w3c::dom::RNode 
LibXMLNamedNodeMap::setNamedItemNS(IN(org::w3c::dom::RNode) arg) THROWS1(org::w3c::dom::RDOMException)
{
  return setNamedItem(arg);
}

org::w3c::dom::RNode 
LibXMLNamedNodeMap::removeNamedItemNS(IN(RString) namespaceURI, IN(RString) localName)
{
  if (_type == NNMTAttributes)
  {
    xmlAttrPtr attr = _getNamedItemNS (_nodePtr, namespaceURI, localName);
    if (attr == 0)
      THROW2(DOMException, NOT_FOUND_ERR, "");
    xmlUnlinkNode((xmlNodePtr) attr);
    return &LibXMLNode::newInstance((xmlNodePtr)attr); // memory leak
  }
  return Nil;
}

bool 
LibXMLAttr::getSpecified()
{
  return ((xmlAttrPtr)_nodePtr.ptr())->atype != 0;
}

RString 
LibXMLAttr::getValue()
{
  xmlChar* text = xmlNodeGetContent(_nodePtr);
  if (text == 0)
    return Nil;
  RString ret = XML2STR(text);
  xmlFree(text);
  return ret;
}
  
void 
LibXMLAttr::setValue(IN(RString) value) THROWS1(org::w3c::dom::RDOMException)
{
  xmlNodeSetContent(_nodePtr, STR2XML(value));
}



} // namespace libxmldom
} // namespace xml
} // namespace acdk