blob: 120a377baa9f0b2d43e8fa75f98419a81dc32fc9 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svl.hxx"
#ifdef _MSC_VER
#pragma hdrstop
#endif
#include <vector>
#include <map>
#include <svl/stylepool.hxx>
#include <svl/itemiter.hxx>
#include <svl/itempool.hxx>
using namespace boost;
namespace {
// A "Node" represents a subset of inserted SfxItemSets
// The root node represents the empty set
// The other nodes contain a SfxPoolItem and represents an item set which contains their
// pool item and the pool items of their parents.
class Node
{
std::vector<Node*> mChildren; // child nodes, create by findChildNode(..)
// container of shared pointers of inserted item sets; for non-poolable
// items more than one item set is needed
std::vector< StylePool::SfxItemSet_Pointer_t > maItemSet;
const SfxPoolItem *mpItem; // my pool item
Node *mpUpper; // if I'm a child node that's my parent node
// --> OD 2008-03-07 #i86923#
const bool mbIsItemIgnorable;
// <--
public:
// --> OD 2008-03-07 #i86923#
Node() // root node Ctor
: mChildren(),
maItemSet(),
mpItem( 0 ),
mpUpper( 0 ),
mbIsItemIgnorable( false )
{}
Node( const SfxPoolItem& rItem, Node* pParent, const bool bIgnorable ) // child node Ctor
: mChildren(),
maItemSet(),
mpItem( rItem.Clone() ),
mpUpper( pParent ),
mbIsItemIgnorable( bIgnorable )
{}
// <--
~Node();
// --> OD 2008-03-11 #i86923#
bool hasItemSet( const bool bCheckUsage ) const;
// <--
// --> OD 2008-04-29 #i87808#
// const StylePool::SfxItemSet_Pointer_t getItemSet() const { return aItemSet[aItemSet.size()-1]; }
const StylePool::SfxItemSet_Pointer_t getItemSet() const
{
return maItemSet.back();
}
const StylePool::SfxItemSet_Pointer_t getUsedOrLastAddedItemSet() const;
// <--
void setItemSet( const SfxItemSet& rSet ){ maItemSet.push_back( StylePool::SfxItemSet_Pointer_t( rSet.Clone() ) ); }
// --> OD 2008-03-11 #i86923#
Node* findChildNode( const SfxPoolItem& rItem,
const bool bIsItemIgnorable = false );
Node* nextItemSet( Node* pLast,
const bool bSkipUnusedItemSet,
const bool bSkipIgnorable );
// <--
const SfxPoolItem& getPoolItem() const { return *mpItem; }
// --> OD 2008-03-11 #i86923#
bool hasIgnorableChildren( const bool bCheckUsage ) const;
const StylePool::SfxItemSet_Pointer_t getItemSetOfIgnorableChild(
const bool bSkipUnusedItemSets ) const;
// <--
};
// --> OD 2008-04-29 #i87808#
const StylePool::SfxItemSet_Pointer_t Node::getUsedOrLastAddedItemSet() const
{
std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
{
if ( (*aIter).use_count() > 1 )
{
return *aIter;
}
}
return maItemSet.back();
}
// <--
// --> OD 2008-05-06 #i86923#
bool Node::hasItemSet( const bool bCheckUsage ) const
{
bool bHasItemSet = false;
if ( maItemSet.size() > 0 )
{
if ( bCheckUsage )
{
std::vector< StylePool::SfxItemSet_Pointer_t >::const_reverse_iterator aIter;
for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
{
if ( (*aIter).use_count() > 1 )
{
bHasItemSet = true;
break;
}
}
}
else
{
bHasItemSet = true;
}
}
return bHasItemSet;
}
// <--
// --> OD 2008-03-07 #i86923#
Node* Node::findChildNode( const SfxPoolItem& rItem,
const bool bIsItemIgnorable )
// <--
{
Node* pNextNode = this;
std::vector<Node*>::iterator aIter = mChildren.begin();
while( aIter != mChildren.end() )
{
if( rItem.Which() == (*aIter)->getPoolItem().Which() &&
rItem == (*aIter)->getPoolItem() )
return *aIter;
++aIter;
}
// --> OD 2008-03-07 #i86923#
pNextNode = new Node( rItem, pNextNode, bIsItemIgnorable );
// <--
mChildren.push_back( pNextNode );
return pNextNode;
}
/* Find the next node which has a SfxItemSet.
The input parameter pLast has a sophisticated meaning:
downstairs only:
pLast == 0 => scan your children and their children
but neither your parents neither your siblings
downstairs and upstairs:
pLast == this => scan your children, their children,
the children of your parent behind you, and so on
partial downstairs and upstairs
pLast != 0 && pLast != this => scan your children behind the given children,
the children of your parent behind you and so on.
OD 2008-03-11 #i86923#
introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
and its handling.
*/
Node* Node::nextItemSet( Node* pLast,
const bool bSkipUnusedItemSets,
const bool bSkipIgnorable )
{
// Searching downstairs
std::vector<Node*>::iterator aIter = mChildren.begin();
// For pLast == 0 and pLast == this all children are of interest
// for another pLast the search starts behind pLast...
if( pLast && pLast != this )
{
aIter = std::find( mChildren.begin(), mChildren.end(), pLast );
if( aIter != mChildren.end() )
++aIter;
}
Node *pNext = 0;
while( aIter != mChildren.end() )
{
// --> OD 2008-03-11 #i86923#
if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable )
{
++aIter;
continue;
}
// <--
pNext = *aIter;
// --> OD 2008-03-11 #i86923#
if ( pNext->hasItemSet( bSkipUnusedItemSets ) )
{
return pNext;
}
if ( bSkipIgnorable &&
pNext->hasIgnorableChildren( bSkipUnusedItemSets ) )
{
return pNext;
}
pNext = pNext->nextItemSet( 0, bSkipUnusedItemSets, bSkipIgnorable ); // 0 => downstairs only
// <--
if( pNext )
return pNext;
++aIter;
}
// Searching upstairs
if( pLast && mpUpper )
{
// --> OD 2008-03-11 #i86923#
pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable );
// <--
}
return pNext;
}
// --> OD 2008-03-11 #i86923#
bool Node::hasIgnorableChildren( const bool bCheckUsage ) const
{
bool bHasIgnorableChildren( false );
std::vector<Node*>::const_iterator aIter = mChildren.begin();
while( aIter != mChildren.end() && !bHasIgnorableChildren )
{
Node* pChild = *aIter;
if ( pChild->mbIsItemIgnorable )
{
bHasIgnorableChildren =
!bCheckUsage ||
( pChild->hasItemSet( bCheckUsage /* == true */ ) ||
pChild->hasIgnorableChildren( bCheckUsage /* == true */ ) );
}
++aIter;
}
return bHasIgnorableChildren;
}
const StylePool::SfxItemSet_Pointer_t Node::getItemSetOfIgnorableChild(
const bool bSkipUnusedItemSets ) const
{
DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets ),
"<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
std::vector<Node*>::const_iterator aIter = mChildren.begin();
while( aIter != mChildren.end() )
{
Node* pChild = *aIter;
if ( pChild->mbIsItemIgnorable )
{
if ( pChild->hasItemSet( bSkipUnusedItemSets ) )
{
return pChild->getUsedOrLastAddedItemSet();
}
else
{
pChild = pChild->nextItemSet( 0, bSkipUnusedItemSets, false );
if ( pChild )
{
return pChild->getUsedOrLastAddedItemSet();
}
}
}
++aIter;
}
StylePool::SfxItemSet_Pointer_t pReturn;
return pReturn;
}
// <--
Node::~Node()
{
std::vector<Node*>::iterator aIter = mChildren.begin();
while( aIter != mChildren.end() )
{
delete *aIter;
++aIter;
}
delete mpItem;
}
class Iterator : public IStylePoolIteratorAccess
{
std::map< const SfxItemSet*, Node >& mrRoot;
std::map< const SfxItemSet*, Node >::iterator mpCurrNode;
Node* mpNode;
const bool mbSkipUnusedItemSets;
const bool mbSkipIgnorable;
public:
// --> OD 2008-03-07 #i86923#
Iterator( std::map< const SfxItemSet*, Node >& rR,
const bool bSkipUnusedItemSets,
const bool bSkipIgnorable )
: mrRoot( rR ),
mpCurrNode( rR.begin() ),
mpNode(0),
mbSkipUnusedItemSets( bSkipUnusedItemSets ),
mbSkipIgnorable( bSkipIgnorable )
{}
// <--
virtual StylePool::SfxItemSet_Pointer_t getNext();
virtual ::rtl::OUString getName();
};
StylePool::SfxItemSet_Pointer_t Iterator::getNext()
{
StylePool::SfxItemSet_Pointer_t pReturn;
while( mpNode || mpCurrNode != mrRoot.end() )
{
if( !mpNode )
{
mpNode = &mpCurrNode->second;
++mpCurrNode;
// --> OD 2008-03-11 #i86923#
if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) )
{
// --> OD 2008-04-30 #i87808#
// return pNode->getItemSet();
return mpNode->getUsedOrLastAddedItemSet();
// <--
}
// <--
}
// --> OD 2008-03-11 #i86923#
mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable );
if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) )
{
// --> OD 2008-04-30 #i87808#
// return pNode->getItemSet();
return mpNode->getUsedOrLastAddedItemSet();
// <--
}
if ( mbSkipIgnorable &&
mpNode && mpNode->hasIgnorableChildren( mbSkipUnusedItemSets ) )
{
return mpNode->getItemSetOfIgnorableChild( mbSkipUnusedItemSets );
}
// <--
}
return pReturn;
}
::rtl::OUString Iterator::getName()
{
::rtl::OUString aString;
if( mpNode && mpNode->hasItemSet( false ) )
{
// --> OD 2008-04-30 #i87808#
// aString = StylePool::nameOf( pNode->getItemSet() );
aString = StylePool::nameOf( mpNode->getUsedOrLastAddedItemSet() );
// <--
}
return aString;
}
}
/* This static method creates a unique name from a shared pointer to a SfxItemSet
The name is the memory address of the SfxItemSet itself. */
::rtl::OUString StylePool::nameOf( SfxItemSet_Pointer_t pSet )
{
return ::rtl::OUString::valueOf( reinterpret_cast<sal_IntPtr>( pSet.get() ), 16 );
}
// class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
// The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
// to a copy of the SfxItemSet.
// The aRoot-Node represents an empty SfxItemSet.
class StylePoolImpl
{
private:
std::map< const SfxItemSet*, Node > maRoot;
sal_Int32 mnCount;
// --> OD 2008-03-07 #i86923#
SfxItemSet* mpIgnorableItems;
// <--
public:
// --> OD 2008-03-07 #i86923#
explicit StylePoolImpl( SfxItemSet* pIgnorableItems = 0 )
: maRoot(),
mnCount(0),
mpIgnorableItems( pIgnorableItems != 0
? pIgnorableItems->Clone( sal_False )
: 0 )
{
DBG_ASSERT( !pIgnorableItems || !pIgnorableItems->Count(),
"<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
DBG_ASSERT( !mpIgnorableItems || !mpIgnorableItems->Count(),
"<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty. Please inform OD." );
}
~StylePoolImpl()
{
delete mpIgnorableItems;
}
// <--
StylePool::SfxItemSet_Pointer_t insertItemSet( const SfxItemSet& rSet );
// --> OD 2008-03-07 #i86923#
IStylePoolIteratorAccess* createIterator( bool bSkipUnusedItemSets = false,
bool bSkipIgnorableItems = false );
// <--
sal_Int32 getCount() const { return mnCount; }
};
StylePool::SfxItemSet_Pointer_t StylePoolImpl::insertItemSet( const SfxItemSet& rSet )
{
bool bNonPoolable = false;
Node* pCurNode = &maRoot[ rSet.GetParent() ];
SfxItemIter aIter( rSet );
const SfxPoolItem* pItem = aIter.GetCurItem();
// Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
// a complete empty SfxItemSet would stay at the root node.
// --> OD 2008-03-07 #i86923#
// insert ignorable items to the tree leaves.
std::auto_ptr<SfxItemSet> pFoundIgnorableItems;
if ( mpIgnorableItems )
{
pFoundIgnorableItems.reset( new SfxItemSet( *mpIgnorableItems ) );
}
while( pItem )
{
if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SFX_ITEM_POOLABLE ) )
bNonPoolable = true;
if ( !pFoundIgnorableItems.get() ||
( pFoundIgnorableItems.get() &&
pFoundIgnorableItems->Put( *pItem ) == 0 ) )
{
pCurNode = pCurNode->findChildNode( *pItem );
}
pItem = aIter.NextItem();
}
if ( pFoundIgnorableItems.get() &&
pFoundIgnorableItems->Count() > 0 )
{
SfxItemIter aIgnorableItemsIter( *pFoundIgnorableItems );
pItem = aIgnorableItemsIter.GetCurItem();
while( pItem )
{
if( !rSet.GetPool()->IsItemFlag(pItem->Which(), SFX_ITEM_POOLABLE ) )
bNonPoolable = true;
pCurNode = pCurNode->findChildNode( *pItem, true );
pItem = aIgnorableItemsIter.NextItem();
}
}
// <--
// Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
// of inserted itemsets.
// These nodes could have but does not need to have a shared_ptr to a item set.
if( !pCurNode->hasItemSet( false ) )
{
pCurNode->setItemSet( rSet );
bNonPoolable = false; // to avoid a double insertion
++mnCount;
}
// If rSet contains at least one non poolable item, a new itemset has to be inserted
if( bNonPoolable )
pCurNode->setItemSet( rSet );
#ifdef DEBUG
{
sal_Int32 nCheck = -1;
sal_Int32 nNo = -1;
IStylePoolIteratorAccess* pIter = createIterator();
StylePool::SfxItemSet_Pointer_t pTemp;
do
{
++nCheck;
pTemp = pIter->getNext();
if( pCurNode->hasItemSet( false ) && pTemp.get() == pCurNode->getItemSet().get() )
{
::rtl::OUString aStr = StylePool::nameOf( pTemp );
nNo = nCheck;
}
} while( pTemp.get() );
DBG_ASSERT( mnCount == nCheck, "Wrong counting");
delete pIter;
}
#endif
return pCurNode->getItemSet();
}
// --> OD 2008-03-07 #i86923#
IStylePoolIteratorAccess* StylePoolImpl::createIterator( bool bSkipUnusedItemSets,
bool bSkipIgnorableItems )
{
return new Iterator( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems );
}
// <--
// Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
// --> OD 2008-03-07 #i86923#
StylePool::StylePool( SfxItemSet* pIgnorableItems )
: pImpl( new StylePoolImpl( pIgnorableItems ) )
{}
// <--
StylePool::SfxItemSet_Pointer_t StylePool::insertItemSet( const SfxItemSet& rSet )
{ return pImpl->insertItemSet( rSet ); }
// --> OD 2008-03-11 #i86923#
IStylePoolIteratorAccess* StylePool::createIterator( const bool bSkipUnusedItemSets,
const bool bSkipIgnorableItems )
{
return pImpl->createIterator( bSkipUnusedItemSets, bSkipIgnorableItems );
}
// <--
sal_Int32 StylePool::getCount() const
{ return pImpl->getCount(); }
StylePool::~StylePool() { delete pImpl; }
// End of class StylePool