summaryrefslogtreecommitdiff
path: root/sfx2/source/doc
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/doc')
-rw-r--r--sfx2/source/doc/DocumentMetadataAccess.cxx1414
-rw-r--r--sfx2/source/doc/Metadatable.cxx1860
-rw-r--r--sfx2/source/doc/docfile.cxx2
-rw-r--r--sfx2/source/doc/makefile.mk7
-rw-r--r--sfx2/source/doc/objserv.cxx4
-rw-r--r--sfx2/source/doc/objstor.cxx1
-rw-r--r--sfx2/source/doc/objxtor.cxx20
-rw-r--r--sfx2/source/doc/sfxbasemodel.cxx394
8 files changed, 3688 insertions, 14 deletions
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);
+}
+