fc6aa9e499
finger trouble in
commit c757117afb
tdf#81765 slow loading of .ods with >1000 of conditional formats
Spotted by sberg.
Turns out my original idea idea with the assert was too restrictive,
so make the check a little smarter.
Change-Id: Ie735d00c87a05a70e8a71f03e75043dcd8dfc88f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143217
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
1012 lines
31 KiB
C++
1012 lines
31 KiB
C++
/* -*- 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 <svl/itempool.hxx>
|
|
#include <svl/setitem.hxx>
|
|
|
|
#include <string.h>
|
|
#include <libxml/xmlwriter.h>
|
|
|
|
#include <osl/diagnose.h>
|
|
#include <sal/log.hxx>
|
|
#include <svl/SfxBroadcaster.hxx>
|
|
#include <svl/hint.hxx>
|
|
#include <svl/itemset.hxx>
|
|
|
|
#include <poolio.hxx>
|
|
|
|
#include <cassert>
|
|
#include <vector>
|
|
|
|
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
#include <map>
|
|
|
|
static void
|
|
lcl_CheckSlots2(std::map<sal_uInt16, sal_uInt16> & rSlotMap,
|
|
SfxItemPool const& rPool, SfxItemInfo const* pInfo)
|
|
{
|
|
if (!pInfo)
|
|
return; // may not be initialized yet
|
|
if (rPool.GetName() == "EditEngineItemPool")
|
|
return; // HACK: this one has loads of duplicates already, ignore it :(
|
|
sal_uInt16 const nFirst(rPool.GetFirstWhich());
|
|
sal_uInt16 const nCount(rPool.GetLastWhich() - rPool.GetFirstWhich() + 1);
|
|
for (sal_uInt16 n = 0; n < nCount; ++n)
|
|
{
|
|
sal_uInt16 const nSlotId(pInfo[n]._nSID);
|
|
if (nSlotId != 0
|
|
&& nSlotId != 10883 // preexisting duplicate SID_ATTR_GRAF_CROP
|
|
&& nSlotId != 10023 // preexisting duplicate SID_ATTR_BORDER_INNER
|
|
&& nSlotId != 10024 // preexisting duplicate SID_ATTR_BORDER_OUTER
|
|
&& nSlotId != 11013 // preexisting duplicate SID_ATTR_BORDER_DIAG_TLBR
|
|
&& nSlotId != 11014) // preexisting duplicate SID_ATTR_BORDER_DIAG_BLTR
|
|
{ // check for duplicate slot-id mapping
|
|
std::map<sal_uInt16, sal_uInt16>::const_iterator const iter(
|
|
rSlotMap.find(nSlotId));
|
|
sal_uInt16 const nWhich(nFirst + n);
|
|
if (iter != rSlotMap.end())
|
|
{
|
|
SAL_WARN("svl", "SfxItemPool: duplicate SlotId " << nSlotId
|
|
<< " mapped to " << iter->second << " and " << nWhich);
|
|
assert(false);
|
|
}
|
|
rSlotMap.insert(std::make_pair(nSlotId, nWhich));
|
|
}
|
|
}
|
|
}
|
|
|
|
#define CHECK_SLOTS() \
|
|
do { \
|
|
std::map<sal_uInt16, sal_uInt16> slotmap; \
|
|
for (SfxItemPool * p = pImpl->mpMaster; p; p = p->pImpl->mpSecondary.get()) \
|
|
{ \
|
|
lcl_CheckSlots2(slotmap, *p, p->pItemInfos); \
|
|
} \
|
|
} while (false)
|
|
|
|
#else
|
|
#define CHECK_SLOTS() do {} while (false)
|
|
#endif
|
|
|
|
/// clear array of PoolItem variants
|
|
/// after all PoolItems are deleted
|
|
/// or all ref counts are decreased
|
|
void SfxPoolItemArray_Impl::clear()
|
|
{
|
|
maPoolItemSet.clear();
|
|
maSortablePoolItems.clear();
|
|
}
|
|
|
|
sal_uInt16 SfxItemPool::GetFirstWhich() const
|
|
{
|
|
return pImpl->mnStart;
|
|
}
|
|
|
|
sal_uInt16 SfxItemPool::GetLastWhich() const
|
|
{
|
|
return pImpl->mnEnd;
|
|
}
|
|
|
|
bool SfxItemPool::IsInRange( sal_uInt16 nWhich ) const
|
|
{
|
|
return nWhich >= pImpl->mnStart && nWhich <= pImpl->mnEnd;
|
|
}
|
|
|
|
sal_uInt16 SfxItemPool::GetIndex_Impl(sal_uInt16 nWhich) const
|
|
{
|
|
if (nWhich < pImpl->mnStart || nWhich > pImpl->mnEnd)
|
|
{
|
|
assert(false && "missing bounds check before use");
|
|
return 0;
|
|
}
|
|
return nWhich - pImpl->mnStart;
|
|
}
|
|
|
|
sal_uInt16 SfxItemPool::GetSize_Impl() const
|
|
{
|
|
return pImpl->mnEnd - pImpl->mnStart + 1;
|
|
}
|
|
|
|
|
|
bool SfxItemPool::CheckItemInPool(const SfxPoolItem *pItem) const
|
|
{
|
|
DBG_ASSERT( pItem, "no 0-Pointer Surrogate" );
|
|
DBG_ASSERT( !IsInvalidItem(pItem), "no Invalid-Item Surrogate" );
|
|
DBG_ASSERT( !IsPoolDefaultItem(pItem), "no Pool-Default-Item Surrogate" );
|
|
|
|
if ( !IsInRange(pItem->Which()) )
|
|
{
|
|
if ( pImpl->mpSecondary )
|
|
return pImpl->mpSecondary->CheckItemInPool( pItem );
|
|
SAL_WARN( "svl.items", "unknown Which-Id - don't ask me for surrogates, with ID/pos " << pItem->Which());
|
|
}
|
|
|
|
// Pointer on static or pool-default attribute?
|
|
if( IsStaticDefaultItem(pItem) || IsPoolDefaultItem(pItem) )
|
|
return true;
|
|
|
|
SfxPoolItemArray_Impl& rItemArr = pImpl->maPoolItemArrays[GetIndex_Impl(pItem->Which())];
|
|
|
|
for ( auto p : rItemArr )
|
|
{
|
|
if ( p == pItem )
|
|
return true;
|
|
}
|
|
SAL_WARN( "svl.items", "Item not in the pool, with ID/pos " << pItem->Which());
|
|
return false;
|
|
}
|
|
|
|
const SfxPoolItem* SfxItemPool::GetPoolDefaultItem( sal_uInt16 nWhich ) const
|
|
{
|
|
const SfxPoolItem* pRet;
|
|
if( IsInRange( nWhich ) )
|
|
pRet = pImpl->maPoolDefaults[GetIndex_Impl(nWhich)];
|
|
else if( pImpl->mpSecondary )
|
|
pRet = pImpl->mpSecondary->GetPoolDefaultItem( nWhich );
|
|
else
|
|
{
|
|
assert(false && "unknown WhichId - cannot get pool default");
|
|
pRet = nullptr;
|
|
}
|
|
return pRet;
|
|
}
|
|
|
|
|
|
bool SfxItemPool::IsItemPoolable_Impl( sal_uInt16 nPos ) const
|
|
{
|
|
return pItemInfos[nPos]._bPoolable;
|
|
}
|
|
|
|
|
|
bool SfxItemPool::IsItemPoolable( sal_uInt16 nWhich ) const
|
|
{
|
|
for ( const SfxItemPool *pPool = this; pPool; pPool = pPool->pImpl->mpSecondary.get() )
|
|
{
|
|
if ( pPool->IsInRange(nWhich) )
|
|
return pPool->IsItemPoolable_Impl( pPool->GetIndex_Impl(nWhich));
|
|
}
|
|
DBG_ASSERT( !IsWhich(nWhich), "unknown which-id" );
|
|
return false;
|
|
}
|
|
|
|
|
|
SfxBroadcaster& SfxItemPool::BC()
|
|
{
|
|
return pImpl->aBC;
|
|
}
|
|
|
|
|
|
/**
|
|
* This is the regular ctor to be used for this class.
|
|
* An SfxItemPool instance is initialized, which can manage Items in the
|
|
* range from 'nStartWhich' to 'nEndWhich'.
|
|
*
|
|
* For every one of these WhichIds a static Default must be present in the
|
|
* 'pDefaults' array. They start with an SfxPoolItem (with the WhichId
|
|
* 'nStartWhich'), are sorted by WhichId and consecutively stored.
|
|
*
|
|
* 'pItemInfos' is a USHORT array arranged in the same way, which holds
|
|
* SlotIds and Flags. These SlotIds can be 0, if the affected Items are
|
|
* exclusively used in the Core.
|
|
* The flags allow for e.g. enabling value sharing (poolable).
|
|
*
|
|
* If the Pool is supposed to hold SfxSetItems, the ctor cannot yet contain
|
|
* static Defaults. This needs to be done afterwards, using
|
|
* @see SfxItemPool::SetDefaults(std::vector<SfxPoolItem*>*).
|
|
*
|
|
* @see SfxItemPool::SetDefaults(std::vector<SfxPoolItem*>*)
|
|
* @see SfxItemPool::ReleaseDefaults(std::vector<SfxPoolItem*>*,bool)
|
|
* @see SfxItemPool::ReleaseDefaults(bool)
|
|
*/
|
|
SfxItemPool::SfxItemPool
|
|
(
|
|
const OUString& rName, /* Pool name to identify in the file format */
|
|
sal_uInt16 nStartWhich, /* First WhichId of the Pool (must be > 0) */
|
|
sal_uInt16 nEndWhich, /* Last WhichId of the Pool */
|
|
const SfxItemInfo* pInfo, /* SID Map and Item flags */
|
|
std::vector<SfxPoolItem*>*
|
|
pDefaults /* Pointer to static Defaults;
|
|
is directly referenced by the Pool,
|
|
but no transfer of ownership */
|
|
) :
|
|
pItemInfos(pInfo),
|
|
pImpl( new SfxItemPool_Impl( this, rName, nStartWhich, nEndWhich ) )
|
|
{
|
|
pImpl->eDefMetric = MapUnit::MapTwip;
|
|
|
|
if ( pDefaults )
|
|
SetDefaults(pDefaults);
|
|
|
|
#ifdef DBG_UTIL
|
|
if (pItemInfos)
|
|
{
|
|
auto p = pItemInfos;
|
|
auto nWhich = nStartWhich;
|
|
while (nWhich <= nEndWhich)
|
|
{
|
|
if (p->_nSID == nWhich)
|
|
{
|
|
SAL_WARN("svl.items", "No point mapping a SID to itself, just put a 0 here in the SfxItemInfo array, at index " << (p - pItemInfos));
|
|
assert(false);
|
|
}
|
|
++p;
|
|
++nWhich;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
* Copy ctor
|
|
*
|
|
* @see SfxItemPool::Clone() const
|
|
*/
|
|
SfxItemPool::SfxItemPool
|
|
(
|
|
const SfxItemPool& rPool, // Copy from this instance
|
|
bool bCloneStaticDefaults /* true
|
|
Copy static Defaults
|
|
|
|
false
|
|
Take over static Defaults */
|
|
) :
|
|
salhelper::SimpleReferenceObject(),
|
|
pItemInfos(rPool.pItemInfos),
|
|
pImpl( new SfxItemPool_Impl( this, rPool.pImpl->aName, rPool.pImpl->mnStart, rPool.pImpl->mnEnd ) )
|
|
{
|
|
pImpl->eDefMetric = rPool.pImpl->eDefMetric;
|
|
|
|
// Take over static Defaults
|
|
if ( bCloneStaticDefaults )
|
|
{
|
|
std::vector<SfxPoolItem *>* ppDefaults = new std::vector<SfxPoolItem*>(pImpl->mnEnd-pImpl->mnStart+1);
|
|
for ( sal_uInt16 n = 0; n <= pImpl->mnEnd - pImpl->mnStart; ++n )
|
|
{
|
|
(*ppDefaults)[n] = (*rPool.pImpl->mpStaticDefaults)[n]->Clone(this);
|
|
(*ppDefaults)[n]->SetKind(SfxItemKind::StaticDefault);
|
|
}
|
|
|
|
SetDefaults( ppDefaults );
|
|
}
|
|
else
|
|
SetDefaults( rPool.pImpl->mpStaticDefaults );
|
|
|
|
// Copy Pool Defaults
|
|
for (size_t n = 0; n < pImpl->maPoolDefaults.size(); ++n )
|
|
if (rPool.pImpl->maPoolDefaults[n])
|
|
{
|
|
pImpl->maPoolDefaults[n] = rPool.pImpl->maPoolDefaults[n]->Clone(this); //resets kind
|
|
pImpl->maPoolDefaults[n]->SetKind(SfxItemKind::PoolDefault);
|
|
}
|
|
|
|
// Repair linkage
|
|
if ( rPool.pImpl->mpSecondary )
|
|
SetSecondaryPool( rPool.pImpl->mpSecondary->Clone().get() );
|
|
}
|
|
|
|
void SfxItemPool::SetDefaults( std::vector<SfxPoolItem*>* pDefaults )
|
|
{
|
|
DBG_ASSERT( pDefaults, "first we ask for it, and then we don't give back..." );
|
|
DBG_ASSERT( !pImpl->mpStaticDefaults, "already have Defaults" );
|
|
|
|
pImpl->mpStaticDefaults = pDefaults;
|
|
//! if ((*mpStaticDefaults)->GetKind() != SfxItemKind::StaticDefault)
|
|
//! FIXME: Probably doesn't work with SetItems at the end
|
|
{
|
|
DBG_ASSERT( (*pImpl->mpStaticDefaults)[0]->GetRefCount() == 0 ||
|
|
IsDefaultItem( (*pImpl->mpStaticDefaults)[0] ),
|
|
"these are not static" );
|
|
for ( sal_uInt16 n = 0; n <= pImpl->mnEnd - pImpl->mnStart; ++n )
|
|
{
|
|
assert( ((*pImpl->mpStaticDefaults)[n]->Which() == n + pImpl->mnStart)
|
|
&& "items ids in pool-ranges and in static-defaults do not match" );
|
|
(*pImpl->mpStaticDefaults)[n]->SetKind(SfxItemKind::StaticDefault);
|
|
DBG_ASSERT( pImpl->maPoolItemArrays[n].empty(), "defaults with setitems with items?!" );
|
|
}
|
|
}
|
|
}
|
|
|
|
void SfxItemPool::ClearDefaults()
|
|
{
|
|
pImpl->mpStaticDefaults = nullptr;
|
|
}
|
|
|
|
/**
|
|
* Frees the static Defaults of the corresponding SfxItemPool instance
|
|
* and deletes them if specified.
|
|
*
|
|
* The SfxItemPool instance MUST NOT BE USED after this function has
|
|
* been called; only the dtor must be called.
|
|
*/
|
|
void SfxItemPool::ReleaseDefaults
|
|
(
|
|
bool bDelete /* true
|
|
Deletes the array as well as the single static Defaults
|
|
|
|
false
|
|
Neither deletes the array not the single static Defaults */
|
|
)
|
|
|
|
|
|
{
|
|
DBG_ASSERT( pImpl->mpStaticDefaults, "requirements not met" );
|
|
ReleaseDefaults( pImpl->mpStaticDefaults, bDelete );
|
|
|
|
// mpStaticDefaults points to deleted memory if bDelete == true.
|
|
if ( bDelete )
|
|
pImpl->mpStaticDefaults = nullptr;
|
|
}
|
|
|
|
|
|
/**
|
|
* Frees the specified static Defaults and also deletes them, if so
|
|
* specified.
|
|
*
|
|
* This method MUST be called AFTER all SfxItemPool instances (which
|
|
* use the specified static Defaults 'pDefault') have been destroyed.
|
|
*/
|
|
void SfxItemPool::ReleaseDefaults
|
|
(
|
|
std::vector<SfxPoolItem*>*
|
|
pDefaults, /* Static Defaults that are to be freed */
|
|
|
|
bool bDelete /* true
|
|
Deletes the array as well as the specified
|
|
static Defaults
|
|
|
|
false
|
|
Neither deletes the array nor the single
|
|
static Defaults */
|
|
)
|
|
{
|
|
DBG_ASSERT( pDefaults, "we first ask for it and the return nothing ..." );
|
|
|
|
for ( auto & rpItem : *pDefaults )
|
|
{
|
|
assert(IsStaticDefaultItem(rpItem));
|
|
rpItem->SetRefCount(0);
|
|
if ( bDelete )
|
|
{
|
|
delete rpItem;
|
|
rpItem = nullptr;
|
|
}
|
|
}
|
|
|
|
if ( bDelete )
|
|
{
|
|
delete pDefaults;
|
|
pDefaults = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
SfxItemPool::~SfxItemPool()
|
|
{
|
|
if ( !pImpl->maPoolItemArrays.empty() && !pImpl->maPoolDefaults.empty() )
|
|
Delete();
|
|
|
|
if (pImpl->mpMaster != nullptr && pImpl->mpMaster != this)
|
|
{
|
|
// This condition indicates an error.
|
|
// A pImpl->mpMaster->SetSecondaryPool(...) call should have been made
|
|
// earlier to prevent this. At this point we can only try to
|
|
// prevent a crash later on.
|
|
DBG_ASSERT( pImpl->mpMaster == this, "destroying active Secondary-Pool" );
|
|
if (pImpl->mpMaster->pImpl->mpSecondary == this)
|
|
pImpl->mpMaster->pImpl->mpSecondary = nullptr;
|
|
}
|
|
}
|
|
|
|
void SfxItemPool::SetSecondaryPool( SfxItemPool *pPool )
|
|
{
|
|
// Reset Master in attached Pools
|
|
if ( pImpl->mpSecondary )
|
|
{
|
|
#ifdef DBG_UTIL
|
|
if (pImpl->mpStaticDefaults != nullptr && !pImpl->maPoolItemArrays.empty()
|
|
&& !pImpl->mpSecondary->pImpl->maPoolItemArrays.empty())
|
|
// Delete() did not yet run?
|
|
{
|
|
// Does the Master have SetItems?
|
|
bool bHasSetItems = false;
|
|
for ( sal_uInt16 i = 0; !bHasSetItems && i < pImpl->mnEnd - pImpl->mnStart; ++i )
|
|
bHasSetItems = dynamic_cast<const SfxSetItem *>((*pImpl->mpStaticDefaults)[i]) != nullptr;
|
|
|
|
// Detached Pools must be empty
|
|
bool bOK = bHasSetItems;
|
|
for (auto const& rSecArray : pImpl->mpSecondary->pImpl->maPoolItemArrays)
|
|
{
|
|
if (!bOK)
|
|
break;
|
|
if (rSecArray.size()>0)
|
|
{
|
|
SAL_WARN("svl.items", "old secondary pool: " << pImpl->mpSecondary->pImpl->aName
|
|
<< " of pool: " << pImpl->aName << " must be empty.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pImpl->mpSecondary->pImpl->mpMaster = pImpl->mpSecondary.get();
|
|
for ( SfxItemPool *p = pImpl->mpSecondary->pImpl->mpSecondary.get(); p; p = p->pImpl->mpSecondary.get() )
|
|
p->pImpl->mpMaster = pImpl->mpSecondary.get();
|
|
}
|
|
|
|
// Set Master of new Secondary Pools
|
|
DBG_ASSERT( !pPool || pPool->pImpl->mpMaster == pPool, "Secondary is present in two Pools" );
|
|
SfxItemPool *pNewMaster = GetMasterPool() ? pImpl->mpMaster : this;
|
|
for ( SfxItemPool *p = pPool; p; p = p->pImpl->mpSecondary.get() )
|
|
p->pImpl->mpMaster = pNewMaster;
|
|
|
|
// Remember new Secondary Pool
|
|
pImpl->mpSecondary = pPool;
|
|
|
|
CHECK_SLOTS();
|
|
}
|
|
|
|
void SfxItemPool::SetItemInfos(SfxItemInfo const*const pInfo)
|
|
{
|
|
pItemInfos = pInfo;
|
|
CHECK_SLOTS();
|
|
}
|
|
|
|
|
|
MapUnit SfxItemPool::GetMetric( sal_uInt16 ) const
|
|
{
|
|
return pImpl->eDefMetric;
|
|
}
|
|
|
|
|
|
void SfxItemPool::SetDefaultMetric( MapUnit eNewMetric )
|
|
{
|
|
// assert((pImpl->eDefMetric == eNewMetric || !pImpl->mpPoolRanges) && "pool already frozen, cannot change metric");
|
|
pImpl->eDefMetric = eNewMetric;
|
|
}
|
|
|
|
MapUnit SfxItemPool::GetDefaultMetric() const
|
|
{
|
|
return pImpl->eDefMetric;
|
|
}
|
|
|
|
const OUString& SfxItemPool::GetName() const
|
|
{
|
|
return pImpl->aName;
|
|
}
|
|
|
|
|
|
bool SfxItemPool::GetPresentation
|
|
(
|
|
const SfxPoolItem& rItem,
|
|
MapUnit eMetric,
|
|
OUString& rText,
|
|
const IntlWrapper& rIntlWrapper
|
|
) const
|
|
{
|
|
return rItem.GetPresentation(
|
|
SfxItemPresentation::Complete, GetMetric(rItem.Which()), eMetric, rText, rIntlWrapper );
|
|
}
|
|
|
|
|
|
rtl::Reference<SfxItemPool> SfxItemPool::Clone() const
|
|
{
|
|
return new SfxItemPool( *this );
|
|
}
|
|
|
|
|
|
void SfxItemPool::Delete()
|
|
{
|
|
// Already deleted?
|
|
if (pImpl->maPoolItemArrays.empty() || pImpl->maPoolDefaults.empty())
|
|
return;
|
|
|
|
// Inform e.g. running Requests
|
|
pImpl->aBC.Broadcast( SfxHint( SfxHintId::Dying ) );
|
|
|
|
// Iterate through twice: first for the SetItems.
|
|
if (pImpl->mpStaticDefaults != nullptr) {
|
|
for (size_t n = 0; n < GetSize_Impl(); ++n)
|
|
{
|
|
// *mpStaticDefaultItem could've already been deleted in a class derived
|
|
// from SfxItemPool
|
|
// This causes chaos in Itempool!
|
|
const SfxPoolItem* pStaticDefaultItem = (*pImpl->mpStaticDefaults)[n];
|
|
if (dynamic_cast<const SfxSetItem*>(pStaticDefaultItem))
|
|
{
|
|
// SfxSetItem found, remove PoolItems (and defaults) with same ID
|
|
auto& rArray = pImpl->maPoolItemArrays[n];
|
|
for (auto& rItemPtr : rArray)
|
|
{
|
|
ReleaseRef(*rItemPtr, rItemPtr->GetRefCount()); // for RefCount check in dtor
|
|
delete rItemPtr;
|
|
}
|
|
rArray.clear();
|
|
// let pImpl->DeleteItems() delete item arrays in maPoolItems
|
|
auto& rItemPtr = pImpl->maPoolDefaults[n];
|
|
if (rItemPtr)
|
|
{
|
|
#ifdef DBG_UTIL
|
|
ClearRefCount(*rItemPtr);
|
|
#endif
|
|
delete rItemPtr;
|
|
rItemPtr = nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now remove remaining PoolItems (and defaults) who didn't have SetItems
|
|
for (auto& rArray : pImpl->maPoolItemArrays)
|
|
{
|
|
for (auto& rItemPtr : rArray)
|
|
{
|
|
ReleaseRef(*rItemPtr, rItemPtr->GetRefCount()); // for RefCount check in dtor
|
|
delete rItemPtr;
|
|
}
|
|
rArray.clear();
|
|
// let pImpl->DeleteItems() delete item arrays in maPoolItems
|
|
}
|
|
pImpl->maPoolItemArrays.clear();
|
|
// default items
|
|
for (auto rItemPtr : pImpl->maPoolDefaults)
|
|
{
|
|
if (rItemPtr)
|
|
{
|
|
#ifdef DBG_UTIL
|
|
ClearRefCount(*rItemPtr);
|
|
#endif
|
|
delete rItemPtr;
|
|
rItemPtr = nullptr;
|
|
}
|
|
}
|
|
|
|
pImpl->DeleteItems();
|
|
}
|
|
|
|
|
|
void SfxItemPool::SetPoolDefaultItem(const SfxPoolItem &rItem)
|
|
{
|
|
if ( IsInRange(rItem.Which()) )
|
|
{
|
|
auto& rOldDefault =
|
|
pImpl->maPoolDefaults[GetIndex_Impl(rItem.Which())];
|
|
SfxPoolItem *pNewDefault = rItem.Clone(this);
|
|
pNewDefault->SetKind(SfxItemKind::PoolDefault);
|
|
if (rOldDefault)
|
|
{
|
|
rOldDefault->SetRefCount(0);
|
|
delete rOldDefault;
|
|
rOldDefault = nullptr;
|
|
}
|
|
rOldDefault = pNewDefault;
|
|
}
|
|
else if ( pImpl->mpSecondary )
|
|
pImpl->mpSecondary->SetPoolDefaultItem(rItem);
|
|
else
|
|
{
|
|
assert(false && "unknown WhichId - cannot set pool default");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets the default of the given WhichId back to the static Default.
|
|
* If a pool default exists, it is removed.
|
|
*/
|
|
void SfxItemPool::ResetPoolDefaultItem( sal_uInt16 nWhichId )
|
|
{
|
|
if ( IsInRange(nWhichId) )
|
|
{
|
|
auto& rOldDefault =
|
|
pImpl->maPoolDefaults[GetIndex_Impl(nWhichId)];
|
|
if (rOldDefault)
|
|
{
|
|
rOldDefault->SetRefCount(0);
|
|
delete rOldDefault;
|
|
rOldDefault = nullptr;
|
|
}
|
|
}
|
|
else if ( pImpl->mpSecondary )
|
|
pImpl->mpSecondary->ResetPoolDefaultItem(nWhichId);
|
|
else
|
|
{
|
|
assert(false && "unknown WhichId - cannot reset pool default");
|
|
}
|
|
}
|
|
|
|
|
|
const SfxPoolItem& SfxItemPool::PutImpl( const SfxPoolItem& rItem, sal_uInt16 nWhich, bool bPassingOwnership )
|
|
{
|
|
if ( 0 == nWhich )
|
|
nWhich = rItem.Which();
|
|
|
|
// Find correct Secondary Pool
|
|
bool bSID = IsSlot(nWhich);
|
|
if ( !bSID && !IsInRange(nWhich) )
|
|
{
|
|
if ( pImpl->mpSecondary )
|
|
return pImpl->mpSecondary->PutImpl( rItem, nWhich, bPassingOwnership );
|
|
OSL_FAIL( "unknown WhichId - cannot put item" );
|
|
}
|
|
|
|
// SID ?
|
|
if (bSID)
|
|
{
|
|
assert((rItem.Which() != nWhich ||
|
|
!IsDefaultItem(&rItem) || rItem.GetKind() == SfxItemKind::DeleteOnIdle)
|
|
&& "a non Pool Item is Default?!");
|
|
SfxPoolItem *pPoolItem = rItem.Clone(pImpl->mpMaster);
|
|
pPoolItem->SetWhich(nWhich);
|
|
AddRef( *pPoolItem );
|
|
if (bPassingOwnership)
|
|
delete &rItem;
|
|
return *pPoolItem;
|
|
}
|
|
|
|
assert(!pImpl->mpStaticDefaults ||
|
|
typeid(rItem) == typeid(GetDefaultItem(nWhich)));
|
|
|
|
const sal_uInt16 nIndex = GetIndex_Impl(nWhich);
|
|
SfxPoolItemArray_Impl& rItemArr = pImpl->maPoolItemArrays[nIndex];
|
|
|
|
// Is this a 'poolable' item - ie. should we re-use and return
|
|
// the same underlying item for equivalent (==) SfxPoolItems?
|
|
if ( IsItemPoolable_Impl( nIndex ) )
|
|
{
|
|
// if is already in a pool, then it is worth checking if it is in this one.
|
|
if ( IsPooledItem(&rItem) )
|
|
{
|
|
// 1. search for an identical pointer in the pool
|
|
auto it = rItemArr.find(const_cast<SfxPoolItem *>(&rItem));
|
|
if (it != rItemArr.end())
|
|
{
|
|
AddRef(rItem);
|
|
assert(!bPassingOwnership && "can't be passing ownership and have the item already in the pool");
|
|
return rItem;
|
|
}
|
|
}
|
|
|
|
const SfxPoolItem* pFoundItem = nullptr;
|
|
// 2. search for an item with matching attributes.
|
|
if (rItem.IsSortable())
|
|
{
|
|
pFoundItem = rItemArr.findByLessThan(&rItem);
|
|
if (pFoundItem)
|
|
assert(*pFoundItem == rItem);
|
|
}
|
|
else if(rItem.HasLookup())
|
|
{
|
|
auto it = rItem.Lookup(rItemArr.begin(), rItemArr.end());
|
|
if( it != rItemArr.end())
|
|
pFoundItem = *it;
|
|
}
|
|
else
|
|
{
|
|
for (auto it = rItemArr.begin(); it != rItemArr.end(); ++it)
|
|
{
|
|
if (**it == rItem)
|
|
{
|
|
pFoundItem = *it;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (pFoundItem)
|
|
{
|
|
assert((!bPassingOwnership || (&rItem != pFoundItem)) && "can't be passing ownership and have the item already in the pool");
|
|
AddRef(*pFoundItem);
|
|
if (bPassingOwnership)
|
|
delete &rItem;
|
|
return *pFoundItem;
|
|
}
|
|
}
|
|
|
|
// 3. not found, so clone to insert into the pointer array.
|
|
SfxPoolItem* pNewItem;
|
|
if (bPassingOwnership)
|
|
{
|
|
#ifndef NDEBUG
|
|
if (auto pSetItem = dynamic_cast<const SfxSetItem*>(&rItem))
|
|
assert(pSetItem->GetItemSet().GetPool() == pImpl->mpMaster && "can't pass ownership of SfxSetItem, unless they have been cloned to the master pool");
|
|
#endif
|
|
pNewItem = const_cast<SfxPoolItem*>(&rItem);
|
|
}
|
|
else
|
|
pNewItem = rItem.Clone(pImpl->mpMaster);
|
|
pNewItem->SetWhich(nWhich);
|
|
assert(typeid(rItem) == typeid(*pNewItem) && "SfxItemPool::Put(): unequal types, no Clone() override?");
|
|
#ifndef NDEBUG
|
|
if (dynamic_cast<const SfxSetItem*>(&rItem) == nullptr)
|
|
{
|
|
assert((!IsItemPoolable(nWhich) || rItem == *pNewItem)
|
|
&& "SfxItemPool::Put(): unequal items: no operator== override?");
|
|
assert((!IsItemPoolable(*pNewItem) || *pNewItem == rItem)
|
|
&& "SfxItemPool::Put(): unequal items: no operator== override?");
|
|
}
|
|
#endif
|
|
AddRef( *pNewItem );
|
|
|
|
// 4. finally insert into the pointer array
|
|
rItemArr.insert( pNewItem );
|
|
return *pNewItem;
|
|
}
|
|
|
|
void SfxItemPool::Remove( const SfxPoolItem& rItem )
|
|
{
|
|
assert(!IsPoolDefaultItem(&rItem) && "cannot remove Pool Default");
|
|
|
|
// Find correct Secondary Pool
|
|
const sal_uInt16 nWhich = rItem.Which();
|
|
bool bSID = IsSlot(nWhich);
|
|
if ( !bSID && !IsInRange(nWhich) )
|
|
{
|
|
if ( pImpl->mpSecondary )
|
|
{
|
|
pImpl->mpSecondary->Remove( rItem );
|
|
return;
|
|
}
|
|
OSL_FAIL( "unknown WhichId - cannot remove item" );
|
|
}
|
|
|
|
// SID ?
|
|
if ( bSID )
|
|
{
|
|
assert(!IsDefaultItem(&rItem) && "a non Pool Item is Default?!");
|
|
if ( 0 == ReleaseRef(rItem) )
|
|
{
|
|
delete &rItem;
|
|
}
|
|
return;
|
|
}
|
|
|
|
assert(rItem.GetRefCount() && "RefCount == 0, Remove impossible");
|
|
|
|
const sal_uInt16 nIndex = GetIndex_Impl(nWhich);
|
|
// Static Defaults are just there
|
|
if ( IsStaticDefaultItem(&rItem) &&
|
|
&rItem == (*pImpl->mpStaticDefaults)[nIndex])
|
|
return;
|
|
|
|
// Find Item in own Pool
|
|
SfxPoolItemArray_Impl& rItemArr = pImpl->maPoolItemArrays[nIndex];
|
|
|
|
auto it = rItemArr.find(const_cast<SfxPoolItem *>(&rItem));
|
|
if (it != rItemArr.end())
|
|
{
|
|
if ( rItem.GetRefCount() ) //!
|
|
ReleaseRef( rItem );
|
|
else
|
|
{
|
|
assert(false && "removing Item without ref");
|
|
}
|
|
|
|
// FIXME: Hack, for as long as we have problems with the Outliner
|
|
// See other MI-REF
|
|
if ( 0 == rItem.GetRefCount() && nWhich < 4000 )
|
|
{
|
|
rItemArr.erase(it);
|
|
delete &rItem;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// not found
|
|
assert(false && "removing Item not in Pool");
|
|
}
|
|
|
|
|
|
const SfxPoolItem& SfxItemPool::GetDefaultItem( sal_uInt16 nWhich ) const
|
|
{
|
|
if ( !IsInRange(nWhich) )
|
|
{
|
|
if ( pImpl->mpSecondary )
|
|
return pImpl->mpSecondary->GetDefaultItem( nWhich );
|
|
assert(!"unknown which - don't ask me for defaults");
|
|
}
|
|
|
|
DBG_ASSERT( pImpl->mpStaticDefaults, "no defaults known - don't ask me for defaults" );
|
|
sal_uInt16 nPos = GetIndex_Impl(nWhich);
|
|
SfxPoolItem* pDefault = pImpl->maPoolDefaults[nPos];
|
|
if ( pDefault )
|
|
return *pDefault;
|
|
return *(*pImpl->mpStaticDefaults)[nPos];
|
|
}
|
|
|
|
SfxItemPool* SfxItemPool::GetSecondaryPool() const
|
|
{
|
|
return pImpl->mpSecondary.get();
|
|
}
|
|
|
|
/* get the last pool by following the GetSecondaryPool chain */
|
|
SfxItemPool* SfxItemPool::GetLastPoolInChain()
|
|
{
|
|
SfxItemPool* pLast = this;
|
|
while(pLast->GetSecondaryPool())
|
|
pLast = pLast->GetSecondaryPool();
|
|
return pLast;
|
|
}
|
|
|
|
SfxItemPool* SfxItemPool::GetMasterPool() const
|
|
{
|
|
return pImpl->mpMaster;
|
|
}
|
|
|
|
/**
|
|
* This method should be called at the master pool, when all secondary
|
|
* pools are appended to it.
|
|
*
|
|
* It calculates the ranges of 'which-ids' for fast construction of
|
|
* item-sets, which contains all 'which-ids'.
|
|
*/
|
|
void SfxItemPool::FreezeIdRanges()
|
|
{
|
|
assert(pImpl->mpPoolRanges.empty() && "pool already frozen, cannot freeze twice");
|
|
FillItemIdRanges_Impl( pImpl->mpPoolRanges );
|
|
}
|
|
|
|
|
|
void SfxItemPool::FillItemIdRanges_Impl( WhichRangesContainer& pWhichRanges ) const
|
|
{
|
|
DBG_ASSERT( pImpl->mpPoolRanges.empty(), "GetFrozenRanges() would be faster!" );
|
|
|
|
pWhichRanges.reset();
|
|
|
|
// Merge all ranges, keeping them sorted
|
|
for (const SfxItemPool* pPool = this; pPool; pPool = pPool->pImpl->mpSecondary.get())
|
|
pWhichRanges = pWhichRanges.MergeRange(pPool->pImpl->mnStart, pPool->pImpl->mnEnd);
|
|
}
|
|
|
|
const WhichRangesContainer& SfxItemPool::GetFrozenIdRanges() const
|
|
{
|
|
return pImpl->mpPoolRanges;
|
|
}
|
|
|
|
const SfxPoolItem *SfxItemPool::GetItem2Default(sal_uInt16 nWhich) const
|
|
{
|
|
if ( !IsInRange(nWhich) )
|
|
{
|
|
if ( pImpl->mpSecondary )
|
|
return pImpl->mpSecondary->GetItem2Default( nWhich );
|
|
assert(false && "unknown WhichId - cannot resolve surrogate");
|
|
return nullptr;
|
|
}
|
|
return (*pImpl->mpStaticDefaults)[ GetIndex_Impl(nWhich) ];
|
|
}
|
|
|
|
SfxItemPool::Item2Range SfxItemPool::GetItemSurrogates(sal_uInt16 nWhich) const
|
|
{
|
|
static const o3tl::sorted_vector<SfxPoolItem*> EMPTY;
|
|
|
|
if ( !IsInRange(nWhich) )
|
|
{
|
|
if ( pImpl->mpSecondary )
|
|
return pImpl->mpSecondary->GetItemSurrogates( nWhich );
|
|
assert(false && "unknown WhichId - cannot resolve surrogate");
|
|
return { EMPTY.end(), EMPTY.end() };
|
|
}
|
|
|
|
SfxPoolItemArray_Impl& rItemArr = pImpl->maPoolItemArrays[GetIndex_Impl(nWhich)];
|
|
return { rItemArr.begin(), rItemArr.end() };
|
|
}
|
|
|
|
/* This is only valid for SfxPoolItem that override IsSortable and operator< */
|
|
std::vector<const SfxPoolItem*> SfxItemPool::FindItemSurrogate(sal_uInt16 nWhich, SfxPoolItem const & rSample) const
|
|
{
|
|
if ( !IsInRange(nWhich) )
|
|
{
|
|
if ( pImpl->mpSecondary )
|
|
return pImpl->mpSecondary->FindItemSurrogate( nWhich, rSample );
|
|
assert(false && "unknown WhichId - cannot resolve surrogate");
|
|
return std::vector<const SfxPoolItem*>();
|
|
}
|
|
|
|
SfxPoolItemArray_Impl& rItemArr = pImpl->maPoolItemArrays[GetIndex_Impl(nWhich)];
|
|
return rItemArr.findSurrogateRange(&rSample);
|
|
}
|
|
|
|
sal_uInt32 SfxItemPool::GetItemCount2(sal_uInt16 nWhich) const
|
|
{
|
|
if ( !IsInRange(nWhich) )
|
|
{
|
|
if ( pImpl->mpSecondary )
|
|
return pImpl->mpSecondary->GetItemCount2( nWhich );
|
|
assert(false && "unknown WhichId - cannot resolve surrogate");
|
|
return 0;
|
|
}
|
|
|
|
SfxPoolItemArray_Impl& rItemArr = pImpl->maPoolItemArrays[GetIndex_Impl(nWhich)];
|
|
return rItemArr.size();
|
|
}
|
|
|
|
|
|
sal_uInt16 SfxItemPool::GetWhich( sal_uInt16 nSlotId, bool bDeep ) const
|
|
{
|
|
if ( !IsSlot(nSlotId) )
|
|
return nSlotId;
|
|
|
|
sal_uInt16 nCount = pImpl->mnEnd - pImpl->mnStart + 1;
|
|
for ( sal_uInt16 nOfs = 0; nOfs < nCount; ++nOfs )
|
|
if ( pItemInfos[nOfs]._nSID == nSlotId )
|
|
return nOfs + pImpl->mnStart;
|
|
if ( pImpl->mpSecondary && bDeep )
|
|
return pImpl->mpSecondary->GetWhich(nSlotId);
|
|
return nSlotId;
|
|
}
|
|
|
|
|
|
sal_uInt16 SfxItemPool::GetSlotId( sal_uInt16 nWhich ) const
|
|
{
|
|
if ( !IsWhich(nWhich) )
|
|
return nWhich;
|
|
|
|
if ( !IsInRange( nWhich ) )
|
|
{
|
|
if ( pImpl->mpSecondary )
|
|
return pImpl->mpSecondary->GetSlotId(nWhich);
|
|
assert(false && "unknown WhichId - cannot get slot-id");
|
|
return 0;
|
|
}
|
|
|
|
sal_uInt16 nSID = pItemInfos[nWhich - pImpl->mnStart]._nSID;
|
|
return nSID ? nSID : nWhich;
|
|
}
|
|
|
|
|
|
sal_uInt16 SfxItemPool::GetTrueWhich( sal_uInt16 nSlotId, bool bDeep ) const
|
|
{
|
|
if ( !IsSlot(nSlotId) )
|
|
return 0;
|
|
|
|
sal_uInt16 nCount = pImpl->mnEnd - pImpl->mnStart + 1;
|
|
for ( sal_uInt16 nOfs = 0; nOfs < nCount; ++nOfs )
|
|
if ( pItemInfos[nOfs]._nSID == nSlotId )
|
|
return nOfs + pImpl->mnStart;
|
|
if ( pImpl->mpSecondary && bDeep )
|
|
return pImpl->mpSecondary->GetTrueWhich(nSlotId);
|
|
return 0;
|
|
}
|
|
|
|
|
|
sal_uInt16 SfxItemPool::GetTrueSlotId( sal_uInt16 nWhich ) const
|
|
{
|
|
if ( !IsWhich(nWhich) )
|
|
return 0;
|
|
|
|
if ( !IsInRange( nWhich ) )
|
|
{
|
|
if ( pImpl->mpSecondary )
|
|
return pImpl->mpSecondary->GetTrueSlotId(nWhich);
|
|
assert(false && "unknown WhichId - cannot get slot-id");
|
|
return 0;
|
|
}
|
|
return pItemInfos[nWhich - pImpl->mnStart]._nSID;
|
|
}
|
|
|
|
void SfxItemPool::dumpAsXml(xmlTextWriterPtr pWriter) const
|
|
{
|
|
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("SfxItemPool"));
|
|
for (auto const & rArray : pImpl->maPoolItemArrays)
|
|
for (auto const & rItem : rArray)
|
|
rItem->dumpAsXml(pWriter);
|
|
(void)xmlTextWriterEndElement(pWriter);
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|