bump product version to 4.1.6.2
[LibreOffice.git] / sfx2 / source / doc / DocumentMetadataAccess.cxx
blob623aa843ea91e970dfaaed37e5d66806d9ab0f3c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <sfx2/DocumentMetadataAccess.hxx>
23 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <com/sun/star/embed/ElementModes.hpp>
26 #include <com/sun/star/embed/XStorage.hpp>
27 #include <com/sun/star/embed/XTransactedObject.hpp>
28 #include <com/sun/star/task/ErrorCodeIOException.hpp>
29 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
30 #include <com/sun/star/rdf/FileFormat.hpp>
31 #include <com/sun/star/rdf/URIs.hpp>
32 #include <com/sun/star/rdf/Statement.hpp>
33 #include <com/sun/star/rdf/Literal.hpp>
34 #include <com/sun/star/rdf/URI.hpp>
35 #include <com/sun/star/rdf/Repository.hpp>
37 #include <rtl/ustrbuf.hxx>
38 #include <rtl/uri.hxx>
39 #include <rtl/bootstrap.hxx>
41 #include <comphelper/interaction.hxx>
42 #include <comphelper/makesequence.hxx>
43 #include <comphelper/mediadescriptor.hxx>
44 #include <comphelper/sequenceasvector.hxx>
45 #include <comphelper/storagehelper.hxx>
47 #include <sfx2/docfile.hxx>
48 #include <sfx2/XmlIdRegistry.hxx>
50 #include <libxml/tree.h> // for xmlValidateNCName
52 #include <boost/bind.hpp>
53 #include <boost/shared_array.hpp>
54 #include <boost/tuple/tuple.hpp>
56 #include <vector>
57 #include <set>
58 #include <map>
59 #include <functional>
60 #include <algorithm>
62 #include <unotools/ucbhelper.hxx>
63 #include <com/sun/star/uri/XUriReference.hpp>
64 #include <com/sun/star/uri/UriReferenceFactory.hpp>
65 #include <com/sun/star/uri/XVndSunStarPkgUrlReferenceFactory.hpp>
69 Note: in the context of this implementation, all rdf.QueryExceptions and
70 rdf.RepositoryExceptions are RuntimeExceptions, and will be reported as such.
72 This implementation assumes that it is only used with ODF documents, not mere
73 ODF packages. In other words, we enforce that metadata files must not be
74 called reserved names.
77 using namespace ::com::sun::star;
79 namespace sfx2 {
82 bool isValidNCName(OUString const & i_rIdref)
84 const OString id(
85 OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) );
86 return !(xmlValidateNCName(
87 reinterpret_cast<const unsigned char*>(id.getStr()), 0));
91 static const char s_content [] = "content.xml";
92 static const char s_styles [] = "styles.xml";
93 static const char s_meta [] = "meta.xml";
94 static const char s_settings[] = "settings.xml";
95 static const char s_manifest[] = "manifest.rdf";
96 static const char s_rdfxml [] = "application/rdf+xml";
97 static const char s_odfmime [] = "application/vnd.oasis.opendocument.";
100 static bool isContentFile(OUString const & i_rPath)
102 return i_rPath == s_content;
105 static bool isStylesFile (OUString const & i_rPath)
107 return i_rPath == s_styles;
110 bool isValidXmlId(OUString const & i_rStreamName,
111 OUString const & i_rIdref)
113 return isValidNCName(i_rIdref)
114 && (isContentFile(i_rStreamName) || isStylesFile(i_rStreamName));
117 static bool isReservedFile(OUString const & i_rPath)
119 return isContentFile(i_rPath) || isStylesFile(i_rPath) || i_rPath == s_meta || i_rPath == s_settings;
123 uno::Reference<rdf::XURI> createBaseURI(
124 uno::Reference<uno::XComponentContext> const & i_xContext,
125 uno::Reference<embed::XStorage> const & i_xStorage,
126 OUString const & i_rPkgURI, OUString const & i_rSubDocument)
128 if (!i_xContext.is() || !i_xStorage.is() || i_rPkgURI.isEmpty()) {
129 throw uno::RuntimeException();
132 // #i108078# workaround non-hierarchical vnd.sun.star.expand URIs
133 // this really should be done somewhere else, not here.
134 OUString pkgURI(i_rPkgURI);
135 if (pkgURI.matchIgnoreAsciiCaseAsciiL(
136 RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.expand:")))
138 // expand it here (makeAbsolute requires hierarchical URI)
139 pkgURI = pkgURI.copy( RTL_CONSTASCII_LENGTH("vnd.sun.star.expand:") );
140 if (!pkgURI.isEmpty()) {
141 pkgURI = ::rtl::Uri::decode(
142 pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8);
143 if (pkgURI.isEmpty()) {
144 throw uno::RuntimeException();
146 ::rtl::Bootstrap::expandMacros(pkgURI);
150 const uno::Reference<uri::XUriReferenceFactory> xUriFactory =
151 uri::UriReferenceFactory::create( i_xContext);
152 uno::Reference< uri::XUriReference > xBaseURI;
154 const uno::Reference< uri::XUriReference > xPkgURI(
155 xUriFactory->parse(pkgURI), uno::UNO_SET_THROW );
156 xPkgURI->clearFragment();
158 // need to know whether the storage is a FileSystemStorage
159 // XServiceInfo would be better, but it is not implemented
160 // if ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) )
161 if (true) {
162 xBaseURI.set( xPkgURI, uno::UNO_SET_THROW );
164 OUStringBuffer buf;
165 if (!xBaseURI->getUriReference().endsWithAsciiL("/", 1))
167 const sal_Int32 count( xBaseURI->getPathSegmentCount() );
168 if (count > 0)
170 const OUString last( xBaseURI->getPathSegment(count - 1) );
171 buf.append(last);
173 buf.append(static_cast<sal_Unicode>('/'));
175 if (!i_rSubDocument.isEmpty())
177 buf.append(i_rSubDocument);
178 buf.append(static_cast<sal_Unicode>('/'));
180 const OUString Path(buf.makeStringAndClear());
181 if (!Path.isEmpty())
183 const uno::Reference< uri::XUriReference > xPathURI(
184 xUriFactory->parse(Path), uno::UNO_SET_THROW );
185 xBaseURI.set(
186 xUriFactory->makeAbsolute(xBaseURI, xPathURI,
187 true, uri::RelativeUriExcessParentSegments_ERROR),
188 uno::UNO_SET_THROW);
191 return rdf::URI::create(i_xContext, xBaseURI->getUriReference());
195 struct DocumentMetadataAccess_Impl
197 // note: these are all initialized in constructor, and loadFromStorage
198 const uno::Reference<uno::XComponentContext> m_xContext;
199 const IXmlIdRegistrySupplier & m_rXmlIdRegistrySupplier;
200 uno::Reference<rdf::XURI> m_xBaseURI;
201 uno::Reference<rdf::XRepository> m_xRepository;
202 uno::Reference<rdf::XNamedGraph> m_xManifest;
203 DocumentMetadataAccess_Impl(
204 uno::Reference<uno::XComponentContext> const& i_xContext,
205 IXmlIdRegistrySupplier const & i_rRegistrySupplier)
206 : m_xContext(i_xContext)
207 , m_rXmlIdRegistrySupplier(i_rRegistrySupplier)
208 , m_xBaseURI()
209 , m_xRepository()
210 , m_xManifest()
212 OSL_ENSURE(m_xContext.is(), "context null");
216 // this is... a hack.
217 template<sal_Int16 Constant>
218 /*static*/ uno::Reference<rdf::XURI>
219 getURI(uno::Reference< uno::XComponentContext > const & i_xContext)
221 static uno::Reference< rdf::XURI > xURI(
222 rdf::URI::createKnown(i_xContext, Constant), uno::UNO_QUERY_THROW);
223 return xURI;
227 /** would storing the file to a XStorage succeed? */
228 static bool isFileNameValid(const OUString & i_rFileName)
230 if (i_rFileName.isEmpty()) return false;
231 if (i_rFileName[0] == '/') return false; // no absolute paths!
232 sal_Int32 idx(0);
233 do {
234 const OUString segment(
235 i_rFileName.getToken(0, static_cast<sal_Unicode> ('/'), idx) );
236 if (segment.isEmpty() || // no empty segments
237 segment == "." || // no . segments
238 segment == ".." || // no .. segments
239 !::comphelper::OStorageHelper::IsValidZipEntryFileName(
240 segment, sal_False)) // no invalid characters
241 return false;
242 } while (idx >= 0);
243 return true;
246 /** split a uri hierarchy into first segment and rest */
247 static bool
248 splitPath(OUString const & i_rPath,
249 OUString & o_rDir, OUString& o_rRest)
251 const sal_Int32 idx(i_rPath.indexOf(static_cast<sal_Unicode>('/')));
252 if (idx < 0 || idx >= i_rPath.getLength()) {
253 o_rDir = OUString();
254 o_rRest = i_rPath;
255 return true;
256 } else if (idx == 0 || idx == i_rPath.getLength() - 1) {
257 // input must not start or end with '/'
258 return false;
259 } else {
260 o_rDir = (i_rPath.copy(0, idx));
261 o_rRest = (i_rPath.copy(idx+1));
262 return true;
266 static bool
267 splitXmlId(OUString const & i_XmlId,
268 OUString & o_StreamName, OUString& o_Idref )
270 const sal_Int32 idx(i_XmlId.indexOf(static_cast<sal_Unicode>('#')));
271 if ((idx <= 0) || (idx >= i_XmlId.getLength() - 1)) {
272 return false;
273 } else {
274 o_StreamName = (i_XmlId.copy(0, idx));
275 o_Idref = (i_XmlId.copy(idx+1));
276 return isValidXmlId(o_StreamName, o_Idref);
281 static uno::Reference<rdf::XURI>
282 getURIForStream(struct DocumentMetadataAccess_Impl& i_rImpl,
283 OUString const& i_rPath)
285 const uno::Reference<rdf::XURI> xURI(
286 rdf::URI::createNS( i_rImpl.m_xContext,
287 i_rImpl.m_xBaseURI->getStringValue(), i_rPath),
288 uno::UNO_SET_THROW);
289 return xURI;
292 /** add statements declaring i_xResource to be a file of type i_xType with
293 path i_rPath to manifest, with optional additional types i_pTypes */
294 static void
295 addFile(struct DocumentMetadataAccess_Impl & i_rImpl,
296 uno::Reference<rdf::XURI> const& i_xType,
297 OUString const & i_rPath,
298 const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes = 0)
300 try {
301 const uno::Reference<rdf::XURI> xURI( getURIForStream(
302 i_rImpl, i_rPath) );
304 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
305 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
306 xURI.get());
307 i_rImpl.m_xManifest->addStatement(xURI.get(),
308 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
309 i_xType.get());
310 if (i_pTypes) {
311 for (sal_Int32 i = 0; i < i_pTypes->getLength(); ++i) {
312 i_rImpl.m_xManifest->addStatement(xURI.get(),
313 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
314 (*i_pTypes)[i].get());
317 } catch (const uno::RuntimeException &) {
318 throw;
319 } catch (const uno::Exception & e) {
320 throw lang::WrappedTargetRuntimeException(
321 OUString(
322 "addFile: exception"), /*this*/0, uno::makeAny(e));
326 /** add content.xml or styles.xml to manifest */
327 static bool
328 addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl & i_rImpl,
329 const OUString & i_rPath)
331 uno::Reference<rdf::XURI> xType;
332 if (isContentFile(i_rPath)) {
333 xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext));
334 } else if (isStylesFile(i_rPath)) {
335 xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext));
336 } else {
337 return false;
339 addFile(i_rImpl, xType.get(), i_rPath);
340 return true;
343 /** add metadata file to manifest */
344 static void
345 addMetadataFileImpl(struct DocumentMetadataAccess_Impl & i_rImpl,
346 const OUString & i_rPath,
347 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
349 addFile(i_rImpl,
350 getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext),
351 i_rPath, &i_rTypes);
354 /** remove a file from the manifest */
355 static void
356 removeFile(struct DocumentMetadataAccess_Impl & i_rImpl,
357 uno::Reference<rdf::XURI> const& i_xPart)
359 if (!i_xPart.is()) throw uno::RuntimeException();
360 try {
361 i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI.get(),
362 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
363 i_xPart.get());
364 i_rImpl.m_xManifest->removeStatements(i_xPart.get(),
365 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 0);
366 } catch (const uno::RuntimeException &) {
367 throw;
368 } catch (const uno::Exception & e) {
369 throw lang::WrappedTargetRuntimeException(
370 OUString("removeFile: exception"),
371 0, uno::makeAny(e));
375 static ::std::vector< uno::Reference< rdf::XURI > >
376 getAllParts(struct DocumentMetadataAccess_Impl & i_rImpl)
378 ::std::vector< uno::Reference< rdf::XURI > > ret;
379 try {
380 const uno::Reference<container::XEnumeration> xEnum(
381 i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI.get(),
382 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), 0),
383 uno::UNO_SET_THROW);
384 while (xEnum->hasMoreElements()) {
385 rdf::Statement stmt;
386 if (!(xEnum->nextElement() >>= stmt)) {
387 throw uno::RuntimeException();
389 const uno::Reference<rdf::XURI> xPart(stmt.Object,
390 uno::UNO_QUERY);
391 if (!xPart.is()) continue;
392 ret.push_back(xPart);
394 return ret;
395 } catch (const uno::RuntimeException &) {
396 throw;
397 } catch (const uno::Exception & e) {
398 throw lang::WrappedTargetRuntimeException(
399 OUString("getAllParts: exception"),
400 0, uno::makeAny(e));
404 static bool
405 isPartOfType(struct DocumentMetadataAccess_Impl & i_rImpl,
406 uno::Reference<rdf::XURI> const & i_xPart,
407 uno::Reference<rdf::XURI> const & i_xType)
409 if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException();
410 try {
411 const uno::Reference<container::XEnumeration> xEnum(
412 i_rImpl.m_xManifest->getStatements(i_xPart.get(),
413 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
414 i_xType.get()),
415 uno::UNO_SET_THROW);
416 return (xEnum->hasMoreElements());
417 } catch (const uno::RuntimeException &) {
418 throw;
419 } catch (const uno::Exception & e) {
420 throw lang::WrappedTargetRuntimeException(
421 OUString("isPartOfType: exception"),
422 0, uno::makeAny(e));
427 static ucb::InteractiveAugmentedIOException
428 mkException( OUString const & i_rMessage,
429 ucb::IOErrorCode const i_ErrorCode,
430 OUString const & i_rUri, OUString const & i_rResource)
432 ucb::InteractiveAugmentedIOException iaioe;
433 iaioe.Message = i_rMessage;
434 iaioe.Classification = task::InteractionClassification_ERROR;
435 iaioe.Code = i_ErrorCode;
437 const beans::PropertyValue uriProp(OUString("Uri"),
438 -1, uno::makeAny(i_rUri), static_cast<beans::PropertyState>(0));
439 const beans::PropertyValue rnProp(
440 OUString("ResourceName"),
441 -1, uno::makeAny(i_rResource), static_cast<beans::PropertyState>(0));
442 iaioe.Arguments = ::comphelper::makeSequence(
443 uno::makeAny(uriProp), uno::makeAny(rnProp));
444 return iaioe;
447 /** error handling policy.
448 <p>If a handler is given, ask it how to proceed:
449 <ul><li>(default:) cancel import, raise exception</li>
450 <li>ignore the error and continue</li>
451 <li>retry the action that led to the error</li></ul></p>
452 N.B.: must not be called before DMA is fully initalized!
453 @returns true iff caller should retry
455 static bool
456 handleError( ucb::InteractiveAugmentedIOException const & i_rException,
457 const uno::Reference<task::XInteractionHandler> & i_xHandler)
459 if (!i_xHandler.is()) {
460 throw lang::WrappedTargetException(OUString(
461 "DocumentMetadataAccess::loadMetadataFromStorage: exception"),
462 /* *this*/ 0, uno::makeAny(i_rException));
465 ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest(
466 new ::comphelper::OInteractionRequest(uno::makeAny(i_rException)) );
467 ::rtl::Reference< ::comphelper::OInteractionRetry > pRetry(
468 new ::comphelper::OInteractionRetry );
469 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove(
470 new ::comphelper::OInteractionApprove );
471 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort(
472 new ::comphelper::OInteractionAbort );
474 pRequest->addContinuation( pApprove.get() );
475 pRequest->addContinuation( pAbort.get() );
476 // actually call the handler
477 i_xHandler->handle( pRequest.get() );
478 if (pRetry->wasSelected()) {
479 return true;
480 } else if (pApprove->wasSelected()) {
481 return false;
482 } else {
483 OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?");
484 throw lang::WrappedTargetException(OUString(
485 "DocumentMetadataAccess::loadMetadataFromStorage: exception"),
486 /* *this*/ 0, uno::makeAny(i_rException));
490 /** check if storage has content.xml/styles.xml;
491 e.g. ODB files seem to only have content.xml */
492 static void
493 collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage,
494 OUString i_Path,
495 std::set< OUString > & o_rFiles)
497 static OUString content(s_content);
498 static OUString styles(s_styles );
499 try {
500 if (i_xStorage->hasByName(content) &&
501 i_xStorage->isStreamElement(content))
503 o_rFiles.insert(i_Path + content);
505 if (i_xStorage->hasByName(styles) &&
506 i_xStorage->isStreamElement(styles))
508 o_rFiles.insert(i_Path + styles);
510 } catch (const uno::Exception &) {
511 OSL_TRACE("collectFilesFromStorage: exception?");
515 /** import a metadata file into repository */
516 static void
517 readStream(struct DocumentMetadataAccess_Impl & i_rImpl,
518 uno::Reference< embed::XStorage > const & i_xStorage,
519 OUString const & i_rPath,
520 OUString const & i_rBaseURI)
522 OUString dir;
523 OUString rest;
524 try {
525 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
526 if (dir.isEmpty()) {
527 if (i_xStorage->isStreamElement(i_rPath)) {
528 const uno::Reference<io::XStream> xStream(
529 i_xStorage->openStreamElement(i_rPath,
530 embed::ElementModes::READ), uno::UNO_SET_THROW);
531 const uno::Reference<io::XInputStream> xInStream(
532 xStream->getInputStream(), uno::UNO_SET_THROW );
533 const uno::Reference<rdf::XURI> xBaseURI(
534 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
535 const uno::Reference<rdf::XURI> xURI(
536 rdf::URI::createNS(i_rImpl.m_xContext,
537 i_rBaseURI, i_rPath));
538 i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML,
539 xInStream, xURI, xBaseURI);
540 } else {
541 throw mkException(OUString(
542 "readStream: is not a stream"),
543 ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath);
545 } else {
546 if (i_xStorage->isStorageElement(dir)) {
547 const uno::Reference<embed::XStorage> xDir(
548 i_xStorage->openStorageElement(dir,
549 embed::ElementModes::READ));
550 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
551 uno::UNO_QUERY_THROW);
552 try {
553 OUString mimeType;
554 xDirProps->getPropertyValue(
555 ::comphelper::MediaDescriptor::PROP_MEDIATYPE() )
556 >>= mimeType;
557 if (mimeType.matchAsciiL(s_odfmime, sizeof(s_odfmime) - 1))
559 OSL_TRACE("readStream: "
560 "refusing to recurse into embedded document");
561 return;
563 } catch (const uno::Exception &) { }
564 OUStringBuffer buf(i_rBaseURI);
565 buf.append(dir).append(static_cast<sal_Unicode>('/'));
566 readStream(i_rImpl, xDir, rest, buf.makeStringAndClear() );
567 } else {
568 throw mkException(OUString(
569 "readStream: is not a directory"),
570 ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir);
573 } catch (const container::NoSuchElementException & e) {
574 throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH,
575 i_rBaseURI + i_rPath, i_rPath);
576 } catch (const io::IOException & e) {
577 throw mkException(e.Message, ucb::IOErrorCode_CANT_READ,
578 i_rBaseURI + i_rPath, i_rPath);
579 } catch (const rdf::ParseException & e) {
580 throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT,
581 i_rBaseURI + i_rPath, i_rPath);
585 /** import a metadata file into repository */
586 static void
587 importFile(struct DocumentMetadataAccess_Impl & i_rImpl,
588 uno::Reference<embed::XStorage> const & i_xStorage,
589 OUString const & i_rBaseURI,
590 uno::Reference<task::XInteractionHandler> const & i_xHandler,
591 OUString i_rPath)
593 retry:
594 try {
595 readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI);
596 } catch (const ucb::InteractiveAugmentedIOException & e) {
597 if (handleError(e, i_xHandler)) goto retry;
598 } catch (const uno::RuntimeException &) {
599 throw;
600 } catch (const uno::Exception & e) {
601 throw lang::WrappedTargetRuntimeException(
602 OUString("importFile: exception"),
603 0, uno::makeAny(e));
607 /** actually write a metadata file to the storage */
608 static void
609 exportStream(struct DocumentMetadataAccess_Impl & i_rImpl,
610 uno::Reference< embed::XStorage > const & i_xStorage,
611 uno::Reference<rdf::XURI> const & i_xGraphName,
612 OUString const & i_rFileName,
613 OUString const & i_rBaseURI)
615 const uno::Reference<io::XStream> xStream(
616 i_xStorage->openStreamElement(i_rFileName,
617 embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
618 uno::UNO_SET_THROW);
619 const uno::Reference< beans::XPropertySet > xStreamProps(xStream,
620 uno::UNO_QUERY);
621 if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
622 xStreamProps->setPropertyValue(
623 OUString("MediaType"),
624 uno::makeAny(OUString(s_rdfxml)));
626 const uno::Reference<io::XOutputStream> xOutStream(
627 xStream->getOutputStream(), uno::UNO_SET_THROW );
628 const uno::Reference<rdf::XURI> xBaseURI(
629 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
630 i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML,
631 xOutStream, i_xGraphName, xBaseURI);
634 /** write a metadata file to the storage */
635 static void
636 writeStream(struct DocumentMetadataAccess_Impl & i_rImpl,
637 uno::Reference< embed::XStorage > const & i_xStorage,
638 uno::Reference<rdf::XURI> const & i_xGraphName,
639 OUString const & i_rPath,
640 OUString const & i_rBaseURI)
642 OUString dir;
643 OUString rest;
644 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
645 try {
646 if (dir.isEmpty()) {
647 exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath,
648 i_rBaseURI);
649 } else {
650 const uno::Reference<embed::XStorage> xDir(
651 i_xStorage->openStorageElement(dir,
652 embed::ElementModes::WRITE));
653 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
654 uno::UNO_QUERY_THROW);
655 try {
656 OUString mimeType;
657 xDirProps->getPropertyValue(
658 ::comphelper::MediaDescriptor::PROP_MEDIATYPE() )
659 >>= mimeType;
660 if (mimeType.matchAsciiL(s_odfmime, sizeof(s_odfmime) - 1)) {
661 OSL_TRACE("writeStream: "
662 "refusing to recurse into embedded document");
663 return;
665 } catch (const uno::Exception &) { }
666 OUStringBuffer buf(i_rBaseURI);
667 buf.append(dir).append(static_cast<sal_Unicode>('/'));
668 writeStream(i_rImpl, xDir, i_xGraphName, rest,
669 buf.makeStringAndClear());
670 uno::Reference<embed::XTransactedObject> const xTransaction(
671 xDir, uno::UNO_QUERY);
672 if (xTransaction.is()) {
673 xTransaction->commit();
676 } catch (const uno::RuntimeException &) {
677 throw;
678 } catch (const io::IOException &) {
679 throw;
683 static void
684 initLoading(struct DocumentMetadataAccess_Impl & i_rImpl,
685 const uno::Reference< embed::XStorage > & i_xStorage,
686 const uno::Reference<rdf::XURI> & i_xBaseURI,
687 const uno::Reference<task::XInteractionHandler> & i_xHandler)
689 retry:
690 // clear old data
691 i_rImpl.m_xManifest.clear();
692 // init BaseURI
693 i_rImpl.m_xBaseURI = i_xBaseURI;
695 // create repository
696 i_rImpl.m_xRepository.clear();
697 i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext),
698 uno::UNO_SET_THROW);
700 const OUString manifest (
701 OUString::createFromAscii(s_manifest));
702 const OUString baseURI( i_xBaseURI->getStringValue() );
703 // try to delay raising errors until after initialization is done
704 uno::Any rterr;
705 ucb::InteractiveAugmentedIOException iaioe;
706 bool err(false);
708 const uno::Reference <rdf::XURI> xManifest(
709 getURIForStream(i_rImpl, manifest));
710 try {
711 readStream(i_rImpl, i_xStorage, manifest, baseURI);
712 } catch (const ucb::InteractiveAugmentedIOException & e) {
713 // no manifest.rdf: this is not an error in ODF < 1.2
714 if (!(ucb::IOErrorCode_NOT_EXISTING_PATH == e.Code)) {
715 iaioe = e;
716 err = true;
718 } catch (const uno::Exception & e) {
719 rterr <<= e;
722 // init manifest graph
723 const uno::Reference<rdf::XNamedGraph> xManifestGraph(
724 i_rImpl.m_xRepository->getGraph(xManifest));
725 i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
726 i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
727 const uno::Reference<container::XEnumeration> xEnum(
728 i_rImpl.m_xManifest->getStatements(0,
729 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
730 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get()));
732 // document statement
733 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
734 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
735 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get());
737 OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null");
738 OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null");
739 OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null");
741 if (rterr.hasValue()) {
742 throw lang::WrappedTargetRuntimeException(
743 OUString(
744 "DocumentMetadataAccess::loadMetadataFromStorage: "
745 "exception"), 0, rterr);
748 if (err) {
749 if (handleError(iaioe, i_xHandler)) goto retry;
753 /** init Impl struct */
754 static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
756 try {
758 i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
759 getURIForStream(i_rImpl,
760 OUString::createFromAscii(s_manifest))),
761 uno::UNO_SET_THROW);
763 // insert the document statement
764 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
765 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
766 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get());
767 } catch (const uno::Exception & e) {
768 throw lang::WrappedTargetRuntimeException(
769 OUString("init: unexpected exception"), 0,
770 uno::makeAny(e));
773 // add top-level content files
774 if (!addContentOrStylesFileImpl(i_rImpl,
775 OUString::createFromAscii(s_content))) {
776 throw uno::RuntimeException();
778 if (!addContentOrStylesFileImpl(i_rImpl,
779 OUString::createFromAscii(s_styles))) {
780 throw uno::RuntimeException();
786 DocumentMetadataAccess::DocumentMetadataAccess(
787 uno::Reference< uno::XComponentContext > const & i_xContext,
788 const IXmlIdRegistrySupplier & i_rRegistrySupplier)
789 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
791 // no initalization: must call loadFrom...
794 DocumentMetadataAccess::DocumentMetadataAccess(
795 uno::Reference< uno::XComponentContext > const & i_xContext,
796 const IXmlIdRegistrySupplier & i_rRegistrySupplier,
797 OUString const & i_rURI)
798 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
800 OSL_ENSURE(!i_rURI.isEmpty(), "DMA::DMA: no URI given!");
801 OSL_ENSURE(i_rURI.endsWithAsciiL("/", 1), "DMA::DMA: URI without / given!");
802 if (!i_rURI.endsWithAsciiL("/", 1)) throw uno::RuntimeException();
803 m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI));
804 m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext),
805 uno::UNO_SET_THROW);
807 // init repository
808 init(*m_pImpl);
810 OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null");
811 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null");
812 OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null");
815 DocumentMetadataAccess::~DocumentMetadataAccess()
819 // ::com::sun::star::rdf::XRepositorySupplier:
820 uno::Reference< rdf::XRepository > SAL_CALL
821 DocumentMetadataAccess::getRDFRepository() throw (uno::RuntimeException)
823 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized");
824 return m_pImpl->m_xRepository;
827 // ::com::sun::star::rdf::XNode:
828 OUString SAL_CALL
829 DocumentMetadataAccess::getStringValue() throw (uno::RuntimeException)
831 return m_pImpl->m_xBaseURI->getStringValue();
834 // ::com::sun::star::rdf::XURI:
835 OUString SAL_CALL
836 DocumentMetadataAccess::getNamespace() throw (uno::RuntimeException)
838 return m_pImpl->m_xBaseURI->getNamespace();
841 OUString SAL_CALL
842 DocumentMetadataAccess::getLocalName() throw (uno::RuntimeException)
844 return m_pImpl->m_xBaseURI->getLocalName();
847 // ::com::sun::star::rdf::XDocumentMetadataAccess:
848 uno::Reference< rdf::XMetadatable > SAL_CALL
849 DocumentMetadataAccess::getElementByMetadataReference(
850 const ::com::sun::star::beans::StringPair & i_rReference)
851 throw (uno::RuntimeException)
853 const IXmlIdRegistry * pReg(
854 m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() );
855 if (!pReg) {
856 throw uno::RuntimeException(OUString(
857 "DocumentMetadataAccess::getElementByXmlId: no registry"), *this);
859 return pReg->GetElementByMetadataReference(i_rReference);
862 uno::Reference< rdf::XMetadatable > SAL_CALL
863 DocumentMetadataAccess::getElementByURI(
864 const uno::Reference< rdf::XURI > & i_xURI )
865 throw (uno::RuntimeException, lang::IllegalArgumentException)
867 if (!i_xURI.is()) {
868 throw lang::IllegalArgumentException(OUString(
869 "DocumentMetadataAccess::getElementByURI: URI is null"), *this, 0);
872 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
873 const OUString name( i_xURI->getStringValue() );
874 if (!name.match(baseURI)) {
875 return 0;
877 const OUString relName( name.copy(baseURI.getLength()) );
878 OUString path;
879 OUString idref;
880 if (!splitXmlId(relName, path, idref)) {
881 return 0;
884 return getElementByMetadataReference( beans::StringPair(path, idref) );
888 uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL
889 DocumentMetadataAccess::getMetadataGraphsWithType(
890 const uno::Reference<rdf::XURI> & i_xType)
891 throw (uno::RuntimeException, lang::IllegalArgumentException)
893 if (!i_xType.is()) {
894 throw lang::IllegalArgumentException(OUString(
895 "DocumentMetadataAccess::getMetadataGraphsWithType: "
896 "type is null"), *this, 0);
899 ::comphelper::SequenceAsVector< uno::Reference< rdf::XURI > > ret;
900 const ::std::vector< uno::Reference< rdf::XURI > > parts(
901 getAllParts(*m_pImpl) );
902 ::std::remove_copy_if(parts.begin(), parts.end(),
903 ::std::back_inserter(ret),
904 ::boost::bind(
905 ::std::logical_not<bool>(),
906 ::boost::bind(&isPartOfType, ::boost::ref(*m_pImpl), _1, i_xType) ));
907 return ret.getAsConstList();
910 uno::Reference<rdf::XURI> SAL_CALL
911 DocumentMetadataAccess::addMetadataFile(const OUString & i_rFileName,
912 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
913 throw (uno::RuntimeException, lang::IllegalArgumentException,
914 container::ElementExistException)
916 if (!isFileNameValid(i_rFileName)) {
917 throw lang::IllegalArgumentException(OUString(
918 "DocumentMetadataAccess::addMetadataFile: invalid FileName"),
919 *this, 0);
921 if (isReservedFile(i_rFileName)) {
922 throw lang::IllegalArgumentException(OUString(
923 "DocumentMetadataAccess::addMetadataFile:"
924 "invalid FileName: reserved"), *this, 0);
926 for (sal_Int32 i = 0; i < i_rTypes.getLength(); ++i) {
927 if (!i_rTypes[i].is()) {
928 throw lang::IllegalArgumentException(
929 OUString(
930 "DocumentMetadataAccess::addMetadataFile: "
931 "null type"), *this, 2);
935 const uno::Reference<rdf::XURI> xGraphName(
936 getURIForStream(*m_pImpl, i_rFileName) );
938 try {
939 m_pImpl->m_xRepository->createGraph(xGraphName);
940 } catch (const rdf::RepositoryException & e) {
941 throw lang::WrappedTargetRuntimeException(
942 OUString(
943 "DocumentMetadataAccess::addMetadataFile: exception"),
944 *this, uno::makeAny(e));
945 // note: all other exceptions are propagated
948 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
949 return xGraphName;
952 uno::Reference<rdf::XURI> SAL_CALL
953 DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format,
954 const uno::Reference< io::XInputStream > & i_xInStream,
955 const OUString & i_rFileName,
956 const uno::Reference< rdf::XURI > & i_xBaseURI,
957 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
958 throw (uno::RuntimeException, lang::IllegalArgumentException,
959 datatransfer::UnsupportedFlavorException,
960 container::ElementExistException, rdf::ParseException, io::IOException)
962 if (!isFileNameValid(i_rFileName)) {
963 throw lang::IllegalArgumentException(OUString(
964 "DocumentMetadataAccess::importMetadataFile: invalid FileName"),
965 *this, 0);
967 if (isReservedFile(i_rFileName)) {
968 throw lang::IllegalArgumentException(OUString(
969 "DocumentMetadataAccess::importMetadataFile:"
970 "invalid FileName: reserved"), *this, 0);
972 for (sal_Int32 i = 0; i < i_rTypes.getLength(); ++i) {
973 if (!i_rTypes[i].is()) {
974 throw lang::IllegalArgumentException(
975 OUString(
976 "DocumentMetadataAccess::importMetadataFile: null type"),
977 *this, 5);
981 const uno::Reference<rdf::XURI> xGraphName(
982 getURIForStream(*m_pImpl, i_rFileName) );
984 try {
985 m_pImpl->m_xRepository->importGraph(
986 i_Format, i_xInStream, xGraphName, i_xBaseURI);
987 } catch (const rdf::RepositoryException & e) {
988 throw lang::WrappedTargetRuntimeException(
989 OUString(
990 "DocumentMetadataAccess::importMetadataFile: "
991 "RepositoryException"), *this, uno::makeAny(e));
992 // note: all other exceptions are propagated
995 // add to manifest
996 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
997 return xGraphName;
1000 void SAL_CALL
1001 DocumentMetadataAccess::removeMetadataFile(
1002 const uno::Reference< rdf::XURI > & i_xGraphName)
1003 throw (uno::RuntimeException, lang::IllegalArgumentException,
1004 container::NoSuchElementException)
1006 try {
1007 m_pImpl->m_xRepository->destroyGraph(i_xGraphName);
1008 } catch (const rdf::RepositoryException & e) {
1009 throw lang::WrappedTargetRuntimeException(
1010 OUString(
1011 "DocumentMetadataAccess::removeMetadataFile: "
1012 "RepositoryException"), *this, uno::makeAny(e));
1013 // note: all other exceptions are propagated
1016 // remove file from manifest
1017 removeFile(*m_pImpl, i_xGraphName.get());
1020 void SAL_CALL
1021 DocumentMetadataAccess::addContentOrStylesFile(
1022 const OUString & i_rFileName)
1023 throw (uno::RuntimeException, lang::IllegalArgumentException,
1024 container::ElementExistException)
1026 if (!isFileNameValid(i_rFileName)) {
1027 throw lang::IllegalArgumentException(OUString(
1028 "DocumentMetadataAccess::addContentOrStylesFile: "
1029 "invalid FileName"), *this, 0);
1032 if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) {
1033 throw lang::IllegalArgumentException(OUString(
1034 "DocumentMetadataAccess::addContentOrStylesFile: "
1035 "invalid FileName: must end with content.xml or styles.xml"),
1036 *this, 0);
1040 void SAL_CALL
1041 DocumentMetadataAccess::removeContentOrStylesFile(
1042 const OUString & i_rFileName)
1043 throw (uno::RuntimeException, lang::IllegalArgumentException,
1044 container::NoSuchElementException)
1046 if (!isFileNameValid(i_rFileName)) {
1047 throw lang::IllegalArgumentException(OUString(
1048 "DocumentMetadataAccess::removeContentOrStylesFile: "
1049 "invalid FileName"), *this, 0);
1052 try {
1053 const uno::Reference<rdf::XURI> xPart(
1054 getURIForStream(*m_pImpl, i_rFileName) );
1055 const uno::Reference<container::XEnumeration> xEnum(
1056 m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI.get(),
1057 getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext),
1058 xPart.get()),
1059 uno::UNO_SET_THROW);
1060 if (!xEnum->hasMoreElements()) {
1061 throw container::NoSuchElementException(
1062 OUString(
1063 "DocumentMetadataAccess::removeContentOrStylesFile: "
1064 "cannot find stream in manifest graph: ") + i_rFileName,
1065 *this);
1068 // remove file from manifest
1069 removeFile(*m_pImpl, xPart);
1071 } catch (const uno::RuntimeException &) {
1072 throw;
1073 } catch (const uno::Exception & e) {
1074 throw lang::WrappedTargetRuntimeException(
1075 OUString(
1076 "DocumentMetadataAccess::removeContentOrStylesFile: exception"),
1077 *this, uno::makeAny(e));
1081 void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
1082 const uno::Reference< embed::XStorage > & i_xStorage,
1083 const uno::Reference<rdf::XURI> & i_xBaseURI,
1084 const uno::Reference<task::XInteractionHandler> & i_xHandler)
1085 throw (uno::RuntimeException, lang::IllegalArgumentException,
1086 lang::WrappedTargetException)
1088 if (!i_xStorage.is()) {
1089 throw lang::IllegalArgumentException(OUString(
1090 "DocumentMetadataAccess::loadMetadataFromStorage: "
1091 "storage is null"), *this, 0);
1093 if (!i_xBaseURI.is()) {
1094 throw lang::IllegalArgumentException(OUString(
1095 "DocumentMetadataAccess::loadMetadataFromStorage: "
1096 "base URI is null"), *this, 1);
1098 const OUString baseURI( i_xBaseURI->getStringValue());
1099 if (baseURI.indexOf('#') >= 0) {
1100 throw lang::IllegalArgumentException(OUString(
1101 "DocumentMetadataAccess::loadMetadataFromStorage: "
1102 "base URI not absolute"), *this, 1);
1104 if (baseURI.isEmpty() || !baseURI.endsWithAsciiL("/", 1)) {
1105 throw lang::IllegalArgumentException(OUString(
1106 "DocumentMetadataAccess::loadMetadataFromStorage: "
1107 "base URI does not end with slash"), *this, 1);
1110 initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler);
1112 std::set< OUString > StgFiles;
1113 collectFilesFromStorage(i_xStorage,
1114 OUString(""), StgFiles);
1116 std::vector< OUString > MfstMetadataFiles;
1118 try {
1119 const ::std::vector< uno::Reference< rdf::XURI > > parts(
1120 getAllParts(*m_pImpl) );
1121 const uno::Reference<rdf::XURI> xContentFile(
1122 getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
1123 const uno::Reference<rdf::XURI> xStylesFile(
1124 getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext));
1125 const uno::Reference<rdf::XURI> xMetadataFile(
1126 getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext));
1127 const sal_Int32 len( baseURI.getLength() );
1128 const OUString manifest (
1129 OUString::createFromAscii(s_manifest));
1130 for (::std::vector< uno::Reference< rdf::XURI > >::const_iterator it
1131 = parts.begin();
1132 it != parts.end(); ++it) {
1133 const OUString name((*it)->getStringValue());
1134 if (!name.match(baseURI)) {
1135 OSL_TRACE("loadMetadataFromStorage: graph not in document: %s",
1136 OUStringToOString(name, RTL_TEXTENCODING_UTF8)
1137 .getStr());
1138 continue;
1140 const OUString relName( name.copy(len) );
1141 if (relName == manifest) {
1142 OSL_TRACE("loadMetadataFromStorage: "
1143 "found ourselves a recursive manifest!");
1144 continue;
1146 // remove found items from StgFiles
1147 StgFiles.erase(relName);
1148 if (isContentFile(relName)) {
1149 if (!isPartOfType(*m_pImpl, *it, xContentFile)) {
1150 const uno::Reference <rdf::XURI> xName(
1151 getURIForStream(*m_pImpl, relName) );
1152 // add missing type statement
1153 m_pImpl->m_xManifest->addStatement(xName.get(),
1154 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1155 xContentFile.get());
1157 } else if (isStylesFile(relName)) {
1158 if (!isPartOfType(*m_pImpl, *it, xStylesFile)) {
1159 const uno::Reference <rdf::XURI> xName(
1160 getURIForStream(*m_pImpl, relName) );
1161 // add missing type statement
1162 m_pImpl->m_xManifest->addStatement(xName.get(),
1163 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1164 xStylesFile.get());
1166 } else if (isReservedFile(relName)) {
1167 OSL_TRACE("loadMetadataFromStorage: "
1168 "reserved file name in manifest");
1169 } else {
1170 if (isPartOfType(*m_pImpl, *it, xMetadataFile)) {
1171 MfstMetadataFiles.push_back(relName);
1173 // do not add statement for MetadataFile; it could be
1174 // something else! just ignore it...
1177 } catch (const uno::RuntimeException &) {
1178 throw;
1179 } catch (const uno::Exception & e) {
1180 throw lang::WrappedTargetRuntimeException(
1181 OUString(
1182 "DocumentMetadataAccess::loadMetadataFromStorage: "
1183 "exception"), *this, uno::makeAny(e));
1186 std::for_each(StgFiles.begin(), StgFiles.end(),
1187 boost::bind(addContentOrStylesFileImpl, boost::ref(*m_pImpl), _1));
1189 std::for_each(MfstMetadataFiles.begin(), MfstMetadataFiles.end(),
1190 boost::bind(importFile, boost::ref(*m_pImpl),
1191 i_xStorage, baseURI, i_xHandler, _1));
1194 void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage(
1195 const uno::Reference< embed::XStorage > & i_xStorage)
1196 throw (uno::RuntimeException, lang::IllegalArgumentException,
1197 lang::WrappedTargetException)
1199 if (!i_xStorage.is()) {
1200 throw lang::IllegalArgumentException(OUString(
1201 "DocumentMetadataAccess::storeMetadataToStorage: "
1202 "storage is null"), *this, 0);
1205 // export manifest
1206 const OUString manifest (
1207 OUString::createFromAscii(s_manifest));
1208 const uno::Reference <rdf::XURI> xManifest(
1209 getURIForStream(*m_pImpl, manifest) );
1210 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
1211 try {
1212 writeStream(*m_pImpl, i_xStorage, xManifest, manifest, baseURI);
1213 } catch (const uno::RuntimeException &) {
1214 throw;
1215 } catch (const io::IOException & e) {
1216 throw lang::WrappedTargetException( OUString(
1217 "storeMetadataToStorage: IO exception"), *this, uno::makeAny(e));
1218 } catch (const uno::Exception & e) {
1219 throw lang::WrappedTargetRuntimeException(
1220 OUString(
1221 "storeMetadataToStorage: exception"), *this, uno::makeAny(e));
1224 // export metadata streams
1225 try {
1226 const uno::Sequence<uno::Reference<rdf::XURI> > graphs(
1227 m_pImpl->m_xRepository->getGraphNames());
1228 const sal_Int32 len( baseURI.getLength() );
1229 for (sal_Int32 i = 0; i < graphs.getLength(); ++i) {
1230 const uno::Reference<rdf::XURI> xName(graphs[i]);
1231 const OUString name(xName->getStringValue());
1232 if (!name.match(baseURI)) {
1233 OSL_TRACE("storeMetadataToStorage: graph not in document: %s",
1234 OUStringToOString(name, RTL_TEXTENCODING_UTF8)
1235 .getStr());
1236 continue;
1238 const OUString relName( name.copy(len) );
1239 if (relName == manifest) {
1240 continue;
1242 if (!isFileNameValid(relName) || isReservedFile(relName)) {
1243 OSL_TRACE("storeMetadataToStorage: invalid file name: %s",
1244 OUStringToOString(relName, RTL_TEXTENCODING_UTF8)
1245 .getStr());
1246 continue;
1248 try {
1249 writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI);
1250 } catch (const uno::RuntimeException &) {
1251 throw;
1252 } catch (const io::IOException & e) {
1253 throw lang::WrappedTargetException(
1254 OUString(
1255 "storeMetadataToStorage: IO exception"),
1256 *this, uno::makeAny(e));
1257 } catch (const uno::Exception & e) {
1258 throw lang::WrappedTargetRuntimeException(
1259 OUString(
1260 "storeMetadataToStorage: exception"),
1261 *this, uno::makeAny(e));
1264 } catch (const rdf::RepositoryException & e) {
1265 throw lang::WrappedTargetRuntimeException(
1266 OUString(
1267 "storeMetadataToStorage: exception"), *this, uno::makeAny(e));
1271 void SAL_CALL
1272 DocumentMetadataAccess::loadMetadataFromMedium(
1273 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1274 throw (uno::RuntimeException, lang::IllegalArgumentException,
1275 lang::WrappedTargetException)
1277 uno::Reference<io::XInputStream> xIn;
1278 ::comphelper::MediaDescriptor md(i_rMedium);
1279 OUString URL;
1280 md[ ::comphelper::MediaDescriptor::PROP_URL() ] >>= URL;
1281 OUString BaseURL;
1282 md[ ::comphelper::MediaDescriptor::PROP_DOCUMENTBASEURL() ] >>= BaseURL;
1283 if (md.addInputStream()) {
1284 md[ ::comphelper::MediaDescriptor::PROP_INPUTSTREAM() ] >>= xIn;
1286 if (!xIn.is() && URL.isEmpty()) {
1287 throw lang::IllegalArgumentException(OUString(
1288 "DocumentMetadataAccess::loadMetadataFromMedium: "
1289 "inalid medium: no URL, no input stream"), *this, 0);
1291 uno::Reference<embed::XStorage> xStorage;
1292 try {
1293 if (xIn.is()) {
1294 xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
1295 xIn, m_pImpl->m_xContext);
1296 } else { // fallback to url
1297 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1298 URL, embed::ElementModes::READ, m_pImpl->m_xContext);
1300 } catch (const uno::RuntimeException &) {
1301 throw;
1302 } catch (const io::IOException &) {
1303 throw;
1304 } catch (const uno::Exception & e) {
1305 throw lang::WrappedTargetException(
1306 OUString(
1307 "DocumentMetadataAccess::loadMetadataFromMedium: "
1308 "exception"), *this, uno::makeAny(e));
1310 if (!xStorage.is()) {
1311 throw uno::RuntimeException(OUString(
1312 "DocumentMetadataAccess::loadMetadataFromMedium: "
1313 "cannot get Storage"), *this);
1315 uno::Reference<rdf::XURI> xBaseURI;
1316 try {
1317 xBaseURI = createBaseURI(m_pImpl->m_xContext, xStorage, BaseURL);
1318 } catch (const uno::Exception &) {
1319 // fall back to URL
1320 try {
1321 xBaseURI = createBaseURI(m_pImpl->m_xContext, xStorage, URL);
1322 } catch (const uno::Exception &) {
1323 OSL_FAIL("cannot create base URI");
1326 uno::Reference<task::XInteractionHandler> xIH;
1327 md[ ::comphelper::MediaDescriptor::PROP_INTERACTIONHANDLER() ] >>= xIH;
1328 loadMetadataFromStorage(xStorage, xBaseURI, xIH);
1331 void SAL_CALL
1332 DocumentMetadataAccess::storeMetadataToMedium(
1333 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1334 throw (uno::RuntimeException, lang::IllegalArgumentException,
1335 lang::WrappedTargetException)
1337 ::comphelper::MediaDescriptor md(i_rMedium);
1338 OUString URL;
1339 md[ ::comphelper::MediaDescriptor::PROP_URL() ] >>= URL;
1340 if (URL.isEmpty()) {
1341 throw lang::IllegalArgumentException(OUString(
1342 "DocumentMetadataAccess::storeMetadataToMedium: "
1343 "invalid medium: no URL"), *this, 0);
1346 SfxMedium aMedium(i_rMedium);
1347 uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage());
1349 bool sfx(false);
1350 if (xStorage.is()) {
1351 sfx = true;
1352 } else {
1353 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1354 URL, embed::ElementModes::WRITE, m_pImpl->m_xContext);
1357 if (!xStorage.is()) {
1358 throw uno::RuntimeException(OUString(
1359 "DocumentMetadataAccess::storeMetadataToMedium: "
1360 "cannot get Storage"), *this);
1362 // set MIME type of the storage
1363 ::comphelper::MediaDescriptor::const_iterator iter
1364 = md.find(::comphelper::MediaDescriptor::PROP_MEDIATYPE());
1365 if (iter != md.end()) {
1366 uno::Reference< beans::XPropertySet > xProps(xStorage,
1367 uno::UNO_QUERY_THROW);
1368 try {
1369 // this is NOT supported in FileSystemStorage
1370 xProps->setPropertyValue(
1371 ::comphelper::MediaDescriptor::PROP_MEDIATYPE(),
1372 iter->second);
1373 } catch (const uno::Exception &) { }
1375 storeMetadataToStorage(xStorage);
1377 if (sfx) {
1378 const sal_Bool bOk = aMedium.Commit();
1379 aMedium.Close();
1380 if ( !bOk ) {
1381 sal_uInt32 nError = aMedium.GetError();
1382 if ( nError == ERRCODE_NONE ) {
1383 nError = ERRCODE_IO_GENERAL;
1385 task::ErrorCodeIOException ex( OUString(),
1386 uno::Reference< uno::XInterface >(), nError);
1387 throw lang::WrappedTargetException(OUString(), *this,
1388 uno::makeAny(ex));
1393 } // namespace sfx2
1395 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */