Line data Source code
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 <algorithm>
24 : #include <map>
25 : #include <memory>
26 : #include <vector>
27 :
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.
33 : */
34 : class Node
35 : {
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 5178 : Node() // root node Ctor
47 : : mChildren(),
48 : maItemSet(),
49 : mpItem( 0 ),
50 : mpUpper( 0 ),
51 5178 : mbIsItemIgnorable( false )
52 5178 : {}
53 83778 : Node( const SfxPoolItem& rItem, Node* pParent, const bool bIgnorable ) // child node Ctor
54 : : mChildren(),
55 : maItemSet(),
56 83778 : mpItem( rItem.Clone() ),
57 : mpUpper( pParent ),
58 167556 : mbIsItemIgnorable( bIgnorable )
59 83778 : {}
60 : ~Node();
61 : // #i86923#
62 : bool hasItemSet( const bool bCheckUsage ) const;
63 : // #i87808#
64 208618 : const StylePool::SfxItemSet_Pointer_t getItemSet() const
65 : {
66 208618 : return maItemSet.back();
67 : }
68 : const StylePool::SfxItemSet_Pointer_t getUsedOrLastAddedItemSet() const;
69 28369 : 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 2845040 : 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;
81 : };
82 :
83 : // #i87808#
84 274 : const StylePool::SfxItemSet_Pointer_t Node::getUsedOrLastAddedItemSet() const
85 : {
86 274 : std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
87 :
88 274 : for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
89 : {
90 274 : if ( (*aIter).use_count() > 1 )
91 : {
92 274 : return *aIter;
93 : }
94 : }
95 :
96 0 : return maItemSet.back();
97 : }
98 :
99 : // #i86923#
100 211138 : bool Node::hasItemSet( const bool bCheckUsage ) const
101 : {
102 211138 : bool bHasItemSet = false;
103 :
104 211138 : if ( !maItemSet.empty())
105 : {
106 181623 : if ( bCheckUsage )
107 : {
108 1219 : std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
109 :
110 1876 : for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
111 : {
112 1220 : if ( (*aIter).use_count() > 1 )
113 : {
114 563 : bHasItemSet = true;
115 563 : break;
116 : }
117 : }
118 : }
119 : else
120 : {
121 180404 : bHasItemSet = true;
122 : }
123 : }
124 211138 : return bHasItemSet;
125 : }
126 :
127 : // #i86923#
128 847916 : Node* Node::findChildNode( const SfxPoolItem& rItem,
129 : const bool bIsItemIgnorable )
130 : {
131 847916 : Node* pNextNode = this;
132 847916 : std::vector<Node*>::iterator aIter = mChildren.begin();
133 2833540 : while( aIter != mChildren.end() )
134 : {
135 2845040 : if( rItem.Which() == (*aIter)->getPoolItem().Which() &&
136 943194 : rItem == (*aIter)->getPoolItem() )
137 764138 : return *aIter;
138 1137708 : ++aIter;
139 : }
140 : // #i86923#
141 83778 : pNextNode = new Node( rItem, pNextNode, bIsItemIgnorable );
142 83778 : mChildren.push_back( pNextNode );
143 83778 : return pNextNode;
144 : }
145 :
146 : /**
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.
158 : *
159 : * OD 2008-03-11 #i86923#
160 : * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
161 : * and its handling.
162 : */
163 3126 : Node* Node::nextItemSet( Node* pLast,
164 : const bool bSkipUnusedItemSets,
165 : const bool bSkipIgnorable )
166 : {
167 : // Searching downstairs
168 3126 : 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 3126 : if( pLast && pLast != this )
172 : {
173 938 : aIter = std::find( mChildren.begin(), mChildren.end(), pLast );
174 938 : if( aIter != mChildren.end() )
175 938 : ++aIter;
176 : }
177 3126 : Node *pNext = 0;
178 7416 : while( aIter != mChildren.end() )
179 : {
180 : // #i86923#
181 2103 : if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable )
182 : {
183 25 : ++aIter;
184 25 : continue;
185 : }
186 2078 : pNext = *aIter;
187 : // #i86923#
188 2078 : if ( pNext->hasItemSet( bSkipUnusedItemSets ) )
189 : {
190 260 : return pNext;
191 : }
192 3636 : if ( bSkipIgnorable &&
193 1818 : pNext->hasIgnorableChildren( bSkipUnusedItemSets ) )
194 : {
195 15 : return pNext;
196 : }
197 1803 : pNext = pNext->nextItemSet( 0, bSkipUnusedItemSets, bSkipIgnorable ); // 0 => downstairs only
198 1803 : if( pNext )
199 664 : return pNext;
200 1139 : ++aIter;
201 : }
202 : // Searching upstairs
203 2187 : if( pLast && mpUpper )
204 : {
205 : // #i86923#
206 938 : pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable );
207 : }
208 2187 : return pNext;
209 : }
210 :
211 : // #i86923#
212 1846 : bool Node::hasIgnorableChildren( const bool bCheckUsage ) const
213 : {
214 1846 : bool bHasIgnorableChildren( false );
215 :
216 1846 : std::vector<Node*>::const_iterator aIter = mChildren.begin();
217 5511 : while( aIter != mChildren.end() && !bHasIgnorableChildren )
218 : {
219 1819 : Node* pChild = *aIter;
220 1819 : if ( pChild->mbIsItemIgnorable )
221 : {
222 : bHasIgnorableChildren =
223 118 : !bCheckUsage ||
224 56 : ( pChild->hasItemSet( bCheckUsage /* == true */ ) ||
225 56 : pChild->hasIgnorableChildren( bCheckUsage /* == true */ ) );
226 : }
227 1819 : ++aIter;
228 : }
229 :
230 1846 : return bHasIgnorableChildren;
231 : }
232 :
233 15 : const StylePool::SfxItemSet_Pointer_t Node::getItemSetOfIgnorableChild(
234 : const bool bSkipUnusedItemSets ) const
235 : {
236 : DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets ),
237 : "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
238 :
239 15 : std::vector<Node*>::const_iterator aIter = mChildren.begin();
240 30 : while( aIter != mChildren.end() )
241 : {
242 15 : Node* pChild = *aIter;
243 15 : if ( pChild->mbIsItemIgnorable )
244 : {
245 15 : if ( pChild->hasItemSet( bSkipUnusedItemSets ) )
246 : {
247 14 : return pChild->getUsedOrLastAddedItemSet();
248 : }
249 : else
250 : {
251 1 : pChild = pChild->nextItemSet( 0, bSkipUnusedItemSets, false );
252 1 : if ( pChild )
253 : {
254 1 : return pChild->getUsedOrLastAddedItemSet();
255 : }
256 : }
257 : }
258 0 : ++aIter;
259 : }
260 :
261 0 : StylePool::SfxItemSet_Pointer_t pReturn;
262 0 : return pReturn;
263 : }
264 :
265 177670 : Node::~Node()
266 : {
267 88835 : std::vector<Node*>::iterator aIter = mChildren.begin();
268 261340 : while( aIter != mChildren.end() )
269 : {
270 83670 : delete *aIter;
271 83670 : ++aIter;
272 : }
273 88835 : delete mpItem;
274 88835 : }
275 :
276 304 : class Iterator : public IStylePoolIteratorAccess
277 : {
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 152 : 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 152 : mbSkipIgnorable( bSkipIgnorable )
293 152 : {}
294 : virtual StylePool::SfxItemSet_Pointer_t getNext() SAL_OVERRIDE;
295 : virtual OUString getName() SAL_OVERRIDE;
296 : };
297 :
298 426 : StylePool::SfxItemSet_Pointer_t Iterator::getNext()
299 : {
300 426 : StylePool::SfxItemSet_Pointer_t pReturn;
301 962 : while( mpNode || mpCurrNode != mrRoot.end() )
302 : {
303 384 : if( !mpNode )
304 : {
305 110 : mpNode = &mpCurrNode->second;
306 110 : ++mpCurrNode;
307 : // #i86923#
308 110 : if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) )
309 : {
310 : // #i87808#
311 0 : return mpNode->getUsedOrLastAddedItemSet();
312 : }
313 : }
314 : // #i86923#
315 384 : mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable );
316 384 : if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) )
317 : {
318 : // #i87808#
319 259 : return mpNode->getUsedOrLastAddedItemSet();
320 : }
321 250 : if ( mbSkipIgnorable &&
322 140 : mpNode && mpNode->hasIgnorableChildren( mbSkipUnusedItemSets ) )
323 : {
324 15 : return mpNode->getItemSetOfIgnorableChild( mbSkipUnusedItemSets );
325 : }
326 : }
327 152 : return pReturn;
328 : }
329 :
330 0 : OUString Iterator::getName()
331 : {
332 0 : OUString aString;
333 0 : if( mpNode && mpNode->hasItemSet( false ) )
334 : {
335 0 : aString = StylePool::nameOf( mpNode->getUsedOrLastAddedItemSet() );
336 : }
337 0 : return aString;
338 : }
339 :
340 : }
341 :
342 : /**
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.
345 : */
346 6016 : OUString StylePool::nameOf( SfxItemSet_Pointer_t pSet )
347 : {
348 6016 : return OUString::number( reinterpret_cast<sal_IntPtr>( pSet.get() ), 16 );
349 : }
350 :
351 : /**
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.
356 : */
357 : class StylePoolImpl
358 : {
359 : private:
360 : std::map< const SfxItemSet*, Node > maRoot;
361 : sal_Int32 mnCount;
362 : // #i86923#
363 : SfxItemSet* mpIgnorableItems;
364 : public:
365 : // #i86923#
366 5916 : explicit StylePoolImpl( SfxItemSet* pIgnorableItems = 0 )
367 : : maRoot(),
368 : mnCount(0),
369 : mpIgnorableItems( pIgnorableItems != 0
370 2958 : ? pIgnorableItems->Clone( false )
371 8874 : : 0 )
372 : {
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." );
377 5916 : }
378 :
379 5898 : ~StylePoolImpl()
380 5898 : {
381 5898 : delete mpIgnorableItems;
382 5898 : }
383 :
384 : StylePool::SfxItemSet_Pointer_t insertItemSet( const SfxItemSet& rSet );
385 :
386 : // #i86923#
387 : IStylePoolIteratorAccess* createIterator( bool bSkipUnusedItemSets = false,
388 : bool bSkipIgnorableItems = false );
389 0 : sal_Int32 getCount() const { return mnCount; }
390 : };
391 :
392 208618 : StylePool::SfxItemSet_Pointer_t StylePoolImpl::insertItemSet( const SfxItemSet& rSet )
393 : {
394 208618 : bool bNonPoolable = false;
395 208618 : Node* pCurNode = &maRoot[ rSet.GetParent() ];
396 208618 : SfxItemIter aIter( rSet );
397 208618 : 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 417236 : std::unique_ptr<SfxItemSet> xFoundIgnorableItems;
402 208618 : if ( mpIgnorableItems )
403 : {
404 118308 : xFoundIgnorableItems.reset( new SfxItemSet( *mpIgnorableItems ) );
405 : }
406 1265152 : while( pItem )
407 : {
408 847916 : if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SfxItemPoolFlags::POOLABLE ) )
409 3083 : bNonPoolable = true;
410 2192114 : if ( !xFoundIgnorableItems.get() ||
411 1018240 : ( xFoundIgnorableItems.get() &&
412 509120 : xFoundIgnorableItems->Put( *pItem ) == 0 ) )
413 : {
414 835078 : pCurNode = pCurNode->findChildNode( *pItem );
415 : }
416 847916 : pItem = aIter.NextItem();
417 : }
418 326926 : if ( xFoundIgnorableItems.get() &&
419 118308 : xFoundIgnorableItems->Count() > 0 )
420 : {
421 12098 : SfxItemIter aIgnorableItemsIter( *xFoundIgnorableItems );
422 12098 : pItem = aIgnorableItemsIter.GetCurItem();
423 37034 : while( pItem )
424 : {
425 12838 : if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SfxItemPoolFlags::POOLABLE ) )
426 0 : bNonPoolable = true;
427 12838 : pCurNode = pCurNode->findChildNode( *pItem, true );
428 12838 : pItem = aIgnorableItemsIter.NextItem();
429 12098 : }
430 : }
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 208618 : if( !pCurNode->hasItemSet( false ) )
435 : {
436 28214 : pCurNode->setItemSet( rSet );
437 28214 : bNonPoolable = false; // to avoid a double insertion
438 28214 : ++mnCount;
439 : }
440 : // If rSet contains at least one non poolable item, a new itemset has to be inserted
441 208618 : if( bNonPoolable )
442 155 : pCurNode->setItemSet( rSet );
443 : #ifdef DEBUG
444 : {
445 : sal_Int32 nCheck = -1;
446 : IStylePoolIteratorAccess* pIter = createIterator();
447 : StylePool::SfxItemSet_Pointer_t pTemp;
448 : do
449 : {
450 : ++nCheck;
451 : pTemp = pIter->getNext();
452 : } while( pTemp.get() );
453 : DBG_ASSERT( mnCount == nCheck, "Wrong counting");
454 : delete pIter;
455 : }
456 : #endif
457 417236 : return pCurNode->getItemSet();
458 : }
459 :
460 : // #i86923#
461 152 : IStylePoolIteratorAccess* StylePoolImpl::createIterator( bool bSkipUnusedItemSets,
462 : bool bSkipIgnorableItems )
463 : {
464 152 : return new Iterator( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems );
465 : }
466 : // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
467 :
468 : // #i86923#
469 5916 : StylePool::StylePool( SfxItemSet* pIgnorableItems )
470 5916 : : pImpl( new StylePoolImpl( pIgnorableItems ) )
471 5916 : {}
472 :
473 208618 : StylePool::SfxItemSet_Pointer_t StylePool::insertItemSet( const SfxItemSet& rSet )
474 208618 : { return pImpl->insertItemSet( rSet ); }
475 :
476 : // #i86923#
477 152 : IStylePoolIteratorAccess* StylePool::createIterator( const bool bSkipUnusedItemSets,
478 : const bool bSkipIgnorableItems )
479 : {
480 152 : return pImpl->createIterator( bSkipUnusedItemSets, bSkipIgnorableItems );
481 : }
482 :
483 0 : sal_Int32 StylePool::getCount() const
484 0 : { return pImpl->getCount(); }
485 :
486 5898 : StylePool::~StylePool() { delete pImpl; }
487 :
488 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|