blob: 55fe069f4b64b920abe8c8564b4968080e77ab52 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "precompiled_sfx2.hxx"
#include <sfx2/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 <rtl/uri.hxx>
#include <rtl/bootstrap.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();
}
// #i108078# workaround non-hierarchical vnd.sun.star.expand URIs
// this really should be done somewhere else, not here.
::rtl::OUString pkgURI(i_rPkgURI);
if (pkgURI.matchIgnoreAsciiCaseAsciiL(
RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.expand:")))
{
// expand it here (makeAbsolute requires hierarchical URI)
pkgURI = pkgURI.copy( RTL_CONSTASCII_LENGTH("vnd.sun.star.expand:") );
if (pkgURI.getLength() != 0) {
pkgURI = ::rtl::Uri::decode(
pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8);
if (pkgURI.getLength() == 0) {
throw uno::RuntimeException();
}
::rtl::Bootstrap::expandMacros(pkgURI);
}
}
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(pkgURI), 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 ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) )
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());
uno::Reference<embed::XTransactedObject> const xTransaction(
xDir, 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