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