1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22
23 #include <comphelper/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/document/XDocumentProperties2.hpp>
28 #include <com/sun/star/lang/XInitialization.hpp>
29 #include <com/sun/star/util/XCloneable.hpp>
30 #include <com/sun/star/util/XModifiable.hpp>
31 #include <com/sun/star/xml/sax/SAXException.hpp>
32 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
33
34 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
35 #include <com/sun/star/lang/EventObject.hpp>
36 #include <com/sun/star/beans/IllegalTypeException.hpp>
37 #include <com/sun/star/beans/PropertyExistException.hpp>
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <com/sun/star/beans/XPropertySetInfo.hpp>
40 #include <com/sun/star/beans/PropertyAttribute.hpp>
41 #include <com/sun/star/task/ErrorCodeIOException.hpp>
42 #include <com/sun/star/embed/XStorage.hpp>
43 #include <com/sun/star/embed/XTransactedObject.hpp>
44 #include <com/sun/star/embed/ElementModes.hpp>
45 #include <com/sun/star/io/WrongFormatException.hpp>
46 #include <com/sun/star/io/XStream.hpp>
47 #include <com/sun/star/document/XImporter.hpp>
48 #include <com/sun/star/document/XExporter.hpp>
49 #include <com/sun/star/document/XFilter.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>
64
65 #include <rtl/ustrbuf.hxx>
66 #include <tools/datetime.hxx>
67 #include <comphelper/diagnose_ex.hxx>
68 #include <osl/mutex.hxx>
69 #include <comphelper/fileformat.h>
70 #include <comphelper/interfacecontainer3.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>
79
80 #include <algorithm>
81 #include <utility>
82 #include <vector>
83 #include <map>
84 #include <cstring>
85 #include <limits>
86
87
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>
92
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.
98 *
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.
103 *
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>).
114 *
115 */
116
117 /// anonymous implementation namespace
118 namespace {
119
120 /// a list of attribute-lists, where attribute means name and content
121 typedef std::vector<std::vector<std::pair<OUString, OUString> > >
122 AttrVector;
123
124 typedef ::comphelper::WeakComponentImplHelper<
125 css::lang::XServiceInfo,
126 css::document::XDocumentProperties2,
127 css::lang::XInitialization,
128 css::util::XCloneable,
129 css::util::XModifiable,
130 css::xml::sax::XSAXSerializable>
131 SfxDocumentMetaData_Base;
132
133 class SfxDocumentMetaData:
134 public SfxDocumentMetaData_Base
135 {
136 public:
137 explicit SfxDocumentMetaData(
138 css::uno::Reference< css::uno::XComponentContext > const & context);
139 SfxDocumentMetaData(const SfxDocumentMetaData&) = delete;
140 SfxDocumentMetaData& operator=(const SfxDocumentMetaData&) = delete;
141
142 // css::lang::XServiceInfo:
143 virtual OUString SAL_CALL getImplementationName() override;
144 virtual sal_Bool SAL_CALL supportsService(
145 const OUString & ServiceName) override;
146 virtual css::uno::Sequence< OUString > SAL_CALL
147 getSupportedServiceNames() override;
148
149 // css::lang::XComponent:
150 virtual void disposing(std::unique_lock<std::mutex>& rGuard) override;
151
152 // css::document::XDocumentProperties:
153 virtual OUString SAL_CALL getAuthor() override;
154 virtual void SAL_CALL setAuthor(const OUString & the_value) override;
155 virtual OUString SAL_CALL getGenerator() override;
156 virtual void SAL_CALL setGenerator(const OUString & the_value) override;
157 virtual css::util::DateTime SAL_CALL getCreationDate() override;
158 virtual void SAL_CALL setCreationDate(const css::util::DateTime & the_value) override;
159 virtual OUString SAL_CALL getTitle() override;
160 virtual void SAL_CALL setTitle(const OUString & the_value) override;
161 virtual OUString SAL_CALL getSubject() override;
162 virtual void SAL_CALL setSubject(const OUString & the_value) override;
163 virtual OUString SAL_CALL getDescription() override;
164 virtual void SAL_CALL setDescription(const OUString & the_value) override;
165 virtual css::uno::Sequence< OUString > SAL_CALL getKeywords() override;
166 virtual void SAL_CALL setKeywords(
167 const css::uno::Sequence< OUString > & the_value) override;
168 virtual css::lang::Locale SAL_CALL getLanguage() override;
169 virtual void SAL_CALL setLanguage(const css::lang::Locale & the_value) override;
170 virtual OUString SAL_CALL getModifiedBy() override;
171 virtual void SAL_CALL setModifiedBy(const OUString & the_value) override;
172 virtual css::util::DateTime SAL_CALL getModificationDate() override;
173 virtual void SAL_CALL setModificationDate(
174 const css::util::DateTime & the_value) override;
175 virtual OUString SAL_CALL getPrintedBy() override;
176 virtual void SAL_CALL setPrintedBy(const OUString & the_value) override;
177 virtual css::util::DateTime SAL_CALL getPrintDate() override;
178 virtual void SAL_CALL setPrintDate(const css::util::DateTime & the_value) override;
179 virtual OUString SAL_CALL getTemplateName() override;
180 virtual void SAL_CALL setTemplateName(const OUString & the_value) override;
181 virtual OUString SAL_CALL getTemplateURL() override;
182 virtual void SAL_CALL setTemplateURL(const OUString & the_value) override;
183 virtual css::util::DateTime SAL_CALL getTemplateDate() override;
184 virtual void SAL_CALL setTemplateDate(const css::util::DateTime & the_value) override;
185 virtual OUString SAL_CALL getAutoloadURL() override;
186 virtual void SAL_CALL setAutoloadURL(const OUString & the_value) override;
187 virtual ::sal_Int32 SAL_CALL getAutoloadSecs() override;
188 virtual void SAL_CALL setAutoloadSecs(::sal_Int32 the_value) override;
189 virtual OUString SAL_CALL getDefaultTarget() override;
190 virtual void SAL_CALL setDefaultTarget(const OUString & the_value) override;
191 virtual css::uno::Sequence< css::beans::NamedValue > SAL_CALL
192 getDocumentStatistics() override;
193 virtual void SAL_CALL setDocumentStatistics(
194 const css::uno::Sequence< css::beans::NamedValue > & the_value) override;
195 virtual ::sal_Int16 SAL_CALL getEditingCycles() override;
196 virtual void SAL_CALL setEditingCycles(::sal_Int16 the_value) override;
197 virtual ::sal_Int32 SAL_CALL getEditingDuration() override;
198 virtual void SAL_CALL setEditingDuration(::sal_Int32 the_value) override;
199 virtual void SAL_CALL resetUserData(const OUString & the_value) override;
200 virtual css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
201 getUserDefinedProperties() override;
202 virtual void SAL_CALL loadFromStorage(
203 const css::uno::Reference< css::embed::XStorage > & Storage,
204 const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
205 virtual void SAL_CALL loadFromMedium(const OUString & URL,
206 const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
207 virtual void SAL_CALL storeToStorage(
208 const css::uno::Reference< css::embed::XStorage > & Storage,
209 const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
210 virtual void SAL_CALL storeToMedium(const OUString & URL,
211 const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
212 virtual css::uno::Sequence< OUString > SAL_CALL getContributor() override;
213 virtual void SAL_CALL setContributor(const css::uno::Sequence< OUString >& the_value) override;
214 virtual OUString SAL_CALL getCoverage() override;
215 virtual void SAL_CALL setCoverage(const OUString & the_value) override;
216 virtual OUString SAL_CALL getIdentifier() override;
217 virtual void SAL_CALL setIdentifier(const OUString & the_value) override;
218 virtual css::uno::Sequence< OUString > SAL_CALL getPublisher() override;
219 virtual void SAL_CALL setPublisher(const css::uno::Sequence< OUString > & the_value) override;
220 virtual css::uno::Sequence< OUString > SAL_CALL getRelation() override;
221 virtual void SAL_CALL setRelation(const css::uno::Sequence< OUString > & the_value) override;
222 virtual OUString SAL_CALL getRights() override;
223 virtual void SAL_CALL setRights(const OUString & the_value) override;
224 virtual OUString SAL_CALL getSource() override;
225 virtual void SAL_CALL setSource(const OUString& the_value) override;
226 virtual OUString SAL_CALL getType() override;
227 virtual void SAL_CALL setType(const OUString& the_value) override;
228
229
230 // css::lang::XInitialization:
231 virtual void SAL_CALL initialize(
232 const css::uno::Sequence< css::uno::Any > & aArguments) override;
233
234 // css::util::XCloneable:
235 virtual css::uno::Reference<css::util::XCloneable> SAL_CALL createClone() override;
236
237 // css::util::XModifiable:
238 virtual sal_Bool SAL_CALL isModified( ) override;
239 virtual void SAL_CALL setModified( sal_Bool bModified ) override;
240
241 // css::util::XModifyBroadcaster:
242 virtual void SAL_CALL addModifyListener(
243 const css::uno::Reference< css::util::XModifyListener > & xListener) override;
244 virtual void SAL_CALL removeModifyListener(
245 const css::uno::Reference< css::util::XModifyListener > & xListener) override;
246
247 // css::xml::sax::XSAXSerializable
248 virtual void SAL_CALL serialize(
249 const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
250 const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces) override;
251
252 protected:
~SfxDocumentMetaData()253 virtual ~SfxDocumentMetaData() override {}
createMe(css::uno::Reference<css::uno::XComponentContext> const & context)254 virtual rtl::Reference<SfxDocumentMetaData> createMe( css::uno::Reference< css::uno::XComponentContext > const & context ) { return new SfxDocumentMetaData( context ); };
255 const css::uno::Reference< css::uno::XComponentContext > m_xContext;
256
257 /// for notification
258 ::comphelper::OInterfaceContainerHelper4<css::util::XModifyListener> m_NotifyListeners;
259 /// flag: false means not initialized yet, or disposed
260 bool m_isInitialized;
261 /// flag
262 bool m_isModified;
263 /// meta-data DOM tree
264 css::uno::Reference< css::xml::dom::XDocument > m_xDoc;
265 /// meta-data super node in the meta-data DOM tree
266 css::uno::Reference< css::xml::dom::XNode> m_xParent;
267 /// standard meta data (single occurrence)
268 std::map< OUString, css::uno::Reference<css::xml::dom::XNode> >
269 m_meta;
270 /// standard meta data (multiple occurrences)
271 std::map< OUString,
272 std::vector<css::uno::Reference<css::xml::dom::XNode> > > m_metaList;
273 /// user-defined meta data (meta:user-defined) @ATTENTION may be null!
274 css::uno::Reference<css::beans::XPropertyContainer> m_xUserDefined;
275 // now for some meta-data attributes; these are not updated directly in the
276 // DOM because updates (detecting "empty" elements) would be quite messy
277 OUString m_TemplateName;
278 OUString m_TemplateURL;
279 css::util::DateTime m_TemplateDate;
280 OUString m_AutoloadURL;
281 sal_Int32 m_AutoloadSecs;
282 OUString m_DefaultTarget;
283
284 /// check if we are initialized properly
285 void checkInit(std::unique_lock<std::mutex>& rGuard) const;
286 /// initialize state from given DOM tree
287 void init(std::unique_lock<std::mutex>& rGuard, const css::uno::Reference<css::xml::dom::XDocument>& i_xDom);
288 /// update element in DOM tree
289 void updateElement(std::unique_lock<std::mutex>& rGuard,
290 const OUString & i_name,
291 std::vector<std::pair<OUString, OUString> >* i_pAttrs = nullptr);
292 /// update user-defined meta data and attributes in DOM tree
293 void updateUserDefinedAndAttributes(std::unique_lock<std::mutex>& rGuard);
294 /// create empty DOM tree (XDocument)
295 css::uno::Reference<css::xml::dom::XDocument> createDOM() const;
296 /// extract base URL (necessary for converting relative links)
297 css::uno::Reference<css::beans::XPropertySet> getURLProperties(
298 std::unique_lock<std::mutex>& rGuard,
299 const css::uno::Sequence<css::beans::PropertyValue> & i_rMedium) const;
300 /// get text of standard meta data element
301 OUString getMetaText(std::unique_lock<std::mutex>& rGuard, const char* i_name) const;
302 /// set text of standard meta data element iff not equal to existing text
303 bool setMetaText(std::unique_lock<std::mutex>& g, const OUString& i_name,
304 const OUString & i_rValue);
305 /// set text of standard meta data element iff not equal to existing text
306 void setMetaTextAndNotify(const OUString& i_name,
307 const OUString & i_rValue);
308 /// get text of standard meta data element's attribute
309 OUString getMetaAttr(std::unique_lock<std::mutex>& rGuard,
310 const OUString& i_name,
311 const OUString& i_attr) const;
312 /// get text of a list of standard meta data elements (multiple occ.)
313 css::uno::Sequence< OUString > getMetaList(
314 std::unique_lock<std::mutex>& rGuard,
315 const char* i_name) const;
316 /// set text of a list of standard meta data elements (multiple occ.)
317 bool setMetaList(std::unique_lock<std::mutex>& rGuard, const OUString& i_name,
318 const css::uno::Sequence< OUString > & i_rValue,
319 AttrVector const*);
320 void createUserDefined(std::unique_lock<std::mutex>& rGuard);
321 };
322
323 typedef ::cppu::ImplInheritanceHelper< SfxDocumentMetaData, css::document::XCompatWriterDocProperties > CompatWriterDocPropsImpl_BASE;
324
325 class CompatWriterDocPropsImpl : public CompatWriterDocPropsImpl_BASE
326 {
327 OUString msManager;
328 OUString msCategory;
329 OUString msCompany;
330 protected:
createMe(css::uno::Reference<css::uno::XComponentContext> const & context)331 virtual rtl::Reference<SfxDocumentMetaData> createMe( css::uno::Reference< css::uno::XComponentContext > const & context ) override { return new CompatWriterDocPropsImpl( context ); };
332 public:
CompatWriterDocPropsImpl(css::uno::Reference<css::uno::XComponentContext> const & context)333 explicit CompatWriterDocPropsImpl( css::uno::Reference< css::uno::XComponentContext > const & context) : CompatWriterDocPropsImpl_BASE( context ) {}
334
335 // XCompatWriterDocPropsImpl
getManager()336 virtual OUString SAL_CALL getManager() override { return msManager; }
setManager(const OUString & _manager)337 virtual void SAL_CALL setManager( const OUString& _manager ) override { msManager = _manager; }
getCategory()338 virtual OUString SAL_CALL getCategory() override { return msCategory; }
setCategory(const OUString & _category)339 virtual void SAL_CALL setCategory( const OUString& _category ) override { msCategory = _category; }
getCompany()340 virtual OUString SAL_CALL getCompany() override { return msCompany; }
setCompany(const OUString & _company)341 virtual void SAL_CALL setCompany( const OUString& _company ) override { msCompany = _company; }
342
343 // XServiceInfo
getImplementationName()344 virtual OUString SAL_CALL getImplementationName( ) override
345 {
346 return u"CompatWriterDocPropsImpl"_ustr;
347 }
348
supportsService(const OUString & ServiceName)349 virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override
350 {
351 return cppu::supportsService(this, ServiceName);
352 }
353
getSupportedServiceNames()354 virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override
355 {
356 css::uno::Sequence<OUString> aServiceNames { u"com.sun.star.writer.DocumentProperties"_ustr };
357 return aServiceNames;
358 }
359 };
360
361 constexpr OUString sMetaPageCount = u"meta:page-count"_ustr;
362 constexpr OUString sMetaTableCount = u"meta:table-count"_ustr;
363 constexpr OUString sMetaDrawCount = u"meta:draw-count"_ustr;
364 constexpr OUString sMetaImageCount = u"meta:image-count"_ustr;
365 constexpr OUString sMetaObjectCount = u"meta:object-count"_ustr;
366 constexpr OUString sMetaOleObjectCount = u"meta:ole-object-count"_ustr;
367 constexpr OUString sMetaParagraphCount = u"meta:paragraph-count"_ustr;
368 constexpr OUString sMetaWordCount = u"meta:word-count"_ustr;
369 constexpr OUString sMetaCharacterCount = u"meta:character-count"_ustr;
370 constexpr OUString sMetaRowCount = u"meta:row-count"_ustr;
371 constexpr OUString sMetaFrameCount = u"meta:frame-count"_ustr;
372 constexpr OUString sMetaSentenceCount = u"meta:sentence-count"_ustr;
373 constexpr OUString sMetaSyllableCount = u"meta:syllable-count"_ustr;
374 constexpr OUString sMetaNonWhitespaceCharacterCount = u"meta:non-whitespace-character-count"_ustr;
375 constexpr OUString sMetaCellCount = u"meta:cell-count"_ustr;
376
377 // NB: keep these two arrays in sync!
378 constexpr OUString s_stdStatAttrs[] = {
379 sMetaPageCount,
380 sMetaTableCount,
381 sMetaDrawCount,
382 sMetaImageCount,
383 sMetaObjectCount,
384 sMetaOleObjectCount,
385 sMetaParagraphCount,
386 sMetaWordCount,
387 sMetaCharacterCount,
388 sMetaRowCount,
389 sMetaFrameCount,
390 sMetaSentenceCount,
391 sMetaSyllableCount,
392 sMetaNonWhitespaceCharacterCount,
393 sMetaCellCount
394 };
395
396 // NB: keep these two arrays in sync!
397 const char* const s_stdStats[] = {
398 "PageCount",
399 "TableCount",
400 "DrawCount",
401 "ImageCount",
402 "ObjectCount",
403 "OLEObjectCount",
404 "ParagraphCount",
405 "WordCount",
406 "CharacterCount",
407 "RowCount",
408 "FrameCount",
409 "SentenceCount",
410 "SyllableCount",
411 "NonWhitespaceCharacterCount",
412 "CellCount",
413 nullptr
414 };
415
416 const char* const s_stdMeta[] = {
417 "meta:generator", // string
418 "dc:title", // string
419 "dc:description", // string
420 "dc:subject", // string
421 "meta:initial-creator", // string
422 "dc:creator", // string
423 "meta:printed-by", // string
424 "meta:creation-date", // dateTime
425 "dc:date", // dateTime
426 "meta:print-date", // dateTime
427 "meta:template", // XLink
428 "meta:auto-reload",
429 "meta:hyperlink-behaviour",
430 "dc:language", // language
431 "meta:editing-cycles", // nonNegativeInteger
432 "meta:editing-duration", // duration
433 "meta:document-statistic", // ... // note: statistic is singular, no s!
434 "dc:coverage",
435 "dc:identifier",
436 "dc:rights",
437 "dc:source",
438 "dc:type",
439 nullptr
440 };
441
442 constexpr OUString sMetaKeyword = u"meta:keyword"_ustr;
443 constexpr OUString sMetaUserDefined = u"meta:user-defined"_ustr;
444 constexpr OUString sDCContributor = u"dc:contributor"_ustr;
445 constexpr OUString sDCPublisher = u"dc:publisher"_ustr;
446 constexpr OUString sDCRelation = u"dc:relation"_ustr;
447 constexpr OUString s_stdMetaList[] {
448 sMetaKeyword, // string*
449 sMetaUserDefined, // ...*
450 sDCContributor, // string*
451 sDCPublisher, // string*
452 sDCRelation, // string*
453 };
454
455 constexpr OUStringLiteral s_nsXLink = u"http://www.w3.org/1999/xlink";
456 constexpr OUString s_nsDC = u"http://purl.org/dc/elements/1.1/"_ustr;
457 constexpr OUString s_nsODF = u"urn:oasis:names:tc:opendocument:xmlns:office:1.0"_ustr;
458 constexpr OUString s_nsODFMeta = u"urn:oasis:names:tc:opendocument:xmlns:meta:1.0"_ustr;
459 // constexpr OUStringLiteral s_nsOOo = "http://openoffice.org/2004/office"; // not used (yet?)
460
461 constexpr OUString s_meta = u"meta.xml"_ustr;
462
isValidDate(const css::util::Date & i_rDate)463 bool isValidDate(const css::util::Date & i_rDate)
464 {
465 return i_rDate.Month > 0;
466 }
467
isValidDateTime(const css::util::DateTime & i_rDateTime)468 bool isValidDateTime(const css::util::DateTime & i_rDateTime)
469 {
470 return i_rDateTime.Month > 0;
471 }
472
473 std::pair< OUString, OUString >
getQualifier(const OUString & nm)474 getQualifier(const OUString& nm) {
475 sal_Int32 ix = nm.indexOf(u':');
476 if (ix == -1) {
477 return std::make_pair(OUString(), nm);
478 } else {
479 return std::make_pair(nm.copy(0,ix), nm.copy(ix+1));
480 }
481 }
482
483 // get namespace for standard qualified names
484 // NB: only call this with statically known strings!
getNameSpace(const OUString & i_qname)485 OUString getNameSpace(const OUString& i_qname) noexcept
486 {
487 OUString ns;
488 OUString n = getQualifier(i_qname).first;
489 if ( n == "xlink" ) ns = s_nsXLink;
490 if ( n == "dc" ) ns = s_nsDC;
491 if ( n == "office" ) ns = s_nsODF;
492 if ( n == "meta" ) ns = s_nsODFMeta;
493 assert(!ns.isEmpty());
494 return ns;
495 }
496
497 bool
textToDateOrDateTime(css::util::Date & io_rd,css::util::DateTime & io_rdt,bool & o_rIsDateTime,std::optional<sal_Int16> & o_rTimeZone,const OUString & i_text)498 textToDateOrDateTime(css::util::Date & io_rd, css::util::DateTime & io_rdt,
499 bool & o_rIsDateTime, std::optional<sal_Int16> & o_rTimeZone,
500 const OUString& i_text) noexcept
501 {
502 if (::sax::Converter::parseDateOrDateTime(
503 &io_rd, io_rdt, o_rIsDateTime, &o_rTimeZone, i_text)) {
504 return true;
505 } else {
506 SAL_WARN_IF(!i_text.isEmpty(), "sfx.doc", "Invalid date: " << i_text);
507 return false;
508 }
509 }
510
511 // convert string to date/time
512 bool
textToDateTime(css::util::DateTime & io_rdt,const OUString & i_text)513 textToDateTime(css::util::DateTime & io_rdt, const OUString& i_text) noexcept
514 {
515 if (::sax::Converter::parseDateTime(io_rdt, i_text)) {
516 return true;
517 } else {
518 SAL_WARN_IF(!i_text.isEmpty(), "sfx.doc", "Invalid date: " << i_text);
519 return false;
520 }
521 }
522
523 // convert string to date/time with default return value
524 css::util::DateTime
textToDateTimeDefault(const OUString & i_text)525 textToDateTimeDefault(const OUString& i_text) noexcept
526 {
527 css::util::DateTime dt;
528 static_cast<void> (textToDateTime(dt, i_text));
529 // on conversion error: return default value (unchanged)
530 return dt;
531 }
532
533 // convert date to string
534 OUString
dateToText(css::util::Date const & i_rd,sal_Int16 const * const pTimeZone)535 dateToText(css::util::Date const& i_rd,
536 sal_Int16 const*const pTimeZone) noexcept
537 {
538 if (isValidDate(i_rd)) {
539 OUStringBuffer buf;
540 ::sax::Converter::convertDate(buf, i_rd, pTimeZone);
541 return buf.makeStringAndClear();
542 } else {
543 return OUString();
544 }
545 }
546
547
548 // convert date/time to string
549 OUString
dateTimeToText(css::util::DateTime const & i_rdt,sal_Int16 const * const pTimeZone=nullptr)550 dateTimeToText(css::util::DateTime const& i_rdt,
551 sal_Int16 const*const pTimeZone = nullptr) noexcept
552 {
553 if (isValidDateTime(i_rdt)) {
554 OUStringBuffer buf(32);
555 ::sax::Converter::convertDateTime(buf, i_rdt, pTimeZone, true);
556 return buf.makeStringAndClear();
557 } else {
558 return OUString();
559 }
560 }
561
562 // convert string to duration
563 bool
textToDuration(css::util::Duration & io_rDur,OUString const & i_rText)564 textToDuration(css::util::Duration& io_rDur, OUString const& i_rText)
565 noexcept
566 {
567 if (::sax::Converter::convertDuration(io_rDur, i_rText)) {
568 return true;
569 } else {
570 SAL_WARN_IF(!i_rText.isEmpty(), "sfx.doc", "Invalid duration: " << i_rText);
571 return false;
572 }
573 }
574
textToDuration(OUString const & i_rText)575 sal_Int32 textToDuration(OUString const& i_rText) noexcept
576 {
577 css::util::Duration d;
578 if (textToDuration(d, i_rText)) {
579 // #i107372#: approximate years/months
580 const sal_Int32 days( (d.Years * 365) + (d.Months * 30) + d.Days );
581 return (days * (24*3600))
582 + (d.Hours * 3600) + (d.Minutes * 60) + d.Seconds;
583 } else {
584 return 0; // default
585 }
586 }
587
588 // convert duration to string
durationToText(css::util::Duration const & i_rDur)589 OUString durationToText(css::util::Duration const& i_rDur) noexcept
590 {
591 OUStringBuffer buf;
592 ::sax::Converter::convertDuration(buf, i_rDur);
593 return buf.makeStringAndClear();
594 }
595
596 // convert duration to string
durationToText(sal_Int32 i_value)597 OUString durationToText(sal_Int32 i_value) noexcept
598 {
599 css::util::Duration ud;
600 ud.Days = static_cast<sal_Int16>(i_value / (24 * 3600));
601 ud.Hours = static_cast<sal_Int16>((i_value % (24 * 3600)) / 3600);
602 ud.Minutes = static_cast<sal_Int16>((i_value % 3600) / 60);
603 ud.Seconds = static_cast<sal_Int16>(i_value % 60);
604 ud.NanoSeconds = 0;
605 return durationToText(ud);
606 }
607
608 // extract base URL (necessary for converting relative links)
609 css::uno::Reference< css::beans::XPropertySet >
getURLProperties(std::unique_lock<std::mutex> &,const css::uno::Sequence<css::beans::PropertyValue> & i_rMedium) const610 SfxDocumentMetaData::getURLProperties(
611 std::unique_lock<std::mutex>& /*rGuard*/,
612 const css::uno::Sequence< css::beans::PropertyValue > & i_rMedium) const
613 {
614 css::uno::Reference< css::beans::XPropertyBag> xPropArg = css::beans::PropertyBag::createDefault( m_xContext );
615 try {
616 css::uno::Any baseUri;
617 for (const auto& rProp : i_rMedium) {
618 if (rProp.Name == "DocumentBaseURL") {
619 baseUri = rProp.Value;
620 } else if (rProp.Name == "URL") {
621 if (!baseUri.hasValue()) {
622 baseUri = rProp.Value;
623 }
624 } else if (rProp.Name == "HierarchicalDocumentName") {
625 xPropArg->addProperty(
626 u"StreamRelPath"_ustr,
627 css::beans::PropertyAttribute::MAYBEVOID,
628 rProp.Value);
629 }
630 }
631 if (baseUri.hasValue()) {
632 xPropArg->addProperty(
633 u"BaseURI"_ustr, css::beans::PropertyAttribute::MAYBEVOID,
634 baseUri);
635 }
636 xPropArg->addProperty(u"StreamName"_ustr,
637 css::beans::PropertyAttribute::MAYBEVOID,
638 css::uno::Any(s_meta));
639 } catch (const css::uno::Exception &) {
640 // ignore
641 }
642 return css::uno::Reference< css::beans::XPropertySet>(xPropArg,
643 css::uno::UNO_QUERY_THROW);
644 }
645
646 // return the text of the (hopefully unique, i.e., normalize first!) text
647 // node _below_ the given node
648 /// @throws css::uno::RuntimeException
649 OUString
getNodeText(const css::uno::Reference<css::xml::dom::XNode> & i_xNode)650 getNodeText(const css::uno::Reference<css::xml::dom::XNode>& i_xNode)
651 {
652 if (!i_xNode.is())
653 throw css::uno::RuntimeException(u"SfxDocumentMetaData::getNodeText: argument is null"_ustr, i_xNode);
654 for (css::uno::Reference<css::xml::dom::XNode> c = i_xNode->getFirstChild();
655 c.is();
656 c = c->getNextSibling()) {
657 if (c->getNodeType() == css::xml::dom::NodeType_TEXT_NODE) {
658 try {
659 return c->getNodeValue();
660 } catch (const css::xml::dom::DOMException &) { // too big?
661 return OUString();
662 }
663 }
664 }
665 return OUString();
666 }
667
668 OUString
getMetaText(std::unique_lock<std::mutex> & rGuard,const char * i_name) const669 SfxDocumentMetaData::getMetaText(std::unique_lock<std::mutex>& rGuard, const char* i_name) const
670 // throw (css::uno::RuntimeException)
671 {
672 checkInit(rGuard);
673
674 const OUString name( OUString::createFromAscii(i_name) );
675 assert(m_meta.find(name) != m_meta.end());
676 css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
677 return (xNode.is()) ? getNodeText(xNode) : OUString();
678 }
679
680 bool
setMetaText(std::unique_lock<std::mutex> & rGuard,const OUString & name,const OUString & i_rValue)681 SfxDocumentMetaData::setMetaText(std::unique_lock<std::mutex>& rGuard, const OUString& name,
682 const OUString & i_rValue)
683 // throw (css::uno::RuntimeException)
684 {
685 checkInit(rGuard);
686
687 assert(m_meta.find(name) != m_meta.end());
688 css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
689
690 try {
691 if (i_rValue.isEmpty()) {
692 if (xNode.is()) { // delete
693 m_xParent->removeChild(xNode);
694 xNode.clear();
695 m_meta[name] = std::move(xNode);
696 return true;
697 } else {
698 return false;
699 }
700 } else {
701 if (xNode.is()) { // update
702 for (css::uno::Reference<css::xml::dom::XNode> c =
703 xNode->getFirstChild();
704 c.is();
705 c = c->getNextSibling()) {
706 if (c->getNodeType() == css::xml::dom::NodeType_TEXT_NODE) {
707 if (c->getNodeValue() != i_rValue) {
708 c->setNodeValue(i_rValue);
709 return true;
710 } else {
711 return false;
712 }
713 }
714 }
715 } else { // insert
716 xNode.set(m_xDoc->createElementNS(getNameSpace(name), name),
717 css::uno::UNO_QUERY_THROW);
718 m_xParent->appendChild(xNode);
719 m_meta[name] = xNode;
720 }
721 css::uno::Reference<css::xml::dom::XNode> xTextNode(
722 m_xDoc->createTextNode(i_rValue), css::uno::UNO_QUERY_THROW);
723 xNode->appendChild(xTextNode);
724 return true;
725 }
726 } catch (const css::xml::dom::DOMException &) {
727 css::uno::Any anyEx = cppu::getCaughtException();
728 throw css::lang::WrappedTargetRuntimeException(
729 u"SfxDocumentMetaData::setMetaText: DOM exception"_ustr,
730 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
731 }
732 }
733
734 void
setMetaTextAndNotify(const OUString & i_name,const OUString & i_rValue)735 SfxDocumentMetaData::setMetaTextAndNotify(const OUString & i_name,
736 const OUString & i_rValue)
737 // throw (css::uno::RuntimeException)
738 {
739 std::unique_lock g(m_aMutex);
740 if (setMetaText(g, i_name, i_rValue)) {
741 g.unlock();
742 setModified(true);
743 }
744 }
745
746 OUString
getMetaAttr(std::unique_lock<std::mutex> &,const OUString & name,const OUString & i_attr) const747 SfxDocumentMetaData::getMetaAttr(std::unique_lock<std::mutex>& /*rGuard*/, const OUString& name, const OUString& i_attr) const
748 // throw (css::uno::RuntimeException)
749 {
750 assert(m_meta.find(name) != m_meta.end());
751 css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
752 if (xNode.is()) {
753 css::uno::Reference<css::xml::dom::XElement> xElem(xNode,
754 css::uno::UNO_QUERY_THROW);
755 return xElem->getAttributeNS(getNameSpace(i_attr),
756 getQualifier(i_attr).second);
757 } else {
758 return OUString();
759 }
760 }
761
762 css::uno::Sequence< OUString>
getMetaList(std::unique_lock<std::mutex> & rGuard,const char * i_name) const763 SfxDocumentMetaData::getMetaList(std::unique_lock<std::mutex>& rGuard, const char* i_name) const
764 // throw (css::uno::RuntimeException)
765 {
766 checkInit(rGuard);
767 OUString name = OUString::createFromAscii(i_name);
768 assert(m_metaList.find(name) != m_metaList.end());
769 std::vector<css::uno::Reference<css::xml::dom::XNode> > const & vec =
770 m_metaList.find(name)->second;
771 css::uno::Sequence< OUString> ret(vec.size());
772 std::transform(vec.begin(), vec.end(), ret.getArray(),
773 [](const auto& node) { return getNodeText(node); });
774 return ret;
775 }
776
777 bool
setMetaList(std::unique_lock<std::mutex> & rGuard,const OUString & name,const css::uno::Sequence<OUString> & i_rValue,AttrVector const * i_pAttrs)778 SfxDocumentMetaData::setMetaList(std::unique_lock<std::mutex>& rGuard, const OUString& name,
779 const css::uno::Sequence<OUString> & i_rValue,
780 AttrVector const* i_pAttrs)
781 // throw (css::uno::RuntimeException)
782 {
783 checkInit(rGuard);
784 assert((i_pAttrs == nullptr) ||
785 (static_cast<size_t>(i_rValue.getLength()) == i_pAttrs->size()));
786
787 try {
788 assert(m_metaList.find(name) != m_metaList.end());
789 std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
790 m_metaList[name];
791
792 // if nothing changed, do nothing
793 // alas, this does not check for permutations, or attributes...
794 if (nullptr == i_pAttrs) {
795 if (static_cast<size_t>(i_rValue.getLength()) == vec.size()) {
796 bool isEqual(true);
797 for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
798 css::uno::Reference<css::xml::dom::XNode> xNode(vec.at(i));
799 if (xNode.is()) {
800 OUString val = getNodeText(xNode);
801 if (val != i_rValue[i]) {
802 isEqual = false;
803 break;
804 }
805 }
806 }
807 if (isEqual) return false;
808 }
809 }
810
811 // remove old meta data nodes
812 {
813 std::vector<css::uno::Reference<css::xml::dom::XNode> >
814 ::reverse_iterator it(vec.rbegin());
815 try {
816 for ( ;it != vec.rend(); ++it)
817 {
818 m_xParent->removeChild(*it);
819 }
820 }
821 catch (...)
822 {
823 // Clean up already removed nodes
824 vec.erase(it.base(), vec.end());
825 throw;
826 }
827 vec.clear();
828 }
829
830 // insert new meta data nodes into DOM tree
831 for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
832 css::uno::Reference<css::xml::dom::XElement> xElem(
833 m_xDoc->createElementNS(getNameSpace(name), name),
834 css::uno::UNO_SET_THROW);
835 css::uno::Reference<css::xml::dom::XNode> xNode(xElem,
836 css::uno::UNO_QUERY_THROW);
837 css::uno::Reference<css::xml::dom::XNode> xTextNode(
838 m_xDoc->createTextNode(i_rValue[i]), css::uno::UNO_QUERY_THROW);
839 // set attributes
840 if (i_pAttrs != nullptr) {
841 for (auto const& elem : (*i_pAttrs)[i])
842 {
843 xElem->setAttributeNS(getNameSpace(elem.first),
844 elem.first, elem.second);
845 }
846 }
847 xNode->appendChild(xTextNode);
848 m_xParent->appendChild(xNode);
849 vec.push_back(xNode);
850 }
851
852 return true;
853 } catch (const css::xml::dom::DOMException &) {
854 css::uno::Any anyEx = cppu::getCaughtException();
855 throw css::lang::WrappedTargetRuntimeException(
856 u"SfxDocumentMetaData::setMetaList: DOM exception"_ustr,
857 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
858 }
859 }
860
861 // convert property list to string list and attribute list
862 std::pair<css::uno::Sequence< OUString>, AttrVector>
propsToStrings(css::uno::Reference<css::beans::XPropertySet> const & i_xPropSet)863 propsToStrings(css::uno::Reference<css::beans::XPropertySet> const & i_xPropSet)
864 {
865 ::std::vector< OUString > values;
866 AttrVector attrs;
867
868 css::uno::Reference<css::beans::XPropertySetInfo> xSetInfo
869 = i_xPropSet->getPropertySetInfo();
870 css::uno::Sequence<css::beans::Property> props = xSetInfo->getProperties();
871
872 for (sal_Int32 i = 0; i < props.getLength(); ++i) {
873 if (props[i].Attributes & css::beans::PropertyAttribute::TRANSIENT) {
874 continue;
875 }
876 const OUString name = props[i].Name;
877 css::uno::Any any;
878 try {
879 any = i_xPropSet->getPropertyValue(name);
880 } catch (const css::uno::Exception &) {
881 // ignore
882 }
883 const css::uno::Type & type = any.getValueType();
884 std::vector<std::pair<OUString, OUString> > as;
885 as.emplace_back("meta:name", name);
886 static constexpr OUString vt = u"meta:value-type"_ustr;
887
888 // convert according to type
889 if (type == ::cppu::UnoType<bool>::get()) {
890 bool b = false;
891 any >>= b;
892 OUStringBuffer buf;
893 ::sax::Converter::convertBool(buf, b);
894 values.push_back(buf.makeStringAndClear());
895 as.emplace_back(vt, u"boolean"_ustr);
896 } else if (type == ::cppu::UnoType< OUString>::get()) {
897 OUString s;
898 any >>= s;
899 values.push_back(s);
900 // #i90847# OOo 2.x does stupid things if value-type="string";
901 // fortunately string is default anyway, so we can just omit it
902 // #i107502#: however, OOo 2.x only reads 4 user-defined without @value-type
903 // => best backward compatibility: first 4 without @value-type, rest with
904 if (4 <= i)
905 {
906 as.emplace_back(vt, u"string"_ustr);
907 }
908 } else if (type == ::cppu::UnoType<css::util::DateTime>::get()) {
909 css::util::DateTime dt;
910 any >>= dt;
911 values.push_back(dateTimeToText(dt));
912 as.emplace_back(vt, u"date"_ustr);
913 } else if (type == ::cppu::UnoType<css::util::Date>::get()) {
914 css::util::Date d;
915 any >>= d;
916 values.push_back(dateToText(d, nullptr));
917 as.emplace_back(vt,u"date"_ustr);
918 } else if (type == ::cppu::UnoType<css::util::DateTimeWithTimezone>::get()) {
919 css::util::DateTimeWithTimezone dttz;
920 any >>= dttz;
921 values.push_back(dateTimeToText(dttz.DateTimeInTZ, &dttz.Timezone));
922 as.emplace_back(vt, u"date"_ustr);
923 } else if (type == ::cppu::UnoType<css::util::DateWithTimezone>::get()) {
924 css::util::DateWithTimezone dtz;
925 any >>= dtz;
926 values.push_back(dateToText(dtz.DateInTZ, &dtz.Timezone));
927 as.emplace_back(vt, u"date"_ustr);
928 } else if (type == ::cppu::UnoType<css::util::Time>::get()) {
929 // #i97029#: replaced by Duration
930 // Time is supported for backward compatibility with OOo 3.x, x<=2
931 css::util::Time ut;
932 any >>= ut;
933 css::util::Duration ud;
934 ud.Hours = ut.Hours;
935 ud.Minutes = ut.Minutes;
936 ud.Seconds = ut.Seconds;
937 ud.NanoSeconds = ut.NanoSeconds;
938 values.push_back(durationToText(ud));
939 as.emplace_back(vt, u"time"_ustr);
940 } else if (type == ::cppu::UnoType<css::util::Duration>::get()) {
941 css::util::Duration ud;
942 any >>= ud;
943 values.push_back(durationToText(ud));
944 as.emplace_back(vt, u"time"_ustr);
945 } else if (::cppu::UnoType<double>::get().isAssignableFrom(type)) {
946 // support not just double, but anything that can be converted
947 double d = 0;
948 any >>= d;
949 OUStringBuffer buf;
950 ::sax::Converter::convertDouble(buf, d);
951 values.push_back(buf.makeStringAndClear());
952 as.emplace_back(vt, u"float"_ustr);
953 } else {
954 SAL_WARN("sfx.doc", "Unsupported property type: " << any.getValueTypeName() );
955 continue;
956 }
957 attrs.push_back(std::move(as));
958 }
959
960 return std::make_pair(comphelper::containerToSequence(values), attrs);
961 }
962
963 // remove the given element from the DOM, and iff i_pAttrs != 0 insert new one
964 void
updateElement(std::unique_lock<std::mutex> &,const OUString & name,std::vector<std::pair<OUString,OUString>> * i_pAttrs)965 SfxDocumentMetaData::updateElement(std::unique_lock<std::mutex>& /*rGuard*/, const OUString& name,
966 std::vector<std::pair<OUString, OUString> >* i_pAttrs)
967 {
968 try {
969 // remove old element
970 css::uno::Reference<css::xml::dom::XNode> xNode =
971 m_meta.find(name)->second;
972 if (xNode.is()) {
973 m_xParent->removeChild(xNode);
974 xNode.clear();
975 }
976 // add new element
977 if (nullptr != i_pAttrs) {
978 css::uno::Reference<css::xml::dom::XElement> xElem(
979 m_xDoc->createElementNS(getNameSpace(name), name),
980 css::uno::UNO_SET_THROW);
981 xNode.set(xElem, css::uno::UNO_QUERY_THROW);
982 // set attributes
983 for (auto const& elem : *i_pAttrs)
984 {
985 xElem->setAttributeNS(getNameSpace(elem.first),
986 elem.first, elem.second);
987 }
988 m_xParent->appendChild(xNode);
989 }
990 m_meta[name] = std::move(xNode);
991 } catch (const css::xml::dom::DOMException &) {
992 css::uno::Any anyEx = cppu::getCaughtException();
993 throw css::lang::WrappedTargetRuntimeException(
994 u"SfxDocumentMetaData::updateElement: DOM exception"_ustr,
995 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
996 }
997 }
998
999 // update user-defined meta data in DOM tree
updateUserDefinedAndAttributes(std::unique_lock<std::mutex> & g)1000 void SfxDocumentMetaData::updateUserDefinedAndAttributes(std::unique_lock<std::mutex>& g)
1001 {
1002 createUserDefined(g);
1003 const css::uno::Reference<css::beans::XPropertySet> xPSet(m_xUserDefined,
1004 css::uno::UNO_QUERY_THROW);
1005 const std::pair<css::uno::Sequence< OUString>, AttrVector>
1006 udStringsAttrs( propsToStrings(xPSet) );
1007 (void) setMetaList(g, u"meta:user-defined"_ustr, udStringsAttrs.first,
1008 &udStringsAttrs.second);
1009
1010 // update elements with attributes
1011 std::vector<std::pair<OUString, OUString> > attributes;
1012 if (!m_TemplateName.isEmpty() || !m_TemplateURL.isEmpty()
1013 || isValidDateTime(m_TemplateDate)) {
1014 attributes.emplace_back("xlink:type", u"simple"_ustr);
1015 attributes.emplace_back("xlink:actuate", u"onRequest"_ustr);
1016 attributes.emplace_back("xlink:title", m_TemplateName);
1017 attributes.emplace_back("xlink:href", m_TemplateURL );
1018 if (isValidDateTime(m_TemplateDate)) {
1019 attributes.emplace_back(
1020 "meta:date", dateTimeToText(m_TemplateDate));
1021 }
1022 updateElement(g, u"meta:template"_ustr, &attributes);
1023 } else {
1024 updateElement(g, u"meta:template"_ustr);
1025 }
1026 attributes.clear();
1027
1028 if (!m_AutoloadURL.isEmpty() || (0 != m_AutoloadSecs)) {
1029 attributes.emplace_back("xlink:href", m_AutoloadURL );
1030 attributes.emplace_back("meta:delay",
1031 durationToText(m_AutoloadSecs));
1032 updateElement(g, u"meta:auto-reload"_ustr, &attributes);
1033 } else {
1034 updateElement(g, u"meta:auto-reload"_ustr);
1035 }
1036 attributes.clear();
1037
1038 if (!m_DefaultTarget.isEmpty()) {
1039 attributes.emplace_back(
1040 "office:target-frame-name",
1041 m_DefaultTarget);
1042 // xlink:show: _blank -> new, any other value -> replace
1043 const char* show = m_DefaultTarget == "_blank" ? "new" : "replace";
1044 attributes.emplace_back(
1045 "xlink:show",
1046 OUString::createFromAscii(show));
1047 updateElement(g, u"meta:hyperlink-behaviour"_ustr, &attributes);
1048 } else {
1049 updateElement(g, u"meta:hyperlink-behaviour"_ustr);
1050 }
1051 attributes.clear();
1052 }
1053
1054 // create empty DOM tree (XDocument)
1055 css::uno::Reference<css::xml::dom::XDocument>
createDOM() const1056 SfxDocumentMetaData::createDOM() const // throw (css::uno::RuntimeException)
1057 {
1058 css::uno::Reference<css::xml::dom::XDocumentBuilder> xBuilder( css::xml::dom::DocumentBuilder::create(m_xContext) );
1059 css::uno::Reference<css::xml::dom::XDocument> xDoc = xBuilder->newDocument();
1060 if (!xDoc.is())
1061 throw css::uno::RuntimeException(
1062 u"SfxDocumentMetaData::createDOM: cannot create new document"_ustr,
1063 *const_cast<SfxDocumentMetaData*>(this));
1064 return xDoc;
1065 }
1066
1067 void
checkInit(std::unique_lock<std::mutex> &) const1068 SfxDocumentMetaData::checkInit(std::unique_lock<std::mutex>& /*rGuard*/) const // throw (css::uno::RuntimeException)
1069 {
1070 if (!m_isInitialized) {
1071 throw css::uno::RuntimeException(
1072 u"SfxDocumentMetaData::checkInit: not initialized"_ustr,
1073 *const_cast<SfxDocumentMetaData*>(this));
1074 }
1075 assert(m_xDoc.is() && m_xParent.is());
1076 }
1077
extractTagAndNamespaceUri(std::u16string_view aChildNodeName,std::u16string_view & rTagName,std::u16string_view & rNamespaceURI)1078 void extractTagAndNamespaceUri(std::u16string_view aChildNodeName,
1079 std::u16string_view& rTagName, std::u16string_view& rNamespaceURI)
1080 {
1081 size_t idx = aChildNodeName.find(':');
1082 assert(idx != std::u16string_view::npos);
1083 std::u16string_view aPrefix = aChildNodeName.substr(0, idx);
1084 rTagName = aChildNodeName.substr(idx + 1);
1085 if (aPrefix == u"dc")
1086 rNamespaceURI = s_nsDC;
1087 else if (aPrefix == u"meta")
1088 rNamespaceURI = s_nsODFMeta;
1089 else if (aPrefix == u"office")
1090 rNamespaceURI = s_nsODF;
1091 else
1092 assert(false);
1093 }
1094
1095
getChildNodeByName(const css::uno::Reference<css::xml::dom::XNode> & xNode,std::u16string_view aChildNodeName)1096 css::uno::Reference<css::xml::dom::XElement> getChildNodeByName(
1097 const css::uno::Reference<css::xml::dom::XNode>& xNode,
1098 std::u16string_view aChildNodeName)
1099 {
1100 css::uno::Reference< css::xml::dom::XNodeList > xList = xNode->getChildNodes();
1101 if (!xList)
1102 return nullptr;
1103 std::u16string_view aTagName, aNamespaceURI;
1104 extractTagAndNamespaceUri(aChildNodeName, aTagName, aNamespaceURI);
1105
1106 const sal_Int32 nLength(xList->getLength());
1107 for (sal_Int32 a(0); a < nLength; a++)
1108 {
1109 const css::uno::Reference< css::xml::dom::XElement > xChild(xList->item(a), css::uno::UNO_QUERY);
1110 if (xChild && xChild->getNodeName() == aTagName && aNamespaceURI == xChild->getNamespaceURI())
1111 return xChild;
1112 }
1113 return nullptr;
1114 }
1115
1116
getChildNodeListByName(const css::uno::Reference<css::xml::dom::XNode> & xNode,std::u16string_view aChildNodeName)1117 std::vector<css::uno::Reference<css::xml::dom::XNode> > getChildNodeListByName(
1118 const css::uno::Reference<css::xml::dom::XNode>& xNode,
1119 std::u16string_view aChildNodeName)
1120 {
1121 css::uno::Reference< css::xml::dom::XNodeList > xList = xNode->getChildNodes();
1122 if (!xList)
1123 return {};
1124 std::u16string_view aTagName, aNamespaceURI;
1125 extractTagAndNamespaceUri(aChildNodeName, aTagName, aNamespaceURI);
1126 std::vector<css::uno::Reference<css::xml::dom::XNode>> aList;
1127 const sal_Int32 nLength(xList->getLength());
1128 for (sal_Int32 a(0); a < nLength; a++)
1129 {
1130 const css::uno::Reference< css::xml::dom::XElement > xChild(xList->item(a), css::uno::UNO_QUERY);
1131 if (xChild && xChild->getNodeName() == aTagName && aNamespaceURI == xChild->getNamespaceURI())
1132 aList.push_back(xChild);
1133 }
1134 return aList;
1135 }
1136
1137 // initialize state from DOM tree
init(std::unique_lock<std::mutex> & g,const css::uno::Reference<css::xml::dom::XDocument> & i_xDoc)1138 void SfxDocumentMetaData::init(
1139 std::unique_lock<std::mutex>& g,
1140 const css::uno::Reference<css::xml::dom::XDocument>& i_xDoc)
1141 {
1142 if (!i_xDoc.is())
1143 throw css::uno::RuntimeException(u"SfxDocumentMetaData::init: no DOM tree given"_ustr, *this);
1144
1145 m_isInitialized = false;
1146 m_xDoc = i_xDoc;
1147
1148 // select nodes for standard meta data stuff
1149 // NB: we do not handle the single-XML-file ODF variant, which would
1150 // have the root element office:document.
1151 // The root of such documents must be converted in the importer!
1152 css::uno::Reference<css::xml::dom::XNode> xDocNode(
1153 m_xDoc, css::uno::UNO_QUERY_THROW);
1154 m_xParent.clear();
1155 try {
1156 css::uno::Reference<css::xml::dom::XNode> xChild = getChildNodeByName(xDocNode, u"office:document-meta");
1157 if (xChild)
1158 m_xParent = getChildNodeByName(xChild, u"office:meta");
1159 } catch (const css::uno::Exception &) {
1160 }
1161
1162 if (!m_xParent.is()) {
1163 // all this create/append stuff may throw DOMException
1164 try {
1165 css::uno::Reference<css::xml::dom::XElement> xRElem;
1166 css::uno::Reference<css::xml::dom::XNode> xNode(
1167 i_xDoc->getFirstChild());
1168 while (xNode.is()) {
1169 if (css::xml::dom::NodeType_ELEMENT_NODE ==xNode->getNodeType())
1170 {
1171 if ( xNode->getNamespaceURI() == s_nsODF && xNode->getLocalName() == "document-meta" )
1172 {
1173 xRElem.set(xNode, css::uno::UNO_QUERY_THROW);
1174 break;
1175 }
1176 else
1177 {
1178 SAL_INFO("sfx.doc", "SfxDocumentMetaData::init(): "
1179 "deleting unexpected root element: "
1180 << xNode->getLocalName());
1181 i_xDoc->removeChild(xNode);
1182 xNode = i_xDoc->getFirstChild(); // start over
1183 }
1184 } else {
1185 xNode = xNode->getNextSibling();
1186 }
1187 }
1188 if (!xRElem.is()) {
1189 static constexpr OUStringLiteral sOfficeDocumentMeta = u"office:document-meta";
1190 xRElem = i_xDoc->createElementNS(
1191 s_nsODF, sOfficeDocumentMeta);
1192 css::uno::Reference<css::xml::dom::XNode> xRNode(xRElem,
1193 css::uno::UNO_QUERY_THROW);
1194 i_xDoc->appendChild(xRNode);
1195 }
1196 static constexpr OUStringLiteral sOfficeVersion = u"office:version";
1197 xRElem->setAttributeNS(s_nsODF, sOfficeVersion, u"1.0"_ustr);
1198 // does not exist, otherwise m_xParent would not be null
1199 static constexpr OUStringLiteral sOfficeMeta = u"office:meta";
1200 css::uno::Reference<css::xml::dom::XNode> xParent (
1201 i_xDoc->createElementNS(s_nsODF, sOfficeMeta),
1202 css::uno::UNO_QUERY_THROW);
1203 xRElem->appendChild(xParent);
1204 m_xParent = std::move(xParent);
1205 } catch (const css::xml::dom::DOMException &) {
1206 css::uno::Any anyEx = cppu::getCaughtException();
1207 throw css::lang::WrappedTargetRuntimeException(
1208 u"SfxDocumentMetaData::init: DOM exception"_ustr,
1209 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
1210 }
1211 }
1212
1213
1214 // select nodes for elements of which we only handle one occurrence
1215 for (const char* const* pName = s_stdMeta; *pName != nullptr; ++pName) {
1216 OUString name = OUString::createFromAscii(*pName);
1217 // NB: If a document contains more than one occurrence of a
1218 // meta-data element, we arbitrarily pick one of them here.
1219 // We do not remove the others, i.e., when we write the
1220 // document, it will contain the duplicates unchanged.
1221 // The ODF spec says that handling multiple occurrences is
1222 // application-specific.
1223
1224 // Do not create an empty element if it is missing;
1225 // for certain elements, such as dateTime, this would be invalid
1226 m_meta[name] = getChildNodeByName(m_xParent, name);
1227 }
1228
1229 // select nodes for elements of which we handle all occurrences
1230 for (const auto & name : s_stdMetaList) {
1231 m_metaList[name] = getChildNodeListByName(m_xParent, name);
1232 }
1233
1234 // initialize members corresponding to attributes from DOM nodes
1235 static constexpr OUString sMetaTemplate = u"meta:template"_ustr;
1236 static constexpr OUString sMetaAutoReload = u"meta:auto-reload"_ustr;
1237 static constexpr OUStringLiteral sMetaHyperlinkBehaviour = u"meta:hyperlink-behaviour";
1238 m_TemplateName = getMetaAttr(g, sMetaTemplate, u"xlink:title"_ustr);
1239 m_TemplateURL = getMetaAttr(g, sMetaTemplate, u"xlink:href"_ustr);
1240 m_TemplateDate =
1241 textToDateTimeDefault(getMetaAttr(g, sMetaTemplate, u"meta:date"_ustr));
1242 m_AutoloadURL = getMetaAttr(g, sMetaAutoReload, u"xlink:href"_ustr);
1243 m_AutoloadSecs =
1244 textToDuration(getMetaAttr(g, sMetaAutoReload, u"meta:delay"_ustr));
1245 m_DefaultTarget =
1246 getMetaAttr(g, sMetaHyperlinkBehaviour, u"office:target-frame-name"_ustr);
1247
1248
1249 std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
1250 m_metaList[u"meta:user-defined"_ustr];
1251 m_xUserDefined.clear(); // #i105826#: reset (may be re-initialization)
1252 if ( !vec.empty() )
1253 {
1254 createUserDefined(g);
1255 }
1256
1257 // user-defined meta data: initialize PropertySet from DOM nodes
1258 for (auto const& elem : vec)
1259 {
1260 css::uno::Reference<css::xml::dom::XElement> xElem(elem,
1261 css::uno::UNO_QUERY_THROW);
1262 css::uno::Any any;
1263 OUString name = xElem->getAttributeNS(s_nsODFMeta, u"name"_ustr);
1264 OUString type = xElem->getAttributeNS(s_nsODFMeta, u"value-type"_ustr);
1265 OUString text = getNodeText(elem);
1266 if ( type == "float" ) {
1267 double d;
1268 if (::sax::Converter::convertDouble(d, text)) {
1269 any <<= d;
1270 } else {
1271 SAL_WARN("sfx.doc", "Invalid float: " << text);
1272 continue;
1273 }
1274 } else if ( type == "date" ) {
1275 bool isDateTime;
1276 css::util::Date d;
1277 css::util::DateTime dt;
1278 std::optional<sal_Int16> nTimeZone;
1279 if (textToDateOrDateTime(d, dt, isDateTime, nTimeZone, text)) {
1280 if (isDateTime) {
1281 if (nTimeZone) {
1282 any <<= css::util::DateTimeWithTimezone(dt,
1283 *nTimeZone);
1284 } else {
1285 any <<= dt;
1286 }
1287 } else {
1288 if (nTimeZone) {
1289 any <<= css::util::DateWithTimezone(d, *nTimeZone);
1290 } else {
1291 any <<= d;
1292 }
1293 }
1294 } else {
1295 SAL_WARN("sfx.doc", "Invalid date: " << text);
1296 continue;
1297 }
1298 } else if ( type == "time" ) {
1299 css::util::Duration ud;
1300 if (textToDuration(ud, text)) {
1301 any <<= ud;
1302 } else {
1303 SAL_WARN("sfx.doc", "Invalid time: " << text);
1304 continue;
1305 }
1306 } else if ( type == "boolean" ) {
1307 bool b;
1308 if (::sax::Converter::convertBool(b, text)) {
1309 any <<= b;
1310 } else {
1311 SAL_WARN("sfx.doc", "Invalid boolean: " << text);
1312 continue;
1313 }
1314 } else { // default
1315 any <<= text;
1316 }
1317 try {
1318 m_xUserDefined->addProperty(name,
1319 css::beans::PropertyAttribute::REMOVABLE, any);
1320 } catch (const css::beans::PropertyExistException &) {
1321 SAL_WARN("sfx.doc", "Duplicate: " << name);
1322 // ignore; duplicate
1323 } catch (const css::beans::IllegalTypeException &) {
1324 SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal type: " << name);
1325 } catch (const css::lang::IllegalArgumentException &) {
1326 SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal arg: " << name);
1327 }
1328 }
1329
1330 m_isModified = false;
1331 m_isInitialized = true;
1332 }
1333
1334
SfxDocumentMetaData(css::uno::Reference<css::uno::XComponentContext> const & context)1335 SfxDocumentMetaData::SfxDocumentMetaData(
1336 css::uno::Reference< css::uno::XComponentContext > const & context)
1337 : m_xContext(context)
1338 , m_isInitialized(false)
1339 , m_isModified(false)
1340 , m_AutoloadSecs(0)
1341 {
1342 assert(context.is());
1343 assert(context->getServiceManager().is());
1344 std::unique_lock g(m_aMutex);
1345 init(g, createDOM());
1346 }
1347
1348 // com.sun.star.uno.XServiceInfo:
1349 OUString SAL_CALL
getImplementationName()1350 SfxDocumentMetaData::getImplementationName()
1351 {
1352 return u"SfxDocumentMetaData"_ustr;
1353 }
1354
1355 sal_Bool SAL_CALL
supportsService(OUString const & serviceName)1356 SfxDocumentMetaData::supportsService(OUString const & serviceName)
1357 {
1358 return cppu::supportsService(this, serviceName);
1359 }
1360
1361 css::uno::Sequence< OUString > SAL_CALL
getSupportedServiceNames()1362 SfxDocumentMetaData::getSupportedServiceNames()
1363 {
1364 css::uno::Sequence< OUString > s { u"com.sun.star.document.DocumentProperties"_ustr };
1365 return s;
1366 }
1367
1368
1369 // css::lang::XComponent:
disposing(std::unique_lock<std::mutex> & rGuard)1370 void SfxDocumentMetaData::disposing(std::unique_lock<std::mutex>& rGuard)
1371 {
1372 m_NotifyListeners.disposeAndClear(rGuard, css::lang::EventObject(
1373 getXWeak()));
1374 m_isInitialized = false;
1375 m_meta.clear();
1376 m_metaList.clear();
1377 m_xParent.clear();
1378 m_xDoc.clear();
1379 m_xUserDefined.clear();
1380 }
1381
1382
1383 // css::document::XDocumentProperties:
1384 OUString SAL_CALL
getAuthor()1385 SfxDocumentMetaData::getAuthor()
1386 {
1387 std::unique_lock g(m_aMutex);
1388 return getMetaText(g, "meta:initial-creator");
1389 }
1390
setAuthor(const OUString & the_value)1391 void SAL_CALL SfxDocumentMetaData::setAuthor(const OUString & the_value)
1392 {
1393 setMetaTextAndNotify(u"meta:initial-creator"_ustr, the_value);
1394 }
1395
1396
1397 OUString SAL_CALL
getGenerator()1398 SfxDocumentMetaData::getGenerator()
1399 {
1400 std::unique_lock g(m_aMutex);
1401 return getMetaText(g, "meta:generator");
1402 }
1403
1404 void SAL_CALL
setGenerator(const OUString & the_value)1405 SfxDocumentMetaData::setGenerator(const OUString & the_value)
1406 {
1407 setMetaTextAndNotify(u"meta:generator"_ustr, the_value);
1408 }
1409
1410 css::util::DateTime SAL_CALL
getCreationDate()1411 SfxDocumentMetaData::getCreationDate()
1412 {
1413 std::unique_lock g(m_aMutex);
1414 return textToDateTimeDefault(getMetaText(g, "meta:creation-date"));
1415 }
1416
1417 void SAL_CALL
setCreationDate(const css::util::DateTime & the_value)1418 SfxDocumentMetaData::setCreationDate(const css::util::DateTime & the_value)
1419 {
1420 setMetaTextAndNotify(u"meta:creation-date"_ustr, dateTimeToText(the_value));
1421 }
1422
1423 OUString SAL_CALL
getTitle()1424 SfxDocumentMetaData::getTitle()
1425 {
1426 std::unique_lock g(m_aMutex);
1427 return getMetaText(g, "dc:title");
1428 }
1429
setTitle(const OUString & the_value)1430 void SAL_CALL SfxDocumentMetaData::setTitle(const OUString & the_value)
1431 {
1432 setMetaTextAndNotify(u"dc:title"_ustr, the_value);
1433 }
1434
1435 OUString SAL_CALL
getSubject()1436 SfxDocumentMetaData::getSubject()
1437 {
1438 std::unique_lock g(m_aMutex);
1439 return getMetaText(g, "dc:subject");
1440 }
1441
1442 void SAL_CALL
setSubject(const OUString & the_value)1443 SfxDocumentMetaData::setSubject(const OUString & the_value)
1444 {
1445 setMetaTextAndNotify(u"dc:subject"_ustr, the_value);
1446 }
1447
1448 OUString SAL_CALL
getDescription()1449 SfxDocumentMetaData::getDescription()
1450 {
1451 std::unique_lock g(m_aMutex);
1452 return getMetaText(g, "dc:description");
1453 }
1454
1455 void SAL_CALL
setDescription(const OUString & the_value)1456 SfxDocumentMetaData::setDescription(const OUString & the_value)
1457 {
1458 setMetaTextAndNotify(u"dc:description"_ustr, the_value);
1459 }
1460
1461 css::uno::Sequence< OUString >
getKeywords()1462 SAL_CALL SfxDocumentMetaData::getKeywords()
1463 {
1464 std::unique_lock g(m_aMutex);
1465 return getMetaList(g, "meta:keyword");
1466 }
1467
1468 void SAL_CALL
setKeywords(const css::uno::Sequence<OUString> & the_value)1469 SfxDocumentMetaData::setKeywords(
1470 const css::uno::Sequence< OUString > & the_value)
1471 {
1472 std::unique_lock g(m_aMutex);
1473 if (setMetaList(g, u"meta:keyword"_ustr, the_value, nullptr)) {
1474 g.unlock();
1475 setModified(true);
1476 }
1477 }
1478
1479 // css::document::XDocumentProperties2
getContributor()1480 css::uno::Sequence<OUString> SAL_CALL SfxDocumentMetaData::getContributor()
1481 {
1482 std::unique_lock g(m_aMutex);
1483 return getMetaList(g, "dc:contributor");
1484 }
1485
setContributor(const css::uno::Sequence<OUString> & the_value)1486 void SAL_CALL SfxDocumentMetaData::setContributor(const css::uno::Sequence<OUString>& the_value)
1487 {
1488 std::unique_lock g(m_aMutex);
1489 if (setMetaList(g, u"dc:contributor"_ustr, the_value, nullptr))
1490 {
1491 g.unlock();
1492 setModified(true);
1493 }
1494 }
1495
getCoverage()1496 OUString SAL_CALL SfxDocumentMetaData::getCoverage()
1497 {
1498 std::unique_lock g(m_aMutex);
1499 return getMetaText(g, "dc:coverage");
1500 }
1501
setCoverage(const OUString & the_value)1502 void SAL_CALL SfxDocumentMetaData::setCoverage(const OUString& the_value)
1503 {
1504 setMetaTextAndNotify(u"dc:coverage"_ustr, the_value);
1505 }
1506
getIdentifier()1507 OUString SAL_CALL SfxDocumentMetaData::getIdentifier()
1508 {
1509 std::unique_lock g(m_aMutex);
1510 return getMetaText(g, "dc:identifier");
1511 }
1512
setIdentifier(const OUString & the_value)1513 void SAL_CALL SfxDocumentMetaData::setIdentifier(const OUString& the_value)
1514 {
1515 setMetaTextAndNotify(u"dc:identifier"_ustr, the_value);
1516 }
1517
getPublisher()1518 css::uno::Sequence<OUString> SAL_CALL SfxDocumentMetaData::getPublisher()
1519 {
1520 std::unique_lock g(m_aMutex);
1521 return getMetaList(g, "dc:publisher");
1522 }
1523
setPublisher(const css::uno::Sequence<OUString> & the_value)1524 void SAL_CALL SfxDocumentMetaData::setPublisher(const css::uno::Sequence<OUString>& the_value)
1525 {
1526 std::unique_lock g(m_aMutex);
1527 if (setMetaList(g, u"dc:publisher"_ustr, the_value, nullptr))
1528 {
1529 g.unlock();
1530 setModified(true);
1531 }
1532 }
1533
getRelation()1534 css::uno::Sequence<OUString> SAL_CALL SfxDocumentMetaData::getRelation()
1535 {
1536 std::unique_lock g(m_aMutex);
1537 return getMetaList(g, "dc:relation");
1538 }
1539
setRelation(const css::uno::Sequence<OUString> & the_value)1540 void SAL_CALL SfxDocumentMetaData::setRelation(const css::uno::Sequence<OUString>& the_value)
1541 {
1542 std::unique_lock g(m_aMutex);
1543 if (setMetaList(g, u"dc:relation"_ustr, the_value, nullptr))
1544 {
1545 g.unlock();
1546 setModified(true);
1547 }
1548 }
1549
getRights()1550 OUString SAL_CALL SfxDocumentMetaData::getRights()
1551 {
1552 std::unique_lock g(m_aMutex);
1553 return getMetaText(g, "dc:rights");
1554 }
1555
setRights(const OUString & the_value)1556 void SAL_CALL SfxDocumentMetaData::setRights(const OUString& the_value)
1557 {
1558 setMetaTextAndNotify(u"dc:rights"_ustr, the_value);
1559 }
1560
getSource()1561 OUString SAL_CALL SfxDocumentMetaData::getSource()
1562 {
1563 std::unique_lock g(m_aMutex);
1564 return getMetaText(g, "dc:source");
1565 }
1566
setSource(const OUString & the_value)1567 void SAL_CALL SfxDocumentMetaData::setSource(const OUString& the_value)
1568 {
1569 setMetaTextAndNotify(u"dc:source"_ustr, the_value);
1570 }
1571
getType()1572 OUString SAL_CALL SfxDocumentMetaData::getType()
1573 {
1574 std::unique_lock g(m_aMutex);
1575 return getMetaText(g, "dc:type");
1576 }
1577
setType(const OUString & the_value)1578 void SAL_CALL SfxDocumentMetaData::setType(const OUString& the_value)
1579 {
1580 setMetaTextAndNotify(u"dc:type"_ustr, the_value);
1581 }
1582
1583 css::lang::Locale SAL_CALL
getLanguage()1584 SfxDocumentMetaData::getLanguage()
1585 {
1586 std::unique_lock g(m_aMutex);
1587 css::lang::Locale loc( LanguageTag::convertToLocale( getMetaText(g, "dc:language"), false));
1588 return loc;
1589 }
1590
1591 void SAL_CALL
setLanguage(const css::lang::Locale & the_value)1592 SfxDocumentMetaData::setLanguage(const css::lang::Locale & the_value)
1593 {
1594 OUString text( LanguageTag::convertToBcp47( the_value, false));
1595 setMetaTextAndNotify(u"dc:language"_ustr, text);
1596 }
1597
1598 OUString SAL_CALL
getModifiedBy()1599 SfxDocumentMetaData::getModifiedBy()
1600 {
1601 std::unique_lock g(m_aMutex);
1602 return getMetaText(g, "dc:creator");
1603 }
1604
1605 void SAL_CALL
setModifiedBy(const OUString & the_value)1606 SfxDocumentMetaData::setModifiedBy(const OUString & the_value)
1607 {
1608 setMetaTextAndNotify(u"dc:creator"_ustr, the_value);
1609 }
1610
1611 css::util::DateTime SAL_CALL
getModificationDate()1612 SfxDocumentMetaData::getModificationDate()
1613 {
1614 std::unique_lock g(m_aMutex);
1615 return textToDateTimeDefault(getMetaText(g, "dc:date"));
1616 }
1617
1618 void SAL_CALL
setModificationDate(const css::util::DateTime & the_value)1619 SfxDocumentMetaData::setModificationDate(const css::util::DateTime & the_value)
1620 {
1621 setMetaTextAndNotify(u"dc:date"_ustr, dateTimeToText(the_value));
1622 }
1623
1624 OUString SAL_CALL
getPrintedBy()1625 SfxDocumentMetaData::getPrintedBy()
1626 {
1627 std::unique_lock g(m_aMutex);
1628 return getMetaText(g, "meta:printed-by");
1629 }
1630
1631 void SAL_CALL
setPrintedBy(const OUString & the_value)1632 SfxDocumentMetaData::setPrintedBy(const OUString & the_value)
1633 {
1634 setMetaTextAndNotify(u"meta:printed-by"_ustr, the_value);
1635 }
1636
1637 css::util::DateTime SAL_CALL
getPrintDate()1638 SfxDocumentMetaData::getPrintDate()
1639 {
1640 std::unique_lock g(m_aMutex);
1641 return textToDateTimeDefault(getMetaText(g, "meta:print-date"));
1642 }
1643
1644 void SAL_CALL
setPrintDate(const css::util::DateTime & the_value)1645 SfxDocumentMetaData::setPrintDate(const css::util::DateTime & the_value)
1646 {
1647 setMetaTextAndNotify(u"meta:print-date"_ustr, dateTimeToText(the_value));
1648 }
1649
1650 OUString SAL_CALL
getTemplateName()1651 SfxDocumentMetaData::getTemplateName()
1652 {
1653 std::unique_lock g(m_aMutex);
1654 checkInit(g);
1655 return m_TemplateName;
1656 }
1657
1658 void SAL_CALL
setTemplateName(const OUString & the_value)1659 SfxDocumentMetaData::setTemplateName(const OUString & the_value)
1660 {
1661 std::unique_lock g(m_aMutex);
1662 checkInit(g);
1663 if (m_TemplateName != the_value) {
1664 m_TemplateName = the_value;
1665 g.unlock();
1666 setModified(true);
1667 }
1668 }
1669
1670 OUString SAL_CALL
getTemplateURL()1671 SfxDocumentMetaData::getTemplateURL()
1672 {
1673 std::unique_lock g(m_aMutex);
1674 checkInit(g);
1675 return m_TemplateURL;
1676 }
1677
1678 void SAL_CALL
setTemplateURL(const OUString & the_value)1679 SfxDocumentMetaData::setTemplateURL(const OUString & the_value)
1680 {
1681 std::unique_lock g(m_aMutex);
1682 checkInit(g);
1683 if (m_TemplateURL != the_value) {
1684 m_TemplateURL = the_value;
1685 g.unlock();
1686 setModified(true);
1687 }
1688 }
1689
1690 css::util::DateTime SAL_CALL
getTemplateDate()1691 SfxDocumentMetaData::getTemplateDate()
1692 {
1693 std::unique_lock g(m_aMutex);
1694 checkInit(g);
1695 return m_TemplateDate;
1696 }
1697
1698 void SAL_CALL
setTemplateDate(const css::util::DateTime & the_value)1699 SfxDocumentMetaData::setTemplateDate(const css::util::DateTime & the_value)
1700 {
1701 std::unique_lock g(m_aMutex);
1702 checkInit(g);
1703 if (m_TemplateDate != the_value) {
1704 m_TemplateDate = the_value;
1705 g.unlock();
1706 setModified(true);
1707 }
1708 }
1709
1710 OUString SAL_CALL
getAutoloadURL()1711 SfxDocumentMetaData::getAutoloadURL()
1712 {
1713 std::unique_lock g(m_aMutex);
1714 checkInit(g);
1715 return m_AutoloadURL;
1716 }
1717
1718 void SAL_CALL
setAutoloadURL(const OUString & the_value)1719 SfxDocumentMetaData::setAutoloadURL(const OUString & the_value)
1720 {
1721 std::unique_lock g(m_aMutex);
1722 checkInit(g);
1723 if (m_AutoloadURL != the_value) {
1724 m_AutoloadURL = the_value;
1725 g.unlock();
1726 setModified(true);
1727 }
1728 }
1729
1730 ::sal_Int32 SAL_CALL
getAutoloadSecs()1731 SfxDocumentMetaData::getAutoloadSecs()
1732 {
1733 std::unique_lock g(m_aMutex);
1734 checkInit(g);
1735 return m_AutoloadSecs;
1736 }
1737
1738 void SAL_CALL
setAutoloadSecs(::sal_Int32 the_value)1739 SfxDocumentMetaData::setAutoloadSecs(::sal_Int32 the_value)
1740 {
1741 if (the_value < 0)
1742 throw css::lang::IllegalArgumentException(
1743 u"SfxDocumentMetaData::setAutoloadSecs: argument is negative"_ustr,
1744 *this, 0);
1745 std::unique_lock g(m_aMutex);
1746 checkInit(g);
1747 if (m_AutoloadSecs != the_value) {
1748 m_AutoloadSecs = the_value;
1749 g.unlock();
1750 setModified(true);
1751 }
1752 }
1753
1754 OUString SAL_CALL
getDefaultTarget()1755 SfxDocumentMetaData::getDefaultTarget()
1756 {
1757 std::unique_lock g(m_aMutex);
1758 checkInit(g);
1759 return m_DefaultTarget;
1760 }
1761
1762 void SAL_CALL
setDefaultTarget(const OUString & the_value)1763 SfxDocumentMetaData::setDefaultTarget(const OUString & the_value)
1764 {
1765 std::unique_lock g(m_aMutex);
1766 checkInit(g);
1767 if (m_DefaultTarget != the_value) {
1768 m_DefaultTarget = the_value;
1769 g.unlock();
1770 setModified(true);
1771 }
1772 }
1773
1774 css::uno::Sequence< css::beans::NamedValue > SAL_CALL
getDocumentStatistics()1775 SfxDocumentMetaData::getDocumentStatistics()
1776 {
1777 std::unique_lock g(m_aMutex);
1778 checkInit(g);
1779 ::std::vector<css::beans::NamedValue> stats;
1780 for (size_t i = 0; s_stdStats[i] != nullptr; ++i) {
1781 OUString text = getMetaAttr(g, u"meta:document-statistic"_ustr, s_stdStatAttrs[i]);
1782 if (text.isEmpty()) continue;
1783 css::beans::NamedValue stat;
1784 stat.Name = OUString::createFromAscii(s_stdStats[i]);
1785 sal_Int32 val;
1786 if (!::sax::Converter::convertNumber(val, text, 0) || (val < 0)) {
1787 val = 0;
1788 SAL_WARN("sfx.doc", "Invalid number: " << text);
1789 }
1790 stat.Value <<= val;
1791 stats.push_back(stat);
1792 }
1793
1794 return ::comphelper::containerToSequence(stats);
1795 }
1796
1797 void SAL_CALL
setDocumentStatistics(const css::uno::Sequence<css::beans::NamedValue> & the_value)1798 SfxDocumentMetaData::setDocumentStatistics(
1799 const css::uno::Sequence< css::beans::NamedValue > & the_value)
1800 {
1801 {
1802 std::unique_lock g(m_aMutex);
1803 checkInit(g);
1804 std::vector<std::pair<OUString, OUString> > attributes;
1805 for (const auto& rValue : the_value) {
1806 const OUString name = rValue.Name;
1807 // inefficiently search for matching attribute
1808 for (size_t j = 0; s_stdStats[j] != nullptr; ++j) {
1809 if (name.equalsAscii(s_stdStats[j])) {
1810 const css::uno::Any any = rValue.Value;
1811 sal_Int32 val = 0;
1812 if (any >>= val) {
1813 attributes.emplace_back(s_stdStatAttrs[j],
1814 OUString::number(val));
1815 }
1816 else {
1817 SAL_WARN("sfx.doc", "Invalid statistic: " << name);
1818 }
1819 break;
1820 }
1821 }
1822 }
1823 updateElement(g, u"meta:document-statistic"_ustr, &attributes);
1824 }
1825 setModified(true);
1826 }
1827
1828 ::sal_Int16 SAL_CALL
getEditingCycles()1829 SfxDocumentMetaData::getEditingCycles()
1830 {
1831 std::unique_lock g(m_aMutex);
1832 OUString text = getMetaText(g, "meta:editing-cycles");
1833 sal_Int32 ret;
1834 if (::sax::Converter::convertNumber(ret, text,
1835 0, std::numeric_limits<sal_Int16>::max())) {
1836 return static_cast<sal_Int16>(ret);
1837 } else {
1838 return 0;
1839 }
1840 }
1841
1842 void SAL_CALL
setEditingCycles(::sal_Int16 the_value)1843 SfxDocumentMetaData::setEditingCycles(::sal_Int16 the_value)
1844 {
1845 if (the_value < 0)
1846 throw css::lang::IllegalArgumentException(
1847 u"SfxDocumentMetaData::setEditingCycles: argument is negative"_ustr,
1848 *this, 0);
1849 setMetaTextAndNotify(u"meta:editing-cycles"_ustr, OUString::number(the_value));
1850 }
1851
1852 ::sal_Int32 SAL_CALL
getEditingDuration()1853 SfxDocumentMetaData::getEditingDuration()
1854 {
1855 std::unique_lock g(m_aMutex);
1856 return textToDuration(getMetaText(g, "meta:editing-duration"));
1857 }
1858
1859 void SAL_CALL
setEditingDuration(::sal_Int32 the_value)1860 SfxDocumentMetaData::setEditingDuration(::sal_Int32 the_value)
1861 {
1862 if (the_value < 0)
1863 throw css::lang::IllegalArgumentException(
1864 u"SfxDocumentMetaData::setEditingDuration: argument is negative"_ustr,
1865 *this, 0);
1866 setMetaTextAndNotify(u"meta:editing-duration"_ustr, durationToText(the_value));
1867 }
1868
1869 void SAL_CALL
resetUserData(const OUString & the_value)1870 SfxDocumentMetaData::resetUserData(const OUString & the_value)
1871 {
1872 std::unique_lock g(m_aMutex);
1873
1874 bool bModified( false );
1875 bModified |= setMetaText(g, u"meta:initial-creator"_ustr, the_value);
1876 ::DateTime now( ::DateTime::SYSTEM );
1877 css::util::DateTime uDT(now.GetUNODateTime());
1878 bModified |= setMetaText(g, u"meta:creation-date"_ustr, dateTimeToText(uDT));
1879 bModified |= setMetaText(g, u"dc:creator"_ustr, OUString());
1880 bModified |= setMetaText(g, u"meta:printed-by"_ustr, OUString());
1881 bModified |= setMetaText(g, u"dc:date"_ustr, dateTimeToText(css::util::DateTime()));
1882 bModified |= setMetaText(g, u"meta:print-date"_ustr,
1883 dateTimeToText(css::util::DateTime()));
1884 bModified |= setMetaText(g, u"meta:editing-duration"_ustr, durationToText(0));
1885 bModified |= setMetaText(g, u"meta:editing-cycles"_ustr,
1886 u"1"_ustr);
1887
1888 if (bModified) {
1889 g.unlock();
1890 setModified(true);
1891 }
1892 }
1893
1894
1895 css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
getUserDefinedProperties()1896 SfxDocumentMetaData::getUserDefinedProperties()
1897 {
1898 std::unique_lock g(m_aMutex);
1899 checkInit(g);
1900 createUserDefined(g);
1901 return m_xUserDefined;
1902 }
1903
1904
1905 void SAL_CALL
loadFromStorage(const css::uno::Reference<css::embed::XStorage> & xStorage,const css::uno::Sequence<css::beans::PropertyValue> & Medium)1906 SfxDocumentMetaData::loadFromStorage(
1907 const css::uno::Reference< css::embed::XStorage > & xStorage,
1908 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1909 {
1910 if (!xStorage.is())
1911 throw css::lang::IllegalArgumentException(u"SfxDocumentMetaData::loadFromStorage: argument is null"_ustr, *this, 0);
1912 std::unique_lock g(m_aMutex);
1913
1914 // open meta data file
1915 css::uno::Reference<css::io::XStream> xStream(
1916 xStorage->openStreamElement(
1917 s_meta,
1918 css::embed::ElementModes::READ) );
1919 if (!xStream.is()) throw css::uno::RuntimeException();
1920 css::uno::Reference<css::io::XInputStream> xInStream =
1921 xStream->getInputStream();
1922 if (!xInStream.is()) throw css::uno::RuntimeException();
1923
1924 // create DOM parser service
1925 css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
1926 m_xContext->getServiceManager());
1927 css::xml::sax::InputSource input;
1928 input.aInputStream = std::move(xInStream);
1929
1930 sal_uInt64 version = SotStorage::GetVersion( xStorage );
1931 // Oasis is also the default (0)
1932 bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
1933 const char *pServiceName = bOasis
1934 ? "com.sun.star.document.XMLOasisMetaImporter"
1935 : "com.sun.star.document.XMLMetaImporter";
1936
1937 // set base URL
1938 css::uno::Reference<css::beans::XPropertySet> xPropArg =
1939 getURLProperties(g, Medium);
1940 try {
1941 xPropArg->getPropertyValue(u"BaseURI"_ustr)
1942 >>= input.sSystemId;
1943 input.sSystemId += OUString::Concat("/") + s_meta;
1944 } catch (const css::uno::Exception &) {
1945 input.sSystemId = s_meta;
1946 }
1947 css::uno::Sequence< css::uno::Any > args{ css::uno::Any(xPropArg) };
1948
1949 // the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler
1950 css::uno::Reference<XInterface> xFilter =
1951 xMsf->createInstanceWithArgumentsAndContext(
1952 OUString::createFromAscii(pServiceName), args, m_xContext);
1953 assert(xFilter);
1954 css::uno::Reference<css::xml::sax::XFastParser> xFastParser(xFilter, css::uno::UNO_QUERY);
1955 css::uno::Reference<css::document::XImporter> xImp(xFilter, css::uno::UNO_QUERY_THROW);
1956 xImp->setTargetDocument(css::uno::Reference<css::lang::XComponent>(this));
1957 g.unlock(); // NB: the implementation of XMLOasisMetaImporter calls initialize
1958 try {
1959 if (xFastParser)
1960 xFastParser->parseStream(input);
1961 else
1962 {
1963 css::uno::Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, css::uno::UNO_QUERY_THROW);
1964 css::uno::Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(m_xContext);
1965 xParser->setDocumentHandler(xDocHandler);
1966 xParser->parseStream(input);
1967 }
1968 } catch (const css::xml::sax::SAXException &) {
1969 throw css::io::WrongFormatException(
1970 u"SfxDocumentMetaData::loadFromStorage:"
1971 " XML parsing exception"_ustr, *this);
1972 }
1973 g.lock();
1974 // NB: the implementation of XMLOasisMetaImporter calls initialize
1975 checkInit(g);
1976 }
1977
1978 void SAL_CALL
storeToStorage(const css::uno::Reference<css::embed::XStorage> & xStorage,const css::uno::Sequence<css::beans::PropertyValue> & Medium)1979 SfxDocumentMetaData::storeToStorage(
1980 const css::uno::Reference< css::embed::XStorage > & xStorage,
1981 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1982 {
1983 if (!xStorage.is())
1984 throw css::lang::IllegalArgumentException(
1985 u"SfxDocumentMetaData::storeToStorage: argument is null"_ustr, *this, 0);
1986 std::unique_lock g(m_aMutex);
1987 checkInit(g);
1988
1989 // update user-defined meta data in DOM tree
1990 // updateUserDefinedAndAttributes(); // this will be done in serialize!
1991
1992 // write into storage
1993 css::uno::Reference<css::io::XStream> xStream =
1994 xStorage->openStreamElement(s_meta,
1995 css::embed::ElementModes::WRITE
1996 | css::embed::ElementModes::TRUNCATE);
1997 if (!xStream.is()) throw css::uno::RuntimeException();
1998 css::uno::Reference< css::beans::XPropertySet > xStreamProps(xStream,
1999 css::uno::UNO_QUERY_THROW);
2000 xStreamProps->setPropertyValue(
2001 u"MediaType"_ustr,
2002 css::uno::Any(u"text/xml"_ustr));
2003 xStreamProps->setPropertyValue(
2004 u"Compressed"_ustr,
2005 css::uno::Any(false));
2006 xStreamProps->setPropertyValue(
2007 u"UseCommonStoragePasswordEncryption"_ustr,
2008 css::uno::Any(false));
2009 css::uno::Reference<css::io::XOutputStream> xOutStream =
2010 xStream->getOutputStream();
2011 if (!xOutStream.is()) throw css::uno::RuntimeException();
2012 css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
2013 m_xContext->getServiceManager());
2014 css::uno::Reference<css::xml::sax::XWriter> xSaxWriter(
2015 css::xml::sax::Writer::create(m_xContext));
2016 xSaxWriter->setOutputStream(xOutStream);
2017
2018 const sal_uInt64 version = SotStorage::GetVersion( xStorage );
2019 // Oasis is also the default (0)
2020 const bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
2021 const char *pServiceName = bOasis
2022 ? "com.sun.star.document.XMLOasisMetaExporter"
2023 : "com.sun.star.document.XMLMetaExporter";
2024
2025 // set base URL
2026 css::uno::Reference<css::beans::XPropertySet> xPropArg =
2027 getURLProperties(g, Medium);
2028 css::uno::Sequence< css::uno::Any > args{ css::uno::Any(xSaxWriter), css::uno::Any(xPropArg) };
2029
2030 css::uno::Reference<css::document::XExporter> xExp(
2031 xMsf->createInstanceWithArgumentsAndContext(
2032 OUString::createFromAscii(pServiceName), args, m_xContext),
2033 css::uno::UNO_QUERY_THROW);
2034 xExp->setSourceDocument(css::uno::Reference<css::lang::XComponent>(this));
2035 css::uno::Reference<css::document::XFilter> xFilter(xExp,
2036 css::uno::UNO_QUERY_THROW);
2037 g.unlock(); // filter calls back into this
2038 if (!xFilter->filter(css::uno::Sequence< css::beans::PropertyValue >())) {
2039 throw css::io::IOException(
2040 u"SfxDocumentMetaData::storeToStorage: cannot filter"_ustr, *this);
2041 }
2042 css::uno::Reference<css::embed::XTransactedObject> xTransaction(
2043 xStorage, css::uno::UNO_QUERY);
2044 if (xTransaction.is()) {
2045 xTransaction->commit();
2046 }
2047 }
2048
2049 void SAL_CALL
loadFromMedium(const OUString & URL,const css::uno::Sequence<css::beans::PropertyValue> & Medium)2050 SfxDocumentMetaData::loadFromMedium(const OUString & URL,
2051 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
2052 {
2053 css::uno::Reference<css::io::XInputStream> xIn;
2054 utl::MediaDescriptor md(Medium);
2055 // if we have a URL parameter, it replaces the one in the media descriptor
2056 if (!URL.isEmpty()) {
2057 md[ utl::MediaDescriptor::PROP_URL ] <<= URL;
2058 md[ utl::MediaDescriptor::PROP_READONLY ] <<= true;
2059 }
2060 if (md.addInputStream()) {
2061 md[ utl::MediaDescriptor::PROP_INPUTSTREAM ] >>= xIn;
2062 }
2063 css::uno::Reference<css::embed::XStorage> xStorage;
2064 try {
2065 if (xIn.is()) {
2066 xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
2067 xIn, m_xContext);
2068 } else { // fallback to url parameter
2069 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(
2070 URL, css::embed::ElementModes::READ, m_xContext);
2071 }
2072 } catch (const css::uno::RuntimeException &) {
2073 throw;
2074 } catch (const css::io::IOException &) {
2075 throw;
2076 } catch (const css::uno::Exception &) {
2077 css::uno::Any anyEx = cppu::getCaughtException();
2078 throw css::lang::WrappedTargetException(
2079 u"SfxDocumentMetaData::loadFromMedium: exception"_ustr,
2080 css::uno::Reference<css::uno::XInterface>(*this),
2081 anyEx);
2082 }
2083 if (!xStorage.is()) {
2084 throw css::uno::RuntimeException(
2085 u"SfxDocumentMetaData::loadFromMedium: cannot get Storage"_ustr,
2086 *this);
2087 }
2088 loadFromStorage(xStorage, md.getAsConstPropertyValueList());
2089 }
2090
2091 void SAL_CALL
storeToMedium(const OUString & URL,const css::uno::Sequence<css::beans::PropertyValue> & Medium)2092 SfxDocumentMetaData::storeToMedium(const OUString & URL,
2093 const css::uno::Sequence< css::beans::PropertyValue > & Medium)
2094 {
2095 utl::MediaDescriptor md(Medium);
2096 if (!URL.isEmpty()) {
2097 md[ utl::MediaDescriptor::PROP_URL ] <<= URL;
2098 }
2099 SfxMedium aMedium(md.getAsConstPropertyValueList());
2100 css::uno::Reference<css::embed::XStorage> xStorage
2101 = aMedium.GetOutputStorage();
2102
2103
2104 if (!xStorage.is()) {
2105 throw css::uno::RuntimeException(
2106 u"SfxDocumentMetaData::storeToMedium: cannot get Storage"_ustr,
2107 *this);
2108 }
2109 // set MIME type of the storage
2110 utl::MediaDescriptor::const_iterator iter
2111 = md.find(utl::MediaDescriptor::PROP_MEDIATYPE);
2112 if (iter != md.end()) {
2113 css::uno::Reference< css::beans::XPropertySet > xProps(xStorage,
2114 css::uno::UNO_QUERY_THROW);
2115 xProps->setPropertyValue(
2116 utl::MediaDescriptor::PROP_MEDIATYPE,
2117 iter->second);
2118 }
2119 storeToStorage(xStorage, md.getAsConstPropertyValueList());
2120
2121
2122 const bool bOk = aMedium.Commit();
2123 aMedium.Close();
2124 if ( !bOk ) {
2125 ErrCodeMsg nError = aMedium.GetErrorIgnoreWarning();
2126 if ( nError == ERRCODE_NONE ) {
2127 nError = ERRCODE_IO_GENERAL;
2128 }
2129
2130 throw css::task::ErrorCodeIOException(
2131 "SfxDocumentMetaData::storeToMedium <" + URL + "> Commit failed: " + nError.toString(),
2132 css::uno::Reference< css::uno::XInterface >(), sal_uInt32(nError.GetCode()));
2133
2134 }
2135 }
2136
2137 // css::lang::XInitialization:
initialize(const css::uno::Sequence<css::uno::Any> & aArguments)2138 void SAL_CALL SfxDocumentMetaData::initialize( const css::uno::Sequence< css::uno::Any > & aArguments)
2139 {
2140 // possible arguments:
2141 // - no argument: default initialization (empty DOM)
2142 // - 1 argument, XDocument: initialize with given DOM and empty base URL
2143 // NB: links in document must be absolute
2144
2145 std::unique_lock g(m_aMutex);
2146 css::uno::Reference<css::xml::dom::XDocument> xDoc;
2147
2148 for (sal_Int32 i = 0; i < aArguments.getLength(); ++i) {
2149 const css::uno::Any& any = aArguments[i];
2150 if (!(any >>= xDoc)) {
2151 throw css::lang::IllegalArgumentException(
2152 u"SfxDocumentMetaData::initialize: argument must be XDocument"_ustr,
2153 *this, static_cast<sal_Int16>(i));
2154 }
2155 if (!xDoc.is()) {
2156 throw css::lang::IllegalArgumentException(
2157 u"SfxDocumentMetaData::initialize: argument is null"_ustr,
2158 *this, static_cast<sal_Int16>(i));
2159 }
2160 }
2161
2162 if (!xDoc.is()) {
2163 // For a new document, we create a new DOM tree here.
2164 xDoc = createDOM();
2165 }
2166
2167 init(g, xDoc);
2168 }
2169
2170 // css::util::XCloneable:
2171 css::uno::Reference<css::util::XCloneable> SAL_CALL
createClone()2172 SfxDocumentMetaData::createClone()
2173 {
2174 std::unique_lock g(m_aMutex);
2175 checkInit(g);
2176
2177 rtl::Reference<SfxDocumentMetaData> pNew = createMe(m_xContext);
2178
2179 // NB: do not copy the modification listeners, only DOM
2180 css::uno::Reference<css::xml::dom::XDocument> xDoc = createDOM();
2181 try {
2182 updateUserDefinedAndAttributes(g);
2183 // deep copy of root node
2184 css::uno::Reference<css::xml::dom::XNode> xRoot(
2185 m_xDoc->getDocumentElement(), css::uno::UNO_QUERY_THROW);
2186 css::uno::Reference<css::xml::dom::XNode> xRootNew(
2187 xDoc->importNode(xRoot, true));
2188 xDoc->appendChild(xRootNew);
2189 g.unlock();
2190 std::unique_lock g2(pNew->m_aMutex);
2191 pNew->init(g2, xDoc);
2192 } catch (const css::uno::RuntimeException &) {
2193 throw;
2194 } catch (const css::uno::Exception &) {
2195 css::uno::Any anyEx = cppu::getCaughtException();
2196 throw css::lang::WrappedTargetRuntimeException(
2197 u"SfxDocumentMetaData::createClone: exception"_ustr,
2198 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
2199 }
2200 return css::uno::Reference<css::util::XCloneable> (pNew);
2201 }
2202
2203 // css::util::XModifiable:
isModified()2204 sal_Bool SAL_CALL SfxDocumentMetaData::isModified( )
2205 {
2206 std::unique_lock g(m_aMutex);
2207 checkInit(g);
2208 css::uno::Reference<css::util::XModifiable> xMB(m_xUserDefined,
2209 css::uno::UNO_QUERY);
2210 return m_isModified || (xMB.is() && xMB->isModified());
2211 }
2212
setModified(sal_Bool bModified)2213 void SAL_CALL SfxDocumentMetaData::setModified( sal_Bool bModified )
2214 {
2215 css::uno::Reference<css::util::XModifiable> xMB;
2216 { // do not lock mutex while notifying (#i93514#) to prevent deadlock
2217 std::unique_lock g(m_aMutex);
2218 checkInit(g);
2219 m_isModified = bModified;
2220 if ( !bModified && m_xUserDefined.is() )
2221 {
2222 xMB.set(m_xUserDefined, css::uno::UNO_QUERY);
2223 assert(xMB.is() &&
2224 "SfxDocumentMetaData::setModified: PropertyBag not Modifiable?");
2225 }
2226 }
2227 if (bModified) {
2228 try {
2229 css::uno::Reference<css::uno::XInterface> xThis(*this);
2230 css::lang::EventObject event(xThis);
2231 std::unique_lock g(m_aMutex);
2232 m_NotifyListeners.notifyEach(g, &css::util::XModifyListener::modified,
2233 event);
2234 } catch (const css::uno::RuntimeException &) {
2235 throw;
2236 } catch (const css::uno::Exception &) {
2237 // ignore
2238 TOOLS_WARN_EXCEPTION("sfx.doc", "setModified");
2239 }
2240 } else {
2241 if (xMB.is()) {
2242 xMB->setModified(false);
2243 }
2244 }
2245 }
2246
2247 // css::util::XModifyBroadcaster:
addModifyListener(const css::uno::Reference<css::util::XModifyListener> & xListener)2248 void SAL_CALL SfxDocumentMetaData::addModifyListener(
2249 const css::uno::Reference< css::util::XModifyListener > & xListener)
2250 {
2251 std::unique_lock g(m_aMutex);
2252 checkInit(g);
2253 m_NotifyListeners.addInterface(g, xListener);
2254 css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
2255 css::uno::UNO_QUERY);
2256 if (xMB.is()) {
2257 xMB->addModifyListener(xListener);
2258 }
2259 }
2260
removeModifyListener(const css::uno::Reference<css::util::XModifyListener> & xListener)2261 void SAL_CALL SfxDocumentMetaData::removeModifyListener(
2262 const css::uno::Reference< css::util::XModifyListener > & xListener)
2263 {
2264 std::unique_lock g(m_aMutex);
2265 checkInit(g);
2266 m_NotifyListeners.removeInterface(g, xListener);
2267 css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
2268 css::uno::UNO_QUERY);
2269 if (xMB.is()) {
2270 xMB->removeModifyListener(xListener);
2271 }
2272 }
2273
2274 // css::xml::sax::XSAXSerializable
serialize(const css::uno::Reference<css::xml::sax::XDocumentHandler> & i_xHandler,const css::uno::Sequence<css::beans::StringPair> & i_rNamespaces)2275 void SAL_CALL SfxDocumentMetaData::serialize(
2276 const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
2277 const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces)
2278 {
2279 std::unique_lock g(m_aMutex);
2280 checkInit(g);
2281 updateUserDefinedAndAttributes(g);
2282 css::uno::Reference<css::xml::sax::XSAXSerializable> xSAXable(m_xDoc,
2283 css::uno::UNO_QUERY_THROW);
2284 xSAXable->serialize(i_xHandler, i_rNamespaces);
2285 }
2286
createUserDefined(std::unique_lock<std::mutex> & g)2287 void SfxDocumentMetaData::createUserDefined(std::unique_lock<std::mutex>& g)
2288 {
2289 // user-defined meta data: create PropertyBag which only accepts property
2290 // values of allowed types
2291 if ( m_xUserDefined.is() )
2292 return;
2293
2294 css::uno::Sequence<css::uno::Type> types{
2295 ::cppu::UnoType<bool>::get(),
2296 ::cppu::UnoType< OUString>::get(),
2297 ::cppu::UnoType<css::util::DateTime>::get(),
2298 ::cppu::UnoType<css::util::Date>::get(),
2299 ::cppu::UnoType<css::util::DateTimeWithTimezone>::get(),
2300 ::cppu::UnoType<css::util::DateWithTimezone>::get(),
2301 ::cppu::UnoType<css::util::Duration>::get(),
2302 ::cppu::UnoType<float>::get(),
2303 ::cppu::UnoType<double>::get(),
2304 ::cppu::UnoType<sal_Int16>::get(),
2305 ::cppu::UnoType<sal_Int32>::get(),
2306 ::cppu::UnoType<sal_Int64>::get(),
2307 // Time is supported for backward compatibility with OOo 3.x, x<=2
2308 ::cppu::UnoType<css::util::Time>::get()
2309 };
2310 // #i94175#: ODF allows empty user-defined property names!
2311 m_xUserDefined.set(
2312 css::beans::PropertyBag::createWithTypes( m_xContext, types, true/*AllowEmptyPropertyName*/, false/*AutomaticAddition*/ ),
2313 css::uno::UNO_QUERY_THROW);
2314
2315 const css::uno::Reference<css::util::XModifyBroadcaster> xMB(
2316 m_xUserDefined, css::uno::UNO_QUERY);
2317 if (xMB.is())
2318 {
2319 m_NotifyListeners.forEach(g,
2320 [xMB] (const css::uno::Reference<css::util::XModifyListener>& l)
2321 {
2322 xMB->addModifyListener(l);
2323 });
2324 }
2325 }
2326
2327 } // closing anonymous implementation namespace
2328
2329 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
CompatWriterDocPropsImpl_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)2330 CompatWriterDocPropsImpl_get_implementation(
2331 css::uno::XComponentContext *context,
2332 css::uno::Sequence<css::uno::Any> const &)
2333 {
2334 return cppu::acquire(new CompatWriterDocPropsImpl(context));
2335 }
2336
2337 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
SfxDocumentMetaData_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)2338 SfxDocumentMetaData_get_implementation(
2339 css::uno::XComponentContext *context,
2340 css::uno::Sequence<css::uno::Any> const &)
2341 {
2342 return cppu::acquire(new SfxDocumentMetaData(context));
2343 }
2344
2345 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2346