xml/xmldomandxpath/src/xmlenginedom/xmlengnode.cpp
author William Roberts <williamr@symbian.org>
Wed, 03 Feb 2010 11:49:58 +0000
changeset 4 d2801e68661f
parent 0 e35f40988205
permissions -rw-r--r--
Add EPL headers

// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// Node functions implementation
//

#include <stdlib.h>

#include <xml/dom/xmlengelement.h>
#include <xml/dom/xmlengdocument.h>
#include <xml/dom/xmlengnamespace.h>
#include <xml/dom/xmlengtext.h>
#include <xml/dom/xmlenguserdata.h>
#include <xml/dom/xmlengnodelist.h>
#include <stdapis/libxml2/libxml2_parser.h>
#include <stdapis/libxml2/libxml2_globals.h>
#include <stdapis/libxml2/libxml2_xmlio.h>
#include "libxml2_xmlsave_private.h"
#include "libxml2_tree_private.h"
#include "xmlengdomdefs.h"
#include <xml/dom/xmlengerrors.h>
#include <xml/utils/xmlengmem.h>
#include <xml/utils/xmlengxestrings.h>

// ---------------------------------------------------------------------------------------------
// Unlinks the internal libxml2's node from double-linked list.
// Relinks neighbour nodes.The node stays virtually linked to its old neighbours! Use with care!!
//
// No checks are made; nor parent's, nor node's properties updated
// ---------------------------------------------------------------------------------------------
//
void TXmlEngNode::DoUnlinkNode()
    {
    xmlNodePtr& next = LIBXML_NODE->next;
    xmlNodePtr& prev = LIBXML_NODE->prev;
    // Unlink this node; relink neighbors
    if (prev)
        {
        prev->next = next;
        }
    else
        {
        // Unlinked node is the first in the list
        // 'children' or 'properties' property of the parent node
        // must be updated (depending on the type of the node: content node or attribute) 
        xmlNodePtr& parent = LIBXML_NODE->parent;
        // Ugly, but works :)
        xmlNodePtr* first = ((LIBXML_NODE->type ==  XML_ATTRIBUTE_NODE) 
                              ? reinterpret_cast<xmlNodePtr*>(&(parent->properties)) 
                              : &(parent->children));
        *first = next;
        }
    if (next)
        {
        next->prev = prev;
        }
    else
        {
        // Unlinked node is the last in the list (of elements or attributes) 
        // 'last' property of the parent element must be updated  (for content nodes only)
        if(LIBXML_NODE->type != XML_ATTRIBUTE_NODE)
            {
            LIBXML_NODE->parent->last = prev;
            }
        }
    }


// ---------------------------------------------------------------------------------------------
// Inserts the node in a double-linked list of nodes before specified node.
//
// No checks are made; nor parent's, nor node's properties updated (except prev/next)
// ---------------------------------------------------------------------------------------------
//
void TXmlEngNode::LinkBefore(TXmlEngNode aTargetNode)
    {
    xmlNodePtr node = INTERNAL_NODEPTR(aTargetNode);
    xmlNodePtr& prev = node->prev;
    LIBXML_NODE->next = node;
    LIBXML_NODE->prev = prev;
    prev->next = LIBXML_NODE;
    node->prev = LIBXML_NODE;
    }

// ---------------------------------------------------------------------------------------------
// Get innerXML string. This method return all content of the node.
// Output text does not include node markup.
//
// @note Returned TString should be freed
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TInt TXmlEngNode::InnerXmlL(RBuf8& aBuffer)
    {
    if(aBuffer.Length())
        {
        aBuffer.Close();
        }
    if(IsNull())
    	{
    	User::Leave(KXmlEngErrWrongUseOfAPI);
    	} 
    	
    if(NodeType() == TXmlEngNode::EDocument)
        {
        return OwnerDocument().SaveL(aBuffer, OwnerDocument(), NULL);
        }
    aBuffer.FillZ();
    RXmlEngNodeList<TXmlEngNode> nodeList;
    RBuf8 tmpStr;
    GetChildNodes(nodeList);
    TXmlEngNode tmpNode;
    while(nodeList.HasNext())
        {
        tmpNode = nodeList.Next();
        tmpNode.OuterXmlL(tmpStr);
        CleanupClosePushL(tmpStr);
        if((aBuffer.MaxSize() - aBuffer.Size()) < tmpStr.Size()) 
            {
            aBuffer.ReAllocL(aBuffer.MaxSize() + tmpStr.Size() + 1);
            }
        aBuffer.Append(tmpStr);
        CleanupStack::PopAndDestroy();
        }
    tmpStr.Close();
    
    return aBuffer.Size();
    }
    
// ---------------------------------------------------------------------------------------------
// Get outerXML string. This method return all content of the node.
// Output text includes node markup.
//
// @note Returned TString should be freed
//
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TInt TXmlEngNode::OuterXmlL(RBuf8& aBuffer)
    {
    TInt size = -1;
    if(aBuffer.Length())
        {
        aBuffer.Close();
        }
    if(IsNull())
    	{
    	User::Leave(KXmlEngErrWrongUseOfAPI);
    	} 
        
    if(NodeType() == TXmlEngNode::EDocument)
        {
        return OwnerDocument().SaveL(aBuffer, OwnerDocument(), NULL);
        }
    
    xmlSaveCtxt ctxt;
    xmlOutputBufferPtr buf;
    
    /* 
     * save the content to a temp buffer.
     */
    buf = xmlAllocOutputBuffer(NULL);
    OOM_IF_NULL(buf);

    memset(&ctxt, 0, sizeof(ctxt));
    ctxt.doc = LIBXML_NODE->doc;
    ctxt.buf = buf;
    ctxt.level = 0;
    ctxt.format = 0; 
   
    xmlSaveCtxtInit(&ctxt);

    xmlNodeDumpOutputInternal(&ctxt, LIBXML_NODE);
    if(xmlOOMFlag())
        {
        xmlOutputBufferClose(buf);
        OOM_HAPPENED;
        }
        
    size = buf->buffer->use;
    if(size > 0)
        {
        // frees any previous contents of aBuffer argument
        aBuffer.Assign(buf->buffer->content,size,size);
        // To prevent it from freeing
        buf->buffer->content = NULL; 
        }
    xmlOutputBufferClose(buf);
    
    // Leaves with KErrNoMemory
    TEST_OOM_FLAG; 
    if (size < 0)
        {
        XmlEngLeaveL(KXmlEngErrNegativeOutputSize);
        }        
        
    return size;
    }

// ---------------------------------------------------------------------------------------------
// Moves the node to become the first in the list of its siblings
// Node is expected to have a parent.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::SetAsFirstSibling()
    {
    if (LIBXML_NODE->type == XML_NAMESPACE_DECL)
        {
        return;
        }
    xmlNodePtr& prev = LIBXML_NODE->prev;
    if (prev)
        {
        DoUnlinkNode();
        // Insert as first
        prev = NULL;
        xmlNodePtr& parent = LIBXML_NODE->parent;
        // Ugly, but works :)
        xmlNodePtr* first = ((LIBXML_NODE->type == XML_ATTRIBUTE_NODE)
                              ? reinterpret_cast<xmlNodePtr*>(&(parent->properties))
                              : &(parent->children));
        
        (*first)->prev = LIBXML_NODE;
        LIBXML_NODE->next = *first;
        *first = LIBXML_NODE;
        // NOTE: it is possible to cope without having parent defined,
        //       it will need to iterate through sibling list to find the first one
        }
    }


// ---------------------------------------------------------------------------------------------
// Moves the node to become the last in the list of its siblings
// Node is expected to have a parent.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::SetAsLastSibling()
    {
    if (LIBXML_NODE->type == XML_NAMESPACE_DECL)
        {
        return;
        }
    xmlNodePtr& next = LIBXML_NODE->next;
    if (next)
        {
        DoUnlinkNode(); 
        xmlNodePtr& parent = LIBXML_NODE->parent;
        xmlNodePtr& prev = LIBXML_NODE->prev;
        if (LIBXML_NODE->type != XML_ATTRIBUTE_NODE)
            {
            if (!prev)
                parent->children = next;
            prev = parent->last;
            parent->last->next = LIBXML_NODE;
            parent->last = LIBXML_NODE;
            }
        else
            {
            if (!prev)
                parent->properties = (xmlAttrPtr)next;
            prev = next; // NOTE: 'next' is always different than NULL
            while (prev->next)
                prev = prev->next;
            prev->next = LIBXML_NODE;
            }
        next = NULL;
        }
    }

// ---------------------------------------------------------------------------------------------
// Moves the node in the list of sibling nodes before another node
// Node is expected to have a parent.
// Do nothing if aSiblingNode is not one of node's siblings
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::MoveBeforeSibling(
    TXmlEngNode aSiblingNode )
    {
    if (LIBXML_NODE->type == XML_NAMESPACE_DECL)
        {
        return;
        }
    xmlNodePtr node = INTERNAL_NODEPTR(aSiblingNode);
    if (!node->prev)
        {
        SetAsFirstSibling();
        }
    else
        {
        DoUnlinkNode();
        LinkBefore(aSiblingNode);
        }
    }

// ---------------------------------------------------------------------------------------------
// Moves the node in the list of sibling nodes after another node
// Node is expected to have a parent.
// Do nothing if aSiblingNode is not one of the node's siblings
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::MoveAfterSibling(
    TXmlEngNode aSiblingNode)
    {
    if (LIBXML_NODE->type == XML_NAMESPACE_DECL)
        {
        return;
        }
    xmlNodePtr node = INTERNAL_NODEPTR(aSiblingNode);
    if (!node->next)
        {
        SetAsLastSibling();
        }
    else
        {
        DoUnlinkNode();
        LinkBefore(aSiblingNode.NextSibling());
        }   
    }

// ---------------------------------------------------------------------------------------------
// Retrieves a "handle" for namespace declaration that applies to the node's namespace
// Note: DOM specs do not consider namespace declarations as a kind of nodes
// This API adds TXmlEngNamespace type of nodes, which is derived from TXmlEngNode.
//
// @return    Object that represents namespace declaration and prefix binding that
//            act on the node; returns NULL object (check using TXmlEngNamespace.IsNull()
//            or TXmlEngNamespace.NotNull()) if no namespace associated
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNamespace TXmlEngNode::NamespaceDeclaration() const
    {
    //
    switch(LIBXML_NODE->type)
        {
        case XML_ELEMENT_NODE:
        case XML_ATTRIBUTE_NODE:
            return TXmlEngNamespace(LIBXML_NODE->ns);
        default:
            return NULL;
        }
    }

// ---------------------------------------------------------------------------------------------
// Clones the node completely: all attributes and namespace declarations (for TXmlEngElement nodes),
// values and children nodes are copied as well.
// 
// Document nodes cannot be copied with this method: RXmlEngDocument::CloneDocumentL() must be used.
//
// @return Complete copy of a node or leaves.
// @note    The node should not be NULL!
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode TXmlEngNode::CopyL() const
    {
    if ( !LIBXML_NODE )
    	{
    	User::Leave(KXmlEngErrNullNode);
    	}
    // Copying of RXmlEngDocument should be made with RXmlEngDocument::CloneDocumentL()
    if(NodeType() == TXmlEngNode::EDocument)
        return NULL;
    //
    xmlNodePtr copy = xmlStaticCopyNode(
                            LIBXML_NODE, 
                            NULL /* doc */, 
                            NULL /* parent */, 
                            1);
    //
    if (xmlOOMFlag())
        {
        if(copy)
            xmlFreeNode(copy); // it may be a partial copy
        OOM_HAPPENED;
        }
    TXmlEngNode ncopy(copy);
    OwnerDocument().TakeOwnership(ncopy);
    return ncopy;
    }

// ---------------------------------------------------------------------------------------------
// Creates a deep copy of the node and appends the subtree as a new child
// to the provided parent node.
//
// @return Created copy of the node after linking it into the target document tree.
// @note Document nodes cannot be copied with this method; use RXmlEngDocument::CloneDocumentL()
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode TXmlEngNode::CopyToL(
    TXmlEngNode aParent ) const
    {
    if ( !LIBXML_NODE )
    	{
    	User::Leave(KXmlEngErrNullNode);
    	}
    if ( LIBXML_NODE->type == XML_DOCUMENT_NODE )
    	{
    	User::Leave(KXmlEngErrWrongUseOfAPI);
    	}
   if ( aParent.IsNull() )
    	{
    	User::Leave(KXmlEngErrNullNode);
    	}
    //
    TXmlEngNode ncopy = CopyL();
    return aParent.AppendChildL(ncopy);
    }

// ---------------------------------------------------------------------------------------------
// Detaches a node from document tree
//
// @return This node, which is already not a part of any document
// @note    Remember to use ReconcileNamespacesL() later, if extracted node (subtree)
//         contains references to namespace declarations outside of the subtree.
// @see     ReconcileNamespacesL()
// @note    The document, from which the node is being unlinked, becomes an owner of the node
//         until it is linked elsewhere.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode TXmlEngNode::Unlink()
    {   
    //
    if(LIBXML_NODE && ParentNode().NotNull())
        {
        RXmlEngDocument tmpDoc = OwnerDocument();
        xmlUnlinkNode(LIBXML_NODE);
        tmpDoc.TakeOwnership(*this);
        }
    return *this;
    }

// ---------------------------------------------------------------------------------------------
// Unlinks the node and destroys it; all child nodes are destroyed as well and all memory is freed
//
// @note  Document nodes cannot be "removed" with this method, uses RXmlEngDocument-specific methods.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::Remove()
    {
    //
    if (!LIBXML_NODE || LIBXML_NODE->type == XML_DOCUMENT_NODE)
        {
        return;
        }
    if(ParentNode().NotNull())
        {
        xmlUnlinkNode(LIBXML_NODE);
        }
    else
        {
        OwnerDocument().RemoveOwnership(*this);
        }
    if (LIBXML_NODE->type != XML_ATTRIBUTE_NODE)
        {
     xmlFreeNode(LIBXML_NODE);
        }
	else
		{
		xmlFreeProp(INTERNAL_ATTRPTR(iInternal));
		}

    iInternal = 0;
    }

// ---------------------------------------------------------------------------------------------
// Ensures that namespaces referred to in the node and its descendants are
// in the scope the node.
//
// * This method checks that all the namespaces declared within the given
// * tree are properly declared. This is needed for example after Copy or Unlink
// * and then Append operations. The subtree may still hold pointers to
// * namespace declarations outside the subtree or they may be invalid/masked. As much
// * as possible the function try to reuse the existing namespaces found in
// * the new environment. If not possible, the new namespaces are redeclared
// * on the top of the subtree.
//
// This method should be used after unlinking nodes and inserting to another
// document tree or to a another part of the original tree, if some nodes of the subtree
// are remove from the scope of a namespace declaration they refer to.
//
// When node is unlinked, it may still refer to namespace declarations from the previous location.
// It is important to reconcile subtree's namespaces if previous parent tree is to be destroyed.
// On the other hand, if the parent tree is not changed before pasting its unlinked part into another
// tree, then reconciliation is needed only after paste operation.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::ReconcileNamespacesL()
    {
    xmlReconciliateNs(LIBXML_NODE->doc, LIBXML_NODE);
    TEST_OOM_FLAG;
    }

// ---------------------------------------------------------------------------------------------
// Current node is replaced with another node (subtree).
//
// The replacement node is linked into document tree instead of this node.
// The replaced node is destroyed.
//
// @see SubstituteForL(TXmlEngNode)
//
// In both cases the argument node is unlinked from its previous location
// (which can be NONE, i.e. not linked; SAME or ANOTHER document tree).
//
// @note Replacement of a node with NULL TXmlEngNode is legal and equivalent to removing the node.
// @note Not applicable to document nodes
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::ReplaceWithL(TXmlEngNode aNode)
    {
    if ( !LIBXML_NODE )
    	{
    	User::Leave(KXmlEngErrNullNode);
    	}
    if ( LIBXML_NODE->type == XML_DOCUMENT_NODE )
    	{
    	User::Leave(KXmlEngErrWrongUseOfAPI);
    	}

    if(aNode.ParentNode().IsNull())
        {
    	// new node is unlinked, so owned by some (maybe other than this) document
	    // previous ownership must be withdrawn
        aNode.OwnerDocument().RemoveOwnership(aNode);
        }
    if(ParentNode().IsNull())
        {
    	// This node is unlinked, thus owned by its document
	    // Ownership must be withdrawn before removing the node
	    RXmlEngDocument doc = OwnerDocument();
	    doc.RemoveOwnership(*this);
	    // Also, there is no place to link the new node! 
	    // So it will be unlinked, but owned by this node's document 
        doc.TakeOwnership(aNode);
        }
    // put new node instead of 'this' node: it works with all combinations of linked/unlinked states of the nodes
    xmlFreeNode(xmlReplaceNode(LIBXML_NODE, INTERNAL_NODEPTR(aNode)));
    }

EXPORT_C void TXmlEngNode::ReplaceWith(TXmlEngNode aNode)
    {
    if(!LIBXML_NODE || LIBXML_NODE->type == XML_DOCUMENT_NODE)
        return;
    if(aNode.ParentNode().IsNull())
        {
    	// new node is unlinked, so owned by some (maybe other than this) document
	    // previous ownership must be withdrawn
        aNode.OwnerDocument().RemoveOwnership(aNode);
        }
    if(ParentNode().IsNull())
        {
    	// This node is unlinked, thus owned by its document
	    // Ownership must be withdrawn before removing the node
	    RXmlEngDocument doc = OwnerDocument();
	    doc.RemoveOwnership(*this);
	    // Also, there is no place to link the new node! 
	    // So it will be unlinked, but owned by this node's document 
        doc.TakeOwnership(aNode);
        }
    // put new node instead of 'this' node: it works with all combinations of linked/unlinked states of the nodes
    xmlFreeNode(xmlReplaceNode(LIBXML_NODE, INTERNAL_NODEPTR(aNode)));
    }

// ---------------------------------------------------------------------------------------------
// Another node is put instead of the current node.
//
// Does the same as ReplaceWith(TXmlEngNode) but does not free node and just returns it.
//
// @return Current node after unlinking it from document tree
// @see ReplaceWith(TXmlEngNode)
//
// In both cases the argument node is unlinked from its previous location
// (which can be NONE, i.e. not linked; SAME or ANOTHER document tree)
//
// It is possible to use NULL TXmlEngNode object as an argument. In such case
// no new node will be put instead of unlinked one.
//
// @note Not applicable to document nodes
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode TXmlEngNode::SubstituteForL(TXmlEngNode aNode)
    {
    if ( !LIBXML_NODE )
    	{
    	User::Leave(KXmlEngErrNullNode);
    	}
    if ( LIBXML_NODE->type == XML_DOCUMENT_NODE )
    	{
    	User::Leave(KXmlEngErrWrongUseOfAPI);
    	}
    //
    if(aNode.ParentNode().IsNull())
        {
      	// new node is unlinked, so owned by other document
    	// it must not be owned to be linked
        aNode.OwnerDocument().RemoveOwnership(aNode);
        }
    if(ParentNode().IsNull())
        {
    	// this node is unlinked - nowhere to link new node: this node's document will own it
        OwnerDocument().TakeOwnership(aNode);
	    // this node will stay as it is
	    // new node will be unlinked by call to xmlReplaceNode
        }        
    TXmlEngNode tmpNode = TXmlEngNode(xmlReplaceNode(LIBXML_NODE, INTERNAL_NODEPTR(aNode)));
    aNode.OwnerDocument().TakeOwnership(tmpNode);
    return tmpNode;
    }

// ---------------------------------------------------------------------------------------------
// DOM Level 3 Core
// ---------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------------------------------
//Initializes a node list with all children of the node
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::GetChildNodes(RXmlEngNodeList<TXmlEngNode>& aList) const
    {
    aList.Open(LIBXML_NODE->children, TDOMNODETYPENONE);
    }

// ---------------------------------------------------------------------------------------------
// @return Parent node of the node or NULL if no parent
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode TXmlEngNode::ParentNode() const
    {
    //
    return TXmlEngNode(LIBXML_NODE->parent);
    }

// ---------------------------------------------------------------------------------------------
// @return The first child node or NULL if no children
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode TXmlEngNode::FirstChild() const
    {
    //
    return TXmlEngNode(LIBXML_NODE->children);
    }

// ---------------------------------------------------------------------------------------------
// @return The last child node or NULL if no children
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode TXmlEngNode::LastChild() const
    {
    //
    return TXmlEngNode(LIBXML_NODE->last);
    }

// ---------------------------------------------------------------------------------------------
// @return Previous node in a child list or NULL if no sibling before
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode TXmlEngNode::PreviousSibling() const
    {
    if(!LIBXML_NODE->parent)
        {
        return NULL;
        }
    return TXmlEngNode(LIBXML_NODE->prev);
    }

// ---------------------------------------------------------------------------------------------
// @return Following node in a child list or NULL if no sibling after
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode TXmlEngNode::NextSibling() const
    {
    if(!LIBXML_NODE->parent)
        {
        return NULL;
        }
    return TXmlEngNode(LIBXML_NODE->next);
    }

// ---------------------------------------------------------------------------------------------
// @return A document node of the DOM tree this node belongs to
// @note    An instance of RXmlEngDocument class returns itself
// ---------------------------------------------------------------------------------------------
//
EXPORT_C RXmlEngDocument TXmlEngNode::OwnerDocument() const
    {
    //
    return RXmlEngDocument(LIBXML_NODE->doc);
    }

// ---------------------------------------------------------------------------------------------
// Append a child node.
//
// This is universal operation for any types of nodes.
// Note, that some types of nodes cannot have children and
// some types of nodes are not allowed to be children of some other types.
//
// @return Appended node, which could changed as a result of adding it to
// list of child nodes (e.g. text nodes can coalesce together)
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode TXmlEngNode::AppendChildL(
    TXmlEngNode aNewChild)
    {
    if ( !LIBXML_NODE || aNewChild.IsNull() )
    	{
    	User::Leave(KXmlEngErrNullNode);
    	}
    //
    if(aNewChild.ParentNode().IsNull())
    {
        aNewChild.OwnerDocument().RemoveOwnership(aNewChild);
    }
    else
    {
         xmlUnlinkNode(INTERNAL_NODEPTR(aNewChild));
    }
   xmlNodePtr   child  =  xmlAddChild(LIBXML_NODE, INTERNAL_NODEPTR(aNewChild));

    TEST_OOM_FLAG;
    return child;
}

// ---------------------------------------------------------------------------------------------
// @return Type of the node
//
// Use NodeType() to find out the type of the node prior to casting object
// of TXmlEngNode class to one of its derived subclasses (TXmlEngElement, TXmlEngAttr, TXmlEngTextNode, etc.)
//
// @see TXmlEngDOMNodeType
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TXmlEngNode::TXmlEngDOMNodeType TXmlEngNode::NodeType() const
    {
    //
    if((TXmlEngDOMNodeType)LIBXML_NODE->type == EText && TEXT_NODE_DATA_TYPE)
    	{
		return (TXmlEngDOMNodeType)(TUint)TEXT_NODE_DATA_TYPE;
    	}
	return (TXmlEngDOMNodeType)LIBXML_NODE->type;
    }

// Note: "" is used for undefined name;  0 means "to use libxml node's name field"
static const char* const KNodeNames[]={
    0,
    0,                  // EElement                 =       1,
    0,                  // EAttribute               =       2,
    "#text",            // EText                    =       3,
    "#cdata-section",   // ECDATASection            =       4,
    0,                  // EEntityReference         =       5,
    "",                 // EEntity                  =       6,
    0,                  // EProcessingInstruction   =       7,
    "#comment",         // EComment                 =       8,
    0,                  // EDocument                =       9,
    0,                  // EDocumentType            =       10,
    "#document-fragment", // EDocumentFragment      =       11,
    "",                 // ENotation                =       12,
    0,                  // ENamespaceDeclaration    =       18  // not in use
	"",					// EBinaryContainer 		= 		 30,
	"",					// EChunkContainer	 		= 		 31,
	""					// EFileContainer 			= 		 32    
};

const TInt KNodeNamesSize = sizeof(KNodeNames);

// ---------------------------------------------------------------------------------------------
// @return Name of the node
//
// This method generally follows DOM spec :
// \verbatim
// -------------------------------------------------------------------------------
// The values of nodeName, nodeValue, and attributes vary according to the node
// type as follows:
//
// interface              nodeName                nodeValue            attributes
// -------------------------------------------------------------------------------
// Attr                   = Attr.name              = Attr.value             = null
// CDATASection           = "#cdata-section"       = CharacterData.data     = null
// Comment                = "#comment"             = CharacterData.data     = null
// Document               = "#document"            = null                   = null
// DocumentFragment       = "#document-fragment"   = null                   = null
// DocumentType           = DocumentType.name      = null                   = null
// Element                = Element.tagName        = null           = NamedNodeMap
// Entity                 = entity name            = null                   = null
// EntityReference        = name of entity referenced  = null               = null
// Notation               = notation name          = null                   = null
// ProcessingInstruction  = target                 = data                   = null
// Text                   = "#text"                = CharacterData.data     = null
// -------------------------------------------------------------------------------
// \endverbatim
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TPtrC8 TXmlEngNode::Name() const
    {
    // DONE: OPTIMIZE: Create static array of names with nodetype as index
    //                 Use NULL if name is not constant and then use such switch..
    //
    TUint type = (TUint) LIBXML_NODE->type;
    if (type < KNodeNamesSize /sizeof(char*))
        {
        const char* KName = KNodeNames[type];
        if(KName)
            {
            return TXmlEngConstString(KName).PtrC8();
            }
        return TXmlEngConstString((char*)LIBXML_NODE->name).PtrC8();
        }
    return KNullDesC8();
    }


// ---------------------------------------------------------------------------------------------
// Fetches value of this node, depending on its type.
//
// @note It is better to always cast nodes to specific type and then use specific
//      method for getting "node value"
//
// @return Node value
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TPtrC8 TXmlEngNode::Value() const
    {
    if (iInternal)
        {
        switch(LIBXML_NODE->type)
            {
            // The content of first Text child is returned
            case XML_ATTRIBUTE_NODE:
                return AsAttr().Value();
            // Note: in DOM spec element's value is Null, but we can
            //       access it: the content of first TXmlEngTextNode child node is returned
            case XML_ELEMENT_NODE:
                return AsElement().Text();
            // TXmlEngTextNode, TXmlEngCDATASection, TXmlEngProcessingInstruction and Comments store
            // content in the same way.
            case XML_TEXT_NODE:
            case XML_CDATA_SECTION_NODE:
            case XML_COMMENT_NODE:      
            case XML_PI_NODE:           
                return ((TXmlEngConstString)CAST_XMLCHAR_TO_DOMSTRING(LIBXML_NODE->content)).PtrC8();

            default:
                ;
            }
        }
    return NULL;
}
// ---------------------------------------------------------------------------------------------
// Sets value of this node.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::SetValueL(
    const TDesC8& aValue )
    {
    if (iInternal)
        {
        switch(LIBXML_NODE->type)
            {
            // The content of first TXmlEngTextNode child is returned
            case XML_ATTRIBUTE_NODE:
            case XML_ELEMENT_NODE:
                // Note: in DOM spec element's value is Null, but we can
                //       access it: the content of first TXmlEngTextNode child node is returned
                AsAttr().SetValueL(aValue); //  same as TXmlEngElement::SetTextL(aValue);
                return;
            // TXmlEngTextNode, TXmlEngCDATASection, TXmlEngProcessingInstruction and Comments store
            // content in the same way.
            case XML_TEXT_NODE:
            case XML_CDATA_SECTION_NODE:
            case XML_COMMENT_NODE:      
            case XML_PI_NODE:       
                AsText().SetContentsL(aValue);
                return;
            default:
                ;
            }
        }
    }


// ---------------------------------------------------------------------------------------------
// @return Whether the value of the node is presented by only one TXmlEngTextNode node
//
// If the value is <i>"simple text"</i> then it is possible to access it as TDOMString
// without making copy, which combines values of all text nodes and entity reference nodes.
//
// @see TXmlEngNode::Value(), TXmlEngAttr::Value(), TXmlEngElement::Text()
//
// This method is applicable to TXmlEngElement and TXmlEngAttr nodes. On other nodes FALSE is returned.
//
// @note
// Values (contents) of TXmlEngComment, TXmlEngCDATASection, TXmlEngTextNode, ProcessingInstuction data are
// always "simple".
//
// When the returned result is FALSE, getting value of the node would not returned
// whole contents because of either entity references present in the contents or
// the contents is mixed (for TXmlEngElement node). In this case WholeTextContentsCopyL()
// should be used.
//
// @see TXmlEngNode::WholeTextContentsCopyL()
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TBool TXmlEngNode::IsSimpleTextContents() const
    {
    if (!LIBXML_NODE)
        return false;
    xmlElementType type = LIBXML_NODE->type;
    if (type == XML_ELEMENT_NODE ||
        type == XML_ATTRIBUTE_NODE)
        {
        xmlNodePtr children = LIBXML_NODE->children;

        return children  &&  children->type == XML_TEXT_NODE &&
               !(children->next);
        }
    return false; // incorrect type node
}

// ---------------------------------------------------------------------------------------------
// @return   the content of the node
//
// What is returned depends on the node type.
// Method caller is responsible for freeing returned string.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::WholeTextContentsCopyL(RBuf8& aOutput) const
    {
    XE_ASSERT_DEBUG(LIBXML_NODE);
    //
    xmlChar* text = xmlNodeGetContent(LIBXML_NODE);
    if (xmlOOMFlag())
        { 
        if(text) 
            xmlFree(text);
        OOM_HAPPENED;
        }
    xmlCharAssignToRbuf8L(aOutput,text);
    }
// -----------------------------------------------------------------------------

EXPORT_C TBool TXmlEngNode::HasChildNodes() const
    {
    //
    return (TBool)LIBXML_NODE->children;
    }

// ---------------------------------------------------------------------------------------------
// @return  Namespace URI of a node
//           - NULL is returned for elements and attributes that do not
//             belong to any namespace.
//           - bound namespace URI is returned for namespace declaration nodes (instances of TXmlEngNamespace).
//           - NULL is returned to all other types of node.
// @note use IsNull() and NotNull() for testing returned result on the subject
//      of having some URI
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TPtrC8 TXmlEngNode::NamespaceUri() const
    {
    //
    switch(LIBXML_NODE->type)
        {
        case XML_ELEMENT_NODE:
        case XML_ATTRIBUTE_NODE:
            return TXmlEngNamespace(LIBXML_NODE->ns).Uri();
        case XML_NAMESPACE_DECL:
            return AsNamespace().Uri();
        default:
            return NULL;
        }
    }

// ---------------------------------------------------------------------------------------------
// @return  Prefix of a node
//           
// Returns NULL for elements and attributes that do not have prefix
// (node belongs to the default namespace or does not belong to any namespace)
// NULL is also returned for all types of node other than TXmlEngElement or TXmlEngAttr
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TPtrC8 TXmlEngNode::Prefix() const
    {
    if (LIBXML_NODE->type < XML_TEXT_NODE) // NOTE: XML_ELEMENT_NODE = 1 and XML_ATTRIBUTE_NODE = 2
        {
        xmlNs* ns = LIBXML_NODE->ns;
        if (ns)
            return ((TXmlEngConstString)CAST_XMLCHAR_TO_DOMSTRING(ns->prefix)).PtrC8();
        }
    return KNullDesC8();
    }


// ---------------------------------------------------------------------------------------------
// @return    True if the node is TXmlEngElement and has at least one attribute
//
// @note Namespace-to-prefix bindings are not attributes.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TBool TXmlEngNode::HasAttributes() const
    {
    return iInternal &&
           LIBXML_NODE->type == XML_ELEMENT_NODE &&
           LIBXML_NODE->properties;
    }

// ---------------------------------------------------------------------------------------------
// Evaluates active base URI for the node by processing xml:base attributes of parents
//
// @return A copy of effective base URI for the node
// @note It's up to the caller to free the string with TDOMString::Free()
// ---------------------------------------------------------------------------------------------
//
EXPORT_C void TXmlEngNode::BaseUriL(RBuf8& aBaseUri) const
    {
    //
    xmlChar* uri = xmlNodeGetBase(LIBXML_NODE->doc, LIBXML_NODE);
    if (xmlOOMFlag())
        {
        if(uri)
            xmlFree(uri); // partial construction is possible
        OOM_HAPPENED;
        }
    xmlCharAssignToRbuf8L(aBaseUri,uri);
    }

// ---------------------------------------------------------------------------------------------
// Searches the prefix that is bound to the given aNamespaceUri and
// applicable in the scope of this TXmlEngNode.
//
// @return    A sought prefix or NULL if not found or aNamespaceUri is the default namespace
// @see TXmlEngElement::LookupNamespaceByUri(TXmlEngNode,TDOMString)
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TPtrC8 TXmlEngNode::LookupPrefixL(
    const TDesC8& aNamespaceUri ) const
    {
    if ( !LIBXML_NODE )
    	{
    	User::Leave(KXmlEngErrNullNode);
    	}
    //
    if (LIBXML_NODE->type == XML_ATTRIBUTE_NODE ||
        LIBXML_NODE->type == XML_ELEMENT_NODE)
        {
        return AsElement().LookupNamespaceByUriL(aNamespaceUri).Prefix();
        }
	if(!ParentNode().IsNull())        
		{
		return ParentNode().AsElement().LookupNamespaceByUriL(aNamespaceUri).Prefix();
		}
    return KNullDesC8();
    }

// ---------------------------------------------------------------------------------------------
// @return True if given namespace URI is a default one for the node (applicable to elements only)
//
// @note "" or NULL can be used to denote undefined namespace
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TBool TXmlEngNode::IsDefaultNamespaceL(
    const TDesC8& aNamespaceUri ) const
    {
    if (LIBXML_NODE->type == XML_ELEMENT_NODE) // No default namespaces for attributes
        {
        return !AsElement().DefaultNamespaceL().Uri().Compare(aNamespaceUri);
        }
    return false;
    }

// ---------------------------------------------------------------------------------------------
// Searches the namespace URI that is bound to the given prefix.
//
// @return    - a sought URI or NULL if the prefix is not bound
// @see TXmlEngElement::LookupNamespaceByPrefix(TXmlEngNode,TDOMString)
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TPtrC8 TXmlEngNode::LookupNamespaceUriL(
    const TDesC8& aPrefix ) const
    {
    if ( !LIBXML_NODE )
    	{
    	User::Leave(KXmlEngErrNullNode);
    	}
    //
    if (LIBXML_NODE->type == XML_ATTRIBUTE_NODE ||
        LIBXML_NODE->type == XML_ELEMENT_NODE)
        {
        return AsElement().LookupNamespaceByPrefixL(aPrefix).Uri();
        }
	if(!ParentNode().IsNull())        
		{
		return ParentNode().AsElement().LookupNamespaceByPrefixL(aPrefix).Uri();
		}
    return KNullDesC8();
    }

// ---------------------------------------------------------------------------------------------
// Returns the user data object attached to this node. Ownership is not transferred.
//
// @return Pointer to data object or NULL if it doesn't exist.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C MXmlEngUserData* TXmlEngNode::UserData() const
    {
    return
        (LIBXML_NODE &&
            (LIBXML_NODE->type == XML_ATTRIBUTE_NODE ||
             LIBXML_NODE->type == XML_ELEMENT_NODE))
        ?
        static_cast<MXmlEngUserData*>(LIBXML_NODE->_private)
        :
        NULL;
    }

// ---------------------------------------------------------------------------------------------
// Removes the user data onject attached to this node. Ownership is transferred
// (the object is not deleted).
//
// @return Pointer to data object or NULL if it doesn't exist.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C MXmlEngUserData* TXmlEngNode::RemoveUserData()
    {
    MXmlEngUserData* data = UserData();
    if (data)
        {
        // node type has been checked by UserData()
        LIBXML_NODE->_private = NULL;
        }
    return data;
    }

// ---------------------------------------------------------------------------------------------
// Attaches a user data object to this node. The ownership of the object is transferred.
// When the (underlying) node is deleted the Destroy method of the MXmlEngUserData class will be
// called. If there already is a user data object associated with this node, it will be
// deleted before attaching the new object. Notet that only TXmlEngElement and Attribute nodes
// currently support this feature.
//
// @param[in] aData Pointer to the data object.
// @return true if successful, false if for example underlying node type doesn't support
// attaching user data.
// ---------------------------------------------------------------------------------------------
//
EXPORT_C TBool TXmlEngNode::AddUserData(
    MXmlEngUserData* aData )
    {
    TBool ret = false;
    if (iInternal)
        {
        if (LIBXML_NODE->type == XML_ATTRIBUTE_NODE ||
            LIBXML_NODE->type == XML_ELEMENT_NODE)
            {
            // release previous data
            if (LIBXML_NODE->_private)
                {
                MXmlEngUserData* oldData = RemoveUserData();
                oldData->Destroy();
                }
            LIBXML_NODE->_private = aData;
            ret = true;
            }
        }
    return ret;
    }