/**************************************************************
 * 
 * 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_xmlscript.hxx"

#include "osl/diagnose.h"
#include "osl/mutex.hxx"
#include "rtl/ustrbuf.hxx"
#include "cppuhelper/factory.hxx"
#include "cppuhelper/implementationentry.hxx"
#include "cppuhelper/implbase1.hxx"
#include "cppuhelper/implbase3.hxx"
#include "xmlscript/xml_import.hxx"

#include "com/sun/star/xml/input/XAttributes.hpp"
#include "com/sun/star/lang/XInitialization.hpp"
#include "com/sun/star/uno/XComponentContext.hpp"

#include <vector>
#include <hash_map>

#include <memory>


using namespace ::rtl;
using namespace ::osl;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;

namespace xmlscript
{

const sal_Int32 UID_UNKNOWN = -1;

Sequence< OUString > getSupportedServiceNames_DocumentHandlerImpl()
{
    OUString name( RTL_CONSTASCII_USTRINGPARAM(
                       "com.sun.star.xml.input.SaxDocumentHandler") );
    return Sequence< OUString >( &name, 1 );
}

OUString getImplementationName_DocumentHandlerImpl()
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM(
                         "com.sun.star.comp.xml.input.SaxDocumentHandler") );
}

typedef ::std::hash_map< OUString, sal_Int32, OUStringHash > t_OUString2LongMap;
typedef ::std::hash_map< sal_Int32, OUString > t_Long2OUStringMap;

struct PrefixEntry
{
    ::std::vector< sal_Int32 > m_Uids;

    inline PrefixEntry() SAL_THROW( () )
        { m_Uids.reserve( 4 ); }
};

typedef ::std::hash_map<
    OUString, PrefixEntry *, OUStringHash > t_OUString2PrefixMap;

struct ElementEntry
{
    Reference< xml::input::XElement > m_xElement;
    ::std::vector< OUString > m_prefixes;

    inline ElementEntry()
        { m_prefixes.reserve( 2 ); }
};

typedef ::std::vector< ElementEntry * > t_ElementVector;

class ExtendedAttributes;

//==============================================================================
struct MGuard
{
    Mutex * m_pMutex;
    explicit MGuard( Mutex * pMutex )
        : m_pMutex( pMutex )
        { if (m_pMutex) m_pMutex->acquire(); }
    ~MGuard() throw ()
        { if (m_pMutex) m_pMutex->release(); }
};

//==============================================================================
class DocumentHandlerImpl :
    public ::cppu::WeakImplHelper3< xml::sax::XDocumentHandler,
                                    xml::input::XNamespaceMapping,
                                    lang::XInitialization >
{
    friend class ExtendedAttributes;

    Reference< xml::input::XRoot > m_xRoot;
    
    t_OUString2LongMap m_URI2Uid;
    sal_Int32 m_uid_count;
    
    OUString m_sXMLNS_PREFIX_UNKNOWN;
    OUString m_sXMLNS;

    sal_Int32 m_nLastURI_lookup;
    OUString m_aLastURI_lookup;

    t_OUString2PrefixMap m_prefixes;
    sal_Int32 m_nLastPrefix_lookup;
    OUString m_aLastPrefix_lookup;

    t_ElementVector m_elements;
    sal_Int32 m_nSkipElements;

    Mutex * m_pMutex;

    inline Reference< xml::input::XElement > getCurrentElement() const;
    
    inline sal_Int32 getUidByURI( OUString const & rURI );
    inline sal_Int32 getUidByPrefix( OUString const & rPrefix );
    
    inline void pushPrefix(
        OUString const & rPrefix, OUString const & rURI );
    inline void popPrefix( OUString const & rPrefix );
    
    inline void getElementName(
        OUString const & rQName, sal_Int32 * pUid, OUString * pLocalName );
    
public:
    DocumentHandlerImpl(
        Reference< xml::input::XRoot > const & xRoot,
        bool bSingleThreadedUse );
    virtual ~DocumentHandlerImpl() throw ();

    // XServiceInfo
    virtual OUString SAL_CALL getImplementationName()
        throw (RuntimeException);
    virtual sal_Bool SAL_CALL supportsService(
        OUString const & servicename )
        throw (RuntimeException);
    virtual Sequence< OUString > SAL_CALL getSupportedServiceNames()
        throw (RuntimeException);
    
    // XInitialization
    virtual void SAL_CALL initialize(
        Sequence< Any > const & arguments )
        throw (Exception);
    
    // XDocumentHandler
    virtual void SAL_CALL startDocument()
        throw (xml::sax::SAXException, RuntimeException);
    virtual void SAL_CALL endDocument()
        throw (xml::sax::SAXException, RuntimeException);
    virtual void SAL_CALL startElement(
        OUString const & rQElementName,
        Reference< xml::sax::XAttributeList > const & xAttribs )
        throw (xml::sax::SAXException, RuntimeException);
    virtual void SAL_CALL endElement(
        OUString const & rQElementName )
        throw (xml::sax::SAXException, RuntimeException);
    virtual void SAL_CALL characters(
        OUString const & rChars )
        throw (xml::sax::SAXException, RuntimeException);
    virtual void SAL_CALL ignorableWhitespace(
        OUString const & rWhitespaces )
        throw (xml::sax::SAXException, RuntimeException);
    virtual void SAL_CALL processingInstruction(
        OUString const & rTarget, OUString const & rData )
        throw (xml::sax::SAXException, RuntimeException);
    virtual void SAL_CALL setDocumentLocator(
        Reference< xml::sax::XLocator > const & xLocator )
        throw (xml::sax::SAXException, RuntimeException);

    // XNamespaceMapping
    virtual sal_Int32 SAL_CALL getUidByUri( OUString const & Uri )
        throw (RuntimeException);
    virtual OUString SAL_CALL getUriByUid( sal_Int32 Uid )
        throw (container::NoSuchElementException, RuntimeException);
};

//______________________________________________________________________________
DocumentHandlerImpl::DocumentHandlerImpl(
    Reference< xml::input::XRoot > const & xRoot,
    bool bSingleThreadedUse )
    : m_xRoot( xRoot ),
      m_uid_count( 0 ),
      m_sXMLNS_PREFIX_UNKNOWN(
          RTL_CONSTASCII_USTRINGPARAM("<<< unknown prefix >>>") ),
      m_sXMLNS( RTL_CONSTASCII_USTRINGPARAM("xmlns") ),
      m_nLastURI_lookup( UID_UNKNOWN ),
      m_aLastURI_lookup( RTL_CONSTASCII_USTRINGPARAM("<<< unknown URI >>>") ),
      m_nLastPrefix_lookup( UID_UNKNOWN ),
      m_aLastPrefix_lookup(
          RTL_CONSTASCII_USTRINGPARAM("<<< unknown URI >>>") ),
      m_nSkipElements( 0 ),
      m_pMutex( 0 )
{
    m_elements.reserve( 10 );
    
    if (! bSingleThreadedUse)
        m_pMutex = new Mutex();
}

//______________________________________________________________________________
DocumentHandlerImpl::~DocumentHandlerImpl() throw ()
{
    if (m_pMutex != 0)
    {
        delete m_pMutex;
#if OSL_DEBUG_LEVEL == 0
        m_pMutex = 0;
#endif
    }
}

//______________________________________________________________________________
inline Reference< xml::input::XElement >
DocumentHandlerImpl::getCurrentElement() const
{
    MGuard aGuard( m_pMutex );
    if (m_elements.empty())
        return Reference< xml::input::XElement >();
    else
        return m_elements.back()->m_xElement;
}

//______________________________________________________________________________
inline sal_Int32 DocumentHandlerImpl::getUidByURI( OUString const & rURI )
{
    MGuard guard( m_pMutex );
    if (m_nLastURI_lookup == UID_UNKNOWN || m_aLastURI_lookup != rURI)
    {
        t_OUString2LongMap::const_iterator iFind( m_URI2Uid.find( rURI ) );
        if (iFind != m_URI2Uid.end()) // id found
        {
            m_nLastURI_lookup = iFind->second;
            m_aLastURI_lookup = rURI;
        }
        else
        {
            m_nLastURI_lookup = m_uid_count;
            ++m_uid_count;
            m_URI2Uid[ rURI ] = m_nLastURI_lookup;
            m_aLastURI_lookup = rURI;
        }
    }
    return m_nLastURI_lookup;
}

//______________________________________________________________________________
inline sal_Int32 DocumentHandlerImpl::getUidByPrefix(
    OUString const & rPrefix )
{
    // commonly the last added prefix is used often for several tags...
    // good guess
    if (m_nLastPrefix_lookup == UID_UNKNOWN || m_aLastPrefix_lookup != rPrefix)
    {
        t_OUString2PrefixMap::const_iterator iFind(
            m_prefixes.find( rPrefix ) );
        if (iFind != m_prefixes.end())
        {
            const PrefixEntry & rPrefixEntry = *iFind->second;
            OSL_ASSERT( ! rPrefixEntry.m_Uids.empty() );
            m_nLastPrefix_lookup = rPrefixEntry.m_Uids.back();
            m_aLastPrefix_lookup = rPrefix;
        }
        else
        {
            m_nLastPrefix_lookup = UID_UNKNOWN;
            m_aLastPrefix_lookup = m_sXMLNS_PREFIX_UNKNOWN;
        }
    }
    return m_nLastPrefix_lookup;
}

//______________________________________________________________________________
inline void DocumentHandlerImpl::pushPrefix(
    OUString const & rPrefix, OUString const & rURI )
{
    // lookup id for URI
    sal_Int32 nUid = getUidByURI( rURI );

    // mark prefix with id
    t_OUString2PrefixMap::const_iterator iFind( m_prefixes.find( rPrefix ) );
    if (iFind == m_prefixes.end()) // unused prefix
    {
        PrefixEntry * pEntry = new PrefixEntry();
        pEntry->m_Uids.push_back( nUid ); // latest id for prefix
        m_prefixes[ rPrefix ] = pEntry;
    }
    else
    {
        PrefixEntry * pEntry = iFind->second;
        OSL_ASSERT( ! pEntry->m_Uids.empty() );
        pEntry->m_Uids.push_back( nUid );
    }

    m_aLastPrefix_lookup = rPrefix;
    m_nLastPrefix_lookup = nUid;
}

//______________________________________________________________________________
inline void DocumentHandlerImpl::popPrefix(
    OUString const & rPrefix )
{
    t_OUString2PrefixMap::iterator iFind( m_prefixes.find( rPrefix ) );
    if (iFind != m_prefixes.end()) // unused prefix
    {
        PrefixEntry * pEntry = iFind->second;
        pEntry->m_Uids.pop_back(); // pop last id for prefix
        if (pEntry->m_Uids.empty()) // erase prefix key
        {
            m_prefixes.erase( iFind );
            delete pEntry;
        }
    }

    m_nLastPrefix_lookup = UID_UNKNOWN;
    m_aLastPrefix_lookup = m_sXMLNS_PREFIX_UNKNOWN;
}

//______________________________________________________________________________
inline void DocumentHandlerImpl::getElementName(
    OUString const & rQName, sal_Int32 * pUid, OUString * pLocalName )
{
    sal_Int32 nColonPos = rQName.indexOf( (sal_Unicode)':' );
    *pLocalName = (nColonPos >= 0 ? rQName.copy( nColonPos +1 ) : rQName);
    *pUid = getUidByPrefix(
        nColonPos >= 0 ? rQName.copy( 0, nColonPos ) : OUString() );
}


//==============================================================================
class ExtendedAttributes :
    public ::cppu::WeakImplHelper1< xml::input::XAttributes >
{
    sal_Int32 m_nAttributes;
    sal_Int32 * m_pUids;
    OUString * m_pPrefixes;
    OUString * m_pLocalNames;
    OUString * m_pQNames;
    OUString * m_pValues;

    DocumentHandlerImpl * m_pHandler;

public:
    inline ExtendedAttributes(
        sal_Int32 nAttributes,
        sal_Int32 * pUids, OUString * pPrefixes,
        OUString * pLocalNames, OUString * pQNames,
        Reference< xml::sax::XAttributeList > const & xAttributeList,
        DocumentHandlerImpl * pHandler );
    virtual ~ExtendedAttributes() throw ();
    
    // XAttributes
    virtual sal_Int32 SAL_CALL getLength()
        throw (RuntimeException);
    virtual sal_Int32 SAL_CALL getIndexByQName(
        OUString const & rQName )
        throw (RuntimeException);
    virtual sal_Int32 SAL_CALL getIndexByUidName(
        sal_Int32 nUid, OUString const & rLocalName )
        throw (RuntimeException);
    virtual OUString SAL_CALL getQNameByIndex(
        sal_Int32 nIndex )
        throw (RuntimeException);
    virtual sal_Int32 SAL_CALL getUidByIndex(
        sal_Int32 nIndex )
        throw (RuntimeException);
    virtual OUString SAL_CALL getLocalNameByIndex(
        sal_Int32 nIndex )
        throw (RuntimeException);
    virtual OUString SAL_CALL getValueByIndex(
        sal_Int32 nIndex )
        throw (RuntimeException);
    virtual OUString SAL_CALL getValueByUidName(
        sal_Int32 nUid, OUString const & rLocalName )
        throw (RuntimeException);
    virtual OUString SAL_CALL getTypeByIndex(
        sal_Int32 nIndex )
        throw (RuntimeException);
};

//______________________________________________________________________________
inline ExtendedAttributes::ExtendedAttributes(
    sal_Int32 nAttributes,
    sal_Int32 * pUids, OUString * pPrefixes,
    OUString * pLocalNames, OUString * pQNames,
    Reference< xml::sax::XAttributeList > const & xAttributeList,
    DocumentHandlerImpl * pHandler )
    : m_nAttributes( nAttributes )
    , m_pUids( pUids )
    , m_pPrefixes( pPrefixes )
    , m_pLocalNames( pLocalNames )
    , m_pQNames( pQNames )
    , m_pValues( new OUString[ nAttributes ] )
    , m_pHandler( pHandler )
{
    m_pHandler->acquire();

    for ( sal_Int16 nPos = 0; nPos < nAttributes; ++nPos )
    {
        m_pValues[ nPos ] = xAttributeList->getValueByIndex( nPos );
    }
}

//______________________________________________________________________________
ExtendedAttributes::~ExtendedAttributes() throw ()
{
    m_pHandler->release();

    delete [] m_pUids;
    delete [] m_pPrefixes;
    delete [] m_pLocalNames;
    delete [] m_pQNames;
    delete [] m_pValues;
}


//##############################################################################

// XServiceInfo

//______________________________________________________________________________
OUString DocumentHandlerImpl::getImplementationName()
    throw (RuntimeException)
{
    return getImplementationName_DocumentHandlerImpl();
}

//______________________________________________________________________________
sal_Bool DocumentHandlerImpl::supportsService(
    OUString const & servicename )
    throw (RuntimeException)
{
    Sequence< OUString > names( getSupportedServiceNames_DocumentHandlerImpl() );
    for ( sal_Int32 nPos = names.getLength(); nPos--; )
    {
        if (names[ nPos ].equals( servicename ))
            return sal_True;
    }
    return sal_False;
}

//______________________________________________________________________________
Sequence< OUString > DocumentHandlerImpl::getSupportedServiceNames()
    throw (RuntimeException)
{
    return getSupportedServiceNames_DocumentHandlerImpl();
}

// XInitialization

//______________________________________________________________________________
void DocumentHandlerImpl::initialize(
    Sequence< Any > const & arguments )
    throw (Exception)
{
    MGuard guard( m_pMutex );
    Reference< xml::input::XRoot > xRoot;
    if (arguments.getLength() == 1 &&
        (arguments[ 0 ] >>= xRoot) &&
        xRoot.is())
    {
        m_xRoot = xRoot;
    }
    else
    {
        throw RuntimeException(
            OUString( RTL_CONSTASCII_USTRINGPARAM(
                          "missing root instance!") ),
            Reference< XInterface >() );
    }
}


// XNamespaceMapping

//______________________________________________________________________________
sal_Int32 DocumentHandlerImpl::getUidByUri( OUString const & Uri )
    throw (RuntimeException)
{
    sal_Int32 uid = getUidByURI( Uri );
    OSL_ASSERT( uid != UID_UNKNOWN );
    return uid;
}

//______________________________________________________________________________
OUString DocumentHandlerImpl::getUriByUid( sal_Int32 Uid )
    throw (container::NoSuchElementException, RuntimeException)
{
    MGuard guard( m_pMutex );
    t_OUString2LongMap::const_iterator iPos( m_URI2Uid.begin() );
    t_OUString2LongMap::const_iterator const iEnd( m_URI2Uid.end() );
    for ( ; iPos != iEnd; ++iPos )
    {
        if (iPos->second == Uid)
            return iPos->first;
    }
    throw container::NoSuchElementException(
        OUString( RTL_CONSTASCII_USTRINGPARAM("no such xmlns uid!") ),
        static_cast< OWeakObject * >(this) );
}


// XDocumentHandler

//______________________________________________________________________________
void DocumentHandlerImpl::startDocument()
    throw (xml::sax::SAXException, RuntimeException)
{
    m_xRoot->startDocument(
        static_cast< xml::input::XNamespaceMapping * >( this ) );
}

//______________________________________________________________________________
void DocumentHandlerImpl::endDocument()
    throw (xml::sax::SAXException, RuntimeException)
{
    m_xRoot->endDocument();
}

//______________________________________________________________________________
void DocumentHandlerImpl::startElement(
    OUString const & rQElementName,
    Reference< xml::sax::XAttributeList > const & xAttribs )
    throw (xml::sax::SAXException, RuntimeException)
{
    Reference< xml::input::XElement > xCurrentElement;
    Reference< xml::input::XAttributes > xAttributes;
    sal_Int32 nUid;
    OUString aLocalName;
    ::std::auto_ptr< ElementEntry > elementEntry( new ElementEntry );
    
    { // guard start:
    MGuard aGuard( m_pMutex );
    // currently skipping elements and waiting for end tags?
    if (m_nSkipElements > 0)
    {
        ++m_nSkipElements; // wait for another end tag
#if OSL_DEBUG_LEVEL > 1
        OString aQName(
            OUStringToOString( rQElementName, RTL_TEXTENCODING_ASCII_US ) );
        OSL_TRACE( "### no context given on createChildElement() "
                   "=> ignoring element \"%s\" ...", aQName.getStr() );
#endif
        return;
    }
    
    sal_Int16 nAttribs = xAttribs->getLength();
    
    // save all namespace ids
    sal_Int32 * pUids = new sal_Int32[ nAttribs ];
    OUString * pPrefixes = new OUString[ nAttribs ];
    OUString * pLocalNames = new OUString[ nAttribs ];
    OUString * pQNames = new OUString[ nAttribs ];
    
    // first recognize all xmlns attributes
    sal_Int16 nPos;
    for ( nPos = 0; nPos < nAttribs; ++nPos )
    {
        // mark attribute to be collected further
        // on with attribute's uid and current prefix
        pUids[ nPos ] = 0; // modified

        pQNames[ nPos ] = xAttribs->getNameByIndex( nPos );
        OUString const & rQAttributeName = pQNames[ nPos ];

        if (rQAttributeName.compareTo( m_sXMLNS, 5 ) == 0)
        {
            if (rQAttributeName.getLength() == 5) // set default namespace
            {
                OUString aDefNamespacePrefix;
                pushPrefix(
                    aDefNamespacePrefix,
                    xAttribs->getValueByIndex( nPos ) );
                elementEntry->m_prefixes.push_back( aDefNamespacePrefix );
                pUids[ nPos ]          = UID_UNKNOWN;
                pPrefixes[ nPos ]      = m_sXMLNS;
                pLocalNames[ nPos ]    = aDefNamespacePrefix;
            }
            else if ((sal_Unicode)':' == rQAttributeName[ 5 ]) // set prefix
            {
                OUString aPrefix( rQAttributeName.copy( 6 ) );
                pushPrefix( aPrefix, xAttribs->getValueByIndex( nPos ) );
                elementEntry->m_prefixes.push_back( aPrefix );
                pUids[ nPos ]          = UID_UNKNOWN;
                pPrefixes[ nPos ]      = m_sXMLNS;
                pLocalNames[ nPos ]    = aPrefix;
            }
            // else just a name starting with xmlns, but no prefix
        }
    }

    // now read out attribute prefixes (all namespace prefixes have been set)
    for ( nPos = 0; nPos < nAttribs; ++nPos )
    {
        if (pUids[ nPos ] >= 0) // no xmlns: attribute
        {
            OUString const & rQAttributeName = pQNames[ nPos ];
            OSL_ENSURE(
                rQAttributeName.compareToAscii(
                    RTL_CONSTASCII_STRINGPARAM("xmlns:") ) != 0,
                "### unexpected xmlns!" );
            
            // collect attribute's uid and current prefix
            sal_Int32 nColonPos = rQAttributeName.indexOf( (sal_Unicode) ':' );
            if (nColonPos >= 0)
            {
                pPrefixes[ nPos ] = rQAttributeName.copy( 0, nColonPos );
                pLocalNames[ nPos ] = rQAttributeName.copy( nColonPos +1 );
            }
            else
            {
                pPrefixes[ nPos ] = OUString();
                pLocalNames[ nPos ] = rQAttributeName;
                // leave local names unmodified
            }
            pUids[ nPos ] = getUidByPrefix( pPrefixes[ nPos ] );
        }
    }
    // ownership of arrays belongs to attribute list
    xAttributes = static_cast< xml::input::XAttributes * >(
        new ExtendedAttributes(
            nAttribs, pUids, pPrefixes, pLocalNames, pQNames,
            xAttribs, this ) );
    
    getElementName( rQElementName, &nUid, &aLocalName );
    
    // create new child context and append to list
    if (! m_elements.empty())
        xCurrentElement = m_elements.back()->m_xElement;
    } // :guard end
    
    if (xCurrentElement.is())
    {
        elementEntry->m_xElement =
            xCurrentElement->startChildElement( nUid, aLocalName, xAttributes );
    }
    else
    {
        elementEntry->m_xElement =
            m_xRoot->startRootElement( nUid, aLocalName, xAttributes );
    }
    
    {
    MGuard aGuard( m_pMutex );
    if (elementEntry->m_xElement.is())
    {
        m_elements.push_back( elementEntry.release() );
    }
    else
    {
        ++m_nSkipElements;
#if OSL_DEBUG_LEVEL > 1
        OString aQName(
            OUStringToOString( rQElementName, RTL_TEXTENCODING_ASCII_US ) );
        OSL_TRACE(
            "### no context given on createChildElement() => "
            "ignoring element \"%s\" ...", aQName.getStr() );
#endif
    }
    }
}

//______________________________________________________________________________
void DocumentHandlerImpl::endElement(
    OUString const & rQElementName )
    throw (xml::sax::SAXException, RuntimeException)
{
    Reference< xml::input::XElement > xCurrentElement;
    {
    MGuard aGuard( m_pMutex );
    if (m_nSkipElements)
    {
        --m_nSkipElements;
#if OSL_DEBUG_LEVEL > 1
        OString aQName(
            OUStringToOString( rQElementName, RTL_TEXTENCODING_ASCII_US ) );
        OSL_TRACE( "### received endElement() for \"%s\".", aQName.getStr() );
#endif
        static_cast<void>(rQElementName);
        return;
    }

    // popping context
    OSL_ASSERT( ! m_elements.empty() );
    ElementEntry * pEntry = m_elements.back();
    xCurrentElement = pEntry->m_xElement;

#if OSL_DEBUG_LEVEL > 0
    sal_Int32 nUid;
    OUString aLocalName;
    getElementName( rQElementName, &nUid, &aLocalName );
    OSL_ASSERT( xCurrentElement->getLocalName() == aLocalName );
    OSL_ASSERT( xCurrentElement->getUid() == nUid );
#endif

    // pop prefixes
    for ( sal_Int32 nPos = pEntry->m_prefixes.size(); nPos--; )
    {
        popPrefix( pEntry->m_prefixes[ nPos ] );
    }
    m_elements.pop_back();
    delete pEntry;
    }
    xCurrentElement->endElement();
}

//______________________________________________________________________________
void DocumentHandlerImpl::characters( OUString const & rChars )
    throw (xml::sax::SAXException, RuntimeException)
{
    Reference< xml::input::XElement > xCurrentElement( getCurrentElement() );
    if (xCurrentElement.is())
        xCurrentElement->characters( rChars );
}

//______________________________________________________________________________
void DocumentHandlerImpl::ignorableWhitespace(
    OUString const & rWhitespaces )
    throw (xml::sax::SAXException, RuntimeException)
{
    Reference< xml::input::XElement > xCurrentElement( getCurrentElement() );
    if (xCurrentElement.is())
        xCurrentElement->ignorableWhitespace( rWhitespaces );
}

//______________________________________________________________________________
void DocumentHandlerImpl::processingInstruction(
    OUString const & rTarget, OUString const & rData )
    throw (xml::sax::SAXException, RuntimeException)
{
    Reference< xml::input::XElement > xCurrentElement( getCurrentElement() );
    if (xCurrentElement.is())
        xCurrentElement->processingInstruction( rTarget, rData );
    else
        m_xRoot->processingInstruction( rTarget, rData );
}

//______________________________________________________________________________
void DocumentHandlerImpl::setDocumentLocator(
    Reference< xml::sax::XLocator > const & xLocator )
    throw (xml::sax::SAXException, RuntimeException)
{
    m_xRoot->setDocumentLocator( xLocator );
}

//##############################################################################

// XAttributes

//______________________________________________________________________________
sal_Int32 ExtendedAttributes::getIndexByQName( OUString const & rQName )
    throw (RuntimeException)
{
    for ( sal_Int32 nPos = m_nAttributes; nPos--; )
    {
        if (m_pQNames[ nPos ].equals( rQName ))
        {
            return nPos;
        }
    }
    return -1;
}

//______________________________________________________________________________
sal_Int32 ExtendedAttributes::getLength()
    throw (RuntimeException)
{
    return m_nAttributes;
}

//______________________________________________________________________________
OUString ExtendedAttributes::getLocalNameByIndex( sal_Int32 nIndex )
    throw (RuntimeException)
{
    if (nIndex < m_nAttributes)
        return m_pLocalNames[ nIndex ];
    else
        return OUString();
}

//______________________________________________________________________________
OUString ExtendedAttributes::getQNameByIndex( sal_Int32 nIndex )
    throw (RuntimeException)
{
    if (nIndex < m_nAttributes)
        return m_pQNames[ nIndex ];
    else
        return OUString();
}

//______________________________________________________________________________
OUString ExtendedAttributes::getTypeByIndex( sal_Int32 nIndex )
    throw (RuntimeException)
{
    static_cast<void>(nIndex);
    OSL_ASSERT( nIndex < m_nAttributes );
    return OUString(); // unsupported
}

//______________________________________________________________________________
OUString ExtendedAttributes::getValueByIndex( sal_Int32 nIndex )
    throw (RuntimeException)
{
    if (nIndex < m_nAttributes)
        return m_pValues[ nIndex ];
    else
        return OUString();
}

//______________________________________________________________________________
sal_Int32 ExtendedAttributes::getIndexByUidName(
    sal_Int32 nUid, OUString const & rLocalName )
    throw (RuntimeException)
{
    for ( sal_Int32 nPos = m_nAttributes; nPos--; )
    {
        if (m_pUids[ nPos ] == nUid && m_pLocalNames[ nPos ] == rLocalName)
        {
            return nPos;
        }
    }
    return -1;
}

//______________________________________________________________________________
sal_Int32 ExtendedAttributes::getUidByIndex( sal_Int32 nIndex )
    throw (RuntimeException)
{
    if (nIndex < m_nAttributes)
        return m_pUids[ nIndex ];
    else
        return -1;
}

//______________________________________________________________________________
OUString ExtendedAttributes::getValueByUidName(
    sal_Int32 nUid, OUString const & rLocalName )
    throw (RuntimeException)
{
    for ( sal_Int32 nPos = m_nAttributes; nPos--; )
    {
        if (m_pUids[ nPos ] == nUid && m_pLocalNames[ nPos ] == rLocalName)
        {
            return m_pValues[ nPos ];
        }
    }
    return OUString();
}


//##############################################################################


//==============================================================================
Reference< xml::sax::XDocumentHandler > SAL_CALL createDocumentHandler(
    Reference< xml::input::XRoot > const & xRoot,
    bool bSingleThreadedUse )
    SAL_THROW( () )
{
    OSL_ASSERT( xRoot.is() );
    if (xRoot.is())
    {
        return static_cast< xml::sax::XDocumentHandler * >(
            new DocumentHandlerImpl( xRoot, bSingleThreadedUse ) );
    }
    return Reference< xml::sax::XDocumentHandler >();
}

//------------------------------------------------------------------------------
Reference< XInterface > SAL_CALL create_DocumentHandlerImpl(
    Reference< XComponentContext > const & )
    SAL_THROW( (Exception) )
{
    return static_cast< ::cppu::OWeakObject * >(
        new DocumentHandlerImpl(
            Reference< xml::input::XRoot >(), false /* mt use */ ) );
}

}
