LibreOffice Module svl (master) 1
stylepool.cxx
Go to the documentation of this file.
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 <tools/debug.hxx>
24#include <algorithm>
25#include <map>
26#include <memory>
27#include <optional>
28#include <vector>
29
30namespace {
36 class Node
37 {
38 std::vector<std::unique_ptr<Node>> mChildren; // child nodes, create by findChildNode(..)
39 // container of shared pointers of inserted item sets; for non-poolable
40 // items more than one item set is needed
41 std::vector< std::shared_ptr<SfxItemSet> > maItemSet;
42 std::unique_ptr<const SfxPoolItem> mpItem; // my pool item
43 Node *mpUpper; // if I'm a child node that's my parent node
44 // #i86923#
45 const bool mbIsItemIgnorable;
46 public:
47 // #i86923#
48 Node() // root node Ctor
49 : mpUpper( nullptr ),
50 mbIsItemIgnorable( false )
51 {}
52 Node( const SfxPoolItem& rItem, Node* pParent, const bool bIgnorable ) // child node Ctor
53 : mpItem( rItem.Clone() ),
54 mpUpper( pParent ),
55 mbIsItemIgnorable( bIgnorable )
56 {}
57 // #i86923#
58 bool hasItemSet( const bool bCheckUsage ) const;
59 // #i87808#
60 std::shared_ptr<SfxItemSet> const & getItemSet() const
61 {
62 return maItemSet.back();
63 }
64 std::shared_ptr<SfxItemSet> const & getUsedOrLastAddedItemSet() const;
65 void setItemSet( const SfxItemSet& rSet ){ maItemSet.push_back( std::shared_ptr<SfxItemSet>( rSet.Clone() ) ); }
66 // #i86923#
67 Node* findChildNode( const SfxPoolItem& rItem,
68 const bool bIsItemIgnorable );
69 Node* nextItemSet( Node const * pLast,
70 const bool bSkipUnusedItemSet,
71 const bool bSkipIgnorable );
72 // #i86923#
73 bool hasIgnorableChildren( const bool bCheckUsage ) const;
74 std::shared_ptr<SfxItemSet> getItemSetOfIgnorableChild(
75 const bool bSkipUnusedItemSets ) const;
76 };
77
78 // #i87808#
79 std::shared_ptr<SfxItemSet> const & Node::getUsedOrLastAddedItemSet() const
80 {
81 auto aIter = std::find_if(maItemSet.rbegin(), maItemSet.rend(),
82 [](const std::shared_ptr<SfxItemSet>& rxItemSet) { return rxItemSet.use_count() > 1; });
83
84 if (aIter != maItemSet.rend())
85 return *aIter;
86
87 return maItemSet.back();
88 }
89
90 // #i86923#
91 bool Node::hasItemSet( const bool bCheckUsage ) const
92 {
93 bool bHasItemSet = false;
94
95 if ( !maItemSet.empty())
96 {
97 if ( bCheckUsage )
98 {
99 bHasItemSet = std::any_of(maItemSet.rbegin(), maItemSet.rend(),
100 [](const std::shared_ptr<SfxItemSet>& rxItemSet) { return rxItemSet.use_count() > 1; });
101 }
102 else
103 {
104 bHasItemSet = true;
105 }
106 }
107 return bHasItemSet;
108 }
109
110 // #i86923#
111 Node* Node::findChildNode( const SfxPoolItem& rItem,
112 const bool bIsItemIgnorable )
113 {
114 for( auto const & rChild : mChildren )
115 {
116 if( rItem.Which() == rChild->mpItem->Which() &&
117 rItem == *rChild->mpItem )
118 return rChild.get();
119 }
120 // #i86923#
121 auto pNextNode = new Node( rItem, this, bIsItemIgnorable );
122 mChildren.emplace_back( pNextNode );
123 return pNextNode;
124 }
125
143 Node* Node::nextItemSet( Node const * pLast,
144 const bool bSkipUnusedItemSets,
145 const bool bSkipIgnorable )
146 {
147 // Searching downstairs
148 auto aIter = mChildren.begin();
149 // For pLast == 0 and pLast == this all children are of interest
150 // for another pLast the search starts behind pLast...
151 if( pLast && pLast != this )
152 {
153 aIter = std::find_if( mChildren.begin(), mChildren.end(),
154 [&] (std::unique_ptr<Node> const &p) { return p.get() == pLast; });
155 if( aIter != mChildren.end() )
156 ++aIter;
157 }
158 Node *pNext = nullptr;
159 while( aIter != mChildren.end() )
160 {
161 // #i86923#
162 if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable )
163 {
164 ++aIter;
165 continue;
166 }
167 pNext = aIter->get();
168 // #i86923#
169 if ( pNext->hasItemSet( bSkipUnusedItemSets ) )
170 {
171 return pNext;
172 }
173 if ( bSkipIgnorable &&
174 pNext->hasIgnorableChildren( bSkipUnusedItemSets ) )
175 {
176 return pNext;
177 }
178 pNext = pNext->nextItemSet( nullptr, bSkipUnusedItemSets, bSkipIgnorable ); // 0 => downstairs only
179 if( pNext )
180 return pNext;
181 ++aIter;
182 }
183 // Searching upstairs
184 if( pLast && mpUpper )
185 {
186 // #i86923#
187 pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable );
188 }
189 return pNext;
190 }
191
192 // #i86923#
193 bool Node::hasIgnorableChildren( const bool bCheckUsage ) const
194 {
195 return std::any_of(mChildren.begin(), mChildren.end(),
196 [&bCheckUsage](const std::unique_ptr<Node>& rxChild) {
197 Node* pChild = rxChild.get();
198 return pChild->mbIsItemIgnorable &&
199 (!bCheckUsage ||
200 ( pChild->hasItemSet( bCheckUsage /* == true */ ) ||
201 pChild->hasIgnorableChildren( bCheckUsage /* == true */ ) ));
202 });
203 }
204
205 std::shared_ptr<SfxItemSet> Node::getItemSetOfIgnorableChild(
206 const bool bSkipUnusedItemSets ) const
207 {
208 DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets ),
209 "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
210
211 for( const auto& rxChild : mChildren )
212 {
213 Node* pChild = rxChild.get();
214 if ( pChild->mbIsItemIgnorable )
215 {
216 if ( pChild->hasItemSet( bSkipUnusedItemSets ) )
217 {
218 return pChild->getUsedOrLastAddedItemSet();
219 }
220 else
221 {
222 pChild = pChild->nextItemSet( nullptr, bSkipUnusedItemSets, false );
223 if ( pChild )
224 {
225 return pChild->getUsedOrLastAddedItemSet();
226 }
227 }
228 }
229 }
230
231 std::shared_ptr<SfxItemSet> pReturn;
232 return pReturn;
233 }
234
235 class Iterator : public IStylePoolIteratorAccess
236 {
237 std::map< const SfxItemSet*, Node >& mrRoot;
238 std::map< const SfxItemSet*, Node >::iterator mpCurrNode;
239 Node* mpNode;
240 const bool mbSkipUnusedItemSets;
241 const bool mbSkipIgnorable;
243 std::vector<const SfxItemSet*> maParents;
245 std::vector<const SfxItemSet*>::iterator mpCurrParent;
246 public:
247 // #i86923#
248 Iterator( std::map< const SfxItemSet*, Node >& rR,
249 const bool bSkipUnusedItemSets,
250 const bool bSkipIgnorable,
251 const std::map< const SfxItemSet*, OUString>& rParentNames )
252 : mrRoot( rR ),
253 mpNode(nullptr),
254 mbSkipUnusedItemSets( bSkipUnusedItemSets ),
255 mbSkipIgnorable( bSkipIgnorable )
256 {
257 // Collect the parent pointers into a vector we can sort.
258 for (const auto& rParent : mrRoot)
259 maParents.push_back(rParent.first);
260
261 // Sort the parents using their name, if they have one.
262 if (!rParentNames.empty())
263 {
264 std::stable_sort(maParents.begin(), maParents.end(),
265 [&rParentNames](const SfxItemSet* pA, const SfxItemSet* pB) {
266 OUString aA;
267 OUString aB;
268 auto it = rParentNames.find(pA);
269 if (it != rParentNames.end())
270 aA = it->second;
271 it = rParentNames.find(pB);
272 if (it != rParentNames.end())
273 aB = it->second;
274 return aA < aB;
275 });
276 }
277
278 // Start the iteration.
279 mpCurrParent = maParents.begin();
280 if (mpCurrParent != maParents.end())
281 mpCurrNode = mrRoot.find(*mpCurrParent);
282 }
283 virtual std::shared_ptr<SfxItemSet> getNext() override;
284 };
285
286 std::shared_ptr<SfxItemSet> Iterator::getNext()
287 {
288 std::shared_ptr<SfxItemSet> pReturn;
289 while( mpNode || mpCurrParent != maParents.end() )
290 {
291 if( !mpNode )
292 {
293 mpNode = &mpCurrNode->second;
294 // Perform the actual increment.
295 ++mpCurrParent;
296 if (mpCurrParent != maParents.end())
297 mpCurrNode = mrRoot.find(*mpCurrParent);
298 // #i86923#
299 if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) )
300 {
301 // #i87808#
302 return mpNode->getUsedOrLastAddedItemSet();
303 }
304 }
305 // #i86923#
306 mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable );
307 if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) )
308 {
309 // #i87808#
310 return mpNode->getUsedOrLastAddedItemSet();
311 }
312 if ( mbSkipIgnorable &&
313 mpNode && mpNode->hasIgnorableChildren( mbSkipUnusedItemSets ) )
314 {
315 return mpNode->getItemSetOfIgnorableChild( mbSkipUnusedItemSets );
316 }
317 }
318 return pReturn;
319 }
320
321}
322
327OUString StylePool::nameOf( const std::shared_ptr<SfxItemSet>& pSet )
328{
329 return OUString::number( reinterpret_cast<sal_IntPtr>( pSet.get() ), 16 );
330}
331
339{
340private:
341 std::map< const SfxItemSet*, Node > maRoot;
343 std::map< const SfxItemSet*, OUString> maParentNames;
344 // #i86923#
345 std::unique_ptr<SfxItemSet> mpIgnorableItems;
346#ifdef DEBUG
347 sal_Int32 mnCount;
348#endif
349public:
350 // #i86923#
351 explicit StylePoolImpl( SfxItemSet const * pIgnorableItems )
352 :
353#ifdef DEBUG
354 mnCount(0),
355#endif
356 mpIgnorableItems( pIgnorableItems != nullptr
357 ? pIgnorableItems->Clone( false )
358 : nullptr )
359 {
360 DBG_ASSERT( !pIgnorableItems || !pIgnorableItems->Count(),
361 "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
363 "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as expected - <mpIgnorableItems> is not empty." );
364 }
365
366 std::shared_ptr<SfxItemSet> insertItemSet( const SfxItemSet& rSet, const OUString* pParentName = nullptr );
367
368 // #i86923#
369 std::unique_ptr<IStylePoolIteratorAccess> createIterator( bool bSkipUnusedItemSets,
370 bool bSkipIgnorableItems );
371};
372
373
374std::shared_ptr<SfxItemSet> StylePoolImpl::insertItemSet( const SfxItemSet& rSet, const OUString* pParentName )
375{
376 bool bNonPoolable = false;
377 Node* pCurNode = &maRoot[ rSet.GetParent() ];
378 if (pParentName)
379 maParentNames[ rSet.GetParent() ] = *pParentName;
380 SfxItemIter aIter( rSet );
381 const SfxPoolItem* pItem = aIter.GetCurItem();
382 // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
383 // a complete empty SfxItemSet would stay at the root node.
384 // #i86923# insert ignorable items to the tree leaves.
385 std::optional<SfxItemSet> xFoundIgnorableItems;
386 if ( mpIgnorableItems )
387 {
388 xFoundIgnorableItems.emplace( *mpIgnorableItems );
389 }
390 while( pItem )
391 {
392 if( !rSet.GetPool()->IsItemPoolable(pItem->Which() ) )
393 bNonPoolable = true;
394 if (!xFoundIgnorableItems || (xFoundIgnorableItems->Put(*pItem) == nullptr))
395 {
396 pCurNode = pCurNode->findChildNode( *pItem, false );
397 }
398 pItem = aIter.NextItem();
399 }
400 if ( xFoundIgnorableItems && xFoundIgnorableItems->Count() > 0 )
401 {
402 SfxItemIter aIgnorableItemsIter( *xFoundIgnorableItems );
403 pItem = aIgnorableItemsIter.GetCurItem();
404 while( pItem )
405 {
406 if( !rSet.GetPool()->IsItemPoolable(pItem->Which() ) )
407 bNonPoolable = true;
408 pCurNode = pCurNode->findChildNode( *pItem, true );
409 pItem = aIgnorableItemsIter.NextItem();
410 }
411 }
412 // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
413 // of inserted itemsets.
414 // These nodes could have but does not need to have a shared_ptr to an item set.
415 if( !pCurNode->hasItemSet( false ) )
416 {
417 pCurNode->setItemSet( rSet );
418 bNonPoolable = false; // to avoid a double insertion
419#ifdef DEBUG
420 ++mnCount;
421#endif
422 }
423 // If rSet contains at least one non poolable item, a new itemset has to be inserted
424 if( bNonPoolable )
425 pCurNode->setItemSet( rSet );
426#ifdef DEBUG
427 {
428 sal_Int32 nCheck = -1;
429 std::unique_ptr<IStylePoolIteratorAccess> pIter = createIterator(false,false);
430 std::shared_ptr<SfxItemSet> pTemp;
431 do
432 {
433 ++nCheck;
434 pTemp = pIter->getNext();
435 } while( pTemp.get() );
436 DBG_ASSERT( mnCount == nCheck, "Wrong counting");
437 }
438#endif
439 return pCurNode->getItemSet();
440}
441
442// #i86923#
443std::unique_ptr<IStylePoolIteratorAccess> StylePoolImpl::createIterator( bool bSkipUnusedItemSets,
444 bool bSkipIgnorableItems )
445{
446 return std::make_unique<Iterator>( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems, maParentNames );
447}
448// Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
449
450// #i86923#
451StylePool::StylePool( SfxItemSet const * pIgnorableItems )
452 : pImpl( new StylePoolImpl( pIgnorableItems ) )
453{}
454
455std::shared_ptr<SfxItemSet> StylePool::insertItemSet( const SfxItemSet& rSet, const OUString* pParentName )
456{ return pImpl->insertItemSet( rSet, pParentName ); }
457
458// #i86923#
459std::unique_ptr<IStylePoolIteratorAccess> StylePool::createIterator( const bool bSkipUnusedItemSets,
460 const bool bSkipIgnorableItems )
461{
462 return pImpl->createIterator( bSkipUnusedItemSets, bSkipIgnorableItems );
463}
464
466{}
467
468/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual std::shared_ptr< SfxItemSet > getNext()=0
Delivers a shared pointer to the next SfxItemSet of the pool If there is no more SfxItemSet,...
Node(int _id)
const SfxPoolItem * GetCurItem() const
get item, or null if no items
Definition: itemiter.hxx:38
const SfxPoolItem * NextItem()
Definition: itemiter.hxx:42
bool IsItemPoolable(sal_uInt16 nWhich) const
Definition: itempool.cxx:178
SfxItemPool * GetPool() const
Definition: itemset.hxx:202
sal_uInt16 Count() const
Definition: itemset.hxx:95
virtual std::unique_ptr< SfxItemSet > Clone(bool bItems=true, SfxItemPool *pToPool=nullptr) const
Definition: itemset.cxx:1273
const SfxItemSet * GetParent() const
Definition: itemset.hxx:207
sal_uInt16 Which() const
Definition: poolitem.hxx:149
class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
Definition: stylepool.cxx:339
std::unique_ptr< SfxItemSet > mpIgnorableItems
Definition: stylepool.cxx:345
StylePoolImpl(SfxItemSet const *pIgnorableItems)
Definition: stylepool.cxx:351
std::unique_ptr< IStylePoolIteratorAccess > createIterator(bool bSkipUnusedItemSets, bool bSkipIgnorableItems)
Definition: stylepool.cxx:443
sal_Int32 mnCount
Definition: stylepool.cxx:347
std::map< const SfxItemSet *, Node > maRoot
Definition: stylepool.cxx:341
std::map< const SfxItemSet *, OUString > maParentNames
Names of maRoot keys.
Definition: stylepool.cxx:343
std::shared_ptr< SfxItemSet > insertItemSet(const SfxItemSet &rSet, const OUString *pParentName=nullptr)
Definition: stylepool.cxx:374
std::unique_ptr< StylePoolImpl > pImpl
Definition: stylepool.hxx:32
StylePool(SfxItemSet const *pIgnorableItems=nullptr)
Definition: stylepool.cxx:451
static OUString nameOf(const std::shared_ptr< SfxItemSet > &pSet)
This static method creates a unique name from a shared pointer to a SfxItemSet The name is the memory...
Definition: stylepool.cxx:327
std::unique_ptr< IStylePoolIteratorAccess > createIterator(const bool bSkipUnusedItemSets=false, const bool bSkipIgnorableItems=false)
Create an iterator.
Definition: stylepool.cxx:459
std::shared_ptr< SfxItemSet > insertItemSet(const SfxItemSet &rSet, const OUString *pParentName=nullptr)
Insert a SfxItemSet into the style pool.
Definition: stylepool.cxx:455
#define DBG_ASSERT(sCon, aError)
AlgAtomPtr mpNode
int DEBUG
css::uno::Reference< css::animations::XAnimationNode > Clone(const css::uno::Reference< css::animations::XAnimationNode > &xSourceNode, const SdPage *pSource=nullptr, const SdPage *pTarget=nullptr)
static SfxItemSet & rSet
SfxItemSet maItemSet