office-gobmx/svl/source/items/stylepool.cxx
2011-08-11 23:23:19 +02:00

501 lines
17 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2000, 2010 Oracle and/or its affiliates.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 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
// #i86923#
const bool mbIsItemIgnorable;
public:
// #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();
// #i86923#
bool hasItemSet( const bool bCheckUsage ) const;
// #i87808#
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() ) ); }
// #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; }
// #i86923#
bool hasIgnorableChildren( const bool bCheckUsage ) const;
const StylePool::SfxItemSet_Pointer_t getItemSetOfIgnorableChild(
const bool bSkipUnusedItemSets ) const;
};
// #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();
}
// #i86923#
bool Node::hasItemSet( const bool bCheckUsage ) const
{
bool bHasItemSet = false;
if ( !maItemSet.empty())
{
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;
}
// #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;
}
// #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() )
{
// #i86923#
if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable )
{
++aIter;
continue;
}
pNext = *aIter;
// #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 )
{
// #i86923#
pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable );
}
return pNext;
}
// #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:
// #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;
// #i86923#
if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) )
{
// #i87808#
return mpNode->getUsedOrLastAddedItemSet();
}
}
// #i86923#
mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable );
if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) )
{
// #i87808#
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 ) )
{
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;
// #i86923#
SfxItemSet* mpIgnorableItems;
public:
// #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 );
// #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.
// #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;
IStylePoolIteratorAccess* pIter = createIterator();
StylePool::SfxItemSet_Pointer_t pTemp;
do
{
++nCheck;
pTemp = pIter->getNext();
} while( pTemp.get() );
DBG_ASSERT( mnCount == nCheck, "Wrong counting");
delete pIter;
}
#endif
return pCurNode->getItemSet();
}
// #i86923#
IStylePoolIteratorAccess* StylePoolImpl::createIterator( bool bSkipUnusedItemSets,
bool bSkipIgnorableItems )
{
return new Iterator( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems );
}
// Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
// #i86923#
StylePool::StylePool( SfxItemSet* pIgnorableItems )
: pImpl( new StylePoolImpl( pIgnorableItems ) )
{}
StylePool::SfxItemSet_Pointer_t StylePool::insertItemSet( const SfxItemSet& rSet )
{ return pImpl->insertItemSet( rSet ); }
// #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; }
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */