xref: /trunk/main/svl/source/items/stylepool.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 // MARKER(update_precomp.py): autogen include statement, do not remove
28 #include "precompiled_svl.hxx"
29 
30 #ifdef _MSC_VER
31 #pragma hdrstop
32 #endif
33 
34 #include <vector>
35 #include <map>
36 
37 #include <svl/stylepool.hxx>
38 #include <svl/itemiter.hxx>
39 #include <svl/itempool.hxx>
40 
41 
42 using namespace boost;
43 
44 namespace {
45     // A "Node" represents a subset of inserted SfxItemSets
46     // The root node represents the empty set
47     // The other nodes contain a SfxPoolItem and represents an item set which contains their
48     // pool item and the pool items of their parents.
49     class Node
50     {
51         std::vector<Node*> mChildren; // child nodes, create by findChildNode(..)
52         // container of shared pointers of inserted item sets; for non-poolable
53         // items more than one item set is needed
54         std::vector< StylePool::SfxItemSet_Pointer_t > maItemSet;
55         const SfxPoolItem *mpItem;   // my pool item
56         Node *mpUpper;               // if I'm a child node that's my parent node
57         // --> OD 2008-03-07 #i86923#
58         const bool mbIsItemIgnorable;
59         // <--
60     public:
61         // --> OD 2008-03-07 #i86923#
62         Node() // root node Ctor
63             : mChildren(),
64               maItemSet(),
65               mpItem( 0 ),
66               mpUpper( 0 ),
67               mbIsItemIgnorable( false )
68         {}
69         Node( const SfxPoolItem& rItem, Node* pParent, const bool bIgnorable ) // child node Ctor
70             : mChildren(),
71               maItemSet(),
72               mpItem( rItem.Clone() ),
73               mpUpper( pParent ),
74               mbIsItemIgnorable( bIgnorable )
75         {}
76         // <--
77         ~Node();
78         // --> OD 2008-03-11 #i86923#
79         bool hasItemSet( const bool bCheckUsage ) const;
80         // <--
81         // --> OD 2008-04-29 #i87808#
82 //        const StylePool::SfxItemSet_Pointer_t getItemSet() const { return aItemSet[aItemSet.size()-1]; }
83         const StylePool::SfxItemSet_Pointer_t getItemSet() const
84         {
85             return maItemSet.back();
86         }
87         const StylePool::SfxItemSet_Pointer_t getUsedOrLastAddedItemSet() const;
88         // <--
89         void setItemSet( const SfxItemSet& rSet ){ maItemSet.push_back( StylePool::SfxItemSet_Pointer_t( rSet.Clone() ) ); }
90         // --> OD 2008-03-11 #i86923#
91         Node* findChildNode( const SfxPoolItem& rItem,
92                              const bool bIsItemIgnorable = false );
93         Node* nextItemSet( Node* pLast,
94                            const bool bSkipUnusedItemSet,
95                            const bool bSkipIgnorable );
96         // <--
97         const SfxPoolItem& getPoolItem() const { return *mpItem; }
98         // --> OD 2008-03-11 #i86923#
99         bool hasIgnorableChildren( const bool bCheckUsage ) const;
100         const StylePool::SfxItemSet_Pointer_t getItemSetOfIgnorableChild(
101                                         const bool bSkipUnusedItemSets ) const;
102         // <--
103     };
104 
105     // --> OD 2008-04-29 #i87808#
106     const StylePool::SfxItemSet_Pointer_t Node::getUsedOrLastAddedItemSet() const
107     {
108         std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
109 
110         for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
111         {
112             if ( (*aIter).use_count() > 1 )
113             {
114                 return *aIter;
115             }
116         }
117 
118         return maItemSet.back();
119     }
120     // <--
121 
122     // --> OD 2008-05-06 #i86923#
123     bool Node::hasItemSet( const bool bCheckUsage ) const
124     {
125         bool bHasItemSet = false;
126 
127         if ( maItemSet.size() > 0 )
128         {
129             if ( bCheckUsage )
130             {
131                 std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
132 
133                 for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
134                 {
135                     if ( (*aIter).use_count() > 1 )
136                     {
137                         bHasItemSet = true;
138                         break;
139                     }
140                 }
141             }
142             else
143             {
144                 bHasItemSet = true;
145             }
146         }
147         return bHasItemSet;
148     }
149     // <--
150 
151     // --> OD 2008-03-07 #i86923#
152     Node* Node::findChildNode( const SfxPoolItem& rItem,
153                                const bool bIsItemIgnorable )
154     // <--
155     {
156         Node* pNextNode = this;
157         std::vector<Node*>::iterator aIter = mChildren.begin();
158         while( aIter != mChildren.end() )
159         {
160             if( rItem.Which() == (*aIter)->getPoolItem().Which() &&
161                 rItem == (*aIter)->getPoolItem() )
162                 return *aIter;
163             ++aIter;
164         }
165         // --> OD 2008-03-07 #i86923#
166         pNextNode = new Node( rItem, pNextNode, bIsItemIgnorable );
167         // <--
168         mChildren.push_back( pNextNode );
169         return pNextNode;
170     }
171 
172     /* Find the next node which has a SfxItemSet.
173        The input parameter pLast has a sophisticated meaning:
174        downstairs only:
175        pLast == 0 => scan your children and their children
176                      but neither your parents neither your siblings
177        downstairs and upstairs:
178        pLast == this => scan your children, their children,
179                         the children of your parent behind you, and so on
180        partial downstairs and upstairs
181        pLast != 0 && pLast != this => scan your children behind the given children,
182                         the children of your parent behind you and so on.
183 
184        OD 2008-03-11 #i86923#
185        introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
186        and its handling.
187     */
188     Node* Node::nextItemSet( Node* pLast,
189                              const bool bSkipUnusedItemSets,
190                              const bool bSkipIgnorable )
191     {
192         // Searching downstairs
193         std::vector<Node*>::iterator aIter = mChildren.begin();
194         // For pLast == 0 and pLast == this all children are of interest
195         // for another pLast the search starts behind pLast...
196         if( pLast && pLast != this )
197         {
198             aIter = std::find( mChildren.begin(), mChildren.end(), pLast );
199             if( aIter != mChildren.end() )
200                 ++aIter;
201         }
202         Node *pNext = 0;
203         while( aIter != mChildren.end() )
204         {
205             // --> OD 2008-03-11 #i86923#
206             if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable )
207             {
208                 ++aIter;
209                 continue;
210             }
211             // <--
212             pNext = *aIter;
213             // --> OD 2008-03-11 #i86923#
214             if ( pNext->hasItemSet( bSkipUnusedItemSets ) )
215             {
216                 return pNext;
217             }
218             if ( bSkipIgnorable &&
219                  pNext->hasIgnorableChildren( bSkipUnusedItemSets ) )
220             {
221                 return pNext;
222             }
223             pNext = pNext->nextItemSet( 0, bSkipUnusedItemSets, bSkipIgnorable ); // 0 => downstairs only
224             // <--
225             if( pNext )
226                 return pNext;
227             ++aIter;
228         }
229         // Searching upstairs
230         if( pLast && mpUpper )
231         {
232             // --> OD 2008-03-11 #i86923#
233             pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable );
234             // <--
235         }
236         return pNext;
237     }
238 
239     // --> OD 2008-03-11 #i86923#
240     bool Node::hasIgnorableChildren( const bool bCheckUsage ) const
241     {
242         bool bHasIgnorableChildren( false );
243 
244         std::vector<Node*>::const_iterator aIter = mChildren.begin();
245         while( aIter != mChildren.end() && !bHasIgnorableChildren )
246         {
247             Node* pChild = *aIter;
248             if ( pChild->mbIsItemIgnorable )
249             {
250                 bHasIgnorableChildren =
251                     !bCheckUsage ||
252                     ( pChild->hasItemSet( bCheckUsage /* == true */ ) ||
253                       pChild->hasIgnorableChildren( bCheckUsage /* == true */ ) );
254             }
255             ++aIter;
256         }
257 
258         return bHasIgnorableChildren;
259     }
260 
261     const StylePool::SfxItemSet_Pointer_t Node::getItemSetOfIgnorableChild(
262                                         const bool bSkipUnusedItemSets ) const
263     {
264         DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets ),
265                     "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
266 
267         std::vector<Node*>::const_iterator aIter = mChildren.begin();
268         while( aIter != mChildren.end() )
269         {
270             Node* pChild = *aIter;
271             if ( pChild->mbIsItemIgnorable )
272             {
273                 if ( pChild->hasItemSet( bSkipUnusedItemSets ) )
274                 {
275                     return pChild->getUsedOrLastAddedItemSet();
276                 }
277                 else
278                 {
279                     pChild = pChild->nextItemSet( 0, bSkipUnusedItemSets, false );
280                     if ( pChild )
281                     {
282                         return pChild->getUsedOrLastAddedItemSet();
283                     }
284                 }
285             }
286             ++aIter;
287         }
288 
289         StylePool::SfxItemSet_Pointer_t pReturn;
290         return pReturn;
291     }
292     // <--
293 
294     Node::~Node()
295     {
296         std::vector<Node*>::iterator aIter = mChildren.begin();
297         while( aIter != mChildren.end() )
298         {
299             delete *aIter;
300             ++aIter;
301         }
302         delete mpItem;
303     }
304 
305     class Iterator : public IStylePoolIteratorAccess
306     {
307         std::map< const SfxItemSet*, Node >& mrRoot;
308         std::map< const SfxItemSet*, Node >::iterator mpCurrNode;
309         Node* mpNode;
310         const bool mbSkipUnusedItemSets;
311         const bool mbSkipIgnorable;
312     public:
313         // --> OD 2008-03-07 #i86923#
314         Iterator( std::map< const SfxItemSet*, Node >& rR,
315                   const bool bSkipUnusedItemSets,
316                   const bool bSkipIgnorable )
317             : mrRoot( rR ),
318               mpCurrNode( rR.begin() ),
319               mpNode(0),
320               mbSkipUnusedItemSets( bSkipUnusedItemSets ),
321               mbSkipIgnorable( bSkipIgnorable )
322         {}
323         // <--
324         virtual StylePool::SfxItemSet_Pointer_t getNext();
325         virtual ::rtl::OUString getName();
326     };
327 
328     StylePool::SfxItemSet_Pointer_t Iterator::getNext()
329     {
330         StylePool::SfxItemSet_Pointer_t pReturn;
331         while( mpNode || mpCurrNode != mrRoot.end() )
332         {
333             if( !mpNode )
334             {
335                 mpNode = &mpCurrNode->second;
336                 ++mpCurrNode;
337                 // --> OD 2008-03-11 #i86923#
338                 if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) )
339                 {
340                     // --> OD 2008-04-30 #i87808#
341 //                    return pNode->getItemSet();
342                     return mpNode->getUsedOrLastAddedItemSet();
343                     // <--
344                 }
345                 // <--
346             }
347             // --> OD 2008-03-11 #i86923#
348             mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable );
349             if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) )
350             {
351                 // --> OD 2008-04-30 #i87808#
352 //                return pNode->getItemSet();
353                 return mpNode->getUsedOrLastAddedItemSet();
354                 // <--
355             }
356             if ( mbSkipIgnorable &&
357                  mpNode && mpNode->hasIgnorableChildren( mbSkipUnusedItemSets ) )
358             {
359                 return mpNode->getItemSetOfIgnorableChild( mbSkipUnusedItemSets );
360             }
361             // <--
362         }
363         return pReturn;
364     }
365 
366     ::rtl::OUString Iterator::getName()
367     {
368         ::rtl::OUString aString;
369         if( mpNode && mpNode->hasItemSet( false ) )
370         {
371             // --> OD 2008-04-30 #i87808#
372 //            aString = StylePool::nameOf( pNode->getItemSet() );
373             aString = StylePool::nameOf( mpNode->getUsedOrLastAddedItemSet() );
374             // <--
375         }
376         return aString;
377     }
378 
379 }
380 
381 /* This static method creates a unique name from a shared pointer to a SfxItemSet
382    The name is the memory address of the SfxItemSet itself. */
383 
384 ::rtl::OUString StylePool::nameOf( SfxItemSet_Pointer_t pSet )
385 {
386     return ::rtl::OUString::valueOf( reinterpret_cast<sal_IntPtr>( pSet.get() ), 16 );
387 }
388 
389 // class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
390 // The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
391 // to a copy of the SfxItemSet.
392 // The aRoot-Node represents an empty SfxItemSet.
393 
394 class StylePoolImpl
395 {
396 private:
397     std::map< const SfxItemSet*, Node > maRoot;
398     sal_Int32 mnCount;
399     // --> OD 2008-03-07 #i86923#
400     SfxItemSet* mpIgnorableItems;
401     // <--
402 public:
403     // --> OD 2008-03-07 #i86923#
404     explicit StylePoolImpl( SfxItemSet* pIgnorableItems = 0 )
405         : maRoot(),
406           mnCount(0),
407           mpIgnorableItems( pIgnorableItems != 0
408                             ? pIgnorableItems->Clone( sal_False )
409                             : 0 )
410     {
411         DBG_ASSERT( !pIgnorableItems || !pIgnorableItems->Count(),
412                     "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
413         DBG_ASSERT( !mpIgnorableItems || !mpIgnorableItems->Count(),
414                     "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty. Please inform OD." );
415     }
416 
417     ~StylePoolImpl()
418     {
419         delete mpIgnorableItems;
420     }
421     // <--
422 
423     StylePool::SfxItemSet_Pointer_t insertItemSet( const SfxItemSet& rSet );
424 
425     // --> OD 2008-03-07 #i86923#
426     IStylePoolIteratorAccess* createIterator( bool bSkipUnusedItemSets = false,
427                                               bool bSkipIgnorableItems = false );
428     // <--
429     sal_Int32 getCount() const { return mnCount; }
430 };
431 
432 StylePool::SfxItemSet_Pointer_t StylePoolImpl::insertItemSet( const SfxItemSet& rSet )
433 {
434     bool bNonPoolable = false;
435     Node* pCurNode = &maRoot[ rSet.GetParent() ];
436     SfxItemIter aIter( rSet );
437     const SfxPoolItem* pItem = aIter.GetCurItem();
438     // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
439     // a complete empty SfxItemSet would stay at the root node.
440     // --> OD 2008-03-07 #i86923#
441     // insert ignorable items to the tree leaves.
442     std::auto_ptr<SfxItemSet> pFoundIgnorableItems;
443     if ( mpIgnorableItems )
444     {
445         pFoundIgnorableItems.reset( new SfxItemSet( *mpIgnorableItems ) );
446     }
447     while( pItem )
448     {
449         if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SFX_ITEM_POOLABLE ) )
450             bNonPoolable = true;
451         if ( !pFoundIgnorableItems.get() ||
452              ( pFoundIgnorableItems.get() &&
453                pFoundIgnorableItems->Put( *pItem ) == 0 ) )
454         {
455             pCurNode = pCurNode->findChildNode( *pItem );
456         }
457 		pItem = aIter.NextItem();
458     }
459     if ( pFoundIgnorableItems.get() &&
460          pFoundIgnorableItems->Count() > 0 )
461     {
462         SfxItemIter aIgnorableItemsIter( *pFoundIgnorableItems );
463         pItem = aIgnorableItemsIter.GetCurItem();
464         while( pItem )
465         {
466             if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SFX_ITEM_POOLABLE ) )
467                 bNonPoolable = true;
468             pCurNode = pCurNode->findChildNode( *pItem, true );
469             pItem = aIgnorableItemsIter.NextItem();
470         }
471     }
472     // <--
473     // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
474     // of inserted itemsets.
475     // These nodes could have but does not need to have a shared_ptr to a item set.
476     if( !pCurNode->hasItemSet( false ) )
477     {
478         pCurNode->setItemSet( rSet );
479         bNonPoolable = false; // to avoid a double insertion
480         ++mnCount;
481     }
482     // If rSet contains at least one non poolable item, a new itemset has to be inserted
483     if( bNonPoolable )
484         pCurNode->setItemSet( rSet );
485 #ifdef DEBUG
486     {
487         sal_Int32 nCheck = -1;
488         sal_Int32 nNo = -1;
489         IStylePoolIteratorAccess* pIter = createIterator();
490         StylePool::SfxItemSet_Pointer_t pTemp;
491         do
492         {
493             ++nCheck;
494             pTemp = pIter->getNext();
495             if( pCurNode->hasItemSet( false ) && pTemp.get() == pCurNode->getItemSet().get() )
496             {
497                 ::rtl::OUString aStr = StylePool::nameOf( pTemp );
498                 nNo = nCheck;
499             }
500         } while( pTemp.get() );
501         DBG_ASSERT( mnCount == nCheck, "Wrong counting");
502         delete pIter;
503     }
504 #endif
505     return pCurNode->getItemSet();
506 }
507 
508 // --> OD 2008-03-07 #i86923#
509 IStylePoolIteratorAccess* StylePoolImpl::createIterator( bool bSkipUnusedItemSets,
510                                                          bool bSkipIgnorableItems )
511 {
512     return new Iterator( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems );
513 }
514 // <--
515 
516 // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
517 
518 // --> OD 2008-03-07 #i86923#
519 StylePool::StylePool( SfxItemSet* pIgnorableItems )
520     : pImpl( new StylePoolImpl( pIgnorableItems ) )
521 {}
522 // <--
523 
524 StylePool::SfxItemSet_Pointer_t StylePool::insertItemSet( const SfxItemSet& rSet )
525 { return pImpl->insertItemSet( rSet ); }
526 
527 // --> OD 2008-03-11 #i86923#
528 IStylePoolIteratorAccess* StylePool::createIterator( const bool bSkipUnusedItemSets,
529                                                      const bool bSkipIgnorableItems )
530 {
531     return pImpl->createIterator( bSkipUnusedItemSets, bSkipIgnorableItems );
532 }
533 // <--
534 
535 sal_Int32 StylePool::getCount() const
536 { return pImpl->getCount(); }
537 
538 StylePool::~StylePool() { delete pImpl; }
539 
540 // End of class StylePool
541 
542