diff options
Diffstat (limited to 'sfx2/source')
-rw-r--r-- | sfx2/source/bastyp/progress.cxx | 2 | ||||
-rw-r--r-- | sfx2/source/dialog/dinfdlg.cxx | 331 | ||||
-rw-r--r-- | sfx2/source/dialog/templdlg.cxx | 7 | ||||
-rw-r--r-- | sfx2/source/doc/DocumentMetadataAccess.cxx | 1414 | ||||
-rw-r--r-- | sfx2/source/doc/Metadatable.cxx | 1860 | ||||
-rw-r--r-- | sfx2/source/doc/docfile.cxx | 2 | ||||
-rw-r--r-- | sfx2/source/doc/makefile.mk | 7 | ||||
-rw-r--r-- | sfx2/source/doc/objserv.cxx | 4 | ||||
-rw-r--r-- | sfx2/source/doc/objstor.cxx | 1 | ||||
-rw-r--r-- | sfx2/source/doc/objxtor.cxx | 20 | ||||
-rw-r--r-- | sfx2/source/doc/sfxbasemodel.cxx | 394 |
11 files changed, 3724 insertions, 318 deletions
diff --git a/sfx2/source/bastyp/progress.cxx b/sfx2/source/bastyp/progress.cxx index 22a3be63dc19..62bc5533e1a9 100644 --- a/sfx2/source/bastyp/progress.cxx +++ b/sfx2/source/bastyp/progress.cxx @@ -117,7 +117,7 @@ struct SfxProgress_Impl : public SfxCancellable #define aTypeLibInfo aProgressTypeLibImpl //======================================================================== -#if (_MSC_VER < 1300) +#if defined(_MSC_VER) && (_MSC_VER < 1300) inline ULONG Get10ThSec() { #if defined (MSC) && defined (WIN) diff --git a/sfx2/source/dialog/dinfdlg.cxx b/sfx2/source/dialog/dinfdlg.cxx index cb442caa6f1f..6de52384a90c 100644 --- a/sfx2/source/dialog/dinfdlg.cxx +++ b/sfx2/source/dialog/dinfdlg.cxx @@ -223,9 +223,9 @@ SfxDocumentInfoItem::SfxDocumentInfoItem() , m_Keywords() , m_Subject() , m_Title() - , bHasTemplate( sal_True ) - , bDeleteUserData( sal_False ) - , bIsUseUserData( sal_True ) + , m_bHasTemplate( sal_True ) + , m_bDeleteUserData( sal_False ) + , m_bUseUserData( sal_True ) { } @@ -253,9 +253,9 @@ SfxDocumentInfoItem::SfxDocumentInfoItem( const String& rFile, i_xDocProps->getKeywords()) ) , m_Subject( i_xDocProps->getSubject() ) , m_Title( i_xDocProps->getTitle() ) - , bHasTemplate( TRUE ) - , bDeleteUserData( sal_False ) - , bIsUseUserData( bIs ) + , m_bHasTemplate( sal_True ) + , m_bDeleteUserData( sal_False ) + , m_bUseUserData( bIs ) { try { @@ -302,15 +302,10 @@ SfxDocumentInfoItem::SfxDocumentInfoItem( const SfxDocumentInfoItem& rItem ) , m_Keywords( rItem.getKeywords() ) , m_Subject( rItem.getSubject() ) , m_Title( rItem.getTitle() ) - , bHasTemplate( rItem.bHasTemplate ) - , bDeleteUserData( rItem.bDeleteUserData ) - , bIsUseUserData( rItem.bIsUseUserData ) + , m_bHasTemplate( rItem.m_bHasTemplate ) + , m_bDeleteUserData( rItem.m_bDeleteUserData ) + , m_bUseUserData( rItem.m_bUseUserData ) { - for (size_t i = 0; i < 4; ++i) { - m_UserDefinedFieldTitles[i] = rItem.getUserDefinedFieldTitle(i); - m_UserDefinedFieldValues[i] = rItem.getUserDefinedFieldValue(i); - } - for ( sal_uInt32 i = 0; i < rItem.m_aCustomProperties.size(); i++ ) { CustomProperty* pProp = new CustomProperty( rItem.m_aCustomProperties[i]->m_sName, @@ -384,8 +379,9 @@ void SfxDocumentInfoItem::resetUserData(const ::rtl::OUString & i_rAuthor) //------------------------------------------------------------------------ -void SfxDocumentInfoItem::updateDocumentInfo( - const uno::Reference<document::XDocumentProperties>& i_xDocProps) const +void SfxDocumentInfoItem::UpdateDocumentInfo( + const uno::Reference<document::XDocumentProperties>& i_xDocProps, + bool i_bDoNotUpdateUserDefined) const { if (isAutoloadEnabled()) { i_xDocProps->setAutoloadSecs(getAutoloadDelay()); @@ -410,6 +406,15 @@ void SfxDocumentInfoItem::updateDocumentInfo( i_xDocProps->setSubject(getSubject()); i_xDocProps->setTitle(getTitle()); + // this is necessary in case of replaying a recorded macro: + // in this case, the macro may contain the 4 old user-defined DocumentInfo + // fields, but not any of the DocumentInfo properties; + // as a consequence, most of the UserDefined properties of the + // DocumentProperties would be summarily deleted here, which does not + // seem like a good idea. + if (i_bDoNotUpdateUserDefined) + return; + try { Reference< beans::XPropertyContainer > xContainer = i_xDocProps->getUserDefinedProperties(); @@ -442,24 +447,24 @@ void SfxDocumentInfoItem::updateDocumentInfo( //------------------------------------------------------------------------ -BOOL SfxDocumentInfoItem::IsDeleteUserData() const +sal_Bool SfxDocumentInfoItem::IsDeleteUserData() const { - return bDeleteUserData; + return m_bDeleteUserData; } -void SfxDocumentInfoItem::SetDeleteUserData( BOOL bSet ) +void SfxDocumentInfoItem::SetDeleteUserData( sal_Bool bSet ) { - bDeleteUserData = bSet; + m_bDeleteUserData = bSet; } -BOOL SfxDocumentInfoItem::IsUseUserData() const +sal_Bool SfxDocumentInfoItem::IsUseUserData() const { - return bIsUseUserData; + return m_bUseUserData; } -void SfxDocumentInfoItem::SetUseUserData( BOOL bSet ) +void SfxDocumentInfoItem::SetUseUserData( sal_Bool bSet ) { - bIsUseUserData = bSet; + m_bUseUserData = bSet; } std::vector< CustomProperty* > SfxDocumentInfoItem::GetCustomProperties() const @@ -488,38 +493,11 @@ void SfxDocumentInfoItem::AddCustomProperty( const ::rtl::OUString& sName, const m_aCustomProperties.push_back( pProp ); } -::rtl::OUString SfxDocumentInfoItem::getUserDefinedFieldTitle(size_t i_ix) const -{ - DBG_ASSERT(i_ix < 4, "SfxDocumentInfoItem: invalid index"); - return m_UserDefinedFieldTitles[i_ix]; -} - -::rtl::OUString SfxDocumentInfoItem::getUserDefinedFieldValue(size_t i_ix) const -{ - DBG_ASSERT(i_ix < 4, "SfxDocumentInfoItem: invalid index"); - return m_UserDefinedFieldValues[i_ix]; -} - -void SfxDocumentInfoItem::setUserDefinedFieldTitle(size_t i_ix, - ::rtl::OUString i_val) -{ - DBG_ASSERT(i_ix < 4, "SfxDocumentInfoItem: invalid index"); - m_UserDefinedFieldTitles[i_ix] = i_val; -} - -void SfxDocumentInfoItem::setUserDefinedFieldValue(size_t i_ix, - ::rtl::OUString i_val) -{ - DBG_ASSERT(i_ix < 4, "SfxDocumentInfoItem: invalid index"); - m_UserDefinedFieldValues[i_ix] = i_val; -} - sal_Bool SfxDocumentInfoItem::QueryValue( Any& rVal, BYTE nMemberId ) const { String aValue; sal_Int32 nValue = 0; sal_Bool bValue = sal_False; - BOOL bField = FALSE; BOOL bIsInt = FALSE; BOOL bIsString = FALSE; nMemberId &= ~CONVERT_TWIPS; @@ -562,43 +540,6 @@ sal_Bool SfxDocumentInfoItem::QueryValue( Any& rVal, BYTE nMemberId ) const bIsString = TRUE; aValue = getTitle(); break; - case MID_DOCINFO_FIELD1: - case MID_DOCINFO_FIELD2: - case MID_DOCINFO_FIELD3: - case MID_DOCINFO_FIELD4: - bField = TRUE; - // no break here - case MID_DOCINFO_FIELD1TITLE: - case MID_DOCINFO_FIELD2TITLE: - case MID_DOCINFO_FIELD3TITLE: - case MID_DOCINFO_FIELD4TITLE: - { - bIsString = TRUE; - USHORT nSub = MID_DOCINFO_FIELD1TITLE; - if ( bField ) - { - nSub = MID_DOCINFO_FIELD1; - } - if ( bField ) - { - DBG_ASSERT( nMemberId == MID_DOCINFO_FIELD1 || - nMemberId == MID_DOCINFO_FIELD2 || - nMemberId == MID_DOCINFO_FIELD3 || - nMemberId == MID_DOCINFO_FIELD4, - "SfxDocumentInfoItem:Anpassungsfehler" ); - aValue = getUserDefinedFieldValue( nMemberId - nSub ); - } - else - { - DBG_ASSERT( nMemberId == MID_DOCINFO_FIELD1TITLE || - nMemberId == MID_DOCINFO_FIELD2TITLE || - nMemberId == MID_DOCINFO_FIELD3TITLE || - nMemberId == MID_DOCINFO_FIELD4TITLE, - "SfxDocumentInfoItem:Anpassungsfehler" ); - aValue = getUserDefinedFieldTitle( nMemberId - nSub ); - } - break; - } default: DBG_ERROR("Wrong MemberId!"); return sal_False; @@ -673,28 +614,6 @@ sal_Bool SfxDocumentInfoItem::PutValue( const Any& rVal, BYTE nMemberId ) if ( bRet ) setTitle(aValue); break; - case MID_DOCINFO_FIELD1TITLE: - case MID_DOCINFO_FIELD2TITLE: - case MID_DOCINFO_FIELD3TITLE: - case MID_DOCINFO_FIELD4TITLE: - { - bRet = (rVal >>= aValue); - if ( bRet ) - setUserDefinedFieldTitle( - nMemberId - MID_DOCINFO_FIELD1TITLE, String(aValue)); - break; - } - case MID_DOCINFO_FIELD1: - case MID_DOCINFO_FIELD2: - case MID_DOCINFO_FIELD3: - case MID_DOCINFO_FIELD4: - { - bRet = (rVal >>= aValue); - if ( bRet ) - setUserDefinedFieldValue( - nMemberId - MID_DOCINFO_FIELD1, String(aValue)); - break; - } default: DBG_ERROR("Wrong MemberId!"); return sal_False; @@ -1178,7 +1097,7 @@ void SfxDocumentPage::Reset( const SfxItemSet& rSet ) aFileValFt.SetText( aURL.GetPartBeforeLastName() ); // handle access data - BOOL bIsUseUserData = pInfoItem->IsUseUserData(); + sal_Bool m_bUseUserData = pInfoItem->IsUseUserData(); LocaleDataWrapper aLocaleWrapper( ::comphelper::getProcessServiceFactory(), Application::GetSettings().GetLocale() ); aCreateValFt.SetText( ConvertDateTime_Impl( pInfoItem->getAuthor(), pInfoItem->getCreationDate(), aLocaleWrapper ) ); @@ -1193,7 +1112,7 @@ void SfxDocumentPage::Reset( const SfxItemSet& rSet ) aPrintValFt.SetText( ConvertDateTime_Impl( pInfoItem->getPrintedBy(), aTime, aLocaleWrapper ) ); const long nTime = pInfoItem->getEditingDuration(); - if( bIsUseUserData ) + if ( m_bUseUserData ) { const Time aT( nTime/3600, (nTime%3600)/60, nTime%60 ); aTimeLogValFt.SetText( aLocaleWrapper.getDuration( aT ) ); @@ -1201,7 +1120,7 @@ void SfxDocumentPage::Reset( const SfxItemSet& rSet ) pInfoItem->getEditingCycles() ) ); } - TriState eState = (TriState)bIsUseUserData; + TriState eState = (TriState)m_bUseUserData; if ( STATE_DONTKNOW == eState ) aUseUserDataCB.EnableTriState( TRUE ); @@ -1558,192 +1477,6 @@ int SfxInternetPage::DeactivatePage( SfxItemSet* /*pSet*/ ) //------------------------------------------------------------------------ -SfxDocumentUserPage::SfxDocumentUserPage( Window* pParent, - const SfxItemSet& rItemSet ) : - - SfxTabPage( pParent, SfxResId( TP_DOCINFOUSER ), rItemSet ), - - bLabelModified ( FALSE ), - aInfo1Ft ( this, SfxResId( FT_INFO1 ) ), - aInfo1Ed ( this, SfxResId( ED_INFO1 ) ), - aInfo2Ft ( this, SfxResId( FT_INFO2 ) ), - aInfo2Ed ( this, SfxResId( ED_INFO2 ) ), - aInfo3Ft ( this, SfxResId( FT_INFO3 ) ), - aInfo3Ed ( this, SfxResId( ED_INFO3 ) ), - aInfo4Ft ( this, SfxResId( FT_INFO4 ) ), - aInfo4Ed ( this, SfxResId( ED_INFO4 ) ), - aEditLabelBtn ( this, SfxResId( BTN_EDITLABEL ) ), - - pInfoItem ( NULL ) - -{ - FreeResource(); - //increase button width in case of long labels - Size aButtonSize = aEditLabelBtn.GetOutputSizePixel(); - sal_Int32 nTextWidth = aEditLabelBtn.GetTextWidth(aEditLabelBtn.GetText()); - //add some additional space - sal_Int32 nDiff = nTextWidth + 4 - aButtonSize.Width(); - if( nDiff > 0) - { - Point aPos(aEditLabelBtn.GetPosPixel()); - aPos.X() -= nDiff; - aButtonSize.Width() += nDiff; - aEditLabelBtn.SetPosSizePixel(aPos, aButtonSize); - } - - aEditLabelBtn.SetClickHdl( LINK( this, SfxDocumentUserPage, EditLabelHdl ) ); -} - -//------------------------------------------------------------------------ - -IMPL_LINK( SfxDocumentUserPage, EditLabelHdl, PushButton *, pPushButton ) -{ - (void)pPushButton; //unused - SfxDocInfoEditDlg* pDlg = new SfxDocInfoEditDlg( this ); - pDlg->SetText1( GetLabelText_Impl( &aInfo1Ft ) ); - pDlg->SetText2( GetLabelText_Impl( &aInfo2Ft ) ); - pDlg->SetText3( GetLabelText_Impl( &aInfo3Ft ) ); - pDlg->SetText4( GetLabelText_Impl( &aInfo4Ft ) ); - - if ( RET_OK == pDlg->Execute() ) - { - SetLabelText_Impl( &aInfo1Ft, pDlg->GetText1() ); - SetLabelText_Impl( &aInfo2Ft, pDlg->GetText2() ); - SetLabelText_Impl( &aInfo3Ft, pDlg->GetText3() ); - SetLabelText_Impl( &aInfo4Ft, pDlg->GetText4() ); - bLabelModified = TRUE; - } - delete pDlg; - return 0; -} - -//------------------------------------------------------------------------ - -String SfxDocumentUserPage::GetLabelText_Impl( FixedText* pLabel ) -{ - DBG_ASSERT( pLabel, "SfxDocumentUserPage::SetLabelText_Impl(): invalid label" ); - String aLabel = pLabel->GetText(); - aLabel.Erase( 0, aLabel.Search( ' ' ) + 1 ); - return aLabel; -} - -//------------------------------------------------------------------------ - -void SfxDocumentUserPage::SetLabelText_Impl( FixedText* pLabel, const String& rNewLabel ) -{ - String aLabel( '~' ); - sal_Int32 nNumber = 0; - if ( &aInfo1Ft == pLabel ) - nNumber = 1; - else if ( &aInfo2Ft == pLabel ) - nNumber = 2; - else if ( &aInfo3Ft == pLabel ) - nNumber = 3; - else if ( &aInfo4Ft == pLabel ) - nNumber = 4; - DBG_ASSERT( nNumber > 0, "SfxDocumentUserPage::SetLabelText_Impl(): wrong label" ); - aLabel += String::CreateFromInt32( nNumber ); - aLabel += String( DEFINE_CONST_UNICODE(": ") ); - aLabel += rNewLabel; - DBG_ASSERT( pLabel, "SfxDocumentUserPage::SetLabelText_Impl(): invalid label" ); - pLabel->SetText( aLabel ); -} - -//------------------------------------------------------------------------ - -SfxTabPage* SfxDocumentUserPage::Create( Window* pParent, const SfxItemSet& rItemSet ) -{ - return new SfxDocumentUserPage(pParent, rItemSet); -} - -//------------------------------------------------------------------------ - -BOOL SfxDocumentUserPage::FillItemSet( SfxItemSet& rSet ) -{ - const BOOL bMod = bLabelModified || - aInfo1Ed.IsModified() || aInfo2Ed.IsModified() || - aInfo3Ed.IsModified() || aInfo4Ed.IsModified(); - if ( !bMod ) - return FALSE; - - const SfxPoolItem* pItem = NULL; - SfxDocumentInfoItem* pInfo = NULL; - SfxTabDialog* pDlg = GetTabDialog(); - const SfxItemSet* pExSet = NULL; - - if ( pDlg ) - pExSet = pDlg->GetExampleSet(); - - if ( pExSet && SFX_ITEM_SET != pExSet->GetItemState( SID_DOCINFO, TRUE, &pItem ) ) - pInfo = pInfoItem; - else if ( pItem ) - pInfo = new SfxDocumentInfoItem( *(const SfxDocumentInfoItem*)pItem ); - - if ( !pInfo ) - { - DBG_ERRORFILE( "SfxDocumentUserPage::FillItemSet(): no item found" ); - return FALSE; - } - - if ( bLabelModified || aInfo1Ed.IsModified() ) - { - XubString aTitle = GetLabelText_Impl( &aInfo1Ft ); - pInfo->setUserDefinedFieldTitle( 0, aTitle ); - pInfo->setUserDefinedFieldValue( 0, aInfo1Ed.GetText() ); - } - if ( bLabelModified || aInfo2Ed.IsModified() ) - { - XubString aTitle = GetLabelText_Impl( &aInfo2Ft ); - pInfo->setUserDefinedFieldTitle( 1, aTitle ); - pInfo->setUserDefinedFieldValue( 1, aInfo2Ed.GetText() ); - } - if ( bLabelModified || aInfo3Ed.IsModified() ) - { - XubString aTitle = GetLabelText_Impl( &aInfo3Ft ); - pInfo->setUserDefinedFieldTitle( 2, aTitle ); - pInfo->setUserDefinedFieldValue( 2, aInfo3Ed.GetText() ); - } - if ( bLabelModified || aInfo4Ed.IsModified() ) - { - XubString aTitle = GetLabelText_Impl( &aInfo4Ft ); - pInfo->setUserDefinedFieldTitle( 3, aTitle ); - pInfo->setUserDefinedFieldValue( 3, aInfo4Ed.GetText() ); - } - rSet.Put( *pInfo ); - if ( pInfo != pInfoItem ) - delete pInfo; - return bMod; -} - -//------------------------------------------------------------------------ - -void SfxDocumentUserPage::Reset(const SfxItemSet &rSet) -{ - pInfoItem = &(SfxDocumentInfoItem&)rSet.Get( SID_DOCINFO ); - - SetLabelText_Impl( &aInfo1Ft, pInfoItem->getUserDefinedFieldTitle(0) ); - aInfo1Ed.SetText( pInfoItem->getUserDefinedFieldValue(0) ); - SetLabelText_Impl( &aInfo2Ft, pInfoItem->getUserDefinedFieldTitle(1) ); - aInfo2Ed.SetText( pInfoItem->getUserDefinedFieldValue(1) ); - SetLabelText_Impl( &aInfo3Ft, pInfoItem->getUserDefinedFieldTitle(2) ); - aInfo3Ed.SetText( pInfoItem->getUserDefinedFieldValue(2) ); - SetLabelText_Impl( &aInfo4Ft, pInfoItem->getUserDefinedFieldTitle(3) ); - aInfo4Ed.SetText( pInfoItem->getUserDefinedFieldValue(3) ); - bLabelModified = FALSE; - - SFX_ITEMSET_ARG( &rSet, pROItem, SfxBoolItem, SID_DOC_READONLY, FALSE ); - if ( pROItem && pROItem->GetValue() ) - { - aInfo1Ed.SetReadOnly( TRUE ); - aInfo2Ed.SetReadOnly( TRUE ); - aInfo3Ed.SetReadOnly( TRUE ); - aInfo4Ed.SetReadOnly( TRUE ); - aEditLabelBtn.Disable(); - } -} - -//------------------------------------------------------------------------ - SfxDocumentInfoDialog::SfxDocumentInfoDialog( Window* pParent, const SfxItemSet& rItemSet ) : diff --git a/sfx2/source/dialog/templdlg.cxx b/sfx2/source/dialog/templdlg.cxx index c0ee288eb882..7dfe5e4b6855 100644 --- a/sfx2/source/dialog/templdlg.cxx +++ b/sfx2/source/dialog/templdlg.cxx @@ -79,7 +79,6 @@ #include "imgmgr.hxx" #include "helpid.hrc" #include "appdata.hxx" -#include "objshimp.hxx" #include <sfx2/viewfrm.hxx> #include <comphelper/configurationhelper.hxx> @@ -883,7 +882,7 @@ void SfxCommonTemplateDialog_Impl::ReadResource() nActFilter = pCurObjShell ? static_cast< USHORT >( LoadFactoryStyleFilter( pCurObjShell ) ) : 0xFFFF; if ( pCurObjShell && 0xFFFF == nActFilter ) - nActFilter = pCurObjShell->pImp->nStyleFilter; + nActFilter = pCurObjShell->GetAutoStyleFilterIndex(); // Einfuegen in die Toolbox // umgekehrte Reihenfolge, da immer vorne eingefuegt wird. @@ -1504,7 +1503,7 @@ void SfxCommonTemplateDialog_Impl::Update_Impl() CheckItem( nActFamily, TRUE ); nActFilter = static_cast< USHORT >( LoadFactoryStyleFilter( pDocShell ) ); if ( 0xFFFF == nActFilter ) - nActFilter = pDocShell->pImp->nStyleFilter; + nActFilter = pDocShell->GetAutoStyleFilterIndex(); nAppFilter = pItem->GetValue(); if(!pTreeBox) @@ -1674,7 +1673,7 @@ void SfxCommonTemplateDialog_Impl::FilterSelect( SfxObjectShell *pDocShell = pViewFrame->GetObjectShell(); if (pDocShell) { - pDocShell->Get_Impl()->nStyleFilter = nActFilter; + pDocShell->SetAutoStyleFilterIndex(nActFilter); SaveFactoryStyleFilter( pDocShell, nActFilter ); } diff --git a/sfx2/source/doc/DocumentMetadataAccess.cxx b/sfx2/source/doc/DocumentMetadataAccess.cxx new file mode 100644 index 000000000000..112183a4efbe --- /dev/null +++ b/sfx2/source/doc/DocumentMetadataAccess.cxx @@ -0,0 +1,1414 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: DocumentMetadataAccess.cxx,v $ + * $Revision: 1.1.2.9 $ + * + * 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. + * + ************************************************************************/ + +#include "precompiled_sfx2.hxx" + +#include <sfx2/DocumentMetadataAccess.hxx> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/task/ErrorCodeIOException.hpp> +#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp> +#include <com/sun/star/rdf/FileFormat.hpp> +#include <com/sun/star/rdf/URIs.hpp> +#include <com/sun/star/rdf/Statement.hpp> +#include <com/sun/star/rdf/Literal.hpp> +#include <com/sun/star/rdf/URI.hpp> +#include <com/sun/star/rdf/Repository.hpp> + +#include <rtl/uuid.h> +#include <rtl/ustrbuf.hxx> + +#include <comphelper/interaction.hxx> +#include <comphelper/makesequence.hxx> +#include <comphelper/mediadescriptor.hxx> +#include <comphelper/sequenceasvector.hxx> +#include <comphelper/storagehelper.hxx> + +#include <sfx2/docfile.hxx> +#include <sfx2/XmlIdRegistry.hxx> + +#include <libxml/tree.h> // for xmlValidateNCName + +#include <boost/bind.hpp> +#include <boost/shared_array.hpp> +#include <boost/tuple/tuple.hpp> + +#include <vector> +#include <set> +#include <map> +#include <functional> +#include <algorithm> + +#include <unotools/ucbhelper.hxx> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XUriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarPkgUrlReferenceFactory.hpp> + + +/* + Note: in the context of this implementation, all rdf.QueryExceptions and + rdf.RepositoryExceptions are RuntimeExceptions, and will be reported as such. + + This implementation assumes that it is only used with ODF documents, not mere + ODF packages. In other words, we enforce that metadata files must not be + called reserved names. + */ + +using namespace ::com::sun::star; + +namespace sfx2 { + + +bool isValidNCName(::rtl::OUString const & i_rIdref) +{ + const ::rtl::OString id( + ::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) ); + return !(xmlValidateNCName( + reinterpret_cast<const unsigned char*>(id.getStr()), 0)); +} + +//////////////////////////////////////////////////////////////////////////// + +static const char s_content [] = "content.xml"; +static const char s_styles [] = "styles.xml"; +static const char s_meta [] = "meta.xml"; +static const char s_settings[] = "settings.xml"; +static const char s_manifest[] = "manifest.rdf"; +static const char s_rdfxml [] = "application/rdf+xml"; +static const char s_odfmime [] = "application/vnd.oasis.opendocument."; + +//////////////////////////////////////////////////////////////////////////// + +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); +} + +static bool isReservedFile(::rtl::OUString const & i_rPath) +{ + return isContentFile(i_rPath) + || isStylesFile(i_rPath) + || i_rPath.equalsAscii(s_meta) + || i_rPath.equalsAscii(s_settings); +} + +//////////////////////////////////////////////////////////////////////////// + +uno::Reference<rdf::XURI> createBaseURI( + uno::Reference<uno::XComponentContext> const & i_xContext, + uno::Reference<embed::XStorage> const & i_xStorage, + ::rtl::OUString const & i_rPkgURI, ::rtl::OUString const & i_rSubDocument) +{ + if (!i_xContext.is() || !i_xStorage.is() || !i_rPkgURI.getLength()) { + throw uno::RuntimeException(); + } + + const uno::Reference<lang::XMultiComponentFactory> xServiceFactory( + i_xContext->getServiceManager(), uno::UNO_SET_THROW); + const uno::Reference<uri::XUriReferenceFactory> xUriFactory( + xServiceFactory->createInstanceWithContext( + ::rtl::OUString::createFromAscii( + "com.sun.star.uri.UriReferenceFactory"), i_xContext), + uno::UNO_QUERY_THROW); + uno::Reference< uri::XUriReference > xBaseURI; + + const uno::Reference< uri::XUriReference > xPkgURI( + xUriFactory->parse(i_rPkgURI), uno::UNO_SET_THROW ); + xPkgURI->clearFragment(); + // need to know whether the storage is a FileSystemStorage + // XServiceInfo would be better, but it is not implemented +// if ( i_rPkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(i_rPkgURI) ) + if (true) { + xBaseURI.set( xPkgURI, uno::UNO_SET_THROW ); +#if 0 + } else { + const uno::Reference<uri::XVndSunStarPkgUrlReferenceFactory> + xPkgUriFactory( xServiceFactory->createInstanceWithContext( + ::rtl::OUString::createFromAscii( + "com.sun.star.uri.VndSunStarPkgUrlReferenceFactory"), + i_xContext), + uno::UNO_QUERY_THROW); + xBaseURI.set( xPkgUriFactory->createVndSunStarPkgUrlReference(xPkgURI), + uno::UNO_SET_THROW ); +#endif + } + ::rtl::OUStringBuffer buf; + if (!xBaseURI->getUriReference().endsWithAsciiL("/", 1)) + { + const sal_Int32 count( xBaseURI->getPathSegmentCount() ); + if (count > 0) + { + const ::rtl::OUString last( xBaseURI->getPathSegment(count - 1) ); + buf.append(last); + } + buf.append(static_cast<sal_Unicode>('/')); + } + if (i_rSubDocument.getLength()) + { + buf.append(i_rSubDocument); + buf.append(static_cast<sal_Unicode>('/')); + } + const ::rtl::OUString Path(buf.makeStringAndClear()); + if (Path.getLength()) + { + const uno::Reference< uri::XUriReference > xPathURI( + xUriFactory->parse(Path), uno::UNO_SET_THROW ); + xBaseURI.set( + xUriFactory->makeAbsolute(xBaseURI, xPathURI, + true, uri::RelativeUriExcessParentSegments_ERROR), + uno::UNO_SET_THROW); + } + + return rdf::URI::create(i_xContext, xBaseURI->getUriReference()); +} + +//////////////////////////////////////////////////////////////////////////// + +struct DocumentMetadataAccess_Impl +{ + // note: these are all initialized in constructor, and loadFromStorage + const uno::Reference<uno::XComponentContext> m_xContext; + const IXmlIdRegistrySupplier & m_rXmlIdRegistrySupplier; + uno::Reference<rdf::XURI> m_xBaseURI; + uno::Reference<rdf::XRepository> m_xRepository; + uno::Reference<rdf::XNamedGraph> m_xManifest; + DocumentMetadataAccess_Impl( + uno::Reference<uno::XComponentContext> const& i_xContext, + IXmlIdRegistrySupplier const & i_rRegistrySupplier) + : m_xContext(i_xContext) + , m_rXmlIdRegistrySupplier(i_rRegistrySupplier) + , m_xBaseURI() + , m_xRepository() + , m_xManifest() + { + OSL_ENSURE(m_xContext.is(), "context null"); + } +}; + +// this is... a hack. +template<sal_Int16 Constant> +/*static*/ uno::Reference<rdf::XURI> +getURI(uno::Reference< uno::XComponentContext > const & i_xContext) +{ + static uno::Reference< rdf::XURI > xURI( + rdf::URI::createKnown(i_xContext, Constant), uno::UNO_QUERY_THROW); + return xURI; +} + + +/** would storing the file to a XStorage succeed? */ +static bool isFileNameValid(const ::rtl::OUString & i_rFileName) +{ + if (i_rFileName.getLength() <= 0) return false; + if (i_rFileName[0] == '/') return false; // no absolute paths! + sal_Int32 idx(0); + do { + const ::rtl::OUString segment( + i_rFileName.getToken(0, static_cast<sal_Unicode> ('/'), idx) ); + if (!segment.getLength() || // no empty segments + segment.equalsAscii(".") || // no . segments + segment.equalsAscii("..") || // no .. segments + !::comphelper::OStorageHelper::IsValidZipEntryFileName( + segment, sal_False)) // no invalid characters + return false; + } while (idx >= 0); + return true; +} + +/** split a uri hierarchy into first segment and rest */ +static bool +splitPath(::rtl::OUString const & i_rPath, + ::rtl::OUString & o_rDir, ::rtl::OUString& o_rRest) +{ + const sal_Int32 idx(i_rPath.indexOf(static_cast<sal_Unicode>('/'))); + if (idx < 0 || idx >= i_rPath.getLength()) { + o_rDir = ::rtl::OUString(); + o_rRest = i_rPath; + return true; + } else if (idx == 0 || idx == i_rPath.getLength() - 1) { + // input must not start or end with '/' + return false; + } else { + o_rDir = (i_rPath.copy(0, idx)); + o_rRest = (i_rPath.copy(idx+1)); + return true; + } +} + +static bool +splitXmlId(::rtl::OUString const & i_XmlId, + ::rtl::OUString & o_StreamName, ::rtl::OUString& o_Idref ) +{ + const sal_Int32 idx(i_XmlId.indexOf(static_cast<sal_Unicode>('#'))); + if ((idx <= 0) || (idx >= i_XmlId.getLength() - 1)) { + return false; + } else { + o_StreamName = (i_XmlId.copy(0, idx)); + o_Idref = (i_XmlId.copy(idx+1)); + return isValidXmlId(o_StreamName, o_Idref); + } +} + +//////////////////////////////////////////////////////////////////////////// + +static uno::Reference<rdf::XURI> +getURIForStream(struct DocumentMetadataAccess_Impl& i_rImpl, + ::rtl::OUString const& i_rPath) +{ + const uno::Reference<rdf::XURI> xURI( + rdf::URI::createNS( i_rImpl.m_xContext, + i_rImpl.m_xBaseURI->getStringValue(), i_rPath), + uno::UNO_SET_THROW); + return xURI; +} + +/** add statements declaring i_xResource to be a file of type i_xType with + path i_rPath to manifest, with optional additional types i_pTypes */ +static void +addFile(struct DocumentMetadataAccess_Impl & i_rImpl, + uno::Reference<rdf::XURI> const& i_xType, + ::rtl::OUString const & i_rPath, + const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes = 0) +{ + try { + const uno::Reference<rdf::XURI> xURI( getURIForStream( + i_rImpl, i_rPath) ); + + i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(), + getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), + xURI.get()); + i_rImpl.m_xManifest->addStatement(xURI.get(), + getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), + i_xType.get()); + if (i_pTypes) { + for (sal_Int32 i = 0; i < i_pTypes->getLength(); ++i) { + i_rImpl.m_xManifest->addStatement(xURI.get(), + getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), + (*i_pTypes)[i].get()); + } + } + } catch (uno::RuntimeException &) { + throw; + } catch (uno::Exception & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii( + "addFile: exception"), /*this*/0, uno::makeAny(e)); + } +} + +/** add content.xml or styles.xml to manifest */ +static bool +addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl & i_rImpl, + const ::rtl::OUString & i_rPath) +{ + uno::Reference<rdf::XURI> xType; + if (isContentFile(i_rPath)) { + xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext)); + } else if (isStylesFile(i_rPath)) { + xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext)); + } else { + return false; + } + addFile(i_rImpl, xType.get(), i_rPath); + return true; +} + +/** add metadata file to manifest */ +static void +addMetadataFileImpl(struct DocumentMetadataAccess_Impl & i_rImpl, + const ::rtl::OUString & i_rPath, + const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes) +{ + addFile(i_rImpl, + getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext), + i_rPath, &i_rTypes); +} + +/** remove a file from the manifest */ +static void +removeFile(struct DocumentMetadataAccess_Impl & i_rImpl, + uno::Reference<rdf::XURI> const& i_xPart) +{ + if (!i_xPart.is()) throw uno::RuntimeException(); + try { + i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI.get(), + getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), + i_xPart.get()); + i_rImpl.m_xManifest->removeStatements(i_xPart.get(), + getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 0); + } catch (uno::RuntimeException &) { + throw; + } catch (uno::Exception & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii("removeFile: exception"), + 0, uno::makeAny(e)); + } +} + +static ::std::vector< uno::Reference< rdf::XURI > > +getAllParts(struct DocumentMetadataAccess_Impl & i_rImpl) +{ + ::std::vector< uno::Reference< rdf::XURI > > ret; + try { + const uno::Reference<container::XEnumeration> xEnum( + i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI.get(), + getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), 0), + uno::UNO_SET_THROW); + while (xEnum->hasMoreElements()) { + rdf::Statement stmt; + if (!(xEnum->nextElement() >>= stmt)) { + throw uno::RuntimeException(); + } + const uno::Reference<rdf::XURI> xPart(stmt.Object, + uno::UNO_QUERY); + if (!xPart.is()) continue; + ret.push_back(xPart); + } + return ret; + } catch (uno::RuntimeException &) { + throw; + } catch (uno::Exception & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii("getAllParts: exception"), + 0, uno::makeAny(e)); + } +} + +static bool +isPartOfType(struct DocumentMetadataAccess_Impl & i_rImpl, + uno::Reference<rdf::XURI> const & i_xPart, + uno::Reference<rdf::XURI> const & i_xType) +{ + if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException(); + try { + const uno::Reference<container::XEnumeration> xEnum( + i_rImpl.m_xManifest->getStatements(i_xPart.get(), + getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), + i_xType.get()), + uno::UNO_SET_THROW); + return (xEnum->hasMoreElements()); + } catch (uno::RuntimeException &) { + throw; + } catch (uno::Exception & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii("isPartOfType: exception"), + 0, uno::makeAny(e)); + } +} + +//////////////////////////////////////////////////////////////////////////// + +static ucb::InteractiveAugmentedIOException +mkException( ::rtl::OUString const & i_rMessage, + ucb::IOErrorCode const i_ErrorCode, + ::rtl::OUString const & i_rUri, ::rtl::OUString const & i_rResource) +{ + ucb::InteractiveAugmentedIOException iaioe; + iaioe.Message = i_rMessage; + iaioe.Classification = task::InteractionClassification_ERROR; + iaioe.Code = i_ErrorCode; + + const beans::PropertyValue uriProp(::rtl::OUString::createFromAscii("Uri"), + -1, uno::makeAny(i_rUri), static_cast<beans::PropertyState>(0)); + const beans::PropertyValue rnProp( + ::rtl::OUString::createFromAscii("ResourceName"), + -1, uno::makeAny(i_rResource), static_cast<beans::PropertyState>(0)); + iaioe.Arguments = ::comphelper::makeSequence( + uno::makeAny(uriProp), uno::makeAny(rnProp)); + return iaioe; +} + +/** error handling policy. + <p>If a handler is given, ask it how to proceed: + <ul><li>(default:) cancel import, raise exception</li> + <li>ignore the error and continue</li> + <li>retry the action that led to the error</li></ul></p> + N.B.: must not be called before DMA is fully initalized! + @returns true iff caller should retry + */ +static bool +handleError( ucb::InteractiveAugmentedIOException const & i_rException, + const uno::Reference<task::XInteractionHandler> & i_xHandler) +{ + if (!i_xHandler.is()) { + throw lang::WrappedTargetException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromStorage: exception"), + /* *this*/ 0, uno::makeAny(i_rException)); + } + + ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest( + new ::comphelper::OInteractionRequest(uno::makeAny(i_rException)) ); + ::rtl::Reference< ::comphelper::OInteractionRetry > pRetry( + new ::comphelper::OInteractionRetry ); + ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( + new ::comphelper::OInteractionApprove ); + ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort( + new ::comphelper::OInteractionAbort ); + /* this does not seem to work + if (i_rException.Code != ucb::IOErrorCode_WRONG_FORMAT) { + pRequest->addContinuation( pRetry.get() ); + } + */ + pRequest->addContinuation( pApprove.get() ); + pRequest->addContinuation( pAbort.get() ); + // actually call the handler + i_xHandler->handle( pRequest.get() ); + if (pRetry->wasSelected()) { + return true; + } else if (pApprove->wasSelected()) { + return false; + } else { + OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?"); + throw lang::WrappedTargetException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromStorage: exception"), + /* *this*/ 0, uno::makeAny(i_rException)); + } +} + +/** check if storage has content.xml/styles.xml; + e.g. ODB files seem to only have content.xml */ +static void +collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage, + ::rtl::OUString i_Path, + std::set< ::rtl::OUString > & o_rFiles) +{ + static ::rtl::OUString content(::rtl::OUString::createFromAscii(s_content)); + static ::rtl::OUString styles (::rtl::OUString::createFromAscii(s_styles )); + try { + if (i_xStorage->hasByName(content) && + i_xStorage->isStreamElement(content)) + { + o_rFiles.insert(i_Path + content); + } + if (i_xStorage->hasByName(styles) && + i_xStorage->isStreamElement(styles)) + { + o_rFiles.insert(i_Path + styles); + } + } catch (uno::Exception &) { + OSL_TRACE("collectFilesFromStorage: exception?"); + } +} + +/** import a metadata file into repository */ +static void +readStream(struct DocumentMetadataAccess_Impl & i_rImpl, + uno::Reference< embed::XStorage > const & i_xStorage, + ::rtl::OUString const & i_rPath, + ::rtl::OUString const & i_rBaseURI) +{ + ::rtl::OUString dir; + ::rtl::OUString rest; + try { + if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException(); + if (dir.equalsAscii("")) { + if (i_xStorage->isStreamElement(i_rPath)) { + const uno::Reference<io::XStream> xStream( + i_xStorage->openStreamElement(i_rPath, + embed::ElementModes::READ), uno::UNO_SET_THROW); + const uno::Reference<io::XInputStream> xInStream( + xStream->getInputStream(), uno::UNO_SET_THROW ); + const uno::Reference<rdf::XURI> xBaseURI( + rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI)); + const uno::Reference<rdf::XURI> xURI( + rdf::URI::createNS(i_rImpl.m_xContext, + i_rBaseURI, i_rPath)); + i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML, + xInStream, xURI, xBaseURI); + } else { + throw mkException(::rtl::OUString::createFromAscii( + "readStream: is not a stream"), + ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath); + } + } else { + if (i_xStorage->isStorageElement(dir)) { + const uno::Reference<embed::XStorage> xDir( + i_xStorage->openStorageElement(dir, + embed::ElementModes::READ)); + const uno::Reference< beans::XPropertySet > xDirProps(xDir, + uno::UNO_QUERY_THROW); + try { + ::rtl::OUString mimeType; + xDirProps->getPropertyValue( + ::comphelper::MediaDescriptor::PROP_MEDIATYPE() ) + >>= mimeType; + if (mimeType.matchAsciiL(s_odfmime, sizeof(s_odfmime) - 1)) + { + OSL_TRACE("readStream: " + "refusing to recurse into embedded document"); + return; + } + } catch (uno::Exception &) { } + ::rtl::OUStringBuffer buf(i_rBaseURI); + buf.append(dir).append(static_cast<sal_Unicode>('/')); + readStream(i_rImpl, xDir, rest, buf.makeStringAndClear() ); + } else { + throw mkException(::rtl::OUString::createFromAscii( + "readStream: is not a directory"), + ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir); + } + } + } catch (container::NoSuchElementException & e) { + throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH, + i_rBaseURI + i_rPath, i_rPath); + } catch (io::IOException & e) { + throw mkException(e.Message, ucb::IOErrorCode_CANT_READ, + i_rBaseURI + i_rPath, i_rPath); + } catch (rdf::ParseException & e) { + throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT, + i_rBaseURI + i_rPath, i_rPath); + } +} + +/** import a metadata file into repository */ +static void +importFile(struct DocumentMetadataAccess_Impl & i_rImpl, + uno::Reference<embed::XStorage> const & i_xStorage, + ::rtl::OUString const & i_rBaseURI, + uno::Reference<task::XInteractionHandler> const & i_xHandler, + ::rtl::OUString i_rPath) +{ +retry: + try { + readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI); + } catch (ucb::InteractiveAugmentedIOException & e) { + if (handleError(e, i_xHandler)) goto retry; + } catch (uno::RuntimeException &) { + throw; + } catch (uno::Exception & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii("importFile: exception"), + 0, uno::makeAny(e)); + } +} + +/** actually write a metadata file to the storage */ +static void +exportStream(struct DocumentMetadataAccess_Impl & i_rImpl, + uno::Reference< embed::XStorage > const & i_xStorage, + uno::Reference<rdf::XURI> const & i_xGraphName, + ::rtl::OUString const & i_rFileName, + ::rtl::OUString const & i_rBaseURI) +{ + const uno::Reference<io::XStream> xStream( + i_xStorage->openStreamElement(i_rFileName, + embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE), + uno::UNO_SET_THROW); + const uno::Reference< beans::XPropertySet > xStreamProps(xStream, + uno::UNO_QUERY); + if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage + xStreamProps->setPropertyValue( + ::rtl::OUString::createFromAscii("MediaType"), + uno::makeAny(::rtl::OUString::createFromAscii(s_rdfxml))); + } + const uno::Reference<io::XOutputStream> xOutStream( + xStream->getOutputStream(), uno::UNO_SET_THROW ); + const uno::Reference<rdf::XURI> xBaseURI( + rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI)); + i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML, + xOutStream, i_xGraphName, xBaseURI); +} + +/** write a metadata file to the storage */ +static void +writeStream(struct DocumentMetadataAccess_Impl & i_rImpl, + uno::Reference< embed::XStorage > const & i_xStorage, + uno::Reference<rdf::XURI> const & i_xGraphName, + ::rtl::OUString const & i_rPath, + ::rtl::OUString const & i_rBaseURI) +{ + ::rtl::OUString dir; + ::rtl::OUString rest; + if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException(); + try { + if (dir.equalsAscii("")) { + exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath, + i_rBaseURI); + } else { + const uno::Reference<embed::XStorage> xDir( + i_xStorage->openStorageElement(dir, + embed::ElementModes::WRITE)); + const uno::Reference< beans::XPropertySet > xDirProps(xDir, + uno::UNO_QUERY_THROW); + try { + ::rtl::OUString mimeType; + xDirProps->getPropertyValue( + ::comphelper::MediaDescriptor::PROP_MEDIATYPE() ) + >>= mimeType; + if (mimeType.matchAsciiL(s_odfmime, sizeof(s_odfmime) - 1)) { + OSL_TRACE("writeStream: " + "refusing to recurse into embedded document"); + return; + } + } catch (uno::Exception &) { } + ::rtl::OUStringBuffer buf(i_rBaseURI); + buf.append(dir).append(static_cast<sal_Unicode>('/')); + writeStream(i_rImpl, xDir, i_xGraphName, rest, + buf.makeStringAndClear()); + } + const uno::Reference<embed::XTransactedObject> xTransaction( + i_xStorage, uno::UNO_QUERY); + if (xTransaction.is()) { + xTransaction->commit(); + } + } catch (uno::RuntimeException &) { + throw; + } catch (io::IOException &) { + throw; + } +} + +static void +initLoading(struct DocumentMetadataAccess_Impl & i_rImpl, + const uno::Reference< embed::XStorage > & i_xStorage, + const uno::Reference<rdf::XURI> & i_xBaseURI, + const uno::Reference<task::XInteractionHandler> & i_xHandler) +{ +retry: + // clear old data + i_rImpl.m_xManifest.clear(); + // init BaseURI + i_rImpl.m_xBaseURI = i_xBaseURI; + + // create repository + i_rImpl.m_xRepository.clear(); + i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext), + uno::UNO_SET_THROW); + + const ::rtl::OUString manifest ( + ::rtl::OUString::createFromAscii(s_manifest)); + const ::rtl::OUString baseURI( i_xBaseURI->getStringValue() ); + // try to delay raising errors until after initialization is done + uno::Any rterr; + ucb::InteractiveAugmentedIOException iaioe; + bool err(false); + + const uno::Reference <rdf::XURI> xManifest( + getURIForStream(i_rImpl, manifest)); + try { + readStream(i_rImpl, i_xStorage, manifest, baseURI); + } catch (ucb::InteractiveAugmentedIOException & e) { + // no manifest.rdf: this is not an error in ODF < 1.2 + if (!(ucb::IOErrorCode_NOT_EXISTING_PATH == e.Code)) { + iaioe = e; + err = true; + } + } catch (uno::Exception & e) { + rterr <<= e; + } + + // init manifest graph + const uno::Reference<rdf::XNamedGraph> xManifestGraph( + i_rImpl.m_xRepository->getGraph(xManifest)); + i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph : + i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW); + const uno::Reference<container::XEnumeration> xEnum( + i_rImpl.m_xManifest->getStatements(0, + getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), + getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get())); + + // document statement + i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(), + getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), + getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get()); + + OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null"); + OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null"); + OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null"); + + if (rterr.hasValue()) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromStorage: " + "exception"), 0, rterr); + } + + if (err) { + if (handleError(iaioe, i_xHandler)) goto retry; + } +} + +/** init Impl struct */ +static void init(struct DocumentMetadataAccess_Impl & i_rImpl) +{ + try { + + i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph( + getURIForStream(i_rImpl, + ::rtl::OUString::createFromAscii(s_manifest))), + uno::UNO_SET_THROW); + + // insert the document statement + i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(), + getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), + getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get()); + } catch (uno::Exception & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii("init: unexpected exception"), 0, + uno::makeAny(e)); + } + + // add top-level content files + if (!addContentOrStylesFileImpl(i_rImpl, + ::rtl::OUString::createFromAscii(s_content))) { + throw uno::RuntimeException(); + } + if (!addContentOrStylesFileImpl(i_rImpl, + ::rtl::OUString::createFromAscii(s_styles))) { + throw uno::RuntimeException(); + } +} + + +//////////////////////////////////////////////////////////////////////////// + +DocumentMetadataAccess::DocumentMetadataAccess( + uno::Reference< uno::XComponentContext > const & i_xContext, + const IXmlIdRegistrySupplier & i_rRegistrySupplier) + : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier)) +{ + // no initalization: must call loadFrom... +} + +DocumentMetadataAccess::DocumentMetadataAccess( + uno::Reference< uno::XComponentContext > const & i_xContext, + const IXmlIdRegistrySupplier & i_rRegistrySupplier, + ::rtl::OUString const & i_rURI) + : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier)) +{ + OSL_ENSURE(i_rURI.getLength(), "DMA::DMA: no URI given!"); + OSL_ENSURE(i_rURI.endsWithAsciiL("/", 1), "DMA::DMA: URI without / given!"); + if (!i_rURI.endsWithAsciiL("/", 1)) throw uno::RuntimeException(); + m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI)); + m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext), + uno::UNO_SET_THROW); + + // init repository + init(*m_pImpl); + + OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null"); + OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null"); + OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null"); +} + +DocumentMetadataAccess::~DocumentMetadataAccess() +{ +} + + +// ::com::sun::star::rdf::XRepositorySupplier: +uno::Reference< rdf::XRepository > SAL_CALL +DocumentMetadataAccess::getRDFRepository() throw (uno::RuntimeException) +{ + OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized"); + return m_pImpl->m_xRepository; +} + +// ::com::sun::star::rdf::XNode: +::rtl::OUString SAL_CALL +DocumentMetadataAccess::getStringValue() throw (uno::RuntimeException) +{ + return m_pImpl->m_xBaseURI->getStringValue(); +} + +// ::com::sun::star::rdf::XURI: +::rtl::OUString SAL_CALL +DocumentMetadataAccess::getNamespace() throw (uno::RuntimeException) +{ + return m_pImpl->m_xBaseURI->getNamespace(); +} + +::rtl::OUString SAL_CALL +DocumentMetadataAccess::getLocalName() throw (uno::RuntimeException) +{ + return m_pImpl->m_xBaseURI->getLocalName(); +} + +// ::com::sun::star::rdf::XDocumentMetadataAccess: +uno::Reference< rdf::XMetadatable > SAL_CALL +DocumentMetadataAccess::getElementByMetadataReference( + const ::com::sun::star::beans::StringPair & i_rReference) +throw (uno::RuntimeException) +{ + const IXmlIdRegistry * pReg( + m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() ); + if (!pReg) { + throw uno::RuntimeException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::getElementByXmlId: no registry"), *this); + } + return pReg->GetElementByMetadataReference(i_rReference); +} + +uno::Reference< rdf::XMetadatable > SAL_CALL +DocumentMetadataAccess::getElementByURI( + const uno::Reference< rdf::XURI > & i_xURI ) +throw (uno::RuntimeException, lang::IllegalArgumentException) +{ + if (!i_xURI.is()) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::getElementByURI: URI is null"), *this, 0); + } + + const ::rtl::OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() ); + const ::rtl::OUString name( i_xURI->getStringValue() ); + if (!name.match(baseURI)) { + return 0; + } + const ::rtl::OUString relName( name.copy(baseURI.getLength()) ); + ::rtl::OUString path; + ::rtl::OUString idref; + if (!splitXmlId(relName, path, idref)) { + return 0; + } + + return getElementByMetadataReference( beans::StringPair(path, idref) ); +} + + +uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL +DocumentMetadataAccess::getMetadataGraphsWithType( + const uno::Reference<rdf::XURI> & i_xType) +throw (uno::RuntimeException, lang::IllegalArgumentException) +{ + if (!i_xType.is()) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::getMetadataGraphsWithType: " + "type is null"), *this, 0); + } + + ::comphelper::SequenceAsVector< uno::Reference< rdf::XURI > > ret; + const ::std::vector< uno::Reference< rdf::XURI > > parts( + getAllParts(*m_pImpl) ); + ::std::remove_copy_if(parts.begin(), parts.end(), + ::std::back_inserter(ret), + ::boost::bind( + ::std::logical_not<bool>(), + ::boost::bind(&isPartOfType, ::boost::ref(*m_pImpl), _1, i_xType) )); + return ret.getAsConstList(); +} + +uno::Reference<rdf::XURI> SAL_CALL +DocumentMetadataAccess::addMetadataFile(const ::rtl::OUString & i_rFileName, + const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes) +throw (uno::RuntimeException, lang::IllegalArgumentException, + container::ElementExistException) +{ + if (!isFileNameValid(i_rFileName)) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::addMetadataFile: invalid FileName"), + *this, 0); + } + if (isReservedFile(i_rFileName)) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::addMetadataFile:" + "invalid FileName: reserved"), *this, 0); + } + for (sal_Int32 i = 0; i < i_rTypes.getLength(); ++i) { + if (!i_rTypes[i].is()) { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::addMetadataFile: " + "null type"), *this, 2); + } + } + + const uno::Reference<rdf::XURI> xGraphName( + getURIForStream(*m_pImpl, i_rFileName) ); + + try { + m_pImpl->m_xRepository->createGraph(xGraphName); + } catch (rdf::RepositoryException & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::addMetadataFile: exception"), + *this, uno::makeAny(e)); + // note: all other exceptions are propagated + } + + addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes); + return xGraphName; +} + +uno::Reference<rdf::XURI> SAL_CALL +DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format, + const uno::Reference< io::XInputStream > & i_xInStream, + const ::rtl::OUString & i_rFileName, + const uno::Reference< rdf::XURI > & i_xBaseURI, + const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes) +throw (uno::RuntimeException, lang::IllegalArgumentException, + datatransfer::UnsupportedFlavorException, + container::ElementExistException, rdf::ParseException, io::IOException) +{ + if (!isFileNameValid(i_rFileName)) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::importMetadataFile: invalid FileName"), + *this, 0); + } + if (isReservedFile(i_rFileName)) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::importMetadataFile:" + "invalid FileName: reserved"), *this, 0); + } + for (sal_Int32 i = 0; i < i_rTypes.getLength(); ++i) { + if (!i_rTypes[i].is()) { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::importMetadataFile: null type"), + *this, 5); + } + } + + const uno::Reference<rdf::XURI> xGraphName( + getURIForStream(*m_pImpl, i_rFileName) ); + + try { + m_pImpl->m_xRepository->importGraph( + i_Format, i_xInStream, xGraphName, i_xBaseURI); + } catch (rdf::RepositoryException & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::importMetadataFile: " + "RepositoryException"), *this, uno::makeAny(e)); + // note: all other exceptions are propagated + } + + // add to manifest + addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes); + return xGraphName; +} + +void SAL_CALL +DocumentMetadataAccess::removeMetadataFile( + const uno::Reference< rdf::XURI > & i_xGraphName) +throw (uno::RuntimeException, lang::IllegalArgumentException, + container::NoSuchElementException) +{ + try { + m_pImpl->m_xRepository->destroyGraph(i_xGraphName); + } catch (rdf::RepositoryException & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::removeMetadataFile: " + "RepositoryException"), *this, uno::makeAny(e)); + // note: all other exceptions are propagated + } + + // remove file from manifest + removeFile(*m_pImpl, i_xGraphName.get()); +} + +void SAL_CALL +DocumentMetadataAccess::addContentOrStylesFile( + const ::rtl::OUString & i_rFileName) +throw (uno::RuntimeException, lang::IllegalArgumentException, + container::ElementExistException) +{ + if (!isFileNameValid(i_rFileName)) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::addContentOrStylesFile: " + "invalid FileName"), *this, 0); + } + + if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::addContentOrStylesFile: " + "invalid FileName: must end with content.xml or styles.xml"), + *this, 0); + } +} + +void SAL_CALL +DocumentMetadataAccess::removeContentOrStylesFile( + const ::rtl::OUString & i_rFileName) +throw (uno::RuntimeException, lang::IllegalArgumentException, + container::NoSuchElementException) +{ + if (!isFileNameValid(i_rFileName)) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::removeContentOrStylesFile: " + "invalid FileName"), *this, 0); + } + + try { + const uno::Reference<rdf::XURI> xPart( + getURIForStream(*m_pImpl, i_rFileName) ); + const uno::Reference<container::XEnumeration> xEnum( + m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI.get(), + getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext), + xPart.get()), + uno::UNO_SET_THROW); + if (!xEnum->hasMoreElements()) { + throw container::NoSuchElementException( + ::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::removeContentOrStylesFile: " + "cannot find stream in manifest graph: ") + i_rFileName, + *this); + } + + // remove file from manifest + removeFile(*m_pImpl, xPart); + + } catch (uno::RuntimeException &) { + throw; + } catch (uno::Exception & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::removeContentOrStylesFile: exception"), + *this, uno::makeAny(e)); + } +} + +void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage( + const uno::Reference< embed::XStorage > & i_xStorage, + const uno::Reference<rdf::XURI> & i_xBaseURI, + const uno::Reference<task::XInteractionHandler> & i_xHandler) +throw (uno::RuntimeException, lang::IllegalArgumentException, + lang::WrappedTargetException) +{ + if (!i_xStorage.is()) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromStorage: " + "storage is null"), *this, 0); + } + if (!i_xBaseURI.is()) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromStorage: " + "base URI is null"), *this, 1); + } + const ::rtl::OUString baseURI( i_xBaseURI->getStringValue()); + if (baseURI.indexOf('#') >= 0) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromStorage: " + "base URI not absolute"), *this, 1); + } + if (!baseURI.getLength() || !baseURI.endsWithAsciiL("/", 1)) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromStorage: " + "base URI does not end with slash"), *this, 1); + } + + initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler); + + std::set< ::rtl::OUString > StgFiles; + collectFilesFromStorage(i_xStorage, + ::rtl::OUString::createFromAscii(""), StgFiles); + + std::vector< ::rtl::OUString > MfstMetadataFiles; + + try { + const ::std::vector< uno::Reference< rdf::XURI > > parts( + getAllParts(*m_pImpl) ); + const uno::Reference<rdf::XURI> xContentFile( + getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext)); + const uno::Reference<rdf::XURI> xStylesFile( + getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext)); + const uno::Reference<rdf::XURI> xMetadataFile( + getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext)); + const sal_Int32 len( baseURI.getLength() ); + const ::rtl::OUString manifest ( + ::rtl::OUString::createFromAscii(s_manifest)); + for (::std::vector< uno::Reference< rdf::XURI > >::const_iterator it + = parts.begin(); + it != parts.end(); ++it) { + const ::rtl::OUString name((*it)->getStringValue()); + if (!name.match(baseURI)) { + OSL_TRACE("loadMetadataFromStorage: graph not in document: %s", + ::rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8) + .getStr()); + continue; + } + const ::rtl::OUString relName( name.copy(len) ); + if (relName == manifest) { + OSL_TRACE("loadMetadataFromStorage: " + "found ourselves a recursive manifest!"); + continue; + } + // remove found items from StgFiles + StgFiles.erase(relName); + if (isContentFile(relName)) { + if (!isPartOfType(*m_pImpl, *it, xContentFile)) { + const uno::Reference <rdf::XURI> xName( + getURIForStream(*m_pImpl, relName) ); + // add missing type statement + m_pImpl->m_xManifest->addStatement(xName.get(), + getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext), + xContentFile.get()); + } + } else if (isStylesFile(relName)) { + if (!isPartOfType(*m_pImpl, *it, xStylesFile)) { + const uno::Reference <rdf::XURI> xName( + getURIForStream(*m_pImpl, relName) ); + // add missing type statement + m_pImpl->m_xManifest->addStatement(xName.get(), + getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext), + xStylesFile.get()); + } + } else if (isReservedFile(relName)) { + OSL_TRACE("loadMetadataFromStorage: " + "reserved file name in manifest"); + } else { + if (isPartOfType(*m_pImpl, *it, xMetadataFile)) { + MfstMetadataFiles.push_back(relName); + } + // do not add statement for MetadataFile; it could be + // something else! just ignore it... + } + } + } catch (uno::RuntimeException &) { + throw; + } catch (uno::Exception & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromStorage: " + "exception"), *this, uno::makeAny(e)); + } + + std::for_each(StgFiles.begin(), StgFiles.end(), + boost::bind(addContentOrStylesFileImpl, boost::ref(*m_pImpl), _1)); + + std::for_each(MfstMetadataFiles.begin(), MfstMetadataFiles.end(), + boost::bind(importFile, boost::ref(*m_pImpl), + i_xStorage, baseURI, i_xHandler, _1)); +} + +void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage( + const uno::Reference< embed::XStorage > & i_xStorage) +throw (uno::RuntimeException, lang::IllegalArgumentException, + lang::WrappedTargetException) +{ + if (!i_xStorage.is()) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::storeMetadataToStorage: " + "storage is null"), *this, 0); + } + + // export manifest + const ::rtl::OUString manifest ( + ::rtl::OUString::createFromAscii(s_manifest)); + const uno::Reference <rdf::XURI> xManifest( + getURIForStream(*m_pImpl, manifest) ); + const ::rtl::OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() ); + try { + writeStream(*m_pImpl, i_xStorage, xManifest, manifest, baseURI); + } catch (uno::RuntimeException &) { + throw; + } catch (io::IOException & e) { + throw lang::WrappedTargetException( ::rtl::OUString::createFromAscii( + "storeMetadataToStorage: IO exception"), *this, uno::makeAny(e)); + } catch (uno::Exception & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii( + "storeMetadataToStorage: exception"), *this, uno::makeAny(e)); + } + + // export metadata streams + try { + const uno::Sequence<uno::Reference<rdf::XURI> > graphs( + m_pImpl->m_xRepository->getGraphNames()); + const sal_Int32 len( baseURI.getLength() ); + for (sal_Int32 i = 0; i < graphs.getLength(); ++i) { + const uno::Reference<rdf::XURI> xName(graphs[i]); + const ::rtl::OUString name(xName->getStringValue()); + if (!name.match(baseURI)) { + OSL_TRACE("storeMetadataToStorage: graph not in document: %s", + ::rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8) + .getStr()); + continue; + } + const ::rtl::OUString relName( name.copy(len) ); + if (relName == manifest) { + continue; + } + if (!isFileNameValid(relName) || isReservedFile(relName)) { + OSL_TRACE("storeMetadataToStorage: invalid file name: %s", + ::rtl::OUStringToOString(relName, RTL_TEXTENCODING_UTF8) + .getStr()); + continue; + } + try { + writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI); + } catch (uno::RuntimeException &) { + throw; + } catch (io::IOException & e) { + throw lang::WrappedTargetException( + ::rtl::OUString::createFromAscii( + "storeMetadataToStorage: IO exception"), + *this, uno::makeAny(e)); + } catch (uno::Exception & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii( + "storeMetadataToStorage: exception"), + *this, uno::makeAny(e)); + } + } + } catch (rdf::RepositoryException & e) { + throw lang::WrappedTargetRuntimeException( + ::rtl::OUString::createFromAscii( + "storeMetadataToStorage: exception"), *this, uno::makeAny(e)); + } +} + +void SAL_CALL +DocumentMetadataAccess::loadMetadataFromMedium( + const uno::Sequence< beans::PropertyValue > & i_rMedium) +throw (uno::RuntimeException, lang::IllegalArgumentException, + lang::WrappedTargetException) +{ + uno::Reference<io::XInputStream> xIn; + ::comphelper::MediaDescriptor md(i_rMedium); + ::rtl::OUString URL; + md[ ::comphelper::MediaDescriptor::PROP_URL() ] >>= URL; + ::rtl::OUString BaseURL; + md[ ::comphelper::MediaDescriptor::PROP_DOCUMENTBASEURL() ] >>= BaseURL; + if (md.addInputStream()) { + md[ ::comphelper::MediaDescriptor::PROP_INPUTSTREAM() ] >>= xIn; + } + if (!xIn.is() && URL.equalsAscii("")) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromMedium: " + "inalid medium: no URL, no input stream"), *this, 0); + } + uno::Reference<embed::XStorage> xStorage; + try { + const uno::Reference<lang::XMultiServiceFactory> xMsf ( + m_pImpl->m_xContext->getServiceManager(), uno::UNO_QUERY_THROW); + if (xIn.is()) { + xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream( + xIn, xMsf); + } else { // fallback to url + xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2( + URL, embed::ElementModes::READ, xMsf); + } + } catch (uno::RuntimeException &) { + throw; + } catch (io::IOException &) { + throw; + } catch (uno::Exception & e) { + throw lang::WrappedTargetException( + ::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromMedium: " + "exception"), *this, uno::makeAny(e)); + } + if (!xStorage.is()) { + throw uno::RuntimeException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::loadMetadataFromMedium: " + "cannot get Storage"), *this); + } + uno::Reference<rdf::XURI> xBaseURI; + try { + xBaseURI = createBaseURI(m_pImpl->m_xContext, xStorage, BaseURL); + } catch (uno::Exception &) { + // fall back to URL + try { + xBaseURI = createBaseURI(m_pImpl->m_xContext, xStorage, URL); + } catch (uno::Exception &) { + OSL_ENSURE(false, "cannot create base URI"); + } + } + uno::Reference<task::XInteractionHandler> xIH; + md[ ::comphelper::MediaDescriptor::PROP_INTERACTIONHANDLER() ] >>= xIH; + loadMetadataFromStorage(xStorage, xBaseURI, xIH); +} + +void SAL_CALL +DocumentMetadataAccess::storeMetadataToMedium( + const uno::Sequence< beans::PropertyValue > & i_rMedium) +throw (uno::RuntimeException, lang::IllegalArgumentException, + lang::WrappedTargetException) +{ + ::comphelper::MediaDescriptor md(i_rMedium); + ::rtl::OUString URL; + md[ ::comphelper::MediaDescriptor::PROP_URL() ] >>= URL; + if (URL.equalsAscii("")) { + throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::storeMetadataToMedium: " + "invalid medium: no URL"), *this, 0); + } + + SfxMedium aMedium(i_rMedium); + uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage()); + + bool sfx(false); + if (xStorage.is()) { + sfx = true; + } else { + const uno::Reference<lang::XMultiServiceFactory> xMsf ( + m_pImpl->m_xContext->getServiceManager(), uno::UNO_QUERY_THROW); + xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2( + URL, embed::ElementModes::WRITE, xMsf); + } + + if (!xStorage.is()) { + throw uno::RuntimeException(::rtl::OUString::createFromAscii( + "DocumentMetadataAccess::storeMetadataToMedium: " + "cannot get Storage"), *this); + } + // set MIME type of the storage + ::comphelper::MediaDescriptor::const_iterator iter + = md.find(::comphelper::MediaDescriptor::PROP_MEDIATYPE()); + if (iter != md.end()) { + uno::Reference< beans::XPropertySet > xProps(xStorage, + uno::UNO_QUERY_THROW); + try { + // this is NOT supported in FileSystemStorage + xProps->setPropertyValue( + ::comphelper::MediaDescriptor::PROP_MEDIATYPE(), + iter->second); + } catch (uno::Exception &) { } + } + storeMetadataToStorage(xStorage); + + if (sfx) { + const sal_Bool bOk = aMedium.Commit(); + aMedium.Close(); + if ( !bOk ) { + sal_uInt32 nError = aMedium.GetError(); + if ( nError == ERRCODE_NONE ) { + nError = ERRCODE_IO_GENERAL; + } + task::ErrorCodeIOException ex( ::rtl::OUString(), + uno::Reference< uno::XInterface >(), nError); + throw lang::WrappedTargetException(::rtl::OUString(), *this, + uno::makeAny(ex)); + } + } +} + +} // namespace sfx2 + diff --git a/sfx2/source/doc/Metadatable.cxx b/sfx2/source/doc/Metadatable.cxx new file mode 100644 index 000000000000..2ad27856c7cc --- /dev/null +++ b/sfx2/source/doc/Metadatable.cxx @@ -0,0 +1,1860 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SwMetadatable.cxx,v $ + * $Revision: 1.1.2.8 $ + * + * 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. + * + ************************************************************************/ + +#include "precompiled_sfx2.hxx" + +#include <sfx2/Metadatable.hxx> +#include <sfx2/XmlIdRegistry.hxx> + +#include <vos/mutex.hxx> +#include <vcl/svapp.hxx> // solarmutex + +#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) +{ + 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 + { + const int n( rand() ); + id = prefix + ::rtl::OUString::valueOf(static_cast<sal_Int64>(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 bool i_isDelete) +{ + 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; + + if (i_isDelete) + { + RemoveMetadataReference(); + } + return pUndo; + } + } + catch (uno::Exception &) + { + OSL_ENSURE(false, "Metadatable::CreateUndo: exception"); + } + return ::boost::shared_ptr<MetadatableUndo>(); +} + +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* pObject( GetCoreObject() ); + if (pObject) + { + return pObject->GetMetadataReference(); + } + else + { + throw uno::RuntimeException(); + } +} + +void SAL_CALL +MetadatableMixin::setMetadataReference( + const beans::StringPair & i_rReference) +throw (uno::RuntimeException, lang::IllegalArgumentException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + Metadatable* pObject( GetCoreObject() ); + if (pObject) + { + return pObject->SetMetadataReference(i_rReference); + } + else + { + throw uno::RuntimeException(); + } +} + +void SAL_CALL MetadatableMixin::ensureMetadataReference() +throw (uno::RuntimeException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + Metadatable* pObject( GetCoreObject() ); + if (pObject) + { + return pObject->EnsureMetadataReference(); + } + else + { + throw uno::RuntimeException(); + } +} + +} // namespace sfx2 + + +//============================================================================= + +#if OSL_DEBUG_LEVEL > 1 + +static ::sfx2::XmlIdRegistryDocument s_Reg; +static ::sfx2::XmlIdRegistryClipboard s_RegClip; + +class MockMetadatable : public ::sfx2::Metadatable +{ +public: + MockMetadatable(bool i_isInClip = false) : + m_bInClipboard(i_isInClip), m_bInUndo(false), m_bInContent(true) {} + bool m_bInClipboard; + bool m_bInUndo; + bool m_bInContent; + virtual bool IsInClipboard() const { return m_bInClipboard; } + virtual bool IsInUndo() const { return m_bInUndo; } + virtual bool IsInContent() const { return m_bInContent; } + virtual ::sfx2::XmlIdRegistry& GetRegistry() { return m_bInClipboard ? static_cast< ::sfx2::XmlIdRegistry&>(s_RegClip) : static_cast< ::sfx2::XmlIdRegistry&>(s_Reg); } + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::rdf::XMetadatable > MakeUnoObject() { return 0; } +}; + +bool operator==(beans::StringPair p1, beans::StringPair p2) +{ + return p1.First == p2.First && p1.Second == p2.Second; +} + +void test() +{ + OSL_TRACE("SwMetadatable test(): start\n"); + MockMetadatable m1; + MockMetadatable m2; + MockMetadatable m3; + MockMetadatable m4; + MockMetadatable m5; + ::rtl::OUString empty; + ::rtl::OUString content( ::rtl::OUString::createFromAscii("content.xml") ); + ::rtl::OUString styles ( ::rtl::OUString::createFromAscii("styles.xml") ); + ::rtl::OUString sid1( ::rtl::OUString::createFromAscii("id1") ); + ::rtl::OUString sid2( ::rtl::OUString::createFromAscii("id2") ); + ::rtl::OUString sid3( ::rtl::OUString::createFromAscii("id3") ); + ::rtl::OUString sid4( ::rtl::OUString::createFromAscii("id4") ); + beans::StringPair id1(content, sid1); + beans::StringPair id2(content, sid2); + beans::StringPair id3(content, sid3); + beans::StringPair id4(styles, sid4); + beans::StringPair id3e(empty, sid3); + beans::StringPair id4e(empty, sid4); + m1.SetMetadataReference(id1); + OSL_ENSURE(m1.GetMetadataReference() == id1, "set failed"); + try { + m2.SetMetadataReference(id1); + OSL_ENSURE(false, "set duplicate succeeded"); + } catch (lang::IllegalArgumentException) { } + m1.SetMetadataReference(id1); + OSL_ENSURE(m1.GetMetadataReference() == id1, "set failed (existing)"); + m1.EnsureMetadataReference(); + OSL_ENSURE(m1.GetMetadataReference() == id1, "ensure failed (existing)"); + + m2.EnsureMetadataReference(); + beans::StringPair m2id(m2.GetMetadataReference()); + OSL_ENSURE(m2id.Second.getLength(), "ensure failed"); + m2.EnsureMetadataReference(); + OSL_ENSURE(m2.GetMetadataReference() == m2id, "ensure failed (idempotent)"); + + m1.m_bInUndo = true; + OSL_ENSURE(!m1.GetMetadataReference().Second.getLength(), "move to undo failed"); + + m1.m_bInUndo = false; + OSL_ENSURE(m1.GetMetadataReference() == id1, "move from undo failed"); + + m1.m_bInUndo = true; + try { + m2.SetMetadataReference(id1); // steal! + } catch (lang::IllegalArgumentException &) { + OSL_ENSURE(false, "set duplicate to undo failed"); + } + m1.m_bInUndo = false; + OSL_ENSURE(!m1.GetMetadataReference().Second.getLength(), "move from undo: duplicate"); + + m3.RegisterAsCopyOf(m2); + OSL_ENSURE(m2.GetMetadataReference() == id1, "copy: source"); + OSL_ENSURE(!m3.GetMetadataReference().Second.getLength(), "copy: duplicate"); + m4.RegisterAsCopyOf(m3); + OSL_ENSURE(m2.GetMetadataReference() == id1, "copy: source"); + OSL_ENSURE(!m3.GetMetadataReference().Second.getLength(), "copy: duplicate"); + OSL_ENSURE(!m4.GetMetadataReference().Second.getLength(), "copy: duplicate"); + m2.m_bInUndo = true; + OSL_ENSURE(m3.GetMetadataReference() == id1, "duplicate to undo"); + OSL_ENSURE(!m2.GetMetadataReference().Second.getLength(), "duplicate to undo"); + m2.m_bInUndo = false; + OSL_ENSURE(m2.GetMetadataReference() == id1, "duplicate from undo"); + OSL_ENSURE(!m3.GetMetadataReference().Second.getLength(), "duplicate from undo"); + + m4.EnsureMetadataReference(); // new! + beans::StringPair m4id(m4.GetMetadataReference()); + OSL_ENSURE(m4id.Second.getLength() && !(m4id == id1), "ensure on duplicate"); + + MockMetadatable mc1(true); // in clipboard + MockMetadatable mc2(true); + MockMetadatable mc3(true); + MockMetadatable mc4(true); + MockMetadatable m2p; + MockMetadatable m3p; + + mc1.SetMetadataReference(id2); + OSL_ENSURE(mc1.GetMetadataReference() == id2, "set failed"); + try { + mc2.SetMetadataReference(id2); + OSL_ENSURE(false, "set duplicate succeeded"); + } catch (lang::IllegalArgumentException) { } + mc1.SetMetadataReference(id2); + OSL_ENSURE(mc1.GetMetadataReference() == id2, "set failed (existing)"); + mc1.EnsureMetadataReference(); + OSL_ENSURE(mc1.GetMetadataReference() == id2, "ensure failed (existing)"); + mc2.EnsureMetadataReference(); + beans::StringPair mc2id(mc2.GetMetadataReference()); + OSL_ENSURE(mc2id.Second.getLength(), "ensure failed"); + mc2.EnsureMetadataReference(); + OSL_ENSURE(mc2.GetMetadataReference() == mc2id, "ensure failed (idempotent)"); + mc2.RemoveMetadataReference(); + OSL_ENSURE(!mc2.GetMetadataReference().Second.getLength(), "remove failed"); + + // set up mc2 as copy of m2 and mc3 as copy of m3 + mc3.RegisterAsCopyOf(m3); + OSL_ENSURE(!mc3.GetMetadataReference().Second.getLength() , "copy to clipboard (latent)"); + mc2.RegisterAsCopyOf(m2); + OSL_ENSURE(mc2.GetMetadataReference() == id1, "copy to clipboard (non-latent)"); + // paste mc2 to m2p and mc3 to m3p + m2p.RegisterAsCopyOf(mc2); + OSL_ENSURE(!m2p.GetMetadataReference().Second.getLength() , "paste from clipboard (non-latent)"); + m3p.RegisterAsCopyOf(mc3); + OSL_ENSURE(!m3p.GetMetadataReference().Second.getLength() , "paste from clipboard (latent)"); + // delete m2, m2p, m3 + m2.RemoveMetadataReference(); + OSL_ENSURE(!m2.GetMetadataReference().Second.getLength(), "remove failed"); + OSL_ENSURE(m2p.GetMetadataReference() == id1, "paste-remove (non-latent)"); + m2p.RemoveMetadataReference(); + OSL_ENSURE(!m2p.GetMetadataReference().Second.getLength(), "remove failed"); + OSL_ENSURE(m3.GetMetadataReference() == id1, "paste-remove2 (non-latent)"); + m3.RemoveMetadataReference(); + OSL_ENSURE(!m3.GetMetadataReference().Second.getLength(), "remove failed"); + OSL_ENSURE(m3p.GetMetadataReference() == id1, "paste-remove (latent)"); + // delete mc2 + mc2.SetMetadataReference(beans::StringPair()); + OSL_ENSURE(!mc3.GetMetadataReference().Second.getLength() , "in clipboard becomes non-latent"); + // paste mc2 + m2p.RegisterAsCopyOf(mc2); + OSL_ENSURE(!m2p.GetMetadataReference().Second.getLength(), "remove-paste"); + OSL_ENSURE(m3p.GetMetadataReference() == id1, "remove-paste (stolen)"); + + // auto-detect stream + m5.SetMetadataReference(id3e); + OSL_ENSURE(m5.GetMetadataReference() == id3, "auto-detect (content)"); + m5.m_bInContent = false; + m5.SetMetadataReference(id4e); + OSL_ENSURE(m5.GetMetadataReference() == id4, "auto-detect (styles)"); + + OSL_TRACE("sfx2::Metadatable test(): finished\n"); +} + +struct Test { Test() { test(); } }; +static Test s_test; + + +#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 + diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx index 79197b6e5f52..c81595cfc6e6 100644 --- a/sfx2/source/doc/docfile.cxx +++ b/sfx2/source/doc/docfile.cxx @@ -3305,6 +3305,7 @@ SfxMedium::SfxMedium( const ::com::sun::star::uno::Sequence< ::com::sun::star::b // that must be copied here SFX_ITEMSET_ARG( pSet, pFileNameItem, SfxStringItem, SID_FILE_NAME, FALSE ); + if (!pFileNameItem) throw uno::RuntimeException(); ::rtl::OUString aNewTempFileURL = SfxMedium::CreateTempCopyWithExt( pFileNameItem->GetValue() ); if ( aNewTempFileURL.getLength() ) { @@ -3326,6 +3327,7 @@ SfxMedium::SfxMedium( const ::com::sun::star::uno::Sequence< ::com::sun::star::b bReadOnly = TRUE; SFX_ITEMSET_ARG( pSet, pFileNameItem, SfxStringItem, SID_FILE_NAME, FALSE ); + if (!pFileNameItem) throw uno::RuntimeException(); aLogicName = pFileNameItem->GetValue(); nStorOpenMode = bReadOnly ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE; bDirect = FALSE; diff --git a/sfx2/source/doc/makefile.mk b/sfx2/source/doc/makefile.mk index b46ee5aa30e8..4c893288ebb8 100644 --- a/sfx2/source/doc/makefile.mk +++ b/sfx2/source/doc/makefile.mk @@ -40,6 +40,10 @@ ENABLE_EXCEPTIONS=TRUE .INCLUDE : settings.mk .INCLUDE : $(PRJ)$/util$/makefile.pmk +.IF "$(SYSTEM_LIBXML)" == "YES" +CFLAGS+=-DSYSTEM_LIBXML $(LIBXML_CFLAGS) +.ENDIF + # --- Files -------------------------------------------------------- SRS1NAME=$(TARGET) @@ -80,10 +84,13 @@ SLOFILES = \ $(SLO)$/docinsert.obj \ $(SLO)$/docmacromode.obj \ $(SLO)$/SfxDocumentMetaData.obj \ + $(SLO)$/DocumentMetadataAccess.obj \ + $(SLO)$/Metadatable.obj \ $(SLO)$/sfxmodelfactory.obj \ $(SLO)$/docstoragemodifylistener.obj \ $(SLO)$/querytemplate.obj + # --- Tagets ------------------------------------------------------- .INCLUDE : target.mk diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx index 3571c4226ff7..a47fc1bf4747 100644 --- a/sfx2/source/doc/objserv.cxx +++ b/sfx2/source/doc/objserv.cxx @@ -415,7 +415,7 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq) if ( pDocInfItem ) { // parameter, e.g. from replayed macro - pDocInfItem->updateDocumentInfo(getDocProperties()); + pDocInfItem->UpdateDocumentInfo(getDocProperties(), true); SetUseUserData( pDocInfItem->IsUseUserData() ); } else @@ -478,7 +478,7 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq) if ( pDocInfoItem ) { // user has done some changes to DocumentInfo - pDocInfoItem->updateDocumentInfo(getDocProperties()); + pDocInfoItem->UpdateDocumentInfo(getDocProperties()); SetUseUserData( ((const SfxDocumentInfoItem *)pDocInfoItem)->IsUseUserData() ); // add data from dialog for possible recording purposes diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx index 76e6bb59e189..2c98f2a5d141 100644 --- a/sfx2/source/doc/objstor.cxx +++ b/sfx2/source/doc/objstor.cxx @@ -621,6 +621,7 @@ sal_Bool SfxObjectShell::DoLoad( SfxMedium *pMed ) else aBaseURL = pMed->GetBaseURL(); } + pMed->GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, aBaseURL ) ); pImp->nLoadedFlags = 0; pImp->bModelInitialized = sal_False; diff --git a/sfx2/source/doc/objxtor.cxx b/sfx2/source/doc/objxtor.cxx index d91ec392aa92..3edf1ddb60d7 100644 --- a/sfx2/source/doc/objxtor.cxx +++ b/sfx2/source/doc/objxtor.cxx @@ -722,11 +722,11 @@ namespace { static BasicManager* lcl_getBasicManagerForDocument( const SfxObjectShell& _rDocument ) { - if ( !_rDocument.pImp->m_bNoBasicCapabilities ) + if ( !_rDocument.Get_Impl()->m_bNoBasicCapabilities ) { - if ( !_rDocument.pImp->bBasicInitialized ) + if ( !_rDocument.Get_Impl()->bBasicInitialized ) const_cast< SfxObjectShell& >( _rDocument ).InitBasicManager_Impl(); - return _rDocument.pImp->pBasicManager->get(); + return _rDocument.Get_Impl()->pBasicManager->get(); } // assume we do not have Basic ourself, but we can refer to another @@ -794,9 +794,13 @@ namespace try { Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY ); + const Reference< XComponentContext > xContext( + ::comphelper::getProcessComponentContext() ); _rxContainer.set ( _bScript - ? DocumentScriptLibraryContainer::create( comphelper_getProcessComponentContext(), xStorageDoc ) - : DocumentDialogLibraryContainer::create( comphelper_getProcessComponentContext(), xStorageDoc ) + ? DocumentScriptLibraryContainer::create( + xContext, xStorageDoc ) + : DocumentDialogLibraryContainer::create( + xContext, xStorageDoc ) , UNO_QUERY_THROW ); } catch( const Exception& ) @@ -1065,6 +1069,12 @@ void SfxObjectShell::SetAutoStyleFilterIndex(sal_uInt16 nSet) pImp->nStyleFilter = nSet; } +sal_uInt16 SfxObjectShell::GetAutoStyleFilterIndex() +{ + return pImp->nStyleFilter; +} + + void SfxObjectShell::SetCurrentComponent( const Reference< XInterface >& _rxComponent ) { if ( _rxComponent.get() == s_xCurrentComponent.get().get() ) diff --git a/sfx2/source/doc/sfxbasemodel.cxx b/sfx2/source/doc/sfxbasemodel.cxx index 43ce1593bbaf..53673b505d6d 100644 --- a/sfx2/source/doc/sfxbasemodel.cxx +++ b/sfx2/source/doc/sfxbasemodel.cxx @@ -66,6 +66,7 @@ #include <com/sun/star/embed/ElementModes.hpp> #include <com/sun/star/embed/Aspects.hpp> #include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/frame/XTransientDocumentsDocumentContentFactory.hpp> #include <comphelper/enumhelper.hxx> // can be removed when this is a "real" service #include <cppuhelper/interfacecontainer.hxx> @@ -124,6 +125,8 @@ #include "brokenpackageint.hxx" #include "graphhelp.hxx" #include <sfx2/msgpool.hxx> +#include <sfx2/DocumentMetadataAccess.hxx> + #include <sfxresid.hxx> //________________________________________________________________________________________________________ @@ -135,13 +138,14 @@ static const ::rtl::OUString SERVICENAME_DESKTOP = ::rtl::OUString::createFromAs //________________________________________________________________________________________________________ namespace css = ::com::sun::star; -using namespace com::sun::star; +using namespace ::com::sun::star; using namespace ::com::sun::star::uno; //________________________________________________________________________________________________________ // impl. declarations //________________________________________________________________________________________________________ + struct IMPL_SfxBaseModel_DataContainer : public ::sfx2::IModifiableDocument { // counter for SfxBaseModel instances created. @@ -172,6 +176,8 @@ struct IMPL_SfxBaseModel_DataContainer : public ::sfx2::IModifiableDocument ::rtl::OUString m_sModuleIdentifier; css::uno::Reference< css::frame::XTitle > m_xTitleHelper; css::uno::Reference< css::frame::XUntitledNumbers > m_xNumberedControllers; + uno::Reference< rdf::XDocumentMetadataAccess> m_xDocumentMetadata; + IMPL_SfxBaseModel_DataContainer( ::osl::Mutex& rMutex, SfxObjectShell* pObjectShell ) : m_pObjectShell ( pObjectShell ) @@ -184,6 +190,7 @@ struct IMPL_SfxBaseModel_DataContainer : public ::sfx2::IModifiableDocument , m_pStorageModifyListen ( NULL ) , m_xTitleHelper () , m_xNumberedControllers () + , m_xDocumentMetadata () // lazy { // increase global instance counter. ++g_nInstanceCounter; @@ -201,6 +208,58 @@ struct IMPL_SfxBaseModel_DataContainer : public ::sfx2::IModifiableDocument if ( m_pObjectShell.Is() && !m_pObjectShell->IsModified() ) m_pObjectShell->SetModified( sal_True ); } + + uno::Reference<rdf::XDocumentMetadataAccess> GetDMA() + { + if (!m_xDocumentMetadata.is()) + { + OSL_ENSURE(m_pObjectShell, "GetDMA: no object shell?"); + if (!m_pObjectShell) + { + return 0; + } + + const uno::Reference<uno::XComponentContext> xContext( + ::comphelper::getProcessComponentContext()); + ::rtl::OUString uri; + const uno::Reference<frame::XModel> xModel( + m_pObjectShell->GetModel()); + const uno::Reference<lang::XMultiComponentFactory> xMsf( + xContext->getServiceManager()); + const uno::Reference<frame:: + XTransientDocumentsDocumentContentFactory> xTDDCF( + xMsf->createInstanceWithContext( + ::rtl::OUString::createFromAscii( "com.sun.star.frame." + "TransientDocumentsDocumentContentFactory"), + xContext), + uno::UNO_QUERY_THROW); + const uno::Reference<ucb::XContent> xContent( + xTDDCF->createDocumentContent(xModel) ); + OSL_ENSURE(xContent.is(), "GetDMA: cannot create DocumentContent"); + if (!xContent.is()) + { + return 0; + } + uri = xContent->getIdentifier()->getContentIdentifier(); + OSL_ENSURE(uri.getLength(), "GetDMA: empty uri?"); + if (uri.getLength() && !uri.endsWithAsciiL("/", 1)) + { + uri = uri + ::rtl::OUString::createFromAscii("/"); + } + + m_xDocumentMetadata = new ::sfx2::DocumentMetadataAccess( + xContext, *m_pObjectShell, uri); + } + return m_xDocumentMetadata; + } + + uno::Reference<rdf::XDocumentMetadataAccess> CreateDMAUninitialized() + { + return (m_pObjectShell) + ? new ::sfx2::DocumentMetadataAccess( + ::comphelper::getProcessComponentContext(), *m_pObjectShell) + : 0; + } }; // static member initialization. @@ -417,9 +476,9 @@ SfxSaveGuard::~SfxSaveGuard() //________________________________________________________________________________________________________ DBG_NAME(sfx2_SfxBaseModel) SfxBaseModel::SfxBaseModel( SfxObjectShell *pObjectShell ) -: IMPL_SfxBaseModel_MutexContainer() +: BaseMutex() , m_pData( new IMPL_SfxBaseModel_DataContainer( m_aMutex, pObjectShell ) ) -, m_bSupportEmbeddedScripts( pObjectShell && pObjectShell->pImp ? !pObjectShell->pImp->m_bNoBasicCapabilities : false ) +, m_bSupportEmbeddedScripts( pObjectShell && pObjectShell->Get_Impl() ? !pObjectShell->Get_Impl()->m_bNoBasicCapabilities : false ) { DBG_CTOR(sfx2_SfxBaseModel,NULL); if ( pObjectShell != NULL ) @@ -686,10 +745,9 @@ void SAL_CALL SfxBaseModel::dispose() throw(::com::sun::star::uno::RuntimeExcept m_pData->m_xDocumentInfo = 0; } - if ( m_pData->m_xDocumentProperties.is() ) - { - m_pData->m_xDocumentProperties = 0; - } + m_pData->m_xDocumentProperties.clear(); + + m_pData->m_xDocumentMetadata.clear(); EndListening( *m_pData->m_pObjectShell ); @@ -3829,3 +3887,325 @@ css::uno::Reference< css::frame::XController2 > SAL_CALL SfxBaseModel::createVie { return css::uno::Reference< css::frame::XController2 >(); } + +//============================================================================= +// RDF DocumentMetadataAccess + +// ::com::sun::star::rdf::XRepositorySupplier: +uno::Reference< rdf::XRepository > SAL_CALL +SfxBaseModel::getRDFRepository() throw (uno::RuntimeException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->getRDFRepository(); +} + +// ::com::sun::star::rdf::XNode: +::rtl::OUString SAL_CALL +SfxBaseModel::getStringValue() throw (uno::RuntimeException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->getStringValue(); +} + +// ::com::sun::star::rdf::XURI: +::rtl::OUString SAL_CALL +SfxBaseModel::getNamespace() throw (uno::RuntimeException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->getNamespace(); +} + +::rtl::OUString SAL_CALL +SfxBaseModel::getLocalName() throw (uno::RuntimeException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->getLocalName(); +} + +// ::com::sun::star::rdf::XDocumentMetadataAccess: +uno::Reference< rdf::XMetadatable > SAL_CALL +SfxBaseModel::getElementByMetadataReference( + const ::com::sun::star::beans::StringPair & i_rReference) +throw (uno::RuntimeException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->getElementByMetadataReference(i_rReference); +} + +uno::Reference< rdf::XMetadatable > SAL_CALL +SfxBaseModel::getElementByURI(const uno::Reference< rdf::XURI > & i_xURI) +throw (uno::RuntimeException, lang::IllegalArgumentException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->getElementByURI(i_xURI); +} + +uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL +SfxBaseModel::getMetadataGraphsWithType( + const uno::Reference<rdf::XURI> & i_xType) +throw (uno::RuntimeException, lang::IllegalArgumentException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->getMetadataGraphsWithType(i_xType); +} + +uno::Reference<rdf::XURI> SAL_CALL +SfxBaseModel::addMetadataFile(const ::rtl::OUString & i_rFileName, + const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes) +throw (uno::RuntimeException, lang::IllegalArgumentException, + container::ElementExistException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->addMetadataFile(i_rFileName, i_rTypes); +} + +uno::Reference<rdf::XURI> SAL_CALL +SfxBaseModel::importMetadataFile(::sal_Int16 i_Format, + const uno::Reference< io::XInputStream > & i_xInStream, + const ::rtl::OUString & i_rFileName, + const uno::Reference< rdf::XURI > & i_xBaseURI, + const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes) +throw (uno::RuntimeException, lang::IllegalArgumentException, + datatransfer::UnsupportedFlavorException, + container::ElementExistException, rdf::ParseException, io::IOException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->importMetadataFile(i_Format, + i_xInStream, i_rFileName, i_xBaseURI, i_rTypes); +} + +void SAL_CALL +SfxBaseModel::removeMetadataFile( + const uno::Reference< rdf::XURI > & i_xGraphName) +throw (uno::RuntimeException, lang::IllegalArgumentException, + container::NoSuchElementException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->removeMetadataFile(i_xGraphName); +} + +void SAL_CALL +SfxBaseModel::addContentOrStylesFile(const ::rtl::OUString & i_rFileName) +throw (uno::RuntimeException, lang::IllegalArgumentException, + container::ElementExistException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->addContentOrStylesFile(i_rFileName); +} + +void SAL_CALL +SfxBaseModel::removeContentOrStylesFile(const ::rtl::OUString & i_rFileName) +throw (uno::RuntimeException, lang::IllegalArgumentException, + container::NoSuchElementException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->removeContentOrStylesFile(i_rFileName); +} + +void SAL_CALL +SfxBaseModel::loadMetadataFromStorage( + uno::Reference< embed::XStorage > const & i_xStorage, + uno::Reference<rdf::XURI> const & i_xBaseURI, + uno::Reference<task::XInteractionHandler> const & i_xHandler) +throw (uno::RuntimeException, lang::IllegalArgumentException, + lang::WrappedTargetException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA( + m_pData->CreateDMAUninitialized()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + try { + xDMA->loadMetadataFromStorage(i_xStorage, i_xBaseURI, i_xHandler); + } catch (lang::IllegalArgumentException &) { + throw; // not initialized + } catch (uno::Exception &) { + // UGLY: if it's a RuntimeException, we can't be sure DMA is initialzed + m_pData->m_xDocumentMetadata = xDMA; + throw; + } + m_pData->m_xDocumentMetadata = xDMA; + +} + +void SAL_CALL +SfxBaseModel::storeMetadataToStorage( + uno::Reference< embed::XStorage > const & i_xStorage) +throw (uno::RuntimeException, lang::IllegalArgumentException, + lang::WrappedTargetException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->storeMetadataToStorage(i_xStorage); +} + +void SAL_CALL +SfxBaseModel::loadMetadataFromMedium( + const uno::Sequence< beans::PropertyValue > & i_rMedium) +throw (uno::RuntimeException, lang::IllegalArgumentException, + lang::WrappedTargetException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA( + m_pData->CreateDMAUninitialized()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + try { + xDMA->loadMetadataFromMedium(i_rMedium); + } catch (lang::IllegalArgumentException &) { + throw; // not initialized + } catch (uno::Exception &) { + // UGLY: if it's a RuntimeException, we can't be sure DMA is initialzed + m_pData->m_xDocumentMetadata = xDMA; + throw; + } + m_pData->m_xDocumentMetadata = xDMA; +} + +void SAL_CALL +SfxBaseModel::storeMetadataToMedium( + const uno::Sequence< beans::PropertyValue > & i_rMedium) +throw (uno::RuntimeException, lang::IllegalArgumentException, + lang::WrappedTargetException) +{ + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + if ( impl_isDisposed() ) + throw lang::DisposedException(); + + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA()); + if (!xDMA.is()) { + throw uno::RuntimeException( ::rtl::OUString::createFromAscii( + "model has no document metadata"), *this ); + } + + return xDMA->storeMetadataToMedium(i_rMedium); +} + |