/* -*- 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 <memory>
#include <optional>
#include <tuple>
#include <vector>

#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/text/ReferenceFieldSource.hpp>
#include <com/sun/star/text/XChapterNumberingSupplier.hpp>
#include <com/sun/star/text/XTextFrame.hpp>
#include <com/sun/star/text/XTextFieldsSupplier.hpp>
#include <com/sun/star/text/XTextFramesSupplier.hpp>
#include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp>
#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
#include <com/sun/star/text/XFormField.hpp>
#include <com/sun/star/ucb/XAnyCompare.hpp>
#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/style/XStyle.hpp>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/txtstyli.hxx>
#include <xmloff/xmlnumi.hxx>
#include <xmloff/maptype.hxx>

#include <sal/log.hxx>
#include "txtparai.hxx"
#include <xmloff/txtprmap.hxx>
#include <xmloff/txtimppr.hxx>
#include <xmloff/xmlimp.hxx>
#include <txtvfldi.hxx>
#include <xmloff/i18nmap.hxx>
#include "XMLTextListItemContext.hxx"
#include "XMLTextListBlockContext.hxx"
#include "XMLTextFrameContext.hxx"
#include "XMLTextFrameHyperlinkContext.hxx"
#include "XMLSectionImportContext.hxx"
#include "XMLIndexTOCContext.hxx"
#include <xmloff/XMLEventsImportContext.hxx>
#include "XMLTrackedChangesImportContext.hxx"
#include "XMLChangeImportContext.hxx"
#include "XMLAutoMarkFileContext.hxx"
#include <xmloff/ProgressBarHelper.hxx>

#include "XMLCalculationSettingsContext.hxx"
#include <XMLNumberStylesImport.hxx>
#include <PageMasterStyleMap.hxx>
#include <PageMasterPropHdlFactory.hxx>
// XML import: reconstruction of assignment of paragraph style to outline levels (#i69629#)
#include <com/sun/star/beans/XPropertyState.hpp>
#include <txtlists.hxx>
#include <xmloff/odffields.hxx>

using ::com::sun::star::ucb::XAnyCompare;

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::style;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::xml::sax;
using namespace ::com::sun::star::lang;
using namespace ::xmloff::token;
using namespace ::com::sun::star::ucb;


// maximum allowed length of combined characters field
#define MAX_COMBINED_CHARACTERS 6

struct XMLTextImportHelper::Impl
{
    std::optional< std::vector<OUString> > m_xPrevFrmNames;
    std::optional< std::vector<OUString> > m_xNextFrmNames;
    std::unique_ptr<XMLTextListsHelper> m_xTextListsHelper;

    rtl::Reference<SvXMLStylesContext> m_xAutoStyles;

    std::unique_ptr< SvXMLImportPropertyMapper > m_xParaImpPrMap;
    std::unique_ptr< SvXMLImportPropertyMapper > m_xTextImpPrMap;
    std::unique_ptr< SvXMLImportPropertyMapper > m_xFrameImpPrMap;
    std::unique_ptr< SvXMLImportPropertyMapper > m_xSectionImpPrMap;
    std::unique_ptr< SvXMLImportPropertyMapper > m_xRubyImpPrMap;

    std::unique_ptr<SvI18NMap> m_xRenameMap;

    /* Change and extend data structure:
       - data structure contains candidates of paragraph styles, which
         will be assigned to the outline style
       - data structure contains more than one candidate for each list level
         of the outline style (#i69629#)
    */
    std::unique_ptr< std::vector< OUString > []>
        m_xOutlineStylesCandidates;

    // start range, xml:id, RDFa stuff
    typedef std::tuple<
        uno::Reference<text::XTextRange>, OUString,
        std::shared_ptr< ::xmloff::ParsedRDFaAttributes > >
            BookmarkMapEntry_t;
    /// start ranges for open bookmarks
    std::map< OUString, BookmarkMapEntry_t > m_BookmarkStartRanges;

    std::vector< OUString > m_BookmarkVector;

    /// name of the last 'open' redline that started between paragraphs
    OUString m_sOpenRedlineIdentifier;

    // Used for frame deduplication, the name of the last frame imported directly before the current one
    OUString msLastImportedFrameName;

    std::map< OUString, bool > m_bBookmarkHidden;
    std::map< OUString, OUString > m_sBookmarkCondition;

    uno::Reference<text::XText> m_xText;
    uno::Reference<text::XTextCursor> m_xCursor;
    uno::Reference<text::XTextRange> m_xCursorAsRange;
    uno::Reference<container::XNameContainer> m_xParaStyles;
    uno::Reference<container::XNameContainer> m_xTextStyles;
    uno::Reference<container::XNameContainer> m_xNumStyles;
    uno::Reference<container::XNameContainer> m_xFrameStyles;
    uno::Reference<container::XNameContainer> m_xPageStyles;
    uno::Reference<container::XNameContainer> m_xCellStyles;
    uno::Reference<container::XIndexReplace> m_xChapterNumbering;
    uno::Reference<container::XNameAccess> m_xTextFrames;
    uno::Reference<container::XNameAccess> m_xGraphics;
    uno::Reference<container::XNameAccess> m_xObjects;
    uno::Reference<lang::XMultiServiceFactory> m_xServiceFactory;

    SvXMLImport & m_rSvXMLImport;

    bool m_bInsertMode : 1;
    bool m_bStylesOnlyMode : 1;
    bool m_bBlockMode : 1;
    bool m_bProgress : 1;
    bool m_bOrganizerMode : 1;
    bool m_bBodyContentStarted : 1;

    /// Are we inside a <text:deletion> element (deleted redline section)
    bool m_bInsideDeleteContext : 1;

    typedef ::std::pair< OUString, OUString> field_name_type_t;
    typedef ::std::pair< OUString, OUString > field_param_t;
    typedef ::std::vector< field_param_t > field_params_t;
    typedef ::std::tuple<field_name_type_t, field_params_t, uno::Reference<text::XFormField>, uno::Reference<text::XTextRange>> field_stack_item_t;
    typedef ::std::stack< field_stack_item_t > field_stack_t;

    field_stack_t m_FieldStack;

    OUString m_sCellParaStyleDefault;

    std::optional<std::map<OUString, OUString>> m_xCrossRefHeadingBookmarkMap;

    Impl(       uno::Reference<frame::XModel> const& rModel,
                SvXMLImport & rImport,
                bool const bInsertMode, bool const bStylesOnlyMode,
                bool const bProgress, bool const bBlockMode,
                bool const bOrganizerMode)
        :   m_xTextListsHelper( new XMLTextListsHelper() )
        // XML import: reconstruction of assignment of paragraph style to outline levels (#i69629#)
        ,   m_xServiceFactory( rModel, UNO_QUERY )
        ,   m_rSvXMLImport( rImport )
        ,   m_bInsertMode( bInsertMode )
        ,   m_bStylesOnlyMode( bStylesOnlyMode )
        ,   m_bBlockMode( bBlockMode )
        ,   m_bProgress( bProgress )
        ,   m_bOrganizerMode( bOrganizerMode )
        ,   m_bBodyContentStarted( true )
        ,   m_bInsideDeleteContext( false )
    {
    }
    Impl(const Impl&) = delete;
    Impl& operator=(const Impl&) = delete;

    void InitOutlineStylesCandidates()
    {
        if (!m_xOutlineStylesCandidates)
        {
            size_t const size(m_xChapterNumbering->getCount());
            m_xOutlineStylesCandidates.reset(
                new ::std::vector< OUString >[size] );
        }
    }

};


uno::Reference< text::XText > & XMLTextImportHelper::GetText()
{
    return m_xImpl->m_xText;
}

uno::Reference< text::XTextCursor > & XMLTextImportHelper::GetCursor()
{
    return m_xImpl->m_xCursor;
}

uno::Reference< text::XTextRange > & XMLTextImportHelper::GetCursorAsRange()
{
    return m_xImpl->m_xCursorAsRange;
}

bool XMLTextImportHelper::IsInsertMode() const
{
    return m_xImpl->m_bInsertMode;
}

bool XMLTextImportHelper::IsStylesOnlyMode() const
{
    return m_xImpl->m_bStylesOnlyMode;
}

bool XMLTextImportHelper::IsBlockMode() const
{
    return m_xImpl->m_bBlockMode;
}

bool XMLTextImportHelper::IsOrganizerMode() const
{
    return m_xImpl->m_bOrganizerMode;
}

bool XMLTextImportHelper::IsProgress() const
{
    return m_xImpl->m_bProgress;
}

uno::Reference<container::XNameContainer> const&
XMLTextImportHelper::GetParaStyles() const
{
    return m_xImpl->m_xParaStyles;
}

uno::Reference<container::XNameContainer> const&
XMLTextImportHelper::GetTextStyles() const
{
    return m_xImpl->m_xTextStyles;
}

uno::Reference<container::XNameContainer> const&
XMLTextImportHelper::GetNumberingStyles() const
{
    return m_xImpl->m_xNumStyles;
}

uno::Reference<container::XNameContainer> const&
XMLTextImportHelper::GetFrameStyles() const
{
    return m_xImpl->m_xFrameStyles;
}

uno::Reference<container::XNameContainer> const&
XMLTextImportHelper::GetPageStyles() const
{
    return m_xImpl->m_xPageStyles;
}

uno::Reference<container::XNameContainer> const&
XMLTextImportHelper::GetCellStyles() const
{
    return m_xImpl->m_xCellStyles;
}

uno::Reference<container::XIndexReplace> const&
XMLTextImportHelper::GetChapterNumbering() const
{
    return m_xImpl->m_xChapterNumbering;
}

SvXMLImportPropertyMapper*
XMLTextImportHelper::GetParaImportPropertySetMapper() const
{
    return m_xImpl->m_xParaImpPrMap.get();
}

SvXMLImportPropertyMapper*
XMLTextImportHelper::GetTextImportPropertySetMapper() const
{
    return m_xImpl->m_xTextImpPrMap.get();
}

SvXMLImportPropertyMapper*
XMLTextImportHelper::GetSectionImportPropertySetMapper() const
{
    return m_xImpl->m_xSectionImpPrMap.get();
}

SvXMLImportPropertyMapper*
XMLTextImportHelper::GetRubyImportPropertySetMapper() const
{
    return m_xImpl->m_xRubyImpPrMap.get();
}

void XMLTextImportHelper::SetInsideDeleteContext(bool const bNew)
{
    m_xImpl->m_bInsideDeleteContext = bNew;
}

bool XMLTextImportHelper::IsInsideDeleteContext() const
{
    return m_xImpl->m_bInsideDeleteContext;
}

SvXMLImport & XMLTextImportHelper::GetXMLImport()
{
    return m_xImpl->m_rSvXMLImport;
}

XMLTextListsHelper & XMLTextImportHelper::GetTextListHelper()
{
    return *m_xImpl->m_xTextListsHelper;
}

namespace
{
    class FieldParamImporter
    {
        public:
            typedef std::pair<OUString,OUString> field_param_t;
            typedef std::vector<field_param_t> field_params_t;
            FieldParamImporter(const field_params_t* const pInParams, Reference<XNameContainer> const & xOutParams)
                : m_pInParams(pInParams)
                , m_xOutParams(xOutParams)
            { };
            void Import();

        private:
            const field_params_t* const m_pInParams;
            Reference<XNameContainer> m_xOutParams;
    };

    void FieldParamImporter::Import()
    {
        ::std::vector<OUString> vListEntries;
        ::std::map<OUString, Any> vOutParams;
        for(const auto& rCurrent : *m_pInParams)
        {
            if(rCurrent.first == ODF_FORMDROPDOWN_RESULT)
            {
                // sal_Int32
                vOutParams[rCurrent.first] <<= rCurrent.second.toInt32();
            }
            else if(rCurrent.first == ODF_FORMCHECKBOX_RESULT)
            {
                // bool
                vOutParams[rCurrent.first] <<= rCurrent.second.toBoolean();
            }
            else if(rCurrent.first == ODF_FORMDROPDOWN_LISTENTRY)
            {
                // sequence
                vListEntries.push_back(rCurrent.second);
            }
            else
                vOutParams[rCurrent.first] <<= rCurrent.second;
        }
        if(!vListEntries.empty())
        {
            Sequence<OUString> vListEntriesSeq(vListEntries.size());
            copy(vListEntries.begin(), vListEntries.end(), vListEntriesSeq.getArray());
            vOutParams[ODF_FORMDROPDOWN_LISTENTRY] <<= vListEntriesSeq;
        }
        for(const auto& rCurrent : vOutParams)
        {
            try
            {
                m_xOutParams->insertByName(rCurrent.first, rCurrent.second);
            }
            catch(const ElementExistException&)
            {
                SAL_INFO("xmloff.text", "duplicate fieldmark param");
            }
        }
    }
}

XMLTextImportHelper::XMLTextImportHelper(
        uno::Reference<frame::XModel> const& rModel,
        SvXMLImport& rImport,
        bool const bInsertMode, bool const bStylesOnlyMode,
        bool const bProgress, bool const bBlockMode,
        bool const bOrganizerMode)
    : m_xImpl( new Impl(rModel, rImport, bInsertMode, bStylesOnlyMode,
                    bProgress, bBlockMode, bOrganizerMode) )
    , m_xBackpatcherImpl( MakeBackpatcherImpl() )
{
    static constexpr OUString s_PropNameDefaultListId = u"DefaultListId"_ustr;

    Reference< XChapterNumberingSupplier > xCNSupplier( rModel, UNO_QUERY );

    if (xCNSupplier.is())
    {
        // note: m_xChapterNumbering is accessed to import some fields
        m_xImpl->m_xChapterNumbering = xCNSupplier->getChapterNumberingRules();
        // the AutoCorrect document doesn't have a proper outline numbering
        if (!IsBlockMode() && m_xImpl->m_xChapterNumbering.is())
        {
            Reference< XPropertySet > const xNumRuleProps(
                m_xImpl->m_xChapterNumbering, UNO_QUERY);
            if ( xNumRuleProps.is() )
            {
                Reference< XPropertySetInfo > xNumRulePropSetInfo(
                                            xNumRuleProps->getPropertySetInfo());
                if (xNumRulePropSetInfo.is() &&
                    xNumRulePropSetInfo->hasPropertyByName(
                         s_PropNameDefaultListId))
                {
                    OUString sListId;
                    xNumRuleProps->getPropertyValue(s_PropNameDefaultListId)
                        >>= sListId;
                    assert( !sListId.isEmpty() &&
                                "no default list id found at chapter numbering rules instance. Serious defect." );
                    if ( !sListId.isEmpty() )
                    {
                        Reference< XNamed > const xChapterNumNamed(
                            m_xImpl->m_xChapterNumbering, UNO_QUERY);
                        if ( xChapterNumNamed.is() )
                        {
                            m_xImpl->m_xTextListsHelper->KeepListAsProcessed(
                                                    sListId,
                                                    xChapterNumNamed->getName(),
                                                    OUString() );
                        }
                    }
                }
            }
        }
    }

    Reference< XStyleFamiliesSupplier > xFamiliesSupp( rModel, UNO_QUERY );
//  SAL_WARN_IF( !xFamiliesSupp.is(), "xmloff", "no chapter numbering supplier" ); for clipboard there may be documents without styles

    if( xFamiliesSupp.is() )
    {
        Reference< XNameAccess > xFamilies(xFamiliesSupp->getStyleFamilies());

        static constexpr OUString aParaStyles(u"ParagraphStyles"_ustr);
        if( xFamilies->hasByName( aParaStyles ) )
        {
            m_xImpl->m_xParaStyles.set(xFamilies->getByName(aParaStyles),
                UNO_QUERY);
        }

        static constexpr OUString aCharStyles(u"CharacterStyles"_ustr);
        if( xFamilies->hasByName( aCharStyles ) )
        {
            m_xImpl->m_xTextStyles.set(xFamilies->getByName(aCharStyles),
                UNO_QUERY);
        }

        static constexpr OUString aNumStyles(u"NumberingStyles"_ustr);
        if( xFamilies->hasByName( aNumStyles ) )
        {
            m_xImpl->m_xNumStyles.set(xFamilies->getByName(aNumStyles),
                UNO_QUERY);
        }

        static constexpr OUString aFrameStyles(u"FrameStyles"_ustr);
        if( xFamilies->hasByName( aFrameStyles ) )
        {
            m_xImpl->m_xFrameStyles.set(xFamilies->getByName(aFrameStyles),
                UNO_QUERY);
        }

        static constexpr OUString aPageStyles(u"PageStyles"_ustr);
        if( xFamilies->hasByName( aPageStyles ) )
        {
            m_xImpl->m_xPageStyles.set(xFamilies->getByName(aPageStyles),
                UNO_QUERY);
        }

        static constexpr OUString aCellStyles(u"CellStyles"_ustr);
        if( xFamilies->hasByName( aCellStyles ) )
        {
            m_xImpl->m_xCellStyles.set(xFamilies->getByName(aCellStyles),
                UNO_QUERY);
        }
    }

    Reference < XTextFramesSupplier > xTFS( rModel, UNO_QUERY );
    if( xTFS.is() )
    {
        m_xImpl->m_xTextFrames.set(xTFS->getTextFrames());
    }

    Reference < XTextGraphicObjectsSupplier > xTGOS( rModel, UNO_QUERY );
    if( xTGOS.is() )
    {
        m_xImpl->m_xGraphics.set(xTGOS->getGraphicObjects());
    }

    Reference < XTextEmbeddedObjectsSupplier > xTEOS( rModel, UNO_QUERY );
    if( xTEOS.is() )
    {
        m_xImpl->m_xObjects.set(xTEOS->getEmbeddedObjects());
    }

    XMLPropertySetMapper *pPropMapper =
            new XMLTextPropertySetMapper( TextPropMap::PARA, false );
    m_xImpl->m_xParaImpPrMap =
        std::make_unique<XMLTextImportPropertyMapper>( pPropMapper, rImport );

    pPropMapper = new XMLTextPropertySetMapper( TextPropMap::TEXT, false );
    m_xImpl->m_xTextImpPrMap =
        std::make_unique<XMLTextImportPropertyMapper>( pPropMapper, rImport );

    pPropMapper = new XMLTextPropertySetMapper( TextPropMap::FRAME, false );
    m_xImpl->m_xFrameImpPrMap =
        std::make_unique<XMLTextImportPropertyMapper>( pPropMapper, rImport );

    pPropMapper = new XMLTextPropertySetMapper( TextPropMap::SECTION, false );
    m_xImpl->m_xSectionImpPrMap =
        std::make_unique<XMLTextImportPropertyMapper>( pPropMapper, rImport );

    pPropMapper = new XMLTextPropertySetMapper( TextPropMap::RUBY, false );
    m_xImpl->m_xRubyImpPrMap =
        std::make_unique<SvXMLImportPropertyMapper>( pPropMapper, rImport );
}

XMLTextImportHelper::~XMLTextImportHelper()
{
}

void XMLTextImportHelper::dispose()
{
    if (m_xImpl->m_xAutoStyles)
        m_xImpl->m_xAutoStyles->dispose();
}

std::unique_ptr<SvXMLImportPropertyMapper> XMLTextImportHelper::CreateShapeExtPropMapper(SvXMLImport& rImport)
{
    XMLPropertySetMapper *pPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::FRAME, false );
    return std::make_unique<XMLTextImportPropertyMapper>( pPropMapper, rImport );
}

std::unique_ptr<SvXMLImportPropertyMapper> XMLTextImportHelper::CreateParaExtPropMapper(SvXMLImport& rImport)
{
    XMLPropertySetMapper *pPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::SHAPE_PARA, false );
    return std::make_unique<XMLTextImportPropertyMapper>( pPropMapper, rImport );
}

std::unique_ptr<SvXMLImportPropertyMapper> XMLTextImportHelper::CreateParaDefaultExtPropMapper(SvXMLImport& rImport)
{
    XMLPropertySetMapper* pPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::SHAPE_PARA, false );
    std::unique_ptr<SvXMLImportPropertyMapper> pImportMapper(new XMLTextImportPropertyMapper( pPropMapper, rImport ));

    pPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::TEXT_ADDITIONAL_DEFAULTS, false );
    pImportMapper->ChainImportMapper( std::make_unique<XMLTextImportPropertyMapper>( pPropMapper, rImport ) );

    return pImportMapper;
}

std::unique_ptr<SvXMLImportPropertyMapper>
    XMLTextImportHelper::CreateTableDefaultExtPropMapper(
        SvXMLImport& rImport )
{
    XMLPropertySetMapper *pPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::TABLE_DEFAULTS, false );
    return std::make_unique<SvXMLImportPropertyMapper>( pPropMapper, rImport );
}

std::unique_ptr<SvXMLImportPropertyMapper>
    XMLTextImportHelper::CreateTableRowDefaultExtPropMapper(
        SvXMLImport& rImport )
{
    XMLPropertySetMapper *pPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::TABLE_ROW_DEFAULTS, false );
    return std::make_unique<SvXMLImportPropertyMapper>( pPropMapper, rImport );
}

std::unique_ptr<SvXMLImportPropertyMapper>
    XMLTextImportHelper::CreateTableCellExtPropMapper(
        SvXMLImport& rImport )
{
    XMLPropertySetMapper *pPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::CELL, false );
    return std::make_unique<XMLTextImportPropertyMapper>( pPropMapper, rImport );
}

std::unique_ptr<SvXMLImportPropertyMapper>
XMLTextImportHelper::CreateDrawingPageExtPropMapper(SvXMLImport& rImport)
{
    rtl::Reference<XMLPropertyHandlerFactory> const pFactory(new XMLPageMasterPropHdlFactory);
    XMLPropertySetMapper *const pPropMapper(
        new XMLPropertySetMapper(g_XMLPageMasterDrawingPageStyleMap, pFactory, false));
    return std::make_unique<SvXMLImportPropertyMapper>(pPropMapper, rImport);
}

void XMLTextImportHelper::SetCursor( const Reference < XTextCursor > & rCursor )
{
    m_xImpl->m_xCursor.set(rCursor);
    m_xImpl->m_xText.set(rCursor->getText());
    m_xImpl->m_xCursorAsRange = rCursor;
}

void XMLTextImportHelper::ResetCursor()
{
    m_xImpl->m_xCursor.clear();
    m_xImpl->m_xText.clear();
    m_xImpl->m_xCursorAsRange.clear();
}


bool XMLTextImportHelper::HasFrameByName( const OUString& rName ) const
{
    return (m_xImpl->m_xTextFrames.is() &&
            m_xImpl->m_xTextFrames->hasByName(rName))
        || (m_xImpl->m_xGraphics.is() &&
            m_xImpl->m_xGraphics->hasByName(rName))
        || (m_xImpl->m_xObjects.is() &&
            m_xImpl->m_xObjects->hasByName(rName));
}

bool XMLTextImportHelper::IsDuplicateFrame(const OUString& sName, sal_Int32 nX, sal_Int32 nY, sal_Int32 nWidth, sal_Int32 nHeight) const
{
    if (HasFrameByName(sName))
    {
        uno::Reference<beans::XPropertySet> xOtherFrame;
        if(m_xImpl->m_xTextFrames.is() && m_xImpl->m_xTextFrames->hasByName(sName))
            xOtherFrame.set(m_xImpl->m_xTextFrames->getByName(sName), uno::UNO_QUERY);
        else if(m_xImpl->m_xGraphics.is() && m_xImpl->m_xGraphics->hasByName(sName))
            xOtherFrame.set(m_xImpl->m_xGraphics->getByName(sName), uno::UNO_QUERY);
        else if (m_xImpl->m_xObjects.is() && m_xImpl->m_xObjects->hasByName(sName))
            xOtherFrame.set(m_xImpl->m_xObjects->getByName(sName), uno::UNO_QUERY);

        Reference< XPropertySetInfo > xPropSetInfo = xOtherFrame->getPropertySetInfo();
        if(xPropSetInfo->hasPropertyByName(u"Width"_ustr))
        {
            sal_Int32 nOtherWidth = 0;
            xOtherFrame->getPropertyValue(u"Width"_ustr) >>= nOtherWidth;
            if(nWidth != nOtherWidth)
                return false;
        }

        if (xPropSetInfo->hasPropertyByName(u"Height"_ustr))
        {
            sal_Int32 nOtherHeight = 0;
            xOtherFrame->getPropertyValue(u"Height"_ustr) >>= nOtherHeight;
            if (nHeight != nOtherHeight)
                return false;
        }

        if (xPropSetInfo->hasPropertyByName(u"HoriOrientPosition"_ustr))
        {
            sal_Int32 nOtherX = 0;
            xOtherFrame->getPropertyValue(u"HoriOrientPosition"_ustr) >>= nOtherX;
            if (nX != nOtherX)
                return false;
        }

        if (xPropSetInfo->hasPropertyByName(u"VertOrientPosition"_ustr))
        {
            sal_Int32 nOtherY = 0;
            xOtherFrame->getPropertyValue(u"VertOrientPosition"_ustr) >>= nOtherY;
            if (nY != nOtherY)
                return false;
        }

        // In some case, position is not defined for frames, so check whether the two frames follow each other (are anchored to the same position)
        return m_xImpl->msLastImportedFrameName == sName;
    }
    return false;
}

void XMLTextImportHelper::StoreLastImportedFrameName(const OUString& rName)
{
    m_xImpl->msLastImportedFrameName = rName;
}

void XMLTextImportHelper::ClearLastImportedTextFrameName()
{
    m_xImpl->msLastImportedFrameName.clear();
}

void XMLTextImportHelper::InsertString( const OUString& rChars )
{
    assert(m_xImpl->m_xText.is());
    assert(m_xImpl->m_xCursorAsRange.is());
    if (m_xImpl->m_xText.is())
    {
        m_xImpl->m_xText->insertString(m_xImpl->m_xCursorAsRange,
            rChars, false);
    }
}

void XMLTextImportHelper::InsertString( std::u16string_view rChars,
                                        bool& rIgnoreLeadingSpace )
{
    assert(m_xImpl->m_xText.is());
    assert(m_xImpl->m_xCursorAsRange.is());
    if (!m_xImpl->m_xText.is())
        return;

    sal_Int32 nLen = rChars.size();
    OUStringBuffer sChars( nLen );

    for( sal_Int32 i=0; i < nLen; i++ )
    {
        sal_Unicode c = rChars[i];
        switch( c )
        {
            case 0x20:
            case 0x09:
            case 0x0a:
            case 0x0d:
                if( !rIgnoreLeadingSpace )
                    sChars.append( u' ' );
                rIgnoreLeadingSpace = true;
                break;
            default:
                rIgnoreLeadingSpace = false;
                sChars.append( c );
                break;
        }
    }
    m_xImpl->m_xText->insertString(m_xImpl->m_xCursorAsRange,
                                   sChars.makeStringAndClear(), false);
}

void XMLTextImportHelper::InsertControlCharacter( sal_Int16 nControl )
{
    assert(m_xImpl->m_xText.is());
    assert(m_xImpl->m_xCursorAsRange.is());
    if (m_xImpl->m_xText.is())
    {
        m_xImpl->m_xText->insertControlCharacter(
            m_xImpl->m_xCursorAsRange, nControl, false);
    }
}

void XMLTextImportHelper::InsertTextContent(
    Reference < XTextContent > const & xContent )
{
    assert(m_xImpl->m_xText.is());
    assert(m_xImpl->m_xCursorAsRange.is());
    if (m_xImpl->m_xText.is())
    {
        // note: this may throw IllegalArgumentException and callers handle it
        m_xImpl->m_xText->insertTextContent( m_xImpl->m_xCursorAsRange, xContent, false);
    }
}

void XMLTextImportHelper::DeleteParagraph(bool dontCorrectBookmarks)
{
    assert(m_xImpl->m_xText.is());
    assert(m_xImpl->m_xCursor.is());
    assert(m_xImpl->m_xCursorAsRange.is());

    bool bDelete = true;
    Reference < XEnumerationAccess > const xEnumAccess(
        m_xImpl->m_xCursor, UNO_QUERY);
    if( xEnumAccess.is() )
    {
        Reference < XEnumeration > xEnum(xEnumAccess->createEnumeration());
        SAL_WARN_IF(!xEnum->hasMoreElements(), "xmloff.text",
                "empty text enumeration");
        if( xEnum->hasMoreElements() )
        {
            Reference < XComponent > xComp( xEnum->nextElement(), UNO_QUERY );
            assert(xComp.is());
            if( xComp.is() )
            {
                if (dontCorrectBookmarks)
                {
                    try
                    {
                        // See SwXParagraph::setPropertyValue
                        if (auto xProps = xComp.query<beans::XPropertySet>())
                            xProps->setPropertyValue(u"DeleteWithoutCorrection"_ustr, {});
                    }
                    catch (const beans::UnknownPropertyException&)
                    {
                    }
                }
                xComp->dispose();
                bDelete = false;
            }
        }
    }
    if( bDelete )
    {
        if (m_xImpl->m_xCursor->goLeft( 1, true ))
        {
            m_xImpl->m_xText->insertString(m_xImpl->m_xCursorAsRange,
                                           u""_ustr, true);
        }
    }
}

OUString XMLTextImportHelper::ConvertStarFonts( const OUString& rChars,
                                                const OUString& rStyleName,
                                                sal_uInt8& rFlags,
                                                bool bPara,
                                                SvXMLImport& rImport ) const
{
    OUStringBuffer sChars( rChars );
    bool bConverted = false;
    for( sal_Int32 j=0; j<rChars.getLength(); j++ )
    {
        sal_Unicode c = rChars[j];
        if( c >= 0xf000 && c <= 0xf0ff )
        {
            if( (rFlags & CONV_STAR_FONT_FLAGS_VALID) == 0 )
            {
                XMLTextStyleContext *pStyle = nullptr;
                XmlStyleFamily nFamily = bPara ? XmlStyleFamily::TEXT_PARAGRAPH
                                               : XmlStyleFamily::TEXT_TEXT;
                if (!rStyleName.isEmpty() && m_xImpl->m_xAutoStyles.is())
                {
                    const SvXMLStyleContext* pTempStyle =
                        m_xImpl->m_xAutoStyles->
                                FindStyleChildContext( nFamily, rStyleName,
                                                       true );
                    pStyle = const_cast<XMLTextStyleContext*>( dynamic_cast< const XMLTextStyleContext* >(pTempStyle));
                }

                if( pStyle )
                {
                    sal_Int32 nCount = pStyle->GetProperties_().size();
                    if( nCount )
                    {
                        SvXMLImportPropertyMapper* pImpPrMap =
                            m_xImpl->m_xAutoStyles->GetImportPropertyMapper(nFamily);
                        if( pImpPrMap )
                        {
                            rtl::Reference<XMLPropertySetMapper> rPropMapper =
                                pImpPrMap->getPropertySetMapper();
                            for( sal_Int32 i=0; i < nCount; i++ )
                            {
                                const XMLPropertyState& rProp = pStyle->GetProperties_()[i];
                                sal_Int32 nIdx = rProp.mnIndex;
                                sal_uInt32 nContextId = rPropMapper->GetEntryContextId(nIdx);
                                if( CTF_FONTFAMILYNAME == nContextId )
                                {
                                    rFlags &= ~(CONV_FROM_STAR_BATS|CONV_FROM_STAR_MATH);
                                    OUString sFontName;
                                    rProp.maValue >>= sFontName;
                                    if( sFontName.equalsIgnoreAsciiCase( "StarBats"  ) )
                                        rFlags |= CONV_FROM_STAR_BATS;
                                    else if( sFontName.equalsIgnoreAsciiCase( "StarMath" ) )
                                        rFlags |= CONV_FROM_STAR_MATH;
                                    break;
                                }
                            }
                        }
                    }

                }

                rFlags |= CONV_STAR_FONT_FLAGS_VALID;
            }
            if( (rFlags & CONV_FROM_STAR_BATS ) != 0 )
            {
                sChars[j] = rImport.ConvStarBatsCharToStarSymbol( c );
                bConverted = true;
            }
            else if( (rFlags & CONV_FROM_STAR_MATH ) != 0 )
            {
                sChars[j] = rImport.ConvStarMathCharToStarSymbol( c );
                bConverted = true;
            }
        }
    }

    return bConverted ? sChars.makeStringAndClear() : rChars;
}

/* Helper method to determine, if a paragraph style has a list style (inclusive
   an empty one) inherits a list style (inclusive an empty one) from one of its parents (#i69629#)
*/
/* Apply special case, that found list style equals the chapter numbering, also
   to the found list styles of the parent styles. (#i73973#)
*/
static bool lcl_HasListStyle( const OUString& sStyleName,
                              const Reference < XNameContainer >& xParaStyles,
                              SvXMLImport const & rImport,
                              const OUString& sNumberingStyleName,
                              std::u16string_view sOutlineStyleName )
{
    bool bRet( false );

    if ( !xParaStyles->hasByName( sStyleName ) )
    {
        // error case
        return true;
    }

    Reference< XPropertyState > xPropState( xParaStyles->getByName( sStyleName ),
                                            UNO_QUERY );
    if ( !xPropState.is() )
    {
        // error case
        return false;
    }

    if ( xPropState->getPropertyState( sNumberingStyleName ) == PropertyState_DIRECT_VALUE )
    {
        // list style found
        bRet = true;
        // special case: the set list style equals the chapter numbering
        Reference< XPropertySet > xPropSet( xPropState, UNO_QUERY );
        if ( xPropSet.is() )
        {
            OUString sListStyle;
            xPropSet->getPropertyValue( sNumberingStyleName ) >>= sListStyle;
            if ( !sListStyle.isEmpty() &&
                 sListStyle == sOutlineStyleName )
            {
                bRet = false;
            }
        }
    }
    else
    {
        // Tools.Outline settings lost on Save (#i77708#)
        sal_Int32 nUPD( 0 );
        sal_Int32 nBuild( 0 );
        // Don't use UPD for versioning: xmloff/source/text/txtstyli.cxx and txtimp.cxx (#i86058#)
        const bool bBuildIdFound = rImport.getBuildIds( nUPD, nBuild );
        // search list style at parent
        Reference<XStyle> xStyle( xPropState, UNO_QUERY );
        while ( xStyle.is() )
        {
            OUString aParentStyle( xStyle->getParentStyle() );
            if ( !aParentStyle.isEmpty() )
            {
                aParentStyle =
                    rImport.GetStyleDisplayName( XmlStyleFamily::TEXT_PARAGRAPH,
                                                 aParentStyle );
            }
            if ( aParentStyle.isEmpty() || !xParaStyles->hasByName( aParentStyle ) )
            {
                // no list style found
                break;
            }
            else
            {
                xPropState.set( xParaStyles->getByName( aParentStyle ),
                                UNO_QUERY );
                if ( !xPropState.is() )
                {
                    // error case
                    return true;
                }
                if ( xPropState->getPropertyState( sNumberingStyleName ) == PropertyState_DIRECT_VALUE )
                {
                    // list style found
                    bRet = true;
                    // Special case: the found list style equals the chapter numbering (#i73973#)
                    Reference< XPropertySet > xPropSet( xPropState, UNO_QUERY );
                    if ( xPropSet.is() )
                    {
                        OUString sListStyle;
                        xPropSet->getPropertyValue( sNumberingStyleName ) >>= sListStyle;
                        if ( !sListStyle.isEmpty() &&
                             sListStyle == sOutlineStyleName )
                        {
                            bRet = false;
                        }
                        // Special handling for text documents from OOo version prior OOo 2.4 (#i77708#)
                        /* Check explicitly on certain versions and on import of
                           text documents in OpenOffice.org file format (#i86058#)
                        */
                        else if ( sListStyle.isEmpty() &&
                                  ( rImport.IsTextDocInOOoFileFormat() ||
                                    ( bBuildIdFound &&
                                      ( ( nUPD == 641 ) || ( nUPD == 645 ) || // prior OOo 2.0
                                        ( nUPD == 680 && nBuild <= 9238 ) ) ) ) ) // OOo 2.0 - OOo 2.3.1
                        {
                            bRet = false;
                        }
                    }
                    break;
                }
                else
                {
                    // search list style at parent
                    Reference<XStyle> xParentStyle(xPropState, UNO_QUERY);
                    if (xStyle == xParentStyle)
                    {
                        // error case
                        return true;
                    }
                    xStyle = std::move(xParentStyle);
                }
            }
        }
    }

    return bRet;
}

OUString XMLTextImportHelper::SetStyleAndAttrs(
        const SvXMLImport & rImport,
        const Reference < XTextCursor >& rCursor,
        const OUString& rStyleName,
        bool bPara,
        bool bOutlineLevelAttrFound,
        sal_Int8 nOutlineLevel,
        // Numberings/Bullets in table not visible after save/reload (#i80724#)
        bool bSetListAttrs,
        bool bOutlineContentVisible)
{
    static constexpr OUString s_NumberingRules = u"NumberingRules"_ustr;
    static constexpr OUString s_NumberingIsNumber = u"NumberingIsNumber"_ustr;
    static constexpr OUString s_NumberingLevel = u"NumberingLevel"_ustr;
    static constexpr OUString s_ParaIsNumberingRestart = u"ParaIsNumberingRestart"_ustr;
    static constexpr OUString s_NumberingStartValue = u"NumberingStartValue"_ustr;
    static constexpr OUString s_PropNameListId = u"ListId"_ustr;
    static constexpr OUString s_PageDescName = u"PageDescName"_ustr;
    static constexpr OUString s_OutlineLevel = u"OutlineLevel"_ustr;

    const XmlStyleFamily nFamily = bPara ? XmlStyleFamily::TEXT_PARAGRAPH
                                         : XmlStyleFamily::TEXT_TEXT;
    XMLTextStyleContext *pStyle = nullptr;
    OUString sStyleName( rStyleName );
    if (!sStyleName.isEmpty() && m_xImpl->m_xAutoStyles.is())
    {
        const SvXMLStyleContext* pTempStyle =
            m_xImpl->m_xAutoStyles->FindStyleChildContext( nFamily, sStyleName, true );
        pStyle = const_cast<XMLTextStyleContext*>(dynamic_cast< const XMLTextStyleContext* >(pTempStyle));
    }
    if( pStyle )
        sStyleName = pStyle->GetParentName();

    Reference < XPropertySet > xPropSet( rCursor, UNO_QUERY );
    Reference< XPropertySetInfo > xPropSetInfo(
        xPropSet->getPropertySetInfo());

    // style
    if( !sStyleName.isEmpty() )
    {
        sStyleName = rImport.GetStyleDisplayName( nFamily, sStyleName );
        const OUString rPropName = bPara ? u"ParaStyleName"_ustr : u"CharStyleName"_ustr;
        const Reference < XNameContainer > & rStyles = bPara
            ? m_xImpl->m_xParaStyles
            : m_xImpl->m_xTextStyles;
        if( rStyles.is() &&
            xPropSetInfo->hasPropertyByName( rPropName ) &&
            rStyles->hasByName( sStyleName ) )
        {
            xPropSet->setPropertyValue( rPropName, Any(sStyleName) );
        }
        else
            sStyleName.clear();
    }

    /* The outline level needs to be only applied as list level, if the heading
       is not inside a list and if it by default applies the outline style. (#i70748#)
    */
    bool bApplyOutlineLevelAsListLevel( false );
    // Numberings/Bullets in table not visible after save/reload (#i80724#)
    if (bSetListAttrs && bPara
        && xPropSetInfo->hasPropertyByName( s_NumberingRules))
    {
        // Set numbering rules
        Reference< XIndexReplace > const xNumRules(
                xPropSet->getPropertyValue(s_NumberingRules), UNO_QUERY);

        XMLTextListBlockContext * pListBlock(nullptr);
        XMLTextListItemContext  * pListItem(nullptr);
        XMLNumberedParaContext  * pNumberedParagraph(nullptr);
        GetTextListHelper().ListContextTop(
            pListBlock, pListItem, pNumberedParagraph);

        assert(!(pListBlock && pNumberedParagraph) && "XMLTextImportHelper::"
            "SetStyleAndAttrs: both list and numbered-paragraph???");

        Reference < XIndexReplace > xNewNumRules;
        sal_Int8 nLevel(-1);
        OUString sListId;
        sal_Int16 nStartValue(-1);
        bool bNumberingIsNumber(true);
        // Assure that list style of automatic paragraph style is applied at paragraph. (#i101349#)
        bool bApplyNumRules(pStyle && pStyle->IsListStyleSet());

        if (pListBlock) {

            if (!pListItem) {
                bNumberingIsNumber = false; // list-header
            }

            // consider text:style-override property of <text:list-item>
            xNewNumRules.set(
                (pListItem != nullptr && pListItem->HasNumRulesOverride())
                    ? pListItem->GetNumRulesOverride()
                    : pListBlock->GetNumRules() );
            nLevel = static_cast<sal_Int8>(pListBlock->GetLevel());

            if ( pListItem && pListItem->HasStartValue() ) {
               nStartValue = pListItem->GetStartValue();
            }

            // Inconsistent behavior regarding lists (#i92811#)
            sListId = m_xImpl->m_xTextListsHelper->GetListIdForListBlock(
                            *pListBlock);
        }
        else if (pNumberedParagraph)
        {
            xNewNumRules.set(pNumberedParagraph->GetNumRules());
            nLevel = static_cast<sal_Int8>(pNumberedParagraph->GetLevel());
            sListId = pNumberedParagraph->GetListId();
            nStartValue = pNumberedParagraph->GetStartValue();
        }


        if (pListBlock || pNumberedParagraph)
        {
            if (!bApplyNumRules)
            {
                bool bSameNumRules = xNewNumRules == xNumRules;
                if( !bSameNumRules && xNewNumRules.is() && xNumRules.is() )
                {
                    // If the interface pointers are different, then this does
                    // not mean that the num rules are different. Further tests
                    // are required then. However, if only one num rule is
                    // set, no tests are required of course.
                    Reference< XNamed > xNewNamed( xNewNumRules, UNO_QUERY );
                    Reference< XNamed > xNamed( xNumRules, UNO_QUERY );
                    if( xNewNamed.is() && xNamed.is() )
                    {
                        bSameNumRules = xNewNamed->getName() == xNamed->getName();
                    }
                    else
                    {
                        Reference< XAnyCompare > xNumRuleCompare( xNumRules, UNO_QUERY );
                        if( xNumRuleCompare.is() )
                        {
                            bSameNumRules = (xNumRuleCompare->compare( Any(xNumRules), Any(xNewNumRules) ) == 0);
                        }
                    }
                }
                bApplyNumRules = !bSameNumRules;
            }

            if ( bApplyNumRules )
            {
                // #102607# This may except when xNewNumRules contains
                // a Writer-NumRule-Implementation bug gets applied to
                // a shape. Since this may occur inside a document
                // (e.g. when edited), this must be handled
                // gracefully.
                try
                {
                    xPropSet->setPropertyValue(
                        s_NumberingRules, Any(xNewNumRules) );
                }
                catch(const Exception&)
                {
                    ; // I would really like to use a warning here,
                      // but I can't access the XMLErrorHandler from
                      // here.
                }
            }

            if (!bNumberingIsNumber &&
                xPropSetInfo->hasPropertyByName(s_NumberingIsNumber))
            {
                xPropSet->setPropertyValue(s_NumberingIsNumber, Any(false));
            }

            xPropSet->setPropertyValue( s_NumberingLevel, Any(nLevel) );

            if( pListBlock && pListBlock->IsRestartNumbering() )
            {
                // TODO: property missing
                if (xPropSetInfo->hasPropertyByName(s_ParaIsNumberingRestart))
                {
                    xPropSet->setPropertyValue(s_ParaIsNumberingRestart,
                                               Any(true) );
                }
                pListBlock->ResetRestartNumbering();
            }

            if ( 0 <= nStartValue &&
                xPropSetInfo->hasPropertyByName(s_NumberingStartValue))
            {
                xPropSet->setPropertyValue(s_NumberingStartValue,
                                           Any(nStartValue));
            }

            if (xPropSetInfo->hasPropertyByName(s_PropNameListId))
            {
                if (!sListId.isEmpty()) {
                    xPropSet->setPropertyValue(s_PropNameListId,
                        Any(sListId) );
                }
            }

            GetTextListHelper().SetListItem( nullptr );
        }
        else
        {
            /* If the paragraph is not in a list but its style, remove it from
               the list. Do not remove it, if the list of the style is
               the chapter numbering rule.
            */
            if( xNumRules.is() )
            {
                bool bRemove( true );
                // Special handling for document from OOo 2.x (#i70748#)
                sal_Int32 nUPD( 0 );
                sal_Int32 nBuild( 0 );
                const bool bBuildIdFound = rImport.getBuildIds( nUPD, nBuild );
                if ( ( bBuildIdFound && nUPD == 680 ) ||
                     !pStyle || !pStyle->IsListStyleSet() )
                {
                    if (m_xImpl->m_xChapterNumbering.is())
                    {
                        Reference< XNamed > xNumNamed( xNumRules, UNO_QUERY );
                        Reference< XNamed > const xChapterNumNamed (
                            m_xImpl->m_xChapterNumbering, UNO_QUERY);
                        if ( xNumNamed.is() && xChapterNumNamed.is() &&
                             xNumNamed->getName() == xChapterNumNamed->getName() )
                        {
                            bRemove = false;
                            // RFE: inserting headings into text documents (#i70748#)
                            bApplyOutlineLevelAsListLevel = true;
                        }
                    }
                }
                else
                {
                    SAL_INFO_IF(!pStyle->GetListStyle().isEmpty(),
                        "xmloff.text",
                        "automatic paragraph style with list style name, but paragraph not in list???");
                }
                if ( bRemove )
                {
                    xPropSet->setPropertyValue( s_NumberingRules, Any() );
                }
            }
        }
    }

    // hard paragraph properties
    if( pStyle )
    {
        pStyle->FillPropertySet( xPropSet );
        if( bPara && pStyle->HasMasterPageName() &&
            xPropSetInfo->hasPropertyByName(s_PageDescName))
        {
            OUString sDisplayName(
                rImport.GetStyleDisplayName(
                                XmlStyleFamily::MASTER_PAGE,
                                pStyle->GetMasterPageName()) );
            if( sDisplayName.isEmpty() ||
                (m_xImpl->m_xPageStyles.is() &&
                 m_xImpl->m_xPageStyles->hasByName( sDisplayName)))
            {
                xPropSet->setPropertyValue(s_PageDescName,
                        Any(sDisplayName));
            }
        }
        if( bPara && !pStyle->GetDropCapStyleName().isEmpty() &&
            m_xImpl->m_xTextStyles.is())
        {
            OUString sDisplayName(
                rImport.GetStyleDisplayName(
                                XmlStyleFamily::TEXT_TEXT,
                                pStyle->GetDropCapStyleName()) );
            if (m_xImpl->m_xTextStyles->hasByName(sDisplayName) &&
                xPropSetInfo->hasPropertyByName(u"DropCapCharStyleName"_ustr))
            {
                xPropSet->setPropertyValue(u"DropCapCharStyleName"_ustr, Any(sDisplayName));
            }
        }

        // combined characters special treatment
        if (!bPara && pStyle->HasCombinedCharactersLetter())
        {
            // insert combined characters text field
            if (m_xImpl->m_xServiceFactory.is())
            {
                uno::Reference<beans::XPropertySet> const xTmp(
                    m_xImpl->m_xServiceFactory->createInstance(
                        u"com.sun.star.text.TextField.CombinedCharacters"_ustr), UNO_QUERY);
                if( xTmp.is() )
                {
                    // fix cursor if larger than possible for
                    // combined characters field
                    if (rCursor->getString().getLength() >
                            MAX_COMBINED_CHARACTERS)
                    {
                        rCursor->gotoRange(rCursor->getStart(), false);
                        rCursor->goRight(MAX_COMBINED_CHARACTERS, true);
                    }

                    // set field value (the combined character string)
                    xTmp->setPropertyValue(u"Content"_ustr,
                        Any(rCursor->getString()));

                    // insert the field over it's original text
                    Reference<XTextContent> xTextContent(xTmp, UNO_QUERY);
                    if (m_xImpl->m_xText.is() && rCursor.is())
                    {
                        // #i107225# the combined characters need to be inserted first
                        // the selected text has to be removed afterwards
                        m_xImpl->m_xText->insertTextContent( rCursor->getStart(), xTextContent, true );

                        if( !rCursor->getString().isEmpty() )
                        {
                            try
                            {
                                uno::Reference< text::XTextCursor > xCrsr = rCursor->getText()->createTextCursorByRange( rCursor->getStart() );
                                xCrsr->goLeft( 1, true );
                                uno::Reference< beans::XPropertySet> xCrsrProperties( xCrsr, uno::UNO_QUERY_THROW );
                                //the hard properties of the removed text need to be applied to the combined characters field
                                pStyle->FillPropertySet( xCrsrProperties );
                                xCrsr->collapseToEnd();
                                xCrsr->gotoRange( rCursor->getEnd(), true );
                                xCrsr->setString( OUString() );
                            }
                            catch(const uno::Exception&)
                            {
                            }
                        }
                    }
                }
            }
        }
    }

    // outline level; set after list style has been set
    // Complete re-worked and corrected: (#i53198#)
    // - set outline level at paragraph
    // - set numbering level at paragraph, if none is already set
    // - assure that style is marked as an outline style for the corresponding
    //   outline level.
    // - DO NOT set type of numbering rule to outline.
    // - DO NOT set numbering rule directly at the paragraph.

    // Some minor rework and adjust access to paragraph styles (#i70748#)
    if ( bPara )
    {
        // Headings not numbered anymore in 3.1 (#i103817#)
        sal_Int16 nCurrentOutlineLevelInheritedFromParagraphStyle = 0;
        const bool bHasOutlineLevelProp(
            xPropSetInfo->hasPropertyByName(s_OutlineLevel));
        if ( bHasOutlineLevelProp )
        {
            xPropSet->getPropertyValue(s_OutlineLevel)
                >>= nCurrentOutlineLevelInheritedFromParagraphStyle;
        }
        if ( nOutlineLevel > 0 )
        {
            if ( bHasOutlineLevelProp )
            {
                // In case that the value equals the value of its paragraph style
                // attribute outline level, the paragraph attribute value is left unset
                if ( nCurrentOutlineLevelInheritedFromParagraphStyle != nOutlineLevel )
                {
                    xPropSet->setPropertyValue( s_OutlineLevel,
                        Any( static_cast<sal_Int16>(nOutlineLevel) ) );
                }
            }
            if (!bOutlineContentVisible)
            {
                uno::Sequence<beans::PropertyValue> aGrabBag;
                xPropSet->getPropertyValue(u"ParaInteropGrabBag"_ustr) >>= aGrabBag;
                sal_Int32 length = aGrabBag.getLength();
                aGrabBag.realloc(length + 1);
                auto pGrabBag = aGrabBag.getArray();
                pGrabBag[length].Name = "OutlineContentVisibleAttr";
                pGrabBag[length].Value <<= bool(bOutlineContentVisible);
                xPropSet->setPropertyValue(u"ParaInteropGrabBag"_ustr, uno::Any(aGrabBag));
            }
            // RFE: inserting headings into text documents (#i70748#)
            if ( bApplyOutlineLevelAsListLevel )
            {
                sal_Int16 nNumLevel = -1;
                xPropSet->getPropertyValue( s_NumberingLevel ) >>= nNumLevel;
                if ( nNumLevel == -1 ||
                     nNumLevel != (nOutlineLevel - 1) )
                {
                    xPropSet->setPropertyValue( s_NumberingLevel,
                            Any( static_cast<sal_Int8>(nOutlineLevel - 1) ) );
                }
            }
            /* Correction: (#i69629#)
               - for text document from version OOo 2.0.4/SO 8 PU4 and earlier
                 the paragraph style of a heading should be assigned to the
                 corresponding list level of the outline style.
               - for other text documents the paragraph style of a heading is only
                 a candidate for an assignment to the list level of the outline
                 style, if it has no direct list style property and (if exists) the
                 automatic paragraph style has also no direct list style set.
            */
            if (m_xImpl->m_xParaStyles.is() && m_xImpl->m_xParaStyles->hasByName(sStyleName))
            {
                bool bOutlineStyleCandidate( false );

                sal_Int32 nUPD( 0 );
                sal_Int32 nBuild( 0 );
                const bool bBuildIdFound = rImport.getBuildIds( nUPD, nBuild );
                // Lost outline numbering in master document (#i73509#)
                // Check explicitly on certain versions (#i86058#)
                if ( rImport.IsTextDocInOOoFileFormat() ||
                     ( bBuildIdFound &&
                       ( nUPD == 645 || nUPD == 641 ) ) )
                {
                    bOutlineStyleCandidate = true;
                }
                else if ( nUPD == 680 && nBuild <= 9073 ) /* BuildId of OOo 2.0.4/SO8 PU4 */
                {
                    bOutlineStyleCandidate = bOutlineLevelAttrFound;
                }
                if ( bOutlineStyleCandidate )
                {
                    AddOutlineStyleCandidate( nOutlineLevel, sStyleName );
                }
                // Assure that heading applies the outline style (#i103817#)
                if ( ( !pStyle || !pStyle->IsListStyleSet() ) &&
                     !bOutlineStyleCandidate &&
                     m_xImpl->m_xChapterNumbering.is())
                {
                    if ( !lcl_HasListStyle( sStyleName,
                                    m_xImpl->m_xParaStyles, GetXMLImport(),
                                    u"NumberingStyleName"_ustr,
                                    u"" ) )
                    {
                        // heading not in a list --> apply outline style
                        xPropSet->setPropertyValue( s_NumberingRules,
                            Any(m_xImpl->m_xChapterNumbering) );
                        xPropSet->setPropertyValue( s_NumberingLevel,
                            Any(static_cast<sal_Int8>(nOutlineLevel - 1)));
                    }
                }
            }
        }
        //handle for text:p,if the paragraphstyle outlinelevel is set to[1~10]
        else if( bHasOutlineLevelProp )
        {
            if ( nCurrentOutlineLevelInheritedFromParagraphStyle != 0 )
            {
                xPropSet->setPropertyValue(s_OutlineLevel,
                    Any( sal_Int16(0) ));
            }
        }
    }

    return sStyleName;
}

void XMLTextImportHelper::FindOutlineStyleName( OUString& rStyleName,
                                                sal_Int8 nOutlineLevel )
{
    // style name empty?
    if( !rStyleName.isEmpty() )
        return;

    // Empty? Then we need o do stuff. Let's do error checking first.
    if (m_xImpl->m_xChapterNumbering.is() &&
        ( nOutlineLevel > 0 ) &&
        (nOutlineLevel <= m_xImpl->m_xChapterNumbering->getCount()))
    {
        nOutlineLevel--;   // for the remainder, the level's are 0-based

        // empty style name: look-up previously used name

        // if we don't have a previously used name, we'll use the default
        m_xImpl->InitOutlineStylesCandidates();
        if (m_xImpl->m_xOutlineStylesCandidates[nOutlineLevel].empty())
        {
            // no other name used previously? Then use default

            // iterate over property value sequence to find the style name
            Sequence<PropertyValue> aProperties;
            m_xImpl->m_xChapterNumbering->getByIndex( nOutlineLevel )
                >>= aProperties;
            auto pProp = std::find_if(std::cbegin(aProperties), std::cend(aProperties),
                [](const PropertyValue& rProp) { return rProp.Name == "HeadingStyleName"; });
            if (pProp != std::cend(aProperties))
            {
                OUString aOutlineStyle;
                pProp->Value >>= aOutlineStyle;
                m_xImpl->m_xOutlineStylesCandidates[nOutlineLevel]
                    .push_back( aOutlineStyle );
            }
        }

        // finally, we'll use the previously used style name for this
        // format (or the default we've just put into that style)
        // take last added one (#i71249#)
        rStyleName =
            m_xImpl->m_xOutlineStylesCandidates[nOutlineLevel].back();
    }
    // else: nothing we can do, so we'll leave it empty
    // else: we already had a style name, so we let it pass.
}

void XMLTextImportHelper::AddOutlineStyleCandidate( const sal_Int8 nOutlineLevel,
                                                    const OUString& rStyleName )
{
    if (!rStyleName.isEmpty()
        && m_xImpl->m_xChapterNumbering.is()
        && (nOutlineLevel > 0)
        && (nOutlineLevel <= m_xImpl->m_xChapterNumbering->getCount()))
    {
        m_xImpl->InitOutlineStylesCandidates();
        m_xImpl->m_xOutlineStylesCandidates[nOutlineLevel-1].push_back(
            rStyleName);
    }
}

void XMLTextImportHelper::SetOutlineStyles( bool bSetEmptyLevels )
{
    if (!(m_xImpl->m_xOutlineStylesCandidates != nullptr || bSetEmptyLevels) ||
        !m_xImpl->m_xChapterNumbering.is() ||
        IsInsertMode())
        return;

    bool bChooseLastOne( false );
    {
        if ( GetXMLImport().IsTextDocInOOoFileFormat() )
        {
            bChooseLastOne = true;
        }
        else
        {
            sal_Int32 nUPD( 0 );
            sal_Int32 nBuild( 0 );
            if ( GetXMLImport().getBuildIds( nUPD, nBuild ) )
            {
                // check explicitly on certain versions
                bChooseLastOne = ( nUPD == 641 ) || ( nUPD == 645 ) ||  // prior OOo 2.0
                                 ( nUPD == 680 && nBuild <= 9073 ); // OOo 2.0 - OOo 2.0.4
            }
        }
    }

    OUString sOutlineStyleName;
    {
        Reference<XPropertySet> xChapterNumRule(
            m_xImpl->m_xChapterNumbering, UNO_QUERY);
        xChapterNumRule->getPropertyValue(u"Name"_ustr) >>= sOutlineStyleName;
    }

    const sal_Int32 nCount = m_xImpl->m_xChapterNumbering->getCount();
    /* First collect all paragraph styles chosen for assignment to each
       list level of the outline style, then perform the intrinsic assignment.
       Reason: The assignment of a certain paragraph style to a list level
               of the outline style causes side effects on the children
               paragraph styles in Writer. (#i106218#)
    */
    ::std::vector<OUString> sChosenStyles(nCount);
    for( sal_Int32 i=0; i < nCount; ++i )
    {
        if ( bSetEmptyLevels ||
             (m_xImpl->m_xOutlineStylesCandidates &&
              !m_xImpl->m_xOutlineStylesCandidates[i].empty()))
        {
            // determine, which candidate is one to be assigned to the list
            // level of the outline style
            if (m_xImpl->m_xOutlineStylesCandidates &&
                !m_xImpl->m_xOutlineStylesCandidates[i].empty())
            {
                if ( bChooseLastOne )
                {
                    sChosenStyles[i] =
                    m_xImpl->m_xOutlineStylesCandidates[i].back();
                }
                else
                {
                    for (size_t j = 0;
                        j < m_xImpl->m_xOutlineStylesCandidates[i].size();
                        ++j)
                    {
                        if (!lcl_HasListStyle(
                                m_xImpl->m_xOutlineStylesCandidates[i][j],
                                m_xImpl->m_xParaStyles,
                                GetXMLImport(),
                                u"NumberingStyleName"_ustr,
                                sOutlineStyleName))
                        {
                            sChosenStyles[i] =
                                m_xImpl->m_xOutlineStylesCandidates[i][j];
                            break;
                        }
                    }
                }
            }
        }
    }
    // Trashed outline numbering in ODF 1.1 text document created by OOo 3.x (#i106218#)
    Sequence < PropertyValue > aProps( 1 );
    PropertyValue *pProps = aProps.getArray();
    pProps->Name = "HeadingStyleName";
    for ( sal_Int32 i = 0; i < nCount; ++i )
    {
        // Paragraph style assignments in Outline of template lost from second level on (#i107610#)
        if ( bSetEmptyLevels || !sChosenStyles[i].isEmpty() )
        {
            pProps->Value <<= sChosenStyles[i];
            m_xImpl->m_xChapterNumbering->replaceByIndex(i,
                    Any( aProps ));
        }
    }

}

void XMLTextImportHelper::SetHyperlink(
    SvXMLImport const & rImport,
    const Reference < XTextCursor >& rCursor,
    const OUString& rHRef,
    const OUString& rName,
    const OUString& rTargetFrameName,
    const OUString& rStyleName,
    const OUString& rVisitedStyleName,
    XMLEventsImportContext* pEvents)
{
    static constexpr OUString s_HyperLinkURL = u"HyperLinkURL"_ustr;
    static constexpr OUString s_HyperLinkName = u"HyperLinkName"_ustr;
    static constexpr OUString s_HyperLinkTarget = u"HyperLinkTarget"_ustr;
    static constexpr OUString s_UnvisitedCharStyleName = u"UnvisitedCharStyleName"_ustr;
    static constexpr OUString s_VisitedCharStyleName = u"VisitedCharStyleName"_ustr;
    static constexpr OUString s_HyperLinkEvents = u"HyperLinkEvents"_ustr;

    Reference < XPropertySet > xPropSet( rCursor, UNO_QUERY );
    Reference < XPropertySetInfo > xPropSetInfo(
        xPropSet->getPropertySetInfo());
    if (!xPropSetInfo.is() || !xPropSetInfo->hasPropertyByName(s_HyperLinkURL))
        return;

    xPropSet->setPropertyValue(s_HyperLinkURL, Any(rHRef));

    if (xPropSetInfo->hasPropertyByName(s_HyperLinkName))
    {
        xPropSet->setPropertyValue(s_HyperLinkName, Any(rName));
    }

    if (xPropSetInfo->hasPropertyByName(s_HyperLinkTarget))
    {
        xPropSet->setPropertyValue(s_HyperLinkTarget,
            Any(rTargetFrameName));
    }

    if ( (pEvents != nullptr) &&
        xPropSetInfo->hasPropertyByName(s_HyperLinkEvents))
    {
        // The API treats events at hyperlinks differently from most
        // other properties: You have to set a name replace with the
        // events in it. The easiest way to do this is to 1) get
        // events, 2) set new ones, and 3) then put events back.
        uno::Reference<XNameReplace> const xReplace(
            xPropSet->getPropertyValue(s_HyperLinkEvents), UNO_QUERY);
        if (xReplace.is())
        {
            // set events
            pEvents->SetEvents(xReplace);

            // put events
            xPropSet->setPropertyValue(s_HyperLinkEvents, Any(xReplace));
        }
    }

    if (!m_xImpl->m_xTextStyles.is())
        return;

    OUString sDisplayName(
        rImport.GetStyleDisplayName(
                        XmlStyleFamily::TEXT_TEXT, rStyleName ) );
    if( !sDisplayName.isEmpty() &&
        xPropSetInfo->hasPropertyByName(s_UnvisitedCharStyleName) &&
        m_xImpl->m_xTextStyles->hasByName(sDisplayName))
    {
        xPropSet->setPropertyValue(s_UnvisitedCharStyleName,
            Any(sDisplayName));
    }

    sDisplayName =
        rImport.GetStyleDisplayName(
                        XmlStyleFamily::TEXT_TEXT, rVisitedStyleName );
    if( !sDisplayName.isEmpty() &&
        xPropSetInfo->hasPropertyByName(s_VisitedCharStyleName) &&
        m_xImpl->m_xTextStyles->hasByName(sDisplayName))
    {
        xPropSet->setPropertyValue(s_VisitedCharStyleName,
            Any(sDisplayName));
    }
}

void XMLTextImportHelper::SetRuby(
    SvXMLImport const & rImport,
    const Reference < XTextCursor >& rCursor,
    const OUString& rStyleName,
    const OUString& rTextStyleName,
    const OUString& rText )
{
    Reference<XPropertySet> xPropSet(rCursor, UNO_QUERY);

    OUString sRubyText(u"RubyText"_ustr);

    // if we have one Ruby property, we assume all of them are present
    if (!xPropSet.is() ||
        !xPropSet->getPropertySetInfo()->hasPropertyByName( sRubyText ))
        return;

    // the ruby text
    xPropSet->setPropertyValue(sRubyText, Any(rText));

    // the ruby style (ruby-adjust)
    if (!rStyleName.isEmpty() && m_xImpl->m_xAutoStyles.is())
    {
        const SvXMLStyleContext* pTempStyle =
            m_xImpl->m_xAutoStyles->FindStyleChildContext( XmlStyleFamily::TEXT_RUBY,
                                   rStyleName, true );
        XMLPropStyleContext *pStyle = const_cast<XMLPropStyleContext*>(dynamic_cast< const XMLPropStyleContext* >(pTempStyle));

        if (nullptr != pStyle)
            pStyle->FillPropertySet( xPropSet );
    }

    // the ruby text character style
    if (m_xImpl->m_xTextStyles.is())
    {
        OUString sDisplayName(
            rImport.GetStyleDisplayName(
                        XmlStyleFamily::TEXT_TEXT, rTextStyleName ) );
        if( (!sDisplayName.isEmpty()) &&
            m_xImpl->m_xTextStyles->hasByName( sDisplayName ))
        {
            xPropSet->setPropertyValue(u"RubyCharStyleName"_ustr, Any(sDisplayName));
        }
    }
}

void XMLTextImportHelper::SetAutoStyles( SvXMLStylesContext *pStyles )
{
    m_xImpl->m_xAutoStyles = pStyles;
}

SvXMLImportContext *XMLTextImportHelper::CreateTextChildContext(
        SvXMLImport& rImport,
        sal_Int32 nElement,
        const Reference< XFastAttributeList > & xAttrList,
        XMLTextType eType )
{
    SvXMLImportContext *pContext = nullptr;

    bool bContent = true;
    switch( nElement )
    {
    case XML_ELEMENT(TEXT, XML_H):
    case XML_ELEMENT(TEXT, XML_P):
    case XML_ELEMENT(LO_EXT, XML_P):
        pContext = new XMLParaContext( rImport,
                                       nElement,
                                       xAttrList );
        if (m_xImpl->m_bProgress && XMLTextType::Shape != eType)
        {
            rImport.GetProgressBarHelper()->Increment();
        }
        break;
    // #i52127#
    case XML_ELEMENT(TEXT, XML_NUMBERED_PARAGRAPH):
        pContext = new XMLNumberedParaContext(
                        rImport, nElement, xAttrList );
        break;
    case XML_ELEMENT(TEXT, XML_LIST):
        pContext = new XMLTextListBlockContext( rImport, *this,
                                                xAttrList );
        break;
    case XML_ELEMENT(TABLE,XML_TABLE):
    case XML_ELEMENT(LO_EXT, XML_TABLE):
        if( XMLTextType::Body == eType ||
            XMLTextType::TextBox == eType ||
             XMLTextType::Section == eType ||
            XMLTextType::HeaderFooter == eType ||
            XMLTextType::ChangedRegion == eType ||
            XMLTextType::Cell == eType )
            pContext = CreateTableChildContext( rImport, nElement, xAttrList );
        break;
    case XML_ELEMENT(TEXT, XML_SEQUENCE_DECLS):
        if ((XMLTextType::Body == eType && m_xImpl->m_bBodyContentStarted) ||
            XMLTextType::HeaderFooter == eType )
        {
            pContext = new XMLVariableDeclsImportContext(
                rImport, *this, VarTypeSequence);
            bContent = false;
        }
        break;
    case XML_ELEMENT(TEXT, XML_VARIABLE_DECLS):
        if ((XMLTextType::Body == eType && m_xImpl->m_bBodyContentStarted) ||
            XMLTextType::HeaderFooter == eType )
        {
            pContext = new XMLVariableDeclsImportContext(
                rImport, *this, VarTypeSimple);
            bContent = false;
        }
        break;
    case XML_ELEMENT(TEXT, XML_USER_FIELD_DECLS):
        if ((XMLTextType::Body == eType && m_xImpl->m_bBodyContentStarted)||
            XMLTextType::HeaderFooter == eType )
        {
            pContext = new XMLVariableDeclsImportContext(
                rImport, *this, VarTypeUserField);
            bContent = false;
        }
        break;
    case XML_ELEMENT(TEXT, XML_DDE_CONNECTION_DECLS):
        if ((XMLTextType::Body == eType && m_xImpl->m_bBodyContentStarted) ||
            XMLTextType::HeaderFooter == eType )
        {
            pContext = new XMLDdeFieldDeclsImportContext(rImport);
            bContent = false;
        }
        break;
    case XML_ELEMENT(DRAW, XML_FRAME):
        if ((XMLTextType::Body == eType && m_xImpl->m_bBodyContentStarted) ||
            XMLTextType::TextBox == eType ||
            XMLTextType::ChangedRegion == eType )
        {
            TextContentAnchorType eAnchorType =
                XMLTextType::TextBox == eType ? TextContentAnchorType_AT_FRAME
                                               : TextContentAnchorType_AT_PAGE;
            pContext = new XMLTextFrameContext( rImport, xAttrList,
                                                eAnchorType );
            bContent = false;
        }
        break;
    case XML_ELEMENT(DRAW, XML_A):
        if ((XMLTextType::Body == eType && m_xImpl->m_bBodyContentStarted) ||
            XMLTextType::TextBox == eType ||
             XMLTextType::ChangedRegion == eType)
        {
            TextContentAnchorType eAnchorType =
                XMLTextType::TextBox == eType ? TextContentAnchorType_AT_FRAME
                                               : TextContentAnchorType_AT_PAGE;
            pContext = new XMLTextFrameHyperlinkContext( rImport, nElement,
                                                xAttrList,
                                                eAnchorType );
            bContent = false;
        }
        break;
    case XML_ELEMENT(TEXT, XML_INDEX_TITLE):
    case XML_ELEMENT(TEXT, XML_SECTION):
        pContext = new XMLSectionImportContext( rImport );
        break;
    case XML_ELEMENT(TEXT, XML_TABLE_OF_CONTENT):
    case XML_ELEMENT(TEXT, XML_OBJECT_INDEX):
    case XML_ELEMENT(TEXT, XML_TABLE_INDEX):
    case XML_ELEMENT(TEXT, XML_ILLUSTRATION_INDEX):
    case XML_ELEMENT(TEXT, XML_USER_INDEX):
    case XML_ELEMENT(TEXT, XML_ALPHABETICAL_INDEX):
    case XML_ELEMENT(TEXT, XML_BIBLIOGRAPHY):
        if( XMLTextType::Shape != eType )
            pContext = new XMLIndexTOCContext( rImport, nElement );
        break;
    case XML_ELEMENT(TEXT, XML_TRACKED_CHANGES):
        pContext = new XMLTrackedChangesImportContext( rImport );
        bContent = false;
        break;
    case XML_ELEMENT(TEXT, XML_CHANGE):
    case XML_ELEMENT(TEXT, XML_CHANGE_START):
    case XML_ELEMENT(TEXT, XML_CHANGE_END):
        pContext = new XMLChangeImportContext(
            rImport,
            ((nElement == XML_ELEMENT(TEXT, XML_CHANGE_END))
                ? XMLChangeImportContext::Element::END
                : (nElement == XML_ELEMENT(TEXT, XML_CHANGE_START))
                    ? XMLChangeImportContext::Element::START
                    : XMLChangeImportContext::Element::POINT),
            true);
        break;
    case XML_ELEMENT(OFFICE, XML_FORMS):
        pContext = xmloff::OFormLayerXMLImport::createOfficeFormsContext(rImport);
        bContent = false;
        break;
    case XML_ELEMENT(TEXT, XML_ALPHABETICAL_INDEX_AUTO_MARK_FILE):
        if( XMLTextType::Body == eType )
        {
            pContext = new XMLAutoMarkFileContext(rImport);
        }
        bContent = false;
        break;
    case XML_ELEMENT(TABLE, XML_CALCULATION_SETTINGS):
        pContext = new XMLCalculationSettingsContext ( rImport, nElement, xAttrList);
        bContent = false;
        break;

    default:
        if ((XMLTextType::Body == eType && m_xImpl->m_bBodyContentStarted) ||
            XMLTextType::TextBox == eType ||
             XMLTextType::ChangedRegion == eType )
        {
            Reference < XShapes > xShapes;
            pContext = XMLShapeImportHelper::CreateGroupChildContext(
                    rImport, nElement, xAttrList, xShapes );
            bContent = false;
        }
    }

    // handle open redlines
    if ( (XML_ELEMENT(TEXT, XML_CHANGE) != nElement) &&
         (XML_ELEMENT(TEXT, XML_CHANGE_END) != nElement) &&
         (XML_ELEMENT(TEXT, XML_CHANGE_START) != nElement) )
    {
//      ResetOpenRedlineId();
    }

    if( XMLTextType::Body == eType && bContent )
    {
        m_xImpl->m_bBodyContentStarted = false;
    }

    if( nElement != XML_ELEMENT(DRAW, XML_FRAME) )
        ClearLastImportedTextFrameName();

    if (!pContext)
        XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);

    return pContext;
}

SvXMLImportContext *XMLTextImportHelper::CreateTableChildContext(
        SvXMLImport&,
        sal_Int32 /*nElement*/,
        const Reference< XFastAttributeList > & )
{
    return nullptr;
}

/// get data style key for use with NumberFormat property
sal_Int32 XMLTextImportHelper::GetDataStyleKey(const OUString& sStyleName,
                                               bool* pIsSystemLanguage )
{
    if (!m_xImpl->m_xAutoStyles.is())
        return -1;

    const SvXMLStyleContext* pStyle =
        m_xImpl->m_xAutoStyles->FindStyleChildContext( XmlStyleFamily::DATA_STYLE,
                                              sStyleName, true );

    // get appropriate context


    // first check if it's an Impress and draw only number format
    // this is needed since it's also a SvXMLNumFormatContext,
    // that was needed to support them for controls in impress/draw also
    const SdXMLNumberFormatImportContext* pSdNumStyle = dynamic_cast<const SdXMLNumberFormatImportContext*>( pStyle  );
    if( pSdNumStyle )
    {
        return pSdNumStyle->GetDrawKey();
    }
    else
    {
        SvXMLNumFormatContext* pNumStyle = const_cast<SvXMLNumFormatContext*>(dynamic_cast<const SvXMLNumFormatContext*>( pStyle ) );
        if( pNumStyle )
        {
            if( pIsSystemLanguage != nullptr )
                *pIsSystemLanguage = pNumStyle->IsSystemLanguage();

            // return key
            return pNumStyle->GetKey();
        }
    }
    return -1;
}

const SvxXMLListStyleContext *XMLTextImportHelper::FindAutoListStyle( const OUString& rName ) const
{
    const SvxXMLListStyleContext *pStyle = nullptr;
    if (m_xImpl->m_xAutoStyles.is())
    {
        const SvXMLStyleContext* pTempStyle =
            m_xImpl->m_xAutoStyles->FindStyleChildContext( XmlStyleFamily::TEXT_LIST, rName,
                                           true );
        pStyle = dynamic_cast< const SvxXMLListStyleContext* >(pTempStyle);
    }

    return pStyle;
}

XMLPropStyleContext *XMLTextImportHelper::FindAutoFrameStyle( const OUString& rName ) const
{
    XMLPropStyleContext *pStyle = nullptr;
    if (m_xImpl->m_xAutoStyles.is())
    {
        const SvXMLStyleContext* pTempStyle =
            m_xImpl->m_xAutoStyles->FindStyleChildContext( XmlStyleFamily::SD_GRAPHICS_ID, rName,
                                           true );
        pStyle = const_cast<XMLPropStyleContext*>(dynamic_cast< const XMLPropStyleContext* >(pTempStyle));
    }

    return pStyle;
}

XMLPropStyleContext* XMLTextImportHelper::FindSectionStyle(
    const OUString& rName ) const
{
    XMLPropStyleContext* pStyle = nullptr;
    if (m_xImpl->m_xAutoStyles.is())
    {
        const SvXMLStyleContext* pTempStyle =
            m_xImpl->m_xAutoStyles->FindStyleChildContext(
                               XmlStyleFamily::TEXT_SECTION,
                               rName, true );
        pStyle = const_cast<XMLPropStyleContext*>(dynamic_cast< const XMLPropStyleContext* >(pTempStyle));
    }

    return pStyle;
}

XMLPropStyleContext* XMLTextImportHelper::FindPageMaster(
    const OUString& rName ) const
{
    XMLPropStyleContext* pStyle = nullptr;
    if (m_xImpl->m_xAutoStyles.is())
    {
        const SvXMLStyleContext* pTempStyle =
            m_xImpl->m_xAutoStyles->FindStyleChildContext(
                               XmlStyleFamily::PAGE_MASTER,
                               rName, true );
        pStyle = const_cast<XMLPropStyleContext*>(dynamic_cast< const XMLPropStyleContext* >(pTempStyle));
    }

    return pStyle;
}

XMLPropStyleContext* XMLTextImportHelper::FindAutoCharStyle(const OUString& rName) const
{
    if (!m_xImpl->m_xAutoStyles)
        return nullptr;
    auto pStyle
        = m_xImpl->m_xAutoStyles->FindStyleChildContext(XmlStyleFamily::TEXT_TEXT, rName, true);
    return dynamic_cast<XMLPropStyleContext*>(const_cast<SvXMLStyleContext*>(pStyle));
}

XMLPropStyleContext * XMLTextImportHelper::FindDrawingPage(OUString const& rName) const
{
    if (!m_xImpl->m_xAutoStyles.is())
    {
        return nullptr;
    }
    SvXMLStyleContext const* pStyle(
        m_xImpl->m_xAutoStyles->FindStyleChildContext(
               XmlStyleFamily::SD_DRAWINGPAGE_ID, rName, true));
    assert(pStyle == nullptr || dynamic_cast<XMLPropStyleContext const*>(pStyle) != nullptr);
    return const_cast<XMLPropStyleContext*>(static_cast<XMLPropStyleContext const*>(pStyle));
}

void XMLTextImportHelper::PushListContext()
{
    GetTextListHelper().PushListContext(static_cast<XMLTextListBlockContext*>(nullptr));
}

void XMLTextImportHelper::PopListContext()
{
    GetTextListHelper().PopListContext();
}


SvI18NMap& XMLTextImportHelper::GetRenameMap()
{
    if (!m_xImpl->m_xRenameMap)
    {
        m_xImpl->m_xRenameMap.reset( new SvI18NMap );
    }
    return *m_xImpl->m_xRenameMap;
}

void XMLTextImportHelper::InsertBookmarkStartRange(
    const OUString & sName,
    const Reference<XTextRange> & rRange,
    OUString const& i_rXmlId,
    std::shared_ptr< ::xmloff::ParsedRDFaAttributes > & i_rpRDFaAttributes)
{
    m_xImpl->m_BookmarkStartRanges[sName] =
        std::make_tuple(rRange, i_rXmlId, i_rpRDFaAttributes);
    m_xImpl->m_BookmarkVector.push_back(sName);
}

bool XMLTextImportHelper::FindAndRemoveBookmarkStartRange(
    const OUString & sName,
    Reference<XTextRange> & o_rRange,
    OUString & o_rXmlId,
    std::shared_ptr< ::xmloff::ParsedRDFaAttributes > & o_rpRDFaAttributes)
{
    if (m_xImpl->m_BookmarkStartRanges.count(sName))
    {
        Impl::BookmarkMapEntry_t & rEntry =
            (*m_xImpl->m_BookmarkStartRanges.find(sName)).second;
        o_rRange.set(std::get<0>(rEntry));
        o_rXmlId = std::get<1>(rEntry);
        o_rpRDFaAttributes = std::get<2>(rEntry);
        m_xImpl->m_BookmarkStartRanges.erase(sName);
        auto it = std::find(m_xImpl->m_BookmarkVector.begin(), m_xImpl->m_BookmarkVector.end(), sName);
        if (it!=m_xImpl->m_BookmarkVector.end())
        {
            m_xImpl->m_BookmarkVector.erase(it);
        }
        return true;
    }
    else
    {
        return false;
    }
}

void XMLTextImportHelper::pushFieldCtx( const OUString& name, const OUString& type )
{
    m_xImpl->m_FieldStack.push(Impl::field_stack_item_t(
        Impl::field_name_type_t(name, type), Impl::field_params_t(), uno::Reference<text::XFormField>{}, GetCursor()->getStart()));
}

uno::Reference<text::XFormField>
XMLTextImportHelper::popFieldCtx()
{
    uno::Reference<text::XFormField> xRet;
    if ( !m_xImpl->m_FieldStack.empty() )
    {
        xRet = std::get<2>(m_xImpl->m_FieldStack.top());
        m_xImpl->m_FieldStack.pop();
    }
    else
    {
        SAL_INFO("xmloff.text", "unexpected fieldmark end");
    }
    return xRet;
}

void XMLTextImportHelper::addFieldParam( const OUString& name, const OUString& value )
{
    assert(!m_xImpl->m_FieldStack.empty());
    Impl::field_stack_item_t & FieldStackItem(m_xImpl->m_FieldStack.top());
    std::get<1>(FieldStackItem).emplace_back( name, value );
}

::std::pair<OUString, OUString> XMLTextImportHelper::getCurrentFieldType() const
{
    assert(!m_xImpl->m_FieldStack.empty());
    return std::get<0>(m_xImpl->m_FieldStack.top());
}

uno::Reference<text::XTextRange> XMLTextImportHelper::getCurrentFieldStart() const
{
    assert(!m_xImpl->m_FieldStack.empty());
    return std::get<3>(m_xImpl->m_FieldStack.top());
}

bool XMLTextImportHelper::hasCurrentFieldSeparator() const
{
    assert(!m_xImpl->m_FieldStack.empty());
    return std::get<2>(m_xImpl->m_FieldStack.top()).is();
}

bool XMLTextImportHelper::hasCurrentFieldCtx() const
{
    return !m_xImpl->m_FieldStack.empty();
}

void XMLTextImportHelper::setCurrentFieldParamsTo(css::uno::Reference< css::text::XFormField> const &xFormField)
{
    assert(!m_xImpl->m_FieldStack.empty());
    if (xFormField.is())
    {
        FieldParamImporter(&std::get<1>(m_xImpl->m_FieldStack.top()),
            xFormField->getParameters()).Import();
        std::get<2>(m_xImpl->m_FieldStack.top()) = xFormField;
    }
}


void XMLTextImportHelper::ConnectFrameChains(
        const OUString& rFrmName,
        const OUString& rNextFrmName,
        const Reference < XPropertySet >& rFrmPropSet )
{
    if( rFrmName.isEmpty() )
        return;

    if( !rNextFrmName.isEmpty() )
    {
        OUString sNextFrmName(GetRenameMap().Get( XML_TEXT_RENAME_TYPE_FRAME,
                                                    rNextFrmName ));
        if (m_xImpl->m_xTextFrames.is()
            && m_xImpl->m_xTextFrames->hasByName(sNextFrmName))
        {
            rFrmPropSet->setPropertyValue(u"ChainNextName"_ustr,
                Any(sNextFrmName));
        }
        else
        {
            if (!m_xImpl->m_xPrevFrmNames)
            {
                m_xImpl->m_xPrevFrmNames.emplace();
                m_xImpl->m_xNextFrmNames.emplace();
            }
            m_xImpl->m_xPrevFrmNames->push_back(rFrmName);
            m_xImpl->m_xNextFrmNames->push_back(sNextFrmName);
        }
    }
    if (!m_xImpl->m_xPrevFrmNames || m_xImpl->m_xPrevFrmNames->empty())
        return;

    for(std::vector<OUString>::iterator i = m_xImpl->m_xPrevFrmNames->begin(), j = m_xImpl->m_xNextFrmNames->begin(); i != m_xImpl->m_xPrevFrmNames->end() && j != m_xImpl->m_xNextFrmNames->end(); ++i, ++j)
    {
        if((*j) == rFrmName)
        {
            // The previous frame must exist, because it existing than
            // inserting the entry
            rFrmPropSet->setPropertyValue(u"ChainPrevName"_ustr, Any(*i));

            i = m_xImpl->m_xPrevFrmNames->erase(i);
            j = m_xImpl->m_xNextFrmNames->erase(j);

            // There cannot be more than one previous frames
            break;
        }
    }
}

bool XMLTextImportHelper::IsInFrame() const
{
    static constexpr OUString s_TextFrame = u"TextFrame"_ustr;

    bool bIsInFrame = false;

    // are we currently in a text frame? yes, if the cursor has a
    // TextFrame property and it's non-NULL
    Reference<XPropertySet> xPropSet(const_cast<XMLTextImportHelper*>(this)->GetCursor(), UNO_QUERY);
    if (xPropSet.is())
    {
        if (xPropSet->getPropertySetInfo()->hasPropertyByName(s_TextFrame))
        {
            uno::Reference<XTextFrame> const xFrame(
                xPropSet->getPropertyValue(s_TextFrame), UNO_QUERY);

            if (xFrame.is())
            {
                bIsInFrame = true;
            }
        }
    }

    return bIsInFrame;
}

bool XMLTextImportHelper::IsInHeaderFooter() const
{
    return false;
}

Reference< XPropertySet> XMLTextImportHelper::createAndInsertOLEObject(
                                        SvXMLImport&,
                                        const OUString& /*rHRef*/,
                                        const OUString& /*rStyleName*/,
                                        const OUString& /*rTblName*/,
                                        sal_Int32 /*nWidth*/, sal_Int32 /*nHeight*/ )
{
    Reference< XPropertySet> xPropSet;
    return xPropSet;
}

Reference< XPropertySet> XMLTextImportHelper::createAndInsertOOoLink(
                                        SvXMLImport&,
                                        const OUString& /*rHRef*/,
                                        const OUString& /*rStyleName*/,
                                        const OUString& /*rTblName*/,
                                        sal_Int32 /*nWidth*/, sal_Int32 /*nHeight*/ )
{
    Reference< XPropertySet> xPropSet;
    return xPropSet;
}

Reference< XPropertySet> XMLTextImportHelper::createAndInsertApplet(
                                        const OUString& /*rCode*/,
                                        const OUString& /*rName*/,
                                        bool /*bMayScript*/,
                                        const OUString& /*rHRef*/,
                                        sal_Int32 /*nWidth*/, sal_Int32 /*nHeight*/ )
{
    Reference< XPropertySet> xPropSet;
    return xPropSet;
}
Reference< XPropertySet> XMLTextImportHelper::createAndInsertPlugin(
                                        const OUString& /*rMimeType*/,
                                        const OUString& /*rHRef*/,
                                        sal_Int32 /*nWidth*/, sal_Int32 /*nHeight*/ )
{
    Reference< XPropertySet> xPropSet;
    return xPropSet;
}
Reference< XPropertySet> XMLTextImportHelper::createAndInsertFloatingFrame(
                                        const OUString& /*rName*/,
                                        const OUString& /*rHRef*/,
                                        const OUString& /*rStyleName*/,
                                        sal_Int32 /*nWidth*/, sal_Int32 /*nHeight*/ )
{
    Reference< XPropertySet> xPropSet;
    return xPropSet;
}

void XMLTextImportHelper::endAppletOrPlugin(
        const Reference < XPropertySet> &,
        std::map < const OUString, OUString > &)
{
}
// redline helper: dummy implementation to be overridden in sw/filter/xml
void XMLTextImportHelper::RedlineAdd( const OUString& /*rType*/,
                                      const OUString& /*rId*/,
                                      const OUString& /*rAuthor*/,
                                      const OUString& /*rComment*/,
                                      const util::DateTime& /*rDateTime*/,
                                      const OUString& /*rMovedID*/,
                                      bool /*bMergeLastPara*/,
                                      const OUString& /*rStyleName*/,
                                      const OUString& /*rAutoName*/)
{
    // dummy implementation: do nothing
}

Reference<XTextCursor> XMLTextImportHelper::RedlineCreateText(
    Reference<XTextCursor> & /*rOldCursor*/,
    const OUString& /*rId*/)
{
    // dummy implementation: do nothing
    Reference<XTextCursor> xRet;
    return xRet;
}

void XMLTextImportHelper::RedlineSetCursor(
    const OUString& /*rId*/,
    bool /*bStart*/,
    bool /*bIsOutsideOfParagraph*/)
{
    // dummy implementation: do nothing
}

void XMLTextImportHelper::RedlineAdjustStartNodeCursor()
{
    // dummy implementation: do nothing
}

void XMLTextImportHelper::SetShowChanges( bool )
{
    // dummy implementation: do nothing
}

void XMLTextImportHelper::SetRecordChanges( bool )
{
    // dummy implementation: do nothing
}
void XMLTextImportHelper::SetChangesProtectionKey(const Sequence<sal_Int8> &)
{
    // dummy implementation: do nothing
}


OUString const & XMLTextImportHelper::GetOpenRedlineId() const
{
    return m_xImpl->m_sOpenRedlineIdentifier;
}

void XMLTextImportHelper::SetOpenRedlineId( OUString const & rId)
{
    m_xImpl->m_sOpenRedlineIdentifier = rId;
}

void XMLTextImportHelper::ResetOpenRedlineId()
{
    SetOpenRedlineId(u""_ustr);
}

void
XMLTextImportHelper::SetCellParaStyleDefault(OUString const& rNewValue)
{
    m_xImpl->m_sCellParaStyleDefault = rNewValue;
}

OUString const& XMLTextImportHelper::GetCellParaStyleDefault() const
{
    return m_xImpl->m_sCellParaStyleDefault;
}

void XMLTextImportHelper::AddCrossRefHeadingMapping(OUString const& rFrom, OUString const& rTo)
{
    if (!m_xImpl->m_xCrossRefHeadingBookmarkMap)
    {
        m_xImpl->m_xCrossRefHeadingBookmarkMap.emplace();
    }
    m_xImpl->m_xCrossRefHeadingBookmarkMap->insert(std::make_pair(rFrom, rTo));
}

// tdf#94804: hack to map cross reference fields that reference duplicate marks
// note that we can't really check meta:generator for this since the file might
// be round-tripped by different versions preserving duplicates => always map
void XMLTextImportHelper::MapCrossRefHeadingFieldsHorribly()
{
    if (!m_xImpl->m_xCrossRefHeadingBookmarkMap)
    {
        return;
    }

    uno::Reference<text::XTextFieldsSupplier> const xFieldsSupplier(
            m_xImpl->m_rSvXMLImport.GetModel(), uno::UNO_QUERY);
    if (!xFieldsSupplier.is())
    {
        return;
    }
    uno::Reference<container::XEnumerationAccess> const xFieldsEA(
            xFieldsSupplier->getTextFields());
    uno::Reference<container::XEnumeration> const xFields(
            xFieldsEA->createEnumeration());
    while (xFields->hasMoreElements())
    {
        uno::Reference<lang::XServiceInfo> const xFieldInfo(
                xFields->nextElement(), uno::UNO_QUERY);
        if (!xFieldInfo->supportsService(u"com.sun.star.text.textfield.GetReference"_ustr))
        {
            continue;
        }
        uno::Reference<beans::XPropertySet> const xField(
                xFieldInfo, uno::UNO_QUERY);
        sal_uInt16 nType(0);
        xField->getPropertyValue(u"ReferenceFieldSource"_ustr) >>= nType;
        if (text::ReferenceFieldSource::BOOKMARK != nType)
        {
            continue;
        }
        OUString name;
        xField->getPropertyValue(u"SourceName"_ustr) >>= name;
        auto const iter(m_xImpl->m_xCrossRefHeadingBookmarkMap->find(name));
        if (iter == m_xImpl->m_xCrossRefHeadingBookmarkMap->end())
        {
            continue;
        }
        xField->setPropertyValue(u"SourceName"_ustr, uno::Any(iter->second));
    }
}

void XMLTextImportHelper::setBookmarkAttributes(OUString const& bookmark, bool hidden, OUString const& condition)
{
    m_xImpl->m_bBookmarkHidden[bookmark] = hidden;
    m_xImpl->m_sBookmarkCondition[bookmark] = condition;
}

bool XMLTextImportHelper::getBookmarkHidden(OUString const& bookmark) const
{
    return m_xImpl->m_bBookmarkHidden[bookmark];
}

const OUString& XMLTextImportHelper::getBookmarkCondition(OUString const& bookmark) const
{
    return m_xImpl->m_sBookmarkCondition[bookmark];
}

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