bump product version to 5.0.4.1
[LibreOffice.git] / svl / source / items / stylepool.cxx
blob301572d035798e933771171a40f463229fb16998
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <algorithm>
24 #include <map>
25 #include <memory>
26 #include <vector>
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.
34 class Node
36 std::vector<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< StylePool::SfxItemSet_Pointer_t > maItemSet;
40 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( 0 ),
50 mpUpper( 0 ),
51 mbIsItemIgnorable( false )
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 )
60 ~Node();
61 // #i86923#
62 bool hasItemSet( const bool bCheckUsage ) const;
63 // #i87808#
64 const StylePool::SfxItemSet_Pointer_t getItemSet() const
66 return maItemSet.back();
68 const StylePool::SfxItemSet_Pointer_t getUsedOrLastAddedItemSet() const;
69 void setItemSet( const SfxItemSet& rSet ){ maItemSet.push_back( StylePool::SfxItemSet_Pointer_t( rSet.Clone() ) ); }
70 // #i86923#
71 Node* findChildNode( const SfxPoolItem& rItem,
72 const bool bIsItemIgnorable = false );
73 Node* nextItemSet( Node* pLast,
74 const bool bSkipUnusedItemSet,
75 const bool bSkipIgnorable );
76 const SfxPoolItem& getPoolItem() const { return *mpItem; }
77 // #i86923#
78 bool hasIgnorableChildren( const bool bCheckUsage ) const;
79 const StylePool::SfxItemSet_Pointer_t getItemSetOfIgnorableChild(
80 const bool bSkipUnusedItemSets ) const;
83 // #i87808#
84 const StylePool::SfxItemSet_Pointer_t Node::getUsedOrLastAddedItemSet() const
86 std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
88 for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
90 if ( (*aIter).use_count() > 1 )
92 return *aIter;
96 return maItemSet.back();
99 // #i86923#
100 bool Node::hasItemSet( const bool bCheckUsage ) const
102 bool bHasItemSet = false;
104 if ( !maItemSet.empty())
106 if ( bCheckUsage )
108 std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
110 for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
112 if ( (*aIter).use_count() > 1 )
114 bHasItemSet = true;
115 break;
119 else
121 bHasItemSet = true;
124 return bHasItemSet;
127 // #i86923#
128 Node* Node::findChildNode( const SfxPoolItem& rItem,
129 const bool bIsItemIgnorable )
131 Node* pNextNode = this;
132 std::vector<Node*>::iterator aIter = mChildren.begin();
133 while( aIter != mChildren.end() )
135 if( rItem.Which() == (*aIter)->getPoolItem().Which() &&
136 rItem == (*aIter)->getPoolItem() )
137 return *aIter;
138 ++aIter;
140 // #i86923#
141 pNextNode = new Node( rItem, pNextNode, bIsItemIgnorable );
142 mChildren.push_back( pNextNode );
143 return pNextNode;
147 * Find the next node which has a SfxItemSet.
148 * The input parameter pLast has a sophisticated meaning:
149 * downstairs only:
150 * pLast == 0 => scan your children and their children
151 * but neither your parents neither your siblings
152 * downstairs and upstairs:
153 * pLast == this => scan your children, their children,
154 * the children of your parent behind you, and so on
155 * partial downstairs and upstairs
156 * pLast != 0 && pLast != this => scan your children behind the given children,
157 * the children of your parent behind you and so on.
159 * OD 2008-03-11 #i86923#
160 * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
161 * and its handling.
163 Node* Node::nextItemSet( Node* pLast,
164 const bool bSkipUnusedItemSets,
165 const bool bSkipIgnorable )
167 // Searching downstairs
168 std::vector<Node*>::iterator aIter = mChildren.begin();
169 // For pLast == 0 and pLast == this all children are of interest
170 // for another pLast the search starts behind pLast...
171 if( pLast && pLast != this )
173 aIter = std::find( mChildren.begin(), mChildren.end(), pLast );
174 if( aIter != mChildren.end() )
175 ++aIter;
177 Node *pNext = 0;
178 while( aIter != mChildren.end() )
180 // #i86923#
181 if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable )
183 ++aIter;
184 continue;
186 pNext = *aIter;
187 // #i86923#
188 if ( pNext->hasItemSet( bSkipUnusedItemSets ) )
190 return pNext;
192 if ( bSkipIgnorable &&
193 pNext->hasIgnorableChildren( bSkipUnusedItemSets ) )
195 return pNext;
197 pNext = pNext->nextItemSet( 0, bSkipUnusedItemSets, bSkipIgnorable ); // 0 => downstairs only
198 if( pNext )
199 return pNext;
200 ++aIter;
202 // Searching upstairs
203 if( pLast && mpUpper )
205 // #i86923#
206 pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable );
208 return pNext;
211 // #i86923#
212 bool Node::hasIgnorableChildren( const bool bCheckUsage ) const
214 bool bHasIgnorableChildren( false );
216 std::vector<Node*>::const_iterator aIter = mChildren.begin();
217 while( aIter != mChildren.end() && !bHasIgnorableChildren )
219 Node* pChild = *aIter;
220 if ( pChild->mbIsItemIgnorable )
222 bHasIgnorableChildren =
223 !bCheckUsage ||
224 ( pChild->hasItemSet( bCheckUsage /* == true */ ) ||
225 pChild->hasIgnorableChildren( bCheckUsage /* == true */ ) );
227 ++aIter;
230 return bHasIgnorableChildren;
233 const StylePool::SfxItemSet_Pointer_t Node::getItemSetOfIgnorableChild(
234 const bool bSkipUnusedItemSets ) const
236 DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets ),
237 "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
239 std::vector<Node*>::const_iterator aIter = mChildren.begin();
240 while( aIter != mChildren.end() )
242 Node* pChild = *aIter;
243 if ( pChild->mbIsItemIgnorable )
245 if ( pChild->hasItemSet( bSkipUnusedItemSets ) )
247 return pChild->getUsedOrLastAddedItemSet();
249 else
251 pChild = pChild->nextItemSet( 0, bSkipUnusedItemSets, false );
252 if ( pChild )
254 return pChild->getUsedOrLastAddedItemSet();
258 ++aIter;
261 StylePool::SfxItemSet_Pointer_t pReturn;
262 return pReturn;
265 Node::~Node()
267 std::vector<Node*>::iterator aIter = mChildren.begin();
268 while( aIter != mChildren.end() )
270 delete *aIter;
271 ++aIter;
273 delete mpItem;
276 class Iterator : public IStylePoolIteratorAccess
278 std::map< const SfxItemSet*, Node >& mrRoot;
279 std::map< const SfxItemSet*, Node >::iterator mpCurrNode;
280 Node* mpNode;
281 const bool mbSkipUnusedItemSets;
282 const bool mbSkipIgnorable;
283 public:
284 // #i86923#
285 Iterator( std::map< const SfxItemSet*, Node >& rR,
286 const bool bSkipUnusedItemSets,
287 const bool bSkipIgnorable )
288 : mrRoot( rR ),
289 mpCurrNode( rR.begin() ),
290 mpNode(0),
291 mbSkipUnusedItemSets( bSkipUnusedItemSets ),
292 mbSkipIgnorable( bSkipIgnorable )
294 virtual StylePool::SfxItemSet_Pointer_t getNext() SAL_OVERRIDE;
295 virtual OUString getName() SAL_OVERRIDE;
298 StylePool::SfxItemSet_Pointer_t Iterator::getNext()
300 StylePool::SfxItemSet_Pointer_t pReturn;
301 while( mpNode || mpCurrNode != mrRoot.end() )
303 if( !mpNode )
305 mpNode = &mpCurrNode->second;
306 ++mpCurrNode;
307 // #i86923#
308 if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) )
310 // #i87808#
311 return mpNode->getUsedOrLastAddedItemSet();
314 // #i86923#
315 mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable );
316 if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) )
318 // #i87808#
319 return mpNode->getUsedOrLastAddedItemSet();
321 if ( mbSkipIgnorable &&
322 mpNode && mpNode->hasIgnorableChildren( mbSkipUnusedItemSets ) )
324 return mpNode->getItemSetOfIgnorableChild( mbSkipUnusedItemSets );
327 return pReturn;
330 OUString Iterator::getName()
332 OUString aString;
333 if( mpNode && mpNode->hasItemSet( false ) )
335 aString = StylePool::nameOf( mpNode->getUsedOrLastAddedItemSet() );
337 return aString;
343 * This static method creates a unique name from a shared pointer to a SfxItemSet
344 * The name is the memory address of the SfxItemSet itself.
346 OUString StylePool::nameOf( SfxItemSet_Pointer_t pSet )
348 return OUString::number( reinterpret_cast<sal_IntPtr>( pSet.get() ), 16 );
352 * class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
353 * The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
354 * to a copy of the SfxItemSet.
355 * The aRoot-Node represents an empty SfxItemSet.
357 class StylePoolImpl
359 private:
360 std::map< const SfxItemSet*, Node > maRoot;
361 sal_Int32 mnCount;
362 // #i86923#
363 SfxItemSet* mpIgnorableItems;
364 public:
365 // #i86923#
366 explicit StylePoolImpl( SfxItemSet* pIgnorableItems = 0 )
367 : maRoot(),
368 mnCount(0),
369 mpIgnorableItems( pIgnorableItems != 0
370 ? pIgnorableItems->Clone( false )
371 : 0 )
373 DBG_ASSERT( !pIgnorableItems || !pIgnorableItems->Count(),
374 "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
375 DBG_ASSERT( !mpIgnorableItems || !mpIgnorableItems->Count(),
376 "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty. Please inform OD." );
379 ~StylePoolImpl()
381 delete mpIgnorableItems;
384 StylePool::SfxItemSet_Pointer_t insertItemSet( const SfxItemSet& rSet );
386 // #i86923#
387 IStylePoolIteratorAccess* createIterator( bool bSkipUnusedItemSets = false,
388 bool bSkipIgnorableItems = false );
389 sal_Int32 getCount() const { return mnCount; }
392 StylePool::SfxItemSet_Pointer_t StylePoolImpl::insertItemSet( const SfxItemSet& rSet )
394 bool bNonPoolable = false;
395 Node* pCurNode = &maRoot[ rSet.GetParent() ];
396 SfxItemIter aIter( rSet );
397 const SfxPoolItem* pItem = aIter.GetCurItem();
398 // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
399 // a complete empty SfxItemSet would stay at the root node.
400 // #i86923# insert ignorable items to the tree leaves.
401 std::unique_ptr<SfxItemSet> xFoundIgnorableItems;
402 if ( mpIgnorableItems )
404 xFoundIgnorableItems.reset( new SfxItemSet( *mpIgnorableItems ) );
406 while( pItem )
408 if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SfxItemPoolFlags::POOLABLE ) )
409 bNonPoolable = true;
410 if ( !xFoundIgnorableItems.get() ||
411 ( xFoundIgnorableItems.get() &&
412 xFoundIgnorableItems->Put( *pItem ) == 0 ) )
414 pCurNode = pCurNode->findChildNode( *pItem );
416 pItem = aIter.NextItem();
418 if ( xFoundIgnorableItems.get() &&
419 xFoundIgnorableItems->Count() > 0 )
421 SfxItemIter aIgnorableItemsIter( *xFoundIgnorableItems );
422 pItem = aIgnorableItemsIter.GetCurItem();
423 while( pItem )
425 if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SfxItemPoolFlags::POOLABLE ) )
426 bNonPoolable = true;
427 pCurNode = pCurNode->findChildNode( *pItem, true );
428 pItem = aIgnorableItemsIter.NextItem();
431 // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
432 // of inserted itemsets.
433 // These nodes could have but does not need to have a shared_ptr to a item set.
434 if( !pCurNode->hasItemSet( false ) )
436 pCurNode->setItemSet( rSet );
437 bNonPoolable = false; // to avoid a double insertion
438 ++mnCount;
440 // If rSet contains at least one non poolable item, a new itemset has to be inserted
441 if( bNonPoolable )
442 pCurNode->setItemSet( rSet );
443 #ifdef DEBUG
445 sal_Int32 nCheck = -1;
446 IStylePoolIteratorAccess* pIter = createIterator();
447 StylePool::SfxItemSet_Pointer_t pTemp;
450 ++nCheck;
451 pTemp = pIter->getNext();
452 } while( pTemp.get() );
453 DBG_ASSERT( mnCount == nCheck, "Wrong counting");
454 delete pIter;
456 #endif
457 return pCurNode->getItemSet();
460 // #i86923#
461 IStylePoolIteratorAccess* StylePoolImpl::createIterator( bool bSkipUnusedItemSets,
462 bool bSkipIgnorableItems )
464 return new Iterator( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems );
466 // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
468 // #i86923#
469 StylePool::StylePool( SfxItemSet* pIgnorableItems )
470 : pImpl( new StylePoolImpl( pIgnorableItems ) )
473 StylePool::SfxItemSet_Pointer_t StylePool::insertItemSet( const SfxItemSet& rSet )
474 { return pImpl->insertItemSet( rSet ); }
476 // #i86923#
477 IStylePoolIteratorAccess* StylePool::createIterator( const bool bSkipUnusedItemSets,
478 const bool bSkipIgnorableItems )
480 return pImpl->createIterator( bSkipUnusedItemSets, bSkipIgnorableItems );
483 sal_Int32 StylePool::getCount() const
484 { return pImpl->getCount(); }
486 StylePool::~StylePool() { delete pImpl; }
488 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */