/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */

#include <config_wasm_strip.h>

#include <memory>
#include <optional>

#include <comphelper/diagnose_ex.hxx>
#include <sal/log.hxx>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <tools/urlobj.hxx>
#include <utility>
#include <vcl/embeddedfontshelper.hxx>
#include <vcl/graph.hxx>
#include <xmloff/unointerfacetouniqueidentifiermapper.hxx>
#include <xmloff/namespacemap.hxx>
#include <xmloff/xmluconv.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmltoken.hxx>
#include <xmloff/XMLFontStylesContext.hxx>
#include <xmloff/xmlictxt.hxx>
#include <xmloff/xmlimp.hxx>
#include <xmloff/xmlnumfi.hxx>
#include <XMLEventImportHelper.hxx>
#include <XMLStarBasicContextFactory.hxx>
#include <XMLScriptContextFactory.hxx>
#include <StyleMap.hxx>
#include <xmloff/ProgressBarHelper.hxx>
#include <xmloff/xmlerror.hxx>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/util/MeasureUnit.hpp>
#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/document/XBinaryStreamResolver.hpp>
#include <com/sun/star/document/XStorageBasedDocument.hpp>
#include <com/sun/star/document/XGraphicStorageHandler.hpp>
#include <com/sun/star/document/XEmbeddedObjectResolver.hpp>
#include <com/sun/star/xml/sax/XLocator.hpp>
#include <com/sun/star/xml/sax/FastParser.hpp>
#include <com/sun/star/xml/sax/SAXException.hpp>
#include <com/sun/star/packages/zip/ZipIOException.hpp>
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <comphelper/fileformat.h>
#include <comphelper/namecontainer.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <comphelper/extract.hxx>
#include <comphelper/documentconstants.hxx>
#include <comphelper/documentinfo.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/attributelist.hxx>
#include <unotools/fontcvt.hxx>
#include <fasttokenhandler.hxx>
#include <vcl/GraphicExternalLink.hxx>
#include <o3tl/string_view.hxx>

#include <com/sun/star/rdf/XMetadatable.hpp>
#include <com/sun/star/rdf/XRepositorySupplier.hpp>
#include <RDFaImportHelper.hxx>

using ::com::sun::star::beans::XPropertySetInfo;

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::document;
using namespace ::xmloff::token;

rtl::Reference<FastTokenHandler> SvXMLImport::xTokenHandler(new FastTokenHandler);
std::unordered_map< sal_Int32, std::pair< OUString, OUString > > SvXMLImport::aNamespaceMap;
std::unordered_map< OUString, OUString > SvXMLImport::aNamespaceURIPrefixMap;
bool SvXMLImport::bIsNSMapsInitialized = false;

namespace {

class SvXMLImportEventListener : public cppu::WeakImplHelper< css::lang::XEventListener >
{
private:
    SvXMLImport*    pImport;

public:
    explicit SvXMLImportEventListener(SvXMLImport* pImport);

                            // XEventListener
    virtual void SAL_CALL disposing(const lang::EventObject& rEventObject) override;
};

}

SvXMLImportEventListener::SvXMLImportEventListener(SvXMLImport* pTempImport)
    : pImport(pTempImport)
{
}

// XEventListener
void SAL_CALL SvXMLImportEventListener::disposing( const lang::EventObject& )
{
    if (pImport)
    {
        pImport->DisposingModel();
        pImport = nullptr;
    }
}

namespace
{

OUString
getBuildIdsProperty(uno::Reference<beans::XPropertySet> const& xImportInfo)
{
    if (xImportInfo.is())
    {
        try
        {
            Reference< XPropertySetInfo > const xSetInfo(
                    xImportInfo->getPropertySetInfo());
            if (xSetInfo.is() && xSetInfo->hasPropertyByName(u"BuildId"_ustr))
            {
                OUString aBuildId;
                xImportInfo->getPropertyValue(u"BuildId"_ustr) >>= aBuildId;
                return aBuildId;
            }
        }
        catch (Exception const&)
        {
            DBG_UNHANDLED_EXCEPTION("xmloff.core", "exception getting BuildId");
        }
    }
    return OUString();
}

class DocumentInfo
{
private:
    sal_uInt16 mnGeneratorVersion;

public:
    explicit DocumentInfo( const SvXMLImport& rImport )
        : mnGeneratorVersion( SvXMLImport::ProductVersionUnknown )
    {
        OUString const buildIds(
                getBuildIdsProperty(rImport.getImportInfo()));
        if (!buildIds.isEmpty())
        {
            sal_Int32 const ix = buildIds.indexOf(';');
            if (-1 != ix)
            {
                OUString const loVersion(buildIds.copy(ix + 1));
                if (!loVersion.isEmpty())
                {
                    auto const firstDot(loVersion.indexOf('.'));
                    if (firstDot == 1)
                    {   // old version scheme 3.3 ... 7.6
                        if ('3' == loVersion[0])
                        {
                            mnGeneratorVersion = SvXMLImport::LO_3x;
                        }
                        else if ('4' == loVersion[0])
                        {
                            if (loVersion.getLength() > 2
                                && (loVersion[2] == '0' || loVersion[2] == '1'))
                            {
                                mnGeneratorVersion = SvXMLImport::LO_41x; // 4.0/4.1
                            }
                            else if (loVersion.getLength() > 2 && '2' == loVersion[2])
                            {
                                mnGeneratorVersion = SvXMLImport::LO_42x; // 4.2
                            }
                            else if (loVersion.getLength() > 2 && '3' == loVersion[2])
                            {
                                mnGeneratorVersion = SvXMLImport::LO_43x; // 4.3
                            }
                            else if (loVersion.getLength() > 2 && '4' == loVersion[2])
                            {
                                mnGeneratorVersion = SvXMLImport::LO_44x; // 4.4
                            }
                        }
                        else if ('5' == loVersion[0])
                        {
                            mnGeneratorVersion = SvXMLImport::LO_5x;
                        }
                        else if ('6' == loVersion[0])
                        {
                            if (loVersion.getLength() > 2
                                && (loVersion[2] == '0' || loVersion[2] == '1'
                                    || loVersion[2] == '2'))
                            {
                                mnGeneratorVersion = SvXMLImport::LO_6x; // 6.0/6.1/6.2
                            }
                            else
                            {
                                mnGeneratorVersion = SvXMLImport::LO_63x; // 6.3/6.4
                            }
                        }
                        else if ('7' == loVersion[0])
                        {
                            if (loVersion.getLength() > 2 && loVersion[2] == '6')
                            {
                                mnGeneratorVersion = SvXMLImport::LO_76; // 7.6
                            }
                            else
                            {
                                mnGeneratorVersion = SvXMLImport::LO_7x;
                            }
                        }
                        else
                        {
                            SAL_INFO("xmloff.core", "unknown LO version: " << loVersion);
                        }
                    }
                    else if (1 < firstDot) // new version scheme 24.2 ...
                    {
                        OUString const nMajor(loVersion.subView(0, firstDot));
                        auto const year(nMajor.toInt32());
                        auto const month(o3tl::toInt32(loVersion.subView(firstDot+1)));
                        if (24 == year && month == 2)
                        {
                            mnGeneratorVersion = SvXMLImport::LO_242;
                        }
                        else if (24 == year && month == 8)
                        {
                            mnGeneratorVersion = SvXMLImport::LO_248;
                        }
                        else if (0 < year)
                        {
                            mnGeneratorVersion = SvXMLImport::LO_New;
                        }
                        else
                        {
                            SAL_INFO("xmloff.core", "unknown LO version: " << loVersion);
                        }
                    }
                    else
                    {
                        SAL_INFO("xmloff.core", "unknown LO version: " << loVersion);
                    }
                    return; // ignore buildIds
                }
            }
        }
        sal_Int32 nUPD, nBuild;
        if ( !rImport.getBuildIds( nUPD, nBuild ) )
            return;

        if ( nUPD >= 640 && nUPD <= 645 )
        {
            mnGeneratorVersion = SvXMLImport::OOo_1x;
        }
        else if ( nUPD == 680 )
        {
            mnGeneratorVersion = SvXMLImport::OOo_2x;
        }
        else if ( nUPD == 300 && nBuild <= 9379 )
        {
            mnGeneratorVersion = SvXMLImport::OOo_30x;
        }
        else if ( nUPD == 310 )
        {
            mnGeneratorVersion = SvXMLImport::OOo_31x;
        }
        else if ( nUPD == 320 )
        {
            mnGeneratorVersion = SvXMLImport::OOo_32x;
        }
        else if ( nUPD == 330 )
        {
            mnGeneratorVersion = SvXMLImport::OOo_33x;
        }
        else if ( nUPD == 340 )
        {
            mnGeneratorVersion = SvXMLImport::OOo_34x;
        }
        else if (nUPD == 400 || nUPD == 401)
        {
            mnGeneratorVersion = SvXMLImport::AOO_40x;
        }
        else if (nUPD >= 410)
        {
            // effectively this means "latest", see use
            // in XMLGraphicsDefaultStyle::SetDefaults()!
            mnGeneratorVersion = SvXMLImport::AOO_4x;
        }
    }

    sal_uInt16 getGeneratorVersion() const
    {
        return mnGeneratorVersion;
    }
};

}

class SvXMLImport_Impl
{
public:
    FontToSubsFontConverter hBatsFontConv;
    FontToSubsFontConverter hMathFontConv;

    bool mbOwnGraphicResolver;
    bool mbOwnEmbeddedResolver;
    INetURLObject aBaseURL;
    INetURLObject aDocBase;

    /// name of stream in package, e.g., "content.xml"
    OUString mStreamName;

    std::optional<OUString> mxODFVersion;

    bool mbIsOOoXML;

    std::optional<bool> mbIsMSO;

    // Boolean, indicating that position attributes
    // of shapes are given in horizontal left-to-right layout. This is the case
    // for the OpenOffice.org file format. (#i28749#)
    bool mbShapePositionInHoriL2R;
    bool mbTextDocInOOoFileFormat;

    const uno::Reference< uno::XComponentContext > mxComponentContext;
    OUString implementationName;
    css::uno::Sequence< OUString > maSupportedServiceNames;

    uno::Reference< embed::XStorage > mxSourceStorage;

    std::unique_ptr< xmloff::RDFaImportHelper > mpRDFaHelper;

    std::optional< DocumentInfo > moDocumentInfo;

    SvXMLImport_Impl( uno::Reference< uno::XComponentContext > xContext,
                      OUString theImplementationName,
                      const css::uno::Sequence< OUString > & sSupportedServiceNames = {})
        : hBatsFontConv( nullptr )
        , hMathFontConv( nullptr )
        , mbOwnGraphicResolver( false )
        , mbOwnEmbeddedResolver( false )
        , mbIsOOoXML(false)
        // Convert drawing object positions from OOo file format to OASIS (#i28749#)
        , mbShapePositionInHoriL2R( false )
        , mbTextDocInOOoFileFormat( false )
        , mxComponentContext(std::move( xContext ))
        , implementationName(std::move(theImplementationName))
        , maSupportedServiceNames(sSupportedServiceNames)
    {
        SAL_WARN_IF(!mxComponentContext.is(), "xmloff.core", "SvXMLImport: no ComponentContext");
        if (!mxComponentContext.is()) throw uno::RuntimeException();
        if (!maSupportedServiceNames.hasElements())
            maSupportedServiceNames = { u"com.sun.star.document.ImportFilter"_ustr, u"com.sun.star.xml.XMLImportFilter"_ustr };
    }

    sal_uInt16 getGeneratorVersion( const SvXMLImport& rImport )
    {
        if (!moDocumentInfo)
        {
            moDocumentInfo.emplace( rImport );
        }

        return moDocumentInfo->getGeneratorVersion();
    }

    ::comphelper::UnoInterfaceToUniqueIdentifierMapper maInterfaceToIdentifierMapper;
};

SvXMLImportContext *SvXMLImport::CreateFastContext( sal_Int32 nElement,
        const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
{
    assert(false);
    SAL_WARN( "xmloff.core", "CreateFastContext should be overridden, for element " << nElement);
    return new SvXMLImportContext( *this );
}

void SvXMLImport::InitCtor_()
{
    if( mnImportFlags != SvXMLImportFlags::NONE )
    {
        // implicit "xml" namespace prefix
        mxNamespaceMap->Add( GetXMLToken(XML_XML), GetXMLToken(XML_N_XML), XML_NAMESPACE_XML );
        mxNamespaceMap->Add( u"_office"_ustr, GetXMLToken(XML_N_OFFICE), XML_NAMESPACE_OFFICE );
        mxNamespaceMap->Add( u"_office_ooo"_ustr, GetXMLToken(XML_N_OFFICE_EXT), XML_NAMESPACE_OFFICE_EXT );
        mxNamespaceMap->Add( u"_ooo"_ustr, GetXMLToken(XML_N_OOO), XML_NAMESPACE_OOO );
        mxNamespaceMap->Add( u"_style"_ustr, GetXMLToken(XML_N_STYLE), XML_NAMESPACE_STYLE );
        mxNamespaceMap->Add( u"_text"_ustr, GetXMLToken(XML_N_TEXT), XML_NAMESPACE_TEXT );
        mxNamespaceMap->Add( u"_table"_ustr, GetXMLToken(XML_N_TABLE), XML_NAMESPACE_TABLE );
        mxNamespaceMap->Add( u"_table_ooo"_ustr, GetXMLToken(XML_N_TABLE_EXT), XML_NAMESPACE_TABLE_EXT );
        mxNamespaceMap->Add( u"_draw"_ustr, GetXMLToken(XML_N_DRAW), XML_NAMESPACE_DRAW );
        mxNamespaceMap->Add( u"_draw_ooo"_ustr, GetXMLToken(XML_N_DRAW_EXT), XML_NAMESPACE_DRAW_EXT );
        mxNamespaceMap->Add( u"_dr3d"_ustr, GetXMLToken(XML_N_DR3D), XML_NAMESPACE_DR3D );
        mxNamespaceMap->Add( u"_fo"_ustr, GetXMLToken(XML_N_FO_COMPAT), XML_NAMESPACE_FO );
        mxNamespaceMap->Add( u"_xlink"_ustr, GetXMLToken(XML_N_XLINK), XML_NAMESPACE_XLINK );
        mxNamespaceMap->Add( u"_dc"_ustr, GetXMLToken(XML_N_DC), XML_NAMESPACE_DC );
        mxNamespaceMap->Add( u"_dom"_ustr, GetXMLToken(XML_N_DOM), XML_NAMESPACE_DOM );
        mxNamespaceMap->Add( u"_meta"_ustr, GetXMLToken(XML_N_META), XML_NAMESPACE_META );
        mxNamespaceMap->Add( u"_number"_ustr, GetXMLToken(XML_N_NUMBER), XML_NAMESPACE_NUMBER );
        mxNamespaceMap->Add( u"_svg"_ustr, GetXMLToken(XML_N_SVG_COMPAT), XML_NAMESPACE_SVG );
        mxNamespaceMap->Add( u"_chart"_ustr, GetXMLToken(XML_N_CHART), XML_NAMESPACE_CHART );
        mxNamespaceMap->Add( u"_math"_ustr, GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH );
        mxNamespaceMap->Add( u"_form"_ustr, GetXMLToken(XML_N_FORM), XML_NAMESPACE_FORM );
        mxNamespaceMap->Add( u"_script"_ustr, GetXMLToken(XML_N_SCRIPT), XML_NAMESPACE_SCRIPT );
        mxNamespaceMap->Add( u"_config"_ustr, GetXMLToken(XML_N_CONFIG), XML_NAMESPACE_CONFIG );
        mxNamespaceMap->Add( u"_xforms"_ustr, GetXMLToken(XML_N_XFORMS_1_0), XML_NAMESPACE_XFORMS );
        mxNamespaceMap->Add( u"_formx"_ustr, GetXMLToken( XML_N_FORMX ), XML_NAMESPACE_FORMX );
        mxNamespaceMap->Add( u"_xsd"_ustr, GetXMLToken(XML_N_XSD), XML_NAMESPACE_XSD );
        mxNamespaceMap->Add( u"_xsi"_ustr, GetXMLToken(XML_N_XSI), XML_NAMESPACE_XFORMS );
        mxNamespaceMap->Add( u"_ooow"_ustr, GetXMLToken(XML_N_OOOW), XML_NAMESPACE_OOOW );
        mxNamespaceMap->Add( u"_oooc"_ustr, GetXMLToken(XML_N_OOOC), XML_NAMESPACE_OOOC );
        mxNamespaceMap->Add( u"_field"_ustr, GetXMLToken(XML_N_FIELD), XML_NAMESPACE_FIELD );
        mxNamespaceMap->Add( u"_of"_ustr, GetXMLToken(XML_N_OF), XML_NAMESPACE_OF );
        mxNamespaceMap->Add( u"_xhtml"_ustr, GetXMLToken(XML_N_XHTML), XML_NAMESPACE_XHTML );
        mxNamespaceMap->Add( u"_css3text"_ustr, GetXMLToken(XML_N_CSS3TEXT), XML_NAMESPACE_CSS3TEXT );

        mxNamespaceMap->Add( u"_calc_libo"_ustr, GetXMLToken(XML_N_CALC_EXT), XML_NAMESPACE_CALC_EXT);
        mxNamespaceMap->Add( u"_office_libo"_ustr,
                             GetXMLToken(XML_N_LO_EXT), XML_NAMESPACE_LO_EXT);
    }

    if (mxNumberFormatsSupplier.is())
        mpNumImport = std::make_unique<SvXMLNumFmtHelper>(mxNumberFormatsSupplier);

    if (mxModel.is() && !mxEventListener.is())
    {
        mxEventListener.set(new SvXMLImportEventListener(this));
        mxModel->addEventListener(mxEventListener);
    }
}

SvXMLImport::SvXMLImport(
    const css::uno::Reference< css::uno::XComponentContext >& xContext,
    OUString const & implementationName,
    SvXMLImportFlags nImportFlags,
    const css::uno::Sequence< OUString > & sSupportedServiceNames )
:   mpImpl( new SvXMLImport_Impl(xContext, implementationName, sSupportedServiceNames) ),
    mxNamespaceMap( SvXMLNamespaceMap() ),

    mpUnitConv( new SvXMLUnitConverter( xContext,
                util::MeasureUnit::MM_100TH, util::MeasureUnit::MM_100TH,
                SvtSaveOptions::ODFSVER_LATEST_EXTENDED) ),

    mnImportFlags( nImportFlags ),
    maNamespaceHandler( new SvXMLImportFastNamespaceHandler() ),
    mbIsFormsSupported( true ),
    mbIsTableShapeSupported( false ),
    mbNotifyMacroEventRead( false )
{
    SAL_WARN_IF( !xContext.is(), "xmloff.core", "got no service manager" );
    InitCtor_();
    mxParser = xml::sax::FastParser::create( xContext );
    setNamespaceHandler( maNamespaceHandler );
    setTokenHandler( xTokenHandler  );
    if ( !bIsNSMapsInitialized )
    {
        initializeNamespaceMaps();
        bIsNSMapsInitialized = true;
    }
    registerNamespaces();
    maNamespaceAttrList = new comphelper::AttributeList;
}

void SvXMLImport::cleanup() noexcept
{
    if (mxEventListener.is() && mxModel.is())
        mxModel->removeEventListener(mxEventListener);
    // clear context stacks first in case of parse error because the context
    // class dtors are full of application logic
    while (!maContexts.empty())
    {
        if (SvXMLStylesContext* pStylesContext = dynamic_cast<SvXMLStylesContext*>(maContexts.top().get()))
            pStylesContext->dispose();
        maContexts.pop();
    }
    if( mxTextImport )
        mxTextImport->dispose();
    mxTextImport.clear(); // XMLRedlineImportHelper needs model
    DisposingModel();
}

SvXMLImport::~SvXMLImport() noexcept
{
    cleanup();
}

bool SvXMLImport::addEmbeddedFont(const css::uno::Reference< css::io::XInputStream >& stream,
                                  const OUString& fontName, std::u16string_view extra,
                                  std::vector<unsigned char> const & key, bool eot)
{
    if (!mxEmbeddedFontHelper)
        mxEmbeddedFontHelper.reset(new EmbeddedFontsHelper);
    return mxEmbeddedFontHelper->addEmbeddedFont(stream, fontName, extra, key, eot);
}

namespace
{
    class setFastDocumentHandlerGuard
    {
    private:
        css::uno::Reference<css::xml::sax::XFastParser> mxParser;
    public:
        setFastDocumentHandlerGuard(css::uno::Reference<css::xml::sax::XFastParser> Parser,
                                    const css::uno::Reference<css::xml::sax::XFastDocumentHandler>& Handler)
            : mxParser(std::move(Parser))
        {
            mxParser->setFastDocumentHandler(Handler);
        }
        //guarantee restoration of null document handler
        ~setFastDocumentHandlerGuard()
        {
            mxParser->setFastDocumentHandler(nullptr);
        }
    };
}

// XFastParser
void SAL_CALL SvXMLImport::parseStream( const xml::sax::InputSource& aInputSource )
{
    setFastDocumentHandlerGuard aDocumentHandlerGuard(mxParser, mxFastDocumentHandler.is() ? mxFastDocumentHandler : this);
    mxParser->parseStream(aInputSource);
}

void SAL_CALL SvXMLImport::setFastDocumentHandler( const uno::Reference< xml::sax::XFastDocumentHandler >& Handler )
{
    mxFastDocumentHandler = Handler;
}

void SAL_CALL SvXMLImport::setTokenHandler( const uno::Reference< xml::sax::XFastTokenHandler >& Handler )
{
    mxParser->setTokenHandler( Handler );
}

void SAL_CALL SvXMLImport::registerNamespace( const OUString& NamespaceURL, sal_Int32 NamespaceToken )
{
    mxParser->registerNamespace( NamespaceURL, NamespaceToken );
}

OUString SAL_CALL SvXMLImport::getNamespaceURL( const OUString& rPrefix )
{
    return mxParser->getNamespaceURL( rPrefix );
}

void SAL_CALL SvXMLImport::setErrorHandler( const uno::Reference< xml::sax::XErrorHandler >& Handler )
{
    mxParser->setErrorHandler( Handler );
}

void SAL_CALL SvXMLImport::setEntityResolver( const uno::Reference< xml::sax::XEntityResolver >& Resolver )
{
    mxParser->setEntityResolver( Resolver );
}

void SAL_CALL SvXMLImport::setLocale( const lang::Locale& rLocale )
{
    mxParser->setLocale( rLocale );
}

void SAL_CALL SvXMLImport::setNamespaceHandler( const uno::Reference< xml::sax::XFastNamespaceHandler >& Handler)
{
    mxParser->setNamespaceHandler( Handler );
}

void SAL_CALL SvXMLImport::setCustomEntityNames( const ::css::uno::Sequence< ::css::beans::Pair<::rtl::OUString, ::rtl::OUString> >& replacements )
{
    mxParser->setCustomEntityNames( replacements );
}

void SAL_CALL SvXMLImport::startDocument()
{
    SAL_INFO( "xmloff.core", "{ SvXMLImport::startDocument" );
    if (mxGraphicStorageHandler.is() && mxEmbeddedResolver.is())
        return;

    Reference< lang::XMultiServiceFactory > xFactory( mxModel,  UNO_QUERY );
    if( !xFactory.is() )
        return;

    try
    {
        if (!mxGraphicStorageHandler.is())
        {
            // #99870# Import... instead of Export...
            mxGraphicStorageHandler.set(
                GetComponentContext()->getServiceManager()->createInstanceWithArgumentsAndContext(
                u"com.sun.star.comp.Svx.GraphicImportHelper"_ustr, uno::Sequence<uno::Any>(), GetComponentContext()),
                uno::UNO_QUERY );
            mpImpl->mbOwnGraphicResolver = mxGraphicStorageHandler.is();
        }

        if( !mxEmbeddedResolver.is() )
        {
            // #99870# Import... instead of Export...
            mxEmbeddedResolver.set(
                xFactory->createInstance(u"com.sun.star.document.ImportEmbeddedObjectResolver"_ustr),
                UNO_QUERY);
            mpImpl->mbOwnEmbeddedResolver = mxEmbeddedResolver.is();
        }
    }
    catch( css::uno::Exception& )
    {
    }
}

void SAL_CALL SvXMLImport::endDocument()
{
    SAL_INFO( "xmloff.core", "} SvXMLImport::endDocument" );
    //  #i9518# All the stuff that accesses the document has to be done here, not in the dtor,
    //  because the SvXMLImport dtor might not be called until after the document has been closed.

    if (mxTextImport)
        mxTextImport->MapCrossRefHeadingFieldsHorribly();

    if (mpImpl->mpRDFaHelper)
    {
        const uno::Reference<rdf::XRepositorySupplier> xRS(mxModel,
            uno::UNO_QUERY);
        if (xRS.is())
        {
            mpImpl->mpRDFaHelper->InsertRDFa( xRS );
        }
    }

    mpNumImport.reset();
    if (mxImportInfo.is())
    {
        uno::Reference< beans::XPropertySetInfo > xPropertySetInfo = mxImportInfo->getPropertySetInfo();
        if (xPropertySetInfo.is())
        {
            if (bool(mpProgressBarHelper))
            {
                OUString sProgressMax(XML_PROGRESSMAX);
                OUString sProgressCurrent(XML_PROGRESSCURRENT);
                OUString sRepeat(XML_PROGRESSREPEAT);
                if (xPropertySetInfo->hasPropertyByName(sProgressMax) &&
                    xPropertySetInfo->hasPropertyByName(sProgressCurrent))
                {
                    sal_Int32 nProgressMax(mpProgressBarHelper->GetReference());
                    sal_Int32 nProgressCurrent(mpProgressBarHelper->GetValue());
                    mxImportInfo->setPropertyValue(sProgressMax, uno::Any(nProgressMax));
                    mxImportInfo->setPropertyValue(sProgressCurrent, uno::Any(nProgressCurrent));
                }
                if (xPropertySetInfo->hasPropertyByName(sRepeat))
                    mxImportInfo->setPropertyValue(sRepeat, css::uno::Any(mpProgressBarHelper->GetRepeat()));
                // pProgressBarHelper is deleted in dtor
            }
            OUString sNumberStyles(XML_NUMBERSTYLES);
            if (mxNumberStyles.is() && xPropertySetInfo->hasPropertyByName(sNumberStyles))
            {
                mxImportInfo->setPropertyValue(sNumberStyles, Any(mxNumberStyles));
            }
        }
    }

    if( mxFontDecls.is() )
        mxFontDecls->dispose();
    if( mxStyles.is() )
        mxStyles->dispose();
    if( mxAutoStyles.is() )
        mxAutoStyles->dispose();
    if( mxMasterStyles.is() )
        mxMasterStyles->dispose();

    // possible form-layer related knittings which can only be done when
    // the whole document exists
    if ( mxFormImport.is() )
        mxFormImport->documentDone();

    //  The shape import helper does the z-order sorting in the dtor,
    //  so it must be deleted here, too.
    mxShapeImport = nullptr;

    if( mpImpl->mbOwnGraphicResolver )
    {
        Reference<lang::XComponent> xComp(mxGraphicStorageHandler, UNO_QUERY);
        xComp->dispose();
    }

    if( mpImpl->mbOwnEmbeddedResolver )
    {
        Reference< lang::XComponent > xComp( mxEmbeddedResolver, UNO_QUERY );
        xComp->dispose();
    }
    mpStyleMap.clear();

    if ( bool( mpXMLErrors ) )
    {
        mpXMLErrors->ThrowErrorAsSAXException( XMLERROR_FLAG_SEVERE );
    }
}

std::optional<SvXMLNamespaceMap> SvXMLImport::processNSAttributes(
        std::optional<SvXMLNamespaceMap> & rpNamespaceMap,
        SvXMLImport *const pImport, // TODO???
        const uno::Reference< xml::sax::XAttributeList >& xAttrList)
{
    std::optional<SvXMLNamespaceMap> pRewindMap;
    sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
    for( sal_Int16 i=0; i < nAttrCount; i++ )
    {
        const OUString aAttrName = xAttrList->getNameByIndex( i );
        if (pImport && aAttrName == "office:version" && !pImport->mpImpl->mxODFVersion)
        {
            pImport->mpImpl->mxODFVersion = xAttrList->getValueByIndex( i );

            // the ODF version in content.xml and manifest.xml must be the same starting from ODF1.2
            if (pImport->mpImpl->mStreamName == "content.xml"
                && !pImport->IsODFVersionConsistent(*pImport->mpImpl->mxODFVersion))
            {
                throw xml::sax::SAXException(u"Inconsistent ODF versions in content.xml and manifest.xml!"_ustr,
                        uno::Reference< uno::XInterface >(),
                        uno::Any(
                            packages::zip::ZipIOException(u"Inconsistent ODF versions in content.xml and manifest.xml!"_ustr ) ) );
            }
        }
        else if( ( aAttrName.getLength() >= 5 ) &&
            ( aAttrName.startsWith( GetXMLToken(XML_XMLNS) ) ) &&
            ( aAttrName.getLength() == 5 || ':' == aAttrName[5] ) )
        {
            if( !pRewindMap )
            {
                pRewindMap = std::move(rpNamespaceMap);
                rpNamespaceMap.emplace(*pRewindMap);
            }
            const OUString aAttrValue = xAttrList->getValueByIndex( i );

            OUString aPrefix( ( aAttrName.getLength() == 5 )
                                 ? OUString()
                                 : aAttrName.copy( 6 ) );
            // Add namespace, but only if it is known.
            sal_uInt16 nKey = rpNamespaceMap->AddIfKnown( aPrefix, aAttrValue );
            // If namespace is unknown, try to match a name with similar
            // TC Id and version
            if( XML_NAMESPACE_UNKNOWN == nKey  )
            {
                OUString aTestName( aAttrValue );
                if( SvXMLNamespaceMap::NormalizeURI( aTestName ) )
                    nKey = rpNamespaceMap->AddIfKnown( aPrefix, aTestName );
            }
            // If that namespace is not known, too, add it as unknown
            if( XML_NAMESPACE_UNKNOWN == nKey  )
                rpNamespaceMap->Add( aPrefix, aAttrValue );

        }
    }
    return pRewindMap;
}


void SAL_CALL SvXMLImport::characters( const OUString& rChars )
{
    maContexts.top()->characters( rChars );
}

void SAL_CALL SvXMLImport::processingInstruction( const OUString&,
                                       const OUString& )
{
}

void SAL_CALL SvXMLImport::setDocumentLocator( const uno::Reference< xml::sax::XLocator >& rLocator )
{
    mxLocator = rLocator;
}

// XFastContextHandler
void SAL_CALL SvXMLImport::startFastElement (sal_Int32 Element,
    const uno::Reference< xml::sax::XFastAttributeList > & Attribs)
{
    SAL_INFO("xmloff.core", "startFastElement " << SvXMLImport::getNameFromToken( Element ));
    if ( Attribs.is() && !mpImpl->mxODFVersion)
    {
        sax_fastparser::FastAttributeList& rAttribList =
            sax_fastparser::castToFastAttributeList( Attribs );
        auto aIter( rAttribList.find( XML_ELEMENT( OFFICE, XML_VERSION ) ) );
        if( aIter != rAttribList.end() )
        {
            mpImpl->mxODFVersion = aIter.toString();

            // the ODF version in content.xml and manifest.xml must be the same starting from ODF1.2
            if ( mpImpl->mStreamName == "content.xml" && !IsODFVersionConsistent( *mpImpl->mxODFVersion ) )
            {
                throw xml::sax::SAXException(u"Inconsistent ODF versions in content.xml and manifest.xml!"_ustr,
                        uno::Reference< uno::XInterface >(),
                        uno::Any(
                            packages::zip::ZipIOException(u"Inconsistent ODF versions in content.xml and manifest.xml!"_ustr ) ) );
            }
        }
    }

    maNamespaceAttrList->Clear();

    maNamespaceHandler->addNSDeclAttributes( maNamespaceAttrList );
    std::optional<SvXMLNamespaceMap> pRewindMap = processNSAttributes(mxNamespaceMap, this, maNamespaceAttrList);

    SvXMLImportContextRef xContext;
    const bool bRootContext = maContexts.empty();
    if (!maContexts.empty())
    {
        const SvXMLImportContextRef & pHandler = maContexts.top();
        SAL_INFO("xmloff.core", "calling createFastChildContext on " << typeid(*pHandler.get()).name());
        auto tmp = pHandler->createFastChildContext( Element, Attribs );
        xContext = static_cast<SvXMLImportContext*>(tmp.get());
        assert((tmp && xContext) || (!tmp && !xContext));
    }
    else
        xContext.set( CreateFastContext( Element, Attribs ) );

    SAL_INFO_IF(!xContext.is(), "xmloff.core", "No fast context for element " << getNameFromToken(Element));
    if (bRootContext && !xContext)
    {
        OUString aName = getNameFromToken(Element);
        SetError( XMLERROR_FLAG_SEVERE | XMLERROR_UNKNOWN_ROOT,
                  { aName }, "Root element " + aName + " unknown", Reference<xml::sax::XLocator>() );
    }
    if ( !xContext )
        xContext.set( new SvXMLImportContext( *this ) );

    // Remember old namespace map.
    if( pRewindMap )
        xContext->PutRewindMap(std::move(pRewindMap));

    // Call a startElement at the new context.
    xContext->startFastElement( Element, Attribs );

    // Push context on stack.
    maContexts.push(xContext);
}

void SAL_CALL SvXMLImport::startUnknownElement (const OUString & rNamespace, const OUString & rName,
    const uno::Reference< xml::sax::XFastAttributeList > & Attribs)
{
    SAL_INFO("xmloff.core", "startUnknownElement " << rNamespace << " " << rName);
    SvXMLImportContextRef xContext;
    const bool bRootContext = maContexts.empty();
    if (!maContexts.empty())
    {
        const SvXMLImportContextRef & pHandler = maContexts.top();
        SAL_INFO("xmloff.core", "calling createUnknownChildContext on " << typeid(*pHandler.get()).name());
        auto tmp = pHandler->createUnknownChildContext( rNamespace, rName, Attribs );
        xContext = static_cast<SvXMLImportContext*>(tmp.get());
        assert((tmp && xContext) || (!tmp && !xContext));
    }
    else
        xContext.set( CreateFastContext( -1, Attribs ) );

    SAL_WARN_IF(!xContext.is(), "xmloff.core", "No context for unknown-element " << rNamespace << " " << rName);
    if (bRootContext && !xContext)
    {
        SetError( XMLERROR_FLAG_SEVERE | XMLERROR_UNKNOWN_ROOT,
                  { rName }, "Root element " + rName + " unknown", Reference<xml::sax::XLocator>() );
    }
    if (!xContext)
    {
        if (!maContexts.empty())
            // This is pretty weird, but it's what the code did before I simplified it, and some parts of the
            // code rely on this behaviour e.g. DocumentBuilderContext
            xContext = maContexts.top();
        else
            xContext = new SvXMLImportContext( *this );
    }

    xContext->startUnknownElement( rNamespace, rName, Attribs );
    maContexts.push(xContext);
}

void SAL_CALL SvXMLImport::endFastElement (sal_Int32 Element)
{
    SAL_INFO("xmloff.core", "endFastElement " << SvXMLImport::getNameFromToken( Element ));
    if (maContexts.empty())
    {
        SAL_WARN("xmloff.core", "SvXMLImport::endFastElement: no context left");
        assert(false);
        return;
    }
    SvXMLImportContextRef xContext = std::move(maContexts.top());
    // Get a namespace map to rewind.
    std::optional<SvXMLNamespaceMap> pRewindMap = xContext->TakeRewindMap();
    maContexts.pop();
    xContext->endFastElement( Element );
    // Rewind a namespace map.
    if (pRewindMap)
        mxNamespaceMap = std::move(pRewindMap);
}

void SAL_CALL SvXMLImport::endUnknownElement (const OUString & rPrefix, const OUString & rLocalName)
{
    SAL_INFO("xmloff.core", "endUnknownElement " << rPrefix << " " << rLocalName);
    if (maContexts.empty())
    {
        SAL_WARN("xmloff.core", "SvXMLImport::endUnknownElement: no context left");
        assert(false);
        return;
    }
    SvXMLImportContextRef xContext = std::move(maContexts.top());
    maContexts.pop();
    xContext->endUnknownElement( rPrefix, rLocalName );
}

uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
    SvXMLImport::createFastChildContext (sal_Int32,
    const uno::Reference< xml::sax::XFastAttributeList > &)
{
    return this;
}

uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
    SvXMLImport::createUnknownChildContext (const OUString &, const OUString &,
    const uno::Reference< xml::sax::XFastAttributeList > &)
{
    return this;
}

void SvXMLImport::SetStatistics(const uno::Sequence< beans::NamedValue> &)
{
    GetProgressBarHelper()->SetRepeat(false);
    GetProgressBarHelper()->SetReference(0);
}

// XImporter
void SAL_CALL SvXMLImport::setTargetDocument( const uno::Reference< lang::XComponent >& xDoc )
{
    mxModel.set( xDoc, UNO_QUERY );
    if( !mxModel.is() )
        throw lang::IllegalArgumentException();

    try
    {
        uno::Reference<document::XStorageBasedDocument> const xSBDoc(mxModel, uno::UNO_QUERY);
        uno::Reference<embed::XStorage> const xStor(xSBDoc.is() ? xSBDoc->getDocumentStorage()
                                                                : nullptr);
        if (xStor.is())
        {
            mpImpl->mbIsOOoXML =
                ::comphelper::OStorageHelper::GetXStorageFormat(xStor)
                   < SOFFICE_FILEFORMAT_8;
        }
    }
    catch (uno::Exception const&)
    {
        DBG_UNHANDLED_EXCEPTION("xmloff.core");
    }
    if (!mxEventListener.is())
    {
        mxEventListener.set(new SvXMLImportEventListener(this));
        mxModel->addEventListener(mxEventListener);
    }

    SAL_WARN_IF( bool(mpNumImport), "xmloff.core", "number format import already exists." );
    mpNumImport.reset();
}

// XFilter
sal_Bool SAL_CALL SvXMLImport::filter( const uno::Sequence< beans::PropertyValue >& )
{
    return false;
}

void SAL_CALL SvXMLImport::cancel(  )
{
}

// XInitialize
void SAL_CALL SvXMLImport::initialize( const uno::Sequence< uno::Any >& aArguments )
{
    for( const auto& rAny : aArguments )
    {
        Reference<XInterface> xValue;
        rAny >>= xValue;

        uno::Reference<task::XStatusIndicator> xTmpStatusIndicator(
            xValue, UNO_QUERY );
        if( xTmpStatusIndicator.is() )
            mxStatusIndicator = std::move(xTmpStatusIndicator);

        uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler(xValue, UNO_QUERY);
        if (xGraphicStorageHandler.is())
            mxGraphicStorageHandler = std::move(xGraphicStorageHandler);

        uno::Reference<document::XEmbeddedObjectResolver> xTmpObjectResolver(
            xValue, UNO_QUERY );
        if( xTmpObjectResolver.is() )
            mxEmbeddedResolver = std::move(xTmpObjectResolver);

        uno::Reference<beans::XPropertySet> xTmpPropSet( xValue, UNO_QUERY );
        if( xTmpPropSet.is() )
        {
            mxImportInfo = std::move(xTmpPropSet);
            uno::Reference< beans::XPropertySetInfo > xPropertySetInfo = mxImportInfo->getPropertySetInfo();
            if (xPropertySetInfo.is())
            {
                OUString sPropName(XML_NUMBERSTYLES);
                if (xPropertySetInfo->hasPropertyByName(sPropName))
                {
                    uno::Any aAny = mxImportInfo->getPropertyValue(sPropName);
                    aAny >>= mxNumberStyles;
                }

                sPropName = "PrivateData";
                if (xPropertySetInfo->hasPropertyByName(sPropName))
                {
                    Reference < XInterface > xIfc;
                    uno::Any aAny = mxImportInfo->getPropertyValue(sPropName);
                    aAny >>= xIfc;

                    StyleMap *pSMap = dynamic_cast<StyleMap*>( xIfc.get() );
                    if( pSMap )
                    {
                        mpStyleMap = pSMap;
                    }
                }
                OUString sBaseURI;
                sPropName = "BaseURI";
                if (xPropertySetInfo->hasPropertyByName(sPropName))
                {
                    uno::Any aAny = mxImportInfo->getPropertyValue(sPropName);
                    aAny >>= sBaseURI;
                    mpImpl->aBaseURL.SetURL( sBaseURI );
                    mpImpl->aDocBase.SetURL( sBaseURI );
                }
                OUString sRelPath;
                sPropName = "StreamRelPath";
                if( xPropertySetInfo->hasPropertyByName(sPropName) )
                {
                    uno::Any aAny = mxImportInfo->getPropertyValue(sPropName);
                    aAny >>= sRelPath;
                }
                OUString sName;
                sPropName = "StreamName";
                if( xPropertySetInfo->hasPropertyByName(sPropName) )
                {
                    uno::Any aAny = mxImportInfo->getPropertyValue(sPropName);
                    aAny >>= sName;
                }
                if( !sBaseURI.isEmpty() && !sName.isEmpty() )
                {
                    if( !sRelPath.isEmpty() )
                        mpImpl->aBaseURL.insertName( sRelPath );
                    mpImpl->aBaseURL.insertName( sName );
                }
                mpImpl->mStreamName = sName; // Note: may be empty (XSLT)
                // Retrieve property <ShapePositionInHoriL2R> (#i28749#)
                sPropName = "ShapePositionInHoriL2R";
                if( xPropertySetInfo->hasPropertyByName(sPropName) )
                {
                    uno::Any aAny = mxImportInfo->getPropertyValue(sPropName);
                    aAny >>= mpImpl->mbShapePositionInHoriL2R;
                }
                sPropName = "TextDocInOOoFileFormat";
                if( xPropertySetInfo->hasPropertyByName(sPropName) )
                {
                    uno::Any aAny = mxImportInfo->getPropertyValue(sPropName);
                    aAny >>= mpImpl->mbTextDocInOOoFileFormat;
                }

                sPropName = "SourceStorage";
                if( xPropertySetInfo->hasPropertyByName(sPropName) )
                    mxImportInfo->getPropertyValue(sPropName) >>= mpImpl->mxSourceStorage;
            }
        }
    }

    uno::Reference<lang::XInitialization> const xInit(mxParser, uno::UNO_QUERY_THROW);
    xInit->initialize( { Any(u"IgnoreMissingNSDecl"_ustr) });
}

// XServiceInfo
OUString SAL_CALL SvXMLImport::getImplementationName()
{
    return mpImpl->implementationName;
}

sal_Bool SAL_CALL SvXMLImport::supportsService( const OUString& rServiceName )
{
    return cppu::supportsService(this, rServiceName);
}

uno::Sequence< OUString > SAL_CALL SvXMLImport::getSupportedServiceNames(  )
{
    return mpImpl->maSupportedServiceNames;
}

XMLTextImportHelper* SvXMLImport::CreateTextImport()
{
    return new XMLTextImportHelper( mxModel, *this );
}

XMLShapeImportHelper* SvXMLImport::CreateShapeImport()
{
    return new XMLShapeImportHelper( *this, mxModel );
}

SchXMLImportHelper* SvXMLImport::CreateChartImport()
{
// WASM_CHART change
// TODO: Instead of importing the ChartModel an alternative may be
// added to convert not to Chart/OLE SdrObejct, but to GraphicObject
// with the Chart visualization. There should be a preview available
// in the imported chart data
#if !ENABLE_WASM_STRIP_CHART
    return new SchXMLImportHelper();
#else
    return nullptr;
#endif
}

::xmloff::OFormLayerXMLImport* SvXMLImport::CreateFormImport()
{
    return new ::xmloff::OFormLayerXMLImport(*this);
}


// Get or create fill/line/lineend-style-helper


const Reference< container::XNameContainer > & SvXMLImport::GetGradientHelper()
{
    if( !mxGradientHelper.is() )
    {
        if( mxModel.is() )
        {
            Reference< lang::XMultiServiceFactory > xServiceFact( mxModel, UNO_QUERY);
            if( xServiceFact.is() )
            {
                try
                {
                    mxGradientHelper.set( xServiceFact->createInstance(
                        u"com.sun.star.drawing.GradientTable"_ustr ), UNO_QUERY);
                }
                catch( lang::ServiceNotRegisteredException& )
                {}
            }
        }
    }

    return mxGradientHelper;
}

const Reference< container::XNameContainer > & SvXMLImport::GetHatchHelper()
{
    if( !mxHatchHelper.is() )
    {
        if( mxModel.is() )
        {
            Reference< lang::XMultiServiceFactory > xServiceFact( mxModel, UNO_QUERY);
            if( xServiceFact.is() )
            {
                try
                {
                    mxHatchHelper.set( xServiceFact->createInstance(
                        u"com.sun.star.drawing.HatchTable"_ustr ), UNO_QUERY);
                }
                catch( lang::ServiceNotRegisteredException& )
                {}
            }
        }
    }

    return mxHatchHelper;
}

const Reference< container::XNameContainer > & SvXMLImport::GetBitmapHelper()
{
    if( !mxBitmapHelper.is() )
    {
        if( mxModel.is() )
        {
            Reference< lang::XMultiServiceFactory > xServiceFact( mxModel, UNO_QUERY);
            if( xServiceFact.is() )
            {
                try
                {
                    mxBitmapHelper.set( xServiceFact->createInstance(
                        u"com.sun.star.drawing.BitmapTable"_ustr ), UNO_QUERY);
                }
                catch( lang::ServiceNotRegisteredException& )
                {}
            }
        }
    }

    return mxBitmapHelper;
}

const Reference< container::XNameContainer > & SvXMLImport::GetTransGradientHelper()
{
    if( !mxTransGradientHelper.is() )
    {
        if( mxModel.is() )
        {
            Reference< lang::XMultiServiceFactory > xServiceFact( mxModel, UNO_QUERY);
            if( xServiceFact.is() )
            {
                try
                {
                    mxTransGradientHelper.set( xServiceFact->createInstance(
                        u"com.sun.star.drawing.TransparencyGradientTable"_ustr ), UNO_QUERY);
                }
                catch( lang::ServiceNotRegisteredException& )
                {}
            }
        }
    }

    return mxTransGradientHelper;
}

const Reference< container::XNameContainer > & SvXMLImport::GetMarkerHelper()
{
    if( !mxMarkerHelper.is() )
    {
        if( mxModel.is() )
        {
            Reference< lang::XMultiServiceFactory > xServiceFact( mxModel, UNO_QUERY);
            if( xServiceFact.is() )
            {
                try
                {
                    mxMarkerHelper.set( xServiceFact->createInstance( u"com.sun.star.drawing.MarkerTable"_ustr ), UNO_QUERY);
                }
                catch( lang::ServiceNotRegisteredException& )
                {}
            }
        }
    }

    return mxMarkerHelper;
}

const Reference< container::XNameContainer > & SvXMLImport::GetDashHelper()
{
    if( !mxDashHelper.is() && mxModel.is() )
    {
        Reference< lang::XMultiServiceFactory > xServiceFact( mxModel, UNO_QUERY);
        if( xServiceFact.is() )
        {
            try
            {
                mxDashHelper.set( xServiceFact->createInstance( u"com.sun.star.drawing.DashTable"_ustr ), UNO_QUERY);
            }
            catch( lang::ServiceNotRegisteredException& )
            {}
        }
    }

    return mxDashHelper;
}

bool SvXMLImport::IsPackageURL( std::u16string_view rURL ) const
{

    // if, and only if, only parts are imported, then we're in a package
    const SvXMLImportFlags nTest = SvXMLImportFlags::META|SvXMLImportFlags::STYLES|SvXMLImportFlags::CONTENT|SvXMLImportFlags::SETTINGS;
    if( (mnImportFlags & nTest) == nTest )
        return false;

    // TODO: from this point extract to own static function

    // Some quick tests: Some may rely on the package structure!
    size_t nLen = rURL.size();
    if( nLen > 0 && '/' == rURL[0] )
        // RFC2396 net_path or abs_path
        return false;
    else if( nLen > 1 && '.' == rURL[0] )
    {
        if( '.' == rURL[1] )
            // ../: We are never going up one level, so we know
            // it's not an external URI
            return false;
        else if( '/' == rURL[1] )
            // we are remaining on a level, so it's a package URI
            return true;
    }

    // Now check for a RFC2396 schema
    size_t nPos = 1;
    while( nPos < nLen )
    {
        switch( rURL[nPos] )
        {
        case '/':
            // a relative path segment
            return true;
        case ':':
            // a schema
            return false;
        default:
            break;
            // we don't care about any other characters
        }
        ++nPos;
    }

    return true;
}

uno::Reference<graphic::XGraphic> SvXMLImport::loadGraphicByURL(OUString const& rURL,
                                                                sal_Int32 nPageNum)
{
    uno::Reference<graphic::XGraphic> xGraphic;

    try
    {
        if (mxGraphicStorageHandler.is())
        {
            if (IsPackageURL(rURL))
            {
                xGraphic = mxGraphicStorageHandler->loadGraphicAtPage(rURL, nPageNum);
            }
            else
            {
                OUString const aAbsoluteURL = GetAbsoluteReference(rURL);
                GraphicExternalLink aExternalLink(aAbsoluteURL);
                Graphic aGraphic(aExternalLink);
                xGraphic = aGraphic.GetXGraphic();
            }
        }
    }
    catch (...)
    {
        bool bRepairPackage = false;
        if (auto const xStorProps{ GetSourceStorage().query<beans::XPropertySet>() })
        {
            try
            {
                xStorProps->getPropertyValue(u"RepairPackage"_ustr) >>= bRepairPackage;
            }
            catch (uno::Exception&)
            {
            }
        }
        if (!bRepairPackage)
            throw;
    }

    return xGraphic;
}

uno::Reference<graphic::XGraphic> SvXMLImport::loadGraphicFromBase64(uno::Reference<io::XOutputStream> const & rxOutputStream)
{
    uno::Reference<graphic::XGraphic> xGraphic;

    if (mxGraphicStorageHandler.is())
    {
        xGraphic = mxGraphicStorageHandler->loadGraphicFromOutputStream(rxOutputStream);
    }

    return xGraphic;
}

Reference< XOutputStream > SvXMLImport::GetStreamForGraphicObjectURLFromBase64() const
{
    Reference< XOutputStream > xOStm;
    Reference< document::XBinaryStreamResolver > xStmResolver(mxGraphicStorageHandler, UNO_QUERY);

    if( xStmResolver.is() )
        xOStm = xStmResolver->createOutputStream();

    return xOStm;
}

OUString SvXMLImport::ResolveEmbeddedObjectURL(
                                    const OUString& rURL,
                                    std::u16string_view rClassId )
{
    OUString sRet;

    if( IsPackageURL( rURL ) )
    {
        if ( mxEmbeddedResolver.is() )
        {
            OUString sURL( rURL );
            if( !rClassId.empty() )
            {
                sURL += OUString::Concat("!") + rClassId;
            }
            sRet = mxEmbeddedResolver->resolveEmbeddedObjectURL( sURL );
        }
    }
    else
        sRet = GetAbsoluteReference( rURL );

    return sRet;
}

Reference< embed::XStorage > const & SvXMLImport::GetSourceStorage() const
{
    return mpImpl->mxSourceStorage;
}

Reference < XOutputStream >
        SvXMLImport::GetStreamForEmbeddedObjectURLFromBase64() const
{
    Reference < XOutputStream > xOLEStream;

    if( mxEmbeddedResolver.is() )
    {
        Reference< XNameAccess > xNA( mxEmbeddedResolver, UNO_QUERY );
        if( xNA.is() )
        {
            Any aAny = xNA->getByName( u"Obj12345678"_ustr );
            aAny >>= xOLEStream;
        }
    }

    return xOLEStream;
}

OUString SvXMLImport::ResolveEmbeddedObjectURLFromBase64()
{
    OUString sRet;

    if( mxEmbeddedResolver.is() )
    {
        sRet = mxEmbeddedResolver->resolveEmbeddedObjectURL( u"Obj12345678"_ustr );
    }

    return sRet;
}

void SvXMLImport::AddStyleDisplayName( XmlStyleFamily nFamily,
                                       const OUString& rName,
                                       const OUString& rDisplayName )
{
    if( !mpStyleMap.is() )
    {
        mpStyleMap = new StyleMap;
        if( mxImportInfo.is() )
        {
            OUString sPrivateData( u"PrivateData"_ustr );
            Reference< beans::XPropertySetInfo > xPropertySetInfo =
                mxImportInfo->getPropertySetInfo();
            if( xPropertySetInfo.is() &&
                xPropertySetInfo->hasPropertyByName(sPrivateData) )
            {
                Reference < XInterface > xIfc(
                        static_cast< css::lang::XTypeProvider *>( mpStyleMap.get() ) );
                mxImportInfo->setPropertyValue( sPrivateData, Any(xIfc) );
            }
        }
    }

    StyleMap::key_type aKey( nFamily, rName );
    StyleMap::value_type aValue( aKey, rDisplayName );
    ::std::pair<StyleMap::iterator,bool> aRes( mpStyleMap->insert( aValue ) );
    SAL_WARN_IF( !aRes.second,
                 "xmloff.core",
       "duplicate style name of family " << static_cast<int>(nFamily) << ": \"" << rName << "\"");

}

OUString SvXMLImport::GetStyleDisplayName( XmlStyleFamily nFamily,
                                           const OUString& rName ) const
{
    OUString sName( rName );
    if( mpStyleMap.is() && !rName.isEmpty() )
    {
        StyleMap::key_type aKey( nFamily, rName );
        StyleMap::const_iterator aIter = mpStyleMap->find( aKey );
        if( aIter != mpStyleMap->end() )
            sName = (*aIter).second;
    }
    return sName;
}

void SvXMLImport::SetViewSettings(const css::uno::Sequence<css::beans::PropertyValue>&)
{
}

void SvXMLImport::SetConfigurationSettings(const css::uno::Sequence<css::beans::PropertyValue>&)
{
}

void SvXMLImport::SetDocumentSpecificSettings(const OUString&, const uno::Sequence<beans::PropertyValue>&)
{
}

ProgressBarHelper*  SvXMLImport::GetProgressBarHelper()
{
    if (!mpProgressBarHelper)
    {
        mpProgressBarHelper = std::make_unique<ProgressBarHelper>(mxStatusIndicator, false);

        if (mxImportInfo.is())
        {
            uno::Reference< beans::XPropertySetInfo > xPropertySetInfo = mxImportInfo->getPropertySetInfo();
            if (xPropertySetInfo.is())
            {
                OUString sProgressRange(XML_PROGRESSRANGE);
                OUString sProgressMax(XML_PROGRESSMAX);
                OUString sProgressCurrent(XML_PROGRESSCURRENT);
                OUString sRepeat(XML_PROGRESSREPEAT);
                if (xPropertySetInfo->hasPropertyByName(sProgressMax) &&
                    xPropertySetInfo->hasPropertyByName(sProgressCurrent) &&
                    xPropertySetInfo->hasPropertyByName(sProgressRange))
                {
                    uno::Any aAny;
                    sal_Int32 nProgressMax(0);
                    sal_Int32 nProgressCurrent(0);
                    sal_Int32 nProgressRange(0);
                    aAny = mxImportInfo->getPropertyValue(sProgressRange);
                    if (aAny >>= nProgressRange)
                        mpProgressBarHelper->SetRange(nProgressRange);
                    aAny = mxImportInfo->getPropertyValue(sProgressMax);
                    if (aAny >>= nProgressMax)
                        mpProgressBarHelper->SetReference(nProgressMax);
                    aAny = mxImportInfo->getPropertyValue(sProgressCurrent);
                    if (aAny >>= nProgressCurrent)
                        mpProgressBarHelper->SetValue(nProgressCurrent);
                }
                if (xPropertySetInfo->hasPropertyByName(sRepeat))
                {
                    uno::Any aAny = mxImportInfo->getPropertyValue(sRepeat);
                    if (aAny.getValueType() == cppu::UnoType<bool>::get())
                        mpProgressBarHelper->SetRepeat(::cppu::any2bool(aAny));
                    else {
                        SAL_WARN( "xmloff.core", "why is it no boolean?" );
                    }
                }
            }
        }
    }
    return mpProgressBarHelper.get();
}

void SvXMLImport::AddNumberStyle(sal_Int32 nKey, const OUString& rName)
{
    if (!mxNumberStyles.is())
        mxNumberStyles.set( comphelper::NameContainer_createInstance( ::cppu::UnoType<sal_Int32>::get()) );
    if (mxNumberStyles.is())
    {
        try
        {
            mxNumberStyles->insertByName(rName, Any(nKey));
        }
        catch ( uno::Exception& )
        {
            DBG_UNHANDLED_EXCEPTION( "xmloff.core", "Numberformat could not be inserted");
        }
    }
    else {
        SAL_WARN( "xmloff.core", "not possible to create NameContainer");
    }
}

XMLEventImportHelper& SvXMLImport::GetEventImport()
{
    if (!mpEventImportHelper)
    {
        // construct event helper and register StarBasic handler and standard
        // event tables
        mpEventImportHelper = std::make_unique<XMLEventImportHelper>();
        const OUString& sStarBasic(GetXMLToken(XML_STARBASIC));
        mpEventImportHelper->RegisterFactory(sStarBasic,
                                            std::make_unique<XMLStarBasicContextFactory>());
        const OUString& sScript(GetXMLToken(XML_SCRIPT));
        mpEventImportHelper->RegisterFactory(sScript,
                                            std::make_unique<XMLScriptContextFactory>());
        mpEventImportHelper->AddTranslationTable(aStandardEventTable);

        // register StarBasic event handler with capitalized spelling
        mpEventImportHelper->RegisterFactory(u"StarBasic"_ustr,
                                            std::make_unique<XMLStarBasicContextFactory>());
    }

    return *mpEventImportHelper;
}

void SvXMLImport::SetFontDecls( XMLFontStylesContext *pFontDecls )
{
    if (mxFontDecls.is())
        mxFontDecls->dispose();
    mxFontDecls = pFontDecls;
}

void SvXMLImport::SetStyles( SvXMLStylesContext *pStyles )
{
    if (mxStyles.is())
        mxStyles->dispose();
    mxStyles = pStyles;
}

void SvXMLImport::SetAutoStyles( SvXMLStylesContext *pAutoStyles )
{
    if (pAutoStyles && mxNumberStyles.is())
    {
        uno::Reference<xml::sax::XFastAttributeList> xAttrList = new sax_fastparser::FastAttributeList(nullptr);
        const uno::Sequence<OUString> aStyleNames = mxNumberStyles->getElementNames();
        for (const auto& name : aStyleNames)
        {
            uno::Any aAny(mxNumberStyles->getByName(name));
            sal_Int32 nKey(0);
            if (aAny >>= nKey)
            {
                SvXMLStyleContext* pContext = new SvXMLNumFormatContext(
                    *this, name, xAttrList, nKey,
                    GetDataStylesImport()->GetLanguageForKey(nKey), *pAutoStyles);
                pAutoStyles->AddStyle(*pContext);
            }
        }
    }
    if (mxAutoStyles.is())
        mxAutoStyles->dispose();
    mxAutoStyles = pAutoStyles;
    GetTextImport()->SetAutoStyles( pAutoStyles );
    GetShapeImport()->SetAutoStylesContext( pAutoStyles );
#if !ENABLE_WASM_STRIP_CHART
    GetChartImport()->SetAutoStylesContext( pAutoStyles );
#endif
    GetFormImport()->setAutoStyleContext( pAutoStyles );
}

void SvXMLImport::SetMasterStyles( SvXMLStylesContext *pMasterStyles )
{
    if (mxMasterStyles.is())
        mxMasterStyles->dispose();
    mxMasterStyles = pMasterStyles;
}

XMLFontStylesContext *SvXMLImport::GetFontDecls()
{
    return mxFontDecls.get();
}

SvXMLStylesContext *SvXMLImport::GetStyles()
{
    return mxStyles.get();
}

SvXMLStylesContext *SvXMLImport::GetAutoStyles()
{
    return mxAutoStyles.get();
}

const XMLFontStylesContext *SvXMLImport::GetFontDecls() const
{
    return mxFontDecls.get();
}

const SvXMLStylesContext *SvXMLImport::GetStyles() const
{
    return mxStyles.get();
}

const SvXMLStylesContext *SvXMLImport::GetAutoStyles() const
{
    return mxAutoStyles.get();
}

OUString SvXMLImport::GetAbsoluteReference(const OUString& rValue) const
{
    if( rValue.isEmpty() || rValue[0] == '#' )
        return rValue;

    INetURLObject aAbsURL;
    if( mpImpl->aBaseURL.GetNewAbsURL( rValue, &aAbsURL ) )
        return aAbsURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
    else
        return rValue;
}

bool SvXMLImport::IsODFVersionConsistent( const OUString& aODFVersion )
{
    // the check returns false only if the storage version could be retrieved
    bool bResult = true;

    if ( !aODFVersion.isEmpty() && aODFVersion.compareTo( ODFVER_012_TEXT ) >= 0 )
    {
        // check the consistency only for the ODF1.2 and later ( according to content.xml )
        // manifest.xml might have no version, it should be checked here and the correct version should be set
        try
        {   // don't use getDocumentStorage(), it's temporary and latest version
            uno::Reference<embed::XStorage> const xStor(GetSourceStorage());
            if (!xStor.is())
                return bResult;
            uno::Reference< beans::XPropertySet > xStorProps( xStor, uno::UNO_QUERY_THROW );

            // the check should be done only for OASIS format
            if (!IsOOoXML())
            {
                bool bRepairPackage = false;
                try
                {
                    xStorProps->getPropertyValue( u"RepairPackage"_ustr )
                        >>= bRepairPackage;
                } catch ( uno::Exception& )
                {}

                // check only if not in Repair mode
                if ( !bRepairPackage )
                {
                    OUString aStorVersion;
                    xStorProps->getPropertyValue( u"Version"_ustr )
                        >>= aStorVersion;

                    // if the storage version is set in manifest.xml, it must be the same as in content.xml
                    // if not, set it explicitly to be used further ( it will work even for readonly storage )
                    // This workaround is not nice, but I see no other way to handle it, since there are
                    // ODF1.2 documents without version in manifest.xml
                    if ( !aStorVersion.isEmpty() )
                        bResult = aODFVersion == aStorVersion;
                    else
                        xStorProps->setPropertyValue( u"Version"_ustr,
                                                      uno::Any( aODFVersion ) );

                    if ( bResult )
                    {
                        bool bInconsistent = false;
                        xStorProps->getPropertyValue( u"IsInconsistent"_ustr )
                            >>= bInconsistent;
                        bResult = !bInconsistent;
                    }
                }
            }
        }
        catch( uno::Exception& )
        {}
    }

    return bResult;
}

void SvXMLImport::CreateNumberFormatsSupplier_()
{
    SAL_WARN_IF( mxNumberFormatsSupplier.is(), "xmloff.core", "number formats supplier already exists!" );
    if(mxModel.is())
        mxNumberFormatsSupplier =
            uno::Reference< util::XNumberFormatsSupplier> (mxModel, uno::UNO_QUERY);
}

void SvXMLImport::CreateDataStylesImport_()
{
    SAL_WARN_IF( bool(mpNumImport), "xmloff.core", "data styles import already exists!" );
    uno::Reference<util::XNumberFormatsSupplier> xNum =
        GetNumberFormatsSupplier();
    if ( xNum.is() )
        mpNumImport = std::make_unique<SvXMLNumFmtHelper>(xNum);
}

sal_Unicode SvXMLImport::ConvStarBatsCharToStarSymbol( sal_Unicode c )
{
    sal_Unicode cNew = c;
    if( !mpImpl->hBatsFontConv )
    {
        mpImpl->hBatsFontConv = CreateFontToSubsFontConverter( u"StarBats",
                 FontToSubsFontFlags::IMPORT );
        SAL_WARN_IF( !mpImpl->hBatsFontConv, "xmloff.core", "Got no symbol font converter" );
    }
    if( mpImpl->hBatsFontConv )
    {
        cNew = ConvertFontToSubsFontChar( mpImpl->hBatsFontConv, c );
    }

    return cNew;
}

sal_Unicode SvXMLImport::ConvStarMathCharToStarSymbol( sal_Unicode c )
{
    sal_Unicode cNew = c;
    if( !mpImpl->hMathFontConv )
    {
        mpImpl->hMathFontConv = CreateFontToSubsFontConverter( u"StarMath",
                 FontToSubsFontFlags::IMPORT );
        SAL_WARN_IF( !mpImpl->hMathFontConv, "xmloff.core", "Got no symbol font converter" );
    }
    if( mpImpl->hMathFontConv )
    {
        cNew = ConvertFontToSubsFontChar( mpImpl->hMathFontConv, c );
    }

    return cNew;
}

void SvXMLImport::SetError(
    sal_Int32 nId,
    const Sequence<OUString>& rMsgParams,
    const OUString& rExceptionMessage,
    const Reference<xml::sax::XLocator>& rLocator )
{
    // create error list on demand
    if ( !mpXMLErrors )
        mpXMLErrors = std::make_unique<XMLErrors>(nullptr);

    // save error information
    // use document locator (if none supplied)
    mpXMLErrors->AddRecord( nId, rMsgParams, rExceptionMessage,
                           rLocator.is() ? rLocator : mxLocator );
}

void SvXMLImport::SetError(
    sal_Int32 nId,
    const Sequence<OUString>& rMsgParams)
{
    SetError( nId, rMsgParams, u""_ustr, nullptr );
}

void SvXMLImport::SetError(
    sal_Int32 nId,
    const OUString& rMsg1)
{
    Sequence<OUString> aSeq { rMsg1 };
    SetError( nId, aSeq );
}

void SvXMLImport::DisposingModel()
{
    if( mxFontDecls.is() )
        mxFontDecls->dispose();
    if( mxStyles.is() )
        mxStyles->dispose();
    if( mxAutoStyles.is() )
        mxAutoStyles->dispose();
    if( mxMasterStyles.is() )
        mxMasterStyles->dispose();

    mxModel.clear();
    mxEventListener.clear();
}

::comphelper::UnoInterfaceToUniqueIdentifierMapper& SvXMLImport::getInterfaceToIdentifierMapper()
{
    return mpImpl->maInterfaceToIdentifierMapper;
}

uno::Reference< uno::XComponentContext > const &
SvXMLImport::GetComponentContext() const
{
    return mpImpl->mxComponentContext;
}

OUString SvXMLImport::GetBaseURL() const
{
    return mpImpl->aBaseURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
}

OUString SvXMLImport::GetDocumentBase() const
{
    return mpImpl->aDocBase.GetMainURL( INetURLObject::DecodeMechanism::NONE );
}

// Convert drawing object positions from OOo file format to OASIS (#i28749#)
bool SvXMLImport::IsShapePositionInHoriL2R() const
{
    return mpImpl->mbShapePositionInHoriL2R;
}

bool SvXMLImport::IsTextDocInOOoFileFormat() const
{
    return mpImpl->mbTextDocInOOoFileFormat;
}

void SvXMLImport::initXForms()
{
    // dummy method; to be implemented by derived classes supporting XForms
}

bool SvXMLImport::getBuildIds( sal_Int32& rUPD, sal_Int32& rBuild ) const
{
    bool bRet = false;
    OUString const aBuildId(getBuildIdsProperty(mxImportInfo));
    if (!aBuildId.isEmpty())
    {
        sal_Int32 nIndex = aBuildId.indexOf('$');
        if (nIndex != -1)
        {
            rUPD = o3tl::toInt32(aBuildId.subView( 0, nIndex ));
            sal_Int32 nIndexEnd = aBuildId.indexOf(';', nIndex);
            rBuild = (nIndexEnd == -1)
                ? o3tl::toInt32(aBuildId.subView(nIndex + 1))
                : o3tl::toInt32(aBuildId.subView(nIndex + 1, nIndexEnd - nIndex - 1));
            bRet = true;
        }
    }
    return bRet;
}

sal_uInt16 SvXMLImport::getGeneratorVersion() const
{
    // --> ORW
    return mpImpl->getGeneratorVersion( *this );
    // <--
}

bool SvXMLImport::isGeneratorVersionOlderThan(
        sal_uInt16 const nOOoVersion, sal_uInt16 const nLOVersion)
{
    assert( (nLOVersion  & LO_flag));
    assert(!(nOOoVersion & LO_flag));
    const sal_uInt16 nGeneratorVersion(getGeneratorVersion());
    return (nGeneratorVersion & LO_flag)
        ?   nGeneratorVersion < nLOVersion
        :   nGeneratorVersion < nOOoVersion;
}


OUString SvXMLImport::GetODFVersion() const
{
    return mpImpl->mxODFVersion ? *mpImpl->mxODFVersion : OUString();
}

bool SvXMLImport::IsOOoXML() const
{
    return mpImpl->mbIsOOoXML;
}

bool SvXMLImport::IsMSO() const
{
    if (!mpImpl->mbIsMSO.has_value())
    {
        uno::Reference<document::XDocumentPropertiesSupplier> xSupplier(GetModel(), uno::UNO_QUERY);
        if (xSupplier.is())
        {
            uno::Reference<document::XDocumentProperties> xProps
                = xSupplier->getDocumentProperties();
            if (xProps.is())
            {
                mpImpl->mbIsMSO = xProps->getGenerator().startsWith("MicrosoftOffice");
            }
        }
    }

    return mpImpl->mbIsMSO.has_value() ? *mpImpl->mbIsMSO : false;
}

// xml:id for RDF metadata
void SvXMLImport::SetXmlId(uno::Reference<uno::XInterface> const & i_xIfc,
    OUString const & i_rXmlId)
{
    if (i_rXmlId.isEmpty())
        return;

    try {
        const uno::Reference<rdf::XMetadatable> xMeta(i_xIfc,
            uno::UNO_QUERY);
//FIXME: not yet
        if (xMeta.is()) {
            const beans::StringPair mdref( mpImpl->mStreamName, i_rXmlId );
            try {
                xMeta->setMetadataReference(mdref);
            } catch (lang::IllegalArgumentException &) {
                // probably duplicate; ignore
                SAL_INFO("xmloff.core", "SvXMLImport::SetXmlId: cannot set xml:id");
            }
        }
    } catch (uno::Exception &) {
        TOOLS_WARN_EXCEPTION("xmloff.core","SvXMLImport::SetXmlId");
    }
}

::xmloff::RDFaImportHelper &
SvXMLImport::GetRDFaImportHelper()
{
    if (!mpImpl->mpRDFaHelper)
    {
        mpImpl->mpRDFaHelper.reset( new ::xmloff::RDFaImportHelper(*this) );
    }
    return *mpImpl->mpRDFaHelper;
}

void
SvXMLImport::AddRDFa(const uno::Reference<rdf::XMetadatable>& i_xObject,
    OUString const & i_rAbout,
    OUString const & i_rProperty,
    OUString const & i_rContent,
    OUString const & i_rDatatype)
{
    // N.B.: we only get called if i_xObject had xhtml:about attribute
    // (an empty attribute value is valid)
    ::xmloff::RDFaImportHelper & rRDFaHelper( GetRDFaImportHelper() );
    rRDFaHelper.ParseAndAddRDFa(i_xObject,
        i_rAbout, i_rProperty, i_rContent, i_rDatatype);
}

bool SvXMLImport::embeddedFontAlreadyProcessed( const OUString& url )
{
    if( m_embeddedFontUrlsKnown.count( url ) != 0 )
        return true;
    m_embeddedFontUrlsKnown.insert( url );
    return false;
}

const OUString & SvXMLImport::getNameFromToken( sal_Int32 nToken )
{
    return FastTokenHandler::getIdentifier( nToken & TOKEN_MASK );
}

OUString SvXMLImport::getPrefixAndNameFromToken( sal_Int32 nToken )
{
    OUString rv;
    sal_Int32 nNamespaceToken = ( nToken & NMSP_MASK ) >> NMSP_SHIFT;
    auto aIter( aNamespaceMap.find( nNamespaceToken ) );
    if( aIter != aNamespaceMap.end() )
        rv = (*aIter).second.second + " " + aIter->second.first + ":";
    return rv + FastTokenHandler::getIdentifier(nToken & TOKEN_MASK);
}

OUString SvXMLImport::getNamespacePrefixFromToken(sal_Int32 nToken, const SvXMLNamespaceMap* pMap)
{
    sal_Int32 nNamespaceToken = ( nToken & NMSP_MASK ) >> NMSP_SHIFT;
    auto aIter( aNamespaceMap.find( nNamespaceToken ) );
    if( aIter != aNamespaceMap.end() )
    {
        if (pMap)
        {
            OUString sRet = pMap->GetPrefixByKey(pMap->GetKeyByName((*aIter).second.second));
            if (!sRet.isEmpty())
                return sRet;
        }
        return (*aIter).second.first;
    }
    else
        return OUString();
}

const OUString & SvXMLImport::getNamespaceURIFromToken( sal_Int32 nToken )
{
    sal_Int32 nNamespaceToken = ( nToken & NMSP_MASK ) >> NMSP_SHIFT;
    auto aIter( aNamespaceMap.find( nNamespaceToken ) );
    if( aIter != aNamespaceMap.end() )
        return (*aIter).second.second;
    else
        return EMPTY_OUSTRING;
}

const OUString & SvXMLImport::getNamespacePrefixFromURI( const OUString& rURI )
{
    auto aIter( aNamespaceURIPrefixMap.find(rURI) );
    if( aIter != aNamespaceURIPrefixMap.end() )
        return (*aIter).second;
    else
        return EMPTY_OUSTRING;
}

sal_Int32 SvXMLImport::getTokenFromName( std::u16string_view rName )
{
    Sequence< sal_Int8 > aLocalNameSeq( reinterpret_cast<sal_Int8 const *>(
        OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ).getStr()), rName.size() );
    return xTokenHandler->getTokenFromUTF8( aLocalNameSeq );
}

void SvXMLImport::initializeNamespaceMaps()
{
    auto mapTokenToNamespace = []( sal_Int32 nToken, sal_Int32 nPrefix, sal_Int32 nNamespace )
    {
        if ( nToken >= 0 )
        {
            const OUString& sNamespace = GetXMLToken( static_cast<XMLTokenEnum>( nNamespace ) );
            const OUString& sPrefix = GetXMLToken( static_cast<XMLTokenEnum>( nPrefix ) );
            assert( aNamespaceMap.find(nToken +1) == aNamespaceMap.end() && "cannot map two namespaces to the same token here");
            aNamespaceMap[ nToken + 1 ] = std::make_pair( sPrefix, sNamespace );
            aNamespaceURIPrefixMap.emplace( sNamespace, sPrefix );
        }
    };

    mapTokenToNamespace( XML_NAMESPACE_XML,              XML_XML,              XML_N_XML              ); // implicit "xml" namespace prefix
    mapTokenToNamespace( XML_NAMESPACE_OFFICE,           XML_NP_OFFICE,        XML_N_OFFICE           );
    mapTokenToNamespace( XML_NAMESPACE_OFFICE_SO52,      XML_NP_OFFICE,        XML_N_OFFICE_OLD       );
    mapTokenToNamespace( XML_NAMESPACE_OFFICE_OOO,       XML_NP_OFFICE,        XML_N_OFFICE_OOO       );
    mapTokenToNamespace( XML_NAMESPACE_STYLE,            XML_NP_STYLE,         XML_N_STYLE            );
    mapTokenToNamespace( XML_NAMESPACE_STYLE_SO52,       XML_NP_STYLE,         XML_N_STYLE_OLD        );
    mapTokenToNamespace( XML_NAMESPACE_STYLE_OOO,        XML_NP_STYLE,         XML_N_STYLE_OOO        );
    mapTokenToNamespace( XML_NAMESPACE_TEXT,             XML_NP_TEXT,          XML_N_TEXT             );
    mapTokenToNamespace( XML_NAMESPACE_TEXT_SO52,        XML_NP_TEXT,          XML_N_TEXT_OLD         );
    mapTokenToNamespace( XML_NAMESPACE_TEXT_OOO,         XML_NP_TEXT,          XML_N_TEXT_OOO         );
    mapTokenToNamespace( XML_NAMESPACE_TABLE,            XML_NP_TABLE,         XML_N_TABLE            );
    mapTokenToNamespace( XML_NAMESPACE_TABLE_SO52,       XML_NP_TABLE,         XML_N_TABLE_OLD        );
    mapTokenToNamespace( XML_NAMESPACE_TABLE_OOO,        XML_NP_TABLE,         XML_N_TABLE_OOO        );
    mapTokenToNamespace( XML_NAMESPACE_DRAW,             XML_NP_DRAW,          XML_N_DRAW             );
    mapTokenToNamespace( XML_NAMESPACE_DRAW_SO52,        XML_NP_DRAW,          XML_N_DRAW_OLD         );
    mapTokenToNamespace( XML_NAMESPACE_DRAW_OOO,         XML_NP_DRAW,          XML_N_DRAW_OOO         );
    mapTokenToNamespace( XML_NAMESPACE_FO,               XML_NP_FO,            XML_N_FO               );
    mapTokenToNamespace( XML_NAMESPACE_FO_SO52,          XML_NP_FO,            XML_N_FO_OLD           );
    mapTokenToNamespace( XML_NAMESPACE_FO_COMPAT,        XML_NP_FO,            XML_N_FO_COMPAT        );
    mapTokenToNamespace( XML_NAMESPACE_XLINK,            XML_NP_XLINK,         XML_N_XLINK            );
    mapTokenToNamespace( XML_NAMESPACE_XLINK_SO52,       XML_NP_XLINK,         XML_N_XLINK_OLD        );
    mapTokenToNamespace( XML_NAMESPACE_DC,               XML_NP_DC,            XML_N_DC               );
    mapTokenToNamespace( XML_NAMESPACE_META,             XML_NP_META,          XML_N_META             );
    mapTokenToNamespace( XML_NAMESPACE_META_SO52,        XML_NP_META,          XML_N_META_OLD         );
    mapTokenToNamespace( XML_NAMESPACE_META_OOO,         XML_NP_META,          XML_N_META_OOO         );
    mapTokenToNamespace( XML_NAMESPACE_NUMBER,           XML_NP_NUMBER,        XML_N_NUMBER           );
    mapTokenToNamespace( XML_NAMESPACE_NUMBER_SO52,      XML_NP_NUMBER,        XML_N_NUMBER_OLD       );
    mapTokenToNamespace( XML_NAMESPACE_NUMBER_OOO,       XML_NP_NUMBER,        XML_N_NUMBER_OOO       );
    mapTokenToNamespace( XML_NAMESPACE_PRESENTATION,     XML_NP_PRESENTATION,  XML_N_PRESENTATION     );
    mapTokenToNamespace( XML_NAMESPACE_PRESENTATION_SO52,XML_NP_PRESENTATION,  XML_N_PRESENTATION_OLD );
    mapTokenToNamespace( XML_NAMESPACE_PRESENTATION_OOO, XML_NP_PRESENTATION,  XML_N_PRESENTATION_OOO );
    mapTokenToNamespace( XML_NAMESPACE_PRESENTATION_OASIS, XML_NP_PRESENTATION, XML_N_PRESENTATION_OASIS );
    mapTokenToNamespace( XML_NAMESPACE_SVG,              XML_NP_SVG,           XML_N_SVG              );
    mapTokenToNamespace( XML_NAMESPACE_SVG_COMPAT,       XML_NP_SVG,           XML_N_SVG_COMPAT       );
    mapTokenToNamespace( XML_NAMESPACE_CHART,            XML_NP_CHART,         XML_N_CHART            );
    mapTokenToNamespace( XML_NAMESPACE_CHART_SO52,       XML_NP_CHART,         XML_N_CHART_OLD        );
    mapTokenToNamespace( XML_NAMESPACE_CHART_OOO,        XML_NP_CHART,         XML_N_CHART_OOO        );
    mapTokenToNamespace( XML_NAMESPACE_DR3D,             XML_NP_DR3D,          XML_N_DR3D             );
    mapTokenToNamespace( XML_NAMESPACE_DR3D_OOO,         XML_NP_DR3D,          XML_N_DR3D_OOO         );
    mapTokenToNamespace( XML_NAMESPACE_MATH,             XML_NP_MATH,          XML_N_MATH             );
    mapTokenToNamespace( XML_NAMESPACE_VERSIONS_LIST,    XML_NP_VERSIONS_LIST, XML_N_VERSIONS_LIST    );
    mapTokenToNamespace( XML_NAMESPACE_FORM,             XML_NP_FORM,          XML_N_FORM             );
    mapTokenToNamespace( XML_NAMESPACE_FORM_OOO,         XML_NP_FORM,          XML_N_FORM_OOO         );
    mapTokenToNamespace( XML_NAMESPACE_SCRIPT,           XML_NP_SCRIPT,        XML_N_SCRIPT           );
    mapTokenToNamespace( XML_NAMESPACE_SCRIPT_OOO,       XML_NP_SCRIPT,        XML_N_SCRIPT_OOO       );
    mapTokenToNamespace( XML_NAMESPACE_BLOCKLIST,        XML_NP_BLOCK_LIST,    XML_N_BLOCK_LIST       );
    mapTokenToNamespace( XML_NAMESPACE_CONFIG,           XML_NP_CONFIG,        XML_N_CONFIG           );
    mapTokenToNamespace( XML_NAMESPACE_CONFIG_OOO,       XML_NP_CONFIG,        XML_N_CONFIG_OOO       );
    mapTokenToNamespace( XML_NAMESPACE_OOO,              XML_NP_OOO,           XML_N_OOO              );
    mapTokenToNamespace( XML_NAMESPACE_OOOW,             XML_NP_OOOW,          XML_N_OOOW             );
    mapTokenToNamespace( XML_NAMESPACE_OOOC,             XML_NP_OOOC,          XML_N_OOOC             );
    mapTokenToNamespace( XML_NAMESPACE_DOM,              XML_NP_DOM,           XML_N_DOM              );
    mapTokenToNamespace( XML_NAMESPACE_DB,               XML_NP_DB,            XML_N_DB               );
    mapTokenToNamespace( XML_NAMESPACE_DB_OASIS,         XML_NP_DB,            XML_N_DB_OASIS         );
    mapTokenToNamespace( XML_NAMESPACE_DLG,              XML_NP_DLG,           XML_N_DLG              );
    mapTokenToNamespace( XML_NAMESPACE_XFORMS,           XML_NP_XFORMS_1_0,    XML_N_XFORMS_1_0       );
    mapTokenToNamespace( XML_NAMESPACE_XSD,              XML_NP_XSD,           XML_N_XSD              );
    mapTokenToNamespace( XML_NAMESPACE_XSI,              XML_NP_XSI,           XML_N_XSI              );
    mapTokenToNamespace( XML_NAMESPACE_SMIL,             XML_NP_SMIL,          XML_N_SMIL             );
    mapTokenToNamespace( XML_NAMESPACE_SMIL_SO52,        XML_NP_SMIL,          XML_N_SMIL_OLD         );
    mapTokenToNamespace( XML_NAMESPACE_SMIL_COMPAT,      XML_NP_SMIL,          XML_N_SMIL_COMPAT      );
    mapTokenToNamespace( XML_NAMESPACE_ANIMATION,        XML_NP_ANIMATION,     XML_N_ANIMATION        );
    mapTokenToNamespace( XML_NAMESPACE_ANIMATION_OOO,    XML_NP_ANIMATION,     XML_N_ANIMATION_OOO    );
    mapTokenToNamespace( XML_NAMESPACE_REPORT,           XML_NP_RPT,           XML_N_RPT              );
    mapTokenToNamespace( XML_NAMESPACE_REPORT_OASIS,     XML_NP_RPT,           XML_N_RPT_OASIS        );
    mapTokenToNamespace( XML_NAMESPACE_OF,               XML_NP_OF,            XML_N_OF               );
    mapTokenToNamespace( XML_NAMESPACE_XHTML,            XML_NP_XHTML,         XML_N_XHTML            );
    mapTokenToNamespace( XML_NAMESPACE_GRDDL,            XML_NP_GRDDL,         XML_N_GRDDL            );
    mapTokenToNamespace( XML_NAMESPACE_OFFICE_EXT,       XML_NP_OFFICE_EXT,    XML_N_OFFICE_EXT       );
    mapTokenToNamespace( XML_NAMESPACE_TABLE_EXT,        XML_NP_TABLE_EXT,     XML_N_TABLE_EXT        );
    mapTokenToNamespace( XML_NAMESPACE_CHART_EXT,        XML_NP_CHART_EXT,     XML_N_CHART_EXT        );
    mapTokenToNamespace( XML_NAMESPACE_DRAW_EXT,         XML_NP_DRAW_EXT,      XML_N_DRAW_EXT         );
    mapTokenToNamespace( XML_NAMESPACE_CALC_EXT,         XML_NP_CALC_EXT,      XML_N_CALC_EXT         );
    mapTokenToNamespace( XML_NAMESPACE_LO_EXT,           XML_NP_LO_EXT,        XML_N_LO_EXT           );
    mapTokenToNamespace( XML_NAMESPACE_CSS3TEXT,         XML_NP_CSS3TEXT,      XML_N_CSS3TEXT         );
    mapTokenToNamespace( XML_NAMESPACE_FIELD,            XML_NP_FIELD,         XML_N_FIELD            );
    mapTokenToNamespace( XML_NAMESPACE_FORMX,            XML_NP_FORMX,         XML_N_FORMX            );
}

void SvXMLImport::registerNamespaces()
{
    for( auto const &aNamespaceEntry : aNamespaceMap )
    {
        // aNamespaceMap = { Token : ( NamespacePrefix, NamespaceURI ) }
        registerNamespace( aNamespaceEntry.second.second, aNamespaceEntry.first << NMSP_SHIFT );
    }
}

void SvXMLImport::NotifyMacroEventRead()
{
    if (mbNotifyMacroEventRead)
        return;

    comphelper::DocumentInfo::notifyMacroEventRead(mxModel);

    mbNotifyMacroEventRead = true;
}

SvXMLImportFastNamespaceHandler::SvXMLImportFastNamespaceHandler()
{
}

void SvXMLImportFastNamespaceHandler::addNSDeclAttributes( rtl::Reference < comphelper::AttributeList > const & rAttrList )
{
    for(const auto& aNamespaceDefine : m_aNamespaceDefines)
    {
        const OUString& rPrefix = aNamespaceDefine.m_aPrefix;
        const OUString& rNamespaceURI = aNamespaceDefine.m_aNamespaceURI;
        OUString sDecl;
        if ( rPrefix.isEmpty() )
            sDecl = "xmlns";
        else
            sDecl = "xmlns:" + rPrefix;
        rAttrList->AddAttribute( sDecl, rNamespaceURI );
    }
    m_aNamespaceDefines.clear();
}

void SvXMLImportFastNamespaceHandler::registerNamespace( const OUString& rNamespacePrefix, const OUString& rNamespaceURI )
{
    // Elements with default namespace parsed by FastParser have namespace prefix.
    // A default namespace needs to be registered with the prefix, to maintain the compatibility.
    if ( rNamespacePrefix.isEmpty() )
        m_aNamespaceDefines.push_back( NamespaceDefine(
                                    SvXMLImport::getNamespacePrefixFromURI( rNamespaceURI ), rNamespaceURI) );

    m_aNamespaceDefines.push_back( NamespaceDefine(
                                    rNamespacePrefix, rNamespaceURI) );
}

OUString SvXMLImportFastNamespaceHandler::getNamespaceURI( const OUString&/* rNamespacePrefix */ )
{
    return OUString();
}

SvXMLLegacyToFastDocHandler::SvXMLLegacyToFastDocHandler( rtl::Reference< SvXMLImport > xImport )
:   mrImport(std::move( xImport )),
    mxFastAttributes( new sax_fastparser::FastAttributeList( SvXMLImport::xTokenHandler.get() ) )
{
}

void SAL_CALL SvXMLLegacyToFastDocHandler::setTargetDocument( const uno::Reference< lang::XComponent >& xDoc )
{
    mrImport->setTargetDocument( xDoc );
}

void SAL_CALL SvXMLLegacyToFastDocHandler::startDocument()
{
    mrImport->startDocument();
}

void SAL_CALL SvXMLLegacyToFastDocHandler::endDocument()
{
    mrImport->endDocument();
}

void SAL_CALL SvXMLLegacyToFastDocHandler::startElement( const OUString& rName,
                        const uno::Reference< xml::sax::XAttributeList >& xAttrList )
{
    sal_uInt16 nDefaultNamespace = XML_NAMESPACE_UNKNOWN;
    if (!maDefaultNamespaces.empty())
        nDefaultNamespace = maDefaultNamespaces.top();
    SvXMLImport::processNSAttributes(mrImport->mxNamespaceMap, mrImport.get(), xAttrList);
    OUString aLocalName;
    sal_uInt16 nPrefix = mrImport->mxNamespaceMap->GetKeyByAttrName( rName, &aLocalName );
    sal_Int32 mnElement = NAMESPACE_TOKEN( nPrefix ) | SvXMLImport::getTokenFromName( aLocalName );
    mxFastAttributes->clear();

    sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
    for( sal_Int16 i=0; i < nAttrCount; i++ )
    {
        const OUString aAttrName = xAttrList->getNameByIndex( i );
        const OUString aAttrValue = xAttrList->getValueByIndex( i );
        if (aAttrName == "xmlns")
        {
            sal_uInt16 nNamespaceKey = mrImport->mxNamespaceMap->GetKeyByName(aAttrValue);
            if (nNamespaceKey != XML_NAMESPACE_UNKNOWN)
            {
                nDefaultNamespace = nNamespaceKey;
                continue;
            }
            assert(false && "unknown namespace");
        }
        else if (aAttrName.indexOf(":") == -1 && nDefaultNamespace != XML_NAMESPACE_UNKNOWN)
        {
            auto const nToken = SvXMLImport::getTokenFromName(aAttrName);
            if (nToken == xmloff::XML_TOKEN_INVALID)
            {
                mxFastAttributes->addUnknown(mrImport->mxNamespaceMap->GetNameByKey(nDefaultNamespace),
                    OUStringToOString(aAttrName, RTL_TEXTENCODING_UTF8),
                    OUStringToOString(aAttrValue, RTL_TEXTENCODING_UTF8));
            }
            else
            {
                sal_Int32 const nAttr = NAMESPACE_TOKEN(nDefaultNamespace) | nToken;
                mxFastAttributes->add(nAttr, OUStringToOString(aAttrValue, RTL_TEXTENCODING_UTF8));
            }
            continue;
        }

        OUString aLocalAttrName;
        OUString aNamespace;
        // don't add unknown namespaces to the map
        sal_uInt16 const nAttrPrefix = mrImport->mxNamespaceMap->GetKeyByQName(
                aAttrName, nullptr, &aLocalAttrName, &aNamespace, SvXMLNamespaceMap::QNameMode::AttrValue);
        if( XML_NAMESPACE_XMLNS == nAttrPrefix )
            continue; // ignore
        auto const nToken = SvXMLImport::getTokenFromName(aLocalAttrName);
        if (XML_NAMESPACE_UNKNOWN == nAttrPrefix || nToken == xmloff::XML_TOKEN_INVALID)
        {
            mxFastAttributes->addUnknown(aNamespace,
                OUStringToOString(aAttrName, RTL_TEXTENCODING_UTF8),
                OUStringToOString(aAttrValue, RTL_TEXTENCODING_UTF8));
        }
        else
        {
            sal_Int32 const nAttr = NAMESPACE_TOKEN(nAttrPrefix) | nToken;
            mxFastAttributes->add(nAttr, OUStringToOString(aAttrValue, RTL_TEXTENCODING_UTF8));
        }
    }
    mrImport->startFastElement( mnElement, mxFastAttributes );
    maDefaultNamespaces.push(nDefaultNamespace);
}

void SAL_CALL SvXMLLegacyToFastDocHandler::endElement( const OUString& rName )
{
    OUString aLocalName;
    sal_uInt16 nPrefix = mrImport->mxNamespaceMap->GetKeyByAttrName( rName, &aLocalName );
    sal_Int32 mnElement = NAMESPACE_TOKEN( nPrefix ) | SvXMLImport::getTokenFromName(aLocalName);
    mrImport->endFastElement( mnElement );
    maDefaultNamespaces.pop();
}

void SAL_CALL SvXMLLegacyToFastDocHandler::characters( const OUString& aChars )
{
    mrImport->characters( aChars );
}

void SAL_CALL SvXMLLegacyToFastDocHandler::ignorableWhitespace( const OUString& )
{
}

void SAL_CALL SvXMLLegacyToFastDocHandler::processingInstruction( const OUString& aTarget,
                                                                  const OUString& aData)
{
    mrImport->processingInstruction( aTarget, aData );
}

void SAL_CALL SvXMLLegacyToFastDocHandler::setDocumentLocator( const uno::Reference< xml::sax::XLocator >& rLocator )
{
    mrImport->setDocumentLocator( rLocator );
}



/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
