/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmlsecurity.hxx"

#include "xmldocumentwrapper_xmlsecimpl.hxx"
#include <com/sun/star/lang/XMultiServiceFactory.hpp>

#include <xmloff/attrlist.hxx>
#include "xmlelementwrapper_xmlsecimpl.hxx"

//#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * Deleted by AF
#include <memory.h>
 */

#include <sys/types.h>
#include <sys/stat.h>

#ifndef INCLUDED_VECTOR
#include <vector>
#define INCLUDED_VECTOR
#endif

#ifdef UNX
#define stricmp strcasecmp
#endif

namespace cssu = com::sun::star::uno;
namespace cssl = com::sun::star::lang;
namespace cssxc = com::sun::star::xml::crypto;
namespace cssxcsax = com::sun::star::xml::csax;
namespace cssxs = com::sun::star::xml::sax;
namespace cssxw = com::sun::star::xml::wrapper;

#define SERVICE_NAME "com.sun.star.xml.wrapper.XMLDocumentWrapper"
#define IMPLEMENTATION_NAME "com.sun.star.xml.security.bridge.xmlsec.XMLDocumentWrapper_XmlSecImpl"
	
#define STRXMLNS "xmlns"

#define RTL_ASCII_USTRINGPARAM( asciiStr ) asciiStr, strlen( asciiStr ), RTL_TEXTENCODING_ASCII_US
#define RTL_UTF8_USTRINGPARAM( asciiStr ) asciiStr, strlen( asciiStr ), RTL_TEXTENCODING_UTF8

/* used by the recursiveDelete method */
#define NODE_REMOVED    0
#define NODE_NOTREMOVED 1
#define NODE_STOPED     2

XMLDocumentWrapper_XmlSecImpl::XMLDocumentWrapper_XmlSecImpl( )
{
	saxHelper.startDocument();
	m_pDocument = saxHelper.getDocument();
	
	/*
	 * creates the virtual root element
	 */
	saxHelper.startElement(rtl::OUString(RTL_UTF8_USTRINGPARAM( "root" )), cssu::Sequence<cssxcsax::XMLAttribute>());
	
	m_pRootElement = saxHelper.getCurrentNode();
	m_pCurrentElement = m_pRootElement;
}

XMLDocumentWrapper_XmlSecImpl::~XMLDocumentWrapper_XmlSecImpl()
{
	saxHelper.endDocument();
	xmlFreeDoc(m_pDocument);
}

void XMLDocumentWrapper_XmlSecImpl::getNextSAXEvent()
/****** XMLDocumentWrapper_XmlSecImpl/getNextSAXEvent *************************
 *
 *   NAME
 *	getNextSAXEvent -- Prepares the next SAX event to be manipulate
 *
 *   SYNOPSIS
 *	getNextSAXEvent();
 *
 *   FUNCTION
 *	When converting the document into SAX events, this method is used to
 *	decide the next SAX event to be generated.
 *	Two member variables are checked to make the decision, the
 *	m_pCurrentElement and the m_nCurrentPosition.
 *	The m_pCurrentElement represents the node which have been covered, and
 *	the m_nCurrentPosition represents the event which have been sent.
 *	For example, suppose that the m_pCurrentElement
 *	points to element A, and the m_nCurrentPosition equals to 
 *	NODEPOSITION_STARTELEMENT, then the next SAX event should be the
 *	endElement for element A if A has no child, or startElement for the
 *	first child element of element A otherwise.
 *	The m_nCurrentPosition can be one of following values:
 *	NODEPOSITION_STARTELEMENT for startElement;
 *	NODEPOSITION_ENDELEMENT for endElement;
 *	NODEPOSITION_NORMAL for other SAX events;
 *
 *   INPUTS
 *	empty
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	OSL_ASSERT( m_pCurrentElement != NULL );
	
        /*
         * Get the next event through tree order.
         *
         * if the current event is a startElement, then the next
         * event depends on whether or not the current node has
         * children.
         */
	if (m_nCurrentPosition == NODEPOSITION_STARTELEMENT)
	{
	        /*
	         * If the current node has children, then its first child
	         * should be next current node, and the next event will be
	         * startElement or charaters(PI) based on that child's node
	         * type. Otherwise, the endElement of current node is the 
	         * next event.
	         */
		if (m_pCurrentElement->children != NULL)
		{
			m_pCurrentElement = m_pCurrentElement->children;
			m_nCurrentPosition 
				= (m_pCurrentElement->type == XML_ELEMENT_NODE)?
					NODEPOSITION_STARTELEMENT:NODEPOSITION_NORMAL;
		}
		else
		{
			m_nCurrentPosition = NODEPOSITION_ENDELEMENT;
		}
	}
        /*
         * if the current event is a not startElement, then the next
         * event depends on whether or not the current node has
         * following sibling.
         */
	else if (m_nCurrentPosition == NODEPOSITION_ENDELEMENT || m_nCurrentPosition == NODEPOSITION_NORMAL)
	{
		xmlNodePtr pNextSibling = m_pCurrentElement->next;
		
	        /*
	         * If the current node has following sibling, that sibling
	         * should be next current node, and the next event will be
	         * startElement or charaters(PI) based on that sibling's node
	         * type. Otherwise, the endElement of current node's parent
	         * becomes the next event.
	         */
		if (pNextSibling != NULL)
		{
			m_pCurrentElement = pNextSibling;
			m_nCurrentPosition 
				= (m_pCurrentElement->type == XML_ELEMENT_NODE)?
					NODEPOSITION_STARTELEMENT:NODEPOSITION_NORMAL;
		}
		else
		{
			m_pCurrentElement = m_pCurrentElement->parent;
			m_nCurrentPosition = NODEPOSITION_ENDELEMENT;
		}
	}
}

void XMLDocumentWrapper_XmlSecImpl::sendStartElement(
	const cssu::Reference< cssxs::XDocumentHandler >& xHandler, 
	const cssu::Reference< cssxs::XDocumentHandler >& xHandler2, 
	const xmlNodePtr pNode) const
	throw (cssxs::SAXException)
/****** XMLDocumentWrapper_XmlSecImpl/sendStartElement ************************
 *
 *   NAME
 *	sendStartElement -- Constructs a startElement SAX event
 *
 *   SYNOPSIS
 *	sendStartElement(xHandler, xHandler2, pNode);
 *
 *   FUNCTION
 *	Used when converting the document into SAX event stream.
 *	This method constructs a startElement SAX event for a particular
 *	element, then calls the startElement methods of the XDocumentHandlers.
 *
 *   INPUTS
 *	xHandler -	the first XDocumentHandler interface to receive the
 *			startElement SAX event. It can be NULL.
 *	xHandler2 -	the second XDocumentHandler interface to receive the
 *			startElement SAX event. It can't be NULL.
 *	pNode -		the node on which the startElement should be generated.
 *			This node must be a element type.
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	SvXMLAttributeList* pAttributeList = new SvXMLAttributeList();
	cssu::Reference < cssxs::XAttributeList > xAttrList = cssu::Reference< cssxs::XAttributeList > (pAttributeList);

	xmlNsPtr pNsDef = pNode->nsDef;

	while (pNsDef != NULL)
	{
		const xmlChar* pNsPrefix = pNsDef->prefix;
		const xmlChar* pNsHref = pNsDef->href;

		if (pNsDef->prefix == NULL)
		{
			pAttributeList->AddAttribute(
				rtl::OUString(RTL_UTF8_USTRINGPARAM( STRXMLNS )),
				rtl::OUString(RTL_UTF8_USTRINGPARAM( (sal_Char*)pNsHref )));
		}
		else
		{
			pAttributeList->AddAttribute(
				rtl::OUString(RTL_UTF8_USTRINGPARAM( STRXMLNS ))
				+rtl::OUString(RTL_UTF8_USTRINGPARAM( ":" ))
				+rtl::OUString(RTL_UTF8_USTRINGPARAM( (sal_Char*)pNsPrefix )),
				rtl::OUString(RTL_UTF8_USTRINGPARAM( (sal_Char*)pNsHref )));
		}

		pNsDef = pNsDef->next;
	}

	xmlAttrPtr pAttr = pNode->properties;
	
	while (pAttr != NULL)
	{
		const xmlChar* pAttrName = pAttr->name;
		xmlNsPtr pAttrNs = pAttr->ns;
		
		rtl::OUString ouAttrName;
		if (pAttrNs == NULL)
		{
			ouAttrName = rtl::OUString(RTL_UTF8_USTRINGPARAM( (sal_Char*)pAttrName ));
		}
		else
		{
			ouAttrName = rtl::OUString(RTL_UTF8_USTRINGPARAM( (sal_Char*)pAttrNs->prefix))
				+rtl::OUString(RTL_UTF8_USTRINGPARAM( (sal_Char*)":" ))
				+rtl::OUString(RTL_UTF8_USTRINGPARAM( (sal_Char*)pAttrName ));
		}

		pAttributeList->AddAttribute(
			ouAttrName,
			rtl::OUString(RTL_UTF8_USTRINGPARAM( (sal_Char*)(pAttr->children->content))));
		pAttr = pAttr->next;
	}
	
	rtl::OString sNodeName = getNodeQName(pNode);
	
	if (xHandler.is())
	{
		xHandler->startElement(
			rtl::OUString(RTL_UTF8_USTRINGPARAM ( ((sal_Char*)(sNodeName.getStr())) )),
			xAttrList);
	}
	
	xHandler2->startElement(
		rtl::OUString(RTL_UTF8_USTRINGPARAM ( ((sal_Char*)(sNodeName.getStr())) )),
		xAttrList);
}
	
void XMLDocumentWrapper_XmlSecImpl::sendEndElement(
	const cssu::Reference< cssxs::XDocumentHandler >& xHandler, 
	const cssu::Reference< cssxs::XDocumentHandler >& xHandler2, 
	const xmlNodePtr pNode) const
	throw (cssxs::SAXException)
/****** XMLDocumentWrapper_XmlSecImpl/sendEndElement **************************
 *
 *   NAME
 *	sendEndElement -- Constructs a endElement SAX event
 *
 *   SYNOPSIS
 *	sendEndElement(xHandler, xHandler2, pNode);
 *
 *   FUNCTION
 *	Used when converting the document into SAX event stream.
 *	This method constructs a endElement SAX event for a particular
 *	element, then calls the endElement methods of the XDocumentHandlers.
 *
 *   INPUTS
 *	xHandler -	the first XDocumentHandler interface to receive the
 *			endElement SAX event. It can be NULL.
 *	xHandler2 -	the second XDocumentHandler interface to receive the
 *			endElement SAX event. It can't be NULL.
 *	pNode -		the node on which the endElement should be generated.
 *			This node must be a element type.
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	rtl::OString sNodeName = getNodeQName(pNode);
	
	if (xHandler.is())
	{
		xHandler->endElement(rtl::OUString(RTL_UTF8_USTRINGPARAM ( ((sal_Char*)(sNodeName.getStr())) )));
	}
	
	xHandler2->endElement(rtl::OUString(RTL_UTF8_USTRINGPARAM ( ((sal_Char*)(sNodeName.getStr())) )));
}
	
void XMLDocumentWrapper_XmlSecImpl::sendNode(
	const cssu::Reference< cssxs::XDocumentHandler >& xHandler, 
	const cssu::Reference< cssxs::XDocumentHandler >& xHandler2, 
	const xmlNodePtr pNode) const
	throw (cssxs::SAXException)
/****** XMLDocumentWrapper_XmlSecImpl/sendNode ********************************
 *
 *   NAME
 *	sendNode -- Constructs a characters SAX event or a
 *	processingInstruction SAX event
 *
 *   SYNOPSIS
 *	sendNode(xHandler, xHandler2, pNode);
 *
 *   FUNCTION
 *	Used when converting the document into SAX event stream.
 *	This method constructs a characters SAX event or a
 *	processingInstructionfor SAX event based on the type of a particular
 *	element, then calls the corresponding methods of the XDocumentHandlers.
 *
 *   INPUTS
 *	xHandler -	the first XDocumentHandler interface to receive the
 *			SAX event. It can be NULL.
 *	xHandler2 -	the second XDocumentHandler interface to receive the
 *			SAX event. It can't be NULL.
 *	pNode -		the node on which the endElement should be generated.
 *			If it is a text node, then a characters SAX event is
 *			generated; if it is a PI node, then a
 *			processingInstructionfor SAX event is generated.
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	xmlElementType type = pNode->type;
		
	if (type == XML_TEXT_NODE)
	{
		if (xHandler.is())
		{
			xHandler->characters(rtl::OUString(RTL_UTF8_USTRINGPARAM ( ((sal_Char*)(pNode->content)) )));
		}
		
		xHandler2->characters(rtl::OUString(RTL_UTF8_USTRINGPARAM ( ((sal_Char*)(pNode->content)) )));
	}
	else if (type == XML_PI_NODE)
	{
		if (xHandler.is())
		{
			xHandler->processingInstruction(
				rtl::OUString(RTL_UTF8_USTRINGPARAM ( ((sal_Char*)(pNode->name)) )),
				rtl::OUString(RTL_UTF8_USTRINGPARAM ( ((sal_Char*)(pNode->content)) )));
		}
		
		xHandler2->processingInstruction(
			rtl::OUString(RTL_UTF8_USTRINGPARAM ( ((sal_Char*)(pNode->name)) )),
			rtl::OUString(RTL_UTF8_USTRINGPARAM ( ((sal_Char*)(pNode->content)) )));
	}
}

rtl::OString XMLDocumentWrapper_XmlSecImpl::getNodeQName(const xmlNodePtr pNode) const
/****** XMLDocumentWrapper_XmlSecImpl/getNodeQName ****************************
 *
 *   NAME
 *	getNodeQName -- Retrives the qualified name of a node
 *
 *   SYNOPSIS
 *	name = getNodeQName(pNode);
 *
 *   FUNCTION
 *	see NAME
 *
 *   INPUTS
 *	pNode -	the node whose name will be retrived
 *
 *   RESULT
 *	name - the node's qualified name
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	rtl::OString sNodeName((const sal_Char*)pNode->name);
	if (pNode->ns != NULL)
	{
		xmlNsPtr pNs = pNode->ns;

		if (pNs->prefix != NULL)
		{
			rtl::OString sPrefix((const sal_Char*)pNs->prefix);
			sNodeName = sPrefix+rtl::OString(":")+sNodeName;
		}
	}
	
	return sNodeName;
}

xmlNodePtr XMLDocumentWrapper_XmlSecImpl::checkElement( const cssu::Reference< cssxw::XXMLElementWrapper >& xXMLElement) const
/****** XMLDocumentWrapper_XmlSecImpl/checkElement ****************************
 *
 *   NAME
 *	checkElement -- Retrives the node wrapped by an XXMLElementWrapper
 *	interface
 *
 *   SYNOPSIS
 *	node = checkElement(xXMLElement);
 *
 *   FUNCTION
 *	see NAME
 *
 *   INPUTS
 *	xXMLElement -	the XXMLElementWrapper interface wraping a node
 *
 *   RESULT
 *	node - the node wrapped in the XXMLElementWrapper interface
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	xmlNodePtr rc = NULL;
	
	if (xXMLElement.is())
	{
		cssu::Reference< cssl::XUnoTunnel > xNodTunnel( xXMLElement, cssu::UNO_QUERY ) ;
		if( !xNodTunnel.is() ) 
		{
			throw cssu::RuntimeException() ;
		}
	
		XMLElementWrapper_XmlSecImpl* pElement 
			= reinterpret_cast<XMLElementWrapper_XmlSecImpl*>(
                sal::static_int_cast<sal_uIntPtr>(
                    xNodTunnel->getSomething(
                        XMLElementWrapper_XmlSecImpl::getUnoTunnelImplementationId() ))) ;
			
		if( pElement == NULL ) {
			throw cssu::RuntimeException() ;
		}
		
		rc = pElement->getNativeElement();
	}
	
	return rc;
}

sal_Int32 XMLDocumentWrapper_XmlSecImpl::recursiveDelete(
	const xmlNodePtr pNode)
/****** XMLDocumentWrapper_XmlSecImpl/recursiveDelete *************************
 *
 *   NAME
 *	recursiveDelete -- Deletes a paticular node with its branch.
 *
 *   SYNOPSIS
 *	result = recursiveDelete(pNode);
 *
 *   FUNCTION
 *	Deletes a paticular node with its branch, while reserving the nodes
 *	(and their brance) listed in the m_aReservedNodes.
 *	The deletion process is preformed in the tree order, that is, a node
 *	is deleted after its previous sibling node is deleted, a parent node
 *	is deleted after its branch is deleted.
 *	During the deletion process when the m_pStopAtNode is reached, the
 *	progress is interrupted at once.
 *
 *   INPUTS
 *	pNode -	the node to be deleted
 *
 *   RESULT
 *	result -	the result of the deletion process, can be one of following 
 *			values:
 *			NODE_STOPED - the process is interrupted by meeting the
 *				m_pStopAtNode
 *			NODE_NOTREMOVED - the pNode is not completely removed
 *				because there is its descendant in the 
 *				m_aReservedNodes list
 *			NODE_REMOVED - the pNode and its branch are completely
 *				removed
 *
 *   NOTES
 *	The node in the m_aReservedNodes list must be in the tree order, otherwise
 *	the result is unpredictable.
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	if (pNode == m_pStopAtNode)
	{
		return NODE_STOPED;
	}
	
	if (pNode != m_pCurrentReservedNode)
	{
		xmlNodePtr pChild = pNode->children;
		
		xmlNodePtr pNextSibling;
		bool bIsRemoved = true;
		sal_Int32 nResult;
		
		while( pChild != NULL )
		{
			pNextSibling = pChild->next;
			nResult = recursiveDelete(pChild);
			
			switch (nResult)
			{
			case NODE_STOPED:
				return NODE_STOPED;
			case NODE_NOTREMOVED:
				bIsRemoved = false;
				break;
			case NODE_REMOVED:
				removeNode(pChild);
				break;
			default:
				throw cssu::RuntimeException();
			}
			
			pChild = pNextSibling;
		}
		
		if (pNode == m_pCurrentElement)
		{
			bIsRemoved = false;
		}
		
		return bIsRemoved?NODE_REMOVED:NODE_NOTREMOVED;
	}
	else
	{
		getNextReservedNode();
		return NODE_NOTREMOVED;
	}
}

void XMLDocumentWrapper_XmlSecImpl::getNextReservedNode()
/****** XMLDocumentWrapper_XmlSecImpl/getNextReservedNode *********************
 *
 *   NAME
 *	getNextReservedNode -- Highlights the next reserved node in the
 *	reserved node list
 *
 *   SYNOPSIS
 *	getNextReservedNode();
 *
 *   FUNCTION
 *	The m_aReservedNodes array holds a node list, while the 
 *	m_pCurrentReservedNode points to the one currently highlighted.
 *	This method is used to highlight the next node in the node list.
 *	This method is called at the time when the current highlighted node
 *	has been already processed, and the next node should be ready.
 *
 *   INPUTS
 *	empty
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	if (m_nReservedNodeIndex < m_aReservedNodes.getLength())
	{
		m_pCurrentReservedNode = checkElement( m_aReservedNodes[m_nReservedNodeIndex] );
		m_nReservedNodeIndex ++;
	}
	else
	{
		m_pCurrentReservedNode = NULL;
	}
}

void XMLDocumentWrapper_XmlSecImpl::removeNode(const xmlNodePtr pNode) const
/****** XMLDocumentWrapper_XmlSecImpl/removeNode ******************************
 *
 *   NAME
 *	removeNode -- Deletes a node with its branch unconditionaly
 *
 *   SYNOPSIS
 *	removeNode( pNode );
 *
 *   FUNCTION
 *	Delete the node along with its branch from the document.
 *
 *   INPUTS
 *	pNode -	the node to be deleted
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	/* you can't remove the current node */
	OSL_ASSERT( m_pCurrentElement != pNode );

	xmlAttrPtr pAttr = pNode->properties;
	
	while (pAttr != NULL)
	{
		if (!stricmp((sal_Char*)pAttr->name,"id")) 
		{
			xmlRemoveID(m_pDocument, pAttr);
		}
						
		pAttr = pAttr->next;
	}
	
	xmlUnlinkNode(pNode);
	xmlFreeNode(pNode);
}	

void XMLDocumentWrapper_XmlSecImpl::buildIDAttr(xmlNodePtr pNode) const
/****** XMLDocumentWrapper_XmlSecImpl/buildIDAttr *****************************
 *
 *   NAME
 *	buildIDAttr -- build the ID attribute of a node
 *
 *   SYNOPSIS
 *	buildIDAttr( pNode );
 *
 *   FUNCTION
 *	see NAME
 *
 *   INPUTS
 *	pNode -	the node whose id attribute will be built
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	14.06.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	xmlAttrPtr idAttr = xmlHasProp( pNode, (const unsigned char *)"id" );
	if (idAttr == NULL)
	{
		idAttr = xmlHasProp( pNode, (const unsigned char *)"Id" );
	}
	
	if (idAttr != NULL)
	{
		xmlChar* idValue = xmlNodeListGetString( m_pDocument, idAttr->children, 1 ) ;
		xmlAddID( NULL, m_pDocument, idValue, idAttr );
	}
}

void XMLDocumentWrapper_XmlSecImpl::rebuildIDLink(xmlNodePtr pNode) const
/****** XMLDocumentWrapper_XmlSecImpl/rebuildIDLink ***************************
 *
 *   NAME
 *	rebuildIDLink -- rebuild the ID link for the branch
 *
 *   SYNOPSIS
 *	rebuildIDLink( pNode );
 *
 *   FUNCTION
 *	see NAME
 *
 *   INPUTS
 *	pNode -	the node, from which the branch will be rebuilt
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	14.06.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	if (pNode != NULL && pNode->type == XML_ELEMENT_NODE)
	{
		buildIDAttr( pNode );
		
		xmlNodePtr child = pNode->children;
		while (child != NULL)
		{
			rebuildIDLink(child);
			child = child->next;
		}
	}
}

/* XXMLDocumentWrapper */
cssu::Reference< cssxw::XXMLElementWrapper > SAL_CALL XMLDocumentWrapper_XmlSecImpl::getCurrentElement(  )
	throw (cssu::RuntimeException)
{
	XMLElementWrapper_XmlSecImpl* pElement = new XMLElementWrapper_XmlSecImpl(m_pCurrentElement);
	return (cssu::Reference< cssxw::XXMLElementWrapper >)pElement;
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::setCurrentElement( const cssu::Reference< cssxw::XXMLElementWrapper >& element )
	throw (cssu::RuntimeException)
{
	m_pCurrentElement = checkElement( element );
	saxHelper.setCurrentNode( m_pCurrentElement );
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::removeCurrentElement(  )
	throw (cssu::RuntimeException)
{
	OSL_ASSERT( m_pCurrentElement != NULL );
	
	xmlNodePtr pOldCurrentElement = m_pCurrentElement;
	
	/*
	 * pop the top node in the parser context's
	 * nodeTab stack, then the parent of that node will
	 * automatically become the new stack top, and
	 * the current node as well.
	 */
	saxHelper.endElement(
		rtl::OUString(
			RTL_UTF8_USTRINGPARAM (
				(sal_Char*)(pOldCurrentElement->name)
			)));
	m_pCurrentElement = saxHelper.getCurrentNode();
	
	/*
	 * remove the node
	 */
	removeNode(pOldCurrentElement);
}		
	
sal_Bool SAL_CALL XMLDocumentWrapper_XmlSecImpl::isCurrent( const cssu::Reference< cssxw::XXMLElementWrapper >& node )
	throw (cssu::RuntimeException)
{
	xmlNodePtr pNode = checkElement(node);
	return (pNode == m_pCurrentElement);
}

sal_Bool SAL_CALL XMLDocumentWrapper_XmlSecImpl::isCurrentElementEmpty(  )
	throw (cssu::RuntimeException)
{
	sal_Bool rc = sal_False;
	
	if (m_pCurrentElement->children == NULL)
	{
		rc = sal_True;
	}
	
	return rc;
}

rtl::OUString SAL_CALL XMLDocumentWrapper_XmlSecImpl::getNodeName( const cssu::Reference< cssxw::XXMLElementWrapper >& node )
	throw (cssu::RuntimeException)
{
	xmlNodePtr pNode = checkElement(node);
	return rtl::OUString(RTL_UTF8_USTRINGPARAM ( (sal_Char*)pNode->name ));
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::clearUselessData( 
	const cssu::Reference< cssxw::XXMLElementWrapper >& node, 
	const cssu::Sequence< cssu::Reference< cssxw::XXMLElementWrapper > >& reservedDescendants, 
	const cssu::Reference< cssxw::XXMLElementWrapper >& stopAtNode )
	throw (cssu::RuntimeException)
{
	xmlNodePtr pTargetNode = checkElement(node);
	
	m_pStopAtNode = checkElement(stopAtNode);
	m_aReservedNodes = reservedDescendants;
	m_nReservedNodeIndex = 0;
	
	getNextReservedNode();
	
	recursiveDelete(pTargetNode);
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::collapse( const cssu::Reference< cssxw::XXMLElementWrapper >& node )
	throw (cssu::RuntimeException)
{
	xmlNodePtr pTargetNode = checkElement(node);
	xmlNodePtr pParent;
	
	while (pTargetNode != NULL)
	{
		if (pTargetNode->children != NULL || pTargetNode == m_pCurrentElement)
		{
			break;
		}
		
		pParent = pTargetNode->parent;
		removeNode(pTargetNode);
		pTargetNode = pParent;
	}
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::getTree( const cssu::Reference< cssxs::XDocumentHandler >& handler )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	if (m_pRootElement != NULL)
	{
		xmlNodePtr pTempCurrentElement = m_pCurrentElement;
		sal_Int32 nTempCurrentPosition = m_nCurrentPosition;
		
		m_pCurrentElement = m_pRootElement;
		
		m_nCurrentPosition = NODEPOSITION_STARTELEMENT;
		cssu::Reference< cssxs::XDocumentHandler > xHandler = handler;
		
		while(true)
		{
			switch (m_nCurrentPosition)
			{
			case NODEPOSITION_STARTELEMENT:
				sendStartElement(NULL, xHandler, m_pCurrentElement);
				break;
			case NODEPOSITION_ENDELEMENT:
				sendEndElement(NULL, xHandler, m_pCurrentElement);
				break;
			case NODEPOSITION_NORMAL:
				sendNode(NULL, xHandler, m_pCurrentElement);
				break;
			}
			
			if ( (m_pCurrentElement == m_pRootElement) && (m_nCurrentPosition == NODEPOSITION_ENDELEMENT ))
			{
				break;
			}
	
			getNextSAXEvent();
		}
		
		m_pCurrentElement = pTempCurrentElement;
		m_nCurrentPosition = nTempCurrentPosition;
	}
}
	
void SAL_CALL XMLDocumentWrapper_XmlSecImpl::generateSAXEvents(
	const cssu::Reference< cssxs::XDocumentHandler >& handler,
	const cssu::Reference< cssxs::XDocumentHandler >& xEventKeeperHandler,
	const cssu::Reference< cssxw::XXMLElementWrapper >& startNode, 
	const cssu::Reference< cssxw::XXMLElementWrapper >& endNode )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
        /*
         * The first SAX event is the startElement of the startNode
         * element.
         */
	bool bHasCurrentElementChild = (m_pCurrentElement->children != NULL);
	
	xmlNodePtr pTempCurrentElement = m_pCurrentElement;
	
	m_pCurrentElement = checkElement(startNode);
	
	if (m_pCurrentElement->type == XML_ELEMENT_NODE)
	{
		m_nCurrentPosition = NODEPOSITION_STARTELEMENT;
	}
	else
	{
		m_nCurrentPosition = NODEPOSITION_NORMAL;
	}
	
	xmlNodePtr pEndNode = checkElement(endNode);
	
	cssu::Reference < cssxc::sax::XSAXEventKeeper > xSAXEventKeeper( xEventKeeperHandler, cssu::UNO_QUERY );
	
	cssu::Reference< cssxs::XDocumentHandler > xHandler = handler;
	
	while(true)
	{
		switch (m_nCurrentPosition)
		{
		case NODEPOSITION_STARTELEMENT:
			sendStartElement(xHandler, xEventKeeperHandler, m_pCurrentElement);
			break;
		case NODEPOSITION_ENDELEMENT:
			sendEndElement(xHandler, xEventKeeperHandler, m_pCurrentElement);
			break;
		case NODEPOSITION_NORMAL:
			sendNode(xHandler, xEventKeeperHandler, m_pCurrentElement);
			break;
		default:
			throw cssu::RuntimeException();
		}
		
		if (xSAXEventKeeper->isBlocking())
		{
			xHandler = NULL;
		}
		
		if (pEndNode == NULL && 
			((bHasCurrentElementChild && m_pCurrentElement == xmlGetLastChild(pTempCurrentElement) && m_nCurrentPosition != NODEPOSITION_STARTELEMENT) ||
			 (!bHasCurrentElementChild && m_pCurrentElement == pTempCurrentElement && m_nCurrentPosition == NODEPOSITION_STARTELEMENT)))
		{
			break;
		}

		getNextSAXEvent();
		
	        /*
	         * If there is an end point specified, then check whether
	         * the current node equals to the end point. If so, stop
	         * generating.
	         */
		if (pEndNode != NULL && m_pCurrentElement == pEndNode)
		{
			break;
		}
	}
	
	m_pCurrentElement = pTempCurrentElement;
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::rebuildIDLink(
	const com::sun::star::uno::Reference< com::sun::star::xml::wrapper::XXMLElementWrapper >& node )
	throw (com::sun::star::uno::RuntimeException)
{
	xmlNodePtr pNode = checkElement( node );
	rebuildIDLink(pNode);
}
	

/* cssxs::XDocumentHandler */
void SAL_CALL XMLDocumentWrapper_XmlSecImpl::startDocument(  )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
}
	
void SAL_CALL XMLDocumentWrapper_XmlSecImpl::endDocument(  )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::startElement( const rtl::OUString& aName, const cssu::Reference< cssxs::XAttributeList >& xAttribs )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	sal_Int32 nLength = xAttribs->getLength();
	cssu::Sequence< cssxcsax::XMLAttribute > aAttributes (nLength);
	
	for (int i = 0; i < nLength; ++i) 
	{
		aAttributes[i].sName = xAttribs->getNameByIndex((short)i);
		aAttributes[i].sValue =xAttribs->getValueByIndex((short)i);
	}
	
	_startElement(aName, aAttributes);
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::endElement( const rtl::OUString& aName ) 
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	saxHelper.endElement(aName);
	m_pCurrentElement = saxHelper.getCurrentNode();
}
	
void SAL_CALL XMLDocumentWrapper_XmlSecImpl::characters( const rtl::OUString& aChars )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	saxHelper.characters(aChars);
}
	
void SAL_CALL XMLDocumentWrapper_XmlSecImpl::ignorableWhitespace( const rtl::OUString& aWhitespaces )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	saxHelper.ignorableWhitespace(aWhitespaces);
}
	
void SAL_CALL XMLDocumentWrapper_XmlSecImpl::processingInstruction( const rtl::OUString& aTarget, const rtl::OUString& aData )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	saxHelper.processingInstruction(aTarget, aData);
}
	
void SAL_CALL XMLDocumentWrapper_XmlSecImpl::setDocumentLocator( const cssu::Reference< cssxs::XLocator >& xLocator )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	saxHelper.setDocumentLocator(xLocator);
}

/* XCompressedDocumentHandler */
void SAL_CALL XMLDocumentWrapper_XmlSecImpl::_startDocument(  )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::_endDocument(  )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::_startElement( const rtl::OUString& aName, const cssu::Sequence< cssxcsax::XMLAttribute >& aAttributes )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	saxHelper.startElement(aName, aAttributes);
	m_pCurrentElement = saxHelper.getCurrentNode();
	
	buildIDAttr( m_pCurrentElement );
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::_endElement( const rtl::OUString& aName )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	endElement( aName );
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::_characters( const rtl::OUString& aChars )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	characters( aChars );
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::_ignorableWhitespace( const rtl::OUString& aWhitespaces )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	ignorableWhitespace( aWhitespaces );
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::_processingInstruction( const rtl::OUString& aTarget, const rtl::OUString& aData )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	processingInstruction( aTarget, aData );
}

void SAL_CALL XMLDocumentWrapper_XmlSecImpl::_setDocumentLocator( sal_Int32 /*columnNumber*/, sal_Int32 /*lineNumber*/, const rtl::OUString& /*publicId*/, const rtl::OUString& /*systemId*/ )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
}

rtl::OUString XMLDocumentWrapper_XmlSecImpl_getImplementationName ()
	throw (cssu::RuntimeException)
{
	return rtl::OUString ( RTL_ASCII_USTRINGPARAM ( IMPLEMENTATION_NAME ) );
}

sal_Bool SAL_CALL XMLDocumentWrapper_XmlSecImpl_supportsService( const rtl::OUString& ServiceName ) 
	throw (cssu::RuntimeException)
{
	return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( SERVICE_NAME ));
}

cssu::Sequence< rtl::OUString > SAL_CALL XMLDocumentWrapper_XmlSecImpl_getSupportedServiceNames(  ) 
	throw (cssu::RuntimeException)
{
	cssu::Sequence < rtl::OUString > aRet(1);
	rtl::OUString* pArray = aRet.getArray();
	pArray[0] =  rtl::OUString ( RTL_ASCII_USTRINGPARAM ( SERVICE_NAME ) );
	return aRet;
}
#undef SERVICE_NAME

cssu::Reference< cssu::XInterface > SAL_CALL XMLDocumentWrapper_XmlSecImpl_createInstance(
	const cssu::Reference< cssl::XMultiServiceFactory > &)
	throw( cssu::Exception )
{
	return (cppu::OWeakObject*) new XMLDocumentWrapper_XmlSecImpl( );
}

/* XServiceInfo */
rtl::OUString SAL_CALL XMLDocumentWrapper_XmlSecImpl::getImplementationName(  ) 
	throw (cssu::RuntimeException)
{
	return XMLDocumentWrapper_XmlSecImpl_getImplementationName();
}
sal_Bool SAL_CALL XMLDocumentWrapper_XmlSecImpl::supportsService( const rtl::OUString& rServiceName ) 
	throw (cssu::RuntimeException)
{
	return XMLDocumentWrapper_XmlSecImpl_supportsService( rServiceName );
}
cssu::Sequence< rtl::OUString > SAL_CALL XMLDocumentWrapper_XmlSecImpl::getSupportedServiceNames(  ) 
	throw (cssu::RuntimeException)
{
	return XMLDocumentWrapper_XmlSecImpl_getSupportedServiceNames();
}