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>
31 /** A "Node" represents a subset of inserted SfxItemSets
32 * The root node represents the empty set
33 * The other nodes contain a SfxPoolItem and represents an item set which contains their
34 * pool item and the pool items of their parents.
38 std::vector
<std::unique_ptr
<Node
>> mChildren
; // child nodes, create by findChildNode(..)
39 // container of shared pointers of inserted item sets; for non-poolable
40 // items more than one item set is needed
41 std::vector
< std::shared_ptr
<SfxItemSet
> > maItemSet
;
42 std::unique_ptr
<const SfxPoolItem
> mpItem
; // my pool item
43 Node
*mpUpper
; // if I'm a child node that's my parent node
45 const bool mbIsItemIgnorable
;
48 Node() // root node Ctor
50 mbIsItemIgnorable( false )
52 Node( const SfxPoolItem
& rItem
, Node
* pParent
, const bool bIgnorable
) // child node Ctor
53 : mpItem( rItem
.Clone() ),
55 mbIsItemIgnorable( bIgnorable
)
58 bool hasItemSet( const bool bCheckUsage
) const;
60 std::shared_ptr
<SfxItemSet
> const & getItemSet() const
62 return maItemSet
.back();
64 std::shared_ptr
<SfxItemSet
> const & getUsedOrLastAddedItemSet() const;
65 void setItemSet( const SfxItemSet
& rSet
){ maItemSet
.push_back( std::shared_ptr
<SfxItemSet
>( rSet
.Clone() ) ); }
67 Node
* findChildNode( const SfxPoolItem
& rItem
,
68 const bool bIsItemIgnorable
);
69 Node
* nextItemSet( Node
const * pLast
,
70 const bool bSkipUnusedItemSet
,
71 const bool bSkipIgnorable
);
73 bool hasIgnorableChildren( const bool bCheckUsage
) const;
74 std::shared_ptr
<SfxItemSet
> getItemSetOfIgnorableChild(
75 const bool bSkipUnusedItemSets
) const;
79 std::shared_ptr
<SfxItemSet
> const & Node::getUsedOrLastAddedItemSet() const
81 auto aIter
= std::find_if(maItemSet
.rbegin(), maItemSet
.rend(),
82 [](const std::shared_ptr
<SfxItemSet
>& rxItemSet
) { return rxItemSet
.use_count() > 1; });
84 if (aIter
!= maItemSet
.rend())
87 return maItemSet
.back();
91 bool Node::hasItemSet( const bool bCheckUsage
) const
93 bool bHasItemSet
= false;
95 if ( !maItemSet
.empty())
99 bHasItemSet
= std::any_of(maItemSet
.rbegin(), maItemSet
.rend(),
100 [](const std::shared_ptr
<SfxItemSet
>& rxItemSet
) { return rxItemSet
.use_count() > 1; });
111 Node
* Node::findChildNode( const SfxPoolItem
& rItem
,
112 const bool bIsItemIgnorable
)
114 for( auto const & rChild
: mChildren
)
116 if( rItem
.Which() == rChild
->mpItem
->Which() &&
117 rItem
== *rChild
->mpItem
)
121 auto pNextNode
= new Node( rItem
, this, bIsItemIgnorable
);
122 mChildren
.emplace_back( pNextNode
);
127 * Find the next node which has a SfxItemSet.
128 * The input parameter pLast has a sophisticated meaning:
130 * pLast == 0 => scan your children and their children
131 * but neither your parents neither your siblings
132 * downstairs and upstairs:
133 * pLast == this => scan your children, their children,
134 * the children of your parent behind you, and so on
135 * partial downstairs and upstairs
136 * pLast != 0 && pLast != this => scan your children behind the given children,
137 * the children of your parent behind you and so on.
139 * OD 2008-03-11 #i86923#
140 * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
143 Node
* Node::nextItemSet( Node
const * pLast
,
144 const bool bSkipUnusedItemSets
,
145 const bool bSkipIgnorable
)
147 // Searching downstairs
148 auto aIter
= mChildren
.begin();
149 // For pLast == 0 and pLast == this all children are of interest
150 // for another pLast the search starts behind pLast...
151 if( pLast
&& pLast
!= this )
153 aIter
= std::find_if( mChildren
.begin(), mChildren
.end(),
154 [&] (std::unique_ptr
<Node
> const &p
) { return p
.get() == pLast
; });
155 if( aIter
!= mChildren
.end() )
158 Node
*pNext
= nullptr;
159 while( aIter
!= mChildren
.end() )
162 if ( bSkipIgnorable
&& (*aIter
)->mbIsItemIgnorable
)
167 pNext
= aIter
->get();
169 if ( pNext
->hasItemSet( bSkipUnusedItemSets
) )
173 if ( bSkipIgnorable
&&
174 pNext
->hasIgnorableChildren( bSkipUnusedItemSets
) )
178 pNext
= pNext
->nextItemSet( nullptr, bSkipUnusedItemSets
, bSkipIgnorable
); // 0 => downstairs only
183 // Searching upstairs
184 if( pLast
&& mpUpper
)
187 pNext
= mpUpper
->nextItemSet( this, bSkipUnusedItemSets
, bSkipIgnorable
);
193 bool Node::hasIgnorableChildren( const bool bCheckUsage
) const
195 return std::any_of(mChildren
.begin(), mChildren
.end(),
196 [&bCheckUsage
](const std::unique_ptr
<Node
>& rxChild
) {
197 Node
* pChild
= rxChild
.get();
198 return pChild
->mbIsItemIgnorable
&&
200 ( pChild
->hasItemSet( bCheckUsage
/* == true */ ) ||
201 pChild
->hasIgnorableChildren( bCheckUsage
/* == true */ ) ));
205 std::shared_ptr
<SfxItemSet
> Node::getItemSetOfIgnorableChild(
206 const bool bSkipUnusedItemSets
) const
208 DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets
),
209 "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
211 for( const auto& rxChild
: mChildren
)
213 Node
* pChild
= rxChild
.get();
214 if ( pChild
->mbIsItemIgnorable
)
216 if ( pChild
->hasItemSet( bSkipUnusedItemSets
) )
218 return pChild
->getUsedOrLastAddedItemSet();
222 pChild
= pChild
->nextItemSet( nullptr, bSkipUnusedItemSets
, false );
225 return pChild
->getUsedOrLastAddedItemSet();
231 return std::shared_ptr
<SfxItemSet
>();
236 std::map
< const SfxItemSet
*, Node
>& mrRoot
;
237 std::map
< const SfxItemSet
*, Node
>::iterator mpCurrNode
;
239 const bool mbSkipUnusedItemSets
;
240 const bool mbSkipIgnorable
;
241 /// List of item set parents, ordered by their name.
242 std::vector
<const SfxItemSet
*> maParents
;
243 /// The iterator's current position.
244 std::vector
<const SfxItemSet
*>::iterator mpCurrParent
;
247 Iterator( std::map
< const SfxItemSet
*, Node
>& rR
,
248 const bool bSkipUnusedItemSets
,
249 const bool bSkipIgnorable
,
250 const std::map
< const SfxItemSet
*, OUString
>& rParentNames
)
253 mbSkipUnusedItemSets( bSkipUnusedItemSets
),
254 mbSkipIgnorable( bSkipIgnorable
)
256 // Collect the parent pointers into a vector we can sort.
257 for (const auto& rParent
: mrRoot
)
258 maParents
.push_back(rParent
.first
);
260 // Sort the parents using their name, if they have one.
261 if (!rParentNames
.empty())
263 std::stable_sort(maParents
.begin(), maParents
.end(),
264 [&rParentNames
](const SfxItemSet
* pA
, const SfxItemSet
* pB
) {
267 auto it
= rParentNames
.find(pA
);
268 if (it
!= rParentNames
.end())
270 it
= rParentNames
.find(pB
);
271 if (it
!= rParentNames
.end())
277 // Start the iteration.
278 mpCurrParent
= maParents
.begin();
279 if (mpCurrParent
!= maParents
.end())
280 mpCurrNode
= mrRoot
.find(*mpCurrParent
);
282 std::shared_ptr
<SfxItemSet
> getNext();
285 std::shared_ptr
<SfxItemSet
> Iterator::getNext()
287 while( mpNode
|| mpCurrParent
!= maParents
.end() )
291 mpNode
= &mpCurrNode
->second
;
292 // Perform the actual increment.
294 if (mpCurrParent
!= maParents
.end())
295 mpCurrNode
= mrRoot
.find(*mpCurrParent
);
297 if ( mpNode
->hasItemSet( mbSkipUnusedItemSets
) )
300 return mpNode
->getUsedOrLastAddedItemSet();
304 mpNode
= mpNode
->nextItemSet( mpNode
, mbSkipUnusedItemSets
, mbSkipIgnorable
);
305 if ( mpNode
&& mpNode
->hasItemSet( mbSkipUnusedItemSets
) )
308 return mpNode
->getUsedOrLastAddedItemSet();
310 if ( mbSkipIgnorable
&&
311 mpNode
&& mpNode
->hasIgnorableChildren( mbSkipUnusedItemSets
) )
313 return mpNode
->getItemSetOfIgnorableChild( mbSkipUnusedItemSets
);
316 return std::shared_ptr
<SfxItemSet
>();
322 * This static method creates a unique name from a shared pointer to a SfxItemSet
323 * The name is the memory address of the SfxItemSet itself.
325 OUString
StylePool::nameOf( const std::shared_ptr
<SfxItemSet
>& pSet
)
327 return OUString::number( reinterpret_cast<sal_IntPtr
>( pSet
.get() ), 16 );
331 * class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
332 * The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
333 * to a copy of the SfxItemSet.
334 * The aRoot-Node represents an empty SfxItemSet.
339 std::map
< const SfxItemSet
*, Node
> maRoot
;
340 /// Names of maRoot keys.
341 std::map
< const SfxItemSet
*, OUString
> maParentNames
;
343 std::unique_ptr
<SfxItemSet
> mpIgnorableItems
;
344 #if OSL_DEBUG_LEVEL >= 2
349 explicit StylePoolImpl( SfxItemSet
const * pIgnorableItems
)
351 #if OSL_DEBUG_LEVEL >= 2
354 mpIgnorableItems( pIgnorableItems
!= nullptr
355 ? pIgnorableItems
->Clone( false )
358 DBG_ASSERT( !pIgnorableItems
|| !pIgnorableItems
->Count(),
359 "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
360 DBG_ASSERT( !mpIgnorableItems
|| !mpIgnorableItems
->Count(),
361 "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as expected - <mpIgnorableItems> is not empty." );
364 std::shared_ptr
<SfxItemSet
> insertItemSet( const SfxItemSet
& rSet
, const OUString
* pParentName
= nullptr );
367 Iterator
createIterator( bool bSkipUnusedItemSets
, bool bSkipIgnorableItems
);
371 std::shared_ptr
<SfxItemSet
> StylePoolImpl::insertItemSet( const SfxItemSet
& rSet
, const OUString
* pParentName
)
373 bool bNonShareable(false);
374 Node
* pCurNode
= &maRoot
[ rSet
.GetParent() ];
376 maParentNames
[ rSet
.GetParent() ] = *pParentName
;
377 SfxItemIter
aIter( rSet
);
378 const SfxPoolItem
* pItem
= aIter
.GetCurItem();
379 // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
380 // a complete empty SfxItemSet would stay at the root node.
381 // #i86923# insert ignorable items to the tree leaves.
382 std::optional
<SfxItemSet
> xFoundIgnorableItems
;
383 if ( mpIgnorableItems
)
385 xFoundIgnorableItems
.emplace( *mpIgnorableItems
);
389 if (!pItem
->isShareable())
390 bNonShareable
= true;
391 if (!xFoundIgnorableItems
|| (xFoundIgnorableItems
->Put(*pItem
) == nullptr))
393 pCurNode
= pCurNode
->findChildNode( *pItem
, false );
395 pItem
= aIter
.NextItem();
397 if ( xFoundIgnorableItems
&& xFoundIgnorableItems
->Count() > 0 )
399 SfxItemIter
aIgnorableItemsIter( *xFoundIgnorableItems
);
400 pItem
= aIgnorableItemsIter
.GetCurItem();
403 if (!pItem
->isShareable())
404 bNonShareable
= true;
405 pCurNode
= pCurNode
->findChildNode( *pItem
, true );
406 pItem
= aIgnorableItemsIter
.NextItem();
409 // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
410 // of inserted itemsets.
411 // These nodes could have but does not need to have a shared_ptr to an item set.
412 if( !pCurNode
->hasItemSet( false ) )
414 pCurNode
->setItemSet( rSet
);
415 bNonShareable
= false; // to avoid a double insertion
416 #if OSL_DEBUG_LEVEL >= 2
420 // If rSet contains at least one non poolable item, a new itemset has to be inserted
422 pCurNode
->setItemSet( rSet
);
423 #if OSL_DEBUG_LEVEL >= 2
425 sal_Int32 nCheck
= -1;
426 Iterator aIter
= createIterator(false,false);
427 std::shared_ptr
<SfxItemSet
> pTemp
;
431 pTemp
= aIter
.getNext();
432 } while( pTemp
.get() );
433 DBG_ASSERT( mnCount
== nCheck
, "Wrong counting");
436 return pCurNode
->getItemSet();
440 Iterator
StylePoolImpl::createIterator( bool bSkipUnusedItemSets
,
441 bool bSkipIgnorableItems
)
443 return Iterator( maRoot
, bSkipUnusedItemSets
, bSkipIgnorableItems
, maParentNames
);
445 // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
448 StylePool::StylePool( SfxItemSet
const * pIgnorableItems
)
449 : pImpl( new StylePoolImpl( pIgnorableItems
) )
452 std::shared_ptr
<SfxItemSet
> StylePool::insertItemSet( const SfxItemSet
& rSet
, const OUString
* pParentName
)
453 { return pImpl
->insertItemSet( rSet
, pParentName
); }
455 void StylePool::populateCacheMap(std::unordered_map
< OUString
, std::shared_ptr
<SfxItemSet
> >& rCacheMap
)
457 Iterator aIter
= pImpl
->createIterator(/*bSkipUnusedItemSets*/false, /*bSkipIgnorableItems*/false);
458 std::shared_ptr
<SfxItemSet
> pStyle
= aIter
.getNext();
461 OUString
aName( StylePool::nameOf(pStyle
) );
462 rCacheMap
[ aName
] = pStyle
;
463 pStyle
= aIter
.getNext();
467 void StylePool::getAllStyles( std::vector
<std::shared_ptr
<SfxItemSet
>> &rStyles
)
469 // setup <StylePool> iterator, which skips unused styles and ignorable items
470 Iterator aIter
= pImpl
->createIterator( true, true );
471 std::shared_ptr
<SfxItemSet
> pStyle
= aIter
.getNext();
474 rStyles
.push_back( pStyle
);
475 pStyle
= aIter
.getNext();
480 StylePool::~StylePool()
483 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */