Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / sfx2 / source / doc / DocumentMetadataAccess.cxx
blobbbe7555e9ae1f8fe1138b29f2b2722cc12cdd150
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include <sfx2/DocumentMetadataAccess.hxx>
32 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/embed/ElementModes.hpp>
35 #include <com/sun/star/embed/XStorage.hpp>
36 #include <com/sun/star/embed/XTransactedObject.hpp>
37 #include <com/sun/star/task/ErrorCodeIOException.hpp>
38 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
39 #include <com/sun/star/rdf/FileFormat.hpp>
40 #include <com/sun/star/rdf/URIs.hpp>
41 #include <com/sun/star/rdf/Statement.hpp>
42 #include <com/sun/star/rdf/Literal.hpp>
43 #include <com/sun/star/rdf/URI.hpp>
44 #include <com/sun/star/rdf/Repository.hpp>
46 #include <rtl/ustrbuf.hxx>
47 #include <rtl/uri.hxx>
48 #include <rtl/bootstrap.hxx>
50 #include <comphelper/interaction.hxx>
51 #include <comphelper/makesequence.hxx>
52 #include <comphelper/mediadescriptor.hxx>
53 #include <comphelper/sequenceasvector.hxx>
54 #include <comphelper/storagehelper.hxx>
56 #include <sfx2/docfile.hxx>
57 #include <sfx2/XmlIdRegistry.hxx>
59 #include <libxml/tree.h> // for xmlValidateNCName
61 #include <boost/bind.hpp>
62 #include <boost/shared_array.hpp>
63 #include <boost/tuple/tuple.hpp>
65 #include <vector>
66 #include <set>
67 #include <map>
68 #include <functional>
69 #include <algorithm>
71 #include <unotools/ucbhelper.hxx>
72 #include <com/sun/star/uri/XUriReference.hpp>
73 #include <com/sun/star/uri/XUriReferenceFactory.hpp>
74 #include <com/sun/star/uri/XVndSunStarPkgUrlReferenceFactory.hpp>
78 Note: in the context of this implementation, all rdf.QueryExceptions and
79 rdf.RepositoryExceptions are RuntimeExceptions, and will be reported as such.
81 This implementation assumes that it is only used with ODF documents, not mere
82 ODF packages. In other words, we enforce that metadata files must not be
83 called reserved names.
86 using namespace ::com::sun::star;
88 namespace sfx2 {
91 bool isValidNCName(::rtl::OUString const & i_rIdref)
93 const ::rtl::OString id(
94 ::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) );
95 return !(xmlValidateNCName(
96 reinterpret_cast<const unsigned char*>(id.getStr()), 0));
100 static const char s_content [] = "content.xml";
101 static const char s_styles [] = "styles.xml";
102 static const char s_meta [] = "meta.xml";
103 static const char s_settings[] = "settings.xml";
104 static const char s_manifest[] = "manifest.rdf";
105 static const char s_rdfxml [] = "application/rdf+xml";
106 static const char s_odfmime [] = "application/vnd.oasis.opendocument.";
109 static bool isContentFile(::rtl::OUString const & i_rPath)
111 return i_rPath == s_content;
114 static bool isStylesFile (::rtl::OUString const & i_rPath)
116 return i_rPath == s_styles;
119 static bool isReservedFile(::rtl::OUString const & i_rPath)
121 return isContentFile(i_rPath) || isStylesFile(i_rPath) || i_rPath == s_meta || i_rPath == s_settings;
125 uno::Reference<rdf::XURI> createBaseURI(
126 uno::Reference<uno::XComponentContext> const & i_xContext,
127 uno::Reference<embed::XStorage> const & i_xStorage,
128 ::rtl::OUString const & i_rPkgURI, ::rtl::OUString const & i_rSubDocument)
130 if (!i_xContext.is() || !i_xStorage.is() || i_rPkgURI.isEmpty()) {
131 throw uno::RuntimeException();
134 // #i108078# workaround non-hierarchical vnd.sun.star.expand URIs
135 // this really should be done somewhere else, not here.
136 ::rtl::OUString pkgURI(i_rPkgURI);
137 if (pkgURI.matchIgnoreAsciiCaseAsciiL(
138 RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.expand:")))
140 // expand it here (makeAbsolute requires hierarchical URI)
141 pkgURI = pkgURI.copy( RTL_CONSTASCII_LENGTH("vnd.sun.star.expand:") );
142 if (!pkgURI.isEmpty()) {
143 pkgURI = ::rtl::Uri::decode(
144 pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8);
145 if (pkgURI.isEmpty()) {
146 throw uno::RuntimeException();
148 ::rtl::Bootstrap::expandMacros(pkgURI);
152 const uno::Reference<lang::XMultiComponentFactory> xServiceFactory(
153 i_xContext->getServiceManager(), uno::UNO_SET_THROW);
154 const uno::Reference<uri::XUriReferenceFactory> xUriFactory(
155 xServiceFactory->createInstanceWithContext(
156 ::rtl::OUString(
157 "com.sun.star.uri.UriReferenceFactory"), i_xContext),
158 uno::UNO_QUERY_THROW);
159 uno::Reference< uri::XUriReference > xBaseURI;
161 const uno::Reference< uri::XUriReference > xPkgURI(
162 xUriFactory->parse(pkgURI), uno::UNO_SET_THROW );
163 xPkgURI->clearFragment();
165 // need to know whether the storage is a FileSystemStorage
166 // XServiceInfo would be better, but it is not implemented
167 // if ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) )
168 if (true) {
169 xBaseURI.set( xPkgURI, uno::UNO_SET_THROW );
171 ::rtl::OUStringBuffer buf;
172 if (!xBaseURI->getUriReference().endsWithAsciiL("/", 1))
174 const sal_Int32 count( xBaseURI->getPathSegmentCount() );
175 if (count > 0)
177 const ::rtl::OUString last( xBaseURI->getPathSegment(count - 1) );
178 buf.append(last);
180 buf.append(static_cast<sal_Unicode>('/'));
182 if (!i_rSubDocument.isEmpty())
184 buf.append(i_rSubDocument);
185 buf.append(static_cast<sal_Unicode>('/'));
187 const ::rtl::OUString Path(buf.makeStringAndClear());
188 if (!Path.isEmpty())
190 const uno::Reference< uri::XUriReference > xPathURI(
191 xUriFactory->parse(Path), uno::UNO_SET_THROW );
192 xBaseURI.set(
193 xUriFactory->makeAbsolute(xBaseURI, xPathURI,
194 true, uri::RelativeUriExcessParentSegments_ERROR),
195 uno::UNO_SET_THROW);
198 return rdf::URI::create(i_xContext, xBaseURI->getUriReference());
202 struct DocumentMetadataAccess_Impl
204 // note: these are all initialized in constructor, and loadFromStorage
205 const uno::Reference<uno::XComponentContext> m_xContext;
206 const IXmlIdRegistrySupplier & m_rXmlIdRegistrySupplier;
207 uno::Reference<rdf::XURI> m_xBaseURI;
208 uno::Reference<rdf::XRepository> m_xRepository;
209 uno::Reference<rdf::XNamedGraph> m_xManifest;
210 DocumentMetadataAccess_Impl(
211 uno::Reference<uno::XComponentContext> const& i_xContext,
212 IXmlIdRegistrySupplier const & i_rRegistrySupplier)
213 : m_xContext(i_xContext)
214 , m_rXmlIdRegistrySupplier(i_rRegistrySupplier)
215 , m_xBaseURI()
216 , m_xRepository()
217 , m_xManifest()
219 OSL_ENSURE(m_xContext.is(), "context null");
223 // this is... a hack.
224 template<sal_Int16 Constant>
225 /*static*/ uno::Reference<rdf::XURI>
226 getURI(uno::Reference< uno::XComponentContext > const & i_xContext)
228 static uno::Reference< rdf::XURI > xURI(
229 rdf::URI::createKnown(i_xContext, Constant), uno::UNO_QUERY_THROW);
230 return xURI;
234 /** would storing the file to a XStorage succeed? */
235 static bool isFileNameValid(const ::rtl::OUString & i_rFileName)
237 if (i_rFileName.isEmpty()) return false;
238 if (i_rFileName[0] == '/') return false; // no absolute paths!
239 sal_Int32 idx(0);
240 do {
241 const ::rtl::OUString segment(
242 i_rFileName.getToken(0, static_cast<sal_Unicode> ('/'), idx) );
243 if (segment.isEmpty() || // no empty segments
244 segment == "." || // no . segments
245 segment == ".." || // no .. segments
246 !::comphelper::OStorageHelper::IsValidZipEntryFileName(
247 segment, sal_False)) // no invalid characters
248 return false;
249 } while (idx >= 0);
250 return true;
253 /** split a uri hierarchy into first segment and rest */
254 static bool
255 splitPath(::rtl::OUString const & i_rPath,
256 ::rtl::OUString & o_rDir, ::rtl::OUString& o_rRest)
258 const sal_Int32 idx(i_rPath.indexOf(static_cast<sal_Unicode>('/')));
259 if (idx < 0 || idx >= i_rPath.getLength()) {
260 o_rDir = ::rtl::OUString();
261 o_rRest = i_rPath;
262 return true;
263 } else if (idx == 0 || idx == i_rPath.getLength() - 1) {
264 // input must not start or end with '/'
265 return false;
266 } else {
267 o_rDir = (i_rPath.copy(0, idx));
268 o_rRest = (i_rPath.copy(idx+1));
269 return true;
273 static bool
274 splitXmlId(::rtl::OUString const & i_XmlId,
275 ::rtl::OUString & o_StreamName, ::rtl::OUString& o_Idref )
277 const sal_Int32 idx(i_XmlId.indexOf(static_cast<sal_Unicode>('#')));
278 if ((idx <= 0) || (idx >= i_XmlId.getLength() - 1)) {
279 return false;
280 } else {
281 o_StreamName = (i_XmlId.copy(0, idx));
282 o_Idref = (i_XmlId.copy(idx+1));
283 return isValidXmlId(o_StreamName, o_Idref);
288 static uno::Reference<rdf::XURI>
289 getURIForStream(struct DocumentMetadataAccess_Impl& i_rImpl,
290 ::rtl::OUString const& i_rPath)
292 const uno::Reference<rdf::XURI> xURI(
293 rdf::URI::createNS( i_rImpl.m_xContext,
294 i_rImpl.m_xBaseURI->getStringValue(), i_rPath),
295 uno::UNO_SET_THROW);
296 return xURI;
299 /** add statements declaring i_xResource to be a file of type i_xType with
300 path i_rPath to manifest, with optional additional types i_pTypes */
301 static void
302 addFile(struct DocumentMetadataAccess_Impl & i_rImpl,
303 uno::Reference<rdf::XURI> const& i_xType,
304 ::rtl::OUString const & i_rPath,
305 const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes = 0)
307 try {
308 const uno::Reference<rdf::XURI> xURI( getURIForStream(
309 i_rImpl, i_rPath) );
311 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
312 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
313 xURI.get());
314 i_rImpl.m_xManifest->addStatement(xURI.get(),
315 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
316 i_xType.get());
317 if (i_pTypes) {
318 for (sal_Int32 i = 0; i < i_pTypes->getLength(); ++i) {
319 i_rImpl.m_xManifest->addStatement(xURI.get(),
320 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
321 (*i_pTypes)[i].get());
324 } catch (const uno::RuntimeException &) {
325 throw;
326 } catch (const uno::Exception & e) {
327 throw lang::WrappedTargetRuntimeException(
328 ::rtl::OUString(
329 "addFile: exception"), /*this*/0, uno::makeAny(e));
333 /** add content.xml or styles.xml to manifest */
334 static bool
335 addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl & i_rImpl,
336 const ::rtl::OUString & i_rPath)
338 uno::Reference<rdf::XURI> xType;
339 if (isContentFile(i_rPath)) {
340 xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext));
341 } else if (isStylesFile(i_rPath)) {
342 xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext));
343 } else {
344 return false;
346 addFile(i_rImpl, xType.get(), i_rPath);
347 return true;
350 /** add metadata file to manifest */
351 static void
352 addMetadataFileImpl(struct DocumentMetadataAccess_Impl & i_rImpl,
353 const ::rtl::OUString & i_rPath,
354 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
356 addFile(i_rImpl,
357 getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext),
358 i_rPath, &i_rTypes);
361 /** remove a file from the manifest */
362 static void
363 removeFile(struct DocumentMetadataAccess_Impl & i_rImpl,
364 uno::Reference<rdf::XURI> const& i_xPart)
366 if (!i_xPart.is()) throw uno::RuntimeException();
367 try {
368 i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI.get(),
369 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
370 i_xPart.get());
371 i_rImpl.m_xManifest->removeStatements(i_xPart.get(),
372 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), 0);
373 } catch (const uno::RuntimeException &) {
374 throw;
375 } catch (const uno::Exception & e) {
376 throw lang::WrappedTargetRuntimeException(
377 ::rtl::OUString("removeFile: exception"),
378 0, uno::makeAny(e));
382 static ::std::vector< uno::Reference< rdf::XURI > >
383 getAllParts(struct DocumentMetadataAccess_Impl & i_rImpl)
385 ::std::vector< uno::Reference< rdf::XURI > > ret;
386 try {
387 const uno::Reference<container::XEnumeration> xEnum(
388 i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI.get(),
389 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), 0),
390 uno::UNO_SET_THROW);
391 while (xEnum->hasMoreElements()) {
392 rdf::Statement stmt;
393 if (!(xEnum->nextElement() >>= stmt)) {
394 throw uno::RuntimeException();
396 const uno::Reference<rdf::XURI> xPart(stmt.Object,
397 uno::UNO_QUERY);
398 if (!xPart.is()) continue;
399 ret.push_back(xPart);
401 return ret;
402 } catch (const uno::RuntimeException &) {
403 throw;
404 } catch (const uno::Exception & e) {
405 throw lang::WrappedTargetRuntimeException(
406 ::rtl::OUString("getAllParts: exception"),
407 0, uno::makeAny(e));
411 static bool
412 isPartOfType(struct DocumentMetadataAccess_Impl & i_rImpl,
413 uno::Reference<rdf::XURI> const & i_xPart,
414 uno::Reference<rdf::XURI> const & i_xType)
416 if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException();
417 try {
418 const uno::Reference<container::XEnumeration> xEnum(
419 i_rImpl.m_xManifest->getStatements(i_xPart.get(),
420 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
421 i_xType.get()),
422 uno::UNO_SET_THROW);
423 return (xEnum->hasMoreElements());
424 } catch (const uno::RuntimeException &) {
425 throw;
426 } catch (const uno::Exception & e) {
427 throw lang::WrappedTargetRuntimeException(
428 ::rtl::OUString("isPartOfType: exception"),
429 0, uno::makeAny(e));
434 static ucb::InteractiveAugmentedIOException
435 mkException( ::rtl::OUString const & i_rMessage,
436 ucb::IOErrorCode const i_ErrorCode,
437 ::rtl::OUString const & i_rUri, ::rtl::OUString const & i_rResource)
439 ucb::InteractiveAugmentedIOException iaioe;
440 iaioe.Message = i_rMessage;
441 iaioe.Classification = task::InteractionClassification_ERROR;
442 iaioe.Code = i_ErrorCode;
444 const beans::PropertyValue uriProp(::rtl::OUString("Uri"),
445 -1, uno::makeAny(i_rUri), static_cast<beans::PropertyState>(0));
446 const beans::PropertyValue rnProp(
447 ::rtl::OUString("ResourceName"),
448 -1, uno::makeAny(i_rResource), static_cast<beans::PropertyState>(0));
449 iaioe.Arguments = ::comphelper::makeSequence(
450 uno::makeAny(uriProp), uno::makeAny(rnProp));
451 return iaioe;
454 /** error handling policy.
455 <p>If a handler is given, ask it how to proceed:
456 <ul><li>(default:) cancel import, raise exception</li>
457 <li>ignore the error and continue</li>
458 <li>retry the action that led to the error</li></ul></p>
459 N.B.: must not be called before DMA is fully initalized!
460 @returns true iff caller should retry
462 static bool
463 handleError( ucb::InteractiveAugmentedIOException const & i_rException,
464 const uno::Reference<task::XInteractionHandler> & i_xHandler)
466 if (!i_xHandler.is()) {
467 throw lang::WrappedTargetException(::rtl::OUString(
468 "DocumentMetadataAccess::loadMetadataFromStorage: exception"),
469 /* *this*/ 0, uno::makeAny(i_rException));
472 ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest(
473 new ::comphelper::OInteractionRequest(uno::makeAny(i_rException)) );
474 ::rtl::Reference< ::comphelper::OInteractionRetry > pRetry(
475 new ::comphelper::OInteractionRetry );
476 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove(
477 new ::comphelper::OInteractionApprove );
478 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort(
479 new ::comphelper::OInteractionAbort );
481 pRequest->addContinuation( pApprove.get() );
482 pRequest->addContinuation( pAbort.get() );
483 // actually call the handler
484 i_xHandler->handle( pRequest.get() );
485 if (pRetry->wasSelected()) {
486 return true;
487 } else if (pApprove->wasSelected()) {
488 return false;
489 } else {
490 OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?");
491 throw lang::WrappedTargetException(::rtl::OUString(
492 "DocumentMetadataAccess::loadMetadataFromStorage: exception"),
493 /* *this*/ 0, uno::makeAny(i_rException));
497 /** check if storage has content.xml/styles.xml;
498 e.g. ODB files seem to only have content.xml */
499 static void
500 collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage,
501 ::rtl::OUString i_Path,
502 std::set< ::rtl::OUString > & o_rFiles)
504 static ::rtl::OUString content(s_content);
505 static ::rtl::OUString styles(s_styles );
506 try {
507 if (i_xStorage->hasByName(content) &&
508 i_xStorage->isStreamElement(content))
510 o_rFiles.insert(i_Path + content);
512 if (i_xStorage->hasByName(styles) &&
513 i_xStorage->isStreamElement(styles))
515 o_rFiles.insert(i_Path + styles);
517 } catch (const uno::Exception &) {
518 OSL_TRACE("collectFilesFromStorage: exception?");
522 /** import a metadata file into repository */
523 static void
524 readStream(struct DocumentMetadataAccess_Impl & i_rImpl,
525 uno::Reference< embed::XStorage > const & i_xStorage,
526 ::rtl::OUString const & i_rPath,
527 ::rtl::OUString const & i_rBaseURI)
529 ::rtl::OUString dir;
530 ::rtl::OUString rest;
531 try {
532 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
533 if (dir.isEmpty()) {
534 if (i_xStorage->isStreamElement(i_rPath)) {
535 const uno::Reference<io::XStream> xStream(
536 i_xStorage->openStreamElement(i_rPath,
537 embed::ElementModes::READ), uno::UNO_SET_THROW);
538 const uno::Reference<io::XInputStream> xInStream(
539 xStream->getInputStream(), uno::UNO_SET_THROW );
540 const uno::Reference<rdf::XURI> xBaseURI(
541 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
542 const uno::Reference<rdf::XURI> xURI(
543 rdf::URI::createNS(i_rImpl.m_xContext,
544 i_rBaseURI, i_rPath));
545 i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML,
546 xInStream, xURI, xBaseURI);
547 } else {
548 throw mkException(::rtl::OUString(
549 "readStream: is not a stream"),
550 ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath);
552 } else {
553 if (i_xStorage->isStorageElement(dir)) {
554 const uno::Reference<embed::XStorage> xDir(
555 i_xStorage->openStorageElement(dir,
556 embed::ElementModes::READ));
557 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
558 uno::UNO_QUERY_THROW);
559 try {
560 ::rtl::OUString mimeType;
561 xDirProps->getPropertyValue(
562 ::comphelper::MediaDescriptor::PROP_MEDIATYPE() )
563 >>= mimeType;
564 if (mimeType.matchAsciiL(s_odfmime, sizeof(s_odfmime) - 1))
566 OSL_TRACE("readStream: "
567 "refusing to recurse into embedded document");
568 return;
570 } catch (const uno::Exception &) { }
571 ::rtl::OUStringBuffer buf(i_rBaseURI);
572 buf.append(dir).append(static_cast<sal_Unicode>('/'));
573 readStream(i_rImpl, xDir, rest, buf.makeStringAndClear() );
574 } else {
575 throw mkException(::rtl::OUString(
576 "readStream: is not a directory"),
577 ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir);
580 } catch (const container::NoSuchElementException & e) {
581 throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH,
582 i_rBaseURI + i_rPath, i_rPath);
583 } catch (const io::IOException & e) {
584 throw mkException(e.Message, ucb::IOErrorCode_CANT_READ,
585 i_rBaseURI + i_rPath, i_rPath);
586 } catch (const rdf::ParseException & e) {
587 throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT,
588 i_rBaseURI + i_rPath, i_rPath);
592 /** import a metadata file into repository */
593 static void
594 importFile(struct DocumentMetadataAccess_Impl & i_rImpl,
595 uno::Reference<embed::XStorage> const & i_xStorage,
596 ::rtl::OUString const & i_rBaseURI,
597 uno::Reference<task::XInteractionHandler> const & i_xHandler,
598 ::rtl::OUString i_rPath)
600 retry:
601 try {
602 readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI);
603 } catch (const ucb::InteractiveAugmentedIOException & e) {
604 if (handleError(e, i_xHandler)) goto retry;
605 } catch (const uno::RuntimeException &) {
606 throw;
607 } catch (const uno::Exception & e) {
608 throw lang::WrappedTargetRuntimeException(
609 ::rtl::OUString("importFile: exception"),
610 0, uno::makeAny(e));
614 /** actually write a metadata file to the storage */
615 static void
616 exportStream(struct DocumentMetadataAccess_Impl & i_rImpl,
617 uno::Reference< embed::XStorage > const & i_xStorage,
618 uno::Reference<rdf::XURI> const & i_xGraphName,
619 ::rtl::OUString const & i_rFileName,
620 ::rtl::OUString const & i_rBaseURI)
622 const uno::Reference<io::XStream> xStream(
623 i_xStorage->openStreamElement(i_rFileName,
624 embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
625 uno::UNO_SET_THROW);
626 const uno::Reference< beans::XPropertySet > xStreamProps(xStream,
627 uno::UNO_QUERY);
628 if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
629 xStreamProps->setPropertyValue(
630 ::rtl::OUString("MediaType"),
631 uno::makeAny(::rtl::OUString(s_rdfxml)));
633 const uno::Reference<io::XOutputStream> xOutStream(
634 xStream->getOutputStream(), uno::UNO_SET_THROW );
635 const uno::Reference<rdf::XURI> xBaseURI(
636 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
637 i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML,
638 xOutStream, i_xGraphName, xBaseURI);
641 /** write a metadata file to the storage */
642 static void
643 writeStream(struct DocumentMetadataAccess_Impl & i_rImpl,
644 uno::Reference< embed::XStorage > const & i_xStorage,
645 uno::Reference<rdf::XURI> const & i_xGraphName,
646 ::rtl::OUString const & i_rPath,
647 ::rtl::OUString const & i_rBaseURI)
649 ::rtl::OUString dir;
650 ::rtl::OUString rest;
651 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
652 try {
653 if (dir.isEmpty()) {
654 exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath,
655 i_rBaseURI);
656 } else {
657 const uno::Reference<embed::XStorage> xDir(
658 i_xStorage->openStorageElement(dir,
659 embed::ElementModes::WRITE));
660 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
661 uno::UNO_QUERY_THROW);
662 try {
663 ::rtl::OUString mimeType;
664 xDirProps->getPropertyValue(
665 ::comphelper::MediaDescriptor::PROP_MEDIATYPE() )
666 >>= mimeType;
667 if (mimeType.matchAsciiL(s_odfmime, sizeof(s_odfmime) - 1)) {
668 OSL_TRACE("writeStream: "
669 "refusing to recurse into embedded document");
670 return;
672 } catch (const uno::Exception &) { }
673 ::rtl::OUStringBuffer buf(i_rBaseURI);
674 buf.append(dir).append(static_cast<sal_Unicode>('/'));
675 writeStream(i_rImpl, xDir, i_xGraphName, rest,
676 buf.makeStringAndClear());
677 uno::Reference<embed::XTransactedObject> const xTransaction(
678 xDir, uno::UNO_QUERY);
679 if (xTransaction.is()) {
680 xTransaction->commit();
683 } catch (const uno::RuntimeException &) {
684 throw;
685 } catch (const io::IOException &) {
686 throw;
690 static void
691 initLoading(struct DocumentMetadataAccess_Impl & i_rImpl,
692 const uno::Reference< embed::XStorage > & i_xStorage,
693 const uno::Reference<rdf::XURI> & i_xBaseURI,
694 const uno::Reference<task::XInteractionHandler> & i_xHandler)
696 retry:
697 // clear old data
698 i_rImpl.m_xManifest.clear();
699 // init BaseURI
700 i_rImpl.m_xBaseURI = i_xBaseURI;
702 // create repository
703 i_rImpl.m_xRepository.clear();
704 i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext),
705 uno::UNO_SET_THROW);
707 const ::rtl::OUString manifest (
708 ::rtl::OUString::createFromAscii(s_manifest));
709 const ::rtl::OUString baseURI( i_xBaseURI->getStringValue() );
710 // try to delay raising errors until after initialization is done
711 uno::Any rterr;
712 ucb::InteractiveAugmentedIOException iaioe;
713 bool err(false);
715 const uno::Reference <rdf::XURI> xManifest(
716 getURIForStream(i_rImpl, manifest));
717 try {
718 readStream(i_rImpl, i_xStorage, manifest, baseURI);
719 } catch (const ucb::InteractiveAugmentedIOException & e) {
720 // no manifest.rdf: this is not an error in ODF < 1.2
721 if (!(ucb::IOErrorCode_NOT_EXISTING_PATH == e.Code)) {
722 iaioe = e;
723 err = true;
725 } catch (const uno::Exception & e) {
726 rterr <<= e;
729 // init manifest graph
730 const uno::Reference<rdf::XNamedGraph> xManifestGraph(
731 i_rImpl.m_xRepository->getGraph(xManifest));
732 i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
733 i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
734 const uno::Reference<container::XEnumeration> xEnum(
735 i_rImpl.m_xManifest->getStatements(0,
736 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
737 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get()));
739 // document statement
740 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
741 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
742 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get());
744 OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null");
745 OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null");
746 OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null");
748 if (rterr.hasValue()) {
749 throw lang::WrappedTargetRuntimeException(
750 ::rtl::OUString(
751 "DocumentMetadataAccess::loadMetadataFromStorage: "
752 "exception"), 0, rterr);
755 if (err) {
756 if (handleError(iaioe, i_xHandler)) goto retry;
760 /** init Impl struct */
761 static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
763 try {
765 i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
766 getURIForStream(i_rImpl,
767 ::rtl::OUString::createFromAscii(s_manifest))),
768 uno::UNO_SET_THROW);
770 // insert the document statement
771 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
772 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
773 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get());
774 } catch (const uno::Exception & e) {
775 throw lang::WrappedTargetRuntimeException(
776 ::rtl::OUString("init: unexpected exception"), 0,
777 uno::makeAny(e));
780 // add top-level content files
781 if (!addContentOrStylesFileImpl(i_rImpl,
782 ::rtl::OUString::createFromAscii(s_content))) {
783 throw uno::RuntimeException();
785 if (!addContentOrStylesFileImpl(i_rImpl,
786 ::rtl::OUString::createFromAscii(s_styles))) {
787 throw uno::RuntimeException();
793 DocumentMetadataAccess::DocumentMetadataAccess(
794 uno::Reference< uno::XComponentContext > const & i_xContext,
795 const IXmlIdRegistrySupplier & i_rRegistrySupplier)
796 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
798 // no initalization: must call loadFrom...
801 DocumentMetadataAccess::DocumentMetadataAccess(
802 uno::Reference< uno::XComponentContext > const & i_xContext,
803 const IXmlIdRegistrySupplier & i_rRegistrySupplier,
804 ::rtl::OUString const & i_rURI)
805 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
807 OSL_ENSURE(!i_rURI.isEmpty(), "DMA::DMA: no URI given!");
808 OSL_ENSURE(i_rURI.endsWithAsciiL("/", 1), "DMA::DMA: URI without / given!");
809 if (!i_rURI.endsWithAsciiL("/", 1)) throw uno::RuntimeException();
810 m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI));
811 m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext),
812 uno::UNO_SET_THROW);
814 // init repository
815 init(*m_pImpl);
817 OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null");
818 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null");
819 OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null");
822 DocumentMetadataAccess::~DocumentMetadataAccess()
826 // ::com::sun::star::rdf::XRepositorySupplier:
827 uno::Reference< rdf::XRepository > SAL_CALL
828 DocumentMetadataAccess::getRDFRepository() throw (uno::RuntimeException)
830 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized");
831 return m_pImpl->m_xRepository;
834 // ::com::sun::star::rdf::XNode:
835 ::rtl::OUString SAL_CALL
836 DocumentMetadataAccess::getStringValue() throw (uno::RuntimeException)
838 return m_pImpl->m_xBaseURI->getStringValue();
841 // ::com::sun::star::rdf::XURI:
842 ::rtl::OUString SAL_CALL
843 DocumentMetadataAccess::getNamespace() throw (uno::RuntimeException)
845 return m_pImpl->m_xBaseURI->getNamespace();
848 ::rtl::OUString SAL_CALL
849 DocumentMetadataAccess::getLocalName() throw (uno::RuntimeException)
851 return m_pImpl->m_xBaseURI->getLocalName();
854 // ::com::sun::star::rdf::XDocumentMetadataAccess:
855 uno::Reference< rdf::XMetadatable > SAL_CALL
856 DocumentMetadataAccess::getElementByMetadataReference(
857 const ::com::sun::star::beans::StringPair & i_rReference)
858 throw (uno::RuntimeException)
860 const IXmlIdRegistry * pReg(
861 m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() );
862 if (!pReg) {
863 throw uno::RuntimeException(::rtl::OUString(
864 "DocumentMetadataAccess::getElementByXmlId: no registry"), *this);
866 return pReg->GetElementByMetadataReference(i_rReference);
869 uno::Reference< rdf::XMetadatable > SAL_CALL
870 DocumentMetadataAccess::getElementByURI(
871 const uno::Reference< rdf::XURI > & i_xURI )
872 throw (uno::RuntimeException, lang::IllegalArgumentException)
874 if (!i_xURI.is()) {
875 throw lang::IllegalArgumentException(::rtl::OUString(
876 "DocumentMetadataAccess::getElementByURI: URI is null"), *this, 0);
879 const ::rtl::OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
880 const ::rtl::OUString name( i_xURI->getStringValue() );
881 if (!name.match(baseURI)) {
882 return 0;
884 const ::rtl::OUString relName( name.copy(baseURI.getLength()) );
885 ::rtl::OUString path;
886 ::rtl::OUString idref;
887 if (!splitXmlId(relName, path, idref)) {
888 return 0;
891 return getElementByMetadataReference( beans::StringPair(path, idref) );
895 uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL
896 DocumentMetadataAccess::getMetadataGraphsWithType(
897 const uno::Reference<rdf::XURI> & i_xType)
898 throw (uno::RuntimeException, lang::IllegalArgumentException)
900 if (!i_xType.is()) {
901 throw lang::IllegalArgumentException(::rtl::OUString(
902 "DocumentMetadataAccess::getMetadataGraphsWithType: "
903 "type is null"), *this, 0);
906 ::comphelper::SequenceAsVector< uno::Reference< rdf::XURI > > ret;
907 const ::std::vector< uno::Reference< rdf::XURI > > parts(
908 getAllParts(*m_pImpl) );
909 ::std::remove_copy_if(parts.begin(), parts.end(),
910 ::std::back_inserter(ret),
911 ::boost::bind(
912 ::std::logical_not<bool>(),
913 ::boost::bind(&isPartOfType, ::boost::ref(*m_pImpl), _1, i_xType) ));
914 return ret.getAsConstList();
917 uno::Reference<rdf::XURI> SAL_CALL
918 DocumentMetadataAccess::addMetadataFile(const ::rtl::OUString & i_rFileName,
919 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
920 throw (uno::RuntimeException, lang::IllegalArgumentException,
921 container::ElementExistException)
923 if (!isFileNameValid(i_rFileName)) {
924 throw lang::IllegalArgumentException(::rtl::OUString(
925 "DocumentMetadataAccess::addMetadataFile: invalid FileName"),
926 *this, 0);
928 if (isReservedFile(i_rFileName)) {
929 throw lang::IllegalArgumentException(::rtl::OUString(
930 "DocumentMetadataAccess::addMetadataFile:"
931 "invalid FileName: reserved"), *this, 0);
933 for (sal_Int32 i = 0; i < i_rTypes.getLength(); ++i) {
934 if (!i_rTypes[i].is()) {
935 throw lang::IllegalArgumentException(
936 ::rtl::OUString(
937 "DocumentMetadataAccess::addMetadataFile: "
938 "null type"), *this, 2);
942 const uno::Reference<rdf::XURI> xGraphName(
943 getURIForStream(*m_pImpl, i_rFileName) );
945 try {
946 m_pImpl->m_xRepository->createGraph(xGraphName);
947 } catch (const rdf::RepositoryException & e) {
948 throw lang::WrappedTargetRuntimeException(
949 ::rtl::OUString(
950 "DocumentMetadataAccess::addMetadataFile: exception"),
951 *this, uno::makeAny(e));
952 // note: all other exceptions are propagated
955 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
956 return xGraphName;
959 uno::Reference<rdf::XURI> SAL_CALL
960 DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format,
961 const uno::Reference< io::XInputStream > & i_xInStream,
962 const ::rtl::OUString & i_rFileName,
963 const uno::Reference< rdf::XURI > & i_xBaseURI,
964 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
965 throw (uno::RuntimeException, lang::IllegalArgumentException,
966 datatransfer::UnsupportedFlavorException,
967 container::ElementExistException, rdf::ParseException, io::IOException)
969 if (!isFileNameValid(i_rFileName)) {
970 throw lang::IllegalArgumentException(::rtl::OUString(
971 "DocumentMetadataAccess::importMetadataFile: invalid FileName"),
972 *this, 0);
974 if (isReservedFile(i_rFileName)) {
975 throw lang::IllegalArgumentException(::rtl::OUString(
976 "DocumentMetadataAccess::importMetadataFile:"
977 "invalid FileName: reserved"), *this, 0);
979 for (sal_Int32 i = 0; i < i_rTypes.getLength(); ++i) {
980 if (!i_rTypes[i].is()) {
981 throw lang::IllegalArgumentException(
982 ::rtl::OUString(
983 "DocumentMetadataAccess::importMetadataFile: null type"),
984 *this, 5);
988 const uno::Reference<rdf::XURI> xGraphName(
989 getURIForStream(*m_pImpl, i_rFileName) );
991 try {
992 m_pImpl->m_xRepository->importGraph(
993 i_Format, i_xInStream, xGraphName, i_xBaseURI);
994 } catch (const rdf::RepositoryException & e) {
995 throw lang::WrappedTargetRuntimeException(
996 ::rtl::OUString(
997 "DocumentMetadataAccess::importMetadataFile: "
998 "RepositoryException"), *this, uno::makeAny(e));
999 // note: all other exceptions are propagated
1002 // add to manifest
1003 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
1004 return xGraphName;
1007 void SAL_CALL
1008 DocumentMetadataAccess::removeMetadataFile(
1009 const uno::Reference< rdf::XURI > & i_xGraphName)
1010 throw (uno::RuntimeException, lang::IllegalArgumentException,
1011 container::NoSuchElementException)
1013 try {
1014 m_pImpl->m_xRepository->destroyGraph(i_xGraphName);
1015 } catch (const rdf::RepositoryException & e) {
1016 throw lang::WrappedTargetRuntimeException(
1017 ::rtl::OUString(
1018 "DocumentMetadataAccess::removeMetadataFile: "
1019 "RepositoryException"), *this, uno::makeAny(e));
1020 // note: all other exceptions are propagated
1023 // remove file from manifest
1024 removeFile(*m_pImpl, i_xGraphName.get());
1027 void SAL_CALL
1028 DocumentMetadataAccess::addContentOrStylesFile(
1029 const ::rtl::OUString & i_rFileName)
1030 throw (uno::RuntimeException, lang::IllegalArgumentException,
1031 container::ElementExistException)
1033 if (!isFileNameValid(i_rFileName)) {
1034 throw lang::IllegalArgumentException(::rtl::OUString(
1035 "DocumentMetadataAccess::addContentOrStylesFile: "
1036 "invalid FileName"), *this, 0);
1039 if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) {
1040 throw lang::IllegalArgumentException(::rtl::OUString(
1041 "DocumentMetadataAccess::addContentOrStylesFile: "
1042 "invalid FileName: must end with content.xml or styles.xml"),
1043 *this, 0);
1047 void SAL_CALL
1048 DocumentMetadataAccess::removeContentOrStylesFile(
1049 const ::rtl::OUString & i_rFileName)
1050 throw (uno::RuntimeException, lang::IllegalArgumentException,
1051 container::NoSuchElementException)
1053 if (!isFileNameValid(i_rFileName)) {
1054 throw lang::IllegalArgumentException(::rtl::OUString(
1055 "DocumentMetadataAccess::removeContentOrStylesFile: "
1056 "invalid FileName"), *this, 0);
1059 try {
1060 const uno::Reference<rdf::XURI> xPart(
1061 getURIForStream(*m_pImpl, i_rFileName) );
1062 const uno::Reference<container::XEnumeration> xEnum(
1063 m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI.get(),
1064 getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext),
1065 xPart.get()),
1066 uno::UNO_SET_THROW);
1067 if (!xEnum->hasMoreElements()) {
1068 throw container::NoSuchElementException(
1069 ::rtl::OUString(
1070 "DocumentMetadataAccess::removeContentOrStylesFile: "
1071 "cannot find stream in manifest graph: ") + i_rFileName,
1072 *this);
1075 // remove file from manifest
1076 removeFile(*m_pImpl, xPart);
1078 } catch (const uno::RuntimeException &) {
1079 throw;
1080 } catch (const uno::Exception & e) {
1081 throw lang::WrappedTargetRuntimeException(
1082 ::rtl::OUString(
1083 "DocumentMetadataAccess::removeContentOrStylesFile: exception"),
1084 *this, uno::makeAny(e));
1088 void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
1089 const uno::Reference< embed::XStorage > & i_xStorage,
1090 const uno::Reference<rdf::XURI> & i_xBaseURI,
1091 const uno::Reference<task::XInteractionHandler> & i_xHandler)
1092 throw (uno::RuntimeException, lang::IllegalArgumentException,
1093 lang::WrappedTargetException)
1095 if (!i_xStorage.is()) {
1096 throw lang::IllegalArgumentException(::rtl::OUString(
1097 "DocumentMetadataAccess::loadMetadataFromStorage: "
1098 "storage is null"), *this, 0);
1100 if (!i_xBaseURI.is()) {
1101 throw lang::IllegalArgumentException(::rtl::OUString(
1102 "DocumentMetadataAccess::loadMetadataFromStorage: "
1103 "base URI is null"), *this, 1);
1105 const ::rtl::OUString baseURI( i_xBaseURI->getStringValue());
1106 if (baseURI.indexOf('#') >= 0) {
1107 throw lang::IllegalArgumentException(::rtl::OUString(
1108 "DocumentMetadataAccess::loadMetadataFromStorage: "
1109 "base URI not absolute"), *this, 1);
1111 if (baseURI.isEmpty() || !baseURI.endsWithAsciiL("/", 1)) {
1112 throw lang::IllegalArgumentException(::rtl::OUString(
1113 "DocumentMetadataAccess::loadMetadataFromStorage: "
1114 "base URI does not end with slash"), *this, 1);
1117 initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler);
1119 std::set< ::rtl::OUString > StgFiles;
1120 collectFilesFromStorage(i_xStorage,
1121 ::rtl::OUString(""), StgFiles);
1123 std::vector< ::rtl::OUString > MfstMetadataFiles;
1125 try {
1126 const ::std::vector< uno::Reference< rdf::XURI > > parts(
1127 getAllParts(*m_pImpl) );
1128 const uno::Reference<rdf::XURI> xContentFile(
1129 getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
1130 const uno::Reference<rdf::XURI> xStylesFile(
1131 getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext));
1132 const uno::Reference<rdf::XURI> xMetadataFile(
1133 getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext));
1134 const sal_Int32 len( baseURI.getLength() );
1135 const ::rtl::OUString manifest (
1136 ::rtl::OUString::createFromAscii(s_manifest));
1137 for (::std::vector< uno::Reference< rdf::XURI > >::const_iterator it
1138 = parts.begin();
1139 it != parts.end(); ++it) {
1140 const ::rtl::OUString name((*it)->getStringValue());
1141 if (!name.match(baseURI)) {
1142 OSL_TRACE("loadMetadataFromStorage: graph not in document: %s",
1143 ::rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8)
1144 .getStr());
1145 continue;
1147 const ::rtl::OUString relName( name.copy(len) );
1148 if (relName == manifest) {
1149 OSL_TRACE("loadMetadataFromStorage: "
1150 "found ourselves a recursive manifest!");
1151 continue;
1153 // remove found items from StgFiles
1154 StgFiles.erase(relName);
1155 if (isContentFile(relName)) {
1156 if (!isPartOfType(*m_pImpl, *it, xContentFile)) {
1157 const uno::Reference <rdf::XURI> xName(
1158 getURIForStream(*m_pImpl, relName) );
1159 // add missing type statement
1160 m_pImpl->m_xManifest->addStatement(xName.get(),
1161 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1162 xContentFile.get());
1164 } else if (isStylesFile(relName)) {
1165 if (!isPartOfType(*m_pImpl, *it, xStylesFile)) {
1166 const uno::Reference <rdf::XURI> xName(
1167 getURIForStream(*m_pImpl, relName) );
1168 // add missing type statement
1169 m_pImpl->m_xManifest->addStatement(xName.get(),
1170 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1171 xStylesFile.get());
1173 } else if (isReservedFile(relName)) {
1174 OSL_TRACE("loadMetadataFromStorage: "
1175 "reserved file name in manifest");
1176 } else {
1177 if (isPartOfType(*m_pImpl, *it, xMetadataFile)) {
1178 MfstMetadataFiles.push_back(relName);
1180 // do not add statement for MetadataFile; it could be
1181 // something else! just ignore it...
1184 } catch (const uno::RuntimeException &) {
1185 throw;
1186 } catch (const uno::Exception & e) {
1187 throw lang::WrappedTargetRuntimeException(
1188 ::rtl::OUString(
1189 "DocumentMetadataAccess::loadMetadataFromStorage: "
1190 "exception"), *this, uno::makeAny(e));
1193 std::for_each(StgFiles.begin(), StgFiles.end(),
1194 boost::bind(addContentOrStylesFileImpl, boost::ref(*m_pImpl), _1));
1196 std::for_each(MfstMetadataFiles.begin(), MfstMetadataFiles.end(),
1197 boost::bind(importFile, boost::ref(*m_pImpl),
1198 i_xStorage, baseURI, i_xHandler, _1));
1201 void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage(
1202 const uno::Reference< embed::XStorage > & i_xStorage)
1203 throw (uno::RuntimeException, lang::IllegalArgumentException,
1204 lang::WrappedTargetException)
1206 if (!i_xStorage.is()) {
1207 throw lang::IllegalArgumentException(::rtl::OUString(
1208 "DocumentMetadataAccess::storeMetadataToStorage: "
1209 "storage is null"), *this, 0);
1212 // export manifest
1213 const ::rtl::OUString manifest (
1214 ::rtl::OUString::createFromAscii(s_manifest));
1215 const uno::Reference <rdf::XURI> xManifest(
1216 getURIForStream(*m_pImpl, manifest) );
1217 const ::rtl::OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
1218 try {
1219 writeStream(*m_pImpl, i_xStorage, xManifest, manifest, baseURI);
1220 } catch (const uno::RuntimeException &) {
1221 throw;
1222 } catch (const io::IOException & e) {
1223 throw lang::WrappedTargetException( ::rtl::OUString(
1224 "storeMetadataToStorage: IO exception"), *this, uno::makeAny(e));
1225 } catch (const uno::Exception & e) {
1226 throw lang::WrappedTargetRuntimeException(
1227 ::rtl::OUString(
1228 "storeMetadataToStorage: exception"), *this, uno::makeAny(e));
1231 // export metadata streams
1232 try {
1233 const uno::Sequence<uno::Reference<rdf::XURI> > graphs(
1234 m_pImpl->m_xRepository->getGraphNames());
1235 const sal_Int32 len( baseURI.getLength() );
1236 for (sal_Int32 i = 0; i < graphs.getLength(); ++i) {
1237 const uno::Reference<rdf::XURI> xName(graphs[i]);
1238 const ::rtl::OUString name(xName->getStringValue());
1239 if (!name.match(baseURI)) {
1240 OSL_TRACE("storeMetadataToStorage: graph not in document: %s",
1241 ::rtl::OUStringToOString(name, RTL_TEXTENCODING_UTF8)
1242 .getStr());
1243 continue;
1245 const ::rtl::OUString relName( name.copy(len) );
1246 if (relName == manifest) {
1247 continue;
1249 if (!isFileNameValid(relName) || isReservedFile(relName)) {
1250 OSL_TRACE("storeMetadataToStorage: invalid file name: %s",
1251 ::rtl::OUStringToOString(relName, RTL_TEXTENCODING_UTF8)
1252 .getStr());
1253 continue;
1255 try {
1256 writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI);
1257 } catch (const uno::RuntimeException &) {
1258 throw;
1259 } catch (const io::IOException & e) {
1260 throw lang::WrappedTargetException(
1261 ::rtl::OUString(
1262 "storeMetadataToStorage: IO exception"),
1263 *this, uno::makeAny(e));
1264 } catch (const uno::Exception & e) {
1265 throw lang::WrappedTargetRuntimeException(
1266 ::rtl::OUString(
1267 "storeMetadataToStorage: exception"),
1268 *this, uno::makeAny(e));
1271 } catch (const rdf::RepositoryException & e) {
1272 throw lang::WrappedTargetRuntimeException(
1273 ::rtl::OUString(
1274 "storeMetadataToStorage: exception"), *this, uno::makeAny(e));
1278 void SAL_CALL
1279 DocumentMetadataAccess::loadMetadataFromMedium(
1280 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1281 throw (uno::RuntimeException, lang::IllegalArgumentException,
1282 lang::WrappedTargetException)
1284 uno::Reference<io::XInputStream> xIn;
1285 ::comphelper::MediaDescriptor md(i_rMedium);
1286 ::rtl::OUString URL;
1287 md[ ::comphelper::MediaDescriptor::PROP_URL() ] >>= URL;
1288 ::rtl::OUString BaseURL;
1289 md[ ::comphelper::MediaDescriptor::PROP_DOCUMENTBASEURL() ] >>= BaseURL;
1290 if (md.addInputStream()) {
1291 md[ ::comphelper::MediaDescriptor::PROP_INPUTSTREAM() ] >>= xIn;
1293 if (!xIn.is() && URL.isEmpty()) {
1294 throw lang::IllegalArgumentException(::rtl::OUString(
1295 "DocumentMetadataAccess::loadMetadataFromMedium: "
1296 "inalid medium: no URL, no input stream"), *this, 0);
1298 uno::Reference<embed::XStorage> xStorage;
1299 try {
1300 const uno::Reference<lang::XMultiServiceFactory> xMsf (
1301 m_pImpl->m_xContext->getServiceManager(), uno::UNO_QUERY_THROW);
1302 if (xIn.is()) {
1303 xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
1304 xIn, xMsf);
1305 } else { // fallback to url
1306 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1307 URL, embed::ElementModes::READ, xMsf);
1309 } catch (const uno::RuntimeException &) {
1310 throw;
1311 } catch (const io::IOException &) {
1312 throw;
1313 } catch (const uno::Exception & e) {
1314 throw lang::WrappedTargetException(
1315 ::rtl::OUString(
1316 "DocumentMetadataAccess::loadMetadataFromMedium: "
1317 "exception"), *this, uno::makeAny(e));
1319 if (!xStorage.is()) {
1320 throw uno::RuntimeException(::rtl::OUString(
1321 "DocumentMetadataAccess::loadMetadataFromMedium: "
1322 "cannot get Storage"), *this);
1324 uno::Reference<rdf::XURI> xBaseURI;
1325 try {
1326 xBaseURI = createBaseURI(m_pImpl->m_xContext, xStorage, BaseURL);
1327 } catch (const uno::Exception &) {
1328 // fall back to URL
1329 try {
1330 xBaseURI = createBaseURI(m_pImpl->m_xContext, xStorage, URL);
1331 } catch (const uno::Exception &) {
1332 OSL_FAIL("cannot create base URI");
1335 uno::Reference<task::XInteractionHandler> xIH;
1336 md[ ::comphelper::MediaDescriptor::PROP_INTERACTIONHANDLER() ] >>= xIH;
1337 loadMetadataFromStorage(xStorage, xBaseURI, xIH);
1340 void SAL_CALL
1341 DocumentMetadataAccess::storeMetadataToMedium(
1342 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1343 throw (uno::RuntimeException, lang::IllegalArgumentException,
1344 lang::WrappedTargetException)
1346 ::comphelper::MediaDescriptor md(i_rMedium);
1347 ::rtl::OUString URL;
1348 md[ ::comphelper::MediaDescriptor::PROP_URL() ] >>= URL;
1349 if (URL.isEmpty()) {
1350 throw lang::IllegalArgumentException(::rtl::OUString(
1351 "DocumentMetadataAccess::storeMetadataToMedium: "
1352 "invalid medium: no URL"), *this, 0);
1355 SfxMedium aMedium(i_rMedium);
1356 uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage());
1358 bool sfx(false);
1359 if (xStorage.is()) {
1360 sfx = true;
1361 } else {
1362 const uno::Reference<lang::XMultiServiceFactory> xMsf (
1363 m_pImpl->m_xContext->getServiceManager(), uno::UNO_QUERY_THROW);
1364 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1365 URL, embed::ElementModes::WRITE, xMsf);
1368 if (!xStorage.is()) {
1369 throw uno::RuntimeException(::rtl::OUString(
1370 "DocumentMetadataAccess::storeMetadataToMedium: "
1371 "cannot get Storage"), *this);
1373 // set MIME type of the storage
1374 ::comphelper::MediaDescriptor::const_iterator iter
1375 = md.find(::comphelper::MediaDescriptor::PROP_MEDIATYPE());
1376 if (iter != md.end()) {
1377 uno::Reference< beans::XPropertySet > xProps(xStorage,
1378 uno::UNO_QUERY_THROW);
1379 try {
1380 // this is NOT supported in FileSystemStorage
1381 xProps->setPropertyValue(
1382 ::comphelper::MediaDescriptor::PROP_MEDIATYPE(),
1383 iter->second);
1384 } catch (const uno::Exception &) { }
1386 storeMetadataToStorage(xStorage);
1388 if (sfx) {
1389 const sal_Bool bOk = aMedium.Commit();
1390 aMedium.Close();
1391 if ( !bOk ) {
1392 sal_uInt32 nError = aMedium.GetError();
1393 if ( nError == ERRCODE_NONE ) {
1394 nError = ERRCODE_IO_GENERAL;
1396 task::ErrorCodeIOException ex( ::rtl::OUString(),
1397 uno::Reference< uno::XInterface >(), nError);
1398 throw lang::WrappedTargetException(::rtl::OUString(), *this,
1399 uno::makeAny(ex));
1404 } // namespace sfx2
1406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */