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 <o3tl/make_unique.hxx>
24 #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
52 mbIsItemIgnorable( false )
54 Node( const SfxPoolItem
& rItem
, Node
* pParent
, const bool bIgnorable
) // child node Ctor
57 mpItem( rItem
.Clone() ),
59 mbIsItemIgnorable( bIgnorable
)
62 bool hasItemSet( const bool bCheckUsage
) const;
64 std::shared_ptr
<SfxItemSet
> const & getItemSet() const
66 return maItemSet
.back();
68 std::shared_ptr
<SfxItemSet
> const & getUsedOrLastAddedItemSet() const;
69 void setItemSet( const SfxItemSet
& rSet
){ maItemSet
.push_back( std::shared_ptr
<SfxItemSet
>( rSet
.Clone() ) ); }
71 Node
* findChildNode( const SfxPoolItem
& rItem
,
72 const bool bIsItemIgnorable
);
73 Node
* nextItemSet( Node
const * pLast
,
74 const bool bSkipUnusedItemSet
,
75 const bool bSkipIgnorable
);
77 bool hasIgnorableChildren( const bool bCheckUsage
) const;
78 const std::shared_ptr
<SfxItemSet
> getItemSetOfIgnorableChild(
79 const bool bSkipUnusedItemSets
) const;
83 std::shared_ptr
<SfxItemSet
> const & Node::getUsedOrLastAddedItemSet() const
85 std::vector
< std::shared_ptr
<SfxItemSet
> >::const_reverse_iterator aIter
;
87 for ( aIter
= maItemSet
.rbegin(); aIter
!= maItemSet
.rend(); ++aIter
)
89 if ( (*aIter
).use_count() > 1 )
95 return maItemSet
.back();
99 bool Node::hasItemSet( const bool bCheckUsage
) const
101 bool bHasItemSet
= false;
103 if ( !maItemSet
.empty())
107 std::vector
< std::shared_ptr
<SfxItemSet
> >::const_reverse_iterator aIter
;
109 for ( aIter
= maItemSet
.rbegin(); aIter
!= maItemSet
.rend(); ++aIter
)
111 if ( (*aIter
).use_count() > 1 )
127 Node
* Node::findChildNode( const SfxPoolItem
& rItem
,
128 const bool bIsItemIgnorable
)
130 for( auto const & rChild
: mChildren
)
132 if( rItem
.Which() == rChild
->mpItem
->Which() &&
133 rItem
== *rChild
->mpItem
)
137 auto pNextNode
= new Node( rItem
, this, bIsItemIgnorable
);
138 mChildren
.emplace_back( pNextNode
);
143 * Find the next node which has a SfxItemSet.
144 * The input parameter pLast has a sophisticated meaning:
146 * pLast == 0 => scan your children and their children
147 * but neither your parents neither your siblings
148 * downstairs and upstairs:
149 * pLast == this => scan your children, their children,
150 * the children of your parent behind you, and so on
151 * partial downstairs and upstairs
152 * pLast != 0 && pLast != this => scan your children behind the given children,
153 * the children of your parent behind you and so on.
155 * OD 2008-03-11 #i86923#
156 * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
159 Node
* Node::nextItemSet( Node
const * pLast
,
160 const bool bSkipUnusedItemSets
,
161 const bool bSkipIgnorable
)
163 // Searching downstairs
164 auto aIter
= mChildren
.begin();
165 // For pLast == 0 and pLast == this all children are of interest
166 // for another pLast the search starts behind pLast...
167 if( pLast
&& pLast
!= this )
169 aIter
= std::find_if( mChildren
.begin(), mChildren
.end(),
170 [&] (std::unique_ptr
<Node
> const &p
) { return p
.get() == pLast
; });
171 if( aIter
!= mChildren
.end() )
174 Node
*pNext
= nullptr;
175 while( aIter
!= mChildren
.end() )
178 if ( bSkipIgnorable
&& (*aIter
)->mbIsItemIgnorable
)
183 pNext
= aIter
->get();
185 if ( pNext
->hasItemSet( bSkipUnusedItemSets
) )
189 if ( bSkipIgnorable
&&
190 pNext
->hasIgnorableChildren( bSkipUnusedItemSets
) )
194 pNext
= pNext
->nextItemSet( nullptr, bSkipUnusedItemSets
, bSkipIgnorable
); // 0 => downstairs only
199 // Searching upstairs
200 if( pLast
&& mpUpper
)
203 pNext
= mpUpper
->nextItemSet( this, bSkipUnusedItemSets
, bSkipIgnorable
);
209 bool Node::hasIgnorableChildren( const bool bCheckUsage
) const
211 bool bHasIgnorableChildren( false );
213 auto aIter
= mChildren
.begin();
214 while( aIter
!= mChildren
.end() && !bHasIgnorableChildren
)
216 Node
* pChild
= aIter
->get();
217 if ( pChild
->mbIsItemIgnorable
)
219 bHasIgnorableChildren
=
221 ( pChild
->hasItemSet( bCheckUsage
/* == true */ ) ||
222 pChild
->hasIgnorableChildren( bCheckUsage
/* == true */ ) );
227 return bHasIgnorableChildren
;
230 const std::shared_ptr
<SfxItemSet
> Node::getItemSetOfIgnorableChild(
231 const bool bSkipUnusedItemSets
) const
233 DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets
),
234 "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
236 auto aIter
= mChildren
.begin();
237 while( aIter
!= mChildren
.end() )
239 Node
* pChild
= aIter
->get();
240 if ( pChild
->mbIsItemIgnorable
)
242 if ( pChild
->hasItemSet( bSkipUnusedItemSets
) )
244 return pChild
->getUsedOrLastAddedItemSet();
248 pChild
= pChild
->nextItemSet( nullptr, bSkipUnusedItemSets
, false );
251 return pChild
->getUsedOrLastAddedItemSet();
258 std::shared_ptr
<SfxItemSet
> pReturn
;
262 class Iterator
: public IStylePoolIteratorAccess
264 std::map
< const SfxItemSet
*, Node
>& mrRoot
;
265 std::map
< const SfxItemSet
*, Node
>::iterator mpCurrNode
;
267 const bool mbSkipUnusedItemSets
;
268 const bool mbSkipIgnorable
;
271 Iterator( std::map
< const SfxItemSet
*, Node
>& rR
,
272 const bool bSkipUnusedItemSets
,
273 const bool bSkipIgnorable
)
275 mpCurrNode( rR
.begin() ),
277 mbSkipUnusedItemSets( bSkipUnusedItemSets
),
278 mbSkipIgnorable( bSkipIgnorable
)
280 virtual std::shared_ptr
<SfxItemSet
> getNext() override
;
283 std::shared_ptr
<SfxItemSet
> Iterator::getNext()
285 std::shared_ptr
<SfxItemSet
> pReturn
;
286 while( mpNode
|| mpCurrNode
!= mrRoot
.end() )
290 mpNode
= &mpCurrNode
->second
;
293 if ( mpNode
->hasItemSet( mbSkipUnusedItemSets
) )
296 return mpNode
->getUsedOrLastAddedItemSet();
300 mpNode
= mpNode
->nextItemSet( mpNode
, mbSkipUnusedItemSets
, mbSkipIgnorable
);
301 if ( mpNode
&& mpNode
->hasItemSet( mbSkipUnusedItemSets
) )
304 return mpNode
->getUsedOrLastAddedItemSet();
306 if ( mbSkipIgnorable
&&
307 mpNode
&& mpNode
->hasIgnorableChildren( mbSkipUnusedItemSets
) )
309 return mpNode
->getItemSetOfIgnorableChild( mbSkipUnusedItemSets
);
318 * This static method creates a unique name from a shared pointer to a SfxItemSet
319 * The name is the memory address of the SfxItemSet itself.
321 OUString
StylePool::nameOf( const std::shared_ptr
<SfxItemSet
>& pSet
)
323 return OUString::number( reinterpret_cast<sal_IntPtr
>( pSet
.get() ), 16 );
327 * class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
328 * The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
329 * to a copy of the SfxItemSet.
330 * The aRoot-Node represents an empty SfxItemSet.
335 std::map
< const SfxItemSet
*, Node
> maRoot
;
337 std::unique_ptr
<SfxItemSet
> mpIgnorableItems
;
343 explicit StylePoolImpl( SfxItemSet
const * pIgnorableItems
)
348 mpIgnorableItems( pIgnorableItems
!= nullptr
349 ? pIgnorableItems
->Clone( false )
352 DBG_ASSERT( !pIgnorableItems
|| !pIgnorableItems
->Count(),
353 "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
354 DBG_ASSERT( !mpIgnorableItems
|| !mpIgnorableItems
->Count(),
355 "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty." );
358 std::shared_ptr
<SfxItemSet
> insertItemSet( const SfxItemSet
& rSet
);
361 std::unique_ptr
<IStylePoolIteratorAccess
> createIterator( bool bSkipUnusedItemSets
,
362 bool bSkipIgnorableItems
);
366 std::shared_ptr
<SfxItemSet
> StylePoolImpl::insertItemSet( const SfxItemSet
& rSet
)
368 bool bNonPoolable
= false;
369 Node
* pCurNode
= &maRoot
[ rSet
.GetParent() ];
370 SfxItemIter
aIter( rSet
);
371 const SfxPoolItem
* pItem
= aIter
.GetCurItem();
372 // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
373 // a complete empty SfxItemSet would stay at the root node.
374 // #i86923# insert ignorable items to the tree leaves.
375 std::unique_ptr
<SfxItemSet
> xFoundIgnorableItems
;
376 if ( mpIgnorableItems
)
378 xFoundIgnorableItems
.reset( new SfxItemSet( *mpIgnorableItems
) );
382 if( !rSet
.GetPool()->IsItemPoolable(pItem
->Which() ) )
384 if (!xFoundIgnorableItems
|| (xFoundIgnorableItems
->Put(*pItem
) == nullptr))
386 pCurNode
= pCurNode
->findChildNode( *pItem
, false );
388 pItem
= aIter
.NextItem();
390 if ( xFoundIgnorableItems
.get() &&
391 xFoundIgnorableItems
->Count() > 0 )
393 SfxItemIter
aIgnorableItemsIter( *xFoundIgnorableItems
);
394 pItem
= aIgnorableItemsIter
.GetCurItem();
397 if( !rSet
.GetPool()->IsItemPoolable(pItem
->Which() ) )
399 pCurNode
= pCurNode
->findChildNode( *pItem
, true );
400 pItem
= aIgnorableItemsIter
.NextItem();
403 // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
404 // of inserted itemsets.
405 // These nodes could have but does not need to have a shared_ptr to a item set.
406 if( !pCurNode
->hasItemSet( false ) )
408 pCurNode
->setItemSet( rSet
);
409 bNonPoolable
= false; // to avoid a double insertion
414 // If rSet contains at least one non poolable item, a new itemset has to be inserted
416 pCurNode
->setItemSet( rSet
);
419 sal_Int32 nCheck
= -1;
420 std::unique_ptr
<IStylePoolIteratorAccess
> pIter
= createIterator(false,false);
421 std::shared_ptr
<SfxItemSet
> pTemp
;
425 pTemp
= pIter
->getNext();
426 } while( pTemp
.get() );
427 DBG_ASSERT( mnCount
== nCheck
, "Wrong counting");
430 return pCurNode
->getItemSet();
434 std::unique_ptr
<IStylePoolIteratorAccess
> StylePoolImpl::createIterator( bool bSkipUnusedItemSets
,
435 bool bSkipIgnorableItems
)
437 return o3tl::make_unique
<Iterator
>( maRoot
, bSkipUnusedItemSets
, bSkipIgnorableItems
);
439 // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
442 StylePool::StylePool( SfxItemSet
const * pIgnorableItems
)
443 : pImpl( new StylePoolImpl( pIgnorableItems
) )
446 std::shared_ptr
<SfxItemSet
> StylePool::insertItemSet( const SfxItemSet
& rSet
)
447 { return pImpl
->insertItemSet( rSet
); }
450 std::unique_ptr
<IStylePoolIteratorAccess
> StylePool::createIterator( const bool bSkipUnusedItemSets
,
451 const bool bSkipIgnorableItems
)
453 return pImpl
->createIterator( bSkipUnusedItemSets
, bSkipIgnorableItems
);
456 StylePool::~StylePool()
459 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */