/**************************************************************
 * 
 * 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 "saxeventkeeperimpl.hxx"
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
#include <com/sun/star/xml/crypto/sax/ConstOfSecurityId.hpp>

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 cssxw = com::sun::star::xml::wrapper;
namespace cssxs = com::sun::star::xml::sax;

#define SERVICE_NAME "com.sun.star.xml.crypto.sax.SAXEventKeeper"
#define IMPLEMENTATION_NAME "com.sun.star.xml.security.framework.SAXEventKeeperImpl"

#define _USECOMPRESSEDDOCUMENTHANDLER

SAXEventKeeperImpl::SAXEventKeeperImpl( )
	:m_pRootBufferNode(NULL),
	 m_pCurrentBufferNode(NULL),
     m_nNextElementMarkId(1),
	 m_pNewBlocker(NULL),
	 m_pCurrentBlockingBufferNode(NULL),
	 m_bIsReleasing(false),
	 m_bIsForwarding(false)
{
	m_vElementMarkBuffers.reserve(2);
	m_vNewElementCollectors.reserve(2);
	m_vReleasedElementMarkBuffers.reserve(2);
}

SAXEventKeeperImpl::~SAXEventKeeperImpl()
{
	/*
	 * delete the BufferNode tree
	 */
	if (m_pRootBufferNode != NULL)
	{
        m_pRootBufferNode->freeAllChildren();
		delete m_pRootBufferNode;
	}
	
	m_pRootBufferNode = m_pCurrentBufferNode = m_pCurrentBlockingBufferNode = NULL;
	
	/*
	 * delete all unfreed ElementMarks
	 */
	m_vNewElementCollectors.clear();
	m_pNewBlocker = NULL;
	
	std::vector< const ElementMark* >::const_iterator ii = m_vElementMarkBuffers.begin();
	for( ; ii != m_vElementMarkBuffers.end(); ++ii ) 
	{
		delete (*ii);
	}
	m_vElementMarkBuffers.clear();
}

void SAXEventKeeperImpl::setCurrentBufferNode(BufferNode* pBufferNode)
/****** SAXEventKeeperImpl/setCurrentBufferNode ******************************
 *
 *   NAME
 *	setCurrentBufferNode -- set a new active BufferNode.
 *
 *   SYNOPSIS
 *	setCurrentBufferNode( pBufferNode );
 *
 *   FUNCTION
 *	connects this BufferNode into the BufferNode tree as a child of the
 *	current active BufferNode. Then makes this BufferNode as the current
 *	active BufferNode.
 *	If the previous active BufferNode points to the root
 *	BufferNode, which means that no buffering operation was proceeding,
 *	then notifies the status change listener that buffering  operation 
 *	will begin at once.
 *
 *   INPUTS
 *	pBufferNode - a BufferNode which will be the new active BufferNode
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	if (pBufferNode != m_pCurrentBufferNode)
	{
		if ( m_pCurrentBufferNode == m_pRootBufferNode &&
		     m_xSAXEventKeeperStatusChangeListener.is())
		{
			m_xSAXEventKeeperStatusChangeListener->collectionStatusChanged(sal_True);
		}
			
		if (pBufferNode->getParent() == NULL)
		{
			m_pCurrentBufferNode->addChild(pBufferNode);
			pBufferNode->setParent(m_pCurrentBufferNode);
		}
		
		m_pCurrentBufferNode = pBufferNode;
	}
}

BufferNode* SAXEventKeeperImpl::addNewElementMarkBuffers()
/****** SAXEventKeeperImpl/addNewElementMarkBuffers **************************
 *
 *   NAME
 *	addNewElementMarkBuffers -- add new ElementCollectors and new Blocker.
 *
 *   SYNOPSIS
 *	pBufferNode = addNewElementMarkBuffers( );
 *
 *   FUNCTION
 *	if there are new ElementCollector or new Blocker to be added, then
 *	connect all of them with the current BufferNode. In case of the
 *	current BufferNode doesn't exist, creates one.
 *	Clears up the new ElementCollector list and the new Blocker pointer.
 *
 *   INPUTS
 *	empty
 *
 *   RESULT
 *	pBufferNode - the BufferNode that has been connected with both new
 *	              ElementCollectors and new Blocker.
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	BufferNode* pBufferNode = NULL;

	if ( (m_vNewElementCollectors.size()>0) ||
	     (m_pNewBlocker != NULL))
	{
		/*
		 * When the current BufferNode is right pointing to the current
		 * working element in the XMLDocumentWrapper component, then
		 * no new BufferNode is needed to create.
		 * This situation can only happen in the "Forwarding" mode.
		 */
		if ( (m_pCurrentBufferNode != NULL) && 
		     (m_xXMLDocument->isCurrent(m_pCurrentBufferNode->getXMLElement())))
		{
			pBufferNode = m_pCurrentBufferNode;
		}
		else
		{
			pBufferNode = new BufferNode(m_xXMLDocument->getCurrentElement());
		}
			
		if (m_pNewBlocker != NULL)
		{
			pBufferNode->setBlocker(m_pNewBlocker);
			
			/*
			 * If no blocking before, then notify the status change listener that
			 * the SAXEventKeeper has entered "blocking" status, during which, no
			 * SAX events will be forwarded to the next document handler.
			 */
			if (m_pCurrentBlockingBufferNode == NULL)
			{
				m_pCurrentBlockingBufferNode = pBufferNode;
				
				if (m_xSAXEventKeeperStatusChangeListener.is())
				{
					m_xSAXEventKeeperStatusChangeListener->blockingStatusChanged(sal_True);
				}
			}
			
			m_pNewBlocker = NULL;
		}
		
		if (m_vNewElementCollectors.size()>0)
		{
			std::vector< const ElementCollector* >::const_iterator ii = m_vNewElementCollectors.begin();
			
			for( ; ii != m_vNewElementCollectors.end(); ++ii ) 
			{
				pBufferNode->addElementCollector(*ii);
			}
		
			m_vNewElementCollectors.clear();
		}
	}
	
	return pBufferNode;
}

ElementMark* SAXEventKeeperImpl::findElementMarkBuffer(sal_Int32 nId) const
/****** SAXEventKeeperImpl/findElementMarkBuffer *****************************
 *
 *   NAME
 *	findElementMarkBuffer -- finds an ElementMark.
 *
 *   SYNOPSIS
 *	pElementMark = findElementMarkBuffer( nId );
 *
 *   FUNCTION
 *	searches an ElementMark with the particular Id in the ElementMark
 *	list.
 *
 *   INPUTS
 *	nId - the Id of the ElementMark to be searched.
 *
 *   RESULT
 *	pElementMark - the ElementMark with the particular Id, or NULL when 
 *	               no such Id exists.
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	ElementMark* pElementMark = NULL;
	
	std::vector< const ElementMark* >::const_iterator ii = m_vElementMarkBuffers.begin();
			
	for( ; ii != m_vElementMarkBuffers.end(); ++ii ) 
	{
		if ( nId == (*ii)->getBufferId())
		{
			pElementMark = (ElementMark*)*ii;
			break;
		}
	}
	
	return pElementMark;
}

void SAXEventKeeperImpl::removeElementMarkBuffer(sal_Int32 nId)
/****** SAXEventKeeperImpl/removeElementMarkBuffer ***************************
 *
 *   NAME
 *	removeElementMarkBuffer -- removes an ElementMark
 *
 *   SYNOPSIS
 *	removeElementMarkBuffer( nId );
 *
 *   FUNCTION
 *	removes an ElementMark with the particular Id in the ElementMark list.
 *
 *   INPUTS
 *	nId - the Id of the ElementMark to be removed.
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	std::vector< const ElementMark* >::iterator ii = m_vElementMarkBuffers.begin();
			
	for( ; ii != m_vElementMarkBuffers.end(); ++ii ) 
	{
		if ( nId == (*ii)->getBufferId())
		{
			/*
			 * checks whether this ElementMark still in the new ElementCollect array
			 */
			std::vector< const ElementCollector* >::iterator jj = m_vNewElementCollectors.begin();
			for( ; jj != m_vNewElementCollectors.end(); ++jj ) 
			{
				if ((*ii) == (*jj))
				{
					m_vNewElementCollectors.erase(jj);
					break;
				}
			}
			
			/*
			 * checks whether this ElementMark is the new Blocker
			 */
			if ((*ii) == m_pNewBlocker)
			{
				m_pNewBlocker = NULL;
			}
			
			/*
			 * destory the ElementMark
			 */
			delete (*ii);
			
			m_vElementMarkBuffers.erase( ii );
			break;
		}
	}
}

rtl::OUString SAXEventKeeperImpl::printBufferNode(
	BufferNode* pBufferNode, sal_Int32 nIndent) const
/****** SAXEventKeeperImpl/printBufferNode ***********************************
 *
 *   NAME
 *	printBufferNode -- retrieves the information of a BufferNode and its
 *	branch.
 *
 *   SYNOPSIS
 *	info = printBufferNode( pBufferNode, nIndent );
 *
 *   FUNCTION
 *	all retrieved information includes:
 *	1. whether it is the current BufferNode;
 *	2. whether it is the current blocking BufferNode;
 *	3. the name of the parent element;
 *	4. the name of this element;
 *	5. all ElementCollectors working on this BufferNode;
 *	6. the Blocker working on this BufferNode;
 *	7. all child BufferNodes' information.
 *
 *   INPUTS
 *	pBufferNode - 	the BufferNode from where information will be retrieved.
 *	nIndent - 	how many space characters prefixed before the output
 *	          	message.
 *
 *   RESULT
 *	info - the information string
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	rtl::OUString rc;

	for ( int i=0; i<nIndent; ++i )
	{
		rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( " " ));
	}

	if (pBufferNode == m_pCurrentBufferNode)
	{
		rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "[%]" ));
	}
		
	if (pBufferNode == m_pCurrentBlockingBufferNode)
	{
		rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "[B]" ));
	}

	rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( " " ));
	rc += m_xXMLDocument->getNodeName(pBufferNode->getXMLElement());
	
	BufferNode* pParent = (BufferNode*)pBufferNode->getParent();
	if (pParent != NULL)
	{
		rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "[" ));
		rc += m_xXMLDocument->getNodeName(pParent->getXMLElement());
		rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "]" ));
	}
	
	rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ":EC=" ));
	rc += pBufferNode->printChildren();
	rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( " BR=" ));
		
	ElementMark * pBlocker = pBufferNode->getBlocker();
	if (pBlocker != NULL)
	{
		rc += rtl::OUString::valueOf( pBlocker->getBufferId() );
		rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "(SecId=" ));
		rc += rtl::OUString::valueOf( pBlocker->getSecurityId() );
		rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ")" ));
		rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( " " ));
	}
	rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "\n" ));
		
	std::vector< const BufferNode* >* vChildren = pBufferNode->getChildren();
	std::vector< const BufferNode* >::const_iterator jj = vChildren->begin();
	for( ; jj != vChildren->end(); ++jj ) 
	{
		rc += printBufferNode((BufferNode *)*jj, nIndent+4);
	}
	
	delete vChildren;
	
	return rc;
}

cssu::Sequence< cssu::Reference< cssxw::XXMLElementWrapper > > 
	SAXEventKeeperImpl::collectChildWorkingElement(BufferNode* pBufferNode) const
/****** SAXEventKeeperImpl/collectChildWorkingElement ************************
 *
 *   NAME
 *	collectChildWorkingElement -- collects a BufferNode's all child
 *	Elements.
 *
 *   SYNOPSIS
 *	list = collectChildWorkingElement( pBufferNode );
 *
 *   FUNCTION
 *	see NAME.
 *
 *   INPUTS
 *	pBufferNode - the BufferNode whose child Elements will be collected.
 *
 *   RESULT
 *	list - the child Elements list.
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	std::vector< const BufferNode* >* vChildren = pBufferNode->getChildren();
	
	cssu::Sequence < cssu::Reference< 
		cssxw::XXMLElementWrapper > > aChildrenCollection ( vChildren->size());
	
	std::vector< const BufferNode* >::const_iterator ii = vChildren->begin();
	
	sal_Int32 nIndex = 0;
	for( ; ii != vChildren->end(); ++ii ) 
	{
		aChildrenCollection[nIndex] = (*ii)->getXMLElement();
		nIndex++;
	}
	
	delete vChildren;
	
	return aChildrenCollection;
}

void SAXEventKeeperImpl::smashBufferNode(
	BufferNode* pBufferNode, bool bClearRoot) const
/****** SAXEventKeeperImpl/smashBufferNode ***********************************
 *
 *   NAME
 *	smashBufferNode -- removes a BufferNode along with its working
 *	element.
 *
 *   SYNOPSIS
 *	smashBufferNode( pBufferNode, bClearRoot );
 *
 *   FUNCTION
 *	removes the BufferNode's working element from the DOM document, while
 *	reserves all ancestor paths for its child BufferNodes.
 *	when any of the BufferNode's ancestor element is useless, removes it
 *	too.
 *	removes the BufferNode from the BufferNode tree.
 *
 *   INPUTS
 *	pBufferNode - 	the BufferNode to be removed
 *	bClearRoot - 	whether the root element also needs to be cleared up.
 *
 *   RESULT
 *	empty
 *
 *   NOTES
 *	when removeing a Blocker's BufferNode, the bClearRoot flag should be
 *	true. Because a Blocker can buffer many SAX events which are not used
 *	by any other ElementCollector or Blocker.
 *	When the bClearRoot is set to true, the root BufferNode will be first
 *	cleared, with a stop flag seting at the next Blocking BufferNode. This
 *	operation can delete all useless bufferred SAX events which are only
 *	needed by the Blocker to be deleted.
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	if (!pBufferNode->hasAnything())
	{
		BufferNode* pParent = (BufferNode*)pBufferNode->getParent();
		
	        /*
	         * delete the XML data
	         */
		if (pParent == m_pRootBufferNode)
		{
			bool bIsNotBlocking = (m_pCurrentBlockingBufferNode == NULL);
			bool bIsBlockInside = false;
			bool bIsBlockingAfterward = false;
			
		        /*
		         * If this is a blocker, then remove any out-element data
		         * which caused by blocking. The removal process will stop
		         * at the next blokcer to avoid removing any useful data.
		         */
			if (bClearRoot)
			{
				cssu::Sequence< cssu::Reference< cssxw::XXMLElementWrapper > > 
					aChildElements = collectChildWorkingElement(m_pRootBufferNode);
				
			        /*
			         * the clearUselessData only clearup the content in the 
			         * node, not the node itself.
			         */
				m_xXMLDocument->clearUselessData(m_pRootBufferNode->getXMLElement(),
					aChildElements,
					bIsNotBlocking?(NULL):
					               (m_pCurrentBlockingBufferNode->getXMLElement()));
					
			        /*
			         * remove the node if it is empty, then if its parent is also
			         * empty, remove it, then if the next parent is also empty,
			         * remove it,..., until parent become null.
			         */
				m_xXMLDocument->collapse( m_pRootBufferNode->getXMLElement() );
			}
			
			/*
			 * if blocking, check the relationship between this BufferNode and 
			 * the current blocking BufferNode.
			 */
			if ( !bIsNotBlocking )
			{
				/*
				 * the current blocking BufferNode is a descendant of this BufferNode.
				 */
				bIsBlockInside = (NULL != pBufferNode->isAncestor(m_pCurrentBlockingBufferNode));
				
				/*
				 * the current blocking BufferNode locates behind this BufferNode in tree
				 * order.
				 */
				bIsBlockingAfterward = pBufferNode->isPrevious(m_pCurrentBlockingBufferNode);
			}
			
			/*
			 * this BufferNode's working element needs to be deleted only when
			 * 1. there is no blocking, or
			 * 2. the current blocking BufferNode is a descendant of this BufferNode, 
			 *    (then in the BufferNode's working element, the useless data before the blocking
			 *     element should be deleted.) or
			 * 3. the current blocking BufferNode is locates behind this BufferNode in tree,
			 *    (then the useless data between the blocking element and the working element
			 *     should be deleted.).
			 * Otherwise, this working element should not be deleted.
			 */
			if ( bIsNotBlocking || bIsBlockInside || bIsBlockingAfterward )
			{
				cssu::Sequence< cssu::Reference< cssxw::XXMLElementWrapper > > 
					aChildElements = collectChildWorkingElement(pBufferNode);
				
			        /*
			         * the clearUselessData only clearup the content in the 
			         * node, not the node itself.
			         */
				m_xXMLDocument->clearUselessData(pBufferNode->getXMLElement(),
					aChildElements,
					bIsBlockInside?(m_pCurrentBlockingBufferNode->getXMLElement()):
						       (NULL));
					
			        /*
			         * remove the node if it is empty, then if its parent is also
			         * empty, remove it, then if the next parent is also empty,
			         * remove it,..., until parent become null.
			         */
				m_xXMLDocument->collapse( pBufferNode->getXMLElement() );
			}
		}

		sal_Int32 nIndex = pParent->indexOfChild(pBufferNode);

		std::vector< const BufferNode* >* vChildren = pBufferNode->getChildren();
		pParent->removeChild(pBufferNode);
		pBufferNode->setParent(NULL);
		
		std::vector< const BufferNode * >::const_iterator ii = vChildren->begin();
		for( ; ii != vChildren->end(); ++ii ) 
		{
			((BufferNode *)(*ii))->setParent(pParent);
			pParent->addChild(*ii, nIndex);
			nIndex++;
		}
		
		delete vChildren;
		
		/*
		 * delete the BufferNode
		 */
		delete pBufferNode;
	}
}

BufferNode* SAXEventKeeperImpl::findNextBlockingBufferNode(
	BufferNode* pStartBufferNode) const
/****** SAXEventKeeperImpl/findNextBlockingBufferNode ************************
 *
 *   NAME
 *	findNextBlockingBufferNode -- finds the next blocking BufferNode
 *	behind the particular BufferNode.
 *
 *   SYNOPSIS
 *	pBufferNode = findNextBlockingBufferNode( pStartBufferNode );
 *
 *   FUNCTION
 *	see NAME.
 *
 *   INPUTS
 *	pStartBufferNode - the BufferNode from where to search the next 
 *	                   blocking BufferNode.
 *
 *   RESULT
 *	pBufferNode - the next blocking BufferNode, or NULL if no such 
 *	              BufferNode exists.
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	BufferNode* pNext = NULL;
	
	if (pStartBufferNode != NULL)
	{
		pNext = pStartBufferNode;
		
		while (NULL != (pNext = (BufferNode*)pNext->getNextNodeByTreeOrder()))
		{
			if (pNext->getBlocker() != NULL)
			{
				break;
			}
		}
	}
		
	return pNext;
}

void SAXEventKeeperImpl::diffuse(BufferNode* pBufferNode) const
/****** SAXEventKeeperImpl/diffuse *******************************************
 *
 *   NAME
 *	diffuse -- diffuse the notification.
 *
 *   SYNOPSIS
 *	diffuse( pBufferNode );
 *
 *   FUNCTION
 *	diffuse the collecting completion notification from the specific
 *	BufferNode along its parent link, until an ancestor which is not
 *	completely received is met.
 *
 *   INPUTS
 *	pBufferNode - the BufferNode from which the notification will be 
 *	              diffused.
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	BufferNode* pParent = pBufferNode;
	
	while(pParent->isAllReceived())
	{
		pParent->elementCollectorNotify();
		pParent = (BufferNode*)pParent->getParent();
	}
}

void SAXEventKeeperImpl::releaseElementMarkBuffer()
/****** SAXEventKeeperImpl/releaseElementMarkBuffer **************************
 *
 *   NAME
 *	releaseElementMarkBuffer -- releases useless ElementMarks 
 *
 *   SYNOPSIS
 *	releaseElementMarkBuffer( );
 *
 *   FUNCTION
 *	releases each ElementMark in the releasing list
 *	m_vReleasedElementMarkBuffers.
 *	The operation differs between an ElementCollector and a Blocker.
 *
 *   INPUTS
 *	empty
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	m_bIsReleasing = true;
	while (m_vReleasedElementMarkBuffers.size()>0)
	{
		std::vector< sal_Int32 >::iterator pId = m_vReleasedElementMarkBuffers.begin();
		sal_Int32 nId = *pId;
		m_vReleasedElementMarkBuffers.erase( pId );
		
		ElementMark* pElementMark = findElementMarkBuffer(nId);

		if (pElementMark != NULL)
		{
			if (cssxc::sax::ElementMarkType_ELEMENTCOLLECTOR 
				== pElementMark->getType()) 
			/*
			 * it is a EC
			 */
			{
				ElementCollector* pElementCollector = (ElementCollector*)pElementMark;
					
				cssxc::sax::ElementMarkPriority nPriority = pElementCollector->getPriority();
				bool bToModify = pElementCollector->getModify();
				
				/*
			         * Delete the EC from the buffer node.
			         */
				BufferNode* pBufferNode = pElementCollector->getBufferNode();
				pBufferNode->removeElementCollector(pElementCollector);

				if ( nPriority == cssxc::sax::ElementMarkPriority_BEFOREMODIFY)
				{
					pBufferNode->notifyBranch();
				}
						
				if (bToModify)
				{
					pBufferNode->notifyAncestor();
				}
				
				/*
				 * delete the ElementMark
				 */		 
				pElementCollector = NULL;
				pElementMark = NULL;
				removeElementMarkBuffer(nId);			
				
				/*
				 * delete the BufferNode
				 */
				diffuse(pBufferNode);
				smashBufferNode(pBufferNode, false);
			}
			else 
			/*
			 * it is a Blocker
			 */
			{
			        /*
			         * Delete the TH from the buffer node.
			         */
				BufferNode *pBufferNode = pElementMark->getBufferNode();
				pBufferNode->setBlocker(NULL);
				
			        /*
			         * If there is a following handler and no blocking now, then
			         * forward this event
			         */
				if (m_pCurrentBlockingBufferNode == pBufferNode)
				{
				        /*
				         * Before forwarding, the next blocking point needs to be 
				         * found.
				         */
					m_pCurrentBlockingBufferNode = findNextBlockingBufferNode(pBufferNode);
							
				        /*
				         * Forward the blocked events between these two STHs.
				         */
	       				if (m_xNextHandler.is())
	       				{
       						BufferNode* pTempCurrentBufferNode = m_pCurrentBufferNode;
       						BufferNode* pTempCurrentBlockingBufferNode = m_pCurrentBlockingBufferNode;
				       					
       						m_pCurrentBufferNode = pBufferNode;
       						m_pCurrentBlockingBufferNode = NULL;
				       					
						m_bIsForwarding = true;

						m_xXMLDocument->generateSAXEvents(
							m_xNextHandler,
							this,
							pBufferNode->getXMLElement(),
							(pTempCurrentBlockingBufferNode == NULL)?NULL:(pTempCurrentBlockingBufferNode->getXMLElement()));

						m_bIsForwarding = false;
								
						m_pCurrentBufferNode = pTempCurrentBufferNode;
						if (m_pCurrentBlockingBufferNode == NULL)
						{
							m_pCurrentBlockingBufferNode = pTempCurrentBlockingBufferNode;
						}
					}
							
					if (m_pCurrentBlockingBufferNode == NULL && 
					    m_xSAXEventKeeperStatusChangeListener.is())
					{
						m_xSAXEventKeeperStatusChangeListener->blockingStatusChanged(sal_False);
					}
				}

				/*
				 * delete the ElementMark
				 */		 
				pElementMark = NULL;
				removeElementMarkBuffer(nId);
						
				/*
				 * delete the BufferNode
				 */
				diffuse(pBufferNode);
				smashBufferNode(pBufferNode, true);
			}
		}
	}
		
	m_bIsReleasing = false;
		
	if (!m_pRootBufferNode->hasAnything() && 
		!m_pRootBufferNode->hasChildren() && 
		m_xSAXEventKeeperStatusChangeListener.is())
	{
		m_xSAXEventKeeperStatusChangeListener->bufferStatusChanged(sal_True);
	}
}

void SAXEventKeeperImpl::markElementMarkBuffer(sal_Int32 nId)
/****** SAXEventKeeperImpl/markElementMarkBuffer *****************************
 *
 *   NAME
 *	markElementMarkBuffer -- marks an ElementMark to be released
 *
 *   SYNOPSIS
 *	markElementMarkBuffer( nId );
 *
 *   FUNCTION
 *	puts the ElementMark with the particular Id into the releasing list,
 *	checks whether the releasing process is runing, if not then launch 
 *	this process.
 *
 *   INPUTS
 *	nId - the Id of the ElementMark which will be released
 *
 *   RESULT
 *	empty
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	m_vReleasedElementMarkBuffers.push_back( nId );
	if ( !m_bIsReleasing )
	{
		releaseElementMarkBuffer();
	}
}

sal_Int32 SAXEventKeeperImpl::createElementCollector(
	sal_Int32 nSecurityId,
	cssxc::sax::ElementMarkPriority nPriority,
	bool bModifyElement,
	const cssu::Reference< cssxc::sax::XReferenceResolvedListener >& xReferenceResolvedListener)
/****** SAXEventKeeperImpl/createElementCollector ****************************
 *
 *   NAME
 *	createElementCollector -- creates a new ElementCollector on the
 *	incoming element.
 *
 *   SYNOPSIS
 *	nId = createElementCollector( nSecurityId, nPriority,
 *	                             bModifyElement,
 *	                             xReferenceResolvedListener );
 *
 *   FUNCTION
 *	allocs a new Id, then create an ElementCollector with this Id value.
 *	Add the new created ElementCollector to the new ElementCollecotor list.
 *
 *   INPUTS
 *	nSecurityId - 	the security Id of the new ElementCollector
 *	nPriority - 	the prirority of the new ElementCollector
 *	bModifyElement -whether this BufferNode will modify the content of
 *	                the corresponding element it works on
 *	xReferenceResolvedListener - the listener for the new ElementCollector.
 *
 *   RESULT
 *	nId - the Id of the new ElementCollector
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	sal_Int32 nId = m_nNextElementMarkId;
	m_nNextElementMarkId ++;
	
	ElementCollector* pElementCollector 
		= new ElementCollector(
			nSecurityId,
			nId,
			nPriority,
			bModifyElement,
			xReferenceResolvedListener);
				
	m_vElementMarkBuffers.push_back( pElementCollector );
		
        /*
         * All the new EC to initial EC array.
         */
	m_vNewElementCollectors.push_back( pElementCollector );
		
	return nId;
}

	
sal_Int32 SAXEventKeeperImpl::createBlocker(sal_Int32 nSecurityId)
/****** SAXEventKeeperImpl/createBlocker *************************************
 *
 *   NAME
 *	createBlocker -- creates a new Blocker on the incoming element.
 *
 *   SYNOPSIS
 *	nId = createBlocker( nSecurityId );
 *
 *   FUNCTION
 *	see NAME.
 *
 *   INPUTS
 *	nSecurityId - 	the security Id of the new Blocker
 *
 *   RESULT
 *	nId - the Id of the new Blocker
 *
 *   HISTORY
 *	05.01.2004 -	implemented
 *
 *   AUTHOR
 *	Michael Mi
 *	Email: michael.mi@sun.com
 ******************************************************************************/
{
	sal_Int32 nId = m_nNextElementMarkId;
	m_nNextElementMarkId ++;

	OSL_ASSERT(m_pNewBlocker == NULL);

	m_pNewBlocker = new ElementMark(nSecurityId, nId);
	m_vElementMarkBuffers.push_back( m_pNewBlocker );
		
	return nId;
}

/* XSAXEventKeeper */
sal_Int32 SAL_CALL SAXEventKeeperImpl::addElementCollector(  )
	throw (cssu::RuntimeException)
{
	return createElementCollector(
		cssxc::sax::ConstOfSecurityId::UNDEFINEDSECURITYID,
		cssxc::sax::ElementMarkPriority_AFTERMODIFY,
		false,
		NULL);
}

void SAL_CALL SAXEventKeeperImpl::removeElementCollector( sal_Int32 id )
	throw (cssu::RuntimeException)
{
	markElementMarkBuffer(id);
}
	
sal_Int32 SAL_CALL SAXEventKeeperImpl::addBlocker(  )
	throw (cssu::RuntimeException)
{
	return createBlocker(cssxc::sax::ConstOfSecurityId::UNDEFINEDSECURITYID);
}
	
void SAL_CALL SAXEventKeeperImpl::removeBlocker( sal_Int32 id )
	throw (cssu::RuntimeException)
{
	markElementMarkBuffer(id);
}
	
sal_Bool SAL_CALL SAXEventKeeperImpl::isBlocking(  )
	throw (cssu::RuntimeException)
{
	return (m_pCurrentBlockingBufferNode != NULL);
}
	
cssu::Reference< cssxw::XXMLElementWrapper > SAL_CALL 
	SAXEventKeeperImpl::getElement( sal_Int32 id )
	throw (cssu::RuntimeException)
{
	cssu::Reference< cssxw::XXMLElementWrapper > rc;
	
	ElementMark* pElementMark = findElementMarkBuffer(id);
	if (pElementMark != NULL)
	{
		rc = pElementMark->getBufferNode()->getXMLElement();
	}
	
	return rc;
}

void SAL_CALL SAXEventKeeperImpl::setElement( 
	sal_Int32 id, 
	const cssu::Reference< cssxw::XXMLElementWrapper >& aElement )
	throw (cssu::RuntimeException)
{
	if (aElement.is())
	{
		m_xXMLDocument->rebuildIDLink(aElement);
		
		ElementMark* pElementMark = findElementMarkBuffer(id);
	
		if (pElementMark != NULL)
		{
			BufferNode* pBufferNode = pElementMark->getBufferNode();
			if (pBufferNode != NULL)
			{
			        bool bIsCurrent = m_xXMLDocument->isCurrent(pBufferNode->getXMLElement());
				pBufferNode->setXMLElement(aElement);
				
				if (bIsCurrent)
				{
					m_xXMLDocument->setCurrentElement(aElement);
				}
			}
		}
	}
	else
	{
		removeElementCollector( id );
	}
}
	
cssu::Reference< cssxs::XDocumentHandler > SAL_CALL SAXEventKeeperImpl::setNextHandler(
	const cssu::Reference< cssxs::XDocumentHandler >& xNewHandler )
	throw (cssu::RuntimeException)
{
	cssu::Reference< cssxs::XDocumentHandler > xOldHandler = m_xNextHandler;
	
	m_xNextHandler = xNewHandler;
	return xOldHandler;
}
	
rtl::OUString SAL_CALL SAXEventKeeperImpl::printBufferNodeTree()
	throw (cssu::RuntimeException)
{
	rtl::OUString rc;
	
	rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ElementMarkBuffers: size = " ));
	rc += rtl::OUString::valueOf((sal_Int32)m_vElementMarkBuffers.size());
	rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "\nCurrentBufferNode: " ));
	rc += m_xXMLDocument->getNodeName(m_pCurrentBufferNode->getXMLElement());
	rc += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "\n" ));
	rc += printBufferNode(m_pRootBufferNode, 0);
	
	return rc;
}
	
cssu::Reference< cssxw::XXMLElementWrapper > SAL_CALL SAXEventKeeperImpl::getCurrentBlockingNode()
	throw (cssu::RuntimeException)
{
	cssu::Reference< cssxw::XXMLElementWrapper > rc;
	
	if (m_pCurrentBlockingBufferNode != NULL)
	{
		rc = m_pCurrentBlockingBufferNode->getXMLElement();
	}
	
	return rc;
}

/* XSecuritySAXEventKeeper */
sal_Int32 SAL_CALL SAXEventKeeperImpl::addSecurityElementCollector( 
	cssxc::sax::ElementMarkPriority priority,
	sal_Bool modifyElement )
	throw (cssu::RuntimeException)
{
	return createElementCollector(
		cssxc::sax::ConstOfSecurityId::UNDEFINEDSECURITYID,
		priority,
		modifyElement,
		NULL);
}
	
sal_Int32 SAL_CALL SAXEventKeeperImpl::cloneElementCollector(
	sal_Int32 referenceId,
	cssxc::sax::ElementMarkPriority priority )
	throw (cssu::RuntimeException)
{
	sal_Int32 nId = -1;
	
	ElementCollector* pElementCollector = (ElementCollector*)findElementMarkBuffer(referenceId);
	if (pElementCollector != NULL)
	{
		nId = m_nNextElementMarkId;
		m_nNextElementMarkId ++;
			
		ElementCollector* pClonedOne 
			= pElementCollector->clone(nId, priority);
			
	        /*
	         * add this EC into the security data buffer array.
	         */
		m_vElementMarkBuffers.push_back(pClonedOne);

	        /*
	         * If the reference EC is still in initial EC array, add
	         * this cloned one into the initial EC array too.
	         */
	        if (pElementCollector->getBufferNode() == NULL)
		{
			m_vNewElementCollectors.push_back(pClonedOne);
		}
	}
	
	return nId;
}
	
void SAL_CALL SAXEventKeeperImpl::setSecurityId( sal_Int32 id, sal_Int32 securityId )
	throw (cssu::RuntimeException)
{
	ElementMark* pElementMark = findElementMarkBuffer(id);
	if (pElementMark != NULL)
	{
		pElementMark->setSecurityId(securityId);
	}
}

	
/* XReferenceResolvedBroadcaster */
void SAL_CALL SAXEventKeeperImpl::addReferenceResolvedListener(
	sal_Int32 referenceId,
	const cssu::Reference< cssxc::sax::XReferenceResolvedListener >& listener )
	throw (cssu::RuntimeException)
{
	ElementCollector* pElementCollector = (ElementCollector*)findElementMarkBuffer(referenceId);
	if (pElementCollector != NULL)
	{
		pElementCollector->setReferenceResolvedListener(listener);
	}
}
	
void SAL_CALL SAXEventKeeperImpl::removeReferenceResolvedListener( 
	sal_Int32 /*referenceId*/, 
	const cssu::Reference< cssxc::sax::XReferenceResolvedListener >&)
	throw (cssu::RuntimeException)
{
}
	
/* XSAXEventKeeperStatusChangeBroadcaster */
void SAL_CALL SAXEventKeeperImpl::addSAXEventKeeperStatusChangeListener(
	const cssu::Reference< cssxc::sax::XSAXEventKeeperStatusChangeListener >& listener )
	throw (cssu::RuntimeException)
{
	m_xSAXEventKeeperStatusChangeListener = listener;
}
	
void SAL_CALL SAXEventKeeperImpl::removeSAXEventKeeperStatusChangeListener(
	const cssu::Reference< cssxc::sax::XSAXEventKeeperStatusChangeListener >&)
	throw (cssu::RuntimeException)
{
}
	
/* XDocumentHandler */
void SAL_CALL SAXEventKeeperImpl::startDocument(  )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	if ( m_xNextHandler.is())
	{
		m_xNextHandler->startDocument();
	}
}
	
void SAL_CALL SAXEventKeeperImpl::endDocument(  )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	if ( m_xNextHandler.is())
	{
		m_xNextHandler->endDocument();
	}
}
	
void SAL_CALL SAXEventKeeperImpl::startElement(
	const rtl::OUString& aName,
	const cssu::Reference< cssxs::XAttributeList >& xAttribs )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
        /*
         * If there is a following handler and no blocking now, then
         * forward this event
         */
	if ((m_pCurrentBlockingBufferNode == NULL) &&
	    (m_xNextHandler.is()) && 
	    (!m_bIsForwarding) && 
	    (m_pNewBlocker == NULL))
	{
		m_xNextHandler->startElement(aName, xAttribs);
	}

        /*
         * If not forwarding, buffer this startElement.
         */
       	if (!m_bIsForwarding)
       	{
	#ifndef _USECOMPRESSEDDOCUMENTHANDLER
		m_xDocumentHandler->startElement(aName, xAttribs);
	#else
		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);
		}
		
		m_xCompressedDocumentHandler->_startElement(aName, aAttributes);
	#endif	       		
       		
	}
		
	BufferNode* pBufferNode = addNewElementMarkBuffers();
        if (pBufferNode != NULL)
        {
		setCurrentBufferNode(pBufferNode);
	}
}
	
void SAL_CALL SAXEventKeeperImpl::endElement( const rtl::OUString& aName ) 
	throw (cssxs::SAXException, cssu::RuntimeException)
{
        sal_Bool bIsCurrent = m_xXMLDocument->isCurrent(m_pCurrentBufferNode->getXMLElement());
	        
        /*
         * If there is a following handler and no blocking now, then
         * forward this event
         */
	if ((m_pCurrentBlockingBufferNode == NULL) && 
	    (m_xNextHandler.is()) && 
	    (!m_bIsForwarding))
	{
		m_xNextHandler->endElement(aName);
	}
		
	if ((m_pCurrentBlockingBufferNode != NULL) || 
	    (m_pCurrentBufferNode != m_pRootBufferNode) || 
	    (!m_xXMLDocument->isCurrentElementEmpty()))
	{
        	if (!m_bIsForwarding)
        	{
		#ifndef _USECOMPRESSEDDOCUMENTHANDLER
			m_xDocumentHandler->endElement(aName);
		#else
			m_xCompressedDocumentHandler->_endElement(aName);
		#endif	       		
		}
				
        /*
        * If the current buffer node has not notified yet, and 
        * the current buffer node is waiting for the current element,
        * then let it notify.
        */
       	if (bIsCurrent && (m_pCurrentBufferNode != m_pRootBufferNode))
		{
			BufferNode* pOldCurrentBufferNode = m_pCurrentBufferNode;
			m_pCurrentBufferNode = (BufferNode*)m_pCurrentBufferNode->getParent();
			
			pOldCurrentBufferNode->setReceivedAll();
				
			if ((m_pCurrentBufferNode == m_pRootBufferNode) && 
			    m_xSAXEventKeeperStatusChangeListener.is())
			{
				m_xSAXEventKeeperStatusChangeListener->collectionStatusChanged(sal_False);
			}
		}
    }
    else
    {
        if (!m_bIsForwarding)
        {
            m_xXMLDocument->removeCurrentElement();
        }
    }
}
	
void SAL_CALL SAXEventKeeperImpl::characters( const rtl::OUString& aChars )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	if (!m_bIsForwarding)
	{
		if ((m_pCurrentBlockingBufferNode == NULL) && m_xNextHandler.is())
		{
			m_xNextHandler->characters(aChars);
		}
		
		if ((m_pCurrentBlockingBufferNode != NULL) || 
		    (m_pCurrentBufferNode != m_pRootBufferNode))
		{
		#ifndef _USECOMPRESSEDDOCUMENTHANDLER
        		m_xDocumentHandler->characters(aChars);
		#else
			m_xCompressedDocumentHandler->_characters(aChars);
		#endif	       		
        	}
        }
}
	
void SAL_CALL SAXEventKeeperImpl::ignorableWhitespace( const rtl::OUString& aWhitespaces )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	characters( aWhitespaces );
}
	
void SAL_CALL SAXEventKeeperImpl::processingInstruction( 
	const rtl::OUString& aTarget, const rtl::OUString& aData )
	throw (cssxs::SAXException, cssu::RuntimeException)
{
	if (!m_bIsForwarding)
	{
		if ((m_pCurrentBlockingBufferNode == NULL) && m_xNextHandler.is())
		{
			m_xNextHandler->processingInstruction(aTarget, aData);
		}
		
		if ((m_pCurrentBlockingBufferNode != NULL) || 
		    (m_pCurrentBufferNode != m_pRootBufferNode))
		{
		#ifndef _USECOMPRESSEDDOCUMENTHANDLER
			m_xDocumentHandler->processingInstruction(aTarget, aData);
		#else
			m_xCompressedDocumentHandler->_processingInstruction(aTarget, aData);
		#endif	       		
        	}
        }
}
	
void SAL_CALL SAXEventKeeperImpl::setDocumentLocator( const cssu::Reference< cssxs::XLocator >&)
	throw (cssxs::SAXException, cssu::RuntimeException)
{
}
	
/* XInitialization */
void SAL_CALL SAXEventKeeperImpl::initialize( const cssu::Sequence< cssu::Any >& aArguments ) 
	throw (cssu::Exception, cssu::RuntimeException)
{
	OSL_ASSERT(aArguments.getLength() == 1);
	
	aArguments[0] >>= m_xXMLDocument;
	m_xDocumentHandler = cssu::Reference< cssxs::XDocumentHandler >( 
		m_xXMLDocument, cssu::UNO_QUERY );
	m_xCompressedDocumentHandler = cssu::Reference< cssxcsax::XCompressedDocumentHandler >( 
		m_xXMLDocument, cssu::UNO_QUERY );
	
	m_pRootBufferNode = new BufferNode(m_xXMLDocument->getCurrentElement());
	m_pCurrentBufferNode = m_pRootBufferNode;
}
	
rtl::OUString SAXEventKeeperImpl_getImplementationName ()
	throw (cssu::RuntimeException)
{
	return rtl::OUString ( RTL_CONSTASCII_USTRINGPARAM ( IMPLEMENTATION_NAME ) );
}

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

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

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

/* XServiceInfo */
rtl::OUString SAL_CALL SAXEventKeeperImpl::getImplementationName(  ) 
	throw (cssu::RuntimeException)
{
	return SAXEventKeeperImpl_getImplementationName();
}
sal_Bool SAL_CALL SAXEventKeeperImpl::supportsService( const rtl::OUString& rServiceName ) 
	throw (cssu::RuntimeException)
{
	return SAXEventKeeperImpl_supportsService( rServiceName );
}
cssu::Sequence< rtl::OUString > SAL_CALL SAXEventKeeperImpl::getSupportedServiceNames(  ) 
	throw (cssu::RuntimeException)
{
	return SAXEventKeeperImpl_getSupportedServiceNames();
}