/* -*- 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 <string.h>

#include <algorithm>
#include <cassert>
#include <cstddef>

#include <libxml/xmlwriter.h>

#include <sal/log.hxx>
#include <svl/itemset.hxx>
#include <svl/itempool.hxx>
#include <svl/itemiter.hxx>
#include <svl/setitem.hxx>
#include <svl/whiter.hxx>

#include <items_helper.hxx>

/**
 * Ctor for a SfxItemSet with exactly the Which Ranges, which are known to
 * the supplied SfxItemPool.
 *
 * For Sfx programmers: an SfxItemSet constructed in this way cannot
 * contain any Items with SlotIds as Which values.
 *
 * Don't create ItemSets with full range before FreezeIdRanges()!
 */
SfxItemSet::SfxItemSet(SfxItemPool& rPool)
    : m_pPool(&rPool), m_pParent(nullptr),
    m_ppItems(new SfxPoolItem const *[svl::detail::CountRanges(rPool.GetFrozenIdRanges())]{}),
    m_pWhichRanges(rPool.GetFrozenIdRanges()),
    m_nCount(0),
    m_bItemsFixed(false)
{
    assert(svl::detail::validRanges2(m_pWhichRanges));
}

SfxItemSet::SfxItemSet( SfxItemPool& rPool, SfxAllItemSetFlag )
    : m_pPool(&rPool)
    , m_pParent(nullptr)
    , m_ppItems(nullptr)
    , m_nCount(0)
    , m_bItemsFixed(false)
{
}

/** special constructor for SfxItemSetFixed */
SfxItemSet::SfxItemSet( SfxItemPool& rPool, WhichRangesContainer&& ranges, SfxPoolItem const ** ppItems )
    : m_pPool(&rPool)
    , m_pParent(nullptr)
    , m_ppItems(ppItems)
    , m_pWhichRanges(std::move(ranges))
    , m_nCount(0)
    , m_bItemsFixed(true)
{
    assert(ppItems);
    assert(m_pWhichRanges.size() > 0);
    assert(svl::detail::validRanges2(m_pWhichRanges));
}

SfxItemSet::SfxItemSet(SfxItemPool& pool, WhichRangesContainer wids)
    : m_pPool(&pool),
    m_pParent(nullptr),
    m_ppItems(new SfxPoolItem const *[svl::detail::CountRanges(wids)]{}),
    m_pWhichRanges(std::move(wids)),
    m_nCount(0),
    m_bItemsFixed(false)
{
    assert(svl::detail::CountRanges(m_pWhichRanges) != 0);
    assert(svl::detail::validRanges2(m_pWhichRanges));
}

SfxItemSet::SfxItemSet( const SfxItemSet& rASet )
    : m_pPool( rASet.m_pPool )
    , m_pParent( rASet.m_pParent )
    , m_pWhichRanges( rASet.m_pWhichRanges )
    , m_nCount( rASet.m_nCount )
    , m_bItemsFixed(false)
{
    if (rASet.m_pWhichRanges.empty())
    {
        m_ppItems = nullptr;
        return;
    }

    auto nCnt = svl::detail::CountRanges(m_pWhichRanges);
    m_ppItems = new const SfxPoolItem* [nCnt] {};

    // Copy attributes
    SfxPoolItem const** ppDst = m_ppItems;
    SfxPoolItem const** ppSrc = rASet.m_ppItems;
    for( sal_uInt16 n = nCnt; n; --n, ++ppDst, ++ppSrc )
        if ( nullptr == *ppSrc ||                 // Current Default?
             IsInvalidItem(*ppSrc) ||       // DontCare?
             IsStaticDefaultItem(*ppSrc) )  // Defaults that are not to be pooled?
            // Just copy the pointer
            *ppDst = *ppSrc;
        else if (m_pPool->IsItemPoolable( **ppSrc ))
        {
            // Just copy the pointer and increase RefCount
            *ppDst = *ppSrc;
            (*ppDst)->AddRef();
        }
        else if ( !(*ppSrc)->Which() )
            *ppDst = (*ppSrc)->Clone();
        else
            // !IsPoolable() => assign via Pool
            *ppDst = &m_pPool->Put( **ppSrc );

    assert(svl::detail::validRanges2(m_pWhichRanges));
}

SfxItemSet::SfxItemSet(SfxItemSet&& rASet) noexcept
    : m_pPool( rASet.m_pPool )
    , m_pParent( rASet.m_pParent )
    , m_ppItems( rASet.m_ppItems )
    , m_pWhichRanges( std::move(rASet.m_pWhichRanges) )
    , m_nCount( rASet.m_nCount )
    , m_bItemsFixed(false)
{
    if (rASet.m_bItemsFixed)
    {
        // have to make a copy
        int noItems = svl::detail::CountRanges(m_pWhichRanges);
        m_ppItems = new const SfxPoolItem* [noItems];
        std::copy(rASet.m_ppItems, rASet.m_ppItems + noItems, m_ppItems);
    }
    else
        // taking over ownership
        rASet.m_ppItems = nullptr;
    rASet.m_pPool = nullptr;
    rASet.m_pParent = nullptr;
    rASet.m_nCount = 0;
    assert(svl::detail::validRanges2(m_pWhichRanges));
}

SfxItemSet::~SfxItemSet()
{
    if (!m_pWhichRanges.empty()) // might be nullptr if we have been moved-from
    {
        if( Count() )
        {
            sal_uInt16 nCount = TotalCount();
            SfxPoolItem const** ppFnd = m_ppItems;
            for( sal_uInt16 nCnt = nCount; nCnt; --nCnt, ++ppFnd )
                if( *ppFnd && !IsInvalidItem(*ppFnd) )
                {
                    if( !(*ppFnd)->Which() )
                        delete *ppFnd;
                    else {
                        // Still multiple references present, so just alter the RefCount
                        if ( 1 < (*ppFnd)->GetRefCount() && !IsDefaultItem(*ppFnd) )
                            (*ppFnd)->ReleaseRef();
                        else
                            if ( !IsDefaultItem(*ppFnd) )
                                // Delete from Pool
                                m_pPool->Remove( **ppFnd );
                    }
                }
        }
    }

    if (!m_bItemsFixed)
        delete[] m_ppItems;
    m_pWhichRanges.reset(); // for invariant-testing
}

/**
 * Delete single Items or all Items (nWhich == 0)
 */
sal_uInt16 SfxItemSet::ClearItem( sal_uInt16 nWhich )
{
    if( !Count() )
        return 0;
    if( nWhich )
        return ClearSingleItemImpl(nWhich, std::nullopt);
    else
        return ClearAllItemsImpl();
}

sal_uInt16 SfxItemSet::ClearSingleItemImpl( sal_uInt16 nWhich, std::optional<sal_uInt16> oItemOffsetHint )
{
    sal_uInt16 nDel = 0;
    SfxPoolItem const** pFoundOne = nullptr;

    if (oItemOffsetHint)
    {
        pFoundOne = m_ppItems + *oItemOffsetHint;
        assert(!*pFoundOne || IsInvalidItem(*pFoundOne) || (*pFoundOne)->IsVoidItem() || (*pFoundOne)->Which() == nWhich);
    }
    else
    {
        SfxPoolItem const** ppFnd = m_ppItems;
        for (const WhichPair& rPair : m_pWhichRanges)
        {
            // Within this range?
            if( rPair.first <= nWhich && nWhich <= rPair.second )
            {
                // Actually set?
                ppFnd += nWhich - rPair.first;
                pFoundOne = ppFnd;

                // found => break
                break;
            }
            ppFnd += rPair.second - rPair.first + 1;
        }
    }
    if (pFoundOne && *pFoundOne)
    {
        // Due to the assertions in the sub calls, we need to do the following
        --m_nCount;
        const SfxPoolItem *pItemToClear = *pFoundOne;
        *pFoundOne = nullptr;

        if ( !IsInvalidItem(pItemToClear) )
        {
            if (SfxItemPool::IsWhich(nWhich))
            {
                const SfxPoolItem& rNew = m_pParent
                        ? m_pParent->Get( nWhich )
                        : m_pPool->GetDefaultItem( nWhich );

                Changed( *pItemToClear, rNew );
            }
            if ( pItemToClear->Which() )
                m_pPool->Remove( *pItemToClear );
        }
        ++nDel;
    }
    return nDel;
}


sal_uInt16 SfxItemSet::ClearAllItemsImpl()
{
    sal_uInt16 nDel = m_nCount;
    SfxPoolItem const** ppFnd = m_ppItems;

    for (const WhichPair& rPair : m_pWhichRanges)
    {
        for( sal_uInt16 nWhich = rPair.first; nWhich <= rPair.second; ++nWhich, ++ppFnd )
        {
            if( !*ppFnd )
                continue;

            // Due to the assertions in the sub calls, we need to do this
            --m_nCount;
            const SfxPoolItem *pItemToClear = *ppFnd;
            *ppFnd = nullptr;

            if ( IsInvalidItem(pItemToClear) )
                continue;

            if (SfxItemPool::IsWhich(nWhich))
            {
                const SfxPoolItem& rNew = m_pParent
                        ? m_pParent->Get( nWhich )
                        : m_pPool->GetDefaultItem( nWhich );

                Changed( *pItemToClear, rNew );
            }

            // #i32448#
            // Take care of disabled items, too.
            if (!pItemToClear->m_nWhich)
            {
                // item is disabled, delete it
                delete pItemToClear;
            }
            else
            {
                // remove item from pool
                m_pPool->Remove( *pItemToClear );
            }
        }
    }
    return nDel;
}

void SfxItemSet::ClearInvalidItems()
{
    SfxPoolItem const** ppFnd = m_ppItems;
    for (const WhichPair& rPair : m_pWhichRanges)
    {
        for( sal_uInt16 nWhich = rPair.first; nWhich <= rPair.second; ++nWhich, ++ppFnd )
            if( IsInvalidItem(*ppFnd) )
            {
                *ppFnd = nullptr;
                --m_nCount;
            }
    }
}

void SfxItemSet::InvalidateAllItems()
{
    assert( !m_nCount && "There are still Items set" );
    m_nCount = TotalCount();
    memset(static_cast<void*>(m_ppItems), -1, m_nCount * sizeof(SfxPoolItem*));
}

SfxItemState SfxItemSet::GetItemState( sal_uInt16 nWhich,
                                        bool bSrchInParent,
                                        const SfxPoolItem **ppItem ) const
{
    return GetItemStateImpl(nWhich, bSrchInParent, ppItem, std::nullopt);
}

SfxItemState SfxItemSet::GetItemStateImpl( sal_uInt16 nWhich,
                                           bool bSrchInParent,
                                           const SfxPoolItem **ppItem,
                                           std::optional<sal_uInt16> oItemsOffsetHint) const
{
    // Find the range in which the Which is located
    const SfxItemSet* pCurrentSet = this;
    SfxItemState eRet = SfxItemState::UNKNOWN;
    do
    {
        SfxPoolItem const** pFoundOne = nullptr;
        if (oItemsOffsetHint)
        {
            pFoundOne = pCurrentSet->m_ppItems + *oItemsOffsetHint;
            assert(!*pFoundOne || IsInvalidItem(*pFoundOne) || (*pFoundOne)->IsVoidItem() || (*pFoundOne)->Which() == nWhich);
            oItemsOffsetHint.reset(); // in case we need to search parent
        }
        else
        {
            SfxPoolItem const** ppFnd = pCurrentSet->m_ppItems;
            for (const WhichPair& rPair : pCurrentSet->m_pWhichRanges)
            {
                if ( rPair.first <= nWhich && nWhich <= rPair.second )
                {
                    // Within this range
                    pFoundOne = ppFnd + nWhich - rPair.first;
                    break;
                }
                ppFnd += rPair.second - rPair.first + 1;
            }
        }

        if (pFoundOne)
        {
            if ( !*pFoundOne )
            {
                eRet = SfxItemState::DEFAULT;
                if( !bSrchInParent )
                    return eRet; // Not present
                // Keep searching in the parents!
            }
            else
            {
                if ( IsInvalidItem(*pFoundOne) )
                    // Different ones are present
                    return SfxItemState::DONTCARE;

                if ( (*pFoundOne)->IsVoidItem() )
                    return SfxItemState::DISABLED;

                if (ppItem)
                {
                    *ppItem = *pFoundOne;
                }
                return SfxItemState::SET;
            }
        }
        if (!bSrchInParent)
            break;
        pCurrentSet = pCurrentSet->m_pParent;
    } while (nullptr != pCurrentSet);
    return eRet;
}

bool SfxItemSet::HasItem(sal_uInt16 nWhich, const SfxPoolItem** ppItem) const
{
    bool bRet = SfxItemState::SET == GetItemState(nWhich, true, ppItem);
    if (!bRet && ppItem)
        *ppItem = nullptr;
    return bRet;
}

const SfxPoolItem* SfxItemSet::PutImpl( const SfxPoolItem& rItem, sal_uInt16 nWhich, bool bPassingOwnership )
{
    if ( !nWhich )
    {
        assert(!bPassingOwnership);
        return nullptr; //FIXME: Only because of Outliner bug
    }

    SfxPoolItem const** ppFnd = m_ppItems;
    for (const WhichPair& rPair : m_pWhichRanges)
    {
        if( rPair.first <= nWhich && nWhich <= rPair.second )
        {
            // Within this range
            ppFnd += nWhich - rPair.first;
            if( *ppFnd ) // Already one present
            {
                // Same Item already present?
                if ( *ppFnd == &rItem )
                {
                    assert(!bPassingOwnership);
                    return nullptr;
                }

                // Will 'dontcare' or 'disabled' be overwritten with some real value?
                if ( rItem.Which() && ( IsInvalidItem(*ppFnd) || !(*ppFnd)->Which() ) )
                {
                    auto const old = *ppFnd;
                    *ppFnd = &m_pPool->PutImpl( rItem, nWhich, bPassingOwnership );
                    if (!IsInvalidItem(old)) {
                        assert(old->Which() == 0);
                        delete old;
                    }
                    return *ppFnd;
                }

                // Turns into disabled?
                if( !rItem.Which() )
                {
                    if (IsInvalidItem(*ppFnd) || (*ppFnd)->Which() != 0) {
                        *ppFnd = rItem.Clone(m_pPool);
                    }
                    if (bPassingOwnership)
                        delete &rItem;
                    return nullptr;
                }
                else
                {
                    // Same value already present?
                    if ( rItem == **ppFnd )
                    {
                        if (bPassingOwnership)
                            delete &rItem;
                        return nullptr;
                    }

                    // Add the new one, remove the old one
                    const SfxPoolItem& rNew = m_pPool->PutImpl( rItem, nWhich, bPassingOwnership );
                    const SfxPoolItem* pOld = *ppFnd;
                    *ppFnd = &rNew;
                    if (SfxItemPool::IsWhich(nWhich))
                        Changed( *pOld, rNew );
                    m_pPool->Remove( *pOld );
                }
            }
            else
            {
                ++m_nCount;
                if( !rItem.Which() )
                {
                    *ppFnd = rItem.Clone(m_pPool);
                    if (bPassingOwnership)
                        delete &rItem;
                }
                else
                {
                    const SfxPoolItem& rNew = m_pPool->PutImpl( rItem, nWhich, bPassingOwnership );
                    *ppFnd = &rNew;
                    if (SfxItemPool::IsWhich(nWhich))
                    {
                        const SfxPoolItem& rOld = m_pParent
                            ? m_pParent->Get( nWhich )
                            : m_pPool->GetDefaultItem( nWhich );
                        Changed( rOld, rNew );
                    }
                }
            }
            SAL_WARN_IF(!bPassingOwnership && m_pPool->IsItemPoolable(nWhich) &&
                        dynamic_cast<const SfxSetItem*>( &rItem ) == nullptr &&
                        **ppFnd != rItem,
                        "svl.items", "putted Item unequal, with ID/pos " << nWhich );
            return *ppFnd;
        }
        ppFnd += rPair.second - rPair.first + 1;
    }
    if (bPassingOwnership)
        delete &rItem;
    return nullptr;
}

bool SfxItemSet::Put( const SfxItemSet& rSet, bool bInvalidAsDefault )
{
    bool bRet = false;
    if( rSet.Count() )
    {
        SfxPoolItem const** ppFnd = rSet.m_ppItems;
        for (const WhichPair& rPair : rSet.m_pWhichRanges)
        {
            for ( sal_uInt16 nWhich = rPair.first; nWhich <= rPair.second; ++nWhich, ++ppFnd )
                if( *ppFnd )
                {
                    if ( IsInvalidItem( *ppFnd ) )
                    {
                        if ( bInvalidAsDefault )
                            bRet |= 0 != ClearItem( nWhich );
                            // FIXME: Caused a SEGFAULT on non Windows-platforms:
                            // bRet |= 0 != Put( rSet.GetPool()->GetDefaultItem(nWhich), nWhich );
                        else
                            InvalidateItem( nWhich );
                    }
                    else
                        bRet |= nullptr != Put( **ppFnd, nWhich );
                }
        }
    }
    return bRet;
}

/**
 * This method takes the Items from the 'rSet' and adds to '*this'.
 * Which ranges in '*this' that are non-existent in 'rSet' will not
 * be altered. The Which range of '*this' is also not changed.
 *
 * Items set in 'rSet' are also set in '*this'.
 * Default (0 pointer) and Invalid (-1 pointer) Items are processed
 * according to their parameter 'eDontCareAs' and 'eDefaultAs':
 *
 * SfxItemState::SET:       Hard set to the default of the Pool
 * SfxItemState::DEFAULT:   Deleted (0 pointer)
 * SfxItemState::DONTCARE:  Invalid (-1 pointer)
 *
 * NB: All other values for 'eDontCareAs' and 'eDefaultAs' are invalid
 */
void SfxItemSet::PutExtended
(
    const SfxItemSet&   rSet,           // Source of the Items to be put
    SfxItemState        eDontCareAs,    // What will happen to the DontCare Items
    SfxItemState        eDefaultAs      // What will happen to the Default Items
)
{
    // don't "optimize" with "if( rSet.Count()" because of dont-care + defaults
    SfxPoolItem const** ppFnd = rSet.m_ppItems;
    for (const WhichPair& rPair : rSet.m_pWhichRanges)
    {
        for ( sal_uInt16 nWhich = rPair.first; nWhich <= rPair.second; ++nWhich, ++ppFnd )
            if( *ppFnd )
            {
                if ( IsInvalidItem( *ppFnd ) )
                {
                    // Item is DontCare:
                    switch ( eDontCareAs )
                    {
                        case SfxItemState::SET:
                            Put( rSet.GetPool()->GetDefaultItem(nWhich), nWhich );
                            break;

                        case SfxItemState::DEFAULT:
                            ClearItem( nWhich );
                            break;

                        case SfxItemState::DONTCARE:
                            InvalidateItem( nWhich );
                            break;

                        default:
                            assert(!"invalid Argument for eDontCareAs");
                    }
                }
                else
                    // Item is set:
                    Put( **ppFnd, nWhich );
            }
            else
            {
                // Item is default:
                switch ( eDefaultAs )
                {
                    case SfxItemState::SET:
                        Put( rSet.GetPool()->GetDefaultItem(nWhich), nWhich );
                        break;

                    case SfxItemState::DEFAULT:
                        ClearItem( nWhich );
                        break;

                    case SfxItemState::DONTCARE:
                        InvalidateItem( nWhich );
                        break;

                    default:
                        assert(!"invalid Argument for eDefaultAs");
                }
            }
    }
}

/**
 * Expands the ranges of settable items by 'nFrom' to 'nTo'. Keeps state of
 * items which are new ranges too.
 */
void SfxItemSet::MergeRange( sal_uInt16 nFrom, sal_uInt16 nTo )
{
    // special case: exactly one sal_uInt16 which is already included?
    if (nFrom == nTo)
        if (SfxItemState eItemState = GetItemState(nFrom, false);
            eItemState == SfxItemState::DEFAULT || eItemState == SfxItemState::SET)
            return;

    auto pNewRanges = m_pWhichRanges.MergeRange(nFrom, nTo);
    RecreateRanges_Impl(pNewRanges);
    m_pWhichRanges = std::move(pNewRanges);
}

/**
 * Modifies the ranges of settable items. Keeps state of items which
 * are new ranges too.
 */
void SfxItemSet::SetRanges( const WhichRangesContainer& pNewRanges )
{
    // Identical Ranges?
    if (m_pWhichRanges == pNewRanges)
        return;
    assert(svl::detail::validRanges2(pNewRanges));
    RecreateRanges_Impl(pNewRanges);
    m_pWhichRanges = pNewRanges;
}

void SfxItemSet::SetRanges( WhichRangesContainer&& pNewRanges )
{
    // Identical Ranges?
    if (m_pWhichRanges == pNewRanges)
        return;
    assert(svl::detail::validRanges2(pNewRanges));
    RecreateRanges_Impl(pNewRanges);
    m_pWhichRanges = std::move(pNewRanges);
}

void SfxItemSet::RecreateRanges_Impl(const WhichRangesContainer& pNewRanges)
{
    // create new item-array (by iterating through all new ranges)
    const auto nSize = svl::detail::CountRanges(pNewRanges);
    SfxPoolItem const** aNewItems = new const SfxPoolItem* [ nSize ];
    sal_uInt16 nNewCount = 0;
    if (m_nCount == 0)
        memset( aNewItems, 0, nSize * sizeof( SfxPoolItem* ) );
    else
    {
        sal_uInt16 n = 0;
        for ( auto const & pRange : pNewRanges )
        {
            // iterate through all ids in the range
            for ( sal_uInt16 nWID = pRange.first; nWID <= pRange.second; ++nWID, ++n )
            {
                // direct move of pointer (not via pool)
                SfxItemState eState = GetItemState( nWID, false, aNewItems+n );
                if ( SfxItemState::SET == eState )
                {
                    // increment new item count and possibly increment ref count
                    ++nNewCount;
                    aNewItems[n]->AddRef();
                }
                else if ( SfxItemState::DISABLED == eState )
                {
                    // put "disabled" item
                    ++nNewCount;
                    aNewItems[n] = new SfxVoidItem(0);
                }
                else if ( SfxItemState::DONTCARE == eState )
                {
                    ++nNewCount;
                    aNewItems[n] = INVALID_POOL_ITEM;
                }
                else
                {
                    // default
                    aNewItems[n] = nullptr;
                }
            }
        }
        // free old items
        sal_uInt16 nOldTotalCount = TotalCount();
        for ( sal_uInt16 nItem = 0; nItem < nOldTotalCount; ++nItem )
        {
            const SfxPoolItem *pItem = m_ppItems[nItem];
            if ( pItem && !IsInvalidItem(pItem) && pItem->Which() )
                m_pPool->Remove(*pItem);
        }
    }

    // replace old items-array and ranges
    if (m_bItemsFixed)
        m_bItemsFixed = false;
    else
        delete[] m_ppItems;
    m_ppItems = aNewItems;
    m_nCount = nNewCount;
}

/**
 * The SfxItemSet takes over exactly those SfxPoolItems that are
 * set in rSet and are in their own Which range. All others are removed.
 * The SfxItemPool is retained, such that SfxPoolItems that have been
 * taken over, are moved from the rSet's SfxItemPool to the SfxItemPool
 * of *this.
 *
 * SfxPoolItems in rSet, for which holds 'IsInvalidItem() == true' are
 * taken over as invalid items.
 *
 * @return bool true
 *              SfxPoolItems have been taken over
 *
 *              false
 *              No SfxPoolItems have been taken over, because
 *              e.g. the Which ranges of SfxItemSets are not intersecting
 *              or the intersection does not contain SfxPoolItems that are
 *              set in rSet
 */
bool SfxItemSet::Set
(
    const SfxItemSet&   rSet,   /*  The SfxItemSet, whose SfxPoolItems are
                                    to been taken over */

    bool                bDeep   /*  true (default)

                                    The SfxPoolItems from the parents that may
                                    be present in rSet, are also taken over into
                                    this SfxPoolItemSet

                                    false
                                    The SfxPoolItems from the parents of
                                    rSet are not taken into account */
)
{
    bool bRet = false;
    if (m_nCount)
        ClearItem();
    if ( bDeep )
    {
        SfxWhichIter aIter1(*this);
        SfxWhichIter aIter2(rSet);
        sal_uInt16 nWhich1 = aIter1.FirstWhich();
        sal_uInt16 nWhich2 = aIter2.FirstWhich();
        for (;;)
        {
            if (!nWhich1 || !nWhich2)
                break;
            if (nWhich1 > nWhich2)
            {
                nWhich2 = aIter2.NextWhich();
                continue;
            }
            if (nWhich1 < nWhich2)
            {
                nWhich1 = aIter1.NextWhich();
                continue;
            }
            const SfxPoolItem* pItem;
            if( SfxItemState::SET == aIter2.GetItemState( true, &pItem ) )
                bRet |= nullptr != Put( *pItem, pItem->Which() );
            nWhich1 = aIter1.NextWhich();
            nWhich2 = aIter2.NextWhich();
        }
    }
    else
        bRet = Put(rSet, false);

    return bRet;
}

const SfxPoolItem* SfxItemSet::GetItem(sal_uInt16 nId, bool bSearchInParent) const
{
    // Convert to WhichId
    sal_uInt16 nWhich = GetPool()->GetWhich(nId);

    // Is the Item set or 'bDeep == true' available?
    const SfxPoolItem *pItem = nullptr;
    SfxItemState eState = GetItemState(nWhich, bSearchInParent, &pItem);
    if (bSearchInParent && SfxItemState::DEFAULT == eState && SfxItemPool::IsWhich(nWhich))
    {
        pItem = &m_pPool->GetDefaultItem(nWhich);
    }

    return pItem;
}

const SfxPoolItem& SfxItemSet::Get( sal_uInt16 nWhich, bool bSrchInParent) const
{
    // Search the Range in which the Which is located in:
    const SfxItemSet* pCurrentSet = this;
    do
    {
        if( pCurrentSet->Count() )
        {
            SfxPoolItem const** ppFnd = pCurrentSet->m_ppItems;
            for (auto const & pPtr : pCurrentSet->m_pWhichRanges)
            {
                if( pPtr.first <= nWhich && nWhich <= pPtr.second )
                {
                    // In this Range
                    ppFnd += nWhich - pPtr.first;
                    if( *ppFnd )
                    {
                        if( IsInvalidItem(*ppFnd) ) {
                            //FIXME: The following code is duplicated further down
                            assert(m_pPool);
                            //!((SfxAllItemSet *)this)->aDefault.SetWhich(nWhich);
                            //!return aDefault;
                            return m_pPool->GetDefaultItem( nWhich );
                        }
#ifdef DBG_UTIL
                        const SfxPoolItem *pItem = *ppFnd;
                        if ( pItem->IsVoidItem() || !pItem->Which() )
                            SAL_INFO("svl.items", "SFX_WARNING: Getting disabled Item");
#endif
                        return **ppFnd;
                    }
                    break; // Continue with Parent
                }
                ppFnd += pPtr.second - pPtr.first + 1;
            }
        }
//TODO: Search until end of Range: What are we supposed to do now? To the Parent or Default??
//      if( !*pPtr )            // Until the end of the search Range?
//      break;
        if (!bSrchInParent)
            break;
        pCurrentSet = pCurrentSet->m_pParent;
    } while (nullptr != pCurrentSet);

    // Get the Default from the Pool and return
    assert(m_pPool);
    return m_pPool->GetDefaultItem( nWhich );
}

/**
 * Notification callback
 */
void SfxItemSet::Changed( const SfxPoolItem&, const SfxPoolItem& )
{
}

sal_uInt16 SfxItemSet::TotalCount() const
{
    return svl::detail::CountRanges(m_pWhichRanges);
}

/**
 * Only retain the Items that are also present in rSet
 * (nevermind their value).
 */
void SfxItemSet::Intersect( const SfxItemSet& rSet )
{
    assert(m_pPool && "Not implemented without Pool");
    if( !Count() ) // None set?
        return;

    // Delete all Items not contained in rSet
    if( !rSet.Count() )
    {
        ClearItem(); // Delete everything
        return;
    }

    // If the Ranges are identical, we can easily process it
    if( m_pWhichRanges == rSet.m_pWhichRanges )
    {
        sal_uInt16 nSize = TotalCount();
        SfxPoolItem const** ppFnd1 = m_ppItems;
        SfxPoolItem const** ppFnd2 = rSet.m_ppItems;

        for( ; nSize; --nSize, ++ppFnd1, ++ppFnd2 )
            if( *ppFnd1 && !*ppFnd2 )
            {
                // Delete from Pool
                if( !IsInvalidItem( *ppFnd1 ) )
                {
                    sal_uInt16 nWhich = (*ppFnd1)->Which();
                    if (SfxItemPool::IsWhich(nWhich))
                    {
                        const SfxPoolItem& rNew = m_pParent
                            ? m_pParent->Get( nWhich )
                            : m_pPool->GetDefaultItem( nWhich );

                        Changed( **ppFnd1, rNew );
                    }
                    m_pPool->Remove( **ppFnd1 );
                }
                *ppFnd1 = nullptr;
                --m_nCount;
            }
    }
    else
    {
        SfxWhichIter aIter( *this );
        sal_uInt16 nWhich = aIter.FirstWhich();
        while ( nWhich )
        {
            if( SfxItemState::UNKNOWN == rSet.GetItemState( nWhich, false ) )
                aIter.ClearItem(); // Delete
            nWhich = aIter.NextWhich();
        }
    }
}

void SfxItemSet::Differentiate( const SfxItemSet& rSet )
{
    if( !Count() || !rSet.Count() )// None set?
        return;

    // If the Ranges are identical, we can easily process it
    if( m_pWhichRanges == rSet.m_pWhichRanges )
    {
        sal_uInt16 nSize = TotalCount();
        SfxPoolItem const** ppFnd1 = m_ppItems;
        SfxPoolItem const** ppFnd2 = rSet.m_ppItems;

        for( ; nSize; --nSize, ++ppFnd1, ++ppFnd2 )
            if( *ppFnd1 && *ppFnd2 )
            {
                // Delete from Pool
                if( !IsInvalidItem( *ppFnd1 ) )
                {
                    sal_uInt16 nWhich = (*ppFnd1)->Which();
                    if (SfxItemPool::IsWhich(nWhich))
                    {
                        const SfxPoolItem& rNew = m_pParent
                            ? m_pParent->Get( nWhich )
                            : m_pPool->GetDefaultItem( nWhich );

                        Changed( **ppFnd1, rNew );
                    }
                    m_pPool->Remove( **ppFnd1 );
                }
                *ppFnd1 = nullptr;
                --m_nCount;
            }
    }
    else
    {
        SfxWhichIter aIter( *this );
        sal_uInt16 nWhich = aIter.FirstWhich();
        while ( nWhich )
        {
            if( SfxItemState::SET == rSet.GetItemState( nWhich, false ) )
                aIter.ClearItem(); // Delete
            nWhich = aIter.NextWhich();
        }
    }
}

/**
 * Decision table for MergeValue(s)
 *
 * Principles:
 * 1. If the Which value in the 1st set is "unknown", there's never any action
 * 2. If the Which value in the 2nd set is "unknown", it's made the "default"
 * 3. For comparisons the values of the "default" Items are take into account
 *
 * 1st Item    2nd Item    Values  bIgnoreDefs       Remove      Assign       Add
 *
 * set         set         ==      sal_False           -           -           -
 * default     set         ==      sal_False           -           -           -
 * dontcare    set         ==      sal_False           -           -           -
 * unknown     set         ==      sal_False           -           -           -
 * set         default     ==      sal_False           -           -           -
 * default     default     ==      sal_False           -           -           -
 * dontcare    default     ==      sal_False           -           -           -
 * unknown     default     ==      sal_False           -           -           -
 * set         dontcare    ==      sal_False        1st Item       -1          -
 * default     dontcare    ==      sal_False           -           -1          -
 * dontcare    dontcare    ==      sal_False           -           -           -
 * unknown     dontcare    ==      sal_False           -           -           -
 * set         unknown     ==      sal_False        1st Item       -1          -
 * default     unknown     ==      sal_False           -           -           -
 * dontcare    unknown     ==      sal_False           -           -           -
 * unknown     unknown     ==      sal_False           -           -           -
 *
 * set         set         !=      sal_False        1st Item       -1          -
 * default     set         !=      sal_False           -           -1          -
 * dontcare    set         !=      sal_False           -           -           -
 * unknown     set         !=      sal_False           -           -           -
 * set         default     !=      sal_False        1st Item       -1          -
 * default     default     !=      sal_False           -           -           -
 * dontcare    default     !=      sal_False           -           -           -
 * unknown     default     !=      sal_False           -           -           -
 * set         dontcare    !=      sal_False        1st Item       -1          -
 * default     dontcare    !=      sal_False           -           -1          -
 * dontcare    dontcare    !=      sal_False           -           -           -
 * unknown     dontcare    !=      sal_False           -           -           -
 * set         unknown     !=      sal_False        1st Item       -1          -
 * default     unknown     !=      sal_False           -           -           -
 * dontcare    unknown     !=      sal_False           -           -           -
 * unknown     unknown     !=      sal_False           -           -           -
 *
 * set         set         ==      sal_True            -           -           -
 * default     set         ==      sal_True            -       2nd Item     2nd Item
 * dontcare    set         ==      sal_True            -           -           -
 * unknown     set         ==      sal_True            -           -           -
 * set         default     ==      sal_True            -           -           -
 * default     default     ==      sal_True            -           -           -
 * dontcare    default     ==      sal_True            -           -           -
 * unknown     default     ==      sal_True            -           -           -
 * set         dontcare    ==      sal_True            -           -           -
 * default     dontcare    ==      sal_True            -           -1          -
 * dontcare    dontcare    ==      sal_True            -           -           -
 * unknown     dontcare    ==      sal_True            -           -           -
 * set         unknown     ==      sal_True            -           -           -
 * default     unknown     ==      sal_True            -           -           -
 * dontcare    unknown     ==      sal_True            -           -           -
 * unknown     unknown     ==      sal_True            -           -           -
 *
 * set         set         !=      sal_True         1st Item       -1          -
 * default     set         !=      sal_True            -        2nd Item    2nd Item
 * dontcare    set         !=      sal_True            -           -           -
 * unknown     set         !=      sal_True            -           -           -
 * set         default     !=      sal_True            -           -           -
 * default     default     !=      sal_True            -           -           -
 * dontcare    default     !=      sal_True            -           -           -
 * unknown     default     !=      sal_True            -           -           -
 * set         dontcare    !=      sal_True         1st Item       -1          -
 * default     dontcare    !=      sal_True            -           -1          -
 * dontcare    dontcare    !=      sal_True            -           -           -
 * unknown     dontcare    !=      sal_True            -           -           -
 * set         unknown     !=      sal_True            -           -           -
 * default     unknown     !=      sal_True            -           -           -
 * dontcare    unknown     !=      sal_True            -           -           -
 * unknown     unknown     !=      sal_True            -           -           -
 */
static void MergeItem_Impl( SfxItemPool *_pPool, sal_uInt16 &rCount,
                            const SfxPoolItem **ppFnd1, const SfxPoolItem *pFnd2,
                            bool bIgnoreDefaults )
{
    assert(ppFnd1 != nullptr && "Merging to 0-Item");

    // 1st Item is Default?
    if ( !*ppFnd1 )
    {
        if ( IsInvalidItem(pFnd2) )
            // Decision table: default, dontcare, doesn't matter, doesn't matter
            *ppFnd1 = INVALID_POOL_ITEM;

        else if ( pFnd2 && !bIgnoreDefaults &&
                  _pPool->GetDefaultItem(pFnd2->Which()) != *pFnd2 )
            // Decision table: default, set, !=, sal_False
            *ppFnd1 = INVALID_POOL_ITEM;

        else if ( pFnd2 && bIgnoreDefaults )
            // Decision table: default, set, doesn't matter, sal_True
            *ppFnd1 = &_pPool->Put( *pFnd2 );

        if ( *ppFnd1 )
            ++rCount;
    }

    // 1st Item set?
    else if ( !IsInvalidItem(*ppFnd1) )
    {
        if ( !pFnd2 )
        {
            // 2nd Item is Default
            if ( !bIgnoreDefaults &&
                 **ppFnd1 != _pPool->GetDefaultItem((*ppFnd1)->Which()) )
            {
                // Decision table: set, default, !=, sal_False
                _pPool->Remove( **ppFnd1 );
                *ppFnd1 = INVALID_POOL_ITEM;
            }
        }
        else if ( IsInvalidItem(pFnd2) )
        {
            // 2nd Item is dontcare
            if ( !bIgnoreDefaults ||
                 **ppFnd1 != _pPool->GetDefaultItem( (*ppFnd1)->Which()) )
            {
                // Decision table: set, dontcare, doesn't matter, sal_False
                // or:             set, dontcare, !=, sal_True
                _pPool->Remove( **ppFnd1 );
                *ppFnd1 = INVALID_POOL_ITEM;
            }
        }
        else
        {
            // 2nd Item is set
            if ( **ppFnd1 != *pFnd2 )
            {
                // Decision table: set, set, !=, doesn't matter
                _pPool->Remove( **ppFnd1 );
                *ppFnd1 = INVALID_POOL_ITEM;
            }
        }
    }
}

void SfxItemSet::MergeValues( const SfxItemSet& rSet )
{
    // WARNING! When making changes/fixing bugs, always update the table above!!
    assert( GetPool() == rSet.GetPool() && "MergeValues with different Pools" );

    // If the Ranges match, they are easier to process!
    if( m_pWhichRanges == rSet.m_pWhichRanges )
    {
        sal_uInt16 nSize = TotalCount();
        SfxPoolItem const** ppFnd1 = m_ppItems;
        SfxPoolItem const** ppFnd2 = rSet.m_ppItems;

        for( ; nSize; --nSize, ++ppFnd1, ++ppFnd2 )
            MergeItem_Impl(m_pPool, m_nCount, ppFnd1, *ppFnd2, false/*bIgnoreDefaults*/);
    }
    else
    {
        SfxWhichIter aIter( rSet );
        sal_uInt16 nWhich;
        while( 0 != ( nWhich = aIter.NextWhich() ) )
        {
            const SfxPoolItem* pItem = nullptr;
            (void)aIter.GetItemState( true, &pItem );
            if( !pItem )
            {
                // Not set, so default
                MergeValue( rSet.GetPool()->GetDefaultItem( nWhich ) );
            }
            else if( IsInvalidItem( pItem ) )
                // don't care
                InvalidateItem( nWhich );
            else
                MergeValue( *pItem );
        }
    }
}

void SfxItemSet::MergeValue( const SfxPoolItem& rAttr, bool bIgnoreDefaults )
{
    SfxPoolItem const** ppFnd = m_ppItems;
    const sal_uInt16 nWhich = rAttr.Which();
    for( auto const & pPtr : m_pWhichRanges )
    {
        // In this Range??
        if( pPtr.first <= nWhich && nWhich <= pPtr.second )
        {
            ppFnd += nWhich - pPtr.first;
            MergeItem_Impl(m_pPool, m_nCount, ppFnd, &rAttr, bIgnoreDefaults);
            break;
        }
        ppFnd += pPtr.second - pPtr.first + 1;
    }
}

void SfxItemSet::InvalidateItem( sal_uInt16 nWhich )
{
    SfxPoolItem const** ppFnd = m_ppItems;
    for( auto const & pPtr : m_pWhichRanges )
    {
        if( pPtr.first <= nWhich && nWhich <= pPtr.second )
        {
            // In this Range?
            ppFnd += nWhich - pPtr.first;

            if( *ppFnd ) // Set for me
            {
                if( !IsInvalidItem(*ppFnd) )
                {
                    m_pPool->Remove( **ppFnd );
                    *ppFnd = INVALID_POOL_ITEM;
                }
            }
            else
            {
                *ppFnd = INVALID_POOL_ITEM;
                ++m_nCount;
            }
            break;
        }
        ppFnd += pPtr.second - pPtr.first + 1;
    }
}

sal_uInt16 SfxItemSet::GetWhichByPos( sal_uInt16 nPos ) const
{
    sal_uInt16 n = 0;
    for( auto const & pPtr : m_pWhichRanges )
    {
        n = ( pPtr.second - pPtr.first ) + 1;
        if( nPos < n )
            return pPtr.first + nPos;
        nPos = nPos - n;
    }
    assert(false);
    return 0;
}

bool SfxItemSet::operator==(const SfxItemSet &rCmp) const
{
    return Equals( rCmp, true);
}

bool SfxItemSet::Equals(const SfxItemSet &rCmp, bool bComparePool) const
{
    // Values we can get quickly need to be the same
    const bool bDifferentPools = (m_pPool != rCmp.m_pPool);
    if ( (bComparePool && m_pParent != rCmp.m_pParent) ||
         (bComparePool && bDifferentPools) ||
         Count() != rCmp.Count() )
        return false;

    // If we reach here and bDifferentPools==true that means bComparePool==false.

    // Counting Ranges takes longer; they also need to be the same, however
    sal_uInt16 nCount1 = TotalCount();
    sal_uInt16 nCount2 = rCmp.TotalCount();
    if ( nCount1 != nCount2 )
        return false;

    // Are the Ranges themselves unequal?
    for (sal_Int32 i = 0; i < m_pWhichRanges.size(); ++i)
    {
        if (m_pWhichRanges[i] != rCmp.m_pWhichRanges[i])
        {
            // We must use the slow method then
            SfxWhichIter aIter( *this );
            for ( sal_uInt16 nWh = aIter.FirstWhich();
                  nWh;
                  nWh = aIter.NextWhich() )
            {
                // If the pointer of the poolable Items are unequal, the Items must match
                const SfxPoolItem *pItem1 = nullptr, *pItem2 = nullptr;
                if ( GetItemState( nWh, false, &pItem1 ) !=
                        rCmp.GetItemState( nWh, false, &pItem2 ) ||
                     ( pItem1 != pItem2 &&
                        ( !pItem1 || IsInvalidItem(pItem1) ||
                          (m_pPool->IsItemPoolable(*pItem1) &&
                            *pItem1 != *pItem2 ) ) ) )
                    return false;
            }

            return true;
        }
    }

    // Are all pointers the same?
    if (0 == memcmp( m_ppItems, rCmp.m_ppItems, nCount1 * sizeof(m_ppItems[0]) ))
        return true;

    // We need to compare each one separately then
    const SfxPoolItem **ppItem1 = m_ppItems;
    const SfxPoolItem **ppItem2 = rCmp.m_ppItems;
    for ( sal_uInt16 nPos = 0; nPos < nCount1; ++nPos )
    {
        // If the pointers of the poolable Items are not the same, the Items
        // must match
        if ( *ppItem1 != *ppItem2 &&
             ( ( !*ppItem1 || !*ppItem2 ) ||
               ( IsInvalidItem(*ppItem1) || IsInvalidItem(*ppItem2) ) ||
               (!bDifferentPools && m_pPool->IsItemPoolable(**ppItem1)) ||
                 **ppItem1 != **ppItem2 ) )
            return false;

        ++ppItem1;
        ++ppItem2;
    }

    return true;
}

std::unique_ptr<SfxItemSet> SfxItemSet::Clone(bool bItems, SfxItemPool *pToPool ) const
{
    if (pToPool && pToPool != m_pPool)
    {
        std::unique_ptr<SfxItemSet> pNewSet(new SfxItemSet(*pToPool, m_pWhichRanges));
        if ( bItems )
        {
            SfxWhichIter aIter(*pNewSet);
            sal_uInt16 nWhich = aIter.FirstWhich();
            while ( nWhich )
            {
                const SfxPoolItem* pItem;
                if ( SfxItemState::SET == GetItemState( nWhich, false, &pItem ) )
                    pNewSet->Put( *pItem, pItem->Which() );
                nWhich = aIter.NextWhich();
            }
        }
        return pNewSet;
    }
    else
        return std::unique_ptr<SfxItemSet>(bItems
                ? new SfxItemSet(*this)
                : new SfxItemSet(*m_pPool, m_pWhichRanges));
}

SfxItemSet SfxItemSet::CloneAsValue(bool bItems, SfxItemPool *pToPool ) const
{
    // if you are trying to clone, then the thing you are cloning is polymorphic, which means
    // it cannot be cloned as a value
    assert((typeid(*this) == typeid(SfxItemSet)) && "cannot call this on a subclass of SfxItemSet");

    if (pToPool && pToPool != m_pPool)
    {
        SfxItemSet aNewSet(*pToPool, m_pWhichRanges);
        if ( bItems )
        {
            SfxWhichIter aIter(aNewSet);
            sal_uInt16 nWhich = aIter.FirstWhich();
            while ( nWhich )
            {
                const SfxPoolItem* pItem;
                if ( SfxItemState::SET == GetItemState( nWhich, false, &pItem ) )
                    aNewSet.Put( *pItem, pItem->Which() );
                nWhich = aIter.NextWhich();
            }
        }
        return aNewSet;
    }
    else
        return bItems
                ? *this
                : SfxItemSet(*m_pPool, m_pWhichRanges);
}

void SfxItemSet::PutDirect(const SfxPoolItem &rItem)
{
    SfxPoolItem const** ppFnd = m_ppItems;
    const sal_uInt16 nWhich = rItem.Which();
#ifdef DBG_UTIL
    IsPoolDefaultItem(&rItem) || m_pPool->CheckItemInPool(&rItem);
        // Only cause assertion in the callees
#endif
    for( auto const & pPtr : m_pWhichRanges)
    {
        if( pPtr.first <= nWhich && nWhich <= pPtr.second )
        {
            // In this Range?
            ppFnd += nWhich - pPtr.first;
            const SfxPoolItem* pOld = *ppFnd;
            if( pOld ) // One already present
            {
                if( rItem == **ppFnd )
                    return; // Already present!
                m_pPool->Remove( *pOld );
            }
            else
                ++m_nCount;

            // Add the new one
            if( IsPoolDefaultItem(&rItem) )
                *ppFnd = &m_pPool->Put( rItem );
            else
            {
                *ppFnd = &rItem;
                if( !IsStaticDefaultItem( &rItem ) )
                    rItem.AddRef();
            }

            return;
        }
        ppFnd += pPtr.second - pPtr.first + 1;
    }
}

void SfxItemSet::dumpAsXml(xmlTextWriterPtr pWriter) const
{
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SfxItemSet"));
    SfxItemIter aIter(*this);
    for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
    {
        if (IsInvalidItem(pItem))
        {
            (void)xmlTextWriterStartElement(pWriter, BAD_CAST("invalid"));
            (void)xmlTextWriterEndElement(pWriter);
        }
        else
        {
            pItem->dumpAsXml(pWriter);
        }
    }
    (void)xmlTextWriterEndElement(pWriter);
}


// ----------------------------------------------- class SfxAllItemSet

SfxAllItemSet::SfxAllItemSet( SfxItemPool &rPool )
:   SfxItemSet(rPool, SfxAllItemSetFlag::Flag)
{
}

SfxAllItemSet::SfxAllItemSet(const SfxItemSet &rCopy)
:   SfxItemSet(rCopy)
{
}

/**
 * Explicitly define this ctor to avoid auto-generation by the compiler.
 * The compiler does not take the ctor with the 'const SfxItemSet&'!
 */
SfxAllItemSet::SfxAllItemSet(const SfxAllItemSet &rCopy)
:   SfxItemSet(rCopy)
{
}

/**
 * Putting with automatic extension of the WhichId with the ID of the Item.
 */
const SfxPoolItem* SfxAllItemSet::PutImpl( const SfxPoolItem& rItem, sal_uInt16 nWhich, bool bPassingOwnership )
{
    MergeRange(nWhich, nWhich);
    return SfxItemSet::PutImpl(rItem, nWhich, bPassingOwnership);
}

/**
 * Disable Item
 * Using a VoidItem with Which value 0
 */
void SfxItemSet::DisableItem(sal_uInt16 nWhich)
{
    Put( SfxVoidItem(0), nWhich );
}

std::unique_ptr<SfxItemSet> SfxAllItemSet::Clone(bool bItems, SfxItemPool *pToPool ) const
{
    if (pToPool && pToPool != m_pPool)
    {
        std::unique_ptr<SfxAllItemSet> pNewSet(new SfxAllItemSet( *pToPool ));
        if ( bItems )
            pNewSet->Set( *this );
        return pNewSet;
    }
    else
        return std::unique_ptr<SfxItemSet>(bItems ? new SfxAllItemSet(*this) : new SfxAllItemSet(*m_pPool));
}


WhichRangesContainer::WhichRangesContainer( const WhichPair* wids, sal_Int32 nSize )
{
    auto p = new WhichPair[nSize];
    for (int i=0; i<nSize; ++i)
        p[i] = wids[i];
    m_pairs = p;
    m_size = nSize;
    m_bOwnRanges = true;
}

WhichRangesContainer::WhichRangesContainer(sal_uInt16 nWhichStart, sal_uInt16 nWhichEnd)
    : m_size(1), m_bOwnRanges(true)
{
    auto p = new WhichPair[1];
    p[0] = { nWhichStart, nWhichEnd };
    m_pairs = p;
}

WhichRangesContainer::WhichRangesContainer(WhichRangesContainer && other)
{
    std::swap(m_pairs, other.m_pairs);
    std::swap(m_size, other.m_size);
    std::swap(m_bOwnRanges, other.m_bOwnRanges);
}

WhichRangesContainer& WhichRangesContainer::operator=(WhichRangesContainer && other)
{
    std::swap(m_pairs, other.m_pairs);
    std::swap(m_size, other.m_size);
    std::swap(m_bOwnRanges, other.m_bOwnRanges);
    return *this;
}

WhichRangesContainer& WhichRangesContainer::operator=(WhichRangesContainer const & other)
{
    reset();
    m_size = other.m_size;
    m_bOwnRanges = other.m_bOwnRanges;
    if (m_bOwnRanges)
    {
        auto p = new WhichPair[m_size];
        for (int i=0; i<m_size; ++i)
            p[i] = other.m_pairs[i];
        m_pairs = p;
    }
    else
        m_pairs = other.m_pairs;
    return *this;
}

WhichRangesContainer::~WhichRangesContainer()
{
    reset();
}

bool WhichRangesContainer::operator==(WhichRangesContainer const & other) const
{
    if (m_size != other.m_size)
        return false;
    if (m_pairs == other.m_pairs)
        return true;
    return std::equal(m_pairs, m_pairs + m_size, other.m_pairs, other.m_pairs + m_size);
}


void WhichRangesContainer::reset()
{
    if (m_bOwnRanges)
    {
        delete [] m_pairs;
        m_bOwnRanges = false;
    }
    m_pairs = nullptr;
    m_size = 0;
}

// Adds a range to which ranges, keeping the ranges in valid state (sorted, non-overlapping)
WhichRangesContainer WhichRangesContainer::MergeRange(sal_uInt16 nFrom,
                                       sal_uInt16 nTo) const
{
    assert(svl::detail::validRange(nFrom, nTo));

    if (empty())
        return WhichRangesContainer(nFrom, nTo);

    // create vector of ranges (sal_uInt16 pairs of lower and upper bound)
    const size_t nOldCount = size();
    // Allocate one item more than we already have.
    // In the worst case scenario we waste a little bit
    // of memory, but we avoid another allocation, which is more important.
    std::unique_ptr<WhichPair[]> aRangesTable(new WhichPair[nOldCount+1]);
    int aRangesTableSize = 0;
    bool bAdded = false;
    for (const auto& rPair : *this)
    {
        if (!bAdded && rPair.first >= nFrom)
        { // insert new range, keep ranges sorted
            aRangesTable[aRangesTableSize++] = { nFrom, nTo };
            bAdded = true;
        }
        // insert current range
        aRangesTable[aRangesTableSize++] = rPair;
    }
    if (!bAdded)
        aRangesTable[aRangesTableSize++] = { nFrom, nTo };

    // true if ranges overlap or adjoin, false if ranges are separate
    auto needMerge = [](WhichPair lhs, WhichPair rhs) {
        return (lhs.first - 1) <= rhs.second && (rhs.first - 1) <= lhs.second;
    };

    auto it = aRangesTable.get();
    auto endIt = aRangesTable.get() + aRangesTableSize;
    // we have at least one range at this point
    for (;;)
    {
        auto itNext = std::next(it);
        if (itNext == endIt)
            break;
        // check if neighbouring ranges overlap or adjoin
        if (needMerge(*it, *itNext))
        {
            // lower bounds are sorted, implies: it->first = min(it[0].first, it[1].first)
            it->second = std::max(it->second, itNext->second);
            // remove next element
            std::move(std::next(itNext), endIt, itNext);
            --aRangesTableSize;
            endIt = aRangesTable.get() + aRangesTableSize;
        }
        else
            ++it;
    }

    return WhichRangesContainer(std::move(aRangesTable), aRangesTableSize);
}

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