blob: 531bc271f6ce2a0659a6fa41db18f5f1f8abc681 [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.
*
*************************************************************/
#include "precompiled_sfx2.hxx"
#include <sfx2/Metadatable.hxx>
#include <sfx2/XmlIdRegistry.hxx>
#include <vos/mutex.hxx>
#include <vcl/svapp.hxx> // solarmutex
#include <rtl/random.h>
#include <boost/bind.hpp>
#include <memory>
#include <hash_map>
#include <list>
#include <algorithm>
#if OSL_DEBUG_LEVEL > 0
#include <typeinfo>
#endif
/** XML ID handling.
There is an abstract base class <type>XmlIdRegistry</type>, with
2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
and <type>XmlIdRegistryClipboard</type> for clipboard documents.
These classes are responsible for managing XML IDs for all elements
of the model. Only the implementation of the <type>Metadatable</type>
base class needs to know the registries, so they are not in the header.
The handling of XML IDs differs between clipboard and non-clipboard
documents in several aspects. Most importantly, non-clipboard documents
can have several elements associated with one XML ID.
This is necessary because of the weird undo implementation:
deleting a text node moves the deleted node to the undo array, but
executing undo will then create a <em>copy</em> of that node in the
document array. These 2 nodes must have the same XML ID, because
we cannot know whether the user will do a redo next, or something else.
Because we need to have a mechanism for several objects per XML ID anyway,
we use that also to enable some usability features:
The document registry has a list of Metadatables per XML ID.
This list is sorted by priority, i.e., the first element has highest
priority. When inserting copies, care must be taken that they are inserted
at the right position: either before or after the source.
This is done by <method>Metadatable::RegisterAsCopyOf</method>.
When a text node is split, then both resulting text nodes are inserted
into the list. If the user then deletes one text node, the other one
will have the XML ID.
Also, when a Metadatable is copied to the clipboard and then pasted,
the copy is inserted into the list. If the user then deletes the source,
the XML ID is not lost.
The goal is that it should be hard to lose an XML ID by accident, which
is especially important as long as we do not have an UI that displays them.
There are two subclasses of <type>Metadatable</type>:
<ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
<li><type>MetadatableUndo</type>: for undo, because a Metadatable
may be destroyed on delete and a new one created on undo.</li></ul>
These serve only to track the position in an XML ID list in a document
registry, so that future actions can insert objects at the right position.
Unfortunately, inserting dummy objects seems to be necessary:
<ul><li>it is not sufficent to just remember the saved id, because then
the relative priorities might change when executing the undo</li>
<li>it is not sufficient to record the position as an integer, because
if we delete a text node and then undo, the node will be copied(!),
and we will have one more node in the list.<li>
<li>it is not sufficient to record the pointer of the previous/next
Metadatable, because if we delete a text node, undo, and then
do something to clear the redo array, the original text node is
destroyed, and is replaced by the copy created by undo</li></ul>
If content from a non-clipboard document is copied into a clipboard
document, a dummy <type>MetadatableClipboard</type> is inserted into the
non-clipboard document registry in order to track the position of the
source element. When the clipboard content is pasted back into the source
document, this dummy object is used to associate the pasted element with
that same XML ID.
If a <type>Metadatable</type> is deleted or merged,
<method>Metadatable::CreateUndo</method> is called, and returns a
<type>MetadatableUndo<type> instance, which can be used to undo the action
by passing it to <method>Metadatable::RestoreMetadata</method>.
@author mst
*/
using namespace ::com::sun::star;
using ::sfx2::isValidXmlId;
namespace sfx2 {
static const char s_content [] = "content.xml";
static const char s_styles [] = "styles.xml";
static const char s_prefix [] = "id"; // prefix for generated xml:id
static bool isContentFile(::rtl::OUString const & i_rPath)
{
return i_rPath.equalsAscii(s_content);
}
static bool isStylesFile (::rtl::OUString const & i_rPath)
{
return i_rPath.equalsAscii(s_styles);
}
//=============================================================================
// XML ID handling ---------------------------------------------------
/** handles registration of XMetadatable.
This class is responsible for guaranteeing that XMetadatable objects
always have XML IDs that are unique within a stream.
This is an abstract base class; see subclasses XmlIdRegistryDocument and
XmlIdRegistryClipboard.
@see SwDoc::GetXmlIdRegistry
@see SwDocShell::GetXmlIdRegistry
*/
class XmlIdRegistry : public sfx2::IXmlIdRegistry
{
public:
XmlIdRegistry();
virtual ~XmlIdRegistry();
/** get the ODF element with the given metadata reference. */
virtual ::com::sun::star::uno::Reference<
::com::sun::star::rdf::XMetadatable > SAL_CALL
GetElementByMetadataReference(
const ::com::sun::star::beans::StringPair & i_rReference) const;
/** register an ODF element at a newly generated, unique metadata reference.
<p>
Find a fresh XML ID, and register it for the element.
The generated ID does not occur in any stream of the document.
</p>
*/
virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) = 0;
/** try to register an ODF element at a given XML ID, or update its
registation to a different XML ID.
<p>
If the given new metadata reference is not already occupied in the
document, unregister the element at its old metadata reference if
it has one, and register the new metadata reference for the element.
Note that this method only ensures that XML IDs are unique per stream,
so using the same XML ID in both content.xml and styles.xml is allowed.
</p>
@returns
true iff the element has successfully been registered
*/
virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref)
= 0;
/** unregister an ODF element.
<p>
Unregister the element at its metadata reference.
Does not remove the metadata reference from the element.
</p>
@see RemoveXmlIdForElement
*/
virtual void UnregisterMetadatable(Metadatable const&) = 0;
/** get the metadata reference for the given element. */
::com::sun::star::beans::StringPair
GetXmlIdForElement(Metadatable const&) const;
/** remove the metadata reference for the given element. */
virtual void RemoveXmlIdForElement(Metadatable const&) = 0;
protected:
virtual bool LookupXmlId(const Metadatable& i_xObject,
::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const = 0;
virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const = 0;
};
// XmlIdRegistryDocument ---------------------------------------------
/** non-clipboard documents */
class XmlIdRegistryDocument : public XmlIdRegistry
{
public:
XmlIdRegistryDocument();
virtual ~XmlIdRegistryDocument();
virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject);
virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref);
virtual void UnregisterMetadatable(Metadatable const&);
virtual void RemoveXmlIdForElement(Metadatable const&);
/** register i_rCopy as a copy of i_rSource,
with precedence iff i_bCopyPrecedesSource is true */
void RegisterCopy(Metadatable const& i_rSource, Metadatable & i_rCopy,
const bool i_bCopyPrecedesSource);
/** create a Undo Metadatable for i_rObject. */
::boost::shared_ptr<MetadatableUndo> CreateUndo(
Metadatable const& i_rObject);
/** merge i_rMerged and i_rOther into i_rMerged. */
void JoinMetadatables(Metadatable & i_rMerged, Metadatable const& i_rOther);
// unfortunately public, Metadatable::RegisterAsCopyOf needs this
virtual bool LookupXmlId(const Metadatable& i_xObject,
::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const;
private:
virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const;
struct XmlIdRegistry_Impl;
::std::auto_ptr<XmlIdRegistry_Impl> m_pImpl;
};
// MetadatableUndo ---------------------------------------------------
/** the horrible Undo Metadatable: is inserted into lists to track position */
class MetadatableUndo : public Metadatable
{
/// as determined by the stream of the source in original document
const bool m_isInContent;
public:
MetadatableUndo(const bool i_isInContent)
: m_isInContent(i_isInContent) { }
virtual ::sfx2::XmlIdRegistry& GetRegistry()
{
// N.B. for Undo, m_pReg is initialized by registering this as copy in
// CreateUndo; it is never cleared
OSL_ENSURE(m_pReg, "no m_pReg in MetadatableUndo ?");
return *m_pReg;
}
virtual bool IsInClipboard() const { return false; }
virtual bool IsInUndo() const { return true; }
virtual bool IsInContent() const { return m_isInContent; }
virtual ::com::sun::star::uno::Reference<
::com::sun::star::rdf::XMetadatable > MakeUnoObject()
{ OSL_ENSURE(false, "MetadatableUndo::MakeUnoObject"); throw; }
};
// MetadatableClipboard ----------------------------------------------
/** the horrible Clipboard Metadatable: inserted into lists to track position */
class MetadatableClipboard : public Metadatable
{
/// as determined by the stream of the source in original document
const bool m_isInContent;
public:
MetadatableClipboard(const bool i_isInContent)
: m_isInContent(i_isInContent) { }
virtual ::sfx2::XmlIdRegistry& GetRegistry()
{
// N.B. for Clipboard, m_pReg is initialized by registering this as copy in
// RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
OSL_ENSURE(m_pReg, "no m_pReg in MetadatableClipboard ?");
return *m_pReg;
}
virtual bool IsInClipboard() const { return true; }
virtual bool IsInUndo() const { return false; }
virtual bool IsInContent() const { return m_isInContent; }
virtual ::com::sun::star::uno::Reference<
::com::sun::star::rdf::XMetadatable > MakeUnoObject()
{ OSL_ENSURE(false, "MetadatableClipboard::MakeUnoObject"); throw; }
void OriginNoLongerInBusinessAnymore() { m_pReg = 0; }
};
// XmlIdRegistryClipboard --------------------------------------------
class XmlIdRegistryClipboard : public XmlIdRegistry
{
public:
XmlIdRegistryClipboard();
virtual ~XmlIdRegistryClipboard();
virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject);
virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref);
virtual void UnregisterMetadatable(Metadatable const&);
virtual void RemoveXmlIdForElement(Metadatable const&);
/** register i_rCopy as a copy of i_rSource */
MetadatableClipboard & RegisterCopyClipboard(Metadatable & i_rCopy,
beans::StringPair const & i_rReference,
const bool i_isLatent);
/** get the Metadatable that links i_rObject to its origin registry */
MetadatableClipboard const* SourceLink(Metadatable const& i_rObject);
private:
virtual bool LookupXmlId(const Metadatable& i_xObject,
::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const;
virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const;
/** create a Clipboard Metadatable for i_rObject. */
::boost::shared_ptr<MetadatableClipboard> CreateClipboard(
const bool i_isInContent);
struct XmlIdRegistry_Impl;
::std::auto_ptr<XmlIdRegistry_Impl> m_pImpl;
};
//=============================================================================
// XmlIdRegistry
::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard)
{
return i_DocIsClipboard
? static_cast<XmlIdRegistry*>( new XmlIdRegistryClipboard )
: static_cast<XmlIdRegistry*>( new XmlIdRegistryDocument );
}
XmlIdRegistry::XmlIdRegistry()
{
}
XmlIdRegistry::~XmlIdRegistry()
{
}
::com::sun::star::uno::Reference< ::com::sun::star::rdf::XMetadatable > SAL_CALL
XmlIdRegistry::GetElementByMetadataReference(
const beans::StringPair & i_rReference) const
{
Metadatable* pObject( LookupElement(i_rReference.First,
i_rReference.Second) );
return pObject ? pObject->MakeUnoObject() : 0;
}
beans::StringPair
XmlIdRegistry::GetXmlIdForElement(const Metadatable& i_rObject) const
{
::rtl::OUString path;
::rtl::OUString idref;
if (LookupXmlId(i_rObject, path, idref))
{
if (LookupElement(path, idref) == &i_rObject)
{
return beans::StringPair(path, idref);
}
}
return beans::StringPair();
}
/// generate unique xml:id
template< typename T >
/*static*/ ::rtl::OUString create_id(const
::std::hash_map< ::rtl::OUString, T, ::rtl::OUStringHash > & i_rXmlIdMap)
{
static rtlRandomPool s_Pool( rtl_random_createPool() );
const ::rtl::OUString prefix( ::rtl::OUString::createFromAscii(s_prefix) );
typename ::std::hash_map< ::rtl::OUString, T, ::rtl::OUStringHash >
::const_iterator iter;
::rtl::OUString id;
do
{
sal_Int32 n;
rtl_random_getBytes(s_Pool, & n, sizeof(n));
id = prefix + ::rtl::OUString::valueOf(static_cast<sal_Int32>(abs(n)));
iter = i_rXmlIdMap.find(id);
}
while (iter != i_rXmlIdMap.end());
return id;
}
//=============================================================================
// Document XML ID Registry (_Impl)
/// element list
typedef ::std::list< Metadatable* > XmlIdList_t;
/// Idref -> (content.xml element list, styles.xml element list)
typedef ::std::hash_map< ::rtl::OUString,
::std::pair< XmlIdList_t, XmlIdList_t >, ::rtl::OUStringHash > XmlIdMap_t;
/// pointer hash template
template<typename T> struct PtrHash
{
size_t operator() (T const * i_pT) const
{
return reinterpret_cast<size_t>(i_pT);
}
};
/// element -> (stream name, idref)
typedef ::std::hash_map< const Metadatable*,
::std::pair< ::rtl::OUString, ::rtl::OUString>, PtrHash<Metadatable> >
XmlIdReverseMap_t;
struct XmlIdRegistryDocument::XmlIdRegistry_Impl
{
XmlIdRegistry_Impl()
: m_XmlIdMap(), m_XmlIdReverseMap() { }
bool TryInsertMetadatable(Metadatable& i_xObject,
const ::rtl::OUString & i_rStream, const ::rtl::OUString & i_rIdref);
bool LookupXmlId(const Metadatable& i_xObject,
::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const;
Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const;
const XmlIdList_t * LookupElementList(
const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const;
XmlIdList_t * LookupElementList(
const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref)
{
return const_cast<XmlIdList_t*>(
const_cast<const XmlIdRegistry_Impl*>(this)
->LookupElementList(i_rStreamName, i_rIdref));
}
XmlIdMap_t m_XmlIdMap;
XmlIdReverseMap_t m_XmlIdReverseMap;
};
// -------------------------------------------------------------------
static void
rmIter(XmlIdMap_t & i_rXmlIdMap, XmlIdMap_t::iterator const& i_rIter,
::rtl::OUString const & i_rStream, Metadatable const& i_rObject)
{
if (i_rIter != i_rXmlIdMap.end())
{
XmlIdList_t & rList( isContentFile(i_rStream)
? i_rIter->second.first : i_rIter->second.second );
rList.remove(&const_cast<Metadatable&>(i_rObject));
if (i_rIter->second.first.empty() && i_rIter->second.second.empty())
{
i_rXmlIdMap.erase(i_rIter);
}
}
}
// -------------------------------------------------------------------
const XmlIdList_t *
XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementList(
const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const
{
const XmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
if (iter != m_XmlIdMap.end())
{
OSL_ENSURE(!iter->second.first.empty() || !iter->second.second.empty(),
"null entry in m_XmlIdMap");
return (isContentFile(i_rStreamName))
? &iter->second.first
: &iter->second.second;
}
else
{
return 0;
}
}
Metadatable*
XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const
{
if (!isValidXmlId(i_rStreamName, i_rIdref))
{
throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
"illegal XmlId"), 0, 0);
}
const XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) );
if (pList)
{
const XmlIdList_t::const_iterator iter(
::std::find_if(pList->begin(), pList->end(),
::boost::bind(
::std::logical_not<bool>(),
::boost::bind(
::std::logical_or<bool>(),
::boost::bind( &Metadatable::IsInUndo, _1 ),
::boost::bind( &Metadatable::IsInClipboard, _1 )
) ) ) );
if (iter != pList->end())
{
return *iter;
}
}
return 0;
}
bool
XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
const Metadatable& i_rObject,
::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const
{
const XmlIdReverseMap_t::const_iterator iter(
m_XmlIdReverseMap.find(&i_rObject) );
if (iter != m_XmlIdReverseMap.end())
{
OSL_ENSURE(!iter->second.first.equalsAscii(""),
"null stream in m_XmlIdReverseMap");
OSL_ENSURE(!iter->second.second.equalsAscii(""),
"null id in m_XmlIdReverseMap");
o_rStream = iter->second.first;
o_rIdref = iter->second.second;
return true;
}
else
{
return false;
}
}
bool
XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
Metadatable & i_rObject,
const ::rtl::OUString & i_rStreamName, const ::rtl::OUString & i_rIdref)
{
const bool bContent( isContentFile(i_rStreamName) );
OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
"invalid stream");
XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) );
if (pList)
{
if (pList->empty())
{
pList->push_back( &i_rObject );
return true;
}
else
{
// this is only called from TryRegister now, so check
// if all elements in the list are deleted (in undo) or
// placeholders, then "steal" the id from them
if ( pList->end() == ::std::find_if(pList->begin(), pList->end(),
::boost::bind(
::std::logical_not<bool>(),
::boost::bind(
::std::logical_or<bool>(),
::boost::bind( &Metadatable::IsInUndo, _1 ),
::boost::bind( &Metadatable::IsInClipboard, _1 )
) ) ) )
{
// ??? this is not undoable
// pList->clear();
// pList->push_back( &i_rObject );
pList->push_front( &i_rObject );
return true;
}
else
{
return false;
}
}
}
else
{
m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() )
: ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) )));
return true;
}
}
//=============================================================================
// Document XML ID Registry
XmlIdRegistryDocument::XmlIdRegistryDocument()
: m_pImpl( new XmlIdRegistry_Impl )
{
}
static void
removeLink(Metadatable* i_pObject)
{
OSL_ENSURE(i_pObject, "null in list ???");
if (!i_pObject) return;
if (i_pObject->IsInClipboard())
{
MetadatableClipboard* pLink(
dynamic_cast<MetadatableClipboard*>( i_pObject ) );
OSL_ENSURE(pLink, "IsInClipboard, but no MetadatableClipboard ?");
if (pLink)
{
pLink->OriginNoLongerInBusinessAnymore();
}
}
}
XmlIdRegistryDocument::~XmlIdRegistryDocument()
{
// notify all list elements that are actually in the clipboard
for (XmlIdMap_t::iterator iter(m_pImpl->m_XmlIdMap.begin());
iter != m_pImpl->m_XmlIdMap.end(); ++iter)
{
::std::for_each(iter->second.first.begin(), iter->second.first.end(),
removeLink);
::std::for_each(iter->second.second.begin(), iter->second.second.end(),
removeLink);
}
}
bool
XmlIdRegistryDocument::LookupXmlId(
const Metadatable& i_rObject,
::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const
{
return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref);
}
Metadatable*
XmlIdRegistryDocument::LookupElement(
const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const
{
return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
}
bool
XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable & i_rObject,
::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref)
{
OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject,
::rtl::OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(),
::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr());
OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
"TryRegisterMetadatable called for MetadatableUndo?");
OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
"TryRegisterMetadatable called for MetadatableClipboard?");
if (!isValidXmlId(i_rStreamName, i_rIdref))
{
throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
"illegal XmlId"), 0, 0);
}
if (i_rObject.IsInContent()
? !isContentFile(i_rStreamName)
: !isStylesFile(i_rStreamName))
{
throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
"illegal XmlId: wrong stream"), 0, 0);
}
::rtl::OUString old_path;
::rtl::OUString old_idref;
m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
if (old_path == i_rStreamName && old_idref == i_rIdref)
{
return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
}
XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
if (!old_idref.equalsAscii(""))
{
old_id = m_pImpl->m_XmlIdMap.find(old_idref);
OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
}
if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
{
rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
m_pImpl->m_XmlIdReverseMap[&i_rObject] =
::std::make_pair(i_rStreamName, i_rIdref);
return true;
}
else
{
return false;
}
}
void
XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
{
OSL_TRACE("RegisterMetadatableAndCreateID: %p\n", &i_rObject);
OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
"RegisterMetadatableAndCreateID called for MetadatableUndo?");
OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
"RegisterMetadatableAndCreateID called for MetadatableClipboard?");
const bool isInContent( i_rObject.IsInContent() );
const ::rtl::OUString stream( ::rtl::OUString::createFromAscii(
isInContent ? s_content : s_styles ) );
// check if we have a latent xmlid, and if yes, remove it
::rtl::OUString old_path;
::rtl::OUString old_idref;
m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
if (!old_idref.equalsAscii(""))
{
old_id = m_pImpl->m_XmlIdMap.find(old_idref);
OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
if (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject)
{
return;
}
else
{
// remove latent xmlid
rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
}
}
// create id
const ::rtl::OUString id( create_id(m_pImpl->m_XmlIdMap) );
OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
"created id is in use");
m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() )
: ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) )));
m_pImpl->m_XmlIdReverseMap[&i_rObject] = ::std::make_pair(stream, id);
}
void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable& i_rObject)
{
OSL_TRACE("UnregisterMetadatable: %p\n", &i_rObject);
::rtl::OUString path;
::rtl::OUString idref;
if (!m_pImpl->LookupXmlId(i_rObject, path, idref))
{
OSL_ENSURE(false, "unregister: no xml id?");
return;
}
const XmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
if (iter != m_pImpl->m_XmlIdMap.end())
{
rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
}
}
void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable& i_rObject)
{
OSL_TRACE("RemoveXmlIdForElement: %p\n", &i_rObject);
const XmlIdReverseMap_t::iterator iter(
m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
if (iter != m_pImpl->m_XmlIdReverseMap.end())
{
OSL_ENSURE(!iter->second.second.equalsAscii(""),
"null id in m_XmlIdReverseMap");
m_pImpl->m_XmlIdReverseMap.erase(iter);
}
}
// -------------------------------------------------------------------
void XmlIdRegistryDocument::RegisterCopy(Metadatable const& i_rSource,
Metadatable & i_rCopy, const bool i_bCopyPrecedesSource)
{
OSL_TRACE("RegisterCopy: %p -> %p (%d)\n",
&i_rSource, &i_rCopy, i_bCopyPrecedesSource);
// potential sources: clipboard, undo array, splitNode
// assumption: stream change can only happen via clipboard, and is handled
// by Metadatable::RegisterAsCopyOf
OSL_ENSURE(i_rSource.IsInUndo() || i_rCopy.IsInUndo() ||
(i_rSource.IsInContent() == i_rCopy.IsInContent()),
"RegisterCopy: not in same stream?");
::rtl::OUString path;
::rtl::OUString idref;
if (!m_pImpl->LookupXmlId( i_rSource, path, idref ))
{
OSL_ENSURE(false, "no xml id?");
return;
}
XmlIdList_t * pList ( m_pImpl->LookupElementList(path, idref) );
OSL_ENSURE( ::std::find( pList->begin(), pList->end(), &i_rCopy )
== pList->end(), "copy already registered???");
XmlIdList_t::iterator srcpos(
::std::find( pList->begin(), pList->end(), &i_rSource ) );
OSL_ENSURE(srcpos != pList->end(), "source not in list???");
if (srcpos == pList->end())
{
return;
}
if (i_bCopyPrecedesSource)
{
pList->insert( srcpos, &i_rCopy );
}
else
{
// for undo push_back does not work! must insert right after source
pList->insert( ++srcpos, &i_rCopy );
}
m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
::std::make_pair(path, idref)));
}
::boost::shared_ptr<MetadatableUndo>
XmlIdRegistryDocument::CreateUndo(Metadatable const& i_rObject)
{
OSL_TRACE("CreateUndo: %p\n", &i_rObject);
return ::boost::shared_ptr<MetadatableUndo>(
new MetadatableUndo(i_rObject.IsInContent()) );
}
/*
i_rMerged is both a source and the target node of the merge
i_rOther is the other source, and will be deleted after the merge
dimensions: none|latent|actual empty|nonempty
i_rMerged(1) i_rOther(2) result
*|empty *|empty => 1|2 (arbitrary)
*|empty *|nonempty => 2
*|nonempty *|empty => 1
none|nonempty none|nonempty => none
none|nonempty latent|nonempty => 2
latent|nonempty none|nonempty => 1
latent|nonempty latent|nonempty => 1|2
*|nonempty actual|nonempty => 2
actual|nonempty *|nonempty => 1
actual|nonempty actual|nonempty => 1|2
*/
void
XmlIdRegistryDocument::JoinMetadatables(
Metadatable & i_rMerged, Metadatable const & i_rOther)
{
OSL_TRACE("JoinMetadatables: %p <- %p\n", &i_rMerged, &i_rOther);
bool mergedOwnsRef;
::rtl::OUString path;
::rtl::OUString idref;
if (m_pImpl->LookupXmlId(i_rMerged, path, idref))
{
mergedOwnsRef = (m_pImpl->LookupElement(path, idref) == &i_rMerged);
}
else
{
OSL_ENSURE(false, "JoinMetadatables: no xmlid?");
return;
}
if (!mergedOwnsRef)
{
i_rMerged.RemoveMetadataReference();
i_rMerged.RegisterAsCopyOf(i_rOther, true);
return;
}
// other cases: merged has actual ref and is nonempty,
// other has latent/actual ref and is nonempty: other loses => nothing to do
}
//=============================================================================
// Clipboard XML ID Registry (_Impl)
struct RMapEntry
{
RMapEntry() : m_pLink() { }
RMapEntry(::rtl::OUString const& i_rStream,
::rtl::OUString const& i_rXmlId,
::boost::shared_ptr<MetadatableClipboard> const& i_pLink
= ::boost::shared_ptr<MetadatableClipboard>())
: m_Stream(i_rStream), m_XmlId(i_rXmlId), m_pLink(i_pLink)
{}
::rtl::OUString m_Stream;
::rtl::OUString m_XmlId;
// this would have been an auto_ptr, if only that would have compiled...
::boost::shared_ptr<MetadatableClipboard> m_pLink;
};
/// element -> (stream name, idref, source)
typedef ::std::hash_map< const Metadatable*,
struct RMapEntry,
PtrHash<Metadatable> >
ClipboardXmlIdReverseMap_t;
/// Idref -> (content.xml element, styles.xml element)
typedef ::std::hash_map< ::rtl::OUString,
::std::pair< Metadatable*, Metadatable* >, ::rtl::OUStringHash >
ClipboardXmlIdMap_t;
struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
{
XmlIdRegistry_Impl()
: m_XmlIdMap(), m_XmlIdReverseMap() { }
bool TryInsertMetadatable(Metadatable& i_xObject,
const ::rtl::OUString & i_rStream, const ::rtl::OUString & i_rIdref);
bool LookupXmlId(const Metadatable& i_xObject,
::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref,
MetadatableClipboard const* &o_rpLink) const;
Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const;
Metadatable* const* LookupEntry(const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const;
Metadatable* * LookupEntry(const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref)
{
return const_cast<Metadatable**>(
const_cast<const XmlIdRegistry_Impl*>(this)
->LookupEntry(i_rStreamName, i_rIdref));
}
ClipboardXmlIdMap_t m_XmlIdMap;
ClipboardXmlIdReverseMap_t m_XmlIdReverseMap;
};
// -------------------------------------------------------------------
static void
rmIter(ClipboardXmlIdMap_t & i_rXmlIdMap,
ClipboardXmlIdMap_t::iterator const& i_rIter,
::rtl::OUString const & i_rStream, Metadatable const& i_rObject)
{
if (i_rIter != i_rXmlIdMap.end())
{
Metadatable *& rMeta = isContentFile(i_rStream)
? i_rIter->second.first : i_rIter->second.second;
if (rMeta == &i_rObject)
{
rMeta = 0;
}
if (!i_rIter->second.first && !i_rIter->second.second)
{
i_rXmlIdMap.erase(i_rIter);
}
}
}
// -------------------------------------------------------------------
Metadatable* const*
XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const
{
if (!isValidXmlId(i_rStreamName, i_rIdref))
{
throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
"illegal XmlId"), 0, 0);
}
const ClipboardXmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
if (iter != m_XmlIdMap.end())
{
OSL_ENSURE(iter->second.first || iter->second.second,
"null entry in m_XmlIdMap");
return (isContentFile(i_rStreamName))
? &iter->second.first
: &iter->second.second;
}
else
{
return 0;
}
}
Metadatable*
XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const
{
Metadatable * const * ppEntry = LookupEntry(i_rStreamName, i_rIdref);
return ppEntry ? *ppEntry : 0;
}
bool
XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
const Metadatable& i_rObject,
::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref,
MetadatableClipboard const* &o_rpLink) const
{
const ClipboardXmlIdReverseMap_t::const_iterator iter(
m_XmlIdReverseMap.find(&i_rObject) );
if (iter != m_XmlIdReverseMap.end())
{
OSL_ENSURE(!iter->second.m_Stream.equalsAscii(""),
"null stream in m_XmlIdReverseMap");
OSL_ENSURE(!iter->second.m_XmlId.equalsAscii(""),
"null id in m_XmlIdReverseMap");
o_rStream = iter->second.m_Stream;
o_rIdref = iter->second.m_XmlId;
o_rpLink = iter->second.m_pLink.get();
return true;
}
else
{
return false;
}
}
bool
XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
Metadatable & i_rObject,
const ::rtl::OUString & i_rStreamName, const ::rtl::OUString & i_rIdref)
{
bool bContent( isContentFile(i_rStreamName) );
OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
"invalid stream");
//wntmsci12 won't parse this:
// Metadatable ** ppEntry( LookupEntry(i_rStreamName, i_rIdref) );
Metadatable ** ppEntry = LookupEntry(i_rStreamName, i_rIdref);
if (ppEntry)
{
if (*ppEntry)
{
return false;
}
else
{
*ppEntry = &i_rObject;
return true;
}
}
else
{
m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(0) )
: ::std::make_pair( static_cast<Metadatable*>(0), &i_rObject )));
return true;
}
}
//=============================================================================
// Clipboard XML ID Registry
XmlIdRegistryClipboard::XmlIdRegistryClipboard()
: m_pImpl( new XmlIdRegistry_Impl )
{
}
XmlIdRegistryClipboard::~XmlIdRegistryClipboard()
{
}
bool
XmlIdRegistryClipboard::LookupXmlId(
const Metadatable& i_rObject,
::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const
{
const MetadatableClipboard * pLink;
return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref, pLink);
}
Metadatable*
XmlIdRegistryClipboard::LookupElement(
const ::rtl::OUString & i_rStreamName,
const ::rtl::OUString & i_rIdref) const
{
return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
}
bool
XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable & i_rObject,
::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref)
{
OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject,
::rtl::OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(),
::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr());
OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
"TryRegisterMetadatable called for MetadatableUndo?");
OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
"TryRegisterMetadatable called for MetadatableClipboard?");
if (!isValidXmlId(i_rStreamName, i_rIdref))
{
throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
"illegal XmlId"), 0, 0);
}
if (i_rObject.IsInContent()
? !isContentFile(i_rStreamName)
: !isStylesFile(i_rStreamName))
{
throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
"illegal XmlId: wrong stream"), 0, 0);
}
::rtl::OUString old_path;
::rtl::OUString old_idref;
const MetadatableClipboard * pLink;
m_pImpl->LookupXmlId(i_rObject, old_path, old_idref, pLink);
if (old_path == i_rStreamName && old_idref == i_rIdref)
{
return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
}
ClipboardXmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
if (!old_idref.equalsAscii(""))
{
old_id = m_pImpl->m_XmlIdMap.find(old_idref);
OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
}
if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
{
rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
m_pImpl->m_XmlIdReverseMap[&i_rObject] =
RMapEntry(i_rStreamName, i_rIdref);
return true;
}
else
{
return false;
}
}
void
XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
{
OSL_TRACE("RegisterMetadatableAndCreateID: %p\n", &i_rObject);
OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
"RegisterMetadatableAndCreateID called for MetadatableUndo?");
OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
"RegisterMetadatableAndCreateID called for MetadatableClipboard?");
bool isInContent( i_rObject.IsInContent() );
::rtl::OUString stream( ::rtl::OUString::createFromAscii(
isInContent ? s_content : s_styles ) );
::rtl::OUString old_path;
::rtl::OUString old_idref;
LookupXmlId(i_rObject, old_path, old_idref);
if (!old_idref.equalsAscii("") &&
(m_pImpl->LookupElement(old_path, old_idref) == &i_rObject))
{
return;
}
// create id
const ::rtl::OUString id( create_id(m_pImpl->m_XmlIdMap) );
OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
"created id is in use");
m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(0) )
: ::std::make_pair( static_cast<Metadatable*>(0), &i_rObject )));
// N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
// MetadatableClipboard and thus the latent XmlId here
m_pImpl->m_XmlIdReverseMap[&i_rObject] = RMapEntry(stream, id);
}
void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable& i_rObject)
{
OSL_TRACE("UnregisterMetadatable: %p\n", &i_rObject);
::rtl::OUString path;
::rtl::OUString idref;
const MetadatableClipboard * pLink;
if (!m_pImpl->LookupXmlId(i_rObject, path, idref, pLink))
{
OSL_ENSURE(false, "unregister: no xml id?");
return;
}
const ClipboardXmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
if (iter != m_pImpl->m_XmlIdMap.end())
{
rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
}
}
void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable& i_rObject)
{
OSL_TRACE("RemoveXmlIdForElement: %p\n", &i_rObject);
ClipboardXmlIdReverseMap_t::iterator iter(
m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
if (iter != m_pImpl->m_XmlIdReverseMap.end())
{
OSL_ENSURE(!iter->second.m_XmlId.equalsAscii(""),
"null id in m_XmlIdReverseMap");
m_pImpl->m_XmlIdReverseMap.erase(iter);
}
}
// -------------------------------------------------------------------
::boost::shared_ptr<MetadatableClipboard>
XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent)
{
OSL_TRACE("CreateClipboard: \n");
return ::boost::shared_ptr<MetadatableClipboard>(
new MetadatableClipboard(i_isInContent) );
}
MetadatableClipboard &
XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable & i_rCopy,
beans::StringPair const & i_rReference,
const bool i_isLatent)
{
OSL_TRACE("RegisterCopyClipboard: %p -> "/*"%p"*/"(%s#%s) (%d)\n",
/*&i_rSource,*/ &i_rCopy,
::rtl::OUStringToOString(i_rReference.First,
RTL_TEXTENCODING_UTF8).getStr(),
::rtl::OUStringToOString(i_rReference.Second,
RTL_TEXTENCODING_UTF8).getStr(),
i_isLatent);
// N.B.: when copying to the clipboard, the selection is always inserted
// into the body, even if the source is a header/footer!
// so we do not check whether the stream is right in this function
if (!isValidXmlId(i_rReference.First, i_rReference.Second))
{
throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
"illegal XmlId"), 0, 0);
}
if (!i_isLatent)
{
// this should succeed assuming clipboard has a single source document
const bool success( m_pImpl->TryInsertMetadatable(i_rCopy,
i_rReference.First, i_rReference.Second) );
OSL_ENSURE(success, "RegisterCopyClipboard: TryInsert failed?");
(void) success;
}
const ::boost::shared_ptr<MetadatableClipboard> pLink(
CreateClipboard( isContentFile(i_rReference.First)) );
m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
RMapEntry(i_rReference.First, i_rReference.Second, pLink)));
return *pLink.get();
}
MetadatableClipboard const*
XmlIdRegistryClipboard::SourceLink(Metadatable const& i_rObject)
{
::rtl::OUString path;
::rtl::OUString idref;
const MetadatableClipboard * pLink( 0 );
m_pImpl->LookupXmlId(i_rObject, path, idref, pLink);
return pLink;
}
//=============================================================================
// Metadatable mixin
Metadatable::~Metadatable()
{
RemoveMetadataReference();
}
void Metadatable::RemoveMetadataReference()
{
try
{
if (m_pReg)
{
m_pReg->UnregisterMetadatable( *this );
m_pReg->RemoveXmlIdForElement( *this );
m_pReg = 0;
}
}
catch (uno::Exception &)
{
OSL_ENSURE(false, "Metadatable::RemoveMetadataReference: exception");
}
}
// ::com::sun::star::rdf::XMetadatable:
beans::StringPair
Metadatable::GetMetadataReference() const
{
if (m_pReg)
{
return m_pReg->GetXmlIdForElement(*this);
}
return beans::StringPair();
}
void
Metadatable::SetMetadataReference(
const ::com::sun::star::beans::StringPair & i_rReference)
{
if (i_rReference.Second.equalsAscii(""))
{
RemoveMetadataReference();
}
else
{
::rtl::OUString streamName( i_rReference.First );
if (streamName.equalsAscii(""))
{
// handle empty stream name as auto-detect.
// necessary for importing flat file format.
streamName = ::rtl::OUString::createFromAscii(
IsInContent() ? s_content : s_styles );
}
XmlIdRegistry & rReg( dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
if (rReg.TryRegisterMetadatable(*this, streamName, i_rReference.Second))
{
m_pReg = &rReg;
}
else
{
throw lang::IllegalArgumentException(
::rtl::OUString::createFromAscii("Metadatable::"
"SetMetadataReference: argument is invalid"), /*this*/0, 0);
}
}
}
void Metadatable::EnsureMetadataReference()
{
XmlIdRegistry& rReg(
m_pReg ? *m_pReg : dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
rReg.RegisterMetadatableAndCreateID( *this );
m_pReg = &rReg;
}
const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject)
{
return const_cast< Metadatable& >( i_rObject ).GetRegistry();
}
void
Metadatable::RegisterAsCopyOf(Metadatable const & i_rSource,
const bool i_bCopyPrecedesSource)
{
OSL_ENSURE(typeid(*this) == typeid(i_rSource)
|| typeid(i_rSource) == typeid(MetadatableUndo)
|| typeid(*this) == typeid(MetadatableUndo)
|| typeid(i_rSource) == typeid(MetadatableClipboard)
|| typeid(*this) == typeid(MetadatableClipboard),
"RegisterAsCopyOf element with different class?");
OSL_ENSURE(!this->m_pReg, "RegisterAsCopyOf called on element with XmlId?");
if (this->m_pReg)
{
RemoveMetadataReference();
}
try
{
if (i_rSource.m_pReg)
{
XmlIdRegistry & rReg(
dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
if (i_rSource.m_pReg == &rReg)
{
OSL_ENSURE(!IsInClipboard(),
"RegisterAsCopy: both in clipboard?");
if (!IsInClipboard())
{
XmlIdRegistryDocument & rRegDoc(
dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
rRegDoc.RegisterCopy(i_rSource, *this,
i_bCopyPrecedesSource);
this->m_pReg = &rRegDoc;
}
return;
}
// source is in different document
XmlIdRegistryDocument * pRegDoc(
dynamic_cast<XmlIdRegistryDocument *>(&rReg) );
XmlIdRegistryClipboard * pRegClp(
dynamic_cast<XmlIdRegistryClipboard*>(&rReg) );
if (pRegClp)
{
beans::StringPair SourceRef(
i_rSource.m_pReg->GetXmlIdForElement(i_rSource) );
bool isLatent( SourceRef.Second.equalsAscii("") );
XmlIdRegistryDocument * pSourceRegDoc(
dynamic_cast<XmlIdRegistryDocument*>(i_rSource.m_pReg) );
OSL_ENSURE(pSourceRegDoc, "RegisterAsCopyOf: 2 clipboards?");
if (!pSourceRegDoc) return;
// this is a copy _to_ the clipboard
if (isLatent)
{
pSourceRegDoc->LookupXmlId(i_rSource,
SourceRef.First, SourceRef.Second);
}
Metadatable & rLink(
pRegClp->RegisterCopyClipboard(*this, SourceRef, isLatent));
this->m_pReg = pRegClp;
// register as copy in the non-clipboard registry
pSourceRegDoc->RegisterCopy(i_rSource, rLink,
false); // i_bCopyPrecedesSource);
rLink.m_pReg = pSourceRegDoc;
}
else if (pRegDoc)
{
XmlIdRegistryClipboard * pSourceRegClp(
dynamic_cast<XmlIdRegistryClipboard*>(i_rSource.m_pReg) );
OSL_ENSURE(pSourceRegClp,
"RegisterAsCopyOf: 2 non-clipboards?");
if (!pSourceRegClp) return;
const MetadatableClipboard * pLink(
pSourceRegClp->SourceLink(i_rSource) );
// may happen if src got its id via UNO call
if (!pLink) return;
// only register copy if clipboard content is from this SwDoc!
if (pLink && (&GetRegistryConst(*pLink) == pRegDoc))
{
// this is a copy _from_ the clipboard; check if the
// element is still in the same stream
// N.B.: we check the stream of pLink, not of i_rSource!
bool srcInContent( pLink->IsInContent() );
bool tgtInContent( this->IsInContent() );
if (srcInContent == tgtInContent)
{
pRegDoc->RegisterCopy(*pLink, *this,
true); // i_bCopyPrecedesSource);
this->m_pReg = pRegDoc;
}
// otherwise: stream change! do not register!
}
}
else
{
OSL_ENSURE(false, "neither RegDoc nor RegClp cannot happen");
}
#if 0
{
//FIXME: do we need this at all???
XmlIdRegistryDocument & rRegDoc(
dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
{
if (rRegDoc.TryRegisterMetadatable(*this, SourceRef))
{
this->m_pReg = &rRegDoc;
}
}
}
#endif
}
}
catch (uno::Exception &)
{
OSL_ENSURE(false, "Metadatable::RegisterAsCopyOf: exception");
}
}
::boost::shared_ptr<MetadatableUndo> Metadatable::CreateUndo() const
{
OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
try
{
if (!IsInClipboard() && !IsInUndo() && m_pReg)
{
XmlIdRegistryDocument * pRegDoc(
dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
::boost::shared_ptr<MetadatableUndo> pUndo(
pRegDoc->CreateUndo(*this) );
pRegDoc->RegisterCopy(*this, *pUndo, false);
pUndo->m_pReg = pRegDoc;
return pUndo;
}
}
catch (uno::Exception &)
{
OSL_ENSURE(false, "Metadatable::CreateUndo: exception");
}
return ::boost::shared_ptr<MetadatableUndo>();
}
::boost::shared_ptr<MetadatableUndo> Metadatable::CreateUndoForDelete()
{
::boost::shared_ptr<MetadatableUndo> const pUndo( CreateUndo() );
RemoveMetadataReference();
return pUndo;
}
void Metadatable::RestoreMetadata(
::boost::shared_ptr<MetadatableUndo> const& i_pUndo)
{
OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
OSL_ENSURE(!IsInClipboard(),
"RestoreMetadata called for object in clipboard?");
if (IsInClipboard() || IsInUndo()) return;
RemoveMetadataReference();
if (i_pUndo)
{
this->RegisterAsCopyOf(*i_pUndo, true);
}
}
void
Metadatable::JoinMetadatable(Metadatable const & i_rOther,
const bool i_isMergedEmpty, const bool i_isOtherEmpty)
{
OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
OSL_ENSURE(!IsInClipboard(),
"JoinMetadatables called for object in clipboard?");
if (IsInClipboard() || IsInUndo()) return;
if (i_isOtherEmpty && !i_isMergedEmpty)
{
// other is empty, thus loses => nothing to do
return;
}
if (i_isMergedEmpty && !i_isOtherEmpty)
{
this->RemoveMetadataReference();
this->RegisterAsCopyOf(i_rOther, true);
return;
}
if (!i_rOther.m_pReg)
{
// other doesn't have xmlid, thus loses => nothing to do
return;
}
if (!m_pReg)
{
this->RegisterAsCopyOf(i_rOther, true);
// assumption: i_rOther will be deleted, so don't unregister it here
return;
}
try
{
XmlIdRegistryDocument * pRegDoc(
dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?");
if (pRegDoc)
{
pRegDoc->JoinMetadatables(*this, i_rOther);
}
}
catch (uno::Exception &)
{
OSL_ENSURE(false, "Metadatable::JoinMetadatable: exception");
}
}
//=============================================================================
// XMetadatable mixin
// ::com::sun::star::rdf::XNode:
::rtl::OUString SAL_CALL MetadatableMixin::getStringValue()
throw (::com::sun::star::uno::RuntimeException)
{
return getNamespace() + getLocalName();
}
// ::com::sun::star::rdf::XURI:
::rtl::OUString SAL_CALL MetadatableMixin::getLocalName()
throw (::com::sun::star::uno::RuntimeException)
{
::vos::OGuard aGuard( Application::GetSolarMutex() );
beans::StringPair mdref( getMetadataReference() );
if (!mdref.Second.getLength())
{
ensureMetadataReference(); // N.B.: side effect!
mdref = getMetadataReference();
}
::rtl::OUStringBuffer buf;
buf.append(mdref.First);
buf.append(static_cast<sal_Unicode>('#'));
buf.append(mdref.Second);
return buf.makeStringAndClear();
}
::rtl::OUString SAL_CALL MetadatableMixin::getNamespace()
throw (::com::sun::star::uno::RuntimeException)
{
::vos::OGuard aGuard( Application::GetSolarMutex() );
const uno::Reference< frame::XModel > xModel( GetModel() );
const uno::Reference< rdf::XURI > xDMA( xModel, uno::UNO_QUERY_THROW );
return xDMA->getStringValue();
}
// ::com::sun::star::rdf::XMetadatable:
beans::StringPair SAL_CALL
MetadatableMixin::getMetadataReference()
throw (uno::RuntimeException)
{
::vos::OGuard aGuard( Application::GetSolarMutex() );
Metadatable *const pObject( GetCoreObject() );
if (!pObject)
{
throw uno::RuntimeException(
::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
"MetadatableMixin: cannot get core object; not inserted?")),
*this);
}
return pObject->GetMetadataReference();
}
void SAL_CALL
MetadatableMixin::setMetadataReference(
const beans::StringPair & i_rReference)
throw (uno::RuntimeException, lang::IllegalArgumentException)
{
::vos::OGuard aGuard( Application::GetSolarMutex() );
Metadatable *const pObject( GetCoreObject() );
if (!pObject)
{
throw uno::RuntimeException(
::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
"MetadatableMixin: cannot get core object; not inserted?")),
*this);
}
return pObject->SetMetadataReference(i_rReference);
}
void SAL_CALL MetadatableMixin::ensureMetadataReference()
throw (uno::RuntimeException)
{
::vos::OGuard aGuard( Application::GetSolarMutex() );
Metadatable *const pObject( GetCoreObject() );
if (!pObject)
{
throw uno::RuntimeException(
::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
"MetadatableMixin: cannot get core object; not inserted?")),
*this);
}
return pObject->EnsureMetadataReference();
}
} // namespace sfx2
//=============================================================================
#if OSL_DEBUG_LEVEL > 1
#include <stdio.h>
static void dump(sfx2::XmlIdList_t * pList)
#ifdef GCC
__attribute__ ((unused))
#endif
;
static void dump(sfx2::XmlIdList_t * pList)
{
fprintf(stderr, "\nXmlIdList(%p): ", pList);
for (sfx2::XmlIdList_t::iterator i = pList->begin(); i != pList->end(); ++i)
{
fprintf(stderr, "%p ", *i);
}
fprintf(stderr, "\n");
}
#endif