/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 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();
}

