    /* -*- 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 <cellatr.hxx>
    #include <charfmt.hxx>
    #include <cmdid.h>
    #include <doc.hxx>
    #include <IDocumentListsAccess.hxx>
    #include <editeng/colritem.hxx>
    #include <editeng/brushitem.hxx>
    #include <editeng/lineitem.hxx>
    #include <editeng/boxitem.hxx>
    #include <editeng/editeng.hxx>
    #include <fmtanchr.hxx>
    #include <fmtpdsc.hxx>
    #include <hintids.hxx>
    #include <istyleaccess.hxx>
    #include <list.hxx>
    #include <node.hxx>
    #include <numrule.hxx>
    #include <pagedesc.hxx>
    #include <paratr.hxx>
    #include <svl/whiter.hxx>
    #include <svx/xtable.hxx>

    #include <svx/svdpool.hxx>
    #include <svx/sxenditm.hxx>
    #include <svx/sdsxyitm.hxx>

    SwAttrPool::SwAttrPool( SwDoc* pD )
        : SfxItemPool( "SWG",
                        POOLATTR_BEGIN, POOLATTR_END-1,
                        aSlotTab, &aAttrTab ),
        m_pDoc( pD )
    {
        // create secondary pools immediately
        createAndAddSecondaryPools();
    }

    SwAttrPool::~SwAttrPool()
    {
        // cleanup secondary pools first
        removeAndDeleteSecondaryPools();
    }

    void SwAttrPool::createAndAddSecondaryPools()
    {
        const SfxItemPool* pCheckAlreadySet = GetSecondaryPool();

        if(pCheckAlreadySet)
        {
            OSL_ENSURE(false, "SwAttrPool already has a secondary pool (!)");
            return;
        }

        // create SfxItemPool and EditEngine pool and add these in a chain. These
        // belong us and will be removed/destroyed in removeAndDeleteSecondaryPools() used from
        // the destructor
        SfxItemPool *pSdrPool = new SdrItemPool(this);

        // #75371# change DefaultItems for the SdrEdgeObj distance items
        // to TWIPS.
        // 1/100th mm in twips
        const long nDefEdgeDist = ((500 * 72) / 127);

        pSdrPool->SetPoolDefaultItem(SdrEdgeNode1HorzDistItem(nDefEdgeDist));
        pSdrPool->SetPoolDefaultItem(SdrEdgeNode1VertDistItem(nDefEdgeDist));
        pSdrPool->SetPoolDefaultItem(SdrEdgeNode2HorzDistItem(nDefEdgeDist));
        pSdrPool->SetPoolDefaultItem(SdrEdgeNode2VertDistItem(nDefEdgeDist));

        // #i33700# // Set shadow distance defaults as PoolDefaultItems
        pSdrPool->SetPoolDefaultItem(makeSdrShadowXDistItem((300 * 72) / 127));
        pSdrPool->SetPoolDefaultItem(makeSdrShadowYDistItem((300 * 72) / 127));

        SfxItemPool *pEEgPool = EditEngine::CreatePool();

        pSdrPool->SetSecondaryPool(pEEgPool);

        if(!GetFrozenIdRanges())
        {
            FreezeIdRanges();
        }
        else
        {
            pSdrPool->FreezeIdRanges();
        }
    }

    void SwAttrPool::removeAndDeleteSecondaryPools()
    {
        SfxItemPool *pSdrPool = GetSecondaryPool();

        if(!pSdrPool)
        {
            OSL_ENSURE(false, "SwAttrPool has no secondary pool, it's missing (!)");
            return;
        }

        SfxItemPool *pEEgPool = pSdrPool->GetSecondaryPool();

        if(!pEEgPool)
        {
            OSL_ENSURE(false, "i don't accept additional pools");
            return;
        }

        // first delete the items, then break the linking
        pSdrPool->Delete();

        SetSecondaryPool(nullptr);
        pSdrPool->SetSecondaryPool(nullptr);

        // final cleanup of secondary pool(s)
        SfxItemPool::Free(pSdrPool);
        SfxItemPool::Free(pEEgPool);
    }

    SwAttrSet::SwAttrSet( SwAttrPool& rPool, sal_uInt16 nWh1, sal_uInt16 nWh2 )
        : SfxItemSet( rPool, {{nWh1, nWh2}} ), m_pOldSet( nullptr ), m_pNewSet( nullptr )
    {
    }

    SwAttrSet::SwAttrSet( SwAttrPool& rPool, const sal_uInt16* nWhichPairTable )
        : SfxItemSet( rPool, nWhichPairTable ), m_pOldSet( nullptr ), m_pNewSet( nullptr )
    {
    }

    SwAttrSet::SwAttrSet( const SwAttrSet& rSet )
        : SfxItemSet( rSet ), m_pOldSet( nullptr ), m_pNewSet( nullptr )
    {
    }

    std::unique_ptr<SfxItemSet> SwAttrSet::Clone( bool bItems, SfxItemPool *pToPool ) const
    {
        if ( pToPool && pToPool != GetPool() )
        {
            SwAttrPool* pAttrPool = dynamic_cast< SwAttrPool* >(pToPool);
            std::unique_ptr<SfxItemSet> pTmpSet;
            if ( !pAttrPool )
                pTmpSet = SfxItemSet::Clone( bItems, pToPool );
            else
            {
                pTmpSet.reset(new SwAttrSet( *pAttrPool, GetRanges() ));
                if ( bItems )
                {
                    SfxWhichIter aIter(*pTmpSet);
                    sal_uInt16 nWhich = aIter.FirstWhich();
                    while ( nWhich )
                    {
                        const SfxPoolItem* pItem;
                        if ( SfxItemState::SET == GetItemState( nWhich, false, &pItem ) )
                            pTmpSet->Put( *pItem );
                        nWhich = aIter.NextWhich();
                    }
                }
            }
            return pTmpSet;
        }
        else
            return std::unique_ptr<SfxItemSet>(
                    bItems
                    ? new SwAttrSet( *this )
                    : new SwAttrSet( *GetPool(), GetRanges() ));
    }

    bool SwAttrSet::Put_BC( const SfxPoolItem& rAttr,
                           SwAttrSet* pOld, SwAttrSet* pNew )
    {
        m_pNewSet = pNew;
        m_pOldSet = pOld;
        bool bRet = nullptr != SfxItemSet::Put( rAttr );
        m_pOldSet = m_pNewSet = nullptr;
        return bRet;
    }

    bool SwAttrSet::Put_BC( const SfxItemSet& rSet,
                           SwAttrSet* pOld, SwAttrSet* pNew )
    {
        m_pNewSet = pNew;
        m_pOldSet = pOld;
        bool bRet = SfxItemSet::Put( rSet );
        m_pOldSet = m_pNewSet = nullptr;
        return bRet;
    }

    sal_uInt16 SwAttrSet::ClearItem_BC( sal_uInt16 nWhich,
                                        SwAttrSet* pOld, SwAttrSet* pNew )
    {
        m_pNewSet = pNew;
        m_pOldSet = pOld;
        sal_uInt16 nRet = SfxItemSet::ClearItem( nWhich );
        m_pOldSet = m_pNewSet = nullptr;
        return nRet;
    }

    sal_uInt16 SwAttrSet::ClearItem_BC( sal_uInt16 nWhich1, sal_uInt16 nWhich2,
                                        SwAttrSet* pOld, SwAttrSet* pNew )
    {
        OSL_ENSURE( nWhich1 <= nWhich2, "no valid range" );
        m_pNewSet = pNew;
        m_pOldSet = pOld;
        sal_uInt16 nRet = 0;
        for( ; nWhich1 <= nWhich2; ++nWhich1 )
            nRet = nRet + SfxItemSet::ClearItem( nWhich1 );
        m_pOldSet = m_pNewSet = nullptr;
        return nRet;
    }

    int SwAttrSet::Intersect_BC( const SfxItemSet& rSet,
                                 SwAttrSet* pOld, SwAttrSet* pNew )
    {
        m_pNewSet = pNew;
        m_pOldSet = pOld;
        SfxItemSet::Intersect( rSet );
        m_pOldSet = m_pNewSet = nullptr;
        return pNew ? pNew->Count() : ( pOld ? pOld->Count() : 0 );
    }

    /// Notification callback
    void  SwAttrSet::Changed( const SfxPoolItem& rOld, const SfxPoolItem& rNew )
    {
        if( m_pOldSet )
            m_pOldSet->PutChgd( rOld );
        if( m_pNewSet )
            m_pNewSet->PutChgd( rNew );
    }

    /** special treatment for some attributes

        Set the Modify pointer (old pDefinedIn) for the following attributes:
         - SwFormatDropCaps
         - SwFormatPageDesc

        (Is called at inserts into formats/nodes)
    */
    bool SwAttrSet::SetModifyAtAttr( const SwModify* pModify )
    {
        bool bSet = false;

        const SfxPoolItem* pItem;
        if( SfxItemState::SET == GetItemState( RES_PAGEDESC, false, &pItem ) &&
            static_cast<const SwFormatPageDesc*>(pItem)->GetDefinedIn() != pModify  )
        {
            const_cast<SwFormatPageDesc*>(static_cast<const SwFormatPageDesc*>(pItem))->ChgDefinedIn( pModify );
            bSet = true;
        }

        if( SfxItemState::SET == GetItemState( RES_PARATR_DROP, false, &pItem ) &&
            static_cast<const SwFormatDrop*>(pItem)->GetDefinedIn() != pModify )
        {
            // If CharFormat is set and it is set in different attribute pools then
            // the CharFormat has to be copied.
            SwCharFormat* pCharFormat;
            if( nullptr != ( pCharFormat = const_cast<SwFormatDrop*>(static_cast<const SwFormatDrop*>(pItem))->GetCharFormat() )
                && GetPool() != pCharFormat->GetAttrSet().GetPool() )
            {
               pCharFormat = GetDoc()->CopyCharFormat( *pCharFormat );
               const_cast<SwFormatDrop*>(static_cast<const SwFormatDrop*>(pItem))->SetCharFormat( pCharFormat );
            }
            const_cast<SwFormatDrop*>(static_cast<const SwFormatDrop*>(pItem))->ChgDefinedIn( pModify );
            bSet = true;
        }

        if( SfxItemState::SET == GetItemState( RES_BOXATR_FORMULA, false, &pItem ) &&
            static_cast<const SwTableBoxFormula*>(pItem)->GetDefinedIn() != pModify )
        {
            const_cast<SwTableBoxFormula*>(static_cast<const SwTableBoxFormula*>(pItem))->ChgDefinedIn( pModify );
            bSet = true;
        }

        return bSet;
    }

    void SwAttrSet::CopyToModify( SwModify& rMod ) const
    {
        // copy attributes across multiple documents if needed
        SwContentNode* pCNd = dynamic_cast<SwContentNode*>( &rMod  );
        SwFormat* pFormat = dynamic_cast<SwFormat*>( &rMod  );

        if( pCNd || pFormat )
        {
            if( Count() )
            {
                // #i92811#
                SfxStringItem* pNewListIdItem( nullptr );

                const SfxPoolItem* pItem;
                const SwDoc *pSrcDoc = GetDoc();
                SwDoc *pDstDoc = pCNd ? pCNd->GetDoc() : pFormat->GetDoc();

                // Does the NumRule has to be copied?
                if( pSrcDoc != pDstDoc &&
                    SfxItemState::SET == GetItemState( RES_PARATR_NUMRULE, false, &pItem ) )
                {
                    const OUString& rNm = static_cast<const SwNumRuleItem*>(pItem)->GetValue();
                    if( !rNm.isEmpty() )
                    {
                        SwNumRule* pDestRule = pDstDoc->FindNumRulePtr( rNm );
                        if( pDestRule )
                            pDestRule->SetInvalidRule( true );
                        else
                            pDstDoc->MakeNumRule( rNm, pSrcDoc->FindNumRulePtr( rNm ) );
                    }
                }

                // copy list and if needed also the corresponding list style
                // for text nodes
                if ( pSrcDoc != pDstDoc &&
                     pCNd && pCNd->IsTextNode() &&
                     GetItemState( RES_PARATR_LIST_ID, false, &pItem ) == SfxItemState::SET )
                {
                    const OUString& sListId =
                            dynamic_cast<const SfxStringItem*>(pItem)->GetValue();
                    if ( !sListId.isEmpty() &&
                         !pDstDoc->getIDocumentListsAccess().getListByName( sListId ) )
                    {
                        const SwList* pList = pSrcDoc->getIDocumentListsAccess().getListByName( sListId );
                        // copy list style, if needed
                        const OUString sDefaultListStyleName =
                                                pList->GetDefaultListStyleName();
                        // #i92811#
                        const SwNumRule* pDstDocNumRule =
                                    pDstDoc->FindNumRulePtr( sDefaultListStyleName );
                        if ( !pDstDocNumRule )
                        {
                            pDstDoc->MakeNumRule( sDefaultListStyleName,
                                                  pSrcDoc->FindNumRulePtr( sDefaultListStyleName ) );
                        }
                        else
                        {
                            const SwNumRule* pSrcDocNumRule =
                                    pSrcDoc->FindNumRulePtr( sDefaultListStyleName );
                            // If list id of text node equals the list style's
                            // default list id in the source document, the same
                            // should be hold in the destination document.
                            // Thus, create new list id item.
                            if (pSrcDocNumRule && sListId == pSrcDocNumRule->GetDefaultListId())
                            {
                                pNewListIdItem = new SfxStringItem (
                                                RES_PARATR_LIST_ID,
                                                pDstDocNumRule->GetDefaultListId() );
                            }
                        }
                        // check again, if list exist, because <SwDoc::MakeNumRule(..)>
                        // could have also created it.
                        if ( pNewListIdItem == nullptr &&
                             !pDstDoc->getIDocumentListsAccess().getListByName( sListId ) )
                        {
                            // copy list
                            pDstDoc->getIDocumentListsAccess().createList( sListId, sDefaultListStyleName );
                        }
                    }
                }

                std::unique_ptr< SfxItemSet > tmpSet;

                const SwPageDesc* pPgDesc;
                if( pSrcDoc != pDstDoc && SfxItemState::SET == GetItemState(
                                                RES_PAGEDESC, false, &pItem ) &&
                    nullptr != ( pPgDesc = static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc()) )
                {
                    if( !tmpSet )
                        tmpSet.reset( new SfxItemSet( *this ));

                    SwPageDesc* pDstPgDesc = pDstDoc->FindPageDesc(pPgDesc->GetName());
                    if( !pDstPgDesc )
                    {
                        pDstPgDesc = pDstDoc->MakePageDesc(pPgDesc->GetName());
                        pDstDoc->CopyPageDesc( *pPgDesc, *pDstPgDesc );
                    }
                    SwFormatPageDesc aDesc( pDstPgDesc );
                    aDesc.SetNumOffset( static_cast<const SwFormatPageDesc*>(pItem)->GetNumOffset() );
                    tmpSet->Put( aDesc );
                }

                if( pSrcDoc != pDstDoc && SfxItemState::SET == GetItemState( RES_ANCHOR, false, &pItem )
                    && static_cast< const SwFormatAnchor* >( pItem )->GetContentAnchor() != nullptr )
                {
                    if( !tmpSet )
                        tmpSet.reset( new SfxItemSet( *this ));
                    // Anchors at any node position cannot be copied to another document, because the SwPosition
                    // would still point to the old document. It needs to be fixed up explicitly.
                    tmpSet->ClearItem( RES_ANCHOR );
                }

                if( tmpSet )
                {
                    if( pCNd )
                    {
                        // #i92811#
                        if ( pNewListIdItem != nullptr )
                        {
                            tmpSet->Put( *pNewListIdItem );
                        }
                        pCNd->SetAttr( *tmpSet );
                    }
                    else
                    {
                        pFormat->SetFormatAttr( *tmpSet );
                    }
                }
                else if( pCNd )
                {
                    // #i92811#
                    if ( pNewListIdItem != nullptr )
                    {
                        SfxItemSet aTmpSet( *this );
                        aTmpSet.Put( *pNewListIdItem );
                        pCNd->SetAttr( aTmpSet );
                    }
                    else
                    {
                        pCNd->SetAttr( *this );
                    }
                }
                else
                {
                    pFormat->SetFormatAttr( *this );
                }

                // #i92811#
                delete pNewListIdItem;
                pNewListIdItem = nullptr;
            }
        }
    #if OSL_DEBUG_LEVEL > 0
        else
            OSL_FAIL("neither Format nor ContentNode - no Attributes copied");
    #endif
    }

    /// check if ID is in range of attribute set IDs
    bool IsInRange( const sal_uInt16* pRange, const sal_uInt16 nId )
    {
        while( *pRange )
        {
            if( *pRange <= nId && nId <= *(pRange+1) )
                return true;
            pRange += 2;
        }
        return false;
    }

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

↑ V614 The 'tmpSet' smart pointer is utilized immediately after being declared or reset. It is suspicious that no value was assigned to it.

↑ V773 Visibility scope of the 'pSdrPool' pointer was exited without releasing the memory. A memory leak is possible.

↑ V547 Expression '!tmpSet' is always true.

↑ V522 There might be dereferencing of a potential null pointer.

↑ V522 There might be dereferencing of a potential null pointer 'pFormat'.