Branch libreoffice-5-0-4
[LibreOffice.git] / sfx2 / source / doc / DocumentMetadataAccess.cxx
blob228f57878a34bd501bba9f09640923c28e077eb6
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 <unotools/mediadescriptor.hxx>
44 #include <comphelper/sequence.hxx>
45 #include <comphelper/storagehelper.hxx>
47 #include <sfx2/docfile.hxx>
48 #include <sfx2/XmlIdRegistry.hxx>
50 #include <libxml/tree.h>
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.matchIgnoreAsciiCase("vnd.sun.star.expand:"))
137 // expand it here (makeAbsolute requires hierarchical URI)
138 pkgURI = pkgURI.copy( RTL_CONSTASCII_LENGTH("vnd.sun.star.expand:") );
139 if (!pkgURI.isEmpty()) {
140 pkgURI = ::rtl::Uri::decode(
141 pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8);
142 if (pkgURI.isEmpty()) {
143 throw uno::RuntimeException();
145 ::rtl::Bootstrap::expandMacros(pkgURI);
149 const uno::Reference<uri::XUriReferenceFactory> xUriFactory =
150 uri::UriReferenceFactory::create( i_xContext);
151 uno::Reference< uri::XUriReference > xBaseURI;
153 const uno::Reference< uri::XUriReference > xPkgURI(
154 xUriFactory->parse(pkgURI), uno::UNO_SET_THROW );
155 xPkgURI->clearFragment();
157 // need to know whether the storage is a FileSystemStorage
158 // XServiceInfo would be better, but it is not implemented
159 // if ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) )
160 if (true) {
161 xBaseURI.set( xPkgURI, uno::UNO_SET_THROW );
163 OUStringBuffer buf;
164 if (!xBaseURI->getUriReference().endsWith("/"))
166 const sal_Int32 count( xBaseURI->getPathSegmentCount() );
167 if (count > 0)
169 const OUString last( xBaseURI->getPathSegment(count - 1) );
170 buf.append(last);
172 buf.append('/');
174 if (!i_rSubDocument.isEmpty())
176 buf.append(i_rSubDocument);
177 buf.append('/');
179 const OUString Path(buf.makeStringAndClear());
180 if (!Path.isEmpty())
182 const uno::Reference< uri::XUriReference > xPathURI(
183 xUriFactory->parse(Path), uno::UNO_SET_THROW );
184 xBaseURI.set(
185 xUriFactory->makeAbsolute(xBaseURI, xPathURI,
186 true, uri::RelativeUriExcessParentSegments_ERROR),
187 uno::UNO_SET_THROW);
190 return rdf::URI::create(i_xContext, xBaseURI->getUriReference());
194 struct DocumentMetadataAccess_Impl
196 // note: these are all initialized in constructor, and loadFromStorage
197 const uno::Reference<uno::XComponentContext> m_xContext;
198 const IXmlIdRegistrySupplier & m_rXmlIdRegistrySupplier;
199 uno::Reference<rdf::XURI> m_xBaseURI;
200 uno::Reference<rdf::XRepository> m_xRepository;
201 uno::Reference<rdf::XNamedGraph> m_xManifest;
202 DocumentMetadataAccess_Impl(
203 uno::Reference<uno::XComponentContext> const& i_xContext,
204 IXmlIdRegistrySupplier const & i_rRegistrySupplier)
205 : m_xContext(i_xContext)
206 , m_rXmlIdRegistrySupplier(i_rRegistrySupplier)
207 , m_xBaseURI()
208 , m_xRepository()
209 , m_xManifest()
211 OSL_ENSURE(m_xContext.is(), "context null");
215 // this is... a hack.
216 template<sal_Int16 Constant>
217 /*static*/ uno::Reference<rdf::XURI>
218 getURI(uno::Reference< uno::XComponentContext > const & i_xContext)
220 static uno::Reference< rdf::XURI > xURI(
221 rdf::URI::createKnown(i_xContext, Constant), uno::UNO_QUERY_THROW);
222 return xURI;
226 /** would storing the file to a XStorage succeed? */
227 static bool isFileNameValid(const OUString & i_rFileName)
229 if (i_rFileName.isEmpty()) return false;
230 if (i_rFileName[0] == '/') return false; // no absolute paths!
231 sal_Int32 idx(0);
232 do {
233 const OUString segment(
234 i_rFileName.getToken(0, static_cast<sal_Unicode> ('/'), idx) );
235 if (segment.isEmpty() || // no empty segments
236 segment == "." || // no . segments
237 segment == ".." || // no .. segments
238 !::comphelper::OStorageHelper::IsValidZipEntryFileName(
239 segment, false)) // no invalid characters
240 return false;
241 } while (idx >= 0);
242 return true;
245 /** split a uri hierarchy into first segment and rest */
246 static bool
247 splitPath(OUString const & i_rPath,
248 OUString & o_rDir, OUString& o_rRest)
250 const sal_Int32 idx(i_rPath.indexOf(static_cast<sal_Unicode>('/')));
251 if (idx < 0 || idx >= i_rPath.getLength()) {
252 o_rDir.clear();
253 o_rRest = i_rPath;
254 return true;
255 } else if (idx == 0 || idx == i_rPath.getLength() - 1) {
256 // input must not start or end with '/'
257 return false;
258 } else {
259 o_rDir = (i_rPath.copy(0, idx));
260 o_rRest = (i_rPath.copy(idx+1));
261 return true;
265 static bool
266 splitXmlId(OUString const & i_XmlId,
267 OUString & o_StreamName, OUString& o_Idref )
269 const sal_Int32 idx(i_XmlId.indexOf(static_cast<sal_Unicode>('#')));
270 if ((idx <= 0) || (idx >= i_XmlId.getLength() - 1)) {
271 return false;
272 } else {
273 o_StreamName = (i_XmlId.copy(0, idx));
274 o_Idref = (i_XmlId.copy(idx+1));
275 return isValidXmlId(o_StreamName, o_Idref);
280 static uno::Reference<rdf::XURI>
281 getURIForStream(struct DocumentMetadataAccess_Impl& i_rImpl,
282 OUString const& i_rPath)
284 const uno::Reference<rdf::XURI> xURI(
285 rdf::URI::createNS( i_rImpl.m_xContext,
286 i_rImpl.m_xBaseURI->getStringValue(), i_rPath),
287 uno::UNO_SET_THROW);
288 return xURI;
291 /** add statements declaring i_xResource to be a file of type i_xType with
292 path i_rPath to manifest, with optional additional types i_pTypes */
293 static void
294 addFile(struct DocumentMetadataAccess_Impl & i_rImpl,
295 uno::Reference<rdf::XURI> const& i_xType,
296 OUString const & i_rPath,
297 const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes = 0)
299 try {
300 const uno::Reference<rdf::XURI> xURI( getURIForStream(
301 i_rImpl, i_rPath) );
303 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
304 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
305 xURI.get());
306 i_rImpl.m_xManifest->addStatement(xURI.get(),
307 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
308 i_xType.get());
309 if (i_pTypes) {
310 for (sal_Int32 i = 0; i < i_pTypes->getLength(); ++i) {
311 i_rImpl.m_xManifest->addStatement(xURI.get(),
312 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
313 (*i_pTypes)[i].get());
316 } catch (const uno::RuntimeException &) {
317 throw;
318 } catch (const uno::Exception & e) {
319 throw lang::WrappedTargetRuntimeException(
320 "addFile: exception", /*this*/0, uno::makeAny(e));
324 /** add content.xml or styles.xml to manifest */
325 static bool
326 addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl & i_rImpl,
327 const OUString & i_rPath)
329 uno::Reference<rdf::XURI> xType;
330 if (isContentFile(i_rPath)) {
331 xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext));
332 } else if (isStylesFile(i_rPath)) {
333 xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext));
334 } else {
335 return false;
337 addFile(i_rImpl, xType.get(), i_rPath);
338 return true;
341 /** add metadata file to manifest */
342 static void
343 addMetadataFileImpl(struct DocumentMetadataAccess_Impl & i_rImpl,
344 const OUString & i_rPath,
345 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
347 addFile(i_rImpl,
348 getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext),
349 i_rPath, &i_rTypes);
352 /** remove a file from the manifest */
353 static void
354 removeFile(struct DocumentMetadataAccess_Impl & i_rImpl,
355 uno::Reference<rdf::XURI> const& i_xPart)
357 if (!i_xPart.is()) throw uno::RuntimeException();
358 try {
359 i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI.get(),
360 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
361 i_xPart.get());
362 i_rImpl.m_xManifest->removeStatements(i_xPart.get(),
363 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 0);
364 } catch (const uno::RuntimeException &) {
365 throw;
366 } catch (const uno::Exception & e) {
367 throw lang::WrappedTargetRuntimeException(
368 "removeFile: exception",
369 0, uno::makeAny(e));
373 static ::std::vector< uno::Reference< rdf::XURI > >
374 getAllParts(struct DocumentMetadataAccess_Impl & i_rImpl)
376 ::std::vector< uno::Reference< rdf::XURI > > ret;
377 try {
378 const uno::Reference<container::XEnumeration> xEnum(
379 i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI.get(),
380 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), 0),
381 uno::UNO_SET_THROW);
382 while (xEnum->hasMoreElements()) {
383 rdf::Statement stmt;
384 if (!(xEnum->nextElement() >>= stmt)) {
385 throw uno::RuntimeException();
387 const uno::Reference<rdf::XURI> xPart(stmt.Object,
388 uno::UNO_QUERY);
389 if (!xPart.is()) continue;
390 ret.push_back(xPart);
392 return ret;
393 } catch (const uno::RuntimeException &) {
394 throw;
395 } catch (const uno::Exception & e) {
396 throw lang::WrappedTargetRuntimeException(
397 "getAllParts: exception",
398 0, uno::makeAny(e));
402 static bool
403 isPartOfType(struct DocumentMetadataAccess_Impl & i_rImpl,
404 uno::Reference<rdf::XURI> const & i_xPart,
405 uno::Reference<rdf::XURI> const & i_xType)
407 if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException();
408 try {
409 const uno::Reference<container::XEnumeration> xEnum(
410 i_rImpl.m_xManifest->getStatements(i_xPart.get(),
411 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
412 i_xType.get()),
413 uno::UNO_SET_THROW);
414 return (xEnum->hasMoreElements());
415 } catch (const uno::RuntimeException &) {
416 throw;
417 } catch (const uno::Exception & e) {
418 throw lang::WrappedTargetRuntimeException(
419 "isPartOfType: exception",
420 0, uno::makeAny(e));
425 static ucb::InteractiveAugmentedIOException
426 mkException( OUString const & i_rMessage,
427 ucb::IOErrorCode const i_ErrorCode,
428 OUString const & i_rUri, OUString const & i_rResource)
430 ucb::InteractiveAugmentedIOException iaioe;
431 iaioe.Message = i_rMessage;
432 iaioe.Classification = task::InteractionClassification_ERROR;
433 iaioe.Code = i_ErrorCode;
435 const beans::PropertyValue uriProp(OUString("Uri"),
436 -1, uno::makeAny(i_rUri), static_cast<beans::PropertyState>(0));
437 const beans::PropertyValue rnProp(
438 OUString("ResourceName"),
439 -1, uno::makeAny(i_rResource), static_cast<beans::PropertyState>(0));
440 iaioe.Arguments = ::comphelper::makeSequence(
441 uno::makeAny(uriProp), uno::makeAny(rnProp));
442 return iaioe;
445 /** error handling policy.
446 <p>If a handler is given, ask it how to proceed:
447 <ul><li>(default:) cancel import, raise exception</li>
448 <li>ignore the error and continue</li>
449 <li>retry the action that led to the error</li></ul></p>
450 N.B.: must not be called before DMA is fully initalized!
451 @returns true iff caller should retry
453 static bool
454 handleError( ucb::InteractiveAugmentedIOException const & i_rException,
455 const uno::Reference<task::XInteractionHandler> & i_xHandler)
457 if (!i_xHandler.is()) {
458 throw lang::WrappedTargetException(
459 "DocumentMetadataAccess::loadMetadataFromStorage: exception",
460 /* *this*/ 0, uno::makeAny(i_rException));
463 ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest(
464 new ::comphelper::OInteractionRequest(uno::makeAny(i_rException)) );
465 ::rtl::Reference< ::comphelper::OInteractionRetry > pRetry(
466 new ::comphelper::OInteractionRetry );
467 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove(
468 new ::comphelper::OInteractionApprove );
469 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort(
470 new ::comphelper::OInteractionAbort );
472 pRequest->addContinuation( pApprove.get() );
473 pRequest->addContinuation( pAbort.get() );
474 // actually call the handler
475 i_xHandler->handle( pRequest.get() );
476 if (pRetry->wasSelected()) {
477 return true;
478 } else if (pApprove->wasSelected()) {
479 return false;
480 } else {
481 OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?");
482 throw lang::WrappedTargetException(
483 "DocumentMetadataAccess::loadMetadataFromStorage: exception",
484 /* *this*/ 0, uno::makeAny(i_rException));
488 /** check if storage has content.xml/styles.xml;
489 e.g. ODB files seem to only have content.xml */
490 static void
491 collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage,
492 const OUString& i_Path,
493 std::set< OUString > & o_rFiles)
495 static OUString content(s_content);
496 static OUString styles(s_styles );
497 try {
498 if (i_xStorage->hasByName(content) &&
499 i_xStorage->isStreamElement(content))
501 o_rFiles.insert(i_Path + content);
503 if (i_xStorage->hasByName(styles) &&
504 i_xStorage->isStreamElement(styles))
506 o_rFiles.insert(i_Path + styles);
508 } catch (const uno::Exception &) {
509 OSL_TRACE("collectFilesFromStorage: exception?");
513 /** import a metadata file into repository */
514 static void
515 readStream(struct DocumentMetadataAccess_Impl & i_rImpl,
516 uno::Reference< embed::XStorage > const & i_xStorage,
517 OUString const & i_rPath,
518 OUString const & i_rBaseURI)
520 OUString dir;
521 OUString rest;
522 try {
523 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
524 if (dir.isEmpty()) {
525 if (i_xStorage->isStreamElement(i_rPath)) {
526 const uno::Reference<io::XStream> xStream(
527 i_xStorage->openStreamElement(i_rPath,
528 embed::ElementModes::READ), uno::UNO_SET_THROW);
529 const uno::Reference<io::XInputStream> xInStream(
530 xStream->getInputStream(), uno::UNO_SET_THROW );
531 const uno::Reference<rdf::XURI> xBaseURI(
532 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
533 const uno::Reference<rdf::XURI> xURI(
534 rdf::URI::createNS(i_rImpl.m_xContext,
535 i_rBaseURI, i_rPath));
536 i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML,
537 xInStream, xURI, xBaseURI);
538 } else {
539 throw mkException(
540 "readStream: is not a stream",
541 ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath);
543 } else {
544 if (i_xStorage->isStorageElement(dir)) {
545 const uno::Reference<embed::XStorage> xDir(
546 i_xStorage->openStorageElement(dir,
547 embed::ElementModes::READ));
548 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
549 uno::UNO_QUERY_THROW);
550 try {
551 OUString mimeType;
552 xDirProps->getPropertyValue(
553 utl::MediaDescriptor::PROP_MEDIATYPE() )
554 >>= mimeType;
555 if (mimeType.startsWith(s_odfmime)) {
556 OSL_TRACE("readStream: "
557 "refusing to recurse into embedded document");
558 return;
560 } catch (const uno::Exception &) { }
561 OUStringBuffer buf(i_rBaseURI);
562 buf.append(dir).append('/');
563 readStream(i_rImpl, xDir, rest, buf.makeStringAndClear() );
564 } else {
565 throw mkException(
566 "readStream: is not a directory",
567 ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir);
570 } catch (const container::NoSuchElementException & e) {
571 throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH,
572 i_rBaseURI + i_rPath, i_rPath);
573 } catch (const io::IOException & e) {
574 throw mkException(e.Message, ucb::IOErrorCode_CANT_READ,
575 i_rBaseURI + i_rPath, i_rPath);
576 } catch (const rdf::ParseException & e) {
577 throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT,
578 i_rBaseURI + i_rPath, i_rPath);
582 /** import a metadata file into repository */
583 static void
584 importFile(struct DocumentMetadataAccess_Impl & i_rImpl,
585 uno::Reference<embed::XStorage> const & i_xStorage,
586 OUString const & i_rBaseURI,
587 uno::Reference<task::XInteractionHandler> const & i_xHandler,
588 const OUString& i_rPath)
590 retry:
591 try {
592 readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI);
593 } catch (const ucb::InteractiveAugmentedIOException & e) {
594 if (handleError(e, i_xHandler)) goto retry;
595 } catch (const uno::RuntimeException &) {
596 throw;
597 } catch (const uno::Exception & e) {
598 throw lang::WrappedTargetRuntimeException(
599 "importFile: exception",
600 0, uno::makeAny(e));
604 /** actually write a metadata file to the storage */
605 static void
606 exportStream(struct DocumentMetadataAccess_Impl & i_rImpl,
607 uno::Reference< embed::XStorage > const & i_xStorage,
608 uno::Reference<rdf::XURI> const & i_xGraphName,
609 OUString const & i_rFileName,
610 OUString const & i_rBaseURI)
612 const uno::Reference<io::XStream> xStream(
613 i_xStorage->openStreamElement(i_rFileName,
614 embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
615 uno::UNO_SET_THROW);
616 const uno::Reference< beans::XPropertySet > xStreamProps(xStream,
617 uno::UNO_QUERY);
618 if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
619 xStreamProps->setPropertyValue(
620 OUString("MediaType"),
621 uno::makeAny(OUString(s_rdfxml)));
623 const uno::Reference<io::XOutputStream> xOutStream(
624 xStream->getOutputStream(), uno::UNO_SET_THROW );
625 const uno::Reference<rdf::XURI> xBaseURI(
626 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
627 i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML,
628 xOutStream, i_xGraphName, xBaseURI);
631 /** write a metadata file to the storage */
632 static void
633 writeStream(struct DocumentMetadataAccess_Impl & i_rImpl,
634 uno::Reference< embed::XStorage > const & i_xStorage,
635 uno::Reference<rdf::XURI> const & i_xGraphName,
636 OUString const & i_rPath,
637 OUString const & i_rBaseURI)
639 OUString dir;
640 OUString rest;
641 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
642 try {
643 if (dir.isEmpty()) {
644 exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath,
645 i_rBaseURI);
646 } else {
647 const uno::Reference<embed::XStorage> xDir(
648 i_xStorage->openStorageElement(dir,
649 embed::ElementModes::WRITE));
650 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
651 uno::UNO_QUERY_THROW);
652 try {
653 OUString mimeType;
654 xDirProps->getPropertyValue(
655 utl::MediaDescriptor::PROP_MEDIATYPE() )
656 >>= mimeType;
657 if (mimeType.startsWith(s_odfmime)) {
658 OSL_TRACE("writeStream: "
659 "refusing to recurse into embedded document");
660 return;
662 } catch (const uno::Exception &) { }
663 OUStringBuffer buf(i_rBaseURI);
664 buf.append(dir).append('/');
665 writeStream(i_rImpl, xDir, i_xGraphName, rest,
666 buf.makeStringAndClear());
667 uno::Reference<embed::XTransactedObject> const xTransaction(
668 xDir, uno::UNO_QUERY);
669 if (xTransaction.is()) {
670 xTransaction->commit();
673 } catch (const uno::RuntimeException &) {
674 throw;
675 } catch (const io::IOException &) {
676 throw;
680 static void
681 initLoading(struct DocumentMetadataAccess_Impl & i_rImpl,
682 const uno::Reference< embed::XStorage > & i_xStorage,
683 const uno::Reference<rdf::XURI> & i_xBaseURI,
684 const uno::Reference<task::XInteractionHandler> & i_xHandler)
686 retry:
687 // clear old data
688 i_rImpl.m_xManifest.clear();
689 // init BaseURI
690 i_rImpl.m_xBaseURI = i_xBaseURI;
692 // create repository
693 i_rImpl.m_xRepository.clear();
694 i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext),
695 uno::UNO_SET_THROW);
697 const OUString baseURI( i_xBaseURI->getStringValue() );
698 // try to delay raising errors until after initialization is done
699 uno::Any rterr;
700 ucb::InteractiveAugmentedIOException iaioe;
701 bool err(false);
703 const uno::Reference <rdf::XURI> xManifest(
704 getURIForStream(i_rImpl, s_manifest));
705 try {
706 readStream(i_rImpl, i_xStorage, s_manifest, baseURI);
707 } catch (const ucb::InteractiveAugmentedIOException & e) {
708 // no manifest.rdf: this is not an error in ODF < 1.2
709 if (!(ucb::IOErrorCode_NOT_EXISTING_PATH == e.Code)) {
710 iaioe = e;
711 err = true;
713 } catch (const uno::Exception & e) {
714 rterr <<= e;
717 // init manifest graph
718 const uno::Reference<rdf::XNamedGraph> xManifestGraph(
719 i_rImpl.m_xRepository->getGraph(xManifest));
720 i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
721 i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
722 const uno::Reference<container::XEnumeration> xEnum(
723 i_rImpl.m_xManifest->getStatements(0,
724 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
725 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get()));
727 // document statement
728 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
729 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
730 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get());
732 OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null");
733 OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null");
734 OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null");
736 if (rterr.hasValue()) {
737 throw lang::WrappedTargetRuntimeException(
738 OUString(
739 "DocumentMetadataAccess::loadMetadataFromStorage: "
740 "exception"), 0, rterr);
743 if (err) {
744 if (handleError(iaioe, i_xHandler)) goto retry;
748 /** init Impl struct */
749 static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
751 try {
753 i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
754 getURIForStream(i_rImpl, s_manifest)),
755 uno::UNO_SET_THROW);
757 // insert the document statement
758 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
759 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
760 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get());
761 } catch (const uno::Exception & e) {
762 throw lang::WrappedTargetRuntimeException(
763 "init: unexpected exception", 0,
764 uno::makeAny(e));
767 // add top-level content files
768 if (!addContentOrStylesFileImpl(i_rImpl, s_content)) {
769 throw uno::RuntimeException();
771 if (!addContentOrStylesFileImpl(i_rImpl, s_styles)) {
772 throw uno::RuntimeException();
778 DocumentMetadataAccess::DocumentMetadataAccess(
779 uno::Reference< uno::XComponentContext > const & i_xContext,
780 const IXmlIdRegistrySupplier & i_rRegistrySupplier)
781 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
783 // no initalization: must call loadFrom...
786 DocumentMetadataAccess::DocumentMetadataAccess(
787 uno::Reference< uno::XComponentContext > const & i_xContext,
788 const IXmlIdRegistrySupplier & i_rRegistrySupplier,
789 OUString const & i_rURI)
790 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
792 OSL_ENSURE(!i_rURI.isEmpty(), "DMA::DMA: no URI given!");
793 OSL_ENSURE(i_rURI.endsWith("/"), "DMA::DMA: URI without / given!");
794 if (!i_rURI.endsWith("/")) throw uno::RuntimeException();
795 m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI));
796 m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext),
797 uno::UNO_SET_THROW);
799 // init repository
800 init(*m_pImpl);
802 OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null");
803 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null");
804 OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null");
807 DocumentMetadataAccess::~DocumentMetadataAccess()
811 // ::com::sun::star::rdf::XRepositorySupplier:
812 uno::Reference< rdf::XRepository > SAL_CALL
813 DocumentMetadataAccess::getRDFRepository() throw (uno::RuntimeException, std::exception)
815 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized");
816 return m_pImpl->m_xRepository;
819 // ::com::sun::star::rdf::XNode:
820 OUString SAL_CALL
821 DocumentMetadataAccess::getStringValue() throw (uno::RuntimeException, std::exception)
823 return m_pImpl->m_xBaseURI->getStringValue();
826 // ::com::sun::star::rdf::XURI:
827 OUString SAL_CALL
828 DocumentMetadataAccess::getNamespace() throw (uno::RuntimeException, std::exception)
830 return m_pImpl->m_xBaseURI->getNamespace();
833 OUString SAL_CALL
834 DocumentMetadataAccess::getLocalName() throw (uno::RuntimeException, std::exception)
836 return m_pImpl->m_xBaseURI->getLocalName();
839 // ::com::sun::star::rdf::XDocumentMetadataAccess:
840 uno::Reference< rdf::XMetadatable > SAL_CALL
841 DocumentMetadataAccess::getElementByMetadataReference(
842 const ::com::sun::star::beans::StringPair & i_rReference)
843 throw (uno::RuntimeException, std::exception)
845 const IXmlIdRegistry * pReg(
846 m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() );
847 if (!pReg) {
848 throw uno::RuntimeException(
849 "DocumentMetadataAccess::getElementByXmlId: no registry", *this);
851 return pReg->GetElementByMetadataReference(i_rReference);
854 uno::Reference< rdf::XMetadatable > SAL_CALL
855 DocumentMetadataAccess::getElementByURI(
856 const uno::Reference< rdf::XURI > & i_xURI )
857 throw (uno::RuntimeException, lang::IllegalArgumentException, std::exception)
859 if (!i_xURI.is()) {
860 throw lang::IllegalArgumentException(
861 "DocumentMetadataAccess::getElementByURI: URI is null", *this, 0);
864 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
865 const OUString name( i_xURI->getStringValue() );
866 if (!name.match(baseURI)) {
867 return 0;
869 const OUString relName( name.copy(baseURI.getLength()) );
870 OUString path;
871 OUString idref;
872 if (!splitXmlId(relName, path, idref)) {
873 return 0;
876 return getElementByMetadataReference( beans::StringPair(path, idref) );
880 uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL
881 DocumentMetadataAccess::getMetadataGraphsWithType(
882 const uno::Reference<rdf::XURI> & i_xType)
883 throw (uno::RuntimeException, lang::IllegalArgumentException, std::exception)
885 if (!i_xType.is()) {
886 throw lang::IllegalArgumentException(
887 "DocumentMetadataAccess::getMetadataGraphsWithType: "
888 "type is null", *this, 0);
891 ::std::vector< uno::Reference< rdf::XURI > > ret;
892 const ::std::vector< uno::Reference< rdf::XURI > > parts(
893 getAllParts(*m_pImpl) );
894 ::std::remove_copy_if(parts.begin(), parts.end(),
895 ::std::back_inserter(ret),
896 ::boost::bind(
897 ::std::logical_not<bool>(),
898 ::boost::bind(&isPartOfType, ::boost::ref(*m_pImpl), _1, i_xType) ));
899 return ::comphelper::containerToSequence(ret);
902 uno::Reference<rdf::XURI> SAL_CALL
903 DocumentMetadataAccess::addMetadataFile(const OUString & i_rFileName,
904 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
905 throw (uno::RuntimeException, lang::IllegalArgumentException,
906 container::ElementExistException, std::exception)
908 if (!isFileNameValid(i_rFileName)) {
909 throw lang::IllegalArgumentException(
910 "DocumentMetadataAccess::addMetadataFile: invalid FileName",
911 *this, 0);
913 if (isReservedFile(i_rFileName)) {
914 throw lang::IllegalArgumentException(
915 "DocumentMetadataAccess::addMetadataFile:"
916 "invalid FileName: reserved", *this, 0);
918 for (sal_Int32 i = 0; i < i_rTypes.getLength(); ++i) {
919 if (!i_rTypes[i].is()) {
920 throw lang::IllegalArgumentException(
921 "DocumentMetadataAccess::addMetadataFile: "
922 "null type", *this, 2);
926 const uno::Reference<rdf::XURI> xGraphName(
927 getURIForStream(*m_pImpl, i_rFileName) );
929 try {
930 m_pImpl->m_xRepository->createGraph(xGraphName);
931 } catch (const rdf::RepositoryException & e) {
932 throw lang::WrappedTargetRuntimeException(
933 "DocumentMetadataAccess::addMetadataFile: exception",
934 *this, uno::makeAny(e));
935 // note: all other exceptions are propagated
938 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
939 return xGraphName;
942 uno::Reference<rdf::XURI> SAL_CALL
943 DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format,
944 const uno::Reference< io::XInputStream > & i_xInStream,
945 const OUString & i_rFileName,
946 const uno::Reference< rdf::XURI > & i_xBaseURI,
947 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
948 throw (uno::RuntimeException, lang::IllegalArgumentException,
949 datatransfer::UnsupportedFlavorException,
950 container::ElementExistException, rdf::ParseException, io::IOException, std::exception)
952 if (!isFileNameValid(i_rFileName)) {
953 throw lang::IllegalArgumentException(
954 "DocumentMetadataAccess::importMetadataFile: invalid FileName",
955 *this, 0);
957 if (isReservedFile(i_rFileName)) {
958 throw lang::IllegalArgumentException(
959 "DocumentMetadataAccess::importMetadataFile:"
960 "invalid FileName: reserved", *this, 0);
962 for (sal_Int32 i = 0; i < i_rTypes.getLength(); ++i) {
963 if (!i_rTypes[i].is()) {
964 throw lang::IllegalArgumentException(
965 "DocumentMetadataAccess::importMetadataFile: null type",
966 *this, 5);
970 const uno::Reference<rdf::XURI> xGraphName(
971 getURIForStream(*m_pImpl, i_rFileName) );
973 try {
974 m_pImpl->m_xRepository->importGraph(
975 i_Format, i_xInStream, xGraphName, i_xBaseURI);
976 } catch (const rdf::RepositoryException & e) {
977 throw lang::WrappedTargetRuntimeException(
978 "DocumentMetadataAccess::importMetadataFile: "
979 "RepositoryException", *this, uno::makeAny(e));
980 // note: all other exceptions are propagated
983 // add to manifest
984 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
985 return xGraphName;
988 void SAL_CALL
989 DocumentMetadataAccess::removeMetadataFile(
990 const uno::Reference< rdf::XURI > & i_xGraphName)
991 throw (uno::RuntimeException, lang::IllegalArgumentException,
992 container::NoSuchElementException, std::exception)
994 try {
995 m_pImpl->m_xRepository->destroyGraph(i_xGraphName);
996 } catch (const rdf::RepositoryException & e) {
997 throw lang::WrappedTargetRuntimeException(
998 "DocumentMetadataAccess::removeMetadataFile: "
999 "RepositoryException", *this, uno::makeAny(e));
1000 // note: all other exceptions are propagated
1003 // remove file from manifest
1004 removeFile(*m_pImpl, i_xGraphName.get());
1007 void SAL_CALL
1008 DocumentMetadataAccess::addContentOrStylesFile(
1009 const OUString & i_rFileName)
1010 throw (uno::RuntimeException, lang::IllegalArgumentException,
1011 container::ElementExistException, std::exception)
1013 if (!isFileNameValid(i_rFileName)) {
1014 throw lang::IllegalArgumentException(
1015 "DocumentMetadataAccess::addContentOrStylesFile: "
1016 "invalid FileName", *this, 0);
1019 if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) {
1020 throw lang::IllegalArgumentException(
1021 "DocumentMetadataAccess::addContentOrStylesFile: "
1022 "invalid FileName: must end with content.xml or styles.xml",
1023 *this, 0);
1027 void SAL_CALL
1028 DocumentMetadataAccess::removeContentOrStylesFile(
1029 const OUString & i_rFileName)
1030 throw (uno::RuntimeException, lang::IllegalArgumentException,
1031 container::NoSuchElementException, std::exception)
1033 if (!isFileNameValid(i_rFileName)) {
1034 throw lang::IllegalArgumentException(
1035 "DocumentMetadataAccess::removeContentOrStylesFile: "
1036 "invalid FileName", *this, 0);
1039 try {
1040 const uno::Reference<rdf::XURI> xPart(
1041 getURIForStream(*m_pImpl, i_rFileName) );
1042 const uno::Reference<container::XEnumeration> xEnum(
1043 m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI.get(),
1044 getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext),
1045 xPart.get()),
1046 uno::UNO_SET_THROW);
1047 if (!xEnum->hasMoreElements()) {
1048 throw container::NoSuchElementException(
1049 "DocumentMetadataAccess::removeContentOrStylesFile: "
1050 "cannot find stream in manifest graph: " + i_rFileName,
1051 *this);
1054 // remove file from manifest
1055 removeFile(*m_pImpl, xPart);
1057 } catch (const uno::RuntimeException &) {
1058 throw;
1059 } catch (const uno::Exception & e) {
1060 throw lang::WrappedTargetRuntimeException(
1061 "DocumentMetadataAccess::removeContentOrStylesFile: exception",
1062 *this, uno::makeAny(e));
1066 void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
1067 const uno::Reference< embed::XStorage > & i_xStorage,
1068 const uno::Reference<rdf::XURI> & i_xBaseURI,
1069 const uno::Reference<task::XInteractionHandler> & i_xHandler)
1070 throw (uno::RuntimeException, lang::IllegalArgumentException,
1071 lang::WrappedTargetException, std::exception)
1073 if (!i_xStorage.is()) {
1074 throw lang::IllegalArgumentException(
1075 "DocumentMetadataAccess::loadMetadataFromStorage: "
1076 "storage is null", *this, 0);
1078 if (!i_xBaseURI.is()) {
1079 throw lang::IllegalArgumentException(
1080 "DocumentMetadataAccess::loadMetadataFromStorage: "
1081 "base URI is null", *this, 1);
1083 const OUString baseURI( i_xBaseURI->getStringValue());
1084 if (baseURI.indexOf('#') >= 0) {
1085 throw lang::IllegalArgumentException(
1086 "DocumentMetadataAccess::loadMetadataFromStorage: "
1087 "base URI not absolute", *this, 1);
1089 if (!baseURI.endsWith("/")) {
1090 throw lang::IllegalArgumentException(
1091 "DocumentMetadataAccess::loadMetadataFromStorage: "
1092 "base URI does not end with slash", *this, 1);
1095 initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler);
1097 std::set< OUString > StgFiles;
1098 collectFilesFromStorage(i_xStorage,
1099 OUString(""), StgFiles);
1101 std::vector< OUString > MfstMetadataFiles;
1103 try {
1104 const ::std::vector< uno::Reference< rdf::XURI > > parts(
1105 getAllParts(*m_pImpl) );
1106 const uno::Reference<rdf::XURI> xContentFile(
1107 getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
1108 const uno::Reference<rdf::XURI> xStylesFile(
1109 getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext));
1110 const uno::Reference<rdf::XURI> xMetadataFile(
1111 getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext));
1112 const sal_Int32 len( baseURI.getLength() );
1113 for (::std::vector< uno::Reference< rdf::XURI > >::const_iterator it
1114 = parts.begin();
1115 it != parts.end(); ++it) {
1116 const OUString name((*it)->getStringValue());
1117 if (!name.match(baseURI)) {
1118 OSL_TRACE("loadMetadataFromStorage: graph not in document: %s",
1119 OUStringToOString(name, RTL_TEXTENCODING_UTF8)
1120 .getStr());
1121 continue;
1123 const OUString relName( name.copy(len) );
1124 if (relName == s_manifest) {
1125 OSL_TRACE("loadMetadataFromStorage: "
1126 "found ourselves a recursive manifest!");
1127 continue;
1129 // remove found items from StgFiles
1130 StgFiles.erase(relName);
1131 if (isContentFile(relName)) {
1132 if (!isPartOfType(*m_pImpl, *it, xContentFile)) {
1133 const uno::Reference <rdf::XURI> xName(
1134 getURIForStream(*m_pImpl, relName) );
1135 // add missing type statement
1136 m_pImpl->m_xManifest->addStatement(xName.get(),
1137 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1138 xContentFile.get());
1140 } else if (isStylesFile(relName)) {
1141 if (!isPartOfType(*m_pImpl, *it, xStylesFile)) {
1142 const uno::Reference <rdf::XURI> xName(
1143 getURIForStream(*m_pImpl, relName) );
1144 // add missing type statement
1145 m_pImpl->m_xManifest->addStatement(xName.get(),
1146 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1147 xStylesFile.get());
1149 } else if (isReservedFile(relName)) {
1150 OSL_TRACE("loadMetadataFromStorage: "
1151 "reserved file name in manifest");
1152 } else {
1153 if (isPartOfType(*m_pImpl, *it, xMetadataFile)) {
1154 MfstMetadataFiles.push_back(relName);
1156 // do not add statement for MetadataFile; it could be
1157 // something else! just ignore it...
1160 } catch (const uno::RuntimeException &) {
1161 throw;
1162 } catch (const uno::Exception & e) {
1163 throw lang::WrappedTargetRuntimeException(
1164 "DocumentMetadataAccess::loadMetadataFromStorage: "
1165 "exception", *this, uno::makeAny(e));
1168 std::for_each(StgFiles.begin(), StgFiles.end(),
1169 boost::bind(addContentOrStylesFileImpl, boost::ref(*m_pImpl), _1));
1171 std::for_each(MfstMetadataFiles.begin(), MfstMetadataFiles.end(),
1172 boost::bind(importFile, boost::ref(*m_pImpl),
1173 i_xStorage, baseURI, i_xHandler, _1));
1176 void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage(
1177 const uno::Reference< embed::XStorage > & i_xStorage)
1178 throw (uno::RuntimeException, lang::IllegalArgumentException,
1179 lang::WrappedTargetException, std::exception)
1181 if (!i_xStorage.is()) {
1182 throw lang::IllegalArgumentException(
1183 "DocumentMetadataAccess::storeMetadataToStorage: "
1184 "storage is null", *this, 0);
1187 // export manifest
1188 const uno::Reference <rdf::XURI> xManifest(
1189 getURIForStream(*m_pImpl, s_manifest) );
1190 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
1191 try {
1192 writeStream(*m_pImpl, i_xStorage, xManifest, s_manifest, baseURI);
1193 } catch (const uno::RuntimeException &) {
1194 throw;
1195 } catch (const io::IOException & e) {
1196 throw lang::WrappedTargetException(
1197 "storeMetadataToStorage: IO exception", *this, uno::makeAny(e));
1198 } catch (const uno::Exception & e) {
1199 throw lang::WrappedTargetRuntimeException(
1200 "storeMetadataToStorage: exception", *this, uno::makeAny(e));
1203 // export metadata streams
1204 try {
1205 const uno::Sequence<uno::Reference<rdf::XURI> > graphs(
1206 m_pImpl->m_xRepository->getGraphNames());
1207 const sal_Int32 len( baseURI.getLength() );
1208 for (sal_Int32 i = 0; i < graphs.getLength(); ++i) {
1209 const uno::Reference<rdf::XURI> xName(graphs[i]);
1210 const OUString name(xName->getStringValue());
1211 if (!name.match(baseURI)) {
1212 OSL_TRACE("storeMetadataToStorage: graph not in document: %s",
1213 OUStringToOString(name, RTL_TEXTENCODING_UTF8)
1214 .getStr());
1215 continue;
1217 const OUString relName( name.copy(len) );
1218 if (relName == s_manifest) {
1219 continue;
1221 if (!isFileNameValid(relName) || isReservedFile(relName)) {
1222 OSL_TRACE("storeMetadataToStorage: invalid file name: %s",
1223 OUStringToOString(relName, RTL_TEXTENCODING_UTF8)
1224 .getStr());
1225 continue;
1227 try {
1228 writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI);
1229 } catch (const uno::RuntimeException &) {
1230 throw;
1231 } catch (const io::IOException & e) {
1232 throw lang::WrappedTargetException(
1233 "storeMetadataToStorage: IO exception",
1234 *this, uno::makeAny(e));
1235 } catch (const uno::Exception & e) {
1236 throw lang::WrappedTargetRuntimeException(
1237 "storeMetadataToStorage: exception",
1238 *this, uno::makeAny(e));
1241 } catch (const rdf::RepositoryException & e) {
1242 throw lang::WrappedTargetRuntimeException(
1243 "storeMetadataToStorage: exception", *this, uno::makeAny(e));
1247 void SAL_CALL
1248 DocumentMetadataAccess::loadMetadataFromMedium(
1249 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1250 throw (uno::RuntimeException, lang::IllegalArgumentException,
1251 lang::WrappedTargetException, std::exception)
1253 uno::Reference<io::XInputStream> xIn;
1254 utl::MediaDescriptor md(i_rMedium);
1255 OUString URL;
1256 md[ utl::MediaDescriptor::PROP_URL() ] >>= URL;
1257 OUString BaseURL;
1258 md[ utl::MediaDescriptor::PROP_DOCUMENTBASEURL() ] >>= BaseURL;
1259 if (md.addInputStream()) {
1260 md[ utl::MediaDescriptor::PROP_INPUTSTREAM() ] >>= xIn;
1262 if (!xIn.is() && URL.isEmpty()) {
1263 throw lang::IllegalArgumentException(
1264 "DocumentMetadataAccess::loadMetadataFromMedium: "
1265 "inalid medium: no URL, no input stream", *this, 0);
1267 uno::Reference<embed::XStorage> xStorage;
1268 try {
1269 if (xIn.is()) {
1270 xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
1271 xIn, m_pImpl->m_xContext);
1272 } else { // fallback to url
1273 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1274 URL, embed::ElementModes::READ, m_pImpl->m_xContext);
1276 } catch (const uno::RuntimeException &) {
1277 throw;
1278 } catch (const io::IOException &) {
1279 throw;
1280 } catch (const uno::Exception & e) {
1281 throw lang::WrappedTargetException(
1282 "DocumentMetadataAccess::loadMetadataFromMedium: "
1283 "exception", *this, uno::makeAny(e));
1285 if (!xStorage.is()) {
1286 throw uno::RuntimeException(
1287 "DocumentMetadataAccess::loadMetadataFromMedium: "
1288 "cannot get Storage", *this);
1290 uno::Reference<rdf::XURI> xBaseURI;
1291 try {
1292 xBaseURI = createBaseURI(m_pImpl->m_xContext, xStorage, BaseURL);
1293 } catch (const uno::Exception &) {
1294 // fall back to URL
1295 try {
1296 xBaseURI = createBaseURI(m_pImpl->m_xContext, xStorage, URL);
1297 } catch (const uno::Exception &) {
1298 OSL_FAIL("cannot create base URI");
1301 uno::Reference<task::XInteractionHandler> xIH;
1302 md[ utl::MediaDescriptor::PROP_INTERACTIONHANDLER() ] >>= xIH;
1303 loadMetadataFromStorage(xStorage, xBaseURI, xIH);
1306 void SAL_CALL
1307 DocumentMetadataAccess::storeMetadataToMedium(
1308 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1309 throw (uno::RuntimeException, lang::IllegalArgumentException,
1310 lang::WrappedTargetException, std::exception)
1312 utl::MediaDescriptor md(i_rMedium);
1313 OUString URL;
1314 md[ utl::MediaDescriptor::PROP_URL() ] >>= URL;
1315 if (URL.isEmpty()) {
1316 throw lang::IllegalArgumentException(
1317 "DocumentMetadataAccess::storeMetadataToMedium: "
1318 "invalid medium: no URL", *this, 0);
1321 SfxMedium aMedium(i_rMedium);
1322 uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage());
1324 bool sfx(false);
1325 if (xStorage.is()) {
1326 sfx = true;
1327 } else {
1328 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1329 URL, embed::ElementModes::WRITE, m_pImpl->m_xContext);
1332 if (!xStorage.is()) {
1333 throw uno::RuntimeException(
1334 "DocumentMetadataAccess::storeMetadataToMedium: "
1335 "cannot get Storage", *this);
1337 // set MIME type of the storage
1338 utl::MediaDescriptor::const_iterator iter
1339 = md.find(utl::MediaDescriptor::PROP_MEDIATYPE());
1340 if (iter != md.end()) {
1341 uno::Reference< beans::XPropertySet > xProps(xStorage,
1342 uno::UNO_QUERY_THROW);
1343 try {
1344 // this is NOT supported in FileSystemStorage
1345 xProps->setPropertyValue(
1346 utl::MediaDescriptor::PROP_MEDIATYPE(),
1347 iter->second);
1348 } catch (const uno::Exception &) { }
1350 storeMetadataToStorage(xStorage);
1352 if (sfx) {
1353 const bool bOk = aMedium.Commit();
1354 aMedium.Close();
1355 if ( !bOk ) {
1356 sal_uInt32 nError = aMedium.GetError();
1357 if ( nError == ERRCODE_NONE ) {
1358 nError = ERRCODE_IO_GENERAL;
1360 task::ErrorCodeIOException ex(
1361 ("DocumentMetadataAccess::storeMetadataToMedium Commit failed: "
1362 "0x" + OUString::number(nError, 16)),
1363 uno::Reference< uno::XInterface >(), nError);
1364 throw lang::WrappedTargetException(OUString(), *this,
1365 uno::makeAny(ex));
1370 } // namespace sfx2
1372 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */