1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <svl/stylepool.hxx>
21 #include <svl/itemiter.hxx>
22 #include <svl/itempool.hxx>
23 #include <tools/debug.hxx>
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.
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
44 const bool mbIsItemIgnorable
;
47 Node() // root node Ctor
51 mbIsItemIgnorable( false )
53 Node( const SfxPoolItem
& rItem
, Node
* pParent
, const bool bIgnorable
) // child node Ctor
56 mpItem( rItem
.Clone() ),
58 mbIsItemIgnorable( bIgnorable
)
61 bool hasItemSet( const bool bCheckUsage
) const;
63 std::shared_ptr
<SfxItemSet
> const & getItemSet() const
65 return maItemSet
.back();
67 std::shared_ptr
<SfxItemSet
> const & getUsedOrLastAddedItemSet() const;
68 void setItemSet( const SfxItemSet
& rSet
){ maItemSet
.push_back( std::shared_ptr
<SfxItemSet
>( rSet
.Clone() ) ); }
70 Node
* findChildNode( const SfxPoolItem
& rItem
,
71 const bool bIsItemIgnorable
);
72 Node
* nextItemSet( Node
const * pLast
,
73 const bool bSkipUnusedItemSet
,
74 const bool bSkipIgnorable
);
76 bool hasIgnorableChildren( const bool bCheckUsage
) const;
77 const std::shared_ptr
<SfxItemSet
> getItemSetOfIgnorableChild(
78 const bool bSkipUnusedItemSets
) const;
82 std::shared_ptr
<SfxItemSet
> const & Node::getUsedOrLastAddedItemSet() const
84 auto aIter
= std::find_if(maItemSet
.rbegin(), maItemSet
.rend(),
85 [](const std::shared_ptr
<SfxItemSet
>& rxItemSet
) { return rxItemSet
.use_count() > 1; });
87 if (aIter
!= maItemSet
.rend())
90 return maItemSet
.back();
94 bool Node::hasItemSet( const bool bCheckUsage
) const
96 bool bHasItemSet
= false;
98 if ( !maItemSet
.empty())
102 bHasItemSet
= std::any_of(maItemSet
.rbegin(), maItemSet
.rend(),
103 [](const std::shared_ptr
<SfxItemSet
>& rxItemSet
) { return rxItemSet
.use_count() > 1; });
114 Node
* Node::findChildNode( const SfxPoolItem
& rItem
,
115 const bool bIsItemIgnorable
)
117 for( auto const & rChild
: mChildren
)
119 if( rItem
.Which() == rChild
->mpItem
->Which() &&
120 rItem
== *rChild
->mpItem
)
124 auto pNextNode
= new Node( rItem
, this, bIsItemIgnorable
);
125 mChildren
.emplace_back( pNextNode
);
130 * Find the next node which has a SfxItemSet.
131 * The input parameter pLast has a sophisticated meaning:
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.
142 * OD 2008-03-11 #i86923#
143 * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
146 Node
* Node::nextItemSet( Node
const * pLast
,
147 const bool bSkipUnusedItemSets
,
148 const bool bSkipIgnorable
)
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 )
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() )
161 Node
*pNext
= nullptr;
162 while( aIter
!= mChildren
.end() )
165 if ( bSkipIgnorable
&& (*aIter
)->mbIsItemIgnorable
)
170 pNext
= aIter
->get();
172 if ( pNext
->hasItemSet( bSkipUnusedItemSets
) )
176 if ( bSkipIgnorable
&&
177 pNext
->hasIgnorableChildren( bSkipUnusedItemSets
) )
181 pNext
= pNext
->nextItemSet( nullptr, bSkipUnusedItemSets
, bSkipIgnorable
); // 0 => downstairs only
186 // Searching upstairs
187 if( pLast
&& mpUpper
)
190 pNext
= mpUpper
->nextItemSet( this, bSkipUnusedItemSets
, bSkipIgnorable
);
196 bool Node::hasIgnorableChildren( const bool bCheckUsage
) const
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
&&
203 ( pChild
->hasItemSet( bCheckUsage
/* == true */ ) ||
204 pChild
->hasIgnorableChildren( bCheckUsage
/* == true */ ) ));
208 const std::shared_ptr
<SfxItemSet
> Node::getItemSetOfIgnorableChild(
209 const bool bSkipUnusedItemSets
) const
211 DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets
),
212 "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
214 for( const auto& rxChild
: mChildren
)
216 Node
* pChild
= rxChild
.get();
217 if ( pChild
->mbIsItemIgnorable
)
219 if ( pChild
->hasItemSet( bSkipUnusedItemSets
) )
221 return pChild
->getUsedOrLastAddedItemSet();
225 pChild
= pChild
->nextItemSet( nullptr, bSkipUnusedItemSets
, false );
228 return pChild
->getUsedOrLastAddedItemSet();
234 std::shared_ptr
<SfxItemSet
> pReturn
;
238 class Iterator
: public IStylePoolIteratorAccess
240 std::map
< const SfxItemSet
*, Node
>& mrRoot
;
241 std::map
< const SfxItemSet
*, Node
>::iterator mpCurrNode
;
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
;
251 Iterator( std::map
< const SfxItemSet
*, Node
>& rR
,
252 const bool bSkipUnusedItemSets
,
253 const bool bSkipIgnorable
,
254 const std::map
< const SfxItemSet
*, OUString
>& rParentNames
)
257 mbSkipUnusedItemSets( bSkipUnusedItemSets
),
258 mbSkipIgnorable( bSkipIgnorable
)
260 // Collect the parent pointers into a vector we can sort.
261 for (const auto& rParent
: mrRoot
)
262 maParents
.push_back(rParent
.first
);
264 // Sort the parents using their name, if they have one.
265 if (!rParentNames
.empty())
267 std::sort(maParents
.begin(), maParents
.end(),
268 [&rParentNames
](const SfxItemSet
* pA
, const SfxItemSet
* pB
) {
271 auto it
= rParentNames
.find(pA
);
272 if (it
!= rParentNames
.end())
274 it
= rParentNames
.find(pB
);
275 if (it
!= rParentNames
.end())
281 // Start the iteration.
282 mpCurrParent
= maParents
.begin();
283 if (mpCurrParent
!= maParents
.end())
284 mpCurrNode
= mrRoot
.find(*mpCurrParent
);
286 virtual std::shared_ptr
<SfxItemSet
> getNext() override
;
289 std::shared_ptr
<SfxItemSet
> Iterator::getNext()
291 std::shared_ptr
<SfxItemSet
> pReturn
;
292 while( mpNode
|| mpCurrParent
!= maParents
.end() )
296 mpNode
= &mpCurrNode
->second
;
297 // Perform the actual increment.
299 if (mpCurrParent
!= maParents
.end())
300 mpCurrNode
= mrRoot
.find(*mpCurrParent
);
302 if ( mpNode
->hasItemSet( mbSkipUnusedItemSets
) )
305 return mpNode
->getUsedOrLastAddedItemSet();
309 mpNode
= mpNode
->nextItemSet( mpNode
, mbSkipUnusedItemSets
, mbSkipIgnorable
);
310 if ( mpNode
&& mpNode
->hasItemSet( mbSkipUnusedItemSets
) )
313 return mpNode
->getUsedOrLastAddedItemSet();
315 if ( mbSkipIgnorable
&&
316 mpNode
&& mpNode
->hasIgnorableChildren( mbSkipUnusedItemSets
) )
318 return mpNode
->getItemSetOfIgnorableChild( mbSkipUnusedItemSets
);
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.
330 OUString
StylePool::nameOf( const std::shared_ptr
<SfxItemSet
>& pSet
)
332 return OUString::number( reinterpret_cast<sal_IntPtr
>( pSet
.get() ), 16 );
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.
344 std::map
< const SfxItemSet
*, Node
> maRoot
;
345 /// Names of maRoot keys.
346 std::map
< const SfxItemSet
*, OUString
> maParentNames
;
348 std::unique_ptr
<SfxItemSet
> mpIgnorableItems
;
354 explicit StylePoolImpl( SfxItemSet
const * pIgnorableItems
)
359 mpIgnorableItems( pIgnorableItems
!= nullptr
360 ? pIgnorableItems
->Clone( false )
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." );
369 std::shared_ptr
<SfxItemSet
> insertItemSet( const SfxItemSet
& rSet
, const OUString
* pParentName
= nullptr );
372 std::unique_ptr
<IStylePoolIteratorAccess
> createIterator( bool bSkipUnusedItemSets
,
373 bool bSkipIgnorableItems
);
377 std::shared_ptr
<SfxItemSet
> StylePoolImpl::insertItemSet( const SfxItemSet
& rSet
, const OUString
* pParentName
)
379 bool bNonPoolable
= false;
380 Node
* pCurNode
= &maRoot
[ rSet
.GetParent() ];
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
)
391 xFoundIgnorableItems
.reset( new SfxItemSet( *mpIgnorableItems
) );
395 if( !rSet
.GetPool()->IsItemPoolable(pItem
->Which() ) )
397 if (!xFoundIgnorableItems
|| (xFoundIgnorableItems
->Put(*pItem
) == nullptr))
399 pCurNode
= pCurNode
->findChildNode( *pItem
, false );
401 pItem
= aIter
.NextItem();
403 if ( xFoundIgnorableItems
.get() &&
404 xFoundIgnorableItems
->Count() > 0 )
406 SfxItemIter
aIgnorableItemsIter( *xFoundIgnorableItems
);
407 pItem
= aIgnorableItemsIter
.GetCurItem();
410 if( !rSet
.GetPool()->IsItemPoolable(pItem
->Which() ) )
412 pCurNode
= pCurNode
->findChildNode( *pItem
, true );
413 pItem
= aIgnorableItemsIter
.NextItem();
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 a item set.
419 if( !pCurNode
->hasItemSet( false ) )
421 pCurNode
->setItemSet( rSet
);
422 bNonPoolable
= false; // to avoid a double insertion
427 // If rSet contains at least one non poolable item, a new itemset has to be inserted
429 pCurNode
->setItemSet( rSet
);
432 sal_Int32 nCheck
= -1;
433 std::unique_ptr
<IStylePoolIteratorAccess
> pIter
= createIterator(false,false);
434 std::shared_ptr
<SfxItemSet
> pTemp
;
438 pTemp
= pIter
->getNext();
439 } while( pTemp
.get() );
440 DBG_ASSERT( mnCount
== nCheck
, "Wrong counting");
443 return pCurNode
->getItemSet();
447 std::unique_ptr
<IStylePoolIteratorAccess
> StylePoolImpl::createIterator( bool bSkipUnusedItemSets
,
448 bool bSkipIgnorableItems
)
450 return std::make_unique
<Iterator
>( maRoot
, bSkipUnusedItemSets
, bSkipIgnorableItems
, maParentNames
);
452 // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
455 StylePool::StylePool( SfxItemSet
const * pIgnorableItems
)
456 : pImpl( new StylePoolImpl( pIgnorableItems
) )
459 std::shared_ptr
<SfxItemSet
> StylePool::insertItemSet( const SfxItemSet
& rSet
, const OUString
* pParentName
)
460 { return pImpl
->insertItemSet( rSet
, pParentName
); }
463 std::unique_ptr
<IStylePoolIteratorAccess
> StylePool::createIterator( const bool bSkipUnusedItemSets
,
464 const bool bSkipIgnorableItems
)
466 return pImpl
->createIterator( bSkipUnusedItemSets
, bSkipIgnorableItems
);
469 StylePool::~StylePool()
472 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */