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<std::unique_ptr<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< std::shared_ptr<SfxItemSet> > maItemSet; 40 std::unique_ptr<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( nullptr ), 50 mpUpper( nullptr ), 51 mbIsItemIgnorable( false ) 52 {} 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 ) 59 {} 60 // #i86923# 61 bool hasItemSet( const bool bCheckUsage ) const; 62 // #i87808# 63 std::shared_ptr<SfxItemSet> const & getItemSet() const 64 { 65 return maItemSet.back(); 66 } 67 std::shared_ptr<SfxItemSet> const & getUsedOrLastAddedItemSet() const; 68 void setItemSet( const SfxItemSet& rSet ){ maItemSet.push_back( std::shared_ptr<SfxItemSet>( rSet.Clone() ) ); } 69 // #i86923# 70 Node* findChildNode( const SfxPoolItem& rItem, 71 const bool bIsItemIgnorable ); 72 Node* nextItemSet( Node const * pLast, 73 const bool bSkipUnusedItemSet, 74 const bool bSkipIgnorable ); 75 // #i86923# 76 bool hasIgnorableChildren( const bool bCheckUsage ) const; 77 const std::shared_ptr<SfxItemSet> getItemSetOfIgnorableChild( 78 const bool bSkipUnusedItemSets ) const; 79 }; 80 81 // #i87808# 82 std::shared_ptr<SfxItemSet> const & Node::getUsedOrLastAddedItemSet() const 83 { 84 std::vector< std::shared_ptr<SfxItemSet> >::const_reverse_iterator aIter; 85 86 for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter ) 87 { 88 if ( (*aIter).use_count() > 1 ) 89 { 90 return *aIter; 91 } 92 } 93 94 return maItemSet.back(); 95 } 96 97 // #i86923# 98 bool Node::hasItemSet( const bool bCheckUsage ) const 99 { 100 bool bHasItemSet = false; 101 102 if ( !maItemSet.empty()) 103 { 104 if ( bCheckUsage ) 105 { 106 std::vector< std::shared_ptr<SfxItemSet> >::const_reverse_iterator aIter; 107 108 for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter ) 109 { 110 if ( (*aIter).use_count() > 1 ) 111 { 112 bHasItemSet = true; 113 break; 114 } 115 } 116 } 117 else 118 { 119 bHasItemSet = true; 120 } 121 } 122 return bHasItemSet; 123 } 124 125 // #i86923# 126 Node* Node::findChildNode( const SfxPoolItem& rItem, 127 const bool bIsItemIgnorable ) 128 { 129 for( auto const & rChild : mChildren ) 130 { 131 if( rItem.Which() == rChild->mpItem->Which() && 132 rItem == *rChild->mpItem ) 133 return rChild.get(); 134 } 135 // #i86923# 136 auto pNextNode = new Node( rItem, this, bIsItemIgnorable ); 137 mChildren.emplace_back( pNextNode ); 138 return pNextNode; 139 } 140 141 /** 142 * Find the next node which has a SfxItemSet. 143 * The input parameter pLast has a sophisticated meaning: 144 * downstairs only: 145 * pLast == 0 => scan your children and their children 146 * but neither your parents neither your siblings 147 * downstairs and upstairs: 148 * pLast == this => scan your children, their children, 149 * the children of your parent behind you, and so on 150 * partial downstairs and upstairs 151 * pLast != 0 && pLast != this => scan your children behind the given children, 152 * the children of your parent behind you and so on. 153 * 154 * OD 2008-03-11 #i86923# 155 * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable> 156 * and its handling. 157 */ 158 Node* Node::nextItemSet( Node const * pLast, 159 const bool bSkipUnusedItemSets, 160 const bool bSkipIgnorable ) 161 { 162 // Searching downstairs 163 auto aIter = mChildren.begin(); 164 // For pLast == 0 and pLast == this all children are of interest 165 // for another pLast the search starts behind pLast... 166 if( pLast && pLast != this ) 167 { 168 aIter = std::find_if( mChildren.begin(), mChildren.end(), 169 [&] (std::unique_ptr<Node> const &p) { return p.get() == pLast; }); 170 if( aIter != mChildren.end() ) 171 ++aIter; 172 } 173 Node *pNext = nullptr; 174 while( aIter != mChildren.end() ) 175 { 176 // #i86923# 177 if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable ) 178 { 179 ++aIter; 180 continue; 181 } 182 pNext = aIter->get(); 183 // #i86923# 184 if ( pNext->hasItemSet( bSkipUnusedItemSets ) ) 185 { 186 return pNext; 187 } 188 if ( bSkipIgnorable && 189 pNext->hasIgnorableChildren( bSkipUnusedItemSets ) ) 190 { 191 return pNext; 192 } 193 pNext = pNext->nextItemSet( nullptr, bSkipUnusedItemSets, bSkipIgnorable ); // 0 => downstairs only 194 if( pNext ) 195 return pNext; 196 ++aIter; 197 } 198 // Searching upstairs 199 if( pLast && mpUpper ) 200 { 201 // #i86923# 202 pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable ); 203 } 204 return pNext; 205 } 206 207 // #i86923# 208 bool Node::hasIgnorableChildren( const bool bCheckUsage ) const 209 { 210 bool bHasIgnorableChildren( false ); 211 212 auto aIter = mChildren.begin(); 213 while( aIter != mChildren.end() && !bHasIgnorableChildren ) 214 { 215 Node* pChild = aIter->get(); 216 if ( pChild->mbIsItemIgnorable ) 217 { 218 bHasIgnorableChildren = 219 !bCheckUsage || 220 ( pChild->hasItemSet( bCheckUsage /* == true */ ) || 221 pChild->hasIgnorableChildren( bCheckUsage /* == true */ ) ); 222 } 223 ++aIter; 224 } 225 226 return bHasIgnorableChildren; 227 } 228 229 const std::shared_ptr<SfxItemSet> Node::getItemSetOfIgnorableChild( 230 const bool bSkipUnusedItemSets ) const 231 { 232 DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets ), 233 "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" ); 234 235 auto aIter = mChildren.begin(); 236 while( aIter != mChildren.end() ) 237 { 238 Node* pChild = aIter->get(); 239 if ( pChild->mbIsItemIgnorable ) 240 { 241 if ( pChild->hasItemSet( bSkipUnusedItemSets ) ) 242 { 243 return pChild->getUsedOrLastAddedItemSet(); 244 } 245 else 246 { 247 pChild = pChild->nextItemSet( nullptr, bSkipUnusedItemSets, false ); 248 if ( pChild ) 249 { 250 return pChild->getUsedOrLastAddedItemSet(); 251 } 252 } 253 } 254 ++aIter; 255 } 256 257 std::shared_ptr<SfxItemSet> pReturn; 258 return pReturn; 259 } 260 261 class Iterator : public IStylePoolIteratorAccess 262 { 263 std::map< const SfxItemSet*, Node >& mrRoot; 264 std::map< const SfxItemSet*, Node >::iterator mpCurrNode; 265 Node* mpNode; 266 const bool mbSkipUnusedItemSets; 267 const bool mbSkipIgnorable; 268 public: 269 // #i86923# 270 Iterator( std::map< const SfxItemSet*, Node >& rR, 271 const bool bSkipUnusedItemSets, 272 const bool bSkipIgnorable ) 273 : mrRoot( rR ), 274 mpCurrNode( rR.begin() ), 275 mpNode(nullptr), 276 mbSkipUnusedItemSets( bSkipUnusedItemSets ), 277 mbSkipIgnorable( bSkipIgnorable ) 278 {} 279 virtual std::shared_ptr<SfxItemSet> getNext() override; 280 }; 281 282 std::shared_ptr<SfxItemSet> Iterator::getNext() 283 { 284 std::shared_ptr<SfxItemSet> pReturn; 285 while( mpNode || mpCurrNode != mrRoot.end() ) 286 { 287 if( !mpNode ) 288 { 289 mpNode = &mpCurrNode->second; 290 ++mpCurrNode; 291 // #i86923# 292 if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) ) 293 { 294 // #i87808# 295 return mpNode->getUsedOrLastAddedItemSet(); 296 } 297 } 298 // #i86923# 299 mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable ); 300 if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) ) 301 { 302 // #i87808# 303 return mpNode->getUsedOrLastAddedItemSet(); 304 } 305 if ( mbSkipIgnorable && 306 mpNode && mpNode->hasIgnorableChildren( mbSkipUnusedItemSets ) ) 307 { 308 return mpNode->getItemSetOfIgnorableChild( mbSkipUnusedItemSets ); 309 } 310 } 311 return pReturn; 312 } 313 314 } 315 316 /** 317 * This static method creates a unique name from a shared pointer to a SfxItemSet 318 * The name is the memory address of the SfxItemSet itself. 319 */ 320 OUString StylePool::nameOf( const std::shared_ptr<SfxItemSet>& pSet ) 321 { 322 return OUString::number( reinterpret_cast<sal_IntPtr>( pSet.get() ), 16 ); 323 } 324 325 /** 326 * class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet. 327 * The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr 328 * to a copy of the SfxItemSet. 329 * The aRoot-Node represents an empty SfxItemSet. 330 */ 331 class StylePoolImpl 332 { 333 private: 334 std::map< const SfxItemSet*, Node > maRoot; 335 // #i86923# 336 std::unique_ptr<SfxItemSet> mpIgnorableItems; 337 public: 338 // #i86923# 339 explicit StylePoolImpl( SfxItemSet const * pIgnorableItems ) 340 : maRoot(), 341 mpIgnorableItems( pIgnorableItems != nullptr 342 ? pIgnorableItems->Clone( false ) 343 : nullptr ) 344 { 345 DBG_ASSERT( !pIgnorableItems || !pIgnorableItems->Count(), 346 "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." ); 347 DBG_ASSERT( !mpIgnorableItems || !mpIgnorableItems->Count(), 348 "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty." ); 349 } 350 351 std::shared_ptr<SfxItemSet> insertItemSet( const SfxItemSet& rSet ); 352 353 // #i86923# 354 IStylePoolIteratorAccess* createIterator( bool bSkipUnusedItemSets, 355 bool bSkipIgnorableItems ); 356 }; 357 358 std::shared_ptr<SfxItemSet> StylePoolImpl::insertItemSet( const SfxItemSet& rSet ) 359 { 360 bool bNonPoolable = false; 361 Node* pCurNode = &maRoot[ rSet.GetParent() ]; 362 SfxItemIter aIter( rSet ); 363 const SfxPoolItem* pItem = aIter.GetCurItem(); 364 // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree, 365 // a complete empty SfxItemSet would stay at the root node. 366 // #i86923# insert ignorable items to the tree leaves. 367 std::unique_ptr<SfxItemSet> xFoundIgnorableItems; 368 if ( mpIgnorableItems ) 369 { 370 xFoundIgnorableItems.reset( new SfxItemSet( *mpIgnorableItems ) ); 371 } 372 while( pItem ) 373 { 374 if( !rSet.GetPool()->IsItemPoolable(pItem->Which() ) ) 375 bNonPoolable = true; 376 if ( !xFoundIgnorableItems.get() || 377 (xFoundIgnorableItems->Put( *pItem ) == nullptr ) ) 378 { 379 pCurNode = pCurNode->findChildNode( *pItem, false ); 380 } 381 pItem = aIter.NextItem(); 382 } 383 if ( xFoundIgnorableItems.get() && 384 xFoundIgnorableItems->Count() > 0 ) 385 { 386 SfxItemIter aIgnorableItemsIter( *xFoundIgnorableItems ); 387 pItem = aIgnorableItemsIter.GetCurItem(); 388 while( pItem ) 389 { 390 if( !rSet.GetPool()->IsItemPoolable(pItem->Which() ) ) 391 bNonPoolable = true; 392 pCurNode = pCurNode->findChildNode( *pItem, true ); 393 pItem = aIgnorableItemsIter.NextItem(); 394 } 395 } 396 // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets 397 // of inserted itemsets. 398 // These nodes could have but does not need to have a shared_ptr to a item set. 399 if( !pCurNode->hasItemSet( false ) ) 400 { 401 pCurNode->setItemSet( rSet ); 402 bNonPoolable = false; // to avoid a double insertion 403 } 404 // If rSet contains at least one non poolable item, a new itemset has to be inserted 405 if( bNonPoolable ) 406 pCurNode->setItemSet( rSet ); 407 #ifdef DEBUG 408 { 409 sal_Int32 nCheck = -1; 410 IStylePoolIteratorAccess* pIter = createIterator(); 411 std::shared_ptr<SfxItemSet> pTemp; 412 do 413 { 414 ++nCheck; 415 pTemp = pIter->getNext(); 416 } while( pTemp.get() ); 417 DBG_ASSERT( mnCount == nCheck, "Wrong counting"); 418 delete pIter; 419 } 420 #endif 421 return pCurNode->getItemSet(); 422 } 423 424 // #i86923# 425 IStylePoolIteratorAccess* StylePoolImpl::createIterator( bool bSkipUnusedItemSets, 426 bool bSkipIgnorableItems ) 427 { 428 return new Iterator( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems ); 429 } 430 // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-) 431 432 // #i86923# 433 StylePool::StylePool( SfxItemSet const * pIgnorableItems ) 434 : pImpl( new StylePoolImpl( pIgnorableItems ) ) 435 {} 436 437 std::shared_ptr<SfxItemSet> StylePool::insertItemSet( const SfxItemSet& rSet ) 438 { return pImpl->insertItemSet( rSet ); } 439 440 // #i86923# 441 IStylePoolIteratorAccess* StylePool::createIterator( const bool bSkipUnusedItemSets, 442 const bool bSkipIgnorableItems ) 443 { 444 return pImpl->createIterator( bSkipUnusedItemSets, bSkipIgnorableItems ); 445 } 446 447 StylePool::~StylePool() 448 {} 449 450 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 451
