LCOV - code coverage report
Current view: top level - svl/source/items - stylepool.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 160 174 92.0 %
Date: 2015-06-13 12:38:46 Functions: 25 29 86.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <svl/stylepool.hxx>
      21             : #include <svl/itemiter.hxx>
      22             : #include <svl/itempool.hxx>
      23             : #include <algorithm>
      24             : #include <map>
      25             : #include <memory>
      26             : #include <vector>
      27             : 
      28             : namespace {
      29             :     /** A "Node" represents a subset of inserted SfxItemSets
      30             :      * The root node represents the empty set
      31             :      * The other nodes contain a SfxPoolItem and represents an item set which contains their
      32             :      * pool item and the pool items of their parents.
      33             :      */
      34             :     class Node
      35             :     {
      36             :         std::vector<Node*> mChildren; // child nodes, create by findChildNode(..)
      37             :         // container of shared pointers of inserted item sets; for non-poolable
      38             :         // items more than one item set is needed
      39             :         std::vector< StylePool::SfxItemSet_Pointer_t > maItemSet;
      40             :         const SfxPoolItem *mpItem;   // my pool item
      41             :         Node *mpUpper;               // if I'm a child node that's my parent node
      42             :         // #i86923#
      43             :         const bool mbIsItemIgnorable;
      44             :     public:
      45             :         // #i86923#
      46        5178 :         Node() // root node Ctor
      47             :             : mChildren(),
      48             :               maItemSet(),
      49             :               mpItem( 0 ),
      50             :               mpUpper( 0 ),
      51        5178 :               mbIsItemIgnorable( false )
      52        5178 :         {}
      53       83778 :         Node( const SfxPoolItem& rItem, Node* pParent, const bool bIgnorable ) // child node Ctor
      54             :             : mChildren(),
      55             :               maItemSet(),
      56       83778 :               mpItem( rItem.Clone() ),
      57             :               mpUpper( pParent ),
      58      167556 :               mbIsItemIgnorable( bIgnorable )
      59       83778 :         {}
      60             :         ~Node();
      61             :         // #i86923#
      62             :         bool hasItemSet( const bool bCheckUsage ) const;
      63             :         // #i87808#
      64      208618 :         const StylePool::SfxItemSet_Pointer_t getItemSet() const
      65             :         {
      66      208618 :             return maItemSet.back();
      67             :         }
      68             :         const StylePool::SfxItemSet_Pointer_t getUsedOrLastAddedItemSet() const;
      69       28369 :         void setItemSet( const SfxItemSet& rSet ){ maItemSet.push_back( StylePool::SfxItemSet_Pointer_t( rSet.Clone() ) ); }
      70             :         // #i86923#
      71             :         Node* findChildNode( const SfxPoolItem& rItem,
      72             :                              const bool bIsItemIgnorable = false );
      73             :         Node* nextItemSet( Node* pLast,
      74             :                            const bool bSkipUnusedItemSet,
      75             :                            const bool bSkipIgnorable );
      76     2845040 :         const SfxPoolItem& getPoolItem() const { return *mpItem; }
      77             :         // #i86923#
      78             :         bool hasIgnorableChildren( const bool bCheckUsage ) const;
      79             :         const StylePool::SfxItemSet_Pointer_t getItemSetOfIgnorableChild(
      80             :                                         const bool bSkipUnusedItemSets ) const;
      81             :     };
      82             : 
      83             :     // #i87808#
      84         274 :     const StylePool::SfxItemSet_Pointer_t Node::getUsedOrLastAddedItemSet() const
      85             :     {
      86         274 :         std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
      87             : 
      88         274 :         for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
      89             :         {
      90         274 :             if ( (*aIter).use_count() > 1 )
      91             :             {
      92         274 :                 return *aIter;
      93             :             }
      94             :         }
      95             : 
      96           0 :         return maItemSet.back();
      97             :     }
      98             : 
      99             :     // #i86923#
     100      211138 :     bool Node::hasItemSet( const bool bCheckUsage ) const
     101             :     {
     102      211138 :         bool bHasItemSet = false;
     103             : 
     104      211138 :         if ( !maItemSet.empty())
     105             :         {
     106      181623 :             if ( bCheckUsage )
     107             :             {
     108        1219 :                 std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
     109             : 
     110        1876 :                 for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
     111             :                 {
     112        1220 :                     if ( (*aIter).use_count() > 1 )
     113             :                     {
     114         563 :                         bHasItemSet = true;
     115         563 :                         break;
     116             :                     }
     117             :                 }
     118             :             }
     119             :             else
     120             :             {
     121      180404 :                 bHasItemSet = true;
     122             :             }
     123             :         }
     124      211138 :         return bHasItemSet;
     125             :     }
     126             : 
     127             :     // #i86923#
     128      847916 :     Node* Node::findChildNode( const SfxPoolItem& rItem,
     129             :                                const bool bIsItemIgnorable )
     130             :     {
     131      847916 :         Node* pNextNode = this;
     132      847916 :         std::vector<Node*>::iterator aIter = mChildren.begin();
     133     2833540 :         while( aIter != mChildren.end() )
     134             :         {
     135     2845040 :             if( rItem.Which() == (*aIter)->getPoolItem().Which() &&
     136      943194 :                 rItem == (*aIter)->getPoolItem() )
     137      764138 :                 return *aIter;
     138     1137708 :             ++aIter;
     139             :         }
     140             :         // #i86923#
     141       83778 :         pNextNode = new Node( rItem, pNextNode, bIsItemIgnorable );
     142       83778 :         mChildren.push_back( pNextNode );
     143       83778 :         return pNextNode;
     144             :     }
     145             : 
     146             :     /**
     147             :      * Find the next node which has a SfxItemSet.
     148             :      * The input parameter pLast has a sophisticated meaning:
     149             :      * downstairs only:
     150             :      * pLast == 0 => scan your children and their children
     151             :      *               but neither your parents neither your siblings
     152             :      * downstairs and upstairs:
     153             :      * pLast == this => scan your children, their children,
     154             :      *                  the children of your parent behind you, and so on
     155             :      * partial downstairs and upstairs
     156             :      *  pLast != 0 && pLast != this => scan your children behind the given children,
     157             :      *                 the children of your parent behind you and so on.
     158             :      *
     159             :      * OD 2008-03-11 #i86923#
     160             :      * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
     161             :      * and its handling.
     162             :      */
     163        3126 :     Node* Node::nextItemSet( Node* pLast,
     164             :                              const bool bSkipUnusedItemSets,
     165             :                              const bool bSkipIgnorable )
     166             :     {
     167             :         // Searching downstairs
     168        3126 :         std::vector<Node*>::iterator aIter = mChildren.begin();
     169             :         // For pLast == 0 and pLast == this all children are of interest
     170             :         // for another pLast the search starts behind pLast...
     171        3126 :         if( pLast && pLast != this )
     172             :         {
     173         938 :             aIter = std::find( mChildren.begin(), mChildren.end(), pLast );
     174         938 :             if( aIter != mChildren.end() )
     175         938 :                 ++aIter;
     176             :         }
     177        3126 :         Node *pNext = 0;
     178        7416 :         while( aIter != mChildren.end() )
     179             :         {
     180             :             // #i86923#
     181        2103 :             if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable )
     182             :             {
     183          25 :                 ++aIter;
     184          25 :                 continue;
     185             :             }
     186        2078 :             pNext = *aIter;
     187             :             // #i86923#
     188        2078 :             if ( pNext->hasItemSet( bSkipUnusedItemSets ) )
     189             :             {
     190         260 :                 return pNext;
     191             :             }
     192        3636 :             if ( bSkipIgnorable &&
     193        1818 :                  pNext->hasIgnorableChildren( bSkipUnusedItemSets ) )
     194             :             {
     195          15 :                 return pNext;
     196             :             }
     197        1803 :             pNext = pNext->nextItemSet( 0, bSkipUnusedItemSets, bSkipIgnorable ); // 0 => downstairs only
     198        1803 :             if( pNext )
     199         664 :                 return pNext;
     200        1139 :             ++aIter;
     201             :         }
     202             :         // Searching upstairs
     203        2187 :         if( pLast && mpUpper )
     204             :         {
     205             :             // #i86923#
     206         938 :             pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable );
     207             :         }
     208        2187 :         return pNext;
     209             :     }
     210             : 
     211             :     // #i86923#
     212        1846 :     bool Node::hasIgnorableChildren( const bool bCheckUsage ) const
     213             :     {
     214        1846 :         bool bHasIgnorableChildren( false );
     215             : 
     216        1846 :         std::vector<Node*>::const_iterator aIter = mChildren.begin();
     217        5511 :         while( aIter != mChildren.end() && !bHasIgnorableChildren )
     218             :         {
     219        1819 :             Node* pChild = *aIter;
     220        1819 :             if ( pChild->mbIsItemIgnorable )
     221             :             {
     222             :                 bHasIgnorableChildren =
     223         118 :                     !bCheckUsage ||
     224          56 :                     ( pChild->hasItemSet( bCheckUsage /* == true */ ) ||
     225          56 :                       pChild->hasIgnorableChildren( bCheckUsage /* == true */ ) );
     226             :             }
     227        1819 :             ++aIter;
     228             :         }
     229             : 
     230        1846 :         return bHasIgnorableChildren;
     231             :     }
     232             : 
     233          15 :     const StylePool::SfxItemSet_Pointer_t Node::getItemSetOfIgnorableChild(
     234             :                                         const bool bSkipUnusedItemSets ) const
     235             :     {
     236             :         DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets ),
     237             :                     "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
     238             : 
     239          15 :         std::vector<Node*>::const_iterator aIter = mChildren.begin();
     240          30 :         while( aIter != mChildren.end() )
     241             :         {
     242          15 :             Node* pChild = *aIter;
     243          15 :             if ( pChild->mbIsItemIgnorable )
     244             :             {
     245          15 :                 if ( pChild->hasItemSet( bSkipUnusedItemSets ) )
     246             :                 {
     247          14 :                     return pChild->getUsedOrLastAddedItemSet();
     248             :                 }
     249             :                 else
     250             :                 {
     251           1 :                     pChild = pChild->nextItemSet( 0, bSkipUnusedItemSets, false );
     252           1 :                     if ( pChild )
     253             :                     {
     254           1 :                         return pChild->getUsedOrLastAddedItemSet();
     255             :                     }
     256             :                 }
     257             :             }
     258           0 :             ++aIter;
     259             :         }
     260             : 
     261           0 :         StylePool::SfxItemSet_Pointer_t pReturn;
     262           0 :         return pReturn;
     263             :     }
     264             : 
     265      177670 :     Node::~Node()
     266             :     {
     267       88835 :         std::vector<Node*>::iterator aIter = mChildren.begin();
     268      261340 :         while( aIter != mChildren.end() )
     269             :         {
     270       83670 :             delete *aIter;
     271       83670 :             ++aIter;
     272             :         }
     273       88835 :         delete mpItem;
     274       88835 :     }
     275             : 
     276         304 :     class Iterator : public IStylePoolIteratorAccess
     277             :     {
     278             :         std::map< const SfxItemSet*, Node >& mrRoot;
     279             :         std::map< const SfxItemSet*, Node >::iterator mpCurrNode;
     280             :         Node* mpNode;
     281             :         const bool mbSkipUnusedItemSets;
     282             :         const bool mbSkipIgnorable;
     283             :     public:
     284             :         // #i86923#
     285         152 :         Iterator( std::map< const SfxItemSet*, Node >& rR,
     286             :                   const bool bSkipUnusedItemSets,
     287             :                   const bool bSkipIgnorable )
     288             :             : mrRoot( rR ),
     289             :               mpCurrNode( rR.begin() ),
     290             :               mpNode(0),
     291             :               mbSkipUnusedItemSets( bSkipUnusedItemSets ),
     292         152 :               mbSkipIgnorable( bSkipIgnorable )
     293         152 :         {}
     294             :         virtual StylePool::SfxItemSet_Pointer_t getNext() SAL_OVERRIDE;
     295             :         virtual OUString getName() SAL_OVERRIDE;
     296             :     };
     297             : 
     298         426 :     StylePool::SfxItemSet_Pointer_t Iterator::getNext()
     299             :     {
     300         426 :         StylePool::SfxItemSet_Pointer_t pReturn;
     301         962 :         while( mpNode || mpCurrNode != mrRoot.end() )
     302             :         {
     303         384 :             if( !mpNode )
     304             :             {
     305         110 :                 mpNode = &mpCurrNode->second;
     306         110 :                 ++mpCurrNode;
     307             :                 // #i86923#
     308         110 :                 if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) )
     309             :                 {
     310             :                     // #i87808#
     311           0 :                     return mpNode->getUsedOrLastAddedItemSet();
     312             :                 }
     313             :             }
     314             :             // #i86923#
     315         384 :             mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable );
     316         384 :             if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) )
     317             :             {
     318             :                 // #i87808#
     319         259 :                 return mpNode->getUsedOrLastAddedItemSet();
     320             :             }
     321         250 :             if ( mbSkipIgnorable &&
     322         140 :                  mpNode && mpNode->hasIgnorableChildren( mbSkipUnusedItemSets ) )
     323             :             {
     324          15 :                 return mpNode->getItemSetOfIgnorableChild( mbSkipUnusedItemSets );
     325             :             }
     326             :         }
     327         152 :         return pReturn;
     328             :     }
     329             : 
     330           0 :     OUString Iterator::getName()
     331             :     {
     332           0 :         OUString aString;
     333           0 :         if( mpNode && mpNode->hasItemSet( false ) )
     334             :         {
     335           0 :             aString = StylePool::nameOf( mpNode->getUsedOrLastAddedItemSet() );
     336             :         }
     337           0 :         return aString;
     338             :     }
     339             : 
     340             : }
     341             : 
     342             : /**
     343             :  * This static method creates a unique name from a shared pointer to a SfxItemSet
     344             :  * The name is the memory address of the SfxItemSet itself.
     345             :  */
     346        6016 : OUString StylePool::nameOf( SfxItemSet_Pointer_t pSet )
     347             : {
     348        6016 :     return OUString::number( reinterpret_cast<sal_IntPtr>( pSet.get() ), 16 );
     349             : }
     350             : 
     351             : /**
     352             :  * class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
     353             :  * The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
     354             :  * to a copy of the SfxItemSet.
     355             :  * The aRoot-Node represents an empty SfxItemSet.
     356             :  */
     357             : class StylePoolImpl
     358             : {
     359             : private:
     360             :     std::map< const SfxItemSet*, Node > maRoot;
     361             :     sal_Int32 mnCount;
     362             :     // #i86923#
     363             :     SfxItemSet* mpIgnorableItems;
     364             : public:
     365             :     // #i86923#
     366        5916 :     explicit StylePoolImpl( SfxItemSet* pIgnorableItems = 0 )
     367             :         : maRoot(),
     368             :           mnCount(0),
     369             :           mpIgnorableItems( pIgnorableItems != 0
     370        2958 :                             ? pIgnorableItems->Clone( false )
     371        8874 :                             : 0 )
     372             :     {
     373             :         DBG_ASSERT( !pIgnorableItems || !pIgnorableItems->Count(),
     374             :                     "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
     375             :         DBG_ASSERT( !mpIgnorableItems || !mpIgnorableItems->Count(),
     376             :                     "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty. Please inform OD." );
     377        5916 :     }
     378             : 
     379        5898 :     ~StylePoolImpl()
     380        5898 :     {
     381        5898 :         delete mpIgnorableItems;
     382        5898 :     }
     383             : 
     384             :     StylePool::SfxItemSet_Pointer_t insertItemSet( const SfxItemSet& rSet );
     385             : 
     386             :     // #i86923#
     387             :     IStylePoolIteratorAccess* createIterator( bool bSkipUnusedItemSets = false,
     388             :                                               bool bSkipIgnorableItems = false );
     389           0 :     sal_Int32 getCount() const { return mnCount; }
     390             : };
     391             : 
     392      208618 : StylePool::SfxItemSet_Pointer_t StylePoolImpl::insertItemSet( const SfxItemSet& rSet )
     393             : {
     394      208618 :     bool bNonPoolable = false;
     395      208618 :     Node* pCurNode = &maRoot[ rSet.GetParent() ];
     396      208618 :     SfxItemIter aIter( rSet );
     397      208618 :     const SfxPoolItem* pItem = aIter.GetCurItem();
     398             :     // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
     399             :     // a complete empty SfxItemSet would stay at the root node.
     400             :     // #i86923# insert ignorable items to the tree leaves.
     401      417236 :     std::unique_ptr<SfxItemSet> xFoundIgnorableItems;
     402      208618 :     if ( mpIgnorableItems )
     403             :     {
     404      118308 :         xFoundIgnorableItems.reset( new SfxItemSet( *mpIgnorableItems ) );
     405             :     }
     406     1265152 :     while( pItem )
     407             :     {
     408      847916 :         if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SfxItemPoolFlags::POOLABLE ) )
     409        3083 :             bNonPoolable = true;
     410     2192114 :         if ( !xFoundIgnorableItems.get() ||
     411     1018240 :              ( xFoundIgnorableItems.get() &&
     412      509120 :                xFoundIgnorableItems->Put( *pItem ) == 0 ) )
     413             :         {
     414      835078 :             pCurNode = pCurNode->findChildNode( *pItem );
     415             :         }
     416      847916 :         pItem = aIter.NextItem();
     417             :     }
     418      326926 :     if ( xFoundIgnorableItems.get() &&
     419      118308 :          xFoundIgnorableItems->Count() > 0 )
     420             :     {
     421       12098 :         SfxItemIter aIgnorableItemsIter( *xFoundIgnorableItems );
     422       12098 :         pItem = aIgnorableItemsIter.GetCurItem();
     423       37034 :         while( pItem )
     424             :         {
     425       12838 :             if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SfxItemPoolFlags::POOLABLE ) )
     426           0 :                 bNonPoolable = true;
     427       12838 :             pCurNode = pCurNode->findChildNode( *pItem, true );
     428       12838 :             pItem = aIgnorableItemsIter.NextItem();
     429       12098 :         }
     430             :     }
     431             :     // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
     432             :     // of inserted itemsets.
     433             :     // These nodes could have but does not need to have a shared_ptr to a item set.
     434      208618 :     if( !pCurNode->hasItemSet( false ) )
     435             :     {
     436       28214 :         pCurNode->setItemSet( rSet );
     437       28214 :         bNonPoolable = false; // to avoid a double insertion
     438       28214 :         ++mnCount;
     439             :     }
     440             :     // If rSet contains at least one non poolable item, a new itemset has to be inserted
     441      208618 :     if( bNonPoolable )
     442         155 :         pCurNode->setItemSet( rSet );
     443             : #ifdef DEBUG
     444             :     {
     445             :         sal_Int32 nCheck = -1;
     446             :         IStylePoolIteratorAccess* pIter = createIterator();
     447             :         StylePool::SfxItemSet_Pointer_t pTemp;
     448             :         do
     449             :         {
     450             :             ++nCheck;
     451             :             pTemp = pIter->getNext();
     452             :         } while( pTemp.get() );
     453             :         DBG_ASSERT( mnCount == nCheck, "Wrong counting");
     454             :         delete pIter;
     455             :     }
     456             : #endif
     457      417236 :     return pCurNode->getItemSet();
     458             : }
     459             : 
     460             : // #i86923#
     461         152 : IStylePoolIteratorAccess* StylePoolImpl::createIterator( bool bSkipUnusedItemSets,
     462             :                                                          bool bSkipIgnorableItems )
     463             : {
     464         152 :     return new Iterator( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems );
     465             : }
     466             : // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
     467             : 
     468             : // #i86923#
     469        5916 : StylePool::StylePool( SfxItemSet* pIgnorableItems )
     470        5916 :     : pImpl( new StylePoolImpl( pIgnorableItems ) )
     471        5916 : {}
     472             : 
     473      208618 : StylePool::SfxItemSet_Pointer_t StylePool::insertItemSet( const SfxItemSet& rSet )
     474      208618 : { return pImpl->insertItemSet( rSet ); }
     475             : 
     476             : // #i86923#
     477         152 : IStylePoolIteratorAccess* StylePool::createIterator( const bool bSkipUnusedItemSets,
     478             :                                                      const bool bSkipIgnorableItems )
     479             : {
     480         152 :     return pImpl->createIterator( bSkipUnusedItemSets, bSkipIgnorableItems );
     481             : }
     482             : 
     483           0 : sal_Int32 StylePool::getCount() const
     484           0 : { return pImpl->getCount(); }
     485             : 
     486        5898 : StylePool::~StylePool() { delete pImpl; }
     487             : 
     488             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11