editeng/source/items/frmitems.cxx    |   33 +++++
 editeng/source/items/paraitem.cxx    |    7 +
 editeng/source/items/textitem.cxx    |  224 ++++++++++++++++++++++++++++++++---
 include/editeng/adjustitem.hxx       |    7 +
 include/editeng/brushitem.hxx        |   16 ++
 include/editeng/charreliefitem.hxx   |    3 
 include/editeng/crossedoutitem.hxx   |    3 
 include/editeng/emphasismarkitem.hxx |    3 
 include/editeng/fhgtitem.hxx         |    6 
 include/editeng/fontitem.hxx         |   28 +---
 include/editeng/frmdiritem.hxx       |    3 
 include/editeng/langitem.hxx         |    3 
 include/editeng/postitem.hxx         |    3 
 include/editeng/udlnitem.hxx         |    9 +
 include/editeng/wghtitem.hxx         |    3 
 include/svl/eitem.hxx                |    6 
 include/svl/itemset.hxx              |    1 
 include/svl/poolitem.hxx             |   50 ++++++-
 svl/source/items/cenumitm.cxx        |   85 +++++++++++++
 svl/source/items/itemset.cxx         |   98 ++++++++++++++-
 svl/source/items/poolitem.cxx        |   17 ++
 vcl/source/app/svapp.cxx             |    3 
 22 files changed, 550 insertions(+), 61 deletions(-)

New commits:
commit 063781f4a94e3960a2cb40d1981c0e0ef9a73153
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Mon Jan 22 15:43:05 2024 +0100
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Tue Jan 23 18:56:12 2024 +0100

    tdf#158605 Add global SfxPoolItem re-use
    
    The task shows that the commit including the Item
    paradigm change has follow-ups: It now does no
    longer try to share Items as much as possible
    (detailed reasons in that commit). Mainly for speed
    reasons since that sharing was done before by
    (mostly) linearly searching in existing instances
    registered at one Pool, using the operator== of the
    SfxPoolItems. That costs runtime.
    
    There is somewhere a sweet-spot between memory and
    runtime: the number of Items allocated and the time
    spent to share them more effectively. This task shows
    - despite being a non-real-world document - that
    for extremes like this putting work in sharing is
    still needed.
    
    But there are possibilities to combine both: If we
    can implement solutions that do not need much time
    to ideintify an aleady existing instance we will get
    the best of both worlds.
    
    As explained already in that change, if we would
    need that again, then on a better base. Thus I drove
    forward ITEM changes to a state where we are now able
    to share Items globally in the office - not per pool
    but for all ItemSets/ItemHolders and thus all Apps/
    Models/opened documents.
    
    NOTE: This currently needs to include the WhichID
    that is included in the Item, so cannot share pure
    Item-data (as the old usage did too). This does not
    need to stay that way: If you think about it, the
    association between WhichID and Pool/Holder is
    defined in Pool/Holder, so theoretically the Item
    does not need to contain the WhichID. This will
    be hard to do due too many places in the code that
    use the WhichID stored at the Item.
    
    To support that I added an ItemInstanceManager with
    a simple interface (find/add/remove) and it's usage
    in the two central Item-existance managing methods
    implCreateItemEntry/implCleanupItemEntry. The
    interface is pure virtual and all methods private,
    only the mentioned managing methods are allowed to
    access these. Also added a virtual method to
    SfxPoolItem called getItemInstanceManager() that
    can be implemented by Items that want to support
    that.
    
    Also added a default implementation of
    ItemInstanceManager called DefaultItemInstanceManager
    that uses linear search using operator== from the
    Item that can be used/added to every Item easily.
    It works for all Items and does in principle what
    the former implementation does. It is intended as
    simple/fast fallback.
    
    I also added a statistic element to measure the most
    used non-RefCounted Items on an Office-run, this
    will be printed at office shutdown using SAL_LOG
    and the 'svl.items' flag.
    
    I then checked all Items that were used in this
    error/bug scenario that used an extensive number
    of incarnations and added an ItemInstanceManager
    for these.
    
    For SvxFontItem I added one that creates a hash and
    thus needs not to search for instances at all, with
    the caveat that the WhichID needs to be included.
    Thus the hash is not at the Item, but only in the
    ItemInstanceManager implementation.
    
    For SfxBoolItem I implemented one that hashes using
    the WhichID and holding both possible states in an
    associated std::pair, true and false, thus the
    SfxBoolItem is identified fast and only two instances
    per WhichID exist (when used in Pool/Holder).
    
    For 11 other Items I just added using the standard
    implementation, DefaultItemInstanceManager. Of
    course the more we optimize the better it will get.
    
    For all Items where I added that mechanism I also
    added ASSERT_CHANGE_REFCOUNTED_ITEM to all write
    calls/methods for that Item. It asserts when the
    RefCounted Item is to be changed. This should be
    done in all write accesses to Items, but we have
    ca. 500 derivations.
    NOTE: There was *one* place I found where that
    was already done -> was alredy seen as problem,
    but not really adressed.
    
    Despite this example file is not representative,
    it is still a start to init this new instance
    re-use of Items.
    
    I am already thinking about doing that globally,
    depending on the usage number (maybe combined with
    sizeof(item)). There is no argument to not even
    change strategy at runtime when a specific number
    of incarnations of one Item derivation is reached,
    but this is not part of this fix.
    
    Change-Id: Ie56cf40e0341f98dcbf29cf5c06368316829415e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162402
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/editeng/source/items/frmitems.cxx 
b/editeng/source/items/frmitems.cxx
index 94b7704303ba..c3a5836b082a 100644
--- a/editeng/source/items/frmitems.cxx
+++ b/editeng/source/items/frmitems.cxx
@@ -3978,6 +3978,12 @@ void SvxLineItem::SetLine( const SvxBorderLine* pNew )
     pLine.reset( pNew ? new SvxBorderLine( *pNew ) : nullptr );
 }
 
+ItemInstanceManager* SvxBrushItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxBrushItem::SvxBrushItem(sal_uInt16 _nWhich)
     : SfxPoolItem(_nWhich)
     , aColor(COL_TRANSPARENT)
@@ -4201,6 +4207,7 @@ bool SvxBrushItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             {
                 aNewCol.SetAlpha(aColor.GetAlpha());
             }
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             aColor = aNewCol;
         }
         break;
@@ -4209,6 +4216,7 @@ bool SvxBrushItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             sal_Int32 nTrans = 0;
             if ( !( rVal >>= nTrans ) || nTrans < 0 || nTrans > 100 )
                 return false;
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             aColor.SetAlpha(255 - lcl_PercentToTransparency(nTrans));
         }
         break;
@@ -4220,7 +4228,10 @@ bool SvxBrushItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
                 return false;
 
             if (xComplexColor.is())
+            {
+                ASSERT_CHANGE_REFCOUNTED_ITEM;
                 maComplexColor = 
model::color::getFromXComplexColor(xComplexColor);
+            }
         }
         break;
 
@@ -4234,11 +4245,13 @@ bool SvxBrushItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
                     return false;
                 eLocation = static_cast<style::GraphicLocation>(nValue);
             }
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             SetGraphicPos( 
static_cast<SvxGraphicPosition>(static_cast<sal_uInt16>(eLocation)) );
         }
         break;
 
         case MID_GRAPHIC_TRANSPARENT:
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             aColor.SetAlpha( Any2Bool( rVal ) ? 0 : 255 );
         break;
 
@@ -4262,6 +4275,7 @@ bool SvxBrushItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             {
                 maStrLink.clear();
 
+                ASSERT_CHANGE_REFCOUNTED_ITEM;
                 std::unique_ptr<GraphicObject> 
xOldGrfObj(std::move(xGraphicObject));
                 xGraphicObject.reset(new GraphicObject(aGraphic));
                 ApplyGraphicTransparency_Impl();
@@ -4285,6 +4299,7 @@ bool SvxBrushItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             {
                 OUString sLink;
                 rVal >>= sLink;
+                ASSERT_CHANGE_REFCOUNTED_ITEM;
                 SetGraphicFilter( sLink );
             }
         }
@@ -4295,6 +4310,7 @@ bool SvxBrushItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             rVal >>= nTmp;
             if(nTmp >= 0 && nTmp <= 100)
             {
+                ASSERT_CHANGE_REFCOUNTED_ITEM;
                 nGraphicTransparency = sal_Int8(nTmp);
                 if (xGraphicObject)
                     ApplyGraphicTransparency_Impl();
@@ -4308,6 +4324,7 @@ bool SvxBrushItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             if (!(rVal >>= nVal))
                 return false;
 
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             nShadingValue = nVal;
         }
         break;
@@ -4457,6 +4474,7 @@ void SvxBrushItem::setGraphicTransparency(sal_Int8 nNew)
 {
     if (nNew != nGraphicTransparency)
     {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         nGraphicTransparency = nNew;
         ApplyGraphicTransparency_Impl();
     }
@@ -4470,6 +4488,10 @@ const Graphic* SvxBrushItem::GetGraphic(OUString const & 
referer) const
 
 void SvxBrushItem::SetGraphicPos( SvxGraphicPosition eNew )
 {
+    if (eGraphicPos == eNew)
+        return;
+
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
     eGraphicPos = eNew;
 
     if ( GPOS_NONE == eGraphicPos )
@@ -4491,6 +4513,7 @@ void SvxBrushItem::SetGraphic( const Graphic& rNew )
 {
     if ( maStrLink.isEmpty() )
     {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         if (xGraphicObject)
             xGraphicObject->SetGraphic(rNew);
         else
@@ -4511,6 +4534,7 @@ void SvxBrushItem::SetGraphicObject( const GraphicObject& 
rNewObj )
 {
     if ( maStrLink.isEmpty() )
     {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         if (xGraphicObject)
             *xGraphicObject = rNewObj;
         else
@@ -4529,6 +4553,7 @@ void SvxBrushItem::SetGraphicObject( const GraphicObject& 
rNewObj )
 
 void SvxBrushItem::SetGraphicLink( const OUString& rNew )
 {
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
     if ( rNew.isEmpty() )
         maStrLink.clear();
     else
@@ -4540,6 +4565,7 @@ void SvxBrushItem::SetGraphicLink( const OUString& rNew )
 
 void SvxBrushItem::SetGraphicFilter( const OUString& rNew )
 {
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
     maStrFilter = rNew;
 }
 
@@ -4570,6 +4596,12 @@ void SvxBrushItem::dumpAsXml(xmlTextWriterPtr pWriter) 
const
 }
 
 
+ItemInstanceManager* SvxFrameDirectionItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxFrameDirectionItem::SvxFrameDirectionItem( SvxFrameDirection nValue ,
                                             sal_uInt16 _nWhich )
     : SfxEnumItem<SvxFrameDirection>( _nWhich, nValue )
@@ -4618,6 +4650,7 @@ bool SvxFrameDirectionItem::PutValue( const 
css::uno::Any& rVal,
     bool bRet = ( rVal >>= nVal );
     if( bRet )
     {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         // translate WritingDirection2 constants into SvxFrameDirection
         switch( nVal )
         {
diff --git a/editeng/source/items/paraitem.cxx 
b/editeng/source/items/paraitem.cxx
index e10c323bcdf6..2a9a514a6461 100644
--- a/editeng/source/items/paraitem.cxx
+++ b/editeng/source/items/paraitem.cxx
@@ -338,6 +338,12 @@ void SvxLineSpacingItem::SetEnumValue( sal_uInt16 nVal )
 
 // class SvxAdjustItem ---------------------------------------------------
 
+ItemInstanceManager* SvxAdjustItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxAdjustItem::SvxAdjustItem(const SvxAdjust eAdjst, const sal_uInt16 nId )
     : SfxEnumItemInterface( nId ),
     bOneBlock( false ), bLastCenter( false ), bLastBlock( false )
@@ -397,6 +403,7 @@ bool SvxAdjustItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
         }
         break;
         case MID_EXPAND_SINGLE :
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             bOneBlock = Any2Bool(rVal);
             break;
     }
diff --git a/editeng/source/items/textitem.cxx 
b/editeng/source/items/textitem.cxx
index cf4017e7911e..3be26195b52d 100644
--- a/editeng/source/items/textitem.cxx
+++ b/editeng/source/items/textitem.cxx
@@ -82,6 +82,7 @@
 #include <docmodel/uno/UnoComplexColor.hxx>
 #include <docmodel/color/ComplexColor.hxx>
 #include <libxml/xmlwriter.h>
+#include <unordered_map>
 
 using namespace ::com::sun::star;
 using namespace ::com::sun::star::text;
@@ -159,29 +160,85 @@ bool SvxFontListItem::GetPresentation
 
 // class SvxFontItem -----------------------------------------------------
 
-SvxFontItem::SvxFontItem( const sal_uInt16 nId ) :
-    SfxPoolItem( nId )
+typedef std::unordered_map<size_t, const SfxPoolItem*> SvxFontItemMap;
+
+namespace
 {
-    eFamily = FAMILY_SWISS;
-    ePitch = PITCH_VARIABLE;
-    eTextEncoding = RTL_TEXTENCODING_DONTKNOW;
-}
+    class SvxFontItemInstanceManager : public ItemInstanceManager
+    {
+        SvxFontItemMap  maRegistered;
+
+        static size_t hashCode(const SfxPoolItem&);
 
+        virtual const SfxPoolItem* find(const SfxPoolItem&) const override;
+        virtual void add(const SfxPoolItem&) override;
+        virtual void remove(const SfxPoolItem&) override;
+    };
 
-SvxFontItem::SvxFontItem( const FontFamily eFam, OUString aName,
-                  OUString aStName, const FontPitch eFontPitch,
-                  const rtl_TextEncoding eFontTextEncoding, const sal_uInt16 
nId ) :
+    size_t SvxFontItemInstanceManager::hashCode(const SfxPoolItem& rItem)
+    {
+        const SvxFontItem& rFontItem(static_cast<const SvxFontItem&>(rItem));
+        std::size_t seed(0);
+        o3tl::hash_combine(seed, rItem.Which());
+        o3tl::hash_combine(seed, rFontItem.GetFamilyName().hashCode());
+        o3tl::hash_combine(seed, rFontItem.GetStyleName().hashCode());
+        o3tl::hash_combine(seed, rFontItem.GetFamily());
+        o3tl::hash_combine(seed, rFontItem.GetPitch());
+        o3tl::hash_combine(seed, rFontItem.GetCharSet());
+        return seed;
+    }
 
-    SfxPoolItem( nId ),
+    const SfxPoolItem* SvxFontItemInstanceManager::find(const SfxPoolItem& 
rItem) const
+    {
+        SvxFontItemMap::const_iterator 
aHit(maRegistered.find(hashCode(rItem)));
+        if (aHit != maRegistered.end())
+            return aHit->second;
+        return nullptr;
+    }
+
+    void SvxFontItemInstanceManager::add(const SfxPoolItem& rItem)
+    {
+        maRegistered.insert({hashCode(rItem), &rItem});
+    }
+
+    void SvxFontItemInstanceManager::remove(const SfxPoolItem& rItem)
+    {
+        maRegistered.erase(hashCode(rItem));
+    }
+}
+
+ItemInstanceManager* SvxFontItem::getItemInstanceManager() const
+{
+    static SvxFontItemInstanceManager aManager;
+    return &aManager;
+}
 
-    aFamilyName(std::move(aName)),
-    aStyleName(std::move(aStName))
+SvxFontItem::SvxFontItem(
+    const sal_uInt16 nId)
+: SfxPoolItem( nId )
+, aFamilyName()
+, aStyleName()
+, eFamily(FAMILY_SWISS)
+, ePitch(PITCH_VARIABLE)
+, eTextEncoding(RTL_TEXTENCODING_DONTKNOW)
 {
-    eFamily = eFam;
-    ePitch = eFontPitch;
-    eTextEncoding = eFontTextEncoding;
 }
 
+SvxFontItem::SvxFontItem(
+    const FontFamily eFam,
+    OUString aName,
+    OUString aStName,
+    const FontPitch eFontPitch,
+    const rtl_TextEncoding eFontTextEncoding,
+    const sal_uInt16 nId)
+: SfxPoolItem( nId )
+, aFamilyName(std::move(aName))
+, aStyleName(std::move(aStName))
+, eFamily(eFam)
+, ePitch(eFontPitch)
+, eTextEncoding(eFontTextEncoding)
+{
+}
 
 bool SvxFontItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
 {
@@ -223,6 +280,7 @@ bool SvxFontItem::PutValue( const uno::Any& rVal, sal_uInt8 
nMemberId)
             if ( !( rVal >>= aFontDescriptor ))
                 return false;
 
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             aFamilyName = aFontDescriptor.Name;
             aStyleName = aFontDescriptor.StyleName;
             eFamily = static_cast<FontFamily>(aFontDescriptor.Family);
@@ -235,6 +293,7 @@ bool SvxFontItem::PutValue( const uno::Any& rVal, sal_uInt8 
nMemberId)
             OUString aStr;
             if(!(rVal >>= aStr))
                 return false;
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             aFamilyName = aStr;
         }
         break;
@@ -243,6 +302,7 @@ bool SvxFontItem::PutValue( const uno::Any& rVal, sal_uInt8 
nMemberId)
             OUString aStr;
             if(!(rVal >>= aStr))
                 return false;
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             aStyleName = aStr;
         }
         break;
@@ -251,6 +311,7 @@ bool SvxFontItem::PutValue( const uno::Any& rVal, sal_uInt8 
nMemberId)
             sal_Int16 nFamily = sal_Int16();
             if(!(rVal >>= nFamily))
                 return false;
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             eFamily = static_cast<FontFamily>(nFamily);
         }
         break;
@@ -259,6 +320,7 @@ bool SvxFontItem::PutValue( const uno::Any& rVal, sal_uInt8 
nMemberId)
             sal_Int16 nSet = sal_Int16();
             if(!(rVal >>= nSet))
                 return false;
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             eTextEncoding = static_cast<rtl_TextEncoding>(nSet);
         }
         break;
@@ -267,6 +329,7 @@ bool SvxFontItem::PutValue( const uno::Any& rVal, sal_uInt8 
nMemberId)
             sal_Int16 nPitch = sal_Int16();
             if(!(rVal >>= nPitch))
                 return false;
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             ePitch =  static_cast<FontPitch>(nPitch);
         }
         break;
@@ -274,13 +337,58 @@ bool SvxFontItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId)
     return true;
 }
 
+void SvxFontItem::SetFamilyName(const OUString& rFamilyName)
+{
+    if (aFamilyName == rFamilyName)
+        return;
 
-bool SvxFontItem::operator==( const SfxPoolItem& rAttr ) const
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
+    aFamilyName = rFamilyName;
+}
+
+void SvxFontItem::SetStyleName(const OUString &rStyleName)
 {
-    assert(SfxPoolItem::operator==(rAttr));
+    if (aStyleName == rStyleName)
+        return;
+
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
+    aStyleName = rStyleName;
+}
+
+void SvxFontItem::SetFamily(FontFamily _eFamily)
+{
+    if (eFamily == _eFamily)
+        return;
+
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
+    eFamily = _eFamily;
+}
+
+void SvxFontItem::SetPitch(FontPitch _ePitch)
+{
+    if (ePitch == _ePitch)
+        return;
+
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
+    ePitch = _ePitch;
+}
+
+void SvxFontItem::SetCharSet(rtl_TextEncoding _eEncoding)
+{
+    if (eTextEncoding == _eEncoding)
+        return;
+
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
+    eTextEncoding = _eEncoding;
+}
 
-    const SvxFontItem& rItem = static_cast<const SvxFontItem&>(rAttr);
+bool SvxFontItem::operator==( const SfxPoolItem& rAttr ) const
+{
+    if (this == &rAttr)
+        return true;
 
+    assert(SfxPoolItem::operator==(rAttr));
+    const SvxFontItem& rItem(static_cast<const SvxFontItem&>(rAttr));
     bool bRet = ( eFamily == rItem.eFamily &&
                  aFamilyName == rItem.aFamilyName &&
                  aStyleName == rItem.aStyleName );
@@ -293,6 +401,7 @@ bool SvxFontItem::operator==( const SfxPoolItem& rAttr ) 
const
             SAL_INFO( "editeng.items", "FontItem::operator==(): only pitch or 
rtl_TextEncoding different ");
         }
     }
+
     return bRet;
 }
 
@@ -328,6 +437,12 @@ void SvxFontItem::dumpAsXml(xmlTextWriterPtr pWriter) const
 
 // class SvxPostureItem --------------------------------------------------
 
+ItemInstanceManager* SvxPostureItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxPostureItem::SvxPostureItem( const FontItalic ePosture, const sal_uInt16 
nId ) :
     SfxEnumItem( nId, ePosture )
 {
@@ -396,6 +511,7 @@ bool SvxPostureItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
     switch( nMemberId )
     {
         case MID_ITALIC:
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             SetBoolValue(Any2Bool(rVal));
         break;
         case MID_POSTURE:
@@ -409,6 +525,7 @@ bool SvxPostureItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
 
                 eSlant = static_cast<awt::FontSlant>(nValue);
             }
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             SetValue(vcl::unohelper::ConvertFontSlant(eSlant));
         }
     }
@@ -427,6 +544,7 @@ bool SvxPostureItem::GetBoolValue() const
 
 void SvxPostureItem::SetBoolValue( bool bVal )
 {
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
     SetValue( bVal ? ITALIC_NORMAL : ITALIC_NONE );
 }
 
@@ -441,6 +559,12 @@ void SvxPostureItem::dumpAsXml(xmlTextWriterPtr pWriter) 
const
 
 // class SvxWeightItem ---------------------------------------------------
 
+ItemInstanceManager* SvxWeightItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxWeightItem::SvxWeightItem( const FontWeight eWght, const sal_uInt16 nId ) :
     SfxEnumItem( nId, eWght )
 {
@@ -461,6 +585,7 @@ bool SvxWeightItem::GetBoolValue() const
 
 void SvxWeightItem::SetBoolValue( bool bVal )
 {
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
     SetValue( bVal ? WEIGHT_BOLD : WEIGHT_NORMAL );
 }
 
@@ -532,6 +657,7 @@ bool SvxWeightItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
     switch( nMemberId )
     {
         case MID_BOLD   :
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             SetBoolValue(Any2Bool(rVal));
         break;
         case MID_WEIGHT:
@@ -544,6 +670,7 @@ bool SvxWeightItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
                     return false;
                 fValue = static_cast<float>(nValue);
             }
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             SetValue( 
vcl::unohelper::ConvertFontWeight(static_cast<float>(fValue)) );
         }
         break;
@@ -562,6 +689,12 @@ void SvxWeightItem::dumpAsXml(xmlTextWriterPtr pWriter) 
const
 
 // class SvxFontHeightItem -----------------------------------------------
 
+ItemInstanceManager* SvxFontHeightItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxFontHeightItem::SvxFontHeightItem( const sal_uInt32 nSz,
                                       const sal_uInt16 nPrp,
                                       const sal_uInt16 nId ) :
@@ -739,6 +872,7 @@ bool SvxFontHeightItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
                 if( fPoint < 0. || fPoint > 10000. )
                     return false;
 
+                ASSERT_CHANGE_REFCOUNTED_ITEM;
                 nHeight = static_cast<tools::Long>( fPoint * 20.0 + 0.5 );     
   // Twips
                 if (!bConvert)
                     nHeight = convertTwipToMm100(nHeight);  // Convert, if the 
item contains 1/100mm
@@ -771,6 +905,7 @@ bool SvxFontHeightItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
                 return false;
             }
 
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             nHeight = static_cast<tools::Long>( fPoint * 20.0 + 0.5 );        
// Twips
             if (!bConvert)
                 nHeight = convertTwipToMm100(nHeight);  // Convert, if the 
item contains 1/100mm
@@ -782,6 +917,7 @@ bool SvxFontHeightItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             if(!(rVal >>= nNew))
                 return true;
 
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             nHeight = lcl_GetRealHeight_Impl(nHeight, nProp, ePropUnit, 
bConvert);
 
             nHeight *= nNew;
@@ -801,6 +937,7 @@ bool SvxFontHeightItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
                     return false;
                 fValue = static_cast<float>(nValue);
             }
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             sal_Int16 nCoreDiffValue = static_cast<sal_Int16>(fValue * 20.);
             nHeight += bConvert ? nCoreDiffValue : 
convertTwipToMm100(nCoreDiffValue);
             nProp = static_cast<sal_uInt16>(static_cast<sal_Int16>(fValue));
@@ -841,6 +978,7 @@ bool SvxFontHeightItem::GetPresentation
 
 void SvxFontHeightItem::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
 {
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
     nHeight = static_cast<sal_uInt32>(BigInt::Scale( nHeight, nMult, nDiv ));
 }
 
@@ -855,6 +993,7 @@ void SvxFontHeightItem::SetHeight( sal_uInt32 nNewHeight, 
const sal_uInt16 nNewP
 {
     DBG_ASSERT( GetRefCount() == 0, "SetValue() with pooled item" );
 
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
     if( MapUnit::MapRelative != eUnit )
         nHeight = nNewHeight + ::ItemToControl( short(nNewProp), eUnit,
                                                 FieldUnit::TWIP );
@@ -872,6 +1011,7 @@ void SvxFontHeightItem::SetHeight( sal_uInt32 nNewHeight, 
sal_uInt16 nNewProp,
 {
     DBG_ASSERT( GetRefCount() == 0, "SetValue() with pooled item" );
 
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
     if( MapUnit::MapRelative != eMetric )
         nHeight = nNewHeight +
                 ::ControlToItem( ::ItemToControl(static_cast<short>(nNewProp), 
eMetric,
@@ -898,6 +1038,12 @@ void SvxFontHeightItem::dumpAsXml(xmlTextWriterPtr 
pWriter) const
 
 // class SvxTextLineItem ------------------------------------------------
 
+ItemInstanceManager* SvxTextLineItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxTextLineItem::SvxTextLineItem( const FontLineStyle eSt, const sal_uInt16 
nId )
     : SfxEnumItem(nId, eSt)
     , maColor(COL_TRANSPARENT)
@@ -919,6 +1065,7 @@ bool SvxTextLineItem::GetBoolValue() const
 
 void SvxTextLineItem::SetBoolValue( bool bVal )
 {
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
     SetValue( bVal ? LINESTYLE_SINGLE : LINESTYLE_NONE );
 }
 
@@ -988,6 +1135,7 @@ bool SvxTextLineItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
     switch(nMemberId)
     {
     case MID_TEXTLINED:
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         SetBoolValue(Any2Bool(rVal));
     break;
     case MID_TL_STYLE:
@@ -996,7 +1144,10 @@ bool SvxTextLineItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
         if(!(rVal >>= nValue))
             bRet = false;
         else
+        {
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             SetValue(static_cast<FontLineStyle>(nValue));
+        }
     }
     break;
     case MID_TL_COLOR:
@@ -1008,6 +1159,7 @@ bool SvxTextLineItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
         {
             // Keep transparence, because it contains the information
             // whether the font color or the stored color should be used
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             sal_uInt8 nAlpha = maColor.GetAlpha();
             maColor = nCol;
             maColor.SetAlpha( nAlpha );
@@ -1021,10 +1173,14 @@ bool SvxTextLineItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             return false;
 
         if (xComplexColor.is())
+        {
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             maComplexColor = model::color::getFromXComplexColor(xComplexColor);
+        }
     }
     break;
     case MID_TL_HASCOLOR:
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         maColor.SetAlpha( Any2Bool( rVal ) ? 255 : 0 );
     break;
     }
@@ -1123,6 +1279,12 @@ OUString SvxOverlineItem::GetValueTextByPos( sal_uInt16 
nPos ) const
 
 // class SvxCrossedOutItem -----------------------------------------------
 
+ItemInstanceManager* SvxCrossedOutItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxCrossedOutItem::SvxCrossedOutItem( const FontStrikeout eSt, const 
sal_uInt16 nId )
     : SfxEnumItem( nId, eSt )
 {
@@ -1143,6 +1305,7 @@ bool SvxCrossedOutItem::GetBoolValue() const
 
 void SvxCrossedOutItem::SetBoolValue( bool bVal )
 {
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
     SetValue( bVal ? STRIKEOUT_SINGLE : STRIKEOUT_NONE );
 }
 
@@ -1214,6 +1377,7 @@ bool SvxCrossedOutItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             sal_Int32 nValue = 0;
             if(!(rVal >>= nValue))
                 return false;
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             SetValue(static_cast<FontStrikeout>(nValue));
         }
         break;
@@ -1917,6 +2081,12 @@ bool SvxEscapementItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
 
 // class SvxLanguageItem -------------------------------------------------
 
+ItemInstanceManager* SvxLanguageItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxLanguageItem::SvxLanguageItem( const LanguageType eLang, const sal_uInt16 
nId )
     : SvxLanguageItem_Base( nId , eLang )
 {
@@ -1976,6 +2146,7 @@ bool SvxLanguageItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             if(!(rVal >>= nValue))
                 return false;
 
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             SetValue(LanguageType(nValue));
         }
         break;
@@ -1985,6 +2156,7 @@ bool SvxLanguageItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             if(!(rVal >>= aLocale))
                 return false;
 
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             SetValue( LanguageTag::convertToLanguageType( aLocale, false));
         }
         break;
@@ -2053,6 +2225,12 @@ bool SvxBlinkItem::GetPresentation
 
 // class SvxEmphaisMarkItem ---------------------------------------------------
 
+ItemInstanceManager* SvxEmphasisMarkItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxEmphasisMarkItem::SvxEmphasisMarkItem( const FontEmphasisMark nValue,
                                         TypedWhichId<SvxEmphasisMarkItem> nId )
     : SfxUInt16Item( nId, static_cast<sal_uInt16>(nValue) )
@@ -2145,6 +2323,7 @@ bool SvxEmphasisMarkItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
             case FontEmphasis::ACCENT_BELOW: nMark = 
FontEmphasisMark::Accent|FontEmphasisMark::PosBelow; break;
             default: return false;
         }
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         SetValue( static_cast<sal_Int16>(nMark) );
     }
     break;
@@ -2486,6 +2665,12 @@ bool SvxCharScaleWidthItem::QueryValue( uno::Any& rVal, 
sal_uInt8 /*nMemberId*/
 |*    class SvxCharReliefItem
 *************************************************************************/
 
+ItemInstanceManager* SvxCharReliefItem::getItemInstanceManager() const
+{
+    static DefaultItemInstanceManager aManager;
+    return &aManager;
+}
+
 SvxCharReliefItem::SvxCharReliefItem( FontRelief eValue,
                                          const sal_uInt16 nId )
     : SfxEnumItem( nId, eValue )
@@ -2539,7 +2724,10 @@ bool SvxCharReliefItem::PutValue( const css::uno::Any& 
rVal,
             sal_Int16 nVal = -1;
             rVal >>= nVal;
             if(nVal >= 0 && nVal <= sal_Int16(FontRelief::Engraved))
+            {
+                ASSERT_CHANGE_REFCOUNTED_ITEM;
                 SetValue( static_cast<FontRelief>(nVal) );
+            }
             else
                 bRet = false;
         }
diff --git a/include/editeng/adjustitem.hxx b/include/editeng/adjustitem.hxx
index e2793907736c..c660598abfc7 100644
--- a/include/editeng/adjustitem.hxx
+++ b/include/editeng/adjustitem.hxx
@@ -44,6 +44,9 @@ class EDITENG_DLLPUBLIC SvxAdjustItem final : public 
SfxEnumItemInterface
     bool    bLastCenter : 1;
     bool    bLastBlock : 1;
 
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
@@ -68,17 +71,20 @@ public:
 
     void SetOneWord( const SvxAdjust eType )
     {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         bOneBlock  = eType == SvxAdjust::Block;
     }
 
     void SetLastBlock( const SvxAdjust eType )
     {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         bLastBlock = eType == SvxAdjust::Block;
         bLastCenter = eType == SvxAdjust::Center;
     }
 
     void SetAdjust( const SvxAdjust eType )
     {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         bLeft = eType == SvxAdjust::Left;
         bRight = eType == SvxAdjust::Right;
         bCenter = eType == SvxAdjust::Center;
@@ -132,6 +138,7 @@ public:
 
     void SetAsFlags(sal_Int8 nFlags)
     {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         bOneBlock = 0 != (nFlags & 0x0001);
         bLastCenter = 0 != (nFlags & 0x0002);
         bLastBlock = 0 != (nFlags & 0x0004);
diff --git a/include/editeng/brushitem.hxx b/include/editeng/brushitem.hxx
index de433f2f272d..6897e319a3b6 100644
--- a/include/editeng/brushitem.hxx
+++ b/include/editeng/brushitem.hxx
@@ -54,6 +54,9 @@ class EDITENG_DLLPUBLIC SvxBrushItem final : public 
SfxPoolItem
 
     void        ApplyGraphicTransparency_Impl();
 
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
@@ -89,7 +92,11 @@ public:
 
     const Color&    GetColor() const                { return aColor; }
     Color&          GetColor()                      { return aColor; }
-    void            SetColor( const Color& rCol)    { aColor = rCol; }
+    void            SetColor( const Color& rCol)
+    {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
+        aColor = rCol;
+    }
 
     const model::ComplexColor& getComplexColor() const
     {
@@ -100,11 +107,16 @@ public:
 
     void setComplexColor(model::ComplexColor const& rComplexColor)
     {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         maComplexColor = rComplexColor;
     }
 
     const Color&    GetFiltColor() const             { return aFilterColor; }
-    void            SetFiltColor( const Color& rCol) { aFilterColor = rCol; }
+    void            SetFiltColor( const Color& rCol)
+    {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
+        aFilterColor = rCol;
+    }
 
     SvxGraphicPosition  GetGraphicPos() const       { return eGraphicPos; }
 
diff --git a/include/editeng/charreliefitem.hxx 
b/include/editeng/charreliefitem.hxx
index 78b57fa7df00..910adcf11e24 100644
--- a/include/editeng/charreliefitem.hxx
+++ b/include/editeng/charreliefitem.hxx
@@ -33,6 +33,9 @@
 
 class EDITENG_DLLPUBLIC SvxCharReliefItem final : public 
SfxEnumItem<FontRelief>
 {
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
diff --git a/include/editeng/crossedoutitem.hxx 
b/include/editeng/crossedoutitem.hxx
index 5d8b18fe2650..259eb412e1e1 100644
--- a/include/editeng/crossedoutitem.hxx
+++ b/include/editeng/crossedoutitem.hxx
@@ -31,6 +31,9 @@
 
 class EDITENG_DLLPUBLIC SvxCrossedOutItem final : public 
SfxEnumItem<FontStrikeout>
 {
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
diff --git a/include/editeng/emphasismarkitem.hxx 
b/include/editeng/emphasismarkitem.hxx
index e0fd74cf4400..94d48e4b8d9c 100644
--- a/include/editeng/emphasismarkitem.hxx
+++ b/include/editeng/emphasismarkitem.hxx
@@ -32,6 +32,9 @@
 
 class EDITENG_DLLPUBLIC SvxEmphasisMarkItem final : public SfxUInt16Item
 {
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
diff --git a/include/editeng/fhgtitem.hxx b/include/editeng/fhgtitem.hxx
index efaad86bdfa8..bdde8c6f0afd 100644
--- a/include/editeng/fhgtitem.hxx
+++ b/include/editeng/fhgtitem.hxx
@@ -41,10 +41,12 @@ class EDITENG_DLLPUBLIC SvxFontHeightItem final : public 
SfxPoolItem
     sal_uInt16  nProp;       // default 100%
     MapUnit ePropUnit;       // Percent, Twip, ...
 
-private:
     friend void Create_legacy_direct_set(SvxFontHeightItem& rItem, sal_uInt32 
nH, sal_uInt16 nP, MapUnit eP);
     void legacy_direct_set(sal_uInt32 nH, sal_uInt16 nP, MapUnit eP) { nHeight 
= nH; nProp = nP; ePropUnit = eP; }
 
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
@@ -75,7 +77,7 @@ public:
 
     void SetProp( sal_uInt16 nNewProp, MapUnit eUnit )
         {
-            DBG_ASSERT( GetRefCount() == 0, "SetValue() with pooled item" );
+            ASSERT_CHANGE_REFCOUNTED_ITEM;
             nProp = nNewProp;
             ePropUnit = eUnit;
         }
diff --git a/include/editeng/fontitem.hxx b/include/editeng/fontitem.hxx
index 7cab5f462e4e..f892178c521e 100644
--- a/include/editeng/fontitem.hxx
+++ b/include/editeng/fontitem.hxx
@@ -34,6 +34,9 @@ class EDITENG_DLLPUBLIC SvxFontItem final : public SfxPoolItem
     FontPitch ePitch;
     rtl_TextEncoding eTextEncoding;
 
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
@@ -55,46 +58,31 @@ public:
                                  OUString &rText, const IntlWrapper&) const 
override;
 
     // Access methods:
-    void SetFamilyName(const OUString& rFamilyName)
-    {
-        aFamilyName = rFamilyName;
-    }
+    void SetFamilyName(const OUString& rFamilyName);
     const OUString &GetFamilyName() const
     {
         return aFamilyName;
     }
 
-    void SetStyleName(const OUString &rStyleName)
-    {
-        aStyleName = rStyleName;
-    }
+    void SetStyleName(const OUString &rStyleName);
     const OUString &GetStyleName() const
     {
         return aStyleName;
     }
 
-    void SetFamily(FontFamily _eFamily)
-    {
-        eFamily = _eFamily;
-    }
+    void SetFamily(FontFamily _eFamily);
     FontFamily GetFamily() const
     {
         return eFamily;
     }
 
-    void SetPitch(FontPitch _ePitch)
-    {
-        ePitch = _ePitch;
-    }
+    void SetPitch(FontPitch _ePitch);
     FontPitch GetPitch() const
     {
         return ePitch;
     }
 
-    void SetCharSet(rtl_TextEncoding _eEncoding)
-    {
-        eTextEncoding = _eEncoding;
-    }
+    void SetCharSet(rtl_TextEncoding _eEncoding);
     rtl_TextEncoding GetCharSet() const
     {
         return eTextEncoding;
diff --git a/include/editeng/frmdiritem.hxx b/include/editeng/frmdiritem.hxx
index 7bb6dc09950d..e2bba96b8974 100644
--- a/include/editeng/frmdiritem.hxx
+++ b/include/editeng/frmdiritem.hxx
@@ -30,6 +30,9 @@
 
 class EDITENG_DLLPUBLIC SvxFrameDirectionItem final : public 
SfxEnumItem<SvxFrameDirection>
 {
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     SvxFrameDirectionItem( SvxFrameDirection nValue, sal_uInt16 nWhich  );
     virtual ~SvxFrameDirectionItem() override;
diff --git a/include/editeng/langitem.hxx b/include/editeng/langitem.hxx
index 4d3852141666..7c8f68e96fd6 100644
--- a/include/editeng/langitem.hxx
+++ b/include/editeng/langitem.hxx
@@ -40,6 +40,9 @@ protected:
 
 class EDITENG_DLLPUBLIC SvxLanguageItem final : public SvxLanguageItem_Base
 {
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
diff --git a/include/editeng/postitem.hxx b/include/editeng/postitem.hxx
index 93ed44b6b794..392a9627feb6 100644
--- a/include/editeng/postitem.hxx
+++ b/include/editeng/postitem.hxx
@@ -32,6 +32,9 @@
 
 class EDITENG_DLLPUBLIC SvxPostureItem final : public SfxEnumItem<FontItalic>
 {
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
diff --git a/include/editeng/udlnitem.hxx b/include/editeng/udlnitem.hxx
index ecbec7a17b8d..560833d4d088 100644
--- a/include/editeng/udlnitem.hxx
+++ b/include/editeng/udlnitem.hxx
@@ -33,6 +33,9 @@ class EDITENG_DLLPUBLIC SvxTextLineItem : public 
SfxEnumItem<FontLineStyle>
     Color maColor;
     model::ComplexColor maComplexColor;
 
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     SvxTextLineItem( const FontLineStyle eSt,
                      const sal_uInt16 nId );
@@ -61,7 +64,10 @@ public:
     FontLineStyle           GetLineStyle() const
                                 { return GetValue(); }
     void                    SetLineStyle( FontLineStyle eNew )
-                                { SetValue(eNew); }
+    {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
+        SetValue(eNew);
+    }
 
     const Color& GetColor() const { return maColor; }
     void SetColor(const Color& rColor) { maColor = rColor; }
@@ -73,6 +79,7 @@ public:
 
     void setComplexColor(model::ComplexColor const& rComplexColor)
     {
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         maComplexColor = rComplexColor;
     }
 };
diff --git a/include/editeng/wghtitem.hxx b/include/editeng/wghtitem.hxx
index 121cfcf318a6..f8beb7c7a62f 100644
--- a/include/editeng/wghtitem.hxx
+++ b/include/editeng/wghtitem.hxx
@@ -32,6 +32,9 @@
 
 class EDITENG_DLLPUBLIC SvxWeightItem final : public SfxEnumItem<FontWeight>
 {
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
diff --git a/include/svl/eitem.hxx b/include/svl/eitem.hxx
index 114e451c061d..18bb334a7e96 100644
--- a/include/svl/eitem.hxx
+++ b/include/svl/eitem.hxx
@@ -69,6 +69,9 @@ class SVL_DLLPUBLIC SfxBoolItem
 {
     bool m_bValue;
 
+protected:
+    virtual ItemInstanceManager* getItemInstanceManager() const override;
+
 public:
     static SfxPoolItem* CreateDefault();
 
@@ -78,8 +81,7 @@ public:
     { }
 
     bool GetValue() const { return m_bValue; }
-
-    void SetValue(bool const bTheValue) { m_bValue = bTheValue; }
+    void SetValue(bool const bTheValue);
 
     // SfxPoolItem
     virtual bool operator ==(const SfxPoolItem & rItem) const override;
diff --git a/include/svl/itemset.hxx b/include/svl/itemset.hxx
index 8762c77e142f..f04b274ec83b 100644
--- a/include/svl/itemset.hxx
+++ b/include/svl/itemset.hxx
@@ -36,6 +36,7 @@ SVL_DLLPUBLIC size_t getAllocatedSfxItemSetCount();
 SVL_DLLPUBLIC size_t getUsedSfxItemSetCount();
 SVL_DLLPUBLIC size_t getAllocatedSfxPoolItemHolderCount();
 SVL_DLLPUBLIC size_t getUsedSfxPoolItemHolderCount();
+SVL_DLLPUBLIC void listSfxPoolItemsWithHighestUsage(sal_uInt16);
 #endif
 
 // ItemSet/ItemPool helpers
diff --git a/include/svl/poolitem.hxx b/include/svl/poolitem.hxx
index ab909c4edfec..79a113b6da89 100644
--- a/include/svl/poolitem.hxx
+++ b/include/svl/poolitem.hxx
@@ -32,12 +32,11 @@
 #include <tools/mapunit.hxx>
 #include <tools/long.hxx>
 #include <boost/property_tree/ptree_fwd.hpp>
+#include <unordered_set>
 
 class IntlWrapper;
 
-#define SFX_ITEMS_OLD_MAXREF                0xffef
-#define SFX_ITEMS_MAXREF                    0xfffffffe
-
+#define SFX_ITEMS_MAXREF                    0xffffffff
 #define CONVERT_TWIPS                       0x80    // Uno conversion for 
measurement (for MemberId)
 
 // warning, if there is no boolean inside the any this will always return the 
value false
@@ -54,6 +53,12 @@ inline bool Any2Bool( const css::uno::Any&rValue )
     return bValue;
 }
 
+// Offer simple assert if Item is RefCounted (RefCnt > 1) and thus CANNOT be 
changed.
+// This should be used at *all* SfxPoolItem set* mehods. Remember that 
SfxPoolItems
+// are by design intended to be create-one, read-only, shared data packages
+#define ASSERT_CHANGE_REFCOUNTED_ITEM \
+    assert(!GetRefCount() && "ERROR: RefCounted SfxPoolItem CANNOT be changed 
(!)")
+
 /*
  * The values of this enum describe the degree of textual
  * representation of an item after calling the virtual
@@ -106,6 +111,7 @@ SVL_DLLPUBLIC void listAllocatedSfxPoolItems();
 class SfxItemPool;
 class SfxItemSet;
 typedef struct _xmlTextWriter* xmlTextWriterPtr;
+class ItemInstanceManager;
 
 class SVL_DLLPUBLIC SfxPoolItem
 {
@@ -168,10 +174,13 @@ protected:
     void setIsSetItem() { m_bIsSetItem = true; }
     void setNonShareable() { m_bShareable = false; }
 
+    // access ItemInstanceManager for this Item, default
+    // is nullptr
+    virtual ItemInstanceManager* getItemInstanceManager() const;
+
 public:
     inline void AddRef(sal_uInt32 n = 1) const
     {
-        assert(m_nRefCount <= SFX_ITEMS_MAXREF && "AddRef with non-Pool-Item");
         assert(n <= SFX_ITEMS_MAXREF - m_nRefCount && "AddRef: refcount 
overflow");
         m_nRefCount += n;
     }
@@ -194,7 +203,6 @@ public:
 private:
     inline sal_uInt32 ReleaseRef(sal_uInt32 n = 1) const
     {
-        assert(m_nRefCount <= SFX_ITEMS_MAXREF && "ReleaseRef with 
non-Pool-Item");
         assert(n <= m_nRefCount);
         m_nRefCount -= n;
         return m_nRefCount;
@@ -281,7 +289,37 @@ private:
     SfxPoolItem&             operator=( const SfxPoolItem& ) = delete;
 };
 
+// basic Interface definition
+class SVL_DLLPUBLIC ItemInstanceManager
+{
+    // allow *only* ItemSetTooling to access
+    friend SfxPoolItem const* implCreateItemEntry(SfxItemPool&, SfxPoolItem 
const*, bool);
+    friend void implCleanupItemEntry(SfxPoolItem const*);
+
+    // standard interface, accessed exclusively
+    // by implCreateItemEntry/implCleanupItemEntry
+    virtual const SfxPoolItem* find(const SfxPoolItem&) const = 0;
+    virtual void add(const SfxPoolItem&) = 0;
+    virtual void remove(const SfxPoolItem&) = 0;
+};
+
+// offering a default implementation that can be use for
+// each SfxPoolItem (except when !isShareable()). It just
+// uses an unordered_set holding ptrs to SfxPoolItems added
+// and SfxPoolItem::opeator== to linearly search for one.
+// Thus thisi is not the fastest, but as fast as old 'poooled'
+// stuff - btter use an intelligent, pro-Item implementation
+// that does e.g. hashing or whatever might be feasible for
+// that specific Item (see other derivations)
+class SVL_DLLPUBLIC DefaultItemInstanceManager : public ItemInstanceManager
+{
+    std::unordered_set<const SfxPoolItem*>  maRegistered;
 
+public:
+    virtual const SfxPoolItem* find(const SfxPoolItem&) const override;
+    virtual void add(const SfxPoolItem&) override;
+    virtual void remove(const SfxPoolItem&) override;
+};
 
 inline bool IsPoolDefaultItem(const SfxPoolItem *pItem )
 {
@@ -300,7 +338,7 @@ inline bool IsDefaultItem( const SfxPoolItem *pItem )
 
 inline bool IsPooledItem( const SfxPoolItem *pItem )
 {
-    return pItem && pItem->GetRefCount() > 0 && pItem->GetRefCount() <= 
SFX_ITEMS_MAXREF;
+    return pItem && pItem->GetRefCount() > 0;
 }
 
 SVL_DLLPUBLIC extern SfxPoolItem const * const INVALID_POOL_ITEM;
diff --git a/svl/source/items/cenumitm.cxx b/svl/source/items/cenumitm.cxx
index 713e1608efd8..86e30b7e6bdc 100644
--- a/svl/source/items/cenumitm.cxx
+++ b/svl/source/items/cenumitm.cxx
@@ -20,7 +20,7 @@
 #include <com/sun/star/uno/Any.hxx>
 #include <svl/cenumitm.hxx>
 #include <svl/eitem.hxx>
-
+#include <unordered_map>
 #include <comphelper/extract.hxx>
 #include <libxml/xmlwriter.h>
 #include <sal/log.hxx>
@@ -83,6 +83,85 @@ bool SfxEnumItemInterface::GetBoolValue() const
 void SfxEnumItemInterface::SetBoolValue(bool)
 {}
 
+typedef std::unordered_map<sal_uInt16, std::pair<const SfxPoolItem*, const 
SfxPoolItem*>> SfxBoolItemMap;
+
+namespace
+{
+    class SfxBoolItemInstanceManager : public ItemInstanceManager
+    {
+        SfxBoolItemMap  maRegistered;
+
+        virtual const SfxPoolItem* find(const SfxPoolItem&) const override;
+        virtual void add(const SfxPoolItem&) override;
+        virtual void remove(const SfxPoolItem&) override;
+    };
+
+    const SfxPoolItem* SfxBoolItemInstanceManager::find(const SfxPoolItem& 
rItem) const
+    {
+        SfxBoolItemMap::const_iterator aHit(maRegistered.find(rItem.Which()));
+        if (aHit == maRegistered.end())
+            return nullptr;
+
+        const SfxBoolItem& rSfxBoolItem(static_cast<const 
SfxBoolItem&>(rItem));
+        if (rSfxBoolItem.GetValue())
+            return aHit->second.first;
+        return aHit->second.second;
+    }
+
+    void SfxBoolItemInstanceManager::add(const SfxPoolItem& rItem)
+    {
+        SfxBoolItemMap::iterator aHit(maRegistered.find(rItem.Which()));
+        const SfxBoolItem& rSfxBoolItem(static_cast<const 
SfxBoolItem&>(rItem));
+
+        if (aHit == maRegistered.end())
+        {
+            if (rSfxBoolItem.GetValue())
+                maRegistered.insert({rItem.Which(), std::make_pair(&rItem, 
nullptr)});
+            else
+                maRegistered.insert({rItem.Which(), std::make_pair(nullptr, 
&rItem)});
+        }
+        else
+        {
+            if (rSfxBoolItem.GetValue())
+                aHit->second.first = &rItem;
+            else
+                aHit->second.second = &rItem;
+        }
+    }
+
+    void SfxBoolItemInstanceManager::remove(const SfxPoolItem& rItem)
+    {
+        SfxBoolItemMap::iterator aHit(maRegistered.find(rItem.Which()));
+        const SfxBoolItem& rSfxBoolItem(static_cast<const 
SfxBoolItem&>(rItem));
+
+        if (aHit != maRegistered.end())
+        {
+            if (rSfxBoolItem.GetValue())
+                aHit->second.first = nullptr;
+            else
+                aHit->second.second = nullptr;
+
+            if (aHit->second.first == nullptr && aHit->second.second == 
nullptr)
+                maRegistered.erase(aHit);
+        }
+    }
+}
+
+ItemInstanceManager* SfxBoolItem::getItemInstanceManager() const
+{
+    static SfxBoolItemInstanceManager aManager;
+    return &aManager;
+}
+
+void SfxBoolItem::SetValue(bool const bTheValue)
+{
+    if (m_bValue == bTheValue)
+        return;
+
+    ASSERT_CHANGE_REFCOUNTED_ITEM;
+    m_bValue = bTheValue;
+}
+
 SfxPoolItem* SfxBoolItem::CreateDefault()
 {
     return new SfxBoolItem();
@@ -126,6 +205,10 @@ bool SfxBoolItem::PutValue(const css::uno::Any& rVal, 
sal_uInt8)
     bool bTheValue = bool();
     if (rVal >>= bTheValue)
     {
+        if (m_bValue == bTheValue)
+            return true;
+
+        ASSERT_CHANGE_REFCOUNTED_ITEM;
         m_bValue = bTheValue;
         return true;
     }
diff --git a/svl/source/items/itemset.cxx b/svl/source/items/itemset.cxx
index a7db03c68932..5eec0a7a9391 100644
--- a/svl/source/items/itemset.cxx
+++ b/svl/source/items/itemset.cxx
@@ -36,6 +36,8 @@
 
 #include <items_helper.hxx>
 
+static bool 
g_bDisableItemInstanceManager(getenv("SVL_DISABLE_ITEM_INSTANCE_MANAGER"));
+
 #ifdef DBG_UTIL
 static size_t nAllocatedSfxItemSetCount(0);
 static size_t nUsedSfxItemSetCount(0);
@@ -45,6 +47,45 @@ size_t getAllocatedSfxItemSetCount() { return 
nAllocatedSfxItemSetCount; }
 size_t getUsedSfxItemSetCount() { return nUsedSfxItemSetCount; }
 size_t getAllocatedSfxPoolItemHolderCount() { return 
nAllocatedSfxPoolItemHolderCount; }
 size_t getUsedSfxPoolItemHolderCount() { return nUsedSfxPoolItemHolderCount; }
+
+typedef std::unordered_map<sal_uInt16, std::pair<sal_uInt32, const char*>> 
HightestUsage;
+static HightestUsage aHightestUsage;
+
+static void addUsage(const SfxPoolItem& rCandidate)
+{
+    HightestUsage::iterator aHit(aHightestUsage.find(rCandidate.Which()));
+    if (aHit == aHightestUsage.end())
+    {
+        aHightestUsage.insert({rCandidate.Which(), {1, 
typeid(rCandidate).name()}});
+        return;
+    }
+    aHit->second.first++;
+}
+
+void listSfxPoolItemsWithHighestUsage(sal_uInt16 nNum)
+{
+    struct sorted {
+        sal_uInt16 nWhich;
+        sal_uInt32 nUsage;
+        const char* pType;
+        sorted(sal_uInt16 _nWhich, sal_uInt32 _nUsage, const char* _pType)
+            : nWhich(_nWhich), nUsage(_nUsage), pType(_pType) {}
+        bool operator<(const sorted& rDesc) const { return nUsage > 
rDesc.nUsage; }
+    };
+    std::vector<sorted> aSorted;
+    aSorted.reserve(aHightestUsage.size());
+    for (const auto& rEntry : aHightestUsage)
+        aSorted.emplace_back(rEntry.first, rEntry.second.first, 
rEntry.second.second);
+    std::sort(aSorted.begin(), aSorted.end());
+    sal_uInt16 a(0);
+    SAL_INFO("svl.items", "ITEM: List of the " << nNum << " SfxPoolItems with 
highest non-RefCounted usages:");
+    for (const auto& rEntry : aSorted)
+    {
+        SAL_INFO("svl.items", "  ITEM(" << a << "): Which: " << rEntry.nWhich 
<< " Uses: " << rEntry.nUsage << " Type: " << rEntry.pType);
+        if (++a >= nNum)
+            break;
+    }
+}
 #endif
 // NOTE: Only needed for one Item in SC (see notes below for
 // ScPatternAttr). Still keep it so that when errors
@@ -390,14 +431,9 @@ SfxPoolItem const* implCreateItemEntry(SfxItemPool& rPool, 
SfxPoolItem const* pS
     //     }
     // }
 
-    // the Item itself is shareable when it already is used somewhere
-    // which is equivalent to be referenced already. IsPooledItem also
-    // checked for SFX_ITEMS_MAXREF, that is not needed here. Use a
-    // fake 'while' loop and 'break' to make this better readable
-
-    // only try to share items that are already shared somehow, else
-    // these items are probably not (heap) item-ptr's (but on the
-    // stack or else)
+    // The Item itself is shareable when it is used/added at an instance
+    // that RefCounts the Item, SfxItemPool or SfxPoolItemHolder. Try
+    // to share items that are already shared
     while(pSource->GetRefCount() > 0)
     {
         if (!pSource->isShareable())
@@ -413,6 +449,29 @@ SfxPoolItem const* implCreateItemEntry(SfxItemPool& rPool, 
SfxPoolItem const* pS
         return pSource;
     }
 
+    // check if we can globally share the Item using the
+    // ItemInstanceManager (only for shareable Items)
+    while (!g_bDisableItemInstanceManager && pSource->isShareable())
+    {
+        ItemInstanceManager* pManager(pSource->getItemInstanceManager());
+        if (nullptr == pManager)
+            // not supported by this Item, done
+            break;
+
+        const SfxPoolItem* pAlternative(pManager->find(*pSource));
+        if(nullptr == pAlternative)
+            // none found, done
+            break;
+
+        // need to delete evtl. handed over ownership change Item
+        if (bPassingOwnership)
+            delete pSource;
+
+        // If we get here we can share the Item
+        pAlternative->AddRef();
+        return pAlternative;
+    }
+
     // check if the handed over and to be directly used item is a
     // SfxSetItem, that would make it pool-dependent. It then must have
     // the same target-pool, ensure that by the cost of cloning it
@@ -426,6 +485,11 @@ SfxPoolItem const* implCreateItemEntry(SfxItemPool& rPool, 
SfxPoolItem const* pS
         delete pOld;
     }
 
+#ifdef DBG_UTIL
+    // create statistics for listSfxPoolItemsWithHighestUsage
+    addUsage(*pSource);
+#endif
+
     // when we reach this line we know that we have to add/create a new item. 
If
     // bPassingOwnership is given just use the item, else clone it
     if (!bPassingOwnership)
@@ -434,6 +498,15 @@ SfxPoolItem const* implCreateItemEntry(SfxItemPool& rPool, 
SfxPoolItem const* pS
     // increase RefCnt 0->1
     pSource->AddRef();
 
+    // check if we should register this Item for the global
+    // ItemInstanceManager mechanism (only for shareable Items)
+    if (!g_bDisableItemInstanceManager && pSource->isShareable())
+    {
+        ItemInstanceManager* pManager(pSource->getItemInstanceManager());
+        if (nullptr != pManager)
+            pManager->add(*pSource);
+    }
+
     return pSource;
 }
 
@@ -472,6 +545,15 @@ void implCleanupItemEntry(SfxPoolItem const* pSource)
         // default items (static and dynamic) are owned by the pool, do not 
delete
         return;
 
+    // check if we should remove this Item from the global
+    // ItemInstanceManager mechanism (only for shareable Items)
+    if (!g_bDisableItemInstanceManager && pSource->isShareable())
+    {
+        ItemInstanceManager* pManager(pSource->getItemInstanceManager());
+        if (nullptr != pManager)
+            pManager->remove(*pSource);
+    }
+
     // decrease RefCnt before deleting (destructor asserts for it and that's
     // good to find other errors)
     pSource->ReleaseRef();
diff --git a/svl/source/items/poolitem.cxx b/svl/source/items/poolitem.cxx
index 60de354cdeba..10fda040f5cb 100644
--- a/svl/source/items/poolitem.cxx
+++ b/svl/source/items/poolitem.cxx
@@ -488,6 +488,21 @@ void listAllocatedSfxPoolItems()
 }
 #endif
 
+const SfxPoolItem* DefaultItemInstanceManager::find(const SfxPoolItem& rItem) 
const
+{
+    for (const auto& rCandidate : maRegistered)
+        if (rCandidate->Which() == rItem.Which() && *rCandidate == rItem)
+            return rCandidate;
+
+    return nullptr;
+}
+
+void DefaultItemInstanceManager::add(const SfxPoolItem& rItem) { 
maRegistered.insert(&rItem); }
+
+void DefaultItemInstanceManager::remove(const SfxPoolItem& rItem) { 
maRegistered.erase(&rItem); }
+
+ItemInstanceManager* SfxPoolItem::getItemInstanceManager() const { return 
nullptr; }
+
 SfxPoolItem::SfxPoolItem(sal_uInt16 const nWhich)
     : m_nRefCount(0)
     , m_nWhich(nWhich)
@@ -517,7 +532,7 @@ SfxPoolItem::~SfxPoolItem()
     incarnatedSfxPoolItems().erase(this);
     m_bDeleted = true;
 #endif
-    assert((m_nRefCount == 0 || m_nRefCount > SFX_ITEMS_MAXREF) && "destroying 
item in use");
+    assert((m_nRefCount == 0) && "destroying item in use");
 }
 
 bool SfxPoolItem::operator==(const SfxPoolItem& rCmp) const
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index e17e4533122f..bda94ef8e898 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -207,6 +207,9 @@ Application::~Application()
     // Additional call to list still incarnated SfxPoolItems (under 
'svl.items')
     listAllocatedSfxPoolItems();
 
+    // List SfxPoolItems with highest RefCounts, these are the best
+    // candidates to add a ItemInstanceManager mechanism
+    listSfxPoolItemsWithHighestUsage(20);
 #endif
 }
 

Reply via email to