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