Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / doc / SfxDocumentMetaData.cxx
blob36d8dde0d75c41cbe1075f28115703c375f9f4a4
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <cppuhelper/compbase.hxx>
24 #include <cppuhelper/exc_hlp.hxx>
25 #include <com/sun/star/lang/XServiceInfo.hpp>
26 #include <com/sun/star/document/XDocumentProperties.hpp>
27 #include <com/sun/star/lang/XInitialization.hpp>
28 #include <com/sun/star/util/XCloneable.hpp>
29 #include <com/sun/star/util/XModifiable.hpp>
30 #include <com/sun/star/xml/sax/SAXException.hpp>
31 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
33 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
34 #include <com/sun/star/lang/EventObject.hpp>
35 #include <com/sun/star/beans/IllegalTypeException.hpp>
36 #include <com/sun/star/beans/PropertyExistException.hpp>
37 #include <com/sun/star/beans/XPropertySet.hpp>
38 #include <com/sun/star/beans/XPropertySetInfo.hpp>
39 #include <com/sun/star/beans/PropertyAttribute.hpp>
40 #include <com/sun/star/task/ErrorCodeIOException.hpp>
41 #include <com/sun/star/embed/XStorage.hpp>
42 #include <com/sun/star/embed/XTransactedObject.hpp>
43 #include <com/sun/star/embed/ElementModes.hpp>
44 #include <com/sun/star/io/WrongFormatException.hpp>
45 #include <com/sun/star/io/XStream.hpp>
46 #include <com/sun/star/document/XImporter.hpp>
47 #include <com/sun/star/document/XExporter.hpp>
48 #include <com/sun/star/document/XFilter.hpp>
49 #include <com/sun/star/xml/sax/Parser.hpp>
50 #include <com/sun/star/xml/sax/Writer.hpp>
51 #include <com/sun/star/xml/sax/Parser.hpp>
52 #include <com/sun/star/xml/sax/XFastParser.hpp>
53 #include <com/sun/star/xml/dom/DOMException.hpp>
54 #include <com/sun/star/xml/dom/XDocument.hpp>
55 #include <com/sun/star/xml/dom/XElement.hpp>
56 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
57 #include <com/sun/star/xml/dom/NodeType.hpp>
58 #include <com/sun/star/xml/xpath/XPathAPI.hpp>
59 #include <com/sun/star/util/Date.hpp>
60 #include <com/sun/star/util/Time.hpp>
61 #include <com/sun/star/util/DateWithTimezone.hpp>
62 #include <com/sun/star/util/DateTimeWithTimezone.hpp>
63 #include <com/sun/star/util/Duration.hpp>
65 #include <rtl/ustrbuf.hxx>
66 #include <tools/datetime.hxx>
67 #include <tools/diagnose_ex.h>
68 #include <osl/mutex.hxx>
69 #include <comphelper/fileformat.h>
70 #include <cppuhelper/basemutex.hxx>
71 #include <comphelper/interfacecontainer2.hxx>
72 #include <comphelper/storagehelper.hxx>
73 #include <unotools/mediadescriptor.hxx>
74 #include <comphelper/sequence.hxx>
75 #include <sot/storage.hxx>
76 #include <sfx2/docfile.hxx>
77 #include <sax/tools/converter.hxx>
78 #include <i18nlangtag/languagetag.hxx>
79 #include <optional>
81 #include <utility>
82 #include <vector>
83 #include <map>
84 #include <cstring>
85 #include <limits>
88 #include <cppuhelper/implbase.hxx>
89 #include <cppuhelper/supportsservice.hxx>
90 #include <com/sun/star/document/XCompatWriterDocProperties.hpp>
91 #include <com/sun/star/beans/PropertyBag.hpp>
93 /**
94 * This file contains the implementation of the service
95 * com.sun.star.document.DocumentProperties.
96 * This service enables access to the meta-data stored in documents.
97 * Currently, this service only handles documents in ODF format.
99 * The implementation uses an XML DOM to store the properties.
100 * This approach was taken because it allows for preserving arbitrary XML data
101 * in loaded documents, which will be stored unmodified when saving the
102 * document again.
104 * Upon access, some properties are directly read from and updated in the DOM.
105 * Exception: it seems impossible to get notified upon addition of a property
106 * to a com.sun.star.beans.PropertyBag, which is used for storing user-defined
107 * properties; because of this, user-defined properties are updated in the
108 * XML DOM only when storing the document.
109 * Exception 2: when setting certain properties which correspond to attributes
110 * in the XML DOM, we want to remove the corresponding XML element. Detecting
111 * this condition can get messy, so we store all such properties as members,
112 * and update the DOM tree only when storing the document (in
113 * <method>updateUserDefinedAndAttributes</method>).
117 /// anonymous implementation namespace
118 namespace {
120 /// a list of attribute-lists, where attribute means name and content
121 typedef std::vector<std::vector<std::pair<const char*, OUString> > >
122 AttrVector;
124 typedef ::cppu::WeakComponentImplHelper<
125 css::lang::XServiceInfo,
126 css::document::XDocumentProperties,
127 css::lang::XInitialization,
128 css::util::XCloneable,
129 css::util::XModifiable,
130 css::xml::sax::XSAXSerializable>
131 SfxDocumentMetaData_Base;
133 class SfxDocumentMetaData:
134 private ::cppu::BaseMutex,
135 public SfxDocumentMetaData_Base
137 public:
138 explicit SfxDocumentMetaData(
139 css::uno::Reference< css::uno::XComponentContext > const & context);
140 SfxDocumentMetaData(const SfxDocumentMetaData&) = delete;
141 SfxDocumentMetaData& operator=(const SfxDocumentMetaData&) = delete;
143 // css::lang::XServiceInfo:
144 virtual OUString SAL_CALL getImplementationName() override;
145 virtual sal_Bool SAL_CALL supportsService(
146 const OUString & ServiceName) override;
147 virtual css::uno::Sequence< OUString > SAL_CALL
148 getSupportedServiceNames() override;
150 // css::lang::XComponent:
151 virtual void SAL_CALL dispose() override;
153 // css::document::XDocumentProperties:
154 virtual OUString SAL_CALL getAuthor() override;
155 virtual void SAL_CALL setAuthor(const OUString & the_value) override;
156 virtual OUString SAL_CALL getGenerator() override;
157 virtual void SAL_CALL setGenerator(const OUString & the_value) override;
158 virtual css::util::DateTime SAL_CALL getCreationDate() override;
159 virtual void SAL_CALL setCreationDate(const css::util::DateTime & the_value) override;
160 virtual OUString SAL_CALL getTitle() override;
161 virtual void SAL_CALL setTitle(const OUString & the_value) override;
162 virtual OUString SAL_CALL getSubject() override;
163 virtual void SAL_CALL setSubject(const OUString & the_value) override;
164 virtual OUString SAL_CALL getDescription() override;
165 virtual void SAL_CALL setDescription(const OUString & the_value) override;
166 virtual css::uno::Sequence< OUString > SAL_CALL getKeywords() override;
167 virtual void SAL_CALL setKeywords(
168 const css::uno::Sequence< OUString > & the_value) override;
169 virtual css::lang::Locale SAL_CALL getLanguage() override;
170 virtual void SAL_CALL setLanguage(const css::lang::Locale & the_value) override;
171 virtual OUString SAL_CALL getModifiedBy() override;
172 virtual void SAL_CALL setModifiedBy(const OUString & the_value) override;
173 virtual css::util::DateTime SAL_CALL getModificationDate() override;
174 virtual void SAL_CALL setModificationDate(
175 const css::util::DateTime & the_value) override;
176 virtual OUString SAL_CALL getPrintedBy() override;
177 virtual void SAL_CALL setPrintedBy(const OUString & the_value) override;
178 virtual css::util::DateTime SAL_CALL getPrintDate() override;
179 virtual void SAL_CALL setPrintDate(const css::util::DateTime & the_value) override;
180 virtual OUString SAL_CALL getTemplateName() override;
181 virtual void SAL_CALL setTemplateName(const OUString & the_value) override;
182 virtual OUString SAL_CALL getTemplateURL() override;
183 virtual void SAL_CALL setTemplateURL(const OUString & the_value) override;
184 virtual css::util::DateTime SAL_CALL getTemplateDate() override;
185 virtual void SAL_CALL setTemplateDate(const css::util::DateTime & the_value) override;
186 virtual OUString SAL_CALL getAutoloadURL() override;
187 virtual void SAL_CALL setAutoloadURL(const OUString & the_value) override;
188 virtual ::sal_Int32 SAL_CALL getAutoloadSecs() override;
189 virtual void SAL_CALL setAutoloadSecs(::sal_Int32 the_value) override;
190 virtual OUString SAL_CALL getDefaultTarget() override;
191 virtual void SAL_CALL setDefaultTarget(const OUString & the_value) override;
192 virtual css::uno::Sequence< css::beans::NamedValue > SAL_CALL
193 getDocumentStatistics() override;
194 virtual void SAL_CALL setDocumentStatistics(
195 const css::uno::Sequence< css::beans::NamedValue > & the_value) override;
196 virtual ::sal_Int16 SAL_CALL getEditingCycles() override;
197 virtual void SAL_CALL setEditingCycles(::sal_Int16 the_value) override;
198 virtual ::sal_Int32 SAL_CALL getEditingDuration() override;
199 virtual void SAL_CALL setEditingDuration(::sal_Int32 the_value) override;
200 virtual void SAL_CALL resetUserData(const OUString & the_value) override;
201 virtual css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
202 getUserDefinedProperties() override;
203 virtual void SAL_CALL loadFromStorage(
204 const css::uno::Reference< css::embed::XStorage > & Storage,
205 const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
206 virtual void SAL_CALL loadFromMedium(const OUString & URL,
207 const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
208 virtual void SAL_CALL storeToStorage(
209 const css::uno::Reference< css::embed::XStorage > & Storage,
210 const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
211 virtual void SAL_CALL storeToMedium(const OUString & URL,
212 const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
214 // css::lang::XInitialization:
215 virtual void SAL_CALL initialize(
216 const css::uno::Sequence< css::uno::Any > & aArguments) override;
218 // css::util::XCloneable:
219 virtual css::uno::Reference<css::util::XCloneable> SAL_CALL createClone() override;
221 // css::util::XModifiable:
222 virtual sal_Bool SAL_CALL isModified( ) override;
223 virtual void SAL_CALL setModified( sal_Bool bModified ) override;
225 // css::util::XModifyBroadcaster:
226 virtual void SAL_CALL addModifyListener(
227 const css::uno::Reference< css::util::XModifyListener > & xListener) override;
228 virtual void SAL_CALL removeModifyListener(
229 const css::uno::Reference< css::util::XModifyListener > & xListener) override;
231 // css::xml::sax::XSAXSerializable
232 virtual void SAL_CALL serialize(
233 const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
234 const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces) override;
236 protected:
237 virtual ~SfxDocumentMetaData() override {}
238 virtual SfxDocumentMetaData* createMe( css::uno::Reference< css::uno::XComponentContext > const & context ) { return new SfxDocumentMetaData( context ); };
239 const css::uno::Reference< css::uno::XComponentContext > m_xContext;
241 /// for notification
242 ::comphelper::OInterfaceContainerHelper2 m_NotifyListeners;
243 /// flag: false means not initialized yet, or disposed
244 bool m_isInitialized;
245 /// flag
246 bool m_isModified;
247 /// meta-data DOM tree
248 css::uno::Reference< css::xml::dom::XDocument > m_xDoc;
249 /// meta-data super node in the meta-data DOM tree
250 css::uno::Reference< css::xml::dom::XNode> m_xParent;
251 /// standard meta data (single occurrence)
252 std::map< OUString, css::uno::Reference<css::xml::dom::XNode> >
253 m_meta;
254 /// standard meta data (multiple occurrences)
255 std::map< OUString,
256 std::vector<css::uno::Reference<css::xml::dom::XNode> > > m_metaList;
257 /// user-defined meta data (meta:user-defined) @ATTENTION may be null!
258 css::uno::Reference<css::beans::XPropertyContainer> m_xUserDefined;
259 // now for some meta-data attributes; these are not updated directly in the
260 // DOM because updates (detecting "empty" elements) would be quite messy
261 OUString m_TemplateName;
262 OUString m_TemplateURL;
263 css::util::DateTime m_TemplateDate;
264 OUString m_AutoloadURL;
265 sal_Int32 m_AutoloadSecs;
266 OUString m_DefaultTarget;
268 /// check if we are initialized properly
269 void checkInit() const;
270 /// initialize state from given DOM tree
271 void init(const css::uno::Reference<css::xml::dom::XDocument>& i_xDom);
272 /// update element in DOM tree
273 void updateElement(const char *i_name,
274 std::vector<std::pair<const char *, OUString> >* i_pAttrs = nullptr);
275 /// update user-defined meta data and attributes in DOM tree
276 void updateUserDefinedAndAttributes();
277 /// create empty DOM tree (XDocument)
278 css::uno::Reference<css::xml::dom::XDocument> createDOM() const;
279 /// extract base URL (necessary for converting relative links)
280 css::uno::Reference<css::beans::XPropertySet> getURLProperties(
281 const css::uno::Sequence<css::beans::PropertyValue> & i_rMedium) const;
282 /// get text of standard meta data element
283 OUString getMetaText(const char* i_name) const;
284 /// set text of standard meta data element iff not equal to existing text
285 bool setMetaText(const char* i_name,
286 const OUString & i_rValue);
287 /// set text of standard meta data element iff not equal to existing text
288 void setMetaTextAndNotify(const char* i_name,
289 const OUString & i_rValue);
290 /// get text of standard meta data element's attribute
291 OUString getMetaAttr(const char* i_name,
292 const char* i_attr) const;
293 /// get text of a list of standard meta data elements (multiple occ.)
294 css::uno::Sequence< OUString > getMetaList(
295 const char* i_name) const;
296 /// set text of a list of standard meta data elements (multiple occ.)
297 bool setMetaList(const char* i_name,
298 const css::uno::Sequence< OUString > & i_rValue,
299 AttrVector const*);
300 void createUserDefined();
303 typedef ::cppu::ImplInheritanceHelper< SfxDocumentMetaData, css::document::XCompatWriterDocProperties > CompatWriterDocPropsImpl_BASE;
305 class CompatWriterDocPropsImpl : public CompatWriterDocPropsImpl_BASE
307 OUString msManager;
308 OUString msCategory;
309 OUString msCompany;
310 protected:
311 virtual SfxDocumentMetaData* createMe( css::uno::Reference< css::uno::XComponentContext > const & context ) override { return new CompatWriterDocPropsImpl( context ); };
312 public:
313 explicit CompatWriterDocPropsImpl( css::uno::Reference< css::uno::XComponentContext > const & context) : CompatWriterDocPropsImpl_BASE( context ) {}
315 // XCompatWriterDocPropsImpl
316 virtual OUString SAL_CALL getManager() override { return msManager; }
317 virtual void SAL_CALL setManager( const OUString& _manager ) override { msManager = _manager; }
318 virtual OUString SAL_CALL getCategory() override { return msCategory; }
319 virtual void SAL_CALL setCategory( const OUString& _category ) override { msCategory = _category; }
320 virtual OUString SAL_CALL getCompany() override { return msCompany; }
321 virtual void SAL_CALL setCompany( const OUString& _company ) override { msCompany = _company; }
323 // XServiceInfo
324 virtual OUString SAL_CALL getImplementationName( ) override
326 return "CompatWriterDocPropsImpl";
329 virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override
331 return cppu::supportsService(this, ServiceName);
334 virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override
336 css::uno::Sequence<OUString> aServiceNames { "com.sun.star.writer.DocumentProperties" };
337 return aServiceNames;
341 // NB: keep these two arrays in sync!
342 const char* s_stdStatAttrs[] = {
343 "meta:page-count",
344 "meta:table-count",
345 "meta:draw-count",
346 "meta:image-count",
347 "meta:object-count",
348 "meta:ole-object-count",
349 "meta:paragraph-count",
350 "meta:word-count",
351 "meta:character-count",
352 "meta:row-count",
353 "meta:frame-count",
354 "meta:sentence-count",
355 "meta:syllable-count",
356 "meta:non-whitespace-character-count",
357 "meta:cell-count",
358 nullptr
361 // NB: keep these two arrays in sync!
362 const char* s_stdStats[] = {
363 "PageCount",
364 "TableCount",
365 "DrawCount",
366 "ImageCount",
367 "ObjectCount",
368 "OLEObjectCount",
369 "ParagraphCount",
370 "WordCount",
371 "CharacterCount",
372 "RowCount",
373 "FrameCount",
374 "SentenceCount",
375 "SyllableCount",
376 "NonWhitespaceCharacterCount",
377 "CellCount",
378 nullptr
381 const char* s_stdMeta[] = {
382 "meta:generator", // string
383 "dc:title", // string
384 "dc:description", // string
385 "dc:subject", // string
386 "meta:initial-creator", // string
387 "dc:creator", // string
388 "meta:printed-by", // string
389 "meta:creation-date", // dateTime
390 "dc:date", // dateTime
391 "meta:print-date", // dateTime
392 "meta:template", // XLink
393 "meta:auto-reload",
394 "meta:hyperlink-behaviour",
395 "dc:language", // language
396 "meta:editing-cycles", // nonNegativeInteger
397 "meta:editing-duration", // duration
398 "meta:document-statistic", // ... // note: statistic is singular, no s!
399 nullptr
402 const char* s_stdMetaList[] = {
403 "meta:keyword", // string*
404 "meta:user-defined", // ...*
405 nullptr
408 const char s_nsXLink[] = "http://www.w3.org/1999/xlink";
409 const char s_nsDC[] = "http://purl.org/dc/elements/1.1/";
410 const char s_nsODF[] = "urn:oasis:names:tc:opendocument:xmlns:office:1.0";
411 const char s_nsODFMeta[] = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0";
412 // const char* s_nsOOo = "http://openoffice.org/2004/office"; // not used (yet?)
414 const char s_meta [] = "meta.xml";
416 bool isValidDate(const css::util::Date & i_rDate)
418 return i_rDate.Month > 0;
421 bool isValidDateTime(const css::util::DateTime & i_rDateTime)
423 return i_rDateTime.Month > 0;
426 std::pair< OUString, OUString >
427 getQualifier(const char* i_name) {
428 OUString nm = OUString::createFromAscii(i_name);
429 sal_Int32 ix = nm.indexOf(u':');
430 if (ix == -1) {
431 return std::make_pair(OUString(), nm);
432 } else {
433 return std::make_pair(nm.copy(0,ix), nm.copy(ix+1));
437 // get namespace for standard qualified names
438 // NB: only call this with statically known strings!
439 OUString getNameSpace(const char* i_qname) throw ()
441 assert(i_qname);
442 const char * ns = "";
443 OUString n = getQualifier(i_qname).first;
444 if ( n == "xlink" ) ns = s_nsXLink;
445 if ( n == "dc" ) ns = s_nsDC;
446 if ( n == "office" ) ns = s_nsODF;
447 if ( n == "meta" ) ns = s_nsODFMeta;
448 assert(*ns);
449 return OUString::createFromAscii(ns);
452 bool
453 textToDateOrDateTime(css::util::Date & io_rd, css::util::DateTime & io_rdt,
454 bool & o_rIsDateTime, std::optional<sal_Int16> & o_rTimeZone,
455 const OUString& i_text) throw ()
457 if (::sax::Converter::parseDateOrDateTime(
458 &io_rd, io_rdt, o_rIsDateTime, &o_rTimeZone, i_text)) {
459 return true;
460 } else {
461 SAL_WARN_IF(!i_text.isEmpty(), "sfx.doc", "Invalid date: " << i_text);
462 return false;
466 // convert string to date/time
467 bool
468 textToDateTime(css::util::DateTime & io_rdt, const OUString& i_text) throw ()
470 if (::sax::Converter::parseDateTime(io_rdt, i_text)) {
471 return true;
472 } else {
473 SAL_WARN_IF(!i_text.isEmpty(), "sfx.doc", "Invalid date: " << i_text);
474 return false;
478 // convert string to date/time with default return value
479 css::util::DateTime
480 textToDateTimeDefault(const OUString& i_text) throw ()
482 css::util::DateTime dt;
483 static_cast<void> (textToDateTime(dt, i_text));
484 // on conversion error: return default value (unchanged)
485 return dt;
488 // convert date to string
489 OUString
490 dateToText(css::util::Date const& i_rd,
491 sal_Int16 const*const pTimeZone) throw ()
493 if (isValidDate(i_rd)) {
494 OUStringBuffer buf;
495 ::sax::Converter::convertDate(buf, i_rd, pTimeZone);
496 return buf.makeStringAndClear();
497 } else {
498 return OUString();
503 // convert date/time to string
504 OUString
505 dateTimeToText(css::util::DateTime const& i_rdt,
506 sal_Int16 const*const pTimeZone = nullptr) throw ()
508 if (isValidDateTime(i_rdt)) {
509 OUStringBuffer buf(32);
510 ::sax::Converter::convertDateTime(buf, i_rdt, pTimeZone, true);
511 return buf.makeStringAndClear();
512 } else {
513 return OUString();
517 // convert string to duration
518 bool
519 textToDuration(css::util::Duration& io_rDur, OUString const& i_rText)
520 throw ()
522 if (::sax::Converter::convertDuration(io_rDur, i_rText)) {
523 return true;
524 } else {
525 SAL_WARN_IF(!i_rText.isEmpty(), "sfx.doc", "Invalid duration: " << i_rText);
526 return false;
530 sal_Int32 textToDuration(OUString const& i_rText) throw ()
532 css::util::Duration d;
533 if (textToDuration(d, i_rText)) {
534 // #i107372#: approximate years/months
535 const sal_Int32 days( (d.Years * 365) + (d.Months * 30) + d.Days );
536 return (days * (24*3600))
537 + (d.Hours * 3600) + (d.Minutes * 60) + d.Seconds;
538 } else {
539 return 0; // default
543 // convert duration to string
544 OUString durationToText(css::util::Duration const& i_rDur) throw ()
546 OUStringBuffer buf;
547 ::sax::Converter::convertDuration(buf, i_rDur);
548 return buf.makeStringAndClear();
551 // convert duration to string
552 OUString durationToText(sal_Int32 i_value) throw ()
554 css::util::Duration ud;
555 ud.Days = static_cast<sal_Int16>(i_value / (24 * 3600));
556 ud.Hours = static_cast<sal_Int16>((i_value % (24 * 3600)) / 3600);
557 ud.Minutes = static_cast<sal_Int16>((i_value % 3600) / 60);
558 ud.Seconds = static_cast<sal_Int16>(i_value % 60);
559 ud.NanoSeconds = 0;
560 return durationToText(ud);
563 // extract base URL (necessary for converting relative links)
564 css::uno::Reference< css::beans::XPropertySet >
565 SfxDocumentMetaData::getURLProperties(
566 const css::uno::Sequence< css::beans::PropertyValue > & i_rMedium) const
568 css::uno::Reference< css::beans::XPropertyBag> xPropArg = css::beans::PropertyBag::createDefault( m_xContext );
569 try {
570 css::uno::Any baseUri;
571 for (const auto& rProp : i_rMedium) {
572 if (rProp.Name == "DocumentBaseURL") {
573 baseUri = rProp.Value;
574 } else if (rProp.Name == "URL") {
575 if (!baseUri.hasValue()) {
576 baseUri = rProp.Value;
578 } else if (rProp.Name == "HierarchicalDocumentName") {
579 xPropArg->addProperty(
580 "StreamRelPath",
581 css::beans::PropertyAttribute::MAYBEVOID,
582 rProp.Value);
585 if (baseUri.hasValue()) {
586 xPropArg->addProperty(
587 "BaseURI", css::beans::PropertyAttribute::MAYBEVOID,
588 baseUri);
590 xPropArg->addProperty("StreamName",
591 css::beans::PropertyAttribute::MAYBEVOID,
592 css::uno::makeAny(OUString(s_meta)));
593 } catch (const css::uno::Exception &) {
594 // ignore
596 return css::uno::Reference< css::beans::XPropertySet>(xPropArg,
597 css::uno::UNO_QUERY_THROW);
600 // return the text of the (hopefully unique, i.e., normalize first!) text
601 // node _below_ the given node
602 /// @throws css::uno::RuntimeException
603 OUString
604 getNodeText(const css::uno::Reference<css::xml::dom::XNode>& i_xNode)
606 if (!i_xNode.is())
607 throw css::uno::RuntimeException("SfxDocumentMetaData::getNodeText: argument is null", i_xNode);
608 for (css::uno::Reference<css::xml::dom::XNode> c = i_xNode->getFirstChild();
609 c.is();
610 c = c->getNextSibling()) {
611 if (c->getNodeType() == css::xml::dom::NodeType_TEXT_NODE) {
612 try {
613 return c->getNodeValue();
614 } catch (const css::xml::dom::DOMException &) { // too big?
615 return OUString();
619 return OUString();
622 OUString
623 SfxDocumentMetaData::getMetaText(const char* i_name) const
624 // throw (css::uno::RuntimeException)
626 checkInit();
628 const OUString name( OUString::createFromAscii(i_name) );
629 assert(m_meta.find(name) != m_meta.end());
630 css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
631 return (xNode.is()) ? getNodeText(xNode) : OUString();
634 bool
635 SfxDocumentMetaData::setMetaText(const char* i_name,
636 const OUString & i_rValue)
637 // throw (css::uno::RuntimeException)
639 checkInit();
641 const OUString name( OUString::createFromAscii(i_name) );
642 assert(m_meta.find(name) != m_meta.end());
643 css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
645 try {
646 if (i_rValue.isEmpty()) {
647 if (xNode.is()) { // delete
648 m_xParent->removeChild(xNode);
649 xNode.clear();
650 m_meta[name] = xNode;
651 return true;
652 } else {
653 return false;
655 } else {
656 if (xNode.is()) { // update
657 for (css::uno::Reference<css::xml::dom::XNode> c =
658 xNode->getFirstChild();
659 c.is();
660 c = c->getNextSibling()) {
661 if (c->getNodeType() == css::xml::dom::NodeType_TEXT_NODE) {
662 if (c->getNodeValue() != i_rValue) {
663 c->setNodeValue(i_rValue);
664 return true;
665 } else {
666 return false;
670 } else { // insert
671 xNode.set(m_xDoc->createElementNS(getNameSpace(i_name), name),
672 css::uno::UNO_QUERY_THROW);
673 m_xParent->appendChild(xNode);
674 m_meta[name] = xNode;
676 css::uno::Reference<css::xml::dom::XNode> xTextNode(
677 m_xDoc->createTextNode(i_rValue), css::uno::UNO_QUERY_THROW);
678 xNode->appendChild(xTextNode);
679 return true;
681 } catch (const css::xml::dom::DOMException &) {
682 css::uno::Any anyEx = cppu::getCaughtException();
683 throw css::lang::WrappedTargetRuntimeException(
684 "SfxDocumentMetaData::setMetaText: DOM exception",
685 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
689 void
690 SfxDocumentMetaData::setMetaTextAndNotify(const char* i_name,
691 const OUString & i_rValue)
692 // throw (css::uno::RuntimeException)
694 ::osl::ClearableMutexGuard g(m_aMutex);
695 if (setMetaText(i_name, i_rValue)) {
696 g.clear();
697 setModified(true);
701 OUString
702 SfxDocumentMetaData::getMetaAttr(const char* i_name, const char* i_attr) const
703 // throw (css::uno::RuntimeException)
705 OUString name = OUString::createFromAscii(i_name);
706 assert(m_meta.find(name) != m_meta.end());
707 css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
708 if (xNode.is()) {
709 css::uno::Reference<css::xml::dom::XElement> xElem(xNode,
710 css::uno::UNO_QUERY_THROW);
711 return xElem->getAttributeNS(getNameSpace(i_attr),
712 getQualifier(i_attr).second);
713 } else {
714 return OUString();
718 css::uno::Sequence< OUString>
719 SfxDocumentMetaData::getMetaList(const char* i_name) const
720 // throw (css::uno::RuntimeException)
722 checkInit();
723 OUString name = OUString::createFromAscii(i_name);
724 assert(m_metaList.find(name) != m_metaList.end());
725 std::vector<css::uno::Reference<css::xml::dom::XNode> > const & vec =
726 m_metaList.find(name)->second;
727 css::uno::Sequence< OUString> ret(vec.size());
728 for (size_t i = 0; i < vec.size(); ++i) {
729 ret[i] = getNodeText(vec.at(i));
731 return ret;
734 bool
735 SfxDocumentMetaData::setMetaList(const char* i_name,
736 const css::uno::Sequence<OUString> & i_rValue,
737 AttrVector const* i_pAttrs)
738 // throw (css::uno::RuntimeException)
740 checkInit();
741 assert((i_pAttrs == nullptr) ||
742 (static_cast<size_t>(i_rValue.getLength()) == i_pAttrs->size()));
744 try {
745 OUString name = OUString::createFromAscii(i_name);
746 assert(m_metaList.find(name) != m_metaList.end());
747 std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
748 m_metaList[name];
750 // if nothing changed, do nothing
751 // alas, this does not check for permutations, or attributes...
752 if (nullptr == i_pAttrs) {
753 if (static_cast<size_t>(i_rValue.getLength()) == vec.size()) {
754 bool isEqual(true);
755 for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
756 css::uno::Reference<css::xml::dom::XNode> xNode(vec.at(i));
757 if (xNode.is()) {
758 OUString val = getNodeText(xNode);
759 if (val != i_rValue[i]) {
760 isEqual = false;
761 break;
765 if (isEqual) return false;
769 // remove old meta data nodes
771 std::vector<css::uno::Reference<css::xml::dom::XNode> >
772 ::reverse_iterator it(vec.rbegin());
773 try {
774 for ( ;it != vec.rend(); ++it)
776 m_xParent->removeChild(*it);
779 catch (...)
781 // Clean up already removed nodes
782 vec.erase(it.base(), vec.end());
783 throw;
785 vec.clear();
788 // insert new meta data nodes into DOM tree
789 for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
790 css::uno::Reference<css::xml::dom::XElement> xElem(
791 m_xDoc->createElementNS(getNameSpace(i_name), name),
792 css::uno::UNO_SET_THROW);
793 css::uno::Reference<css::xml::dom::XNode> xNode(xElem,
794 css::uno::UNO_QUERY_THROW);
795 css::uno::Reference<css::xml::dom::XNode> xTextNode(
796 m_xDoc->createTextNode(i_rValue[i]), css::uno::UNO_QUERY_THROW);
797 // set attributes
798 if (i_pAttrs != nullptr) {
799 for (auto const& elem : (*i_pAttrs)[i])
801 xElem->setAttributeNS(getNameSpace(elem.first),
802 OUString::createFromAscii(elem.first),
803 elem.second);
806 xNode->appendChild(xTextNode);
807 m_xParent->appendChild(xNode);
808 vec.push_back(xNode);
811 return true;
812 } catch (const css::xml::dom::DOMException &) {
813 css::uno::Any anyEx = cppu::getCaughtException();
814 throw css::lang::WrappedTargetRuntimeException(
815 "SfxDocumentMetaData::setMetaList: DOM exception",
816 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
820 // convert property list to string list and attribute list
821 std::pair<css::uno::Sequence< OUString>, AttrVector>
822 propsToStrings(css::uno::Reference<css::beans::XPropertySet> const & i_xPropSet)
824 ::std::vector< OUString > values;
825 AttrVector attrs;
827 css::uno::Reference<css::beans::XPropertySetInfo> xSetInfo
828 = i_xPropSet->getPropertySetInfo();
829 css::uno::Sequence<css::beans::Property> props = xSetInfo->getProperties();
831 for (sal_Int32 i = 0; i < props.getLength(); ++i) {
832 if (props[i].Attributes & css::beans::PropertyAttribute::TRANSIENT) {
833 continue;
835 const OUString name = props[i].Name;
836 css::uno::Any any;
837 try {
838 any = i_xPropSet->getPropertyValue(name);
839 } catch (const css::uno::Exception &) {
840 // ignore
842 const css::uno::Type & type = any.getValueType();
843 std::vector<std::pair<const char*, OUString> > as;
844 as.emplace_back("meta:name", name);
845 const char* vt = "meta:value-type";
847 // convert according to type
848 if (type == ::cppu::UnoType<bool>::get()) {
849 bool b = false;
850 any >>= b;
851 OUStringBuffer buf;
852 ::sax::Converter::convertBool(buf, b);
853 values.push_back(buf.makeStringAndClear());
854 as.emplace_back(vt, OUString("boolean"));
855 } else if (type == ::cppu::UnoType< OUString>::get()) {
856 OUString s;
857 any >>= s;
858 values.push_back(s);
859 // #i90847# OOo 2.x does stupid things if value-type="string";
860 // fortunately string is default anyway, so we can just omit it
861 // #i107502#: however, OOo 2.x only reads 4 user-defined without @value-type
862 // => best backward compatibility: first 4 without @value-type, rest with
863 if (4 <= i)
865 as.emplace_back(vt, OUString("string"));
867 } else if (type == ::cppu::UnoType<css::util::DateTime>::get()) {
868 css::util::DateTime dt;
869 any >>= dt;
870 values.push_back(dateTimeToText(dt));
871 as.emplace_back(vt, OUString("date"));
872 } else if (type == ::cppu::UnoType<css::util::Date>::get()) {
873 css::util::Date d;
874 any >>= d;
875 values.push_back(dateToText(d, nullptr));
876 as.emplace_back(vt,OUString("date"));
877 } else if (type == ::cppu::UnoType<css::util::DateTimeWithTimezone>::get()) {
878 css::util::DateTimeWithTimezone dttz;
879 any >>= dttz;
880 values.push_back(dateTimeToText(dttz.DateTimeInTZ, &dttz.Timezone));
881 as.emplace_back(vt, OUString("date"));
882 } else if (type == ::cppu::UnoType<css::util::DateWithTimezone>::get()) {
883 css::util::DateWithTimezone dtz;
884 any >>= dtz;
885 values.push_back(dateToText(dtz.DateInTZ, &dtz.Timezone));
886 as.emplace_back(vt, OUString("date"));
887 } else if (type == ::cppu::UnoType<css::util::Time>::get()) {
888 // #i97029#: replaced by Duration
889 // Time is supported for backward compatibility with OOo 3.x, x<=2
890 css::util::Time ut;
891 any >>= ut;
892 css::util::Duration ud;
893 ud.Hours = ut.Hours;
894 ud.Minutes = ut.Minutes;
895 ud.Seconds = ut.Seconds;
896 ud.NanoSeconds = ut.NanoSeconds;
897 values.push_back(durationToText(ud));
898 as.emplace_back(vt, OUString("time"));
899 } else if (type == ::cppu::UnoType<css::util::Duration>::get()) {
900 css::util::Duration ud;
901 any >>= ud;
902 values.push_back(durationToText(ud));
903 as.emplace_back(vt, OUString("time"));
904 } else if (::cppu::UnoType<double>::get().isAssignableFrom(type)) {
905 // support not just double, but anything that can be converted
906 double d = 0;
907 any >>= d;
908 OUStringBuffer buf;
909 ::sax::Converter::convertDouble(buf, d);
910 values.push_back(buf.makeStringAndClear());
911 as.emplace_back(vt, OUString("float"));
912 } else {
913 SAL_WARN("sfx.doc", "Unsupported property type: " << any.getValueTypeName() );
914 continue;
916 attrs.push_back(as);
919 return std::make_pair(comphelper::containerToSequence(values), attrs);
922 // remove the given element from the DOM, and iff i_pAttrs != 0 insert new one
923 void
924 SfxDocumentMetaData::updateElement(const char *i_name,
925 std::vector<std::pair<const char *, OUString> >* i_pAttrs)
927 OUString name = OUString::createFromAscii(i_name);
928 try {
929 // remove old element
930 css::uno::Reference<css::xml::dom::XNode> xNode =
931 m_meta.find(name)->second;
932 if (xNode.is()) {
933 m_xParent->removeChild(xNode);
934 xNode.clear();
936 // add new element
937 if (nullptr != i_pAttrs) {
938 css::uno::Reference<css::xml::dom::XElement> xElem(
939 m_xDoc->createElementNS(getNameSpace(i_name), name),
940 css::uno::UNO_SET_THROW);
941 xNode.set(xElem, css::uno::UNO_QUERY_THROW);
942 // set attributes
943 for (auto const& elem : *i_pAttrs)
945 xElem->setAttributeNS(getNameSpace(elem.first),
946 OUString::createFromAscii(elem.first), elem.second);
948 m_xParent->appendChild(xNode);
950 m_meta[name] = xNode;
951 } catch (const css::xml::dom::DOMException &) {
952 css::uno::Any anyEx = cppu::getCaughtException();
953 throw css::lang::WrappedTargetRuntimeException(
954 "SfxDocumentMetaData::updateElement: DOM exception",
955 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
959 // update user-defined meta data in DOM tree
960 void SfxDocumentMetaData::updateUserDefinedAndAttributes()
962 createUserDefined();
963 const css::uno::Reference<css::beans::XPropertySet> xPSet(m_xUserDefined,
964 css::uno::UNO_QUERY_THROW);
965 const std::pair<css::uno::Sequence< OUString>, AttrVector>
966 udStringsAttrs( propsToStrings(xPSet) );
967 (void) setMetaList("meta:user-defined", udStringsAttrs.first,
968 &udStringsAttrs.second);
970 // update elements with attributes
971 std::vector<std::pair<const char *, OUString> > attributes;
972 if (!m_TemplateName.isEmpty() || !m_TemplateURL.isEmpty()
973 || isValidDateTime(m_TemplateDate)) {
974 attributes.emplace_back("xlink:type", OUString("simple"));
975 attributes.emplace_back("xlink:actuate", OUString("onRequest"));
976 attributes.emplace_back("xlink:title", m_TemplateName);
977 attributes.emplace_back("xlink:href", m_TemplateURL );
978 if (isValidDateTime(m_TemplateDate)) {
979 attributes.emplace_back(
980 "meta:date", dateTimeToText(m_TemplateDate));
982 updateElement("meta:template", &attributes);
983 } else {
984 updateElement("meta:template");
986 attributes.clear();
988 if (!m_AutoloadURL.isEmpty() || (0 != m_AutoloadSecs)) {
989 attributes.emplace_back("xlink:href", m_AutoloadURL );
990 attributes.emplace_back("meta:delay",
991 durationToText(m_AutoloadSecs));
992 updateElement("meta:auto-reload", &attributes);
993 } else {
994 updateElement("meta:auto-reload");
996 attributes.clear();
998 if (!m_DefaultTarget.isEmpty()) {
999 attributes.emplace_back(
1000 "office:target-frame-name",
1001 m_DefaultTarget);
1002 // xlink:show: _blank -> new, any other value -> replace
1003 const char* show = m_DefaultTarget == "_blank" ? "new" : "replace";
1004 attributes.emplace_back(
1005 "xlink:show",
1006 OUString::createFromAscii(show));
1007 updateElement("meta:hyperlink-behaviour", &attributes);
1008 } else {
1009 updateElement("meta:hyperlink-behaviour");
1011 attributes.clear();
1014 // create empty DOM tree (XDocument)
1015 css::uno::Reference<css::xml::dom::XDocument>
1016 SfxDocumentMetaData::createDOM() const // throw (css::uno::RuntimeException)
1018 css::uno::Reference<css::xml::dom::XDocumentBuilder> xBuilder( css::xml::dom::DocumentBuilder::create(m_xContext) );
1019 css::uno::Reference<css::xml::dom::XDocument> xDoc = xBuilder->newDocument();
1020 if (!xDoc.is())
1021 throw css::uno::RuntimeException(
1022 "SfxDocumentMetaData::createDOM: cannot create new document",
1023 *const_cast<SfxDocumentMetaData*>(this));
1024 return xDoc;
1027 void
1028 SfxDocumentMetaData::checkInit() const // throw (css::uno::RuntimeException)
1030 if (!m_isInitialized) {
1031 throw css::uno::RuntimeException(
1032 "SfxDocumentMetaData::checkInit: not initialized",
1033 *const_cast<SfxDocumentMetaData*>(this));
1035 assert(m_xDoc.is() && m_xParent.is());
1038 // initialize state from DOM tree
1039 void SfxDocumentMetaData::init(
1040 const css::uno::Reference<css::xml::dom::XDocument>& i_xDoc)
1042 if (!i_xDoc.is())
1043 throw css::uno::RuntimeException("SfxDocumentMetaData::init: no DOM tree given", *this);
1045 css::uno::Reference<css::xml::xpath::XXPathAPI> xPath = css::xml::xpath::XPathAPI::create(m_xContext);
1047 m_isInitialized = false;
1048 m_xDoc = i_xDoc;
1050 // select nodes for standard meta data stuff
1051 xPath->registerNS("xlink", s_nsXLink);
1052 xPath->registerNS("dc", s_nsDC);
1053 xPath->registerNS("office", s_nsODF);
1054 xPath->registerNS("meta", s_nsODFMeta);
1055 // NB: we do not handle the single-XML-file ODF variant, which would
1056 // have the root element office:document.
1057 // The root of such documents must be converted in the importer!
1058 css::uno::Reference<css::xml::dom::XNode> xDocNode(
1059 m_xDoc, css::uno::UNO_QUERY_THROW);
1060 m_xParent.clear();
1061 try {
1062 m_xParent = xPath->selectSingleNode(xDocNode, "/child::office:document-meta/child::office:meta");
1063 } catch (const css::uno::Exception &) {
1066 if (!m_xParent.is()) {
1067 // all this create/append stuff may throw DOMException
1068 try {
1069 css::uno::Reference<css::xml::dom::XElement> xRElem;
1070 css::uno::Reference<css::xml::dom::XNode> xNode(
1071 i_xDoc->getFirstChild());
1072 while (xNode.is()) {
1073 if (css::xml::dom::NodeType_ELEMENT_NODE ==xNode->getNodeType())
1075 if ( xNode->getNamespaceURI() == s_nsODF && xNode->getLocalName() == "document-meta" )
1077 xRElem.set(xNode, css::uno::UNO_QUERY_THROW);
1078 break;
1080 else
1082 SAL_INFO("sfx.doc", "SfxDocumentMetaData::init(): "
1083 "deleting unexpected root element: "
1084 << xNode->getLocalName());
1085 i_xDoc->removeChild(xNode);
1086 xNode = i_xDoc->getFirstChild(); // start over
1088 } else {
1089 xNode = xNode->getNextSibling();
1092 if (!xRElem.is()) {
1093 xRElem = i_xDoc->createElementNS(
1094 s_nsODF, "office:document-meta");
1095 css::uno::Reference<css::xml::dom::XNode> xRNode(xRElem,
1096 css::uno::UNO_QUERY_THROW);
1097 i_xDoc->appendChild(xRNode);
1099 xRElem->setAttributeNS(s_nsODF, "office:version", "1.0");
1100 // does not exist, otherwise m_xParent would not be null
1101 css::uno::Reference<css::xml::dom::XNode> xParent (
1102 i_xDoc->createElementNS(s_nsODF, "office:meta"),
1103 css::uno::UNO_QUERY_THROW);
1104 xRElem->appendChild(xParent);
1105 m_xParent = xParent;
1106 } catch (const css::xml::dom::DOMException &) {
1107 css::uno::Any anyEx = cppu::getCaughtException();
1108 throw css::lang::WrappedTargetRuntimeException(
1109 "SfxDocumentMetaData::init: DOM exception",
1110 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
1115 // select nodes for elements of which we only handle one occurrence
1116 for (const char **pName = s_stdMeta; *pName != nullptr; ++pName) {
1117 OUString name = OUString::createFromAscii(*pName);
1118 // NB: If a document contains more than one occurrence of a
1119 // meta-data element, we arbitrarily pick one of them here.
1120 // We do not remove the others, i.e., when we write the
1121 // document, it will contain the duplicates unchanged.
1122 // The ODF spec says that handling multiple occurrences is
1123 // application-specific.
1124 css::uno::Reference<css::xml::dom::XNode> xNode =
1125 xPath->selectSingleNode(m_xParent, "child::" + name);
1126 // Do not create an empty element if it is missing;
1127 // for certain elements, such as dateTime, this would be invalid
1128 m_meta[name] = xNode;
1131 // select nodes for elements of which we handle all occurrences
1132 for (const char **pName = s_stdMetaList; *pName != nullptr; ++pName) {
1133 OUString name = OUString::createFromAscii(*pName);
1134 css::uno::Reference<css::xml::dom::XNodeList> nodes =
1135 xPath->selectNodeList(m_xParent, "child::" + name);
1136 std::vector<css::uno::Reference<css::xml::dom::XNode> > v;
1137 v.reserve(nodes->getLength());
1138 for (sal_Int32 i = 0; i < nodes->getLength(); ++i)
1140 v.push_back(nodes->item(i));
1142 m_metaList[name] = v;
1145 // initialize members corresponding to attributes from DOM nodes
1146 m_TemplateName = getMetaAttr("meta:template", "xlink:title");
1147 m_TemplateURL = getMetaAttr("meta:template", "xlink:href");
1148 m_TemplateDate =
1149 textToDateTimeDefault(getMetaAttr("meta:template", "meta:date"));
1150 m_AutoloadURL = getMetaAttr("meta:auto-reload", "xlink:href");
1151 m_AutoloadSecs =
1152 textToDuration(getMetaAttr("meta:auto-reload", "meta:delay"));
1153 m_DefaultTarget =
1154 getMetaAttr("meta:hyperlink-behaviour", "office:target-frame-name");
1157 std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
1158 m_metaList[OUString("meta:user-defined")];
1159 m_xUserDefined.clear(); // #i105826#: reset (may be re-initialization)
1160 if ( !vec.empty() )
1162 createUserDefined();
1165 // user-defined meta data: initialize PropertySet from DOM nodes
1166 for (auto const& elem : vec)
1168 css::uno::Reference<css::xml::dom::XElement> xElem(elem,
1169 css::uno::UNO_QUERY_THROW);
1170 css::uno::Any any;
1171 OUString name = xElem->getAttributeNS(s_nsODFMeta, "name");
1172 OUString type = xElem->getAttributeNS(s_nsODFMeta, "value-type");
1173 OUString text = getNodeText(elem);
1174 if ( type == "float" ) {
1175 double d;
1176 if (::sax::Converter::convertDouble(d, text)) {
1177 any <<= d;
1178 } else {
1179 SAL_WARN("sfx.doc", "Invalid float: " << text);
1180 continue;
1182 } else if ( type == "date" ) {
1183 bool isDateTime;
1184 css::util::Date d;
1185 css::util::DateTime dt;
1186 std::optional<sal_Int16> nTimeZone;
1187 if (textToDateOrDateTime(d, dt, isDateTime, nTimeZone, text)) {
1188 if (isDateTime) {
1189 if (nTimeZone) {
1190 any <<= css::util::DateTimeWithTimezone(dt,
1191 *nTimeZone);
1192 } else {
1193 any <<= dt;
1195 } else {
1196 if (nTimeZone) {
1197 any <<= css::util::DateWithTimezone(d, *nTimeZone);
1198 } else {
1199 any <<= d;
1202 } else {
1203 SAL_WARN("sfx.doc", "Invalid date: " << text);
1204 continue;
1206 } else if ( type == "time" ) {
1207 css::util::Duration ud;
1208 if (textToDuration(ud, text)) {
1209 any <<= ud;
1210 } else {
1211 SAL_WARN("sfx.doc", "Invalid time: " << text);
1212 continue;
1214 } else if ( type == "boolean" ) {
1215 bool b;
1216 if (::sax::Converter::convertBool(b, text)) {
1217 any <<= b;
1218 } else {
1219 SAL_WARN("sfx.doc", "Invalid boolean: " << text);
1220 continue;
1222 } else { // default
1223 any <<= text;
1225 try {
1226 m_xUserDefined->addProperty(name,
1227 css::beans::PropertyAttribute::REMOVABLE, any);
1228 } catch (const css::beans::PropertyExistException &) {
1229 SAL_WARN("sfx.doc", "Duplicate: " << name);
1230 // ignore; duplicate
1231 } catch (const css::beans::IllegalTypeException &) {
1232 SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal type: " << name);
1233 } catch (const css::lang::IllegalArgumentException &) {
1234 SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal arg: " << name);
1238 m_isModified = false;
1239 m_isInitialized = true;
1243 SfxDocumentMetaData::SfxDocumentMetaData(
1244 css::uno::Reference< css::uno::XComponentContext > const & context)
1245 : BaseMutex()
1246 , SfxDocumentMetaData_Base(m_aMutex)
1247 , m_xContext(context)
1248 , m_NotifyListeners(m_aMutex)
1249 , m_isInitialized(false)
1250 , m_isModified(false)
1251 , m_AutoloadSecs(0)
1253 assert(context.is());
1254 assert(context->getServiceManager().is());
1255 init(createDOM());
1258 // com.sun.star.uno.XServiceInfo:
1259 OUString SAL_CALL
1260 SfxDocumentMetaData::getImplementationName()
1262 return "SfxDocumentMetaData";
1265 sal_Bool SAL_CALL
1266 SfxDocumentMetaData::supportsService(OUString const & serviceName)
1268 return cppu::supportsService(this, serviceName);
1271 css::uno::Sequence< OUString > SAL_CALL
1272 SfxDocumentMetaData::getSupportedServiceNames()
1274 css::uno::Sequence< OUString > s { "com.sun.star.document.DocumentProperties" };
1275 return s;
1279 // css::lang::XComponent:
1280 void SAL_CALL SfxDocumentMetaData::dispose()
1282 ::osl::MutexGuard g(m_aMutex);
1283 if (!m_isInitialized) {
1284 return;
1286 WeakComponentImplHelperBase::dispose(); // superclass
1287 m_NotifyListeners.disposeAndClear(css::lang::EventObject(
1288 static_cast< ::cppu::OWeakObject* >(this)));
1289 m_isInitialized = false;
1290 m_meta.clear();
1291 m_metaList.clear();
1292 m_xParent.clear();
1293 m_xDoc.clear();
1294 m_xUserDefined.clear();
1298 // css::document::XDocumentProperties:
1299 OUString SAL_CALL
1300 SfxDocumentMetaData::getAuthor()
1302 ::osl::MutexGuard g(m_aMutex);
1303 return getMetaText("meta:initial-creator");
1306 void SAL_CALL SfxDocumentMetaData::setAuthor(const OUString & the_value)
1308 setMetaTextAndNotify("meta:initial-creator", the_value);
1312 OUString SAL_CALL
1313 SfxDocumentMetaData::getGenerator()
1315 ::osl::MutexGuard g(m_aMutex);
1316 return getMetaText("meta:generator");
1319 void SAL_CALL
1320 SfxDocumentMetaData::setGenerator(const OUString & the_value)
1322 setMetaTextAndNotify("meta:generator", the_value);
1325 css::util::DateTime SAL_CALL
1326 SfxDocumentMetaData::getCreationDate()
1328 ::osl::MutexGuard g(m_aMutex);
1329 return textToDateTimeDefault(getMetaText("meta:creation-date"));
1332 void SAL_CALL
1333 SfxDocumentMetaData::setCreationDate(const css::util::DateTime & the_value)
1335 setMetaTextAndNotify("meta:creation-date", dateTimeToText(the_value));
1338 OUString SAL_CALL
1339 SfxDocumentMetaData::getTitle()
1341 ::osl::MutexGuard g(m_aMutex);
1342 return getMetaText("dc:title");
1345 void SAL_CALL SfxDocumentMetaData::setTitle(const OUString & the_value)
1347 setMetaTextAndNotify("dc:title", the_value);
1350 OUString SAL_CALL
1351 SfxDocumentMetaData::getSubject()
1353 ::osl::MutexGuard g(m_aMutex);
1354 return getMetaText("dc:subject");
1357 void SAL_CALL
1358 SfxDocumentMetaData::setSubject(const OUString & the_value)
1360 setMetaTextAndNotify("dc:subject", the_value);
1363 OUString SAL_CALL
1364 SfxDocumentMetaData::getDescription()
1366 ::osl::MutexGuard g(m_aMutex);
1367 return getMetaText("dc:description");
1370 void SAL_CALL
1371 SfxDocumentMetaData::setDescription(const OUString & the_value)
1373 setMetaTextAndNotify("dc:description", the_value);
1376 css::uno::Sequence< OUString >
1377 SAL_CALL SfxDocumentMetaData::getKeywords()
1379 ::osl::MutexGuard g(m_aMutex);
1380 return getMetaList("meta:keyword");
1383 void SAL_CALL
1384 SfxDocumentMetaData::setKeywords(
1385 const css::uno::Sequence< OUString > & the_value)
1387 ::osl::ClearableMutexGuard g(m_aMutex);
1388 if (setMetaList("meta:keyword", the_value, nullptr)) {
1389 g.clear();
1390 setModified(true);
1394 css::lang::Locale SAL_CALL
1395 SfxDocumentMetaData::getLanguage()
1397 ::osl::MutexGuard g(m_aMutex);
1398 css::lang::Locale loc( LanguageTag::convertToLocale( getMetaText("dc:language"), false));
1399 return loc;
1402 void SAL_CALL
1403 SfxDocumentMetaData::setLanguage(const css::lang::Locale & the_value)
1405 OUString text( LanguageTag::convertToBcp47( the_value, false));
1406 setMetaTextAndNotify("dc:language", text);
1409 OUString SAL_CALL
1410 SfxDocumentMetaData::getModifiedBy()
1412 ::osl::MutexGuard g(m_aMutex);
1413 return getMetaText("dc:creator");
1416 void SAL_CALL
1417 SfxDocumentMetaData::setModifiedBy(const OUString & the_value)
1419 setMetaTextAndNotify("dc:creator", the_value);
1422 css::util::DateTime SAL_CALL
1423 SfxDocumentMetaData::getModificationDate()
1425 ::osl::MutexGuard g(m_aMutex);
1426 return textToDateTimeDefault(getMetaText("dc:date"));
1429 void SAL_CALL
1430 SfxDocumentMetaData::setModificationDate(const css::util::DateTime & the_value)
1432 setMetaTextAndNotify("dc:date", dateTimeToText(the_value));
1435 OUString SAL_CALL
1436 SfxDocumentMetaData::getPrintedBy()
1438 ::osl::MutexGuard g(m_aMutex);
1439 return getMetaText("meta:printed-by");
1442 void SAL_CALL
1443 SfxDocumentMetaData::setPrintedBy(const OUString & the_value)
1445 setMetaTextAndNotify("meta:printed-by", the_value);
1448 css::util::DateTime SAL_CALL
1449 SfxDocumentMetaData::getPrintDate()
1451 ::osl::MutexGuard g(m_aMutex);
1452 return textToDateTimeDefault(getMetaText("meta:print-date"));
1455 void SAL_CALL
1456 SfxDocumentMetaData::setPrintDate(const css::util::DateTime & the_value)
1458 setMetaTextAndNotify("meta:print-date", dateTimeToText(the_value));
1461 OUString SAL_CALL
1462 SfxDocumentMetaData::getTemplateName()
1464 ::osl::MutexGuard g(m_aMutex);
1465 checkInit();
1466 return m_TemplateName;
1469 void SAL_CALL
1470 SfxDocumentMetaData::setTemplateName(const OUString & the_value)
1472 ::osl::ClearableMutexGuard g(m_aMutex);
1473 checkInit();
1474 if (m_TemplateName != the_value) {
1475 m_TemplateName = the_value;
1476 g.clear();
1477 setModified(true);
1481 OUString SAL_CALL
1482 SfxDocumentMetaData::getTemplateURL()
1484 ::osl::MutexGuard g(m_aMutex);
1485 checkInit();
1486 return m_TemplateURL;
1489 void SAL_CALL
1490 SfxDocumentMetaData::setTemplateURL(const OUString & the_value)
1492 ::osl::ClearableMutexGuard g(m_aMutex);
1493 checkInit();
1494 if (m_TemplateURL != the_value) {
1495 m_TemplateURL = the_value;
1496 g.clear();
1497 setModified(true);
1501 css::util::DateTime SAL_CALL
1502 SfxDocumentMetaData::getTemplateDate()
1504 ::osl::MutexGuard g(m_aMutex);
1505 checkInit();
1506 return m_TemplateDate;
1509 void SAL_CALL
1510 SfxDocumentMetaData::setTemplateDate(const css::util::DateTime & the_value)
1512 ::osl::ClearableMutexGuard g(m_aMutex);
1513 checkInit();
1514 if (m_TemplateDate != the_value) {
1515 m_TemplateDate = the_value;
1516 g.clear();
1517 setModified(true);
1521 OUString SAL_CALL
1522 SfxDocumentMetaData::getAutoloadURL()
1524 ::osl::MutexGuard g(m_aMutex);
1525 checkInit();
1526 return m_AutoloadURL;
1529 void SAL_CALL
1530 SfxDocumentMetaData::setAutoloadURL(const OUString & the_value)
1532 ::osl::ClearableMutexGuard g(m_aMutex);
1533 checkInit();
1534 if (m_AutoloadURL != the_value) {
1535 m_AutoloadURL = the_value;
1536 g.clear();
1537 setModified(true);
1541 ::sal_Int32 SAL_CALL
1542 SfxDocumentMetaData::getAutoloadSecs()
1544 ::osl::MutexGuard g(m_aMutex);
1545 checkInit();
1546 return m_AutoloadSecs;
1549 void SAL_CALL
1550 SfxDocumentMetaData::setAutoloadSecs(::sal_Int32 the_value)
1552 if (the_value < 0)
1553 throw css::lang::IllegalArgumentException(
1554 "SfxDocumentMetaData::setAutoloadSecs: argument is negative",
1555 *this, 0);
1556 ::osl::ClearableMutexGuard g(m_aMutex);
1557 checkInit();
1558 if (m_AutoloadSecs != the_value) {
1559 m_AutoloadSecs = the_value;
1560 g.clear();
1561 setModified(true);
1565 OUString SAL_CALL
1566 SfxDocumentMetaData::getDefaultTarget()
1568 ::osl::MutexGuard g(m_aMutex);
1569 checkInit();
1570 return m_DefaultTarget;
1573 void SAL_CALL
1574 SfxDocumentMetaData::setDefaultTarget(const OUString & the_value)
1576 ::osl::ClearableMutexGuard g(m_aMutex);
1577 checkInit();
1578 if (m_DefaultTarget != the_value) {
1579 m_DefaultTarget = the_value;
1580 g.clear();
1581 setModified(true);
1585 css::uno::Sequence< css::beans::NamedValue > SAL_CALL
1586 SfxDocumentMetaData::getDocumentStatistics()
1588 ::osl::MutexGuard g(m_aMutex);
1589 checkInit();
1590 ::std::vector<css::beans::NamedValue> stats;
1591 for (size_t i = 0; s_stdStats[i] != nullptr; ++i) {
1592 const char * aName = s_stdStatAttrs[i];
1593 OUString text = getMetaAttr("meta:document-statistic", aName);
1594 if (text.isEmpty()) continue;
1595 css::beans::NamedValue stat;
1596 stat.Name = OUString::createFromAscii(s_stdStats[i]);
1597 sal_Int32 val;
1598 css::uno::Any any;
1599 if (!::sax::Converter::convertNumber(val, text, 0) || (val < 0)) {
1600 val = 0;
1601 SAL_WARN("sfx.doc", "Invalid number: " << text);
1603 any <<= val;
1604 stat.Value = any;
1605 stats.push_back(stat);
1608 return ::comphelper::containerToSequence(stats);
1611 void SAL_CALL
1612 SfxDocumentMetaData::setDocumentStatistics(
1613 const css::uno::Sequence< css::beans::NamedValue > & the_value)
1616 osl::MutexGuard g(m_aMutex);
1617 checkInit();
1618 std::vector<std::pair<const char *, OUString> > attributes;
1619 for (const auto& rValue : the_value) {
1620 const OUString name = rValue.Name;
1621 // inefficiently search for matching attribute
1622 for (size_t j = 0; s_stdStats[j] != nullptr; ++j) {
1623 if (name.equalsAscii(s_stdStats[j])) {
1624 const css::uno::Any any = rValue.Value;
1625 sal_Int32 val = 0;
1626 if (any >>= val) {
1627 attributes.emplace_back(s_stdStatAttrs[j],
1628 OUString::number(val));
1630 else {
1631 SAL_WARN("sfx.doc", "Invalid statistic: " << name);
1633 break;
1637 updateElement("meta:document-statistic", &attributes);
1639 setModified(true);
1642 ::sal_Int16 SAL_CALL
1643 SfxDocumentMetaData::getEditingCycles()
1645 ::osl::MutexGuard g(m_aMutex);
1646 OUString text = getMetaText("meta:editing-cycles");
1647 sal_Int32 ret;
1648 if (::sax::Converter::convertNumber(ret, text,
1649 0, std::numeric_limits<sal_Int16>::max())) {
1650 return static_cast<sal_Int16>(ret);
1651 } else {
1652 return 0;
1656 void SAL_CALL
1657 SfxDocumentMetaData::setEditingCycles(::sal_Int16 the_value)
1659 if (the_value < 0)
1660 throw css::lang::IllegalArgumentException(
1661 "SfxDocumentMetaData::setEditingCycles: argument is negative",
1662 *this, 0);
1663 setMetaTextAndNotify("meta:editing-cycles", OUString::number(the_value));
1666 ::sal_Int32 SAL_CALL
1667 SfxDocumentMetaData::getEditingDuration()
1669 ::osl::MutexGuard g(m_aMutex);
1670 return textToDuration(getMetaText("meta:editing-duration"));
1673 void SAL_CALL
1674 SfxDocumentMetaData::setEditingDuration(::sal_Int32 the_value)
1676 if (the_value < 0)
1677 throw css::lang::IllegalArgumentException(
1678 "SfxDocumentMetaData::setEditingDuration: argument is negative",
1679 *this, 0);
1680 setMetaTextAndNotify("meta:editing-duration", durationToText(the_value));
1683 void SAL_CALL
1684 SfxDocumentMetaData::resetUserData(const OUString & the_value)
1686 ::osl::ClearableMutexGuard g(m_aMutex);
1688 bool bModified( false );
1689 bModified |= setMetaText("meta:initial-creator", the_value);
1690 ::DateTime now( ::DateTime::SYSTEM );
1691 css::util::DateTime uDT(now.GetUNODateTime());
1692 bModified |= setMetaText("meta:creation-date", dateTimeToText(uDT));
1693 bModified |= setMetaText("dc:creator", OUString());
1694 bModified |= setMetaText("meta:printed-by", OUString());
1695 bModified |= setMetaText("dc:date", dateTimeToText(css::util::DateTime()));
1696 bModified |= setMetaText("meta:print-date",
1697 dateTimeToText(css::util::DateTime()));
1698 bModified |= setMetaText("meta:editing-duration", durationToText(0));
1699 bModified |= setMetaText("meta:editing-cycles",
1700 "1");
1702 if (bModified) {
1703 g.clear();
1704 setModified(true);
1709 css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
1710 SfxDocumentMetaData::getUserDefinedProperties()
1712 ::osl::MutexGuard g(m_aMutex);
1713 checkInit();
1714 createUserDefined();
1715 return m_xUserDefined;
1719 void SAL_CALL
1720 SfxDocumentMetaData::loadFromStorage(
1721 const css::uno::Reference< css::embed::XStorage > & xStorage,
1722 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1724 if (!xStorage.is())
1725 throw css::lang::IllegalArgumentException("SfxDocumentMetaData::loadFromStorage: argument is null", *this, 0);
1726 ::osl::MutexGuard g(m_aMutex);
1728 // open meta data file
1729 css::uno::Reference<css::io::XStream> xStream(
1730 xStorage->openStreamElement(
1731 s_meta,
1732 css::embed::ElementModes::READ) );
1733 if (!xStream.is()) throw css::uno::RuntimeException();
1734 css::uno::Reference<css::io::XInputStream> xInStream =
1735 xStream->getInputStream();
1736 if (!xInStream.is()) throw css::uno::RuntimeException();
1738 // create DOM parser service
1739 css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
1740 m_xContext->getServiceManager());
1741 css::xml::sax::InputSource input;
1742 input.aInputStream = xInStream;
1744 sal_uInt64 version = SotStorage::GetVersion( xStorage );
1745 // Oasis is also the default (0)
1746 bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
1747 const char *pServiceName = bOasis
1748 ? "com.sun.star.document.XMLOasisMetaImporter"
1749 : "com.sun.star.document.XMLMetaImporter";
1751 // set base URL
1752 css::uno::Reference<css::beans::XPropertySet> xPropArg =
1753 getURLProperties(Medium);
1754 try {
1755 xPropArg->getPropertyValue("BaseURI")
1756 >>= input.sSystemId;
1757 input.sSystemId += OUStringLiteral(u"/") + s_meta;
1758 } catch (const css::uno::Exception &) {
1759 input.sSystemId = s_meta;
1761 css::uno::Sequence< css::uno::Any > args(1);
1762 args[0] <<= xPropArg;
1764 // the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler
1765 css::uno::Reference<XInterface> xFilter =
1766 xMsf->createInstanceWithArgumentsAndContext(
1767 OUString::createFromAscii(pServiceName), args, m_xContext);
1768 assert(xFilter);
1769 css::uno::Reference<css::xml::sax::XFastParser> xFastParser(xFilter, css::uno::UNO_QUERY);
1770 css::uno::Reference<css::document::XImporter> xImp(xFilter, css::uno::UNO_QUERY_THROW);
1771 xImp->setTargetDocument(css::uno::Reference<css::lang::XComponent>(this));
1772 try {
1773 if (xFastParser)
1774 xFastParser->parseStream(input);
1775 else
1777 css::uno::Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, css::uno::UNO_QUERY_THROW);
1778 css::uno::Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(m_xContext);
1779 xParser->setDocumentHandler(xDocHandler);
1780 xParser->parseStream(input);
1782 } catch (const css::xml::sax::SAXException &) {
1783 throw css::io::WrongFormatException(
1784 "SfxDocumentMetaData::loadFromStorage:"
1785 " XML parsing exception", *this);
1787 // NB: the implementation of XMLOasisMetaImporter calls initialize
1788 checkInit();
1791 void SAL_CALL
1792 SfxDocumentMetaData::storeToStorage(
1793 const css::uno::Reference< css::embed::XStorage > & xStorage,
1794 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1796 if (!xStorage.is())
1797 throw css::lang::IllegalArgumentException(
1798 "SfxDocumentMetaData::storeToStorage: argument is null", *this, 0);
1799 ::osl::MutexGuard g(m_aMutex);
1800 checkInit();
1802 // update user-defined meta data in DOM tree
1803 // updateUserDefinedAndAttributes(); // this will be done in serialize!
1805 // write into storage
1806 css::uno::Reference<css::io::XStream> xStream =
1807 xStorage->openStreamElement(s_meta,
1808 css::embed::ElementModes::WRITE
1809 | css::embed::ElementModes::TRUNCATE);
1810 if (!xStream.is()) throw css::uno::RuntimeException();
1811 css::uno::Reference< css::beans::XPropertySet > xStreamProps(xStream,
1812 css::uno::UNO_QUERY_THROW);
1813 xStreamProps->setPropertyValue(
1814 "MediaType",
1815 css::uno::makeAny(OUString("text/xml")));
1816 xStreamProps->setPropertyValue(
1817 "Compressed",
1818 css::uno::makeAny(false));
1819 xStreamProps->setPropertyValue(
1820 "UseCommonStoragePasswordEncryption",
1821 css::uno::makeAny(false));
1822 css::uno::Reference<css::io::XOutputStream> xOutStream =
1823 xStream->getOutputStream();
1824 if (!xOutStream.is()) throw css::uno::RuntimeException();
1825 css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
1826 m_xContext->getServiceManager());
1827 css::uno::Reference<css::xml::sax::XWriter> xSaxWriter(
1828 css::xml::sax::Writer::create(m_xContext));
1829 xSaxWriter->setOutputStream(xOutStream);
1831 const sal_uInt64 version = SotStorage::GetVersion( xStorage );
1832 // Oasis is also the default (0)
1833 const bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
1834 const char *pServiceName = bOasis
1835 ? "com.sun.star.document.XMLOasisMetaExporter"
1836 : "com.sun.star.document.XMLMetaExporter";
1838 // set base URL
1839 css::uno::Reference<css::beans::XPropertySet> xPropArg =
1840 getURLProperties(Medium);
1841 css::uno::Sequence< css::uno::Any > args(2);
1842 args[0] <<= xSaxWriter;
1843 args[1] <<= xPropArg;
1845 css::uno::Reference<css::document::XExporter> xExp(
1846 xMsf->createInstanceWithArgumentsAndContext(
1847 OUString::createFromAscii(pServiceName), args, m_xContext),
1848 css::uno::UNO_QUERY_THROW);
1849 xExp->setSourceDocument(css::uno::Reference<css::lang::XComponent>(this));
1850 css::uno::Reference<css::document::XFilter> xFilter(xExp,
1851 css::uno::UNO_QUERY_THROW);
1852 if (!xFilter->filter(css::uno::Sequence< css::beans::PropertyValue >())) {
1853 throw css::io::IOException(
1854 "SfxDocumentMetaData::storeToStorage: cannot filter", *this);
1856 css::uno::Reference<css::embed::XTransactedObject> xTransaction(
1857 xStorage, css::uno::UNO_QUERY);
1858 if (xTransaction.is()) {
1859 xTransaction->commit();
1863 void SAL_CALL
1864 SfxDocumentMetaData::loadFromMedium(const OUString & URL,
1865 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1867 css::uno::Reference<css::io::XInputStream> xIn;
1868 utl::MediaDescriptor md(Medium);
1869 // if we have a URL parameter, it replaces the one in the media descriptor
1870 if (!URL.isEmpty()) {
1871 md[ utl::MediaDescriptor::PROP_URL() ] <<= URL;
1872 md[ utl::MediaDescriptor::PROP_READONLY() ] <<= true;
1874 if (md.addInputStream()) {
1875 md[ utl::MediaDescriptor::PROP_INPUTSTREAM() ] >>= xIn;
1877 css::uno::Reference<css::embed::XStorage> xStorage;
1878 try {
1879 if (xIn.is()) {
1880 xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
1881 xIn, m_xContext);
1882 } else { // fallback to url parameter
1883 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(
1884 URL, css::embed::ElementModes::READ, m_xContext);
1886 } catch (const css::uno::RuntimeException &) {
1887 throw;
1888 } catch (const css::io::IOException &) {
1889 throw;
1890 } catch (const css::uno::Exception &) {
1891 css::uno::Any anyEx = cppu::getCaughtException();
1892 throw css::lang::WrappedTargetException(
1893 "SfxDocumentMetaData::loadFromMedium: exception",
1894 css::uno::Reference<css::uno::XInterface>(*this),
1895 anyEx);
1897 if (!xStorage.is()) {
1898 throw css::uno::RuntimeException(
1899 "SfxDocumentMetaData::loadFromMedium: cannot get Storage",
1900 *this);
1902 loadFromStorage(xStorage, md.getAsConstPropertyValueList());
1905 void SAL_CALL
1906 SfxDocumentMetaData::storeToMedium(const OUString & URL,
1907 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1909 utl::MediaDescriptor md(Medium);
1910 if (!URL.isEmpty()) {
1911 md[ utl::MediaDescriptor::PROP_URL() ] <<= URL;
1913 SfxMedium aMedium(md.getAsConstPropertyValueList());
1914 css::uno::Reference<css::embed::XStorage> xStorage
1915 = aMedium.GetOutputStorage();
1918 if (!xStorage.is()) {
1919 throw css::uno::RuntimeException(
1920 "SfxDocumentMetaData::storeToMedium: cannot get Storage",
1921 *this);
1923 // set MIME type of the storage
1924 utl::MediaDescriptor::const_iterator iter
1925 = md.find(utl::MediaDescriptor::PROP_MEDIATYPE());
1926 if (iter != md.end()) {
1927 css::uno::Reference< css::beans::XPropertySet > xProps(xStorage,
1928 css::uno::UNO_QUERY_THROW);
1929 xProps->setPropertyValue(
1930 utl::MediaDescriptor::PROP_MEDIATYPE(),
1931 iter->second);
1933 storeToStorage(xStorage, md.getAsConstPropertyValueList());
1936 const bool bOk = aMedium.Commit();
1937 aMedium.Close();
1938 if ( !bOk ) {
1939 ErrCode nError = aMedium.GetError();
1940 if ( nError == ERRCODE_NONE ) {
1941 nError = ERRCODE_IO_GENERAL;
1944 throw css::task::ErrorCodeIOException(
1945 "SfxDocumentMetaData::storeToMedium <" + URL + "> Commit failed: " + nError.toHexString(),
1946 css::uno::Reference< css::uno::XInterface >(), sal_uInt32(nError));
1951 // css::lang::XInitialization:
1952 void SAL_CALL SfxDocumentMetaData::initialize( const css::uno::Sequence< css::uno::Any > & aArguments)
1954 // possible arguments:
1955 // - no argument: default initialization (empty DOM)
1956 // - 1 argument, XDocument: initialize with given DOM and empty base URL
1957 // NB: links in document must be absolute
1959 ::osl::MutexGuard g(m_aMutex);
1960 css::uno::Reference<css::xml::dom::XDocument> xDoc;
1962 for (sal_Int32 i = 0; i < aArguments.getLength(); ++i) {
1963 const css::uno::Any any = aArguments[i];
1964 if (!(any >>= xDoc)) {
1965 throw css::lang::IllegalArgumentException(
1966 "SfxDocumentMetaData::initialize: argument must be XDocument",
1967 *this, static_cast<sal_Int16>(i));
1969 if (!xDoc.is()) {
1970 throw css::lang::IllegalArgumentException(
1971 "SfxDocumentMetaData::initialize: argument is null",
1972 *this, static_cast<sal_Int16>(i));
1976 if (!xDoc.is()) {
1977 // For a new document, we create a new DOM tree here.
1978 xDoc = createDOM();
1981 init(xDoc);
1984 // css::util::XCloneable:
1985 css::uno::Reference<css::util::XCloneable> SAL_CALL
1986 SfxDocumentMetaData::createClone()
1988 ::osl::MutexGuard g(m_aMutex);
1989 checkInit();
1991 SfxDocumentMetaData *pNew = createMe(m_xContext);
1993 // NB: do not copy the modification listeners, only DOM
1994 css::uno::Reference<css::xml::dom::XDocument> xDoc = createDOM();
1995 try {
1996 updateUserDefinedAndAttributes();
1997 // deep copy of root node
1998 css::uno::Reference<css::xml::dom::XNode> xRoot(
1999 m_xDoc->getDocumentElement(), css::uno::UNO_QUERY_THROW);
2000 css::uno::Reference<css::xml::dom::XNode> xRootNew(
2001 xDoc->importNode(xRoot, true));
2002 xDoc->appendChild(xRootNew);
2003 pNew->init(xDoc);
2004 } catch (const css::uno::RuntimeException &) {
2005 throw;
2006 } catch (const css::uno::Exception &) {
2007 css::uno::Any anyEx = cppu::getCaughtException();
2008 throw css::lang::WrappedTargetRuntimeException(
2009 "SfxDocumentMetaData::createClone: exception",
2010 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
2012 return css::uno::Reference<css::util::XCloneable> (pNew);
2015 // css::util::XModifiable:
2016 sal_Bool SAL_CALL SfxDocumentMetaData::isModified( )
2018 ::osl::MutexGuard g(m_aMutex);
2019 checkInit();
2020 css::uno::Reference<css::util::XModifiable> xMB(m_xUserDefined,
2021 css::uno::UNO_QUERY);
2022 return m_isModified || (xMB.is() && xMB->isModified());
2025 void SAL_CALL SfxDocumentMetaData::setModified( sal_Bool bModified )
2027 css::uno::Reference<css::util::XModifiable> xMB;
2028 { // do not lock mutex while notifying (#i93514#) to prevent deadlock
2029 ::osl::MutexGuard g(m_aMutex);
2030 checkInit();
2031 m_isModified = bModified;
2032 if ( !bModified && m_xUserDefined.is() )
2034 xMB.set(m_xUserDefined, css::uno::UNO_QUERY);
2035 assert(xMB.is() &&
2036 "SfxDocumentMetaData::setModified: PropertyBag not Modifiable?");
2039 if (bModified) {
2040 try {
2041 css::uno::Reference<css::uno::XInterface> xThis(*this);
2042 css::lang::EventObject event(xThis);
2043 m_NotifyListeners.notifyEach(&css::util::XModifyListener::modified,
2044 event);
2045 } catch (const css::uno::RuntimeException &) {
2046 throw;
2047 } catch (const css::uno::Exception &) {
2048 // ignore
2049 TOOLS_WARN_EXCEPTION("sfx.doc", "setModified");
2051 } else {
2052 if (xMB.is()) {
2053 xMB->setModified(false);
2058 // css::util::XModifyBroadcaster:
2059 void SAL_CALL SfxDocumentMetaData::addModifyListener(
2060 const css::uno::Reference< css::util::XModifyListener > & xListener)
2062 ::osl::MutexGuard g(m_aMutex);
2063 checkInit();
2064 m_NotifyListeners.addInterface(xListener);
2065 css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
2066 css::uno::UNO_QUERY);
2067 if (xMB.is()) {
2068 xMB->addModifyListener(xListener);
2072 void SAL_CALL SfxDocumentMetaData::removeModifyListener(
2073 const css::uno::Reference< css::util::XModifyListener > & xListener)
2075 ::osl::MutexGuard g(m_aMutex);
2076 checkInit();
2077 m_NotifyListeners.removeInterface(xListener);
2078 css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
2079 css::uno::UNO_QUERY);
2080 if (xMB.is()) {
2081 xMB->removeModifyListener(xListener);
2085 // css::xml::sax::XSAXSerializable
2086 void SAL_CALL SfxDocumentMetaData::serialize(
2087 const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
2088 const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces)
2090 ::osl::MutexGuard g(m_aMutex);
2091 checkInit();
2092 updateUserDefinedAndAttributes();
2093 css::uno::Reference<css::xml::sax::XSAXSerializable> xSAXable(m_xDoc,
2094 css::uno::UNO_QUERY_THROW);
2095 xSAXable->serialize(i_xHandler, i_rNamespaces);
2098 void SfxDocumentMetaData::createUserDefined()
2100 // user-defined meta data: create PropertyBag which only accepts property
2101 // values of allowed types
2102 if ( m_xUserDefined.is() )
2103 return;
2105 css::uno::Sequence<css::uno::Type> types(13);
2106 types[ 0] = ::cppu::UnoType<bool>::get();
2107 types[ 1] = ::cppu::UnoType< OUString>::get();
2108 types[ 2] = ::cppu::UnoType<css::util::DateTime>::get();
2109 types[ 3] = ::cppu::UnoType<css::util::Date>::get();
2110 types[ 4] = ::cppu::UnoType<css::util::DateTimeWithTimezone>::get();
2111 types[ 5] = ::cppu::UnoType<css::util::DateWithTimezone>::get();
2112 types[ 6] = ::cppu::UnoType<css::util::Duration>::get();
2113 types[ 7] = ::cppu::UnoType<float>::get();
2114 types[ 8] = ::cppu::UnoType<double>::get();
2115 types[ 9] = ::cppu::UnoType<sal_Int16>::get();
2116 types[10] = ::cppu::UnoType<sal_Int32>::get();
2117 types[11] = ::cppu::UnoType<sal_Int64>::get();
2118 // Time is supported for backward compatibility with OOo 3.x, x<=2
2119 types[12] = ::cppu::UnoType<css::util::Time>::get();
2120 // #i94175#: ODF allows empty user-defined property names!
2121 m_xUserDefined.set(
2122 css::beans::PropertyBag::createWithTypes( m_xContext, types, true/*AllowEmptyPropertyName*/, false/*AutomaticAddition*/ ),
2123 css::uno::UNO_QUERY_THROW);
2125 const css::uno::Reference<css::util::XModifyBroadcaster> xMB(
2126 m_xUserDefined, css::uno::UNO_QUERY);
2127 if (xMB.is())
2129 const std::vector<css::uno::Reference<css::uno::XInterface> >
2130 listeners(m_NotifyListeners.getElements());
2131 for (const auto& l : listeners) {
2132 xMB->addModifyListener(
2133 css::uno::Reference< css::util::XModifyListener >(l, css::uno::UNO_QUERY) );
2138 } // closing anonymous implementation namespace
2140 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
2141 CompatWriterDocPropsImpl_get_implementation(
2142 css::uno::XComponentContext *context,
2143 css::uno::Sequence<css::uno::Any> const &)
2145 return cppu::acquire(new CompatWriterDocPropsImpl(context));
2148 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
2149 SfxDocumentMetaData_get_implementation(
2150 css::uno::XComponentContext *context,
2151 css::uno::Sequence<css::uno::Any> const &)
2153 return cppu::acquire(new SfxDocumentMetaData(context));
2156 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */