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 : #ifdef _MSC_VER
21 : #pragma hdrstop
22 : #endif
23 :
24 : #include <vector>
25 : #include <map>
26 :
27 : #include <svl/stylepool.hxx>
28 : #include <svl/itemiter.hxx>
29 : #include <svl/itempool.hxx>
30 : #include <boost/scoped_ptr.hpp>
31 :
32 : using namespace boost;
33 :
34 : namespace {
35 : /** A "Node" represents a subset of inserted SfxItemSets
36 : * The root node represents the empty set
37 : * The other nodes contain a SfxPoolItem and represents an item set which contains their
38 : * pool item and the pool items of their parents.
39 : */
40 : class Node
41 : {
42 : std::vector<Node*> mChildren; // child nodes, create by findChildNode(..)
43 : // container of shared pointers of inserted item sets; for non-poolable
44 : // items more than one item set is needed
45 : std::vector< StylePool::SfxItemSet_Pointer_t > maItemSet;
46 : const SfxPoolItem *mpItem; // my pool item
47 : Node *mpUpper; // if I'm a child node that's my parent node
48 : // #i86923#
49 : const bool mbIsItemIgnorable;
50 : public:
51 : // #i86923#
52 8932 : Node() // root node Ctor
53 : : mChildren(),
54 : maItemSet(),
55 : mpItem( 0 ),
56 : mpUpper( 0 ),
57 8932 : mbIsItemIgnorable( false )
58 8932 : {}
59 144486 : Node( const SfxPoolItem& rItem, Node* pParent, const bool bIgnorable ) // child node Ctor
60 : : mChildren(),
61 : maItemSet(),
62 144486 : mpItem( rItem.Clone() ),
63 : mpUpper( pParent ),
64 288972 : mbIsItemIgnorable( bIgnorable )
65 144486 : {}
66 : ~Node();
67 : // #i86923#
68 : bool hasItemSet( const bool bCheckUsage ) const;
69 : // #i87808#
70 373312 : const StylePool::SfxItemSet_Pointer_t getItemSet() const
71 : {
72 373312 : return maItemSet.back();
73 : }
74 : const StylePool::SfxItemSet_Pointer_t getUsedOrLastAddedItemSet() const;
75 47778 : void setItemSet( const SfxItemSet& rSet ){ maItemSet.push_back( StylePool::SfxItemSet_Pointer_t( rSet.Clone() ) ); }
76 : // #i86923#
77 : Node* findChildNode( const SfxPoolItem& rItem,
78 : const bool bIsItemIgnorable = false );
79 : Node* nextItemSet( Node* pLast,
80 : const bool bSkipUnusedItemSet,
81 : const bool bSkipIgnorable );
82 5143422 : const SfxPoolItem& getPoolItem() const { return *mpItem; }
83 : // #i86923#
84 : bool hasIgnorableChildren( const bool bCheckUsage ) const;
85 : const StylePool::SfxItemSet_Pointer_t getItemSetOfIgnorableChild(
86 : const bool bSkipUnusedItemSets ) const;
87 : };
88 :
89 : // #i87808#
90 88 : const StylePool::SfxItemSet_Pointer_t Node::getUsedOrLastAddedItemSet() const
91 : {
92 88 : std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
93 :
94 88 : for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
95 : {
96 88 : if ( (*aIter).use_count() > 1 )
97 : {
98 88 : return *aIter;
99 : }
100 : }
101 :
102 0 : return maItemSet.back();
103 : }
104 :
105 : // #i86923#
106 373942 : bool Node::hasItemSet( const bool bCheckUsage ) const
107 : {
108 373942 : bool bHasItemSet = false;
109 :
110 373942 : if ( !maItemSet.empty())
111 : {
112 325902 : if ( bCheckUsage )
113 : {
114 228 : std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
115 :
116 282 : for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
117 : {
118 230 : if ( (*aIter).use_count() > 1 )
119 : {
120 176 : bHasItemSet = true;
121 176 : break;
122 : }
123 : }
124 : }
125 : else
126 : {
127 325674 : bHasItemSet = true;
128 : }
129 : }
130 373942 : return bHasItemSet;
131 : }
132 :
133 : // #i86923#
134 1505902 : Node* Node::findChildNode( const SfxPoolItem& rItem,
135 : const bool bIsItemIgnorable )
136 : {
137 1505902 : Node* pNextNode = this;
138 1505902 : std::vector<Node*>::iterator aIter = mChildren.begin();
139 5142076 : while( aIter != mChildren.end() )
140 : {
141 5143422 : if( rItem.Which() == (*aIter)->getPoolItem().Which() &&
142 1651734 : rItem == (*aIter)->getPoolItem() )
143 1361416 : return *aIter;
144 2130272 : ++aIter;
145 : }
146 : // #i86923#
147 144486 : pNextNode = new Node( rItem, pNextNode, bIsItemIgnorable );
148 144486 : mChildren.push_back( pNextNode );
149 144486 : return pNextNode;
150 : }
151 :
152 : /**
153 : * Find the next node which has a SfxItemSet.
154 : * The input parameter pLast has a sophisticated meaning:
155 : * downstairs only:
156 : * pLast == 0 => scan your children and their children
157 : * but neither your parents neither your siblings
158 : * downstairs and upstairs:
159 : * pLast == this => scan your children, their children,
160 : * the children of your parent behind you, and so on
161 : * partial downstairs and upstairs
162 : * pLast != 0 && pLast != this => scan your children behind the given children,
163 : * the children of your parent behind you and so on.
164 : *
165 : * OD 2008-03-11 #i86923#
166 : * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
167 : * and its handling.
168 : */
169 854 : Node* Node::nextItemSet( Node* pLast,
170 : const bool bSkipUnusedItemSets,
171 : const bool bSkipIgnorable )
172 : {
173 : // Searching downstairs
174 854 : std::vector<Node*>::iterator aIter = mChildren.begin();
175 : // For pLast == 0 and pLast == this all children are of interest
176 : // for another pLast the search starts behind pLast...
177 854 : if( pLast && pLast != this )
178 : {
179 312 : aIter = std::find( mChildren.begin(), mChildren.end(), pLast );
180 312 : if( aIter != mChildren.end() )
181 312 : ++aIter;
182 : }
183 854 : Node *pNext = 0;
184 1852 : while( aIter != mChildren.end() )
185 : {
186 : // #i86923#
187 456 : if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable )
188 : {
189 0 : ++aIter;
190 0 : continue;
191 : }
192 456 : pNext = *aIter;
193 : // #i86923#
194 456 : if ( pNext->hasItemSet( bSkipUnusedItemSets ) )
195 : {
196 88 : return pNext;
197 : }
198 736 : if ( bSkipIgnorable &&
199 368 : pNext->hasIgnorableChildren( bSkipUnusedItemSets ) )
200 : {
201 0 : return pNext;
202 : }
203 368 : pNext = pNext->nextItemSet( 0, bSkipUnusedItemSets, bSkipIgnorable ); // 0 => downstairs only
204 368 : if( pNext )
205 224 : return pNext;
206 144 : ++aIter;
207 : }
208 : // Searching upstairs
209 542 : if( pLast && mpUpper )
210 : {
211 : // #i86923#
212 312 : pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable );
213 : }
214 542 : return pNext;
215 : }
216 :
217 : // #i86923#
218 368 : bool Node::hasIgnorableChildren( const bool bCheckUsage ) const
219 : {
220 368 : bool bHasIgnorableChildren( false );
221 :
222 368 : std::vector<Node*>::const_iterator aIter = mChildren.begin();
223 1066 : while( aIter != mChildren.end() && !bHasIgnorableChildren )
224 : {
225 330 : Node* pChild = *aIter;
226 330 : if ( pChild->mbIsItemIgnorable )
227 : {
228 : bHasIgnorableChildren =
229 0 : !bCheckUsage ||
230 0 : ( pChild->hasItemSet( bCheckUsage /* == true */ ) ||
231 0 : pChild->hasIgnorableChildren( bCheckUsage /* == true */ ) );
232 : }
233 330 : ++aIter;
234 : }
235 :
236 368 : return bHasIgnorableChildren;
237 : }
238 :
239 0 : const StylePool::SfxItemSet_Pointer_t Node::getItemSetOfIgnorableChild(
240 : const bool bSkipUnusedItemSets ) const
241 : {
242 : DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets ),
243 : "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
244 :
245 0 : std::vector<Node*>::const_iterator aIter = mChildren.begin();
246 0 : while( aIter != mChildren.end() )
247 : {
248 0 : Node* pChild = *aIter;
249 0 : if ( pChild->mbIsItemIgnorable )
250 : {
251 0 : if ( pChild->hasItemSet( bSkipUnusedItemSets ) )
252 : {
253 0 : return pChild->getUsedOrLastAddedItemSet();
254 : }
255 : else
256 : {
257 0 : pChild = pChild->nextItemSet( 0, bSkipUnusedItemSets, false );
258 0 : if ( pChild )
259 : {
260 0 : return pChild->getUsedOrLastAddedItemSet();
261 : }
262 : }
263 : }
264 0 : ++aIter;
265 : }
266 :
267 0 : StylePool::SfxItemSet_Pointer_t pReturn;
268 0 : return pReturn;
269 : }
270 :
271 306472 : Node::~Node()
272 : {
273 153236 : std::vector<Node*>::iterator aIter = mChildren.begin();
274 450791 : while( aIter != mChildren.end() )
275 : {
276 144319 : delete *aIter;
277 144319 : ++aIter;
278 : }
279 153236 : delete mpItem;
280 153236 : }
281 :
282 264 : class Iterator : public IStylePoolIteratorAccess
283 : {
284 : std::map< const SfxItemSet*, Node >& mrRoot;
285 : std::map< const SfxItemSet*, Node >::iterator mpCurrNode;
286 : Node* mpNode;
287 : const bool mbSkipUnusedItemSets;
288 : const bool mbSkipIgnorable;
289 : public:
290 : // #i86923#
291 132 : Iterator( std::map< const SfxItemSet*, Node >& rR,
292 : const bool bSkipUnusedItemSets,
293 : const bool bSkipIgnorable )
294 : : mrRoot( rR ),
295 : mpCurrNode( rR.begin() ),
296 : mpNode(0),
297 : mbSkipUnusedItemSets( bSkipUnusedItemSets ),
298 132 : mbSkipIgnorable( bSkipIgnorable )
299 132 : {}
300 : virtual StylePool::SfxItemSet_Pointer_t getNext() SAL_OVERRIDE;
301 : virtual OUString getName() SAL_OVERRIDE;
302 : };
303 :
304 220 : StylePool::SfxItemSet_Pointer_t Iterator::getNext()
305 : {
306 220 : StylePool::SfxItemSet_Pointer_t pReturn;
307 220 : while( mpNode || mpCurrNode != mrRoot.end() )
308 : {
309 174 : if( !mpNode )
310 : {
311 86 : mpNode = &mpCurrNode->second;
312 86 : ++mpCurrNode;
313 : // #i86923#
314 86 : if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) )
315 : {
316 : // #i87808#
317 0 : return mpNode->getUsedOrLastAddedItemSet();
318 : }
319 : }
320 : // #i86923#
321 174 : mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable );
322 174 : if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) )
323 : {
324 : // #i87808#
325 88 : return mpNode->getUsedOrLastAddedItemSet();
326 : }
327 172 : if ( mbSkipIgnorable &&
328 86 : mpNode && mpNode->hasIgnorableChildren( mbSkipUnusedItemSets ) )
329 : {
330 0 : return mpNode->getItemSetOfIgnorableChild( mbSkipUnusedItemSets );
331 : }
332 : }
333 132 : return pReturn;
334 : }
335 :
336 0 : OUString Iterator::getName()
337 : {
338 0 : OUString aString;
339 0 : if( mpNode && mpNode->hasItemSet( false ) )
340 : {
341 0 : aString = StylePool::nameOf( mpNode->getUsedOrLastAddedItemSet() );
342 : }
343 0 : return aString;
344 : }
345 :
346 : }
347 :
348 : /**
349 : * This static method creates a unique name from a shared pointer to a SfxItemSet
350 : * The name is the memory address of the SfxItemSet itself.
351 : */
352 5356 : OUString StylePool::nameOf( SfxItemSet_Pointer_t pSet )
353 : {
354 5356 : return OUString::number( reinterpret_cast<sal_IntPtr>( pSet.get() ), 16 );
355 : }
356 :
357 : /**
358 : * class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
359 : * The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
360 : * to a copy of the SfxItemSet.
361 : * The aRoot-Node represents an empty SfxItemSet.
362 : */
363 : class StylePoolImpl
364 : {
365 : private:
366 : std::map< const SfxItemSet*, Node > maRoot;
367 : sal_Int32 mnCount;
368 : // #i86923#
369 : SfxItemSet* mpIgnorableItems;
370 : public:
371 : // #i86923#
372 10104 : explicit StylePoolImpl( SfxItemSet* pIgnorableItems = 0 )
373 : : maRoot(),
374 : mnCount(0),
375 : mpIgnorableItems( pIgnorableItems != 0
376 5052 : ? pIgnorableItems->Clone( false )
377 15156 : : 0 )
378 : {
379 : DBG_ASSERT( !pIgnorableItems || !pIgnorableItems->Count(),
380 : "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
381 : DBG_ASSERT( !mpIgnorableItems || !mpIgnorableItems->Count(),
382 : "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty. Please inform OD." );
383 10104 : }
384 :
385 10090 : ~StylePoolImpl()
386 10090 : {
387 10090 : delete mpIgnorableItems;
388 10090 : }
389 :
390 : StylePool::SfxItemSet_Pointer_t insertItemSet( const SfxItemSet& rSet );
391 :
392 : // #i86923#
393 : IStylePoolIteratorAccess* createIterator( bool bSkipUnusedItemSets = false,
394 : bool bSkipIgnorableItems = false );
395 0 : sal_Int32 getCount() const { return mnCount; }
396 : };
397 :
398 373312 : StylePool::SfxItemSet_Pointer_t StylePoolImpl::insertItemSet( const SfxItemSet& rSet )
399 : {
400 373312 : bool bNonPoolable = false;
401 373312 : Node* pCurNode = &maRoot[ rSet.GetParent() ];
402 373312 : SfxItemIter aIter( rSet );
403 373312 : const SfxPoolItem* pItem = aIter.GetCurItem();
404 : // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
405 : // a complete empty SfxItemSet would stay at the root node.
406 : // #i86923# insert ignorable items to the tree leaves.
407 746624 : boost::scoped_ptr<SfxItemSet> pFoundIgnorableItems;
408 373312 : if ( mpIgnorableItems )
409 : {
410 197932 : pFoundIgnorableItems.reset( new SfxItemSet( *mpIgnorableItems ) );
411 : }
412 2252526 : while( pItem )
413 : {
414 1505902 : if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SFX_ITEM_POOLABLE ) )
415 5274 : bNonPoolable = true;
416 3846194 : if ( !pFoundIgnorableItems.get() ||
417 1705548 : ( pFoundIgnorableItems.get() &&
418 852774 : pFoundIgnorableItems->Put( *pItem ) == 0 ) )
419 : {
420 1487518 : pCurNode = pCurNode->findChildNode( *pItem );
421 : }
422 1505902 : pItem = aIter.NextItem();
423 : }
424 571244 : if ( pFoundIgnorableItems.get() &&
425 197932 : pFoundIgnorableItems->Count() > 0 )
426 : {
427 17846 : SfxItemIter aIgnorableItemsIter( *pFoundIgnorableItems );
428 17846 : pItem = aIgnorableItemsIter.GetCurItem();
429 54076 : while( pItem )
430 : {
431 18384 : if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SFX_ITEM_POOLABLE ) )
432 0 : bNonPoolable = true;
433 18384 : pCurNode = pCurNode->findChildNode( *pItem, true );
434 18384 : pItem = aIgnorableItemsIter.NextItem();
435 17846 : }
436 : }
437 : // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
438 : // of inserted itemsets.
439 : // These nodes could have but does not need to have a shared_ptr to a item set.
440 373312 : if( !pCurNode->hasItemSet( false ) )
441 : {
442 47638 : pCurNode->setItemSet( rSet );
443 47638 : bNonPoolable = false; // to avoid a double insertion
444 47638 : ++mnCount;
445 : }
446 : // If rSet contains at least one non poolable item, a new itemset has to be inserted
447 373312 : if( bNonPoolable )
448 140 : pCurNode->setItemSet( rSet );
449 : #ifdef DEBUG
450 : {
451 : sal_Int32 nCheck = -1;
452 : IStylePoolIteratorAccess* pIter = createIterator();
453 : StylePool::SfxItemSet_Pointer_t pTemp;
454 : do
455 : {
456 : ++nCheck;
457 : pTemp = pIter->getNext();
458 : } while( pTemp.get() );
459 : DBG_ASSERT( mnCount == nCheck, "Wrong counting");
460 : delete pIter;
461 : }
462 : #endif
463 746624 : return pCurNode->getItemSet();
464 : }
465 :
466 : // #i86923#
467 132 : IStylePoolIteratorAccess* StylePoolImpl::createIterator( bool bSkipUnusedItemSets,
468 : bool bSkipIgnorableItems )
469 : {
470 132 : return new Iterator( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems );
471 : }
472 : // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
473 :
474 : // #i86923#
475 10104 : StylePool::StylePool( SfxItemSet* pIgnorableItems )
476 10104 : : pImpl( new StylePoolImpl( pIgnorableItems ) )
477 10104 : {}
478 :
479 373312 : StylePool::SfxItemSet_Pointer_t StylePool::insertItemSet( const SfxItemSet& rSet )
480 373312 : { return pImpl->insertItemSet( rSet ); }
481 :
482 : // #i86923#
483 132 : IStylePoolIteratorAccess* StylePool::createIterator( const bool bSkipUnusedItemSets,
484 : const bool bSkipIgnorableItems )
485 : {
486 132 : return pImpl->createIterator( bSkipUnusedItemSets, bSkipIgnorableItems );
487 : }
488 :
489 0 : sal_Int32 StylePool::getCount() const
490 0 : { return pImpl->getCount(); }
491 :
492 10090 : StylePool::~StylePool() { delete pImpl; }
493 :
494 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|