LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / doc / SfxDocumentMetaData.cxx
blob1abdb68d778dec05ea268ca7881ce2443a4d91b0
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/Writer.hpp>
50 #include <com/sun/star/xml/sax/Parser.hpp>
51 #include <com/sun/star/xml/sax/XFastParser.hpp>
52 #include <com/sun/star/xml/dom/DOMException.hpp>
53 #include <com/sun/star/xml/dom/XDocument.hpp>
54 #include <com/sun/star/xml/dom/XElement.hpp>
55 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
56 #include <com/sun/star/xml/dom/NodeType.hpp>
57 #include <com/sun/star/xml/xpath/XPathAPI.hpp>
58 #include <com/sun/star/util/Date.hpp>
59 #include <com/sun/star/util/Time.hpp>
60 #include <com/sun/star/util/DateWithTimezone.hpp>
61 #include <com/sun/star/util/DateTimeWithTimezone.hpp>
62 #include <com/sun/star/util/Duration.hpp>
64 #include <rtl/ustrbuf.hxx>
65 #include <tools/datetime.hxx>
66 #include <tools/diagnose_ex.h>
67 #include <osl/mutex.hxx>
68 #include <comphelper/fileformat.h>
69 #include <cppuhelper/basemutex.hxx>
70 #include <comphelper/interfacecontainer2.hxx>
71 #include <comphelper/storagehelper.hxx>
72 #include <unotools/mediadescriptor.hxx>
73 #include <comphelper/sequence.hxx>
74 #include <sot/storage.hxx>
75 #include <sfx2/docfile.hxx>
76 #include <sax/tools/converter.hxx>
77 #include <i18nlangtag/languagetag.hxx>
78 #include <optional>
80 #include <algorithm>
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 rtl::Reference<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 rtl::Reference<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 constexpr OUStringLiteral s_nsXLink = u"http://www.w3.org/1999/xlink";
409 constexpr OUStringLiteral s_nsDC = u"http://purl.org/dc/elements/1.1/";
410 constexpr OUStringLiteral s_nsODF = u"urn:oasis:names:tc:opendocument:xmlns:office:1.0";
411 constexpr OUStringLiteral s_nsODFMeta = u"urn:oasis:names:tc:opendocument:xmlns:meta:1.0";
412 // constexpr OUStringLiteral s_nsOOo = "http://openoffice.org/2004/office"; // not used (yet?)
414 constexpr OUStringLiteral s_meta = u"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) noexcept
441 assert(i_qname);
442 OUString 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.isEmpty());
449 return 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) noexcept
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) noexcept
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) noexcept
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) noexcept
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) noexcept
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 noexcept
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) noexcept
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) noexcept
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) noexcept
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 std::transform(vec.begin(), vec.end(), ret.getArray(),
729 [](const auto& node) { return getNodeText(node); });
730 return ret;
733 bool
734 SfxDocumentMetaData::setMetaList(const char* i_name,
735 const css::uno::Sequence<OUString> & i_rValue,
736 AttrVector const* i_pAttrs)
737 // throw (css::uno::RuntimeException)
739 checkInit();
740 assert((i_pAttrs == nullptr) ||
741 (static_cast<size_t>(i_rValue.getLength()) == i_pAttrs->size()));
743 try {
744 OUString name = OUString::createFromAscii(i_name);
745 assert(m_metaList.find(name) != m_metaList.end());
746 std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
747 m_metaList[name];
749 // if nothing changed, do nothing
750 // alas, this does not check for permutations, or attributes...
751 if (nullptr == i_pAttrs) {
752 if (static_cast<size_t>(i_rValue.getLength()) == vec.size()) {
753 bool isEqual(true);
754 for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
755 css::uno::Reference<css::xml::dom::XNode> xNode(vec.at(i));
756 if (xNode.is()) {
757 OUString val = getNodeText(xNode);
758 if (val != i_rValue[i]) {
759 isEqual = false;
760 break;
764 if (isEqual) return false;
768 // remove old meta data nodes
770 std::vector<css::uno::Reference<css::xml::dom::XNode> >
771 ::reverse_iterator it(vec.rbegin());
772 try {
773 for ( ;it != vec.rend(); ++it)
775 m_xParent->removeChild(*it);
778 catch (...)
780 // Clean up already removed nodes
781 vec.erase(it.base(), vec.end());
782 throw;
784 vec.clear();
787 // insert new meta data nodes into DOM tree
788 for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
789 css::uno::Reference<css::xml::dom::XElement> xElem(
790 m_xDoc->createElementNS(getNameSpace(i_name), name),
791 css::uno::UNO_SET_THROW);
792 css::uno::Reference<css::xml::dom::XNode> xNode(xElem,
793 css::uno::UNO_QUERY_THROW);
794 css::uno::Reference<css::xml::dom::XNode> xTextNode(
795 m_xDoc->createTextNode(i_rValue[i]), css::uno::UNO_QUERY_THROW);
796 // set attributes
797 if (i_pAttrs != nullptr) {
798 for (auto const& elem : (*i_pAttrs)[i])
800 xElem->setAttributeNS(getNameSpace(elem.first),
801 OUString::createFromAscii(elem.first),
802 elem.second);
805 xNode->appendChild(xTextNode);
806 m_xParent->appendChild(xNode);
807 vec.push_back(xNode);
810 return true;
811 } catch (const css::xml::dom::DOMException &) {
812 css::uno::Any anyEx = cppu::getCaughtException();
813 throw css::lang::WrappedTargetRuntimeException(
814 "SfxDocumentMetaData::setMetaList: DOM exception",
815 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
819 // convert property list to string list and attribute list
820 std::pair<css::uno::Sequence< OUString>, AttrVector>
821 propsToStrings(css::uno::Reference<css::beans::XPropertySet> const & i_xPropSet)
823 ::std::vector< OUString > values;
824 AttrVector attrs;
826 css::uno::Reference<css::beans::XPropertySetInfo> xSetInfo
827 = i_xPropSet->getPropertySetInfo();
828 css::uno::Sequence<css::beans::Property> props = xSetInfo->getProperties();
830 for (sal_Int32 i = 0; i < props.getLength(); ++i) {
831 if (props[i].Attributes & css::beans::PropertyAttribute::TRANSIENT) {
832 continue;
834 const OUString name = props[i].Name;
835 css::uno::Any any;
836 try {
837 any = i_xPropSet->getPropertyValue(name);
838 } catch (const css::uno::Exception &) {
839 // ignore
841 const css::uno::Type & type = any.getValueType();
842 std::vector<std::pair<const char*, OUString> > as;
843 as.emplace_back("meta:name", name);
844 const char* vt = "meta:value-type";
846 // convert according to type
847 if (type == ::cppu::UnoType<bool>::get()) {
848 bool b = false;
849 any >>= b;
850 OUStringBuffer buf;
851 ::sax::Converter::convertBool(buf, b);
852 values.push_back(buf.makeStringAndClear());
853 as.emplace_back(vt, OUString("boolean"));
854 } else if (type == ::cppu::UnoType< OUString>::get()) {
855 OUString s;
856 any >>= s;
857 values.push_back(s);
858 // #i90847# OOo 2.x does stupid things if value-type="string";
859 // fortunately string is default anyway, so we can just omit it
860 // #i107502#: however, OOo 2.x only reads 4 user-defined without @value-type
861 // => best backward compatibility: first 4 without @value-type, rest with
862 if (4 <= i)
864 as.emplace_back(vt, OUString("string"));
866 } else if (type == ::cppu::UnoType<css::util::DateTime>::get()) {
867 css::util::DateTime dt;
868 any >>= dt;
869 values.push_back(dateTimeToText(dt));
870 as.emplace_back(vt, OUString("date"));
871 } else if (type == ::cppu::UnoType<css::util::Date>::get()) {
872 css::util::Date d;
873 any >>= d;
874 values.push_back(dateToText(d, nullptr));
875 as.emplace_back(vt,OUString("date"));
876 } else if (type == ::cppu::UnoType<css::util::DateTimeWithTimezone>::get()) {
877 css::util::DateTimeWithTimezone dttz;
878 any >>= dttz;
879 values.push_back(dateTimeToText(dttz.DateTimeInTZ, &dttz.Timezone));
880 as.emplace_back(vt, OUString("date"));
881 } else if (type == ::cppu::UnoType<css::util::DateWithTimezone>::get()) {
882 css::util::DateWithTimezone dtz;
883 any >>= dtz;
884 values.push_back(dateToText(dtz.DateInTZ, &dtz.Timezone));
885 as.emplace_back(vt, OUString("date"));
886 } else if (type == ::cppu::UnoType<css::util::Time>::get()) {
887 // #i97029#: replaced by Duration
888 // Time is supported for backward compatibility with OOo 3.x, x<=2
889 css::util::Time ut;
890 any >>= ut;
891 css::util::Duration ud;
892 ud.Hours = ut.Hours;
893 ud.Minutes = ut.Minutes;
894 ud.Seconds = ut.Seconds;
895 ud.NanoSeconds = ut.NanoSeconds;
896 values.push_back(durationToText(ud));
897 as.emplace_back(vt, OUString("time"));
898 } else if (type == ::cppu::UnoType<css::util::Duration>::get()) {
899 css::util::Duration ud;
900 any >>= ud;
901 values.push_back(durationToText(ud));
902 as.emplace_back(vt, OUString("time"));
903 } else if (::cppu::UnoType<double>::get().isAssignableFrom(type)) {
904 // support not just double, but anything that can be converted
905 double d = 0;
906 any >>= d;
907 OUStringBuffer buf;
908 ::sax::Converter::convertDouble(buf, d);
909 values.push_back(buf.makeStringAndClear());
910 as.emplace_back(vt, OUString("float"));
911 } else {
912 SAL_WARN("sfx.doc", "Unsupported property type: " << any.getValueTypeName() );
913 continue;
915 attrs.push_back(as);
918 return std::make_pair(comphelper::containerToSequence(values), attrs);
921 // remove the given element from the DOM, and iff i_pAttrs != 0 insert new one
922 void
923 SfxDocumentMetaData::updateElement(const char *i_name,
924 std::vector<std::pair<const char *, OUString> >* i_pAttrs)
926 OUString name = OUString::createFromAscii(i_name);
927 try {
928 // remove old element
929 css::uno::Reference<css::xml::dom::XNode> xNode =
930 m_meta.find(name)->second;
931 if (xNode.is()) {
932 m_xParent->removeChild(xNode);
933 xNode.clear();
935 // add new element
936 if (nullptr != i_pAttrs) {
937 css::uno::Reference<css::xml::dom::XElement> xElem(
938 m_xDoc->createElementNS(getNameSpace(i_name), name),
939 css::uno::UNO_SET_THROW);
940 xNode.set(xElem, css::uno::UNO_QUERY_THROW);
941 // set attributes
942 for (auto const& elem : *i_pAttrs)
944 xElem->setAttributeNS(getNameSpace(elem.first),
945 OUString::createFromAscii(elem.first), elem.second);
947 m_xParent->appendChild(xNode);
949 m_meta[name] = xNode;
950 } catch (const css::xml::dom::DOMException &) {
951 css::uno::Any anyEx = cppu::getCaughtException();
952 throw css::lang::WrappedTargetRuntimeException(
953 "SfxDocumentMetaData::updateElement: DOM exception",
954 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
958 // update user-defined meta data in DOM tree
959 void SfxDocumentMetaData::updateUserDefinedAndAttributes()
961 createUserDefined();
962 const css::uno::Reference<css::beans::XPropertySet> xPSet(m_xUserDefined,
963 css::uno::UNO_QUERY_THROW);
964 const std::pair<css::uno::Sequence< OUString>, AttrVector>
965 udStringsAttrs( propsToStrings(xPSet) );
966 (void) setMetaList("meta:user-defined", udStringsAttrs.first,
967 &udStringsAttrs.second);
969 // update elements with attributes
970 std::vector<std::pair<const char *, OUString> > attributes;
971 if (!m_TemplateName.isEmpty() || !m_TemplateURL.isEmpty()
972 || isValidDateTime(m_TemplateDate)) {
973 attributes.emplace_back("xlink:type", OUString("simple"));
974 attributes.emplace_back("xlink:actuate", OUString("onRequest"));
975 attributes.emplace_back("xlink:title", m_TemplateName);
976 attributes.emplace_back("xlink:href", m_TemplateURL );
977 if (isValidDateTime(m_TemplateDate)) {
978 attributes.emplace_back(
979 "meta:date", dateTimeToText(m_TemplateDate));
981 updateElement("meta:template", &attributes);
982 } else {
983 updateElement("meta:template");
985 attributes.clear();
987 if (!m_AutoloadURL.isEmpty() || (0 != m_AutoloadSecs)) {
988 attributes.emplace_back("xlink:href", m_AutoloadURL );
989 attributes.emplace_back("meta:delay",
990 durationToText(m_AutoloadSecs));
991 updateElement("meta:auto-reload", &attributes);
992 } else {
993 updateElement("meta:auto-reload");
995 attributes.clear();
997 if (!m_DefaultTarget.isEmpty()) {
998 attributes.emplace_back(
999 "office:target-frame-name",
1000 m_DefaultTarget);
1001 // xlink:show: _blank -> new, any other value -> replace
1002 const char* show = m_DefaultTarget == "_blank" ? "new" : "replace";
1003 attributes.emplace_back(
1004 "xlink:show",
1005 OUString::createFromAscii(show));
1006 updateElement("meta:hyperlink-behaviour", &attributes);
1007 } else {
1008 updateElement("meta:hyperlink-behaviour");
1010 attributes.clear();
1013 // create empty DOM tree (XDocument)
1014 css::uno::Reference<css::xml::dom::XDocument>
1015 SfxDocumentMetaData::createDOM() const // throw (css::uno::RuntimeException)
1017 css::uno::Reference<css::xml::dom::XDocumentBuilder> xBuilder( css::xml::dom::DocumentBuilder::create(m_xContext) );
1018 css::uno::Reference<css::xml::dom::XDocument> xDoc = xBuilder->newDocument();
1019 if (!xDoc.is())
1020 throw css::uno::RuntimeException(
1021 "SfxDocumentMetaData::createDOM: cannot create new document",
1022 *const_cast<SfxDocumentMetaData*>(this));
1023 return xDoc;
1026 void
1027 SfxDocumentMetaData::checkInit() const // throw (css::uno::RuntimeException)
1029 if (!m_isInitialized) {
1030 throw css::uno::RuntimeException(
1031 "SfxDocumentMetaData::checkInit: not initialized",
1032 *const_cast<SfxDocumentMetaData*>(this));
1034 assert(m_xDoc.is() && m_xParent.is());
1037 // initialize state from DOM tree
1038 void SfxDocumentMetaData::init(
1039 const css::uno::Reference<css::xml::dom::XDocument>& i_xDoc)
1041 if (!i_xDoc.is())
1042 throw css::uno::RuntimeException("SfxDocumentMetaData::init: no DOM tree given", *this);
1044 css::uno::Reference<css::xml::xpath::XXPathAPI> xPath = css::xml::xpath::XPathAPI::create(m_xContext);
1046 m_isInitialized = false;
1047 m_xDoc = i_xDoc;
1049 // select nodes for standard meta data stuff
1050 xPath->registerNS("xlink", s_nsXLink);
1051 xPath->registerNS("dc", s_nsDC);
1052 xPath->registerNS("office", s_nsODF);
1053 xPath->registerNS("meta", s_nsODFMeta);
1054 // NB: we do not handle the single-XML-file ODF variant, which would
1055 // have the root element office:document.
1056 // The root of such documents must be converted in the importer!
1057 css::uno::Reference<css::xml::dom::XNode> xDocNode(
1058 m_xDoc, css::uno::UNO_QUERY_THROW);
1059 m_xParent.clear();
1060 try {
1061 m_xParent = xPath->selectSingleNode(xDocNode, "/child::office:document-meta/child::office:meta");
1062 } catch (const css::uno::Exception &) {
1065 if (!m_xParent.is()) {
1066 // all this create/append stuff may throw DOMException
1067 try {
1068 css::uno::Reference<css::xml::dom::XElement> xRElem;
1069 css::uno::Reference<css::xml::dom::XNode> xNode(
1070 i_xDoc->getFirstChild());
1071 while (xNode.is()) {
1072 if (css::xml::dom::NodeType_ELEMENT_NODE ==xNode->getNodeType())
1074 if ( xNode->getNamespaceURI() == s_nsODF && xNode->getLocalName() == "document-meta" )
1076 xRElem.set(xNode, css::uno::UNO_QUERY_THROW);
1077 break;
1079 else
1081 SAL_INFO("sfx.doc", "SfxDocumentMetaData::init(): "
1082 "deleting unexpected root element: "
1083 << xNode->getLocalName());
1084 i_xDoc->removeChild(xNode);
1085 xNode = i_xDoc->getFirstChild(); // start over
1087 } else {
1088 xNode = xNode->getNextSibling();
1091 if (!xRElem.is()) {
1092 xRElem = i_xDoc->createElementNS(
1093 s_nsODF, "office:document-meta");
1094 css::uno::Reference<css::xml::dom::XNode> xRNode(xRElem,
1095 css::uno::UNO_QUERY_THROW);
1096 i_xDoc->appendChild(xRNode);
1098 xRElem->setAttributeNS(s_nsODF, "office:version", "1.0");
1099 // does not exist, otherwise m_xParent would not be null
1100 css::uno::Reference<css::xml::dom::XNode> xParent (
1101 i_xDoc->createElementNS(s_nsODF, "office:meta"),
1102 css::uno::UNO_QUERY_THROW);
1103 xRElem->appendChild(xParent);
1104 m_xParent = xParent;
1105 } catch (const css::xml::dom::DOMException &) {
1106 css::uno::Any anyEx = cppu::getCaughtException();
1107 throw css::lang::WrappedTargetRuntimeException(
1108 "SfxDocumentMetaData::init: DOM exception",
1109 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
1114 // select nodes for elements of which we only handle one occurrence
1115 for (const char **pName = s_stdMeta; *pName != nullptr; ++pName) {
1116 OUString name = OUString::createFromAscii(*pName);
1117 // NB: If a document contains more than one occurrence of a
1118 // meta-data element, we arbitrarily pick one of them here.
1119 // We do not remove the others, i.e., when we write the
1120 // document, it will contain the duplicates unchanged.
1121 // The ODF spec says that handling multiple occurrences is
1122 // application-specific.
1123 css::uno::Reference<css::xml::dom::XNode> xNode =
1124 xPath->selectSingleNode(m_xParent, "child::" + name);
1125 // Do not create an empty element if it is missing;
1126 // for certain elements, such as dateTime, this would be invalid
1127 m_meta[name] = xNode;
1130 // select nodes for elements of which we handle all occurrences
1131 for (const char **pName = s_stdMetaList; *pName != nullptr; ++pName) {
1132 OUString name = OUString::createFromAscii(*pName);
1133 css::uno::Reference<css::xml::dom::XNodeList> nodes =
1134 xPath->selectNodeList(m_xParent, "child::" + name);
1135 std::vector<css::uno::Reference<css::xml::dom::XNode> > v;
1136 v.reserve(nodes->getLength());
1137 for (sal_Int32 i = 0; i < nodes->getLength(); ++i)
1139 v.push_back(nodes->item(i));
1141 m_metaList[name] = v;
1144 // initialize members corresponding to attributes from DOM nodes
1145 m_TemplateName = getMetaAttr("meta:template", "xlink:title");
1146 m_TemplateURL = getMetaAttr("meta:template", "xlink:href");
1147 m_TemplateDate =
1148 textToDateTimeDefault(getMetaAttr("meta:template", "meta:date"));
1149 m_AutoloadURL = getMetaAttr("meta:auto-reload", "xlink:href");
1150 m_AutoloadSecs =
1151 textToDuration(getMetaAttr("meta:auto-reload", "meta:delay"));
1152 m_DefaultTarget =
1153 getMetaAttr("meta:hyperlink-behaviour", "office:target-frame-name");
1156 std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
1157 m_metaList[OUString("meta:user-defined")];
1158 m_xUserDefined.clear(); // #i105826#: reset (may be re-initialization)
1159 if ( !vec.empty() )
1161 createUserDefined();
1164 // user-defined meta data: initialize PropertySet from DOM nodes
1165 for (auto const& elem : vec)
1167 css::uno::Reference<css::xml::dom::XElement> xElem(elem,
1168 css::uno::UNO_QUERY_THROW);
1169 css::uno::Any any;
1170 OUString name = xElem->getAttributeNS(s_nsODFMeta, "name");
1171 OUString type = xElem->getAttributeNS(s_nsODFMeta, "value-type");
1172 OUString text = getNodeText(elem);
1173 if ( type == "float" ) {
1174 double d;
1175 if (::sax::Converter::convertDouble(d, text)) {
1176 any <<= d;
1177 } else {
1178 SAL_WARN("sfx.doc", "Invalid float: " << text);
1179 continue;
1181 } else if ( type == "date" ) {
1182 bool isDateTime;
1183 css::util::Date d;
1184 css::util::DateTime dt;
1185 std::optional<sal_Int16> nTimeZone;
1186 if (textToDateOrDateTime(d, dt, isDateTime, nTimeZone, text)) {
1187 if (isDateTime) {
1188 if (nTimeZone) {
1189 any <<= css::util::DateTimeWithTimezone(dt,
1190 *nTimeZone);
1191 } else {
1192 any <<= dt;
1194 } else {
1195 if (nTimeZone) {
1196 any <<= css::util::DateWithTimezone(d, *nTimeZone);
1197 } else {
1198 any <<= d;
1201 } else {
1202 SAL_WARN("sfx.doc", "Invalid date: " << text);
1203 continue;
1205 } else if ( type == "time" ) {
1206 css::util::Duration ud;
1207 if (textToDuration(ud, text)) {
1208 any <<= ud;
1209 } else {
1210 SAL_WARN("sfx.doc", "Invalid time: " << text);
1211 continue;
1213 } else if ( type == "boolean" ) {
1214 bool b;
1215 if (::sax::Converter::convertBool(b, text)) {
1216 any <<= b;
1217 } else {
1218 SAL_WARN("sfx.doc", "Invalid boolean: " << text);
1219 continue;
1221 } else { // default
1222 any <<= text;
1224 try {
1225 m_xUserDefined->addProperty(name,
1226 css::beans::PropertyAttribute::REMOVABLE, any);
1227 } catch (const css::beans::PropertyExistException &) {
1228 SAL_WARN("sfx.doc", "Duplicate: " << name);
1229 // ignore; duplicate
1230 } catch (const css::beans::IllegalTypeException &) {
1231 SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal type: " << name);
1232 } catch (const css::lang::IllegalArgumentException &) {
1233 SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal arg: " << name);
1237 m_isModified = false;
1238 m_isInitialized = true;
1242 SfxDocumentMetaData::SfxDocumentMetaData(
1243 css::uno::Reference< css::uno::XComponentContext > const & context)
1244 : BaseMutex()
1245 , SfxDocumentMetaData_Base(m_aMutex)
1246 , m_xContext(context)
1247 , m_NotifyListeners(m_aMutex)
1248 , m_isInitialized(false)
1249 , m_isModified(false)
1250 , m_AutoloadSecs(0)
1252 assert(context.is());
1253 assert(context->getServiceManager().is());
1254 init(createDOM());
1257 // com.sun.star.uno.XServiceInfo:
1258 OUString SAL_CALL
1259 SfxDocumentMetaData::getImplementationName()
1261 return "SfxDocumentMetaData";
1264 sal_Bool SAL_CALL
1265 SfxDocumentMetaData::supportsService(OUString const & serviceName)
1267 return cppu::supportsService(this, serviceName);
1270 css::uno::Sequence< OUString > SAL_CALL
1271 SfxDocumentMetaData::getSupportedServiceNames()
1273 css::uno::Sequence< OUString > s { "com.sun.star.document.DocumentProperties" };
1274 return s;
1278 // css::lang::XComponent:
1279 void SAL_CALL SfxDocumentMetaData::dispose()
1281 ::osl::MutexGuard g(m_aMutex);
1282 if (!m_isInitialized) {
1283 return;
1285 WeakComponentImplHelperBase::dispose(); // superclass
1286 m_NotifyListeners.disposeAndClear(css::lang::EventObject(
1287 static_cast< ::cppu::OWeakObject* >(this)));
1288 m_isInitialized = false;
1289 m_meta.clear();
1290 m_metaList.clear();
1291 m_xParent.clear();
1292 m_xDoc.clear();
1293 m_xUserDefined.clear();
1297 // css::document::XDocumentProperties:
1298 OUString SAL_CALL
1299 SfxDocumentMetaData::getAuthor()
1301 ::osl::MutexGuard g(m_aMutex);
1302 return getMetaText("meta:initial-creator");
1305 void SAL_CALL SfxDocumentMetaData::setAuthor(const OUString & the_value)
1307 setMetaTextAndNotify("meta:initial-creator", the_value);
1311 OUString SAL_CALL
1312 SfxDocumentMetaData::getGenerator()
1314 ::osl::MutexGuard g(m_aMutex);
1315 return getMetaText("meta:generator");
1318 void SAL_CALL
1319 SfxDocumentMetaData::setGenerator(const OUString & the_value)
1321 setMetaTextAndNotify("meta:generator", the_value);
1324 css::util::DateTime SAL_CALL
1325 SfxDocumentMetaData::getCreationDate()
1327 ::osl::MutexGuard g(m_aMutex);
1328 return textToDateTimeDefault(getMetaText("meta:creation-date"));
1331 void SAL_CALL
1332 SfxDocumentMetaData::setCreationDate(const css::util::DateTime & the_value)
1334 setMetaTextAndNotify("meta:creation-date", dateTimeToText(the_value));
1337 OUString SAL_CALL
1338 SfxDocumentMetaData::getTitle()
1340 ::osl::MutexGuard g(m_aMutex);
1341 return getMetaText("dc:title");
1344 void SAL_CALL SfxDocumentMetaData::setTitle(const OUString & the_value)
1346 setMetaTextAndNotify("dc:title", the_value);
1349 OUString SAL_CALL
1350 SfxDocumentMetaData::getSubject()
1352 ::osl::MutexGuard g(m_aMutex);
1353 return getMetaText("dc:subject");
1356 void SAL_CALL
1357 SfxDocumentMetaData::setSubject(const OUString & the_value)
1359 setMetaTextAndNotify("dc:subject", the_value);
1362 OUString SAL_CALL
1363 SfxDocumentMetaData::getDescription()
1365 ::osl::MutexGuard g(m_aMutex);
1366 return getMetaText("dc:description");
1369 void SAL_CALL
1370 SfxDocumentMetaData::setDescription(const OUString & the_value)
1372 setMetaTextAndNotify("dc:description", the_value);
1375 css::uno::Sequence< OUString >
1376 SAL_CALL SfxDocumentMetaData::getKeywords()
1378 ::osl::MutexGuard g(m_aMutex);
1379 return getMetaList("meta:keyword");
1382 void SAL_CALL
1383 SfxDocumentMetaData::setKeywords(
1384 const css::uno::Sequence< OUString > & the_value)
1386 ::osl::ClearableMutexGuard g(m_aMutex);
1387 if (setMetaList("meta:keyword", the_value, nullptr)) {
1388 g.clear();
1389 setModified(true);
1393 css::lang::Locale SAL_CALL
1394 SfxDocumentMetaData::getLanguage()
1396 ::osl::MutexGuard g(m_aMutex);
1397 css::lang::Locale loc( LanguageTag::convertToLocale( getMetaText("dc:language"), false));
1398 return loc;
1401 void SAL_CALL
1402 SfxDocumentMetaData::setLanguage(const css::lang::Locale & the_value)
1404 OUString text( LanguageTag::convertToBcp47( the_value, false));
1405 setMetaTextAndNotify("dc:language", text);
1408 OUString SAL_CALL
1409 SfxDocumentMetaData::getModifiedBy()
1411 ::osl::MutexGuard g(m_aMutex);
1412 return getMetaText("dc:creator");
1415 void SAL_CALL
1416 SfxDocumentMetaData::setModifiedBy(const OUString & the_value)
1418 setMetaTextAndNotify("dc:creator", the_value);
1421 css::util::DateTime SAL_CALL
1422 SfxDocumentMetaData::getModificationDate()
1424 ::osl::MutexGuard g(m_aMutex);
1425 return textToDateTimeDefault(getMetaText("dc:date"));
1428 void SAL_CALL
1429 SfxDocumentMetaData::setModificationDate(const css::util::DateTime & the_value)
1431 setMetaTextAndNotify("dc:date", dateTimeToText(the_value));
1434 OUString SAL_CALL
1435 SfxDocumentMetaData::getPrintedBy()
1437 ::osl::MutexGuard g(m_aMutex);
1438 return getMetaText("meta:printed-by");
1441 void SAL_CALL
1442 SfxDocumentMetaData::setPrintedBy(const OUString & the_value)
1444 setMetaTextAndNotify("meta:printed-by", the_value);
1447 css::util::DateTime SAL_CALL
1448 SfxDocumentMetaData::getPrintDate()
1450 ::osl::MutexGuard g(m_aMutex);
1451 return textToDateTimeDefault(getMetaText("meta:print-date"));
1454 void SAL_CALL
1455 SfxDocumentMetaData::setPrintDate(const css::util::DateTime & the_value)
1457 setMetaTextAndNotify("meta:print-date", dateTimeToText(the_value));
1460 OUString SAL_CALL
1461 SfxDocumentMetaData::getTemplateName()
1463 ::osl::MutexGuard g(m_aMutex);
1464 checkInit();
1465 return m_TemplateName;
1468 void SAL_CALL
1469 SfxDocumentMetaData::setTemplateName(const OUString & the_value)
1471 ::osl::ClearableMutexGuard g(m_aMutex);
1472 checkInit();
1473 if (m_TemplateName != the_value) {
1474 m_TemplateName = the_value;
1475 g.clear();
1476 setModified(true);
1480 OUString SAL_CALL
1481 SfxDocumentMetaData::getTemplateURL()
1483 ::osl::MutexGuard g(m_aMutex);
1484 checkInit();
1485 return m_TemplateURL;
1488 void SAL_CALL
1489 SfxDocumentMetaData::setTemplateURL(const OUString & the_value)
1491 ::osl::ClearableMutexGuard g(m_aMutex);
1492 checkInit();
1493 if (m_TemplateURL != the_value) {
1494 m_TemplateURL = the_value;
1495 g.clear();
1496 setModified(true);
1500 css::util::DateTime SAL_CALL
1501 SfxDocumentMetaData::getTemplateDate()
1503 ::osl::MutexGuard g(m_aMutex);
1504 checkInit();
1505 return m_TemplateDate;
1508 void SAL_CALL
1509 SfxDocumentMetaData::setTemplateDate(const css::util::DateTime & the_value)
1511 ::osl::ClearableMutexGuard g(m_aMutex);
1512 checkInit();
1513 if (m_TemplateDate != the_value) {
1514 m_TemplateDate = the_value;
1515 g.clear();
1516 setModified(true);
1520 OUString SAL_CALL
1521 SfxDocumentMetaData::getAutoloadURL()
1523 ::osl::MutexGuard g(m_aMutex);
1524 checkInit();
1525 return m_AutoloadURL;
1528 void SAL_CALL
1529 SfxDocumentMetaData::setAutoloadURL(const OUString & the_value)
1531 ::osl::ClearableMutexGuard g(m_aMutex);
1532 checkInit();
1533 if (m_AutoloadURL != the_value) {
1534 m_AutoloadURL = the_value;
1535 g.clear();
1536 setModified(true);
1540 ::sal_Int32 SAL_CALL
1541 SfxDocumentMetaData::getAutoloadSecs()
1543 ::osl::MutexGuard g(m_aMutex);
1544 checkInit();
1545 return m_AutoloadSecs;
1548 void SAL_CALL
1549 SfxDocumentMetaData::setAutoloadSecs(::sal_Int32 the_value)
1551 if (the_value < 0)
1552 throw css::lang::IllegalArgumentException(
1553 "SfxDocumentMetaData::setAutoloadSecs: argument is negative",
1554 *this, 0);
1555 ::osl::ClearableMutexGuard g(m_aMutex);
1556 checkInit();
1557 if (m_AutoloadSecs != the_value) {
1558 m_AutoloadSecs = the_value;
1559 g.clear();
1560 setModified(true);
1564 OUString SAL_CALL
1565 SfxDocumentMetaData::getDefaultTarget()
1567 ::osl::MutexGuard g(m_aMutex);
1568 checkInit();
1569 return m_DefaultTarget;
1572 void SAL_CALL
1573 SfxDocumentMetaData::setDefaultTarget(const OUString & the_value)
1575 ::osl::ClearableMutexGuard g(m_aMutex);
1576 checkInit();
1577 if (m_DefaultTarget != the_value) {
1578 m_DefaultTarget = the_value;
1579 g.clear();
1580 setModified(true);
1584 css::uno::Sequence< css::beans::NamedValue > SAL_CALL
1585 SfxDocumentMetaData::getDocumentStatistics()
1587 ::osl::MutexGuard g(m_aMutex);
1588 checkInit();
1589 ::std::vector<css::beans::NamedValue> stats;
1590 for (size_t i = 0; s_stdStats[i] != nullptr; ++i) {
1591 const char * aName = s_stdStatAttrs[i];
1592 OUString text = getMetaAttr("meta:document-statistic", aName);
1593 if (text.isEmpty()) continue;
1594 css::beans::NamedValue stat;
1595 stat.Name = OUString::createFromAscii(s_stdStats[i]);
1596 sal_Int32 val;
1597 css::uno::Any any;
1598 if (!::sax::Converter::convertNumber(val, text, 0) || (val < 0)) {
1599 val = 0;
1600 SAL_WARN("sfx.doc", "Invalid number: " << text);
1602 any <<= val;
1603 stat.Value = any;
1604 stats.push_back(stat);
1607 return ::comphelper::containerToSequence(stats);
1610 void SAL_CALL
1611 SfxDocumentMetaData::setDocumentStatistics(
1612 const css::uno::Sequence< css::beans::NamedValue > & the_value)
1615 osl::MutexGuard g(m_aMutex);
1616 checkInit();
1617 std::vector<std::pair<const char *, OUString> > attributes;
1618 for (const auto& rValue : the_value) {
1619 const OUString name = rValue.Name;
1620 // inefficiently search for matching attribute
1621 for (size_t j = 0; s_stdStats[j] != nullptr; ++j) {
1622 if (name.equalsAscii(s_stdStats[j])) {
1623 const css::uno::Any any = rValue.Value;
1624 sal_Int32 val = 0;
1625 if (any >>= val) {
1626 attributes.emplace_back(s_stdStatAttrs[j],
1627 OUString::number(val));
1629 else {
1630 SAL_WARN("sfx.doc", "Invalid statistic: " << name);
1632 break;
1636 updateElement("meta:document-statistic", &attributes);
1638 setModified(true);
1641 ::sal_Int16 SAL_CALL
1642 SfxDocumentMetaData::getEditingCycles()
1644 ::osl::MutexGuard g(m_aMutex);
1645 OUString text = getMetaText("meta:editing-cycles");
1646 sal_Int32 ret;
1647 if (::sax::Converter::convertNumber(ret, text,
1648 0, std::numeric_limits<sal_Int16>::max())) {
1649 return static_cast<sal_Int16>(ret);
1650 } else {
1651 return 0;
1655 void SAL_CALL
1656 SfxDocumentMetaData::setEditingCycles(::sal_Int16 the_value)
1658 if (the_value < 0)
1659 throw css::lang::IllegalArgumentException(
1660 "SfxDocumentMetaData::setEditingCycles: argument is negative",
1661 *this, 0);
1662 setMetaTextAndNotify("meta:editing-cycles", OUString::number(the_value));
1665 ::sal_Int32 SAL_CALL
1666 SfxDocumentMetaData::getEditingDuration()
1668 ::osl::MutexGuard g(m_aMutex);
1669 return textToDuration(getMetaText("meta:editing-duration"));
1672 void SAL_CALL
1673 SfxDocumentMetaData::setEditingDuration(::sal_Int32 the_value)
1675 if (the_value < 0)
1676 throw css::lang::IllegalArgumentException(
1677 "SfxDocumentMetaData::setEditingDuration: argument is negative",
1678 *this, 0);
1679 setMetaTextAndNotify("meta:editing-duration", durationToText(the_value));
1682 void SAL_CALL
1683 SfxDocumentMetaData::resetUserData(const OUString & the_value)
1685 ::osl::ClearableMutexGuard g(m_aMutex);
1687 bool bModified( false );
1688 bModified |= setMetaText("meta:initial-creator", the_value);
1689 ::DateTime now( ::DateTime::SYSTEM );
1690 css::util::DateTime uDT(now.GetUNODateTime());
1691 bModified |= setMetaText("meta:creation-date", dateTimeToText(uDT));
1692 bModified |= setMetaText("dc:creator", OUString());
1693 bModified |= setMetaText("meta:printed-by", OUString());
1694 bModified |= setMetaText("dc:date", dateTimeToText(css::util::DateTime()));
1695 bModified |= setMetaText("meta:print-date",
1696 dateTimeToText(css::util::DateTime()));
1697 bModified |= setMetaText("meta:editing-duration", durationToText(0));
1698 bModified |= setMetaText("meta:editing-cycles",
1699 "1");
1701 if (bModified) {
1702 g.clear();
1703 setModified(true);
1708 css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
1709 SfxDocumentMetaData::getUserDefinedProperties()
1711 ::osl::MutexGuard g(m_aMutex);
1712 checkInit();
1713 createUserDefined();
1714 return m_xUserDefined;
1718 void SAL_CALL
1719 SfxDocumentMetaData::loadFromStorage(
1720 const css::uno::Reference< css::embed::XStorage > & xStorage,
1721 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1723 if (!xStorage.is())
1724 throw css::lang::IllegalArgumentException("SfxDocumentMetaData::loadFromStorage: argument is null", *this, 0);
1725 ::osl::MutexGuard g(m_aMutex);
1727 // open meta data file
1728 css::uno::Reference<css::io::XStream> xStream(
1729 xStorage->openStreamElement(
1730 s_meta,
1731 css::embed::ElementModes::READ) );
1732 if (!xStream.is()) throw css::uno::RuntimeException();
1733 css::uno::Reference<css::io::XInputStream> xInStream =
1734 xStream->getInputStream();
1735 if (!xInStream.is()) throw css::uno::RuntimeException();
1737 // create DOM parser service
1738 css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
1739 m_xContext->getServiceManager());
1740 css::xml::sax::InputSource input;
1741 input.aInputStream = xInStream;
1743 sal_uInt64 version = SotStorage::GetVersion( xStorage );
1744 // Oasis is also the default (0)
1745 bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
1746 const char *pServiceName = bOasis
1747 ? "com.sun.star.document.XMLOasisMetaImporter"
1748 : "com.sun.star.document.XMLMetaImporter";
1750 // set base URL
1751 css::uno::Reference<css::beans::XPropertySet> xPropArg =
1752 getURLProperties(Medium);
1753 try {
1754 xPropArg->getPropertyValue("BaseURI")
1755 >>= input.sSystemId;
1756 input.sSystemId += OUString::Concat("/") + s_meta;
1757 } catch (const css::uno::Exception &) {
1758 input.sSystemId = s_meta;
1760 css::uno::Sequence< css::uno::Any > args{ css::uno::Any(xPropArg) };
1762 // the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler
1763 css::uno::Reference<XInterface> xFilter =
1764 xMsf->createInstanceWithArgumentsAndContext(
1765 OUString::createFromAscii(pServiceName), args, m_xContext);
1766 assert(xFilter);
1767 css::uno::Reference<css::xml::sax::XFastParser> xFastParser(xFilter, css::uno::UNO_QUERY);
1768 css::uno::Reference<css::document::XImporter> xImp(xFilter, css::uno::UNO_QUERY_THROW);
1769 xImp->setTargetDocument(css::uno::Reference<css::lang::XComponent>(this));
1770 try {
1771 if (xFastParser)
1772 xFastParser->parseStream(input);
1773 else
1775 css::uno::Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, css::uno::UNO_QUERY_THROW);
1776 css::uno::Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(m_xContext);
1777 xParser->setDocumentHandler(xDocHandler);
1778 xParser->parseStream(input);
1780 } catch (const css::xml::sax::SAXException &) {
1781 throw css::io::WrongFormatException(
1782 "SfxDocumentMetaData::loadFromStorage:"
1783 " XML parsing exception", *this);
1785 // NB: the implementation of XMLOasisMetaImporter calls initialize
1786 checkInit();
1789 void SAL_CALL
1790 SfxDocumentMetaData::storeToStorage(
1791 const css::uno::Reference< css::embed::XStorage > & xStorage,
1792 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1794 if (!xStorage.is())
1795 throw css::lang::IllegalArgumentException(
1796 "SfxDocumentMetaData::storeToStorage: argument is null", *this, 0);
1797 ::osl::MutexGuard g(m_aMutex);
1798 checkInit();
1800 // update user-defined meta data in DOM tree
1801 // updateUserDefinedAndAttributes(); // this will be done in serialize!
1803 // write into storage
1804 css::uno::Reference<css::io::XStream> xStream =
1805 xStorage->openStreamElement(s_meta,
1806 css::embed::ElementModes::WRITE
1807 | css::embed::ElementModes::TRUNCATE);
1808 if (!xStream.is()) throw css::uno::RuntimeException();
1809 css::uno::Reference< css::beans::XPropertySet > xStreamProps(xStream,
1810 css::uno::UNO_QUERY_THROW);
1811 xStreamProps->setPropertyValue(
1812 "MediaType",
1813 css::uno::makeAny(OUString("text/xml")));
1814 xStreamProps->setPropertyValue(
1815 "Compressed",
1816 css::uno::makeAny(false));
1817 xStreamProps->setPropertyValue(
1818 "UseCommonStoragePasswordEncryption",
1819 css::uno::makeAny(false));
1820 css::uno::Reference<css::io::XOutputStream> xOutStream =
1821 xStream->getOutputStream();
1822 if (!xOutStream.is()) throw css::uno::RuntimeException();
1823 css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
1824 m_xContext->getServiceManager());
1825 css::uno::Reference<css::xml::sax::XWriter> xSaxWriter(
1826 css::xml::sax::Writer::create(m_xContext));
1827 xSaxWriter->setOutputStream(xOutStream);
1829 const sal_uInt64 version = SotStorage::GetVersion( xStorage );
1830 // Oasis is also the default (0)
1831 const bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
1832 const char *pServiceName = bOasis
1833 ? "com.sun.star.document.XMLOasisMetaExporter"
1834 : "com.sun.star.document.XMLMetaExporter";
1836 // set base URL
1837 css::uno::Reference<css::beans::XPropertySet> xPropArg =
1838 getURLProperties(Medium);
1839 css::uno::Sequence< css::uno::Any > args{ css::uno::Any(xSaxWriter), css::uno::Any(xPropArg) };
1841 css::uno::Reference<css::document::XExporter> xExp(
1842 xMsf->createInstanceWithArgumentsAndContext(
1843 OUString::createFromAscii(pServiceName), args, m_xContext),
1844 css::uno::UNO_QUERY_THROW);
1845 xExp->setSourceDocument(css::uno::Reference<css::lang::XComponent>(this));
1846 css::uno::Reference<css::document::XFilter> xFilter(xExp,
1847 css::uno::UNO_QUERY_THROW);
1848 if (!xFilter->filter(css::uno::Sequence< css::beans::PropertyValue >())) {
1849 throw css::io::IOException(
1850 "SfxDocumentMetaData::storeToStorage: cannot filter", *this);
1852 css::uno::Reference<css::embed::XTransactedObject> xTransaction(
1853 xStorage, css::uno::UNO_QUERY);
1854 if (xTransaction.is()) {
1855 xTransaction->commit();
1859 void SAL_CALL
1860 SfxDocumentMetaData::loadFromMedium(const OUString & URL,
1861 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1863 css::uno::Reference<css::io::XInputStream> xIn;
1864 utl::MediaDescriptor md(Medium);
1865 // if we have a URL parameter, it replaces the one in the media descriptor
1866 if (!URL.isEmpty()) {
1867 md[ utl::MediaDescriptor::PROP_URL ] <<= URL;
1868 md[ utl::MediaDescriptor::PROP_READONLY ] <<= true;
1870 if (md.addInputStream()) {
1871 md[ utl::MediaDescriptor::PROP_INPUTSTREAM ] >>= xIn;
1873 css::uno::Reference<css::embed::XStorage> xStorage;
1874 try {
1875 if (xIn.is()) {
1876 xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
1877 xIn, m_xContext);
1878 } else { // fallback to url parameter
1879 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(
1880 URL, css::embed::ElementModes::READ, m_xContext);
1882 } catch (const css::uno::RuntimeException &) {
1883 throw;
1884 } catch (const css::io::IOException &) {
1885 throw;
1886 } catch (const css::uno::Exception &) {
1887 css::uno::Any anyEx = cppu::getCaughtException();
1888 throw css::lang::WrappedTargetException(
1889 "SfxDocumentMetaData::loadFromMedium: exception",
1890 css::uno::Reference<css::uno::XInterface>(*this),
1891 anyEx);
1893 if (!xStorage.is()) {
1894 throw css::uno::RuntimeException(
1895 "SfxDocumentMetaData::loadFromMedium: cannot get Storage",
1896 *this);
1898 loadFromStorage(xStorage, md.getAsConstPropertyValueList());
1901 void SAL_CALL
1902 SfxDocumentMetaData::storeToMedium(const OUString & URL,
1903 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1905 utl::MediaDescriptor md(Medium);
1906 if (!URL.isEmpty()) {
1907 md[ utl::MediaDescriptor::PROP_URL ] <<= URL;
1909 SfxMedium aMedium(md.getAsConstPropertyValueList());
1910 css::uno::Reference<css::embed::XStorage> xStorage
1911 = aMedium.GetOutputStorage();
1914 if (!xStorage.is()) {
1915 throw css::uno::RuntimeException(
1916 "SfxDocumentMetaData::storeToMedium: cannot get Storage",
1917 *this);
1919 // set MIME type of the storage
1920 utl::MediaDescriptor::const_iterator iter
1921 = md.find(utl::MediaDescriptor::PROP_MEDIATYPE);
1922 if (iter != md.end()) {
1923 css::uno::Reference< css::beans::XPropertySet > xProps(xStorage,
1924 css::uno::UNO_QUERY_THROW);
1925 xProps->setPropertyValue(
1926 utl::MediaDescriptor::PROP_MEDIATYPE,
1927 iter->second);
1929 storeToStorage(xStorage, md.getAsConstPropertyValueList());
1932 const bool bOk = aMedium.Commit();
1933 aMedium.Close();
1934 if ( !bOk ) {
1935 ErrCode nError = aMedium.GetError();
1936 if ( nError == ERRCODE_NONE ) {
1937 nError = ERRCODE_IO_GENERAL;
1940 throw css::task::ErrorCodeIOException(
1941 "SfxDocumentMetaData::storeToMedium <" + URL + "> Commit failed: " + nError.toHexString(),
1942 css::uno::Reference< css::uno::XInterface >(), sal_uInt32(nError));
1947 // css::lang::XInitialization:
1948 void SAL_CALL SfxDocumentMetaData::initialize( const css::uno::Sequence< css::uno::Any > & aArguments)
1950 // possible arguments:
1951 // - no argument: default initialization (empty DOM)
1952 // - 1 argument, XDocument: initialize with given DOM and empty base URL
1953 // NB: links in document must be absolute
1955 ::osl::MutexGuard g(m_aMutex);
1956 css::uno::Reference<css::xml::dom::XDocument> xDoc;
1958 for (sal_Int32 i = 0; i < aArguments.getLength(); ++i) {
1959 const css::uno::Any any = aArguments[i];
1960 if (!(any >>= xDoc)) {
1961 throw css::lang::IllegalArgumentException(
1962 "SfxDocumentMetaData::initialize: argument must be XDocument",
1963 *this, static_cast<sal_Int16>(i));
1965 if (!xDoc.is()) {
1966 throw css::lang::IllegalArgumentException(
1967 "SfxDocumentMetaData::initialize: argument is null",
1968 *this, static_cast<sal_Int16>(i));
1972 if (!xDoc.is()) {
1973 // For a new document, we create a new DOM tree here.
1974 xDoc = createDOM();
1977 init(xDoc);
1980 // css::util::XCloneable:
1981 css::uno::Reference<css::util::XCloneable> SAL_CALL
1982 SfxDocumentMetaData::createClone()
1984 ::osl::MutexGuard g(m_aMutex);
1985 checkInit();
1987 rtl::Reference<SfxDocumentMetaData> pNew = createMe(m_xContext);
1989 // NB: do not copy the modification listeners, only DOM
1990 css::uno::Reference<css::xml::dom::XDocument> xDoc = createDOM();
1991 try {
1992 updateUserDefinedAndAttributes();
1993 // deep copy of root node
1994 css::uno::Reference<css::xml::dom::XNode> xRoot(
1995 m_xDoc->getDocumentElement(), css::uno::UNO_QUERY_THROW);
1996 css::uno::Reference<css::xml::dom::XNode> xRootNew(
1997 xDoc->importNode(xRoot, true));
1998 xDoc->appendChild(xRootNew);
1999 pNew->init(xDoc);
2000 } catch (const css::uno::RuntimeException &) {
2001 throw;
2002 } catch (const css::uno::Exception &) {
2003 css::uno::Any anyEx = cppu::getCaughtException();
2004 throw css::lang::WrappedTargetRuntimeException(
2005 "SfxDocumentMetaData::createClone: exception",
2006 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
2008 return css::uno::Reference<css::util::XCloneable> (pNew);
2011 // css::util::XModifiable:
2012 sal_Bool SAL_CALL SfxDocumentMetaData::isModified( )
2014 ::osl::MutexGuard g(m_aMutex);
2015 checkInit();
2016 css::uno::Reference<css::util::XModifiable> xMB(m_xUserDefined,
2017 css::uno::UNO_QUERY);
2018 return m_isModified || (xMB.is() && xMB->isModified());
2021 void SAL_CALL SfxDocumentMetaData::setModified( sal_Bool bModified )
2023 css::uno::Reference<css::util::XModifiable> xMB;
2024 { // do not lock mutex while notifying (#i93514#) to prevent deadlock
2025 ::osl::MutexGuard g(m_aMutex);
2026 checkInit();
2027 m_isModified = bModified;
2028 if ( !bModified && m_xUserDefined.is() )
2030 xMB.set(m_xUserDefined, css::uno::UNO_QUERY);
2031 assert(xMB.is() &&
2032 "SfxDocumentMetaData::setModified: PropertyBag not Modifiable?");
2035 if (bModified) {
2036 try {
2037 css::uno::Reference<css::uno::XInterface> xThis(*this);
2038 css::lang::EventObject event(xThis);
2039 m_NotifyListeners.notifyEach(&css::util::XModifyListener::modified,
2040 event);
2041 } catch (const css::uno::RuntimeException &) {
2042 throw;
2043 } catch (const css::uno::Exception &) {
2044 // ignore
2045 TOOLS_WARN_EXCEPTION("sfx.doc", "setModified");
2047 } else {
2048 if (xMB.is()) {
2049 xMB->setModified(false);
2054 // css::util::XModifyBroadcaster:
2055 void SAL_CALL SfxDocumentMetaData::addModifyListener(
2056 const css::uno::Reference< css::util::XModifyListener > & xListener)
2058 ::osl::MutexGuard g(m_aMutex);
2059 checkInit();
2060 m_NotifyListeners.addInterface(xListener);
2061 css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
2062 css::uno::UNO_QUERY);
2063 if (xMB.is()) {
2064 xMB->addModifyListener(xListener);
2068 void SAL_CALL SfxDocumentMetaData::removeModifyListener(
2069 const css::uno::Reference< css::util::XModifyListener > & xListener)
2071 ::osl::MutexGuard g(m_aMutex);
2072 checkInit();
2073 m_NotifyListeners.removeInterface(xListener);
2074 css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
2075 css::uno::UNO_QUERY);
2076 if (xMB.is()) {
2077 xMB->removeModifyListener(xListener);
2081 // css::xml::sax::XSAXSerializable
2082 void SAL_CALL SfxDocumentMetaData::serialize(
2083 const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
2084 const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces)
2086 ::osl::MutexGuard g(m_aMutex);
2087 checkInit();
2088 updateUserDefinedAndAttributes();
2089 css::uno::Reference<css::xml::sax::XSAXSerializable> xSAXable(m_xDoc,
2090 css::uno::UNO_QUERY_THROW);
2091 xSAXable->serialize(i_xHandler, i_rNamespaces);
2094 void SfxDocumentMetaData::createUserDefined()
2096 // user-defined meta data: create PropertyBag which only accepts property
2097 // values of allowed types
2098 if ( m_xUserDefined.is() )
2099 return;
2101 css::uno::Sequence<css::uno::Type> types{
2102 ::cppu::UnoType<bool>::get(),
2103 ::cppu::UnoType< OUString>::get(),
2104 ::cppu::UnoType<css::util::DateTime>::get(),
2105 ::cppu::UnoType<css::util::Date>::get(),
2106 ::cppu::UnoType<css::util::DateTimeWithTimezone>::get(),
2107 ::cppu::UnoType<css::util::DateWithTimezone>::get(),
2108 ::cppu::UnoType<css::util::Duration>::get(),
2109 ::cppu::UnoType<float>::get(),
2110 ::cppu::UnoType<double>::get(),
2111 ::cppu::UnoType<sal_Int16>::get(),
2112 ::cppu::UnoType<sal_Int32>::get(),
2113 ::cppu::UnoType<sal_Int64>::get(),
2114 // Time is supported for backward compatibility with OOo 3.x, x<=2
2115 ::cppu::UnoType<css::util::Time>::get()
2117 // #i94175#: ODF allows empty user-defined property names!
2118 m_xUserDefined.set(
2119 css::beans::PropertyBag::createWithTypes( m_xContext, types, true/*AllowEmptyPropertyName*/, false/*AutomaticAddition*/ ),
2120 css::uno::UNO_QUERY_THROW);
2122 const css::uno::Reference<css::util::XModifyBroadcaster> xMB(
2123 m_xUserDefined, css::uno::UNO_QUERY);
2124 if (xMB.is())
2126 const std::vector<css::uno::Reference<css::uno::XInterface> >
2127 listeners(m_NotifyListeners.getElements());
2128 for (const auto& l : listeners) {
2129 xMB->addModifyListener(
2130 css::uno::Reference< css::util::XModifyListener >(l, css::uno::UNO_QUERY) );
2135 } // closing anonymous implementation namespace
2137 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
2138 CompatWriterDocPropsImpl_get_implementation(
2139 css::uno::XComponentContext *context,
2140 css::uno::Sequence<css::uno::Any> const &)
2142 return cppu::acquire(new CompatWriterDocPropsImpl(context));
2145 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
2146 SfxDocumentMetaData_get_implementation(
2147 css::uno::XComponentContext *context,
2148 css::uno::Sequence<css::uno::Any> const &)
2150 return cppu::acquire(new SfxDocumentMetaData(context));
2153 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */