xref: /core/svl/source/items/stylepool.cxx (revision 945a1196)
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<std::unique_ptr<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< std::shared_ptr<SfxItemSet> > maItemSet;
40         std::unique_ptr<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         Node() // root node Ctor
47             : mChildren(),
48               maItemSet(),
49               mpItem( nullptr ),
50               mpUpper( nullptr ),
51               mbIsItemIgnorable( false )
52         {}
53         Node( const SfxPoolItem& rItem, Node* pParent, const bool bIgnorable ) // child node Ctor
54             : mChildren(),
55               maItemSet(),
56               mpItem( rItem.Clone() ),
57               mpUpper( pParent ),
58               mbIsItemIgnorable( bIgnorable )
59         {}
60         // #i86923#
61         bool hasItemSet( const bool bCheckUsage ) const;
62         // #i87808#
63         std::shared_ptr<SfxItemSet> const & getItemSet() const
64         {
65             return maItemSet.back();
66         }
67         std::shared_ptr<SfxItemSet> const & getUsedOrLastAddedItemSet() const;
68         void setItemSet( const SfxItemSet& rSet ){ maItemSet.push_back( std::shared_ptr<SfxItemSet>( rSet.Clone() ) ); }
69         // #i86923#
70         Node* findChildNode( const SfxPoolItem& rItem,
71                              const bool bIsItemIgnorable );
72         Node* nextItemSet( Node const * pLast,
73                            const bool bSkipUnusedItemSet,
74                            const bool bSkipIgnorable );
75         // #i86923#
76         bool hasIgnorableChildren( const bool bCheckUsage ) const;
77         const std::shared_ptr<SfxItemSet> getItemSetOfIgnorableChild(
78                                         const bool bSkipUnusedItemSets ) const;
79     };
80 
81     // #i87808#
82     std::shared_ptr<SfxItemSet> const & Node::getUsedOrLastAddedItemSet() const
83     {
84         std::vector< std::shared_ptr<SfxItemSet> >::const_reverse_iterator aIter;
85 
86         for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
87         {
88             if ( (*aIter).use_count() > 1 )
89             {
90                 return *aIter;
91             }
92         }
93 
94         return maItemSet.back();
95     }
96 
97     // #i86923#
98     bool Node::hasItemSet( const bool bCheckUsage ) const
99     {
100         bool bHasItemSet = false;
101 
102         if ( !maItemSet.empty())
103         {
104             if ( bCheckUsage )
105             {
106                 std::vector< std::shared_ptr<SfxItemSet> >::const_reverse_iterator aIter;
107 
108                 for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
109                 {
110                     if ( (*aIter).use_count() > 1 )
111                     {
112                         bHasItemSet = true;
113                         break;
114                     }
115                 }
116             }
117             else
118             {
119                 bHasItemSet = true;
120             }
121         }
122         return bHasItemSet;
123     }
124 
125     // #i86923#
126     Node* Node::findChildNode( const SfxPoolItem& rItem,
127                                const bool bIsItemIgnorable )
128     {
129         for( auto const & rChild : mChildren )
130         {
131             if( rItem.Which() == rChild->mpItem->Which() &&
132                 rItem == *rChild->mpItem )
133                 return rChild.get();
134         }
135         // #i86923#
136         auto pNextNode = new Node( rItem, this, bIsItemIgnorable );
137         mChildren.emplace_back( pNextNode );
138         return pNextNode;
139     }
140 
141     /**
142      * Find the next node which has a SfxItemSet.
143      * The input parameter pLast has a sophisticated meaning:
144      * downstairs only:
145      * pLast == 0 => scan your children and their children
146      *               but neither your parents neither your siblings
147      * downstairs and upstairs:
148      * pLast == this => scan your children, their children,
149      *                  the children of your parent behind you, and so on
150      * partial downstairs and upstairs
151      *  pLast != 0 && pLast != this => scan your children behind the given children,
152      *                 the children of your parent behind you and so on.
153      *
154      * OD 2008-03-11 #i86923#
155      * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
156      * and its handling.
157      */
158     Node* Node::nextItemSet( Node const * pLast,
159                              const bool bSkipUnusedItemSets,
160                              const bool bSkipIgnorable )
161     {
162         // Searching downstairs
163         auto aIter = mChildren.begin();
164         // For pLast == 0 and pLast == this all children are of interest
165         // for another pLast the search starts behind pLast...
166         if( pLast && pLast != this )
167         {
168             aIter = std::find_if( mChildren.begin(), mChildren.end(),
169                                   [&] (std::unique_ptr<Node> const &p) { return p.get() == pLast; });
170             if( aIter != mChildren.end() )
171                 ++aIter;
172         }
173         Node *pNext = nullptr;
174         while( aIter != mChildren.end() )
175         {
176             // #i86923#
177             if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable )
178             {
179                 ++aIter;
180                 continue;
181             }
182             pNext = aIter->get();
183             // #i86923#
184             if ( pNext->hasItemSet( bSkipUnusedItemSets ) )
185             {
186                 return pNext;
187             }
188             if ( bSkipIgnorable &&
189                  pNext->hasIgnorableChildren( bSkipUnusedItemSets ) )
190             {
191                 return pNext;
192             }
193             pNext = pNext->nextItemSet( nullptr, bSkipUnusedItemSets, bSkipIgnorable ); // 0 => downstairs only
194             if( pNext )
195                 return pNext;
196             ++aIter;
197         }
198         // Searching upstairs
199         if( pLast && mpUpper )
200         {
201             // #i86923#
202             pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable );
203         }
204         return pNext;
205     }
206 
207     // #i86923#
208     bool Node::hasIgnorableChildren( const bool bCheckUsage ) const
209     {
210         bool bHasIgnorableChildren( false );
211 
212         auto aIter = mChildren.begin();
213         while( aIter != mChildren.end() && !bHasIgnorableChildren )
214         {
215             Node* pChild = aIter->get();
216             if ( pChild->mbIsItemIgnorable )
217             {
218                 bHasIgnorableChildren =
219                     !bCheckUsage ||
220                     ( pChild->hasItemSet( bCheckUsage /* == true */ ) ||
221                       pChild->hasIgnorableChildren( bCheckUsage /* == true */ ) );
222             }
223             ++aIter;
224         }
225 
226         return bHasIgnorableChildren;
227     }
228 
229     const std::shared_ptr<SfxItemSet> Node::getItemSetOfIgnorableChild(
230                                         const bool bSkipUnusedItemSets ) const
231     {
232         DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets ),
233                     "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
234 
235         auto aIter = mChildren.begin();
236         while( aIter != mChildren.end() )
237         {
238             Node* pChild = aIter->get();
239             if ( pChild->mbIsItemIgnorable )
240             {
241                 if ( pChild->hasItemSet( bSkipUnusedItemSets ) )
242                 {
243                     return pChild->getUsedOrLastAddedItemSet();
244                 }
245                 else
246                 {
247                     pChild = pChild->nextItemSet( nullptr, bSkipUnusedItemSets, false );
248                     if ( pChild )
249                     {
250                         return pChild->getUsedOrLastAddedItemSet();
251                     }
252                 }
253             }
254             ++aIter;
255         }
256 
257         std::shared_ptr<SfxItemSet> pReturn;
258         return pReturn;
259     }
260 
261     class Iterator : public IStylePoolIteratorAccess
262     {
263         std::map< const SfxItemSet*, Node >& mrRoot;
264         std::map< const SfxItemSet*, Node >::iterator mpCurrNode;
265         Node* mpNode;
266         const bool mbSkipUnusedItemSets;
267         const bool mbSkipIgnorable;
268     public:
269         // #i86923#
270         Iterator( std::map< const SfxItemSet*, Node >& rR,
271                   const bool bSkipUnusedItemSets,
272                   const bool bSkipIgnorable )
273             : mrRoot( rR ),
274               mpCurrNode( rR.begin() ),
275               mpNode(nullptr),
276               mbSkipUnusedItemSets( bSkipUnusedItemSets ),
277               mbSkipIgnorable( bSkipIgnorable )
278         {}
279         virtual std::shared_ptr<SfxItemSet> getNext() override;
280     };
281 
282     std::shared_ptr<SfxItemSet> Iterator::getNext()
283     {
284         std::shared_ptr<SfxItemSet> pReturn;
285         while( mpNode || mpCurrNode != mrRoot.end() )
286         {
287             if( !mpNode )
288             {
289                 mpNode = &mpCurrNode->second;
290                 ++mpCurrNode;
291                 // #i86923#
292                 if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) )
293                 {
294                     // #i87808#
295                     return mpNode->getUsedOrLastAddedItemSet();
296                 }
297             }
298             // #i86923#
299             mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable );
300             if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) )
301             {
302                 // #i87808#
303                 return mpNode->getUsedOrLastAddedItemSet();
304             }
305             if ( mbSkipIgnorable &&
306                  mpNode && mpNode->hasIgnorableChildren( mbSkipUnusedItemSets ) )
307             {
308                 return mpNode->getItemSetOfIgnorableChild( mbSkipUnusedItemSets );
309             }
310         }
311         return pReturn;
312     }
313 
314 }
315 
316 /**
317  * This static method creates a unique name from a shared pointer to a SfxItemSet
318  * The name is the memory address of the SfxItemSet itself.
319  */
320 OUString StylePool::nameOf( const std::shared_ptr<SfxItemSet>& pSet )
321 {
322     return OUString::number( reinterpret_cast<sal_IntPtr>( pSet.get() ), 16 );
323 }
324 
325 /**
326  * class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
327  * The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
328  * to a copy of the SfxItemSet.
329  * The aRoot-Node represents an empty SfxItemSet.
330  */
331 class StylePoolImpl
332 {
333 private:
334     std::map< const SfxItemSet*, Node > maRoot;
335     // #i86923#
336     std::unique_ptr<SfxItemSet> mpIgnorableItems;
337 public:
338     // #i86923#
339     explicit StylePoolImpl( SfxItemSet const * pIgnorableItems )
340         : maRoot(),
341           mpIgnorableItems( pIgnorableItems != nullptr
342                             ? pIgnorableItems->Clone( false )
343                             : nullptr )
344     {
345         DBG_ASSERT( !pIgnorableItems || !pIgnorableItems->Count(),
346                     "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
347         DBG_ASSERT( !mpIgnorableItems || !mpIgnorableItems->Count(),
348                     "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty." );
349     }
350 
351     std::shared_ptr<SfxItemSet> insertItemSet( const SfxItemSet& rSet );
352 
353     // #i86923#
354     IStylePoolIteratorAccess* createIterator( bool bSkipUnusedItemSets,
355                                               bool bSkipIgnorableItems );
356 };
357 
358 std::shared_ptr<SfxItemSet> StylePoolImpl::insertItemSet( const SfxItemSet& rSet )
359 {
360     bool bNonPoolable = false;
361     Node* pCurNode = &maRoot[ rSet.GetParent() ];
362     SfxItemIter aIter( rSet );
363     const SfxPoolItem* pItem = aIter.GetCurItem();
364     // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
365     // a complete empty SfxItemSet would stay at the root node.
366     // #i86923# insert ignorable items to the tree leaves.
367     std::unique_ptr<SfxItemSet> xFoundIgnorableItems;
368     if ( mpIgnorableItems )
369     {
370         xFoundIgnorableItems.reset( new SfxItemSet( *mpIgnorableItems ) );
371     }
372     while( pItem )
373     {
374         if( !rSet.GetPool()->IsItemPoolable(pItem->Which() ) )
375             bNonPoolable = true;
376         if ( !xFoundIgnorableItems.get() ||
377              (xFoundIgnorableItems->Put( *pItem ) == nullptr ) )
378         {
379             pCurNode = pCurNode->findChildNode( *pItem, false );
380         }
381         pItem = aIter.NextItem();
382     }
383     if ( xFoundIgnorableItems.get() &&
384          xFoundIgnorableItems->Count() > 0 )
385     {
386         SfxItemIter aIgnorableItemsIter( *xFoundIgnorableItems );
387         pItem = aIgnorableItemsIter.GetCurItem();
388         while( pItem )
389         {
390             if( !rSet.GetPool()->IsItemPoolable(pItem->Which() ) )
391                 bNonPoolable = true;
392             pCurNode = pCurNode->findChildNode( *pItem, true );
393             pItem = aIgnorableItemsIter.NextItem();
394         }
395     }
396     // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
397     // of inserted itemsets.
398     // These nodes could have but does not need to have a shared_ptr to a item set.
399     if( !pCurNode->hasItemSet( false ) )
400     {
401         pCurNode->setItemSet( rSet );
402         bNonPoolable = false; // to avoid a double insertion
403     }
404     // If rSet contains at least one non poolable item, a new itemset has to be inserted
405     if( bNonPoolable )
406         pCurNode->setItemSet( rSet );
407 #ifdef DEBUG
408     {
409         sal_Int32 nCheck = -1;
410         IStylePoolIteratorAccess* pIter = createIterator();
411         std::shared_ptr<SfxItemSet> pTemp;
412         do
413         {
414             ++nCheck;
415             pTemp = pIter->getNext();
416         } while( pTemp.get() );
417         DBG_ASSERT( mnCount == nCheck, "Wrong counting");
418         delete pIter;
419     }
420 #endif
421     return pCurNode->getItemSet();
422 }
423 
424 // #i86923#
425 IStylePoolIteratorAccess* StylePoolImpl::createIterator( bool bSkipUnusedItemSets,
426                                                          bool bSkipIgnorableItems )
427 {
428     return new Iterator( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems );
429 }
430 // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
431 
432 // #i86923#
433 StylePool::StylePool( SfxItemSet const * pIgnorableItems )
434     : pImpl( new StylePoolImpl( pIgnorableItems ) )
435 {}
436 
437 std::shared_ptr<SfxItemSet> StylePool::insertItemSet( const SfxItemSet& rSet )
438 { return pImpl->insertItemSet( rSet ); }
439 
440 // #i86923#
441 IStylePoolIteratorAccess* StylePool::createIterator( const bool bSkipUnusedItemSets,
442                                                      const bool bSkipIgnorableItems )
443 {
444     return pImpl->createIterator( bSkipUnusedItemSets, bSkipIgnorableItems );
445 }
446 
447 StylePool::~StylePool()
448 {}
449 
450 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
451