1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/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>
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>
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 <cppuhelper/basemutex.hxx>
71 #include <comphelper/interfacecontainer3.hxx>
72 #include <comphelper/storagehelper.hxx>
73 #include <unotools/mediadescriptor.hxx>
74 #include <comphelper/sequence.hxx>
75 #include <sot/storage.hxx>
76 #include <sfx2/docfile.hxx>
77 #include <sax/tools/converter.hxx>
78 #include <i18nlangtag/languagetag.hxx>
89 #include <cppuhelper/implbase.hxx>
90 #include <cppuhelper/supportsservice.hxx>
91 #include <com/sun/star/document/XCompatWriterDocProperties.hpp>
92 #include <com/sun/star/beans/PropertyBag.hpp>
95 * This file contains the implementation of the service
96 * com.sun.star.document.DocumentProperties.
97 * This service enables access to the meta-data stored in documents.
98 * Currently, this service only handles documents in ODF format.
100 * The implementation uses an XML DOM to store the properties.
101 * This approach was taken because it allows for preserving arbitrary XML data
102 * in loaded documents, which will be stored unmodified when saving the
105 * Upon access, some properties are directly read from and updated in the DOM.
106 * Exception: it seems impossible to get notified upon addition of a property
107 * to a com.sun.star.beans.PropertyBag, which is used for storing user-defined
108 * properties; because of this, user-defined properties are updated in the
109 * XML DOM only when storing the document.
110 * Exception 2: when setting certain properties which correspond to attributes
111 * in the XML DOM, we want to remove the corresponding XML element. Detecting
112 * this condition can get messy, so we store all such properties as members,
113 * and update the DOM tree only when storing the document (in
114 * <method>updateUserDefinedAndAttributes</method>).
118 /// anonymous implementation namespace
121 /// a list of attribute-lists, where attribute means name and content
122 typedef std::vector
<std::vector
<std::pair
<OUString
, OUString
> > >
125 typedef ::cppu::WeakComponentImplHelper
<
126 css::lang::XServiceInfo
,
127 css::document::XDocumentProperties2
,
128 css::lang::XInitialization
,
129 css::util::XCloneable
,
130 css::util::XModifiable
,
131 css::xml::sax::XSAXSerializable
>
132 SfxDocumentMetaData_Base
;
134 class SfxDocumentMetaData
:
135 private ::cppu::BaseMutex
,
136 public SfxDocumentMetaData_Base
139 explicit SfxDocumentMetaData(
140 css::uno::Reference
< css::uno::XComponentContext
> const & context
);
141 SfxDocumentMetaData(const SfxDocumentMetaData
&) = delete;
142 SfxDocumentMetaData
& operator=(const SfxDocumentMetaData
&) = delete;
144 // css::lang::XServiceInfo:
145 virtual OUString SAL_CALL
getImplementationName() override
;
146 virtual sal_Bool SAL_CALL
supportsService(
147 const OUString
& ServiceName
) override
;
148 virtual css::uno::Sequence
< OUString
> SAL_CALL
149 getSupportedServiceNames() override
;
151 // css::lang::XComponent:
152 virtual void SAL_CALL
dispose() override
;
154 // css::document::XDocumentProperties:
155 virtual OUString SAL_CALL
getAuthor() override
;
156 virtual void SAL_CALL
setAuthor(const OUString
& the_value
) override
;
157 virtual OUString SAL_CALL
getGenerator() override
;
158 virtual void SAL_CALL
setGenerator(const OUString
& the_value
) override
;
159 virtual css::util::DateTime SAL_CALL
getCreationDate() override
;
160 virtual void SAL_CALL
setCreationDate(const css::util::DateTime
& the_value
) override
;
161 virtual OUString SAL_CALL
getTitle() override
;
162 virtual void SAL_CALL
setTitle(const OUString
& the_value
) override
;
163 virtual OUString SAL_CALL
getSubject() override
;
164 virtual void SAL_CALL
setSubject(const OUString
& the_value
) override
;
165 virtual OUString SAL_CALL
getDescription() override
;
166 virtual void SAL_CALL
setDescription(const OUString
& the_value
) override
;
167 virtual css::uno::Sequence
< OUString
> SAL_CALL
getKeywords() override
;
168 virtual void SAL_CALL
setKeywords(
169 const css::uno::Sequence
< OUString
> & the_value
) override
;
170 virtual css::lang::Locale SAL_CALL
getLanguage() override
;
171 virtual void SAL_CALL
setLanguage(const css::lang::Locale
& the_value
) override
;
172 virtual OUString SAL_CALL
getModifiedBy() override
;
173 virtual void SAL_CALL
setModifiedBy(const OUString
& the_value
) override
;
174 virtual css::util::DateTime SAL_CALL
getModificationDate() override
;
175 virtual void SAL_CALL
setModificationDate(
176 const css::util::DateTime
& the_value
) override
;
177 virtual OUString SAL_CALL
getPrintedBy() override
;
178 virtual void SAL_CALL
setPrintedBy(const OUString
& the_value
) override
;
179 virtual css::util::DateTime SAL_CALL
getPrintDate() override
;
180 virtual void SAL_CALL
setPrintDate(const css::util::DateTime
& the_value
) override
;
181 virtual OUString SAL_CALL
getTemplateName() override
;
182 virtual void SAL_CALL
setTemplateName(const OUString
& the_value
) override
;
183 virtual OUString SAL_CALL
getTemplateURL() override
;
184 virtual void SAL_CALL
setTemplateURL(const OUString
& the_value
) override
;
185 virtual css::util::DateTime SAL_CALL
getTemplateDate() override
;
186 virtual void SAL_CALL
setTemplateDate(const css::util::DateTime
& the_value
) override
;
187 virtual OUString SAL_CALL
getAutoloadURL() override
;
188 virtual void SAL_CALL
setAutoloadURL(const OUString
& the_value
) override
;
189 virtual ::sal_Int32 SAL_CALL
getAutoloadSecs() override
;
190 virtual void SAL_CALL
setAutoloadSecs(::sal_Int32 the_value
) override
;
191 virtual OUString SAL_CALL
getDefaultTarget() override
;
192 virtual void SAL_CALL
setDefaultTarget(const OUString
& the_value
) override
;
193 virtual css::uno::Sequence
< css::beans::NamedValue
> SAL_CALL
194 getDocumentStatistics() override
;
195 virtual void SAL_CALL
setDocumentStatistics(
196 const css::uno::Sequence
< css::beans::NamedValue
> & the_value
) override
;
197 virtual ::sal_Int16 SAL_CALL
getEditingCycles() override
;
198 virtual void SAL_CALL
setEditingCycles(::sal_Int16 the_value
) override
;
199 virtual ::sal_Int32 SAL_CALL
getEditingDuration() override
;
200 virtual void SAL_CALL
setEditingDuration(::sal_Int32 the_value
) override
;
201 virtual void SAL_CALL
resetUserData(const OUString
& the_value
) override
;
202 virtual css::uno::Reference
< css::beans::XPropertyContainer
> SAL_CALL
203 getUserDefinedProperties() override
;
204 virtual void SAL_CALL
loadFromStorage(
205 const css::uno::Reference
< css::embed::XStorage
> & Storage
,
206 const css::uno::Sequence
< css::beans::PropertyValue
> & Medium
) override
;
207 virtual void SAL_CALL
loadFromMedium(const OUString
& URL
,
208 const css::uno::Sequence
< css::beans::PropertyValue
> & Medium
) override
;
209 virtual void SAL_CALL
storeToStorage(
210 const css::uno::Reference
< css::embed::XStorage
> & Storage
,
211 const css::uno::Sequence
< css::beans::PropertyValue
> & Medium
) override
;
212 virtual void SAL_CALL
storeToMedium(const OUString
& URL
,
213 const css::uno::Sequence
< css::beans::PropertyValue
> & Medium
) override
;
214 virtual css::uno::Sequence
< OUString
> SAL_CALL
getContributor() override
;
215 virtual void SAL_CALL
setContributor(const css::uno::Sequence
< OUString
>& the_value
) override
;
216 virtual OUString SAL_CALL
getCoverage() override
;
217 virtual void SAL_CALL
setCoverage(const OUString
& the_value
) override
;
218 virtual OUString SAL_CALL
getIdentifier() override
;
219 virtual void SAL_CALL
setIdentifier(const OUString
& the_value
) override
;
220 virtual css::uno::Sequence
< OUString
> SAL_CALL
getPublisher() override
;
221 virtual void SAL_CALL
setPublisher(const css::uno::Sequence
< OUString
> & the_value
) override
;
222 virtual css::uno::Sequence
< OUString
> SAL_CALL
getRelation() override
;
223 virtual void SAL_CALL
setRelation(const css::uno::Sequence
< OUString
> & the_value
) override
;
224 virtual OUString SAL_CALL
getRights() override
;
225 virtual void SAL_CALL
setRights(const OUString
& the_value
) override
;
226 virtual OUString SAL_CALL
getSource() override
;
227 virtual void SAL_CALL
setSource(const OUString
& the_value
) override
;
228 virtual OUString SAL_CALL
getType() override
;
229 virtual void SAL_CALL
setType(const OUString
& the_value
) override
;
232 // css::lang::XInitialization:
233 virtual void SAL_CALL
initialize(
234 const css::uno::Sequence
< css::uno::Any
> & aArguments
) override
;
236 // css::util::XCloneable:
237 virtual css::uno::Reference
<css::util::XCloneable
> SAL_CALL
createClone() override
;
239 // css::util::XModifiable:
240 virtual sal_Bool SAL_CALL
isModified( ) override
;
241 virtual void SAL_CALL
setModified( sal_Bool bModified
) override
;
243 // css::util::XModifyBroadcaster:
244 virtual void SAL_CALL
addModifyListener(
245 const css::uno::Reference
< css::util::XModifyListener
> & xListener
) override
;
246 virtual void SAL_CALL
removeModifyListener(
247 const css::uno::Reference
< css::util::XModifyListener
> & xListener
) override
;
249 // css::xml::sax::XSAXSerializable
250 virtual void SAL_CALL
serialize(
251 const css::uno::Reference
<css::xml::sax::XDocumentHandler
>& i_xHandler
,
252 const css::uno::Sequence
< css::beans::StringPair
>& i_rNamespaces
) override
;
255 virtual ~SfxDocumentMetaData() override
{}
256 virtual rtl::Reference
<SfxDocumentMetaData
> createMe( css::uno::Reference
< css::uno::XComponentContext
> const & context
) { return new SfxDocumentMetaData( context
); };
257 const css::uno::Reference
< css::uno::XComponentContext
> m_xContext
;
260 ::comphelper::OInterfaceContainerHelper3
<css::util::XModifyListener
> m_NotifyListeners
;
261 /// flag: false means not initialized yet, or disposed
262 bool m_isInitialized
;
265 /// meta-data DOM tree
266 css::uno::Reference
< css::xml::dom::XDocument
> m_xDoc
;
267 /// meta-data super node in the meta-data DOM tree
268 css::uno::Reference
< css::xml::dom::XNode
> m_xParent
;
269 /// standard meta data (single occurrence)
270 std::map
< OUString
, css::uno::Reference
<css::xml::dom::XNode
> >
272 /// standard meta data (multiple occurrences)
274 std::vector
<css::uno::Reference
<css::xml::dom::XNode
> > > m_metaList
;
275 /// user-defined meta data (meta:user-defined) @ATTENTION may be null!
276 css::uno::Reference
<css::beans::XPropertyContainer
> m_xUserDefined
;
277 // now for some meta-data attributes; these are not updated directly in the
278 // DOM because updates (detecting "empty" elements) would be quite messy
279 OUString m_TemplateName
;
280 OUString m_TemplateURL
;
281 css::util::DateTime m_TemplateDate
;
282 OUString m_AutoloadURL
;
283 sal_Int32 m_AutoloadSecs
;
284 OUString m_DefaultTarget
;
286 /// check if we are initialized properly
287 void checkInit() const;
288 /// initialize state from given DOM tree
289 void init(const css::uno::Reference
<css::xml::dom::XDocument
>& i_xDom
);
290 /// update element in DOM tree
291 void updateElement(const OUString
& i_name
,
292 std::vector
<std::pair
<OUString
, OUString
> >* i_pAttrs
= nullptr);
293 /// update user-defined meta data and attributes in DOM tree
294 void updateUserDefinedAndAttributes();
295 /// create empty DOM tree (XDocument)
296 css::uno::Reference
<css::xml::dom::XDocument
> createDOM() const;
297 /// extract base URL (necessary for converting relative links)
298 css::uno::Reference
<css::beans::XPropertySet
> getURLProperties(
299 const css::uno::Sequence
<css::beans::PropertyValue
> & i_rMedium
) const;
300 /// get text of standard meta data element
301 OUString
getMetaText(const char* i_name
) const;
302 /// set text of standard meta data element iff not equal to existing text
303 bool setMetaText(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(const OUString
& i_name
,
310 const OUString
& i_attr
) const;
311 /// get text of a list of standard meta data elements (multiple occ.)
312 css::uno::Sequence
< OUString
> getMetaList(
313 const char* i_name
) const;
314 /// set text of a list of standard meta data elements (multiple occ.)
315 bool setMetaList(const OUString
& i_name
,
316 const css::uno::Sequence
< OUString
> & i_rValue
,
318 void createUserDefined();
321 typedef ::cppu::ImplInheritanceHelper
< SfxDocumentMetaData
, css::document::XCompatWriterDocProperties
> CompatWriterDocPropsImpl_BASE
;
323 class CompatWriterDocPropsImpl
: public CompatWriterDocPropsImpl_BASE
329 virtual rtl::Reference
<SfxDocumentMetaData
> createMe( css::uno::Reference
< css::uno::XComponentContext
> const & context
) override
{ return new CompatWriterDocPropsImpl( context
); };
331 explicit CompatWriterDocPropsImpl( css::uno::Reference
< css::uno::XComponentContext
> const & context
) : CompatWriterDocPropsImpl_BASE( context
) {}
333 // XCompatWriterDocPropsImpl
334 virtual OUString SAL_CALL
getManager() override
{ return msManager
; }
335 virtual void SAL_CALL
setManager( const OUString
& _manager
) override
{ msManager
= _manager
; }
336 virtual OUString SAL_CALL
getCategory() override
{ return msCategory
; }
337 virtual void SAL_CALL
setCategory( const OUString
& _category
) override
{ msCategory
= _category
; }
338 virtual OUString SAL_CALL
getCompany() override
{ return msCompany
; }
339 virtual void SAL_CALL
setCompany( const OUString
& _company
) override
{ msCompany
= _company
; }
342 virtual OUString SAL_CALL
getImplementationName( ) override
344 return "CompatWriterDocPropsImpl";
347 virtual sal_Bool SAL_CALL
supportsService( const OUString
& ServiceName
) override
349 return cppu::supportsService(this, ServiceName
);
352 virtual css::uno::Sequence
< OUString
> SAL_CALL
getSupportedServiceNames( ) override
354 css::uno::Sequence
<OUString
> aServiceNames
{ "com.sun.star.writer.DocumentProperties" };
355 return aServiceNames
;
359 constexpr OUString sMetaPageCount
= u
"meta:page-count"_ustr
;
360 constexpr OUString sMetaTableCount
= u
"meta:table-count"_ustr
;
361 constexpr OUString sMetaDrawCount
= u
"meta:draw-count"_ustr
;
362 constexpr OUString sMetaImageCount
= u
"meta:image-count"_ustr
;
363 constexpr OUString sMetaObjectCount
= u
"meta:object-count"_ustr
;
364 constexpr OUString sMetaOleObjectCount
= u
"meta:ole-object-count"_ustr
;
365 constexpr OUString sMetaParagraphCount
= u
"meta:paragraph-count"_ustr
;
366 constexpr OUString sMetaWordCount
= u
"meta:word-count"_ustr
;
367 constexpr OUString sMetaCharacterCount
= u
"meta:character-count"_ustr
;
368 constexpr OUString sMetaRowCount
= u
"meta:row-count"_ustr
;
369 constexpr OUString sMetaFrameCount
= u
"meta:frame-count"_ustr
;
370 constexpr OUString sMetaSentenceCount
= u
"meta:sentence-count"_ustr
;
371 constexpr OUString sMetaSyllableCount
= u
"meta:syllable-count"_ustr
;
372 constexpr OUString sMetaNonWhitespaceCharacterCount
= u
"meta:non-whitespace-character-count"_ustr
;
373 constexpr OUString sMetaCellCount
= u
"meta:cell-count"_ustr
;
375 // NB: keep these two arrays in sync!
376 constexpr OUString s_stdStatAttrs
[] = {
390 sMetaNonWhitespaceCharacterCount
,
394 // NB: keep these two arrays in sync!
395 const char* s_stdStats
[] = {
409 "NonWhitespaceCharacterCount",
414 const char* s_stdMeta
[] = {
415 "meta:generator", // string
416 "dc:title", // string
417 "dc:description", // string
418 "dc:subject", // string
419 "meta:initial-creator", // string
420 "dc:creator", // string
421 "meta:printed-by", // string
422 "meta:creation-date", // dateTime
423 "dc:date", // dateTime
424 "meta:print-date", // dateTime
425 "meta:template", // XLink
427 "meta:hyperlink-behaviour",
428 "dc:language", // language
429 "meta:editing-cycles", // nonNegativeInteger
430 "meta:editing-duration", // duration
431 "meta:document-statistic", // ... // note: statistic is singular, no s!
440 constexpr OUString sMetaKeyword
= u
"meta:keyword"_ustr
;
441 constexpr OUString sMetaUserDefined
= u
"meta:user-defined"_ustr
;
442 constexpr OUString sDCContributor
= u
"dc:contributor"_ustr
;
443 constexpr OUString sDCPublisher
= u
"dc:publisher"_ustr
;
444 constexpr OUString sDCRelation
= u
"dc:relation"_ustr
;
445 constexpr OUString s_stdMetaList
[] {
446 sMetaKeyword
, // string*
447 sMetaUserDefined
, // ...*
448 sDCContributor
, // string*
449 sDCPublisher
, // string*
450 sDCRelation
, // string*
453 constexpr OUStringLiteral s_nsXLink
= u
"http://www.w3.org/1999/xlink";
454 constexpr OUString s_nsDC
= u
"http://purl.org/dc/elements/1.1/"_ustr
;
455 constexpr OUString s_nsODF
= u
"urn:oasis:names:tc:opendocument:xmlns:office:1.0"_ustr
;
456 constexpr OUString s_nsODFMeta
= u
"urn:oasis:names:tc:opendocument:xmlns:meta:1.0"_ustr
;
457 // constexpr OUStringLiteral s_nsOOo = "http://openoffice.org/2004/office"; // not used (yet?)
459 constexpr OUString s_meta
= u
"meta.xml"_ustr
;
461 bool isValidDate(const css::util::Date
& i_rDate
)
463 return i_rDate
.Month
> 0;
466 bool isValidDateTime(const css::util::DateTime
& i_rDateTime
)
468 return i_rDateTime
.Month
> 0;
471 std::pair
< OUString
, OUString
>
472 getQualifier(const OUString
& nm
) {
473 sal_Int32 ix
= nm
.indexOf(u
':');
475 return std::make_pair(OUString(), nm
);
477 return std::make_pair(nm
.copy(0,ix
), nm
.copy(ix
+1));
481 // get namespace for standard qualified names
482 // NB: only call this with statically known strings!
483 OUString
getNameSpace(const OUString
& i_qname
) noexcept
486 OUString n
= getQualifier(i_qname
).first
;
487 if ( n
== "xlink" ) ns
= s_nsXLink
;
488 if ( n
== "dc" ) ns
= s_nsDC
;
489 if ( n
== "office" ) ns
= s_nsODF
;
490 if ( n
== "meta" ) ns
= s_nsODFMeta
;
491 assert(!ns
.isEmpty());
496 textToDateOrDateTime(css::util::Date
& io_rd
, css::util::DateTime
& io_rdt
,
497 bool & o_rIsDateTime
, std::optional
<sal_Int16
> & o_rTimeZone
,
498 const OUString
& i_text
) noexcept
500 if (::sax::Converter::parseDateOrDateTime(
501 &io_rd
, io_rdt
, o_rIsDateTime
, &o_rTimeZone
, i_text
)) {
504 SAL_WARN_IF(!i_text
.isEmpty(), "sfx.doc", "Invalid date: " << i_text
);
509 // convert string to date/time
511 textToDateTime(css::util::DateTime
& io_rdt
, const OUString
& i_text
) noexcept
513 if (::sax::Converter::parseDateTime(io_rdt
, i_text
)) {
516 SAL_WARN_IF(!i_text
.isEmpty(), "sfx.doc", "Invalid date: " << i_text
);
521 // convert string to date/time with default return value
523 textToDateTimeDefault(const OUString
& i_text
) noexcept
525 css::util::DateTime dt
;
526 static_cast<void> (textToDateTime(dt
, i_text
));
527 // on conversion error: return default value (unchanged)
531 // convert date to string
533 dateToText(css::util::Date
const& i_rd
,
534 sal_Int16
const*const pTimeZone
) noexcept
536 if (isValidDate(i_rd
)) {
538 ::sax::Converter::convertDate(buf
, i_rd
, pTimeZone
);
539 return buf
.makeStringAndClear();
546 // convert date/time to string
548 dateTimeToText(css::util::DateTime
const& i_rdt
,
549 sal_Int16
const*const pTimeZone
= nullptr) noexcept
551 if (isValidDateTime(i_rdt
)) {
552 OUStringBuffer
buf(32);
553 ::sax::Converter::convertDateTime(buf
, i_rdt
, pTimeZone
, true);
554 return buf
.makeStringAndClear();
560 // convert string to duration
562 textToDuration(css::util::Duration
& io_rDur
, OUString
const& i_rText
)
565 if (::sax::Converter::convertDuration(io_rDur
, i_rText
)) {
568 SAL_WARN_IF(!i_rText
.isEmpty(), "sfx.doc", "Invalid duration: " << i_rText
);
573 sal_Int32
textToDuration(OUString
const& i_rText
) noexcept
575 css::util::Duration d
;
576 if (textToDuration(d
, i_rText
)) {
577 // #i107372#: approximate years/months
578 const sal_Int32
days( (d
.Years
* 365) + (d
.Months
* 30) + d
.Days
);
579 return (days
* (24*3600))
580 + (d
.Hours
* 3600) + (d
.Minutes
* 60) + d
.Seconds
;
586 // convert duration to string
587 OUString
durationToText(css::util::Duration
const& i_rDur
) noexcept
590 ::sax::Converter::convertDuration(buf
, i_rDur
);
591 return buf
.makeStringAndClear();
594 // convert duration to string
595 OUString
durationToText(sal_Int32 i_value
) noexcept
597 css::util::Duration ud
;
598 ud
.Days
= static_cast<sal_Int16
>(i_value
/ (24 * 3600));
599 ud
.Hours
= static_cast<sal_Int16
>((i_value
% (24 * 3600)) / 3600);
600 ud
.Minutes
= static_cast<sal_Int16
>((i_value
% 3600) / 60);
601 ud
.Seconds
= static_cast<sal_Int16
>(i_value
% 60);
603 return durationToText(ud
);
606 // extract base URL (necessary for converting relative links)
607 css::uno::Reference
< css::beans::XPropertySet
>
608 SfxDocumentMetaData::getURLProperties(
609 const css::uno::Sequence
< css::beans::PropertyValue
> & i_rMedium
) const
611 css::uno::Reference
< css::beans::XPropertyBag
> xPropArg
= css::beans::PropertyBag::createDefault( m_xContext
);
613 css::uno::Any baseUri
;
614 for (const auto& rProp
: i_rMedium
) {
615 if (rProp
.Name
== "DocumentBaseURL") {
616 baseUri
= rProp
.Value
;
617 } else if (rProp
.Name
== "URL") {
618 if (!baseUri
.hasValue()) {
619 baseUri
= rProp
.Value
;
621 } else if (rProp
.Name
== "HierarchicalDocumentName") {
622 xPropArg
->addProperty(
624 css::beans::PropertyAttribute::MAYBEVOID
,
628 if (baseUri
.hasValue()) {
629 xPropArg
->addProperty(
630 "BaseURI", css::beans::PropertyAttribute::MAYBEVOID
,
633 xPropArg
->addProperty("StreamName",
634 css::beans::PropertyAttribute::MAYBEVOID
,
635 css::uno::Any(s_meta
));
636 } catch (const css::uno::Exception
&) {
639 return css::uno::Reference
< css::beans::XPropertySet
>(xPropArg
,
640 css::uno::UNO_QUERY_THROW
);
643 // return the text of the (hopefully unique, i.e., normalize first!) text
644 // node _below_ the given node
645 /// @throws css::uno::RuntimeException
647 getNodeText(const css::uno::Reference
<css::xml::dom::XNode
>& i_xNode
)
650 throw css::uno::RuntimeException("SfxDocumentMetaData::getNodeText: argument is null", i_xNode
);
651 for (css::uno::Reference
<css::xml::dom::XNode
> c
= i_xNode
->getFirstChild();
653 c
= c
->getNextSibling()) {
654 if (c
->getNodeType() == css::xml::dom::NodeType_TEXT_NODE
) {
656 return c
->getNodeValue();
657 } catch (const css::xml::dom::DOMException
&) { // too big?
666 SfxDocumentMetaData::getMetaText(const char* i_name
) const
667 // throw (css::uno::RuntimeException)
671 const OUString
name( OUString::createFromAscii(i_name
) );
672 assert(m_meta
.find(name
) != m_meta
.end());
673 css::uno::Reference
<css::xml::dom::XNode
> xNode
= m_meta
.find(name
)->second
;
674 return (xNode
.is()) ? getNodeText(xNode
) : OUString();
678 SfxDocumentMetaData::setMetaText(const OUString
& name
,
679 const OUString
& i_rValue
)
680 // throw (css::uno::RuntimeException)
684 assert(m_meta
.find(name
) != m_meta
.end());
685 css::uno::Reference
<css::xml::dom::XNode
> xNode
= m_meta
.find(name
)->second
;
688 if (i_rValue
.isEmpty()) {
689 if (xNode
.is()) { // delete
690 m_xParent
->removeChild(xNode
);
692 m_meta
[name
] = xNode
;
698 if (xNode
.is()) { // update
699 for (css::uno::Reference
<css::xml::dom::XNode
> c
=
700 xNode
->getFirstChild();
702 c
= c
->getNextSibling()) {
703 if (c
->getNodeType() == css::xml::dom::NodeType_TEXT_NODE
) {
704 if (c
->getNodeValue() != i_rValue
) {
705 c
->setNodeValue(i_rValue
);
713 xNode
.set(m_xDoc
->createElementNS(getNameSpace(name
), name
),
714 css::uno::UNO_QUERY_THROW
);
715 m_xParent
->appendChild(xNode
);
716 m_meta
[name
] = xNode
;
718 css::uno::Reference
<css::xml::dom::XNode
> xTextNode(
719 m_xDoc
->createTextNode(i_rValue
), css::uno::UNO_QUERY_THROW
);
720 xNode
->appendChild(xTextNode
);
723 } catch (const css::xml::dom::DOMException
&) {
724 css::uno::Any anyEx
= cppu::getCaughtException();
725 throw css::lang::WrappedTargetRuntimeException(
726 "SfxDocumentMetaData::setMetaText: DOM exception",
727 css::uno::Reference
<css::uno::XInterface
>(*this), anyEx
);
732 SfxDocumentMetaData::setMetaTextAndNotify(const OUString
& i_name
,
733 const OUString
& i_rValue
)
734 // throw (css::uno::RuntimeException)
736 ::osl::ClearableMutexGuard
g(m_aMutex
);
737 if (setMetaText(i_name
, i_rValue
)) {
744 SfxDocumentMetaData::getMetaAttr(const OUString
& name
, const OUString
& i_attr
) const
745 // throw (css::uno::RuntimeException)
747 assert(m_meta
.find(name
) != m_meta
.end());
748 css::uno::Reference
<css::xml::dom::XNode
> xNode
= m_meta
.find(name
)->second
;
750 css::uno::Reference
<css::xml::dom::XElement
> xElem(xNode
,
751 css::uno::UNO_QUERY_THROW
);
752 return xElem
->getAttributeNS(getNameSpace(i_attr
),
753 getQualifier(i_attr
).second
);
759 css::uno::Sequence
< OUString
>
760 SfxDocumentMetaData::getMetaList(const char* i_name
) const
761 // throw (css::uno::RuntimeException)
764 OUString name
= OUString::createFromAscii(i_name
);
765 assert(m_metaList
.find(name
) != m_metaList
.end());
766 std::vector
<css::uno::Reference
<css::xml::dom::XNode
> > const & vec
=
767 m_metaList
.find(name
)->second
;
768 css::uno::Sequence
< OUString
> ret(vec
.size());
769 std::transform(vec
.begin(), vec
.end(), ret
.getArray(),
770 [](const auto& node
) { return getNodeText(node
); });
775 SfxDocumentMetaData::setMetaList(const OUString
& name
,
776 const css::uno::Sequence
<OUString
> & i_rValue
,
777 AttrVector
const* i_pAttrs
)
778 // throw (css::uno::RuntimeException)
781 assert((i_pAttrs
== nullptr) ||
782 (static_cast<size_t>(i_rValue
.getLength()) == i_pAttrs
->size()));
785 assert(m_metaList
.find(name
) != m_metaList
.end());
786 std::vector
<css::uno::Reference
<css::xml::dom::XNode
> > & vec
=
789 // if nothing changed, do nothing
790 // alas, this does not check for permutations, or attributes...
791 if (nullptr == i_pAttrs
) {
792 if (static_cast<size_t>(i_rValue
.getLength()) == vec
.size()) {
794 for (sal_Int32 i
= 0; i
< i_rValue
.getLength(); ++i
) {
795 css::uno::Reference
<css::xml::dom::XNode
> xNode(vec
.at(i
));
797 OUString val
= getNodeText(xNode
);
798 if (val
!= i_rValue
[i
]) {
804 if (isEqual
) return false;
808 // remove old meta data nodes
810 std::vector
<css::uno::Reference
<css::xml::dom::XNode
> >
811 ::reverse_iterator
it(vec
.rbegin());
813 for ( ;it
!= vec
.rend(); ++it
)
815 m_xParent
->removeChild(*it
);
820 // Clean up already removed nodes
821 vec
.erase(it
.base(), vec
.end());
827 // insert new meta data nodes into DOM tree
828 for (sal_Int32 i
= 0; i
< i_rValue
.getLength(); ++i
) {
829 css::uno::Reference
<css::xml::dom::XElement
> xElem(
830 m_xDoc
->createElementNS(getNameSpace(name
), name
),
831 css::uno::UNO_SET_THROW
);
832 css::uno::Reference
<css::xml::dom::XNode
> xNode(xElem
,
833 css::uno::UNO_QUERY_THROW
);
834 css::uno::Reference
<css::xml::dom::XNode
> xTextNode(
835 m_xDoc
->createTextNode(i_rValue
[i
]), css::uno::UNO_QUERY_THROW
);
837 if (i_pAttrs
!= nullptr) {
838 for (auto const& elem
: (*i_pAttrs
)[i
])
840 xElem
->setAttributeNS(getNameSpace(elem
.first
),
841 elem
.first
, elem
.second
);
844 xNode
->appendChild(xTextNode
);
845 m_xParent
->appendChild(xNode
);
846 vec
.push_back(xNode
);
850 } catch (const css::xml::dom::DOMException
&) {
851 css::uno::Any anyEx
= cppu::getCaughtException();
852 throw css::lang::WrappedTargetRuntimeException(
853 "SfxDocumentMetaData::setMetaList: DOM exception",
854 css::uno::Reference
<css::uno::XInterface
>(*this), anyEx
);
858 // convert property list to string list and attribute list
859 std::pair
<css::uno::Sequence
< OUString
>, AttrVector
>
860 propsToStrings(css::uno::Reference
<css::beans::XPropertySet
> const & i_xPropSet
)
862 ::std::vector
< OUString
> values
;
865 css::uno::Reference
<css::beans::XPropertySetInfo
> xSetInfo
866 = i_xPropSet
->getPropertySetInfo();
867 css::uno::Sequence
<css::beans::Property
> props
= xSetInfo
->getProperties();
869 for (sal_Int32 i
= 0; i
< props
.getLength(); ++i
) {
870 if (props
[i
].Attributes
& css::beans::PropertyAttribute::TRANSIENT
) {
873 const OUString name
= props
[i
].Name
;
876 any
= i_xPropSet
->getPropertyValue(name
);
877 } catch (const css::uno::Exception
&) {
880 const css::uno::Type
& type
= any
.getValueType();
881 std::vector
<std::pair
<OUString
, OUString
> > as
;
882 as
.emplace_back("meta:name", name
);
883 static constexpr OUString vt
= u
"meta:value-type"_ustr
;
885 // convert according to type
886 if (type
== ::cppu::UnoType
<bool>::get()) {
890 ::sax::Converter::convertBool(buf
, b
);
891 values
.push_back(buf
.makeStringAndClear());
892 as
.emplace_back(vt
, OUString("boolean"));
893 } else if (type
== ::cppu::UnoType
< OUString
>::get()) {
897 // #i90847# OOo 2.x does stupid things if value-type="string";
898 // fortunately string is default anyway, so we can just omit it
899 // #i107502#: however, OOo 2.x only reads 4 user-defined without @value-type
900 // => best backward compatibility: first 4 without @value-type, rest with
903 as
.emplace_back(vt
, OUString("string"));
905 } else if (type
== ::cppu::UnoType
<css::util::DateTime
>::get()) {
906 css::util::DateTime dt
;
908 values
.push_back(dateTimeToText(dt
));
909 as
.emplace_back(vt
, OUString("date"));
910 } else if (type
== ::cppu::UnoType
<css::util::Date
>::get()) {
913 values
.push_back(dateToText(d
, nullptr));
914 as
.emplace_back(vt
,OUString("date"));
915 } else if (type
== ::cppu::UnoType
<css::util::DateTimeWithTimezone
>::get()) {
916 css::util::DateTimeWithTimezone dttz
;
918 values
.push_back(dateTimeToText(dttz
.DateTimeInTZ
, &dttz
.Timezone
));
919 as
.emplace_back(vt
, OUString("date"));
920 } else if (type
== ::cppu::UnoType
<css::util::DateWithTimezone
>::get()) {
921 css::util::DateWithTimezone dtz
;
923 values
.push_back(dateToText(dtz
.DateInTZ
, &dtz
.Timezone
));
924 as
.emplace_back(vt
, OUString("date"));
925 } else if (type
== ::cppu::UnoType
<css::util::Time
>::get()) {
926 // #i97029#: replaced by Duration
927 // Time is supported for backward compatibility with OOo 3.x, x<=2
930 css::util::Duration ud
;
932 ud
.Minutes
= ut
.Minutes
;
933 ud
.Seconds
= ut
.Seconds
;
934 ud
.NanoSeconds
= ut
.NanoSeconds
;
935 values
.push_back(durationToText(ud
));
936 as
.emplace_back(vt
, OUString("time"));
937 } else if (type
== ::cppu::UnoType
<css::util::Duration
>::get()) {
938 css::util::Duration ud
;
940 values
.push_back(durationToText(ud
));
941 as
.emplace_back(vt
, OUString("time"));
942 } else if (::cppu::UnoType
<double>::get().isAssignableFrom(type
)) {
943 // support not just double, but anything that can be converted
947 ::sax::Converter::convertDouble(buf
, d
);
948 values
.push_back(buf
.makeStringAndClear());
949 as
.emplace_back(vt
, OUString("float"));
951 SAL_WARN("sfx.doc", "Unsupported property type: " << any
.getValueTypeName() );
957 return std::make_pair(comphelper::containerToSequence(values
), attrs
);
960 // remove the given element from the DOM, and iff i_pAttrs != 0 insert new one
962 SfxDocumentMetaData::updateElement(const OUString
& name
,
963 std::vector
<std::pair
<OUString
, OUString
> >* i_pAttrs
)
966 // remove old element
967 css::uno::Reference
<css::xml::dom::XNode
> xNode
=
968 m_meta
.find(name
)->second
;
970 m_xParent
->removeChild(xNode
);
974 if (nullptr != i_pAttrs
) {
975 css::uno::Reference
<css::xml::dom::XElement
> xElem(
976 m_xDoc
->createElementNS(getNameSpace(name
), name
),
977 css::uno::UNO_SET_THROW
);
978 xNode
.set(xElem
, css::uno::UNO_QUERY_THROW
);
980 for (auto const& elem
: *i_pAttrs
)
982 xElem
->setAttributeNS(getNameSpace(elem
.first
),
983 elem
.first
, elem
.second
);
985 m_xParent
->appendChild(xNode
);
987 m_meta
[name
] = xNode
;
988 } catch (const css::xml::dom::DOMException
&) {
989 css::uno::Any anyEx
= cppu::getCaughtException();
990 throw css::lang::WrappedTargetRuntimeException(
991 "SfxDocumentMetaData::updateElement: DOM exception",
992 css::uno::Reference
<css::uno::XInterface
>(*this), anyEx
);
996 // update user-defined meta data in DOM tree
997 void SfxDocumentMetaData::updateUserDefinedAndAttributes()
1000 const css::uno::Reference
<css::beans::XPropertySet
> xPSet(m_xUserDefined
,
1001 css::uno::UNO_QUERY_THROW
);
1002 const std::pair
<css::uno::Sequence
< OUString
>, AttrVector
>
1003 udStringsAttrs( propsToStrings(xPSet
) );
1004 (void) setMetaList("meta:user-defined", udStringsAttrs
.first
,
1005 &udStringsAttrs
.second
);
1007 // update elements with attributes
1008 std::vector
<std::pair
<OUString
, OUString
> > attributes
;
1009 if (!m_TemplateName
.isEmpty() || !m_TemplateURL
.isEmpty()
1010 || isValidDateTime(m_TemplateDate
)) {
1011 attributes
.emplace_back("xlink:type", OUString("simple"));
1012 attributes
.emplace_back("xlink:actuate", OUString("onRequest"));
1013 attributes
.emplace_back("xlink:title", m_TemplateName
);
1014 attributes
.emplace_back("xlink:href", m_TemplateURL
);
1015 if (isValidDateTime(m_TemplateDate
)) {
1016 attributes
.emplace_back(
1017 "meta:date", dateTimeToText(m_TemplateDate
));
1019 updateElement("meta:template", &attributes
);
1021 updateElement("meta:template");
1025 if (!m_AutoloadURL
.isEmpty() || (0 != m_AutoloadSecs
)) {
1026 attributes
.emplace_back("xlink:href", m_AutoloadURL
);
1027 attributes
.emplace_back("meta:delay",
1028 durationToText(m_AutoloadSecs
));
1029 updateElement("meta:auto-reload", &attributes
);
1031 updateElement("meta:auto-reload");
1035 if (!m_DefaultTarget
.isEmpty()) {
1036 attributes
.emplace_back(
1037 "office:target-frame-name",
1039 // xlink:show: _blank -> new, any other value -> replace
1040 const char* show
= m_DefaultTarget
== "_blank" ? "new" : "replace";
1041 attributes
.emplace_back(
1043 OUString::createFromAscii(show
));
1044 updateElement("meta:hyperlink-behaviour", &attributes
);
1046 updateElement("meta:hyperlink-behaviour");
1051 // create empty DOM tree (XDocument)
1052 css::uno::Reference
<css::xml::dom::XDocument
>
1053 SfxDocumentMetaData::createDOM() const // throw (css::uno::RuntimeException)
1055 css::uno::Reference
<css::xml::dom::XDocumentBuilder
> xBuilder( css::xml::dom::DocumentBuilder::create(m_xContext
) );
1056 css::uno::Reference
<css::xml::dom::XDocument
> xDoc
= xBuilder
->newDocument();
1058 throw css::uno::RuntimeException(
1059 "SfxDocumentMetaData::createDOM: cannot create new document",
1060 *const_cast<SfxDocumentMetaData
*>(this));
1065 SfxDocumentMetaData::checkInit() const // throw (css::uno::RuntimeException)
1067 if (!m_isInitialized
) {
1068 throw css::uno::RuntimeException(
1069 "SfxDocumentMetaData::checkInit: not initialized",
1070 *const_cast<SfxDocumentMetaData
*>(this));
1072 assert(m_xDoc
.is() && m_xParent
.is());
1075 void extractTagAndNamespaceUri(std::u16string_view aChildNodeName
,
1076 std::u16string_view
& rTagName
, std::u16string_view
& rNamespaceURI
)
1078 size_t idx
= aChildNodeName
.find(':');
1079 assert(idx
!= std::u16string_view::npos
);
1080 std::u16string_view aPrefix
= aChildNodeName
.substr(0, idx
);
1081 rTagName
= aChildNodeName
.substr(idx
+ 1);
1082 if (aPrefix
== u
"dc")
1083 rNamespaceURI
= s_nsDC
;
1084 else if (aPrefix
== u
"meta")
1085 rNamespaceURI
= s_nsODFMeta
;
1086 else if (aPrefix
== u
"office")
1087 rNamespaceURI
= s_nsODF
;
1093 css::uno::Reference
<css::xml::dom::XElement
> getChildNodeByName(
1094 const css::uno::Reference
<css::xml::dom::XNode
>& xNode
,
1095 std::u16string_view aChildNodeName
)
1097 css::uno::Reference
< css::xml::dom::XNodeList
> xList
= xNode
->getChildNodes();
1100 std::u16string_view aTagName
, aNamespaceURI
;
1101 extractTagAndNamespaceUri(aChildNodeName
, aTagName
, aNamespaceURI
);
1103 const sal_Int32
nLength(xList
->getLength());
1104 for (sal_Int32
a(0); a
< nLength
; a
++)
1106 const css::uno::Reference
< css::xml::dom::XElement
> xChild(xList
->item(a
), css::uno::UNO_QUERY
);
1107 if (xChild
&& xChild
->getNodeName() == aTagName
&& aNamespaceURI
== xChild
->getNamespaceURI())
1114 std::vector
<css::uno::Reference
<css::xml::dom::XNode
> > getChildNodeListByName(
1115 const css::uno::Reference
<css::xml::dom::XNode
>& xNode
,
1116 std::u16string_view aChildNodeName
)
1118 css::uno::Reference
< css::xml::dom::XNodeList
> xList
= xNode
->getChildNodes();
1121 std::u16string_view aTagName
, aNamespaceURI
;
1122 extractTagAndNamespaceUri(aChildNodeName
, aTagName
, aNamespaceURI
);
1123 std::vector
<css::uno::Reference
<css::xml::dom::XNode
>> aList
;
1124 const sal_Int32
nLength(xList
->getLength());
1125 for (sal_Int32
a(0); a
< nLength
; a
++)
1127 const css::uno::Reference
< css::xml::dom::XElement
> xChild(xList
->item(a
), css::uno::UNO_QUERY
);
1128 if (xChild
&& xChild
->getNodeName() == aTagName
&& aNamespaceURI
== xChild
->getNamespaceURI())
1129 aList
.push_back(xChild
);
1134 // initialize state from DOM tree
1135 void SfxDocumentMetaData::init(
1136 const css::uno::Reference
<css::xml::dom::XDocument
>& i_xDoc
)
1139 throw css::uno::RuntimeException("SfxDocumentMetaData::init: no DOM tree given", *this);
1141 m_isInitialized
= false;
1144 // select nodes for standard meta data stuff
1145 // NB: we do not handle the single-XML-file ODF variant, which would
1146 // have the root element office:document.
1147 // The root of such documents must be converted in the importer!
1148 css::uno::Reference
<css::xml::dom::XNode
> xDocNode(
1149 m_xDoc
, css::uno::UNO_QUERY_THROW
);
1152 css::uno::Reference
<css::xml::dom::XNode
> xChild
= getChildNodeByName(xDocNode
, u
"office:document-meta");
1154 m_xParent
= getChildNodeByName(xChild
, u
"office:meta");
1155 } catch (const css::uno::Exception
&) {
1158 if (!m_xParent
.is()) {
1159 // all this create/append stuff may throw DOMException
1161 css::uno::Reference
<css::xml::dom::XElement
> xRElem
;
1162 css::uno::Reference
<css::xml::dom::XNode
> xNode(
1163 i_xDoc
->getFirstChild());
1164 while (xNode
.is()) {
1165 if (css::xml::dom::NodeType_ELEMENT_NODE
==xNode
->getNodeType())
1167 if ( xNode
->getNamespaceURI() == s_nsODF
&& xNode
->getLocalName() == "document-meta" )
1169 xRElem
.set(xNode
, css::uno::UNO_QUERY_THROW
);
1174 SAL_INFO("sfx.doc", "SfxDocumentMetaData::init(): "
1175 "deleting unexpected root element: "
1176 << xNode
->getLocalName());
1177 i_xDoc
->removeChild(xNode
);
1178 xNode
= i_xDoc
->getFirstChild(); // start over
1181 xNode
= xNode
->getNextSibling();
1185 static constexpr OUStringLiteral sOfficeDocumentMeta
= u
"office:document-meta";
1186 xRElem
= i_xDoc
->createElementNS(
1187 s_nsODF
, sOfficeDocumentMeta
);
1188 css::uno::Reference
<css::xml::dom::XNode
> xRNode(xRElem
,
1189 css::uno::UNO_QUERY_THROW
);
1190 i_xDoc
->appendChild(xRNode
);
1192 static constexpr OUStringLiteral sOfficeVersion
= u
"office:version";
1193 xRElem
->setAttributeNS(s_nsODF
, sOfficeVersion
, "1.0");
1194 // does not exist, otherwise m_xParent would not be null
1195 static constexpr OUStringLiteral sOfficeMeta
= u
"office:meta";
1196 css::uno::Reference
<css::xml::dom::XNode
> xParent (
1197 i_xDoc
->createElementNS(s_nsODF
, sOfficeMeta
),
1198 css::uno::UNO_QUERY_THROW
);
1199 xRElem
->appendChild(xParent
);
1200 m_xParent
= xParent
;
1201 } catch (const css::xml::dom::DOMException
&) {
1202 css::uno::Any anyEx
= cppu::getCaughtException();
1203 throw css::lang::WrappedTargetRuntimeException(
1204 "SfxDocumentMetaData::init: DOM exception",
1205 css::uno::Reference
<css::uno::XInterface
>(*this), anyEx
);
1210 // select nodes for elements of which we only handle one occurrence
1211 for (const char **pName
= s_stdMeta
; *pName
!= nullptr; ++pName
) {
1212 OUString name
= OUString::createFromAscii(*pName
);
1213 // NB: If a document contains more than one occurrence of a
1214 // meta-data element, we arbitrarily pick one of them here.
1215 // We do not remove the others, i.e., when we write the
1216 // document, it will contain the duplicates unchanged.
1217 // The ODF spec says that handling multiple occurrences is
1218 // application-specific.
1219 css::uno::Reference
<css::xml::dom::XNode
> xNode
=
1220 getChildNodeByName(m_xParent
, name
);
1221 // Do not create an empty element if it is missing;
1222 // for certain elements, such as dateTime, this would be invalid
1223 m_meta
[name
] = xNode
;
1226 // select nodes for elements of which we handle all occurrences
1227 for (const auto & name
: s_stdMetaList
) {
1228 std::vector
<css::uno::Reference
<css::xml::dom::XNode
> > nodes
=
1229 getChildNodeListByName(m_xParent
, name
);
1230 m_metaList
[name
] = nodes
;
1233 // initialize members corresponding to attributes from DOM nodes
1234 static constexpr OUString sMetaTemplate
= u
"meta:template"_ustr
;
1235 static constexpr OUString sMetaAutoReload
= u
"meta:auto-reload"_ustr
;
1236 static constexpr OUStringLiteral sMetaHyperlinkBehaviour
= u
"meta:hyperlink-behaviour";
1237 m_TemplateName
= getMetaAttr(sMetaTemplate
, "xlink:title");
1238 m_TemplateURL
= getMetaAttr(sMetaTemplate
, "xlink:href");
1240 textToDateTimeDefault(getMetaAttr(sMetaTemplate
, "meta:date"));
1241 m_AutoloadURL
= getMetaAttr(sMetaAutoReload
, "xlink:href");
1243 textToDuration(getMetaAttr(sMetaAutoReload
, "meta:delay"));
1245 getMetaAttr(sMetaHyperlinkBehaviour
, "office:target-frame-name");
1248 std::vector
<css::uno::Reference
<css::xml::dom::XNode
> > & vec
=
1249 m_metaList
[OUString("meta:user-defined")];
1250 m_xUserDefined
.clear(); // #i105826#: reset (may be re-initialization)
1253 createUserDefined();
1256 // user-defined meta data: initialize PropertySet from DOM nodes
1257 for (auto const& elem
: vec
)
1259 css::uno::Reference
<css::xml::dom::XElement
> xElem(elem
,
1260 css::uno::UNO_QUERY_THROW
);
1262 OUString name
= xElem
->getAttributeNS(s_nsODFMeta
, "name");
1263 OUString type
= xElem
->getAttributeNS(s_nsODFMeta
, "value-type");
1264 OUString text
= getNodeText(elem
);
1265 if ( type
== "float" ) {
1267 if (::sax::Converter::convertDouble(d
, text
)) {
1270 SAL_WARN("sfx.doc", "Invalid float: " << text
);
1273 } else if ( type
== "date" ) {
1276 css::util::DateTime dt
;
1277 std::optional
<sal_Int16
> nTimeZone
;
1278 if (textToDateOrDateTime(d
, dt
, isDateTime
, nTimeZone
, text
)) {
1281 any
<<= css::util::DateTimeWithTimezone(dt
,
1288 any
<<= css::util::DateWithTimezone(d
, *nTimeZone
);
1294 SAL_WARN("sfx.doc", "Invalid date: " << text
);
1297 } else if ( type
== "time" ) {
1298 css::util::Duration ud
;
1299 if (textToDuration(ud
, text
)) {
1302 SAL_WARN("sfx.doc", "Invalid time: " << text
);
1305 } else if ( type
== "boolean" ) {
1307 if (::sax::Converter::convertBool(b
, text
)) {
1310 SAL_WARN("sfx.doc", "Invalid boolean: " << text
);
1317 m_xUserDefined
->addProperty(name
,
1318 css::beans::PropertyAttribute::REMOVABLE
, any
);
1319 } catch (const css::beans::PropertyExistException
&) {
1320 SAL_WARN("sfx.doc", "Duplicate: " << name
);
1321 // ignore; duplicate
1322 } catch (const css::beans::IllegalTypeException
&) {
1323 SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal type: " << name
);
1324 } catch (const css::lang::IllegalArgumentException
&) {
1325 SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal arg: " << name
);
1329 m_isModified
= false;
1330 m_isInitialized
= true;
1334 SfxDocumentMetaData::SfxDocumentMetaData(
1335 css::uno::Reference
< css::uno::XComponentContext
> const & context
)
1337 , SfxDocumentMetaData_Base(m_aMutex
)
1338 , m_xContext(context
)
1339 , m_NotifyListeners(m_aMutex
)
1340 , m_isInitialized(false)
1341 , m_isModified(false)
1344 assert(context
.is());
1345 assert(context
->getServiceManager().is());
1349 // com.sun.star.uno.XServiceInfo:
1351 SfxDocumentMetaData::getImplementationName()
1353 return "SfxDocumentMetaData";
1357 SfxDocumentMetaData::supportsService(OUString
const & serviceName
)
1359 return cppu::supportsService(this, serviceName
);
1362 css::uno::Sequence
< OUString
> SAL_CALL
1363 SfxDocumentMetaData::getSupportedServiceNames()
1365 css::uno::Sequence
< OUString
> s
{ "com.sun.star.document.DocumentProperties" };
1370 // css::lang::XComponent:
1371 void SAL_CALL
SfxDocumentMetaData::dispose()
1373 ::osl::MutexGuard
g(m_aMutex
);
1374 if (!m_isInitialized
) {
1377 WeakComponentImplHelperBase::dispose(); // superclass
1378 m_NotifyListeners
.disposeAndClear(css::lang::EventObject(
1380 m_isInitialized
= false;
1385 m_xUserDefined
.clear();
1389 // css::document::XDocumentProperties:
1391 SfxDocumentMetaData::getAuthor()
1393 ::osl::MutexGuard
g(m_aMutex
);
1394 return getMetaText("meta:initial-creator");
1397 void SAL_CALL
SfxDocumentMetaData::setAuthor(const OUString
& the_value
)
1399 setMetaTextAndNotify("meta:initial-creator", the_value
);
1404 SfxDocumentMetaData::getGenerator()
1406 ::osl::MutexGuard
g(m_aMutex
);
1407 return getMetaText("meta:generator");
1411 SfxDocumentMetaData::setGenerator(const OUString
& the_value
)
1413 setMetaTextAndNotify("meta:generator", the_value
);
1416 css::util::DateTime SAL_CALL
1417 SfxDocumentMetaData::getCreationDate()
1419 ::osl::MutexGuard
g(m_aMutex
);
1420 return textToDateTimeDefault(getMetaText("meta:creation-date"));
1424 SfxDocumentMetaData::setCreationDate(const css::util::DateTime
& the_value
)
1426 setMetaTextAndNotify("meta:creation-date", dateTimeToText(the_value
));
1430 SfxDocumentMetaData::getTitle()
1432 ::osl::MutexGuard
g(m_aMutex
);
1433 return getMetaText("dc:title");
1436 void SAL_CALL
SfxDocumentMetaData::setTitle(const OUString
& the_value
)
1438 setMetaTextAndNotify("dc:title", the_value
);
1442 SfxDocumentMetaData::getSubject()
1444 ::osl::MutexGuard
g(m_aMutex
);
1445 return getMetaText("dc:subject");
1449 SfxDocumentMetaData::setSubject(const OUString
& the_value
)
1451 setMetaTextAndNotify("dc:subject", the_value
);
1455 SfxDocumentMetaData::getDescription()
1457 ::osl::MutexGuard
g(m_aMutex
);
1458 return getMetaText("dc:description");
1462 SfxDocumentMetaData::setDescription(const OUString
& the_value
)
1464 setMetaTextAndNotify("dc:description", the_value
);
1467 css::uno::Sequence
< OUString
>
1468 SAL_CALL
SfxDocumentMetaData::getKeywords()
1470 ::osl::MutexGuard
g(m_aMutex
);
1471 return getMetaList("meta:keyword");
1475 SfxDocumentMetaData::setKeywords(
1476 const css::uno::Sequence
< OUString
> & the_value
)
1478 ::osl::ClearableMutexGuard
g(m_aMutex
);
1479 if (setMetaList("meta:keyword", the_value
, nullptr)) {
1485 // css::document::XDocumentProperties2
1486 css::uno::Sequence
<OUString
> SAL_CALL
SfxDocumentMetaData::getContributor()
1488 ::osl::MutexGuard
g(m_aMutex
);
1489 return getMetaList("dc:contributor");
1492 void SAL_CALL
SfxDocumentMetaData::setContributor(const css::uno::Sequence
<OUString
>& the_value
)
1494 ::osl::ClearableMutexGuard
g(m_aMutex
);
1495 if (setMetaList("dc:contributor", the_value
, nullptr))
1502 OUString SAL_CALL
SfxDocumentMetaData::getCoverage()
1504 ::osl::MutexGuard
g(m_aMutex
);
1505 return getMetaText("dc:coverage");
1508 void SAL_CALL
SfxDocumentMetaData::setCoverage(const OUString
& the_value
)
1510 setMetaTextAndNotify("dc:coverage", the_value
);
1513 OUString SAL_CALL
SfxDocumentMetaData::getIdentifier()
1515 ::osl::MutexGuard
g(m_aMutex
);
1516 return getMetaText("dc:identifier");
1519 void SAL_CALL
SfxDocumentMetaData::setIdentifier(const OUString
& the_value
)
1521 setMetaTextAndNotify("dc:identifier", the_value
);
1524 css::uno::Sequence
<OUString
> SAL_CALL
SfxDocumentMetaData::getPublisher()
1526 ::osl::MutexGuard
g(m_aMutex
);
1527 return getMetaList("dc:publisher");
1530 void SAL_CALL
SfxDocumentMetaData::setPublisher(const css::uno::Sequence
<OUString
>& the_value
)
1532 ::osl::ClearableMutexGuard
g(m_aMutex
);
1533 if (setMetaList("dc:publisher", the_value
, nullptr))
1540 css::uno::Sequence
<OUString
> SAL_CALL
SfxDocumentMetaData::getRelation()
1542 ::osl::MutexGuard
g(m_aMutex
);
1543 return getMetaList("dc:relation");
1546 void SAL_CALL
SfxDocumentMetaData::setRelation(const css::uno::Sequence
<OUString
>& the_value
)
1548 ::osl::ClearableMutexGuard
g(m_aMutex
);
1549 if (setMetaList("dc:relation", the_value
, nullptr))
1556 OUString SAL_CALL
SfxDocumentMetaData::getRights()
1558 ::osl::MutexGuard
g(m_aMutex
);
1559 return getMetaText("dc:rights");
1562 void SAL_CALL
SfxDocumentMetaData::setRights(const OUString
& the_value
)
1564 setMetaTextAndNotify("dc:rights", the_value
);
1567 OUString SAL_CALL
SfxDocumentMetaData::getSource()
1569 ::osl::MutexGuard
g(m_aMutex
);
1570 return getMetaText("dc:source");
1573 void SAL_CALL
SfxDocumentMetaData::setSource(const OUString
& the_value
)
1575 setMetaTextAndNotify("dc:source", the_value
);
1578 OUString SAL_CALL
SfxDocumentMetaData::getType()
1580 ::osl::MutexGuard
g(m_aMutex
);
1581 return getMetaText("dc:type");
1584 void SAL_CALL
SfxDocumentMetaData::setType(const OUString
& the_value
)
1586 setMetaTextAndNotify("dc:type", the_value
);
1589 css::lang::Locale SAL_CALL
1590 SfxDocumentMetaData::getLanguage()
1592 ::osl::MutexGuard
g(m_aMutex
);
1593 css::lang::Locale
loc( LanguageTag::convertToLocale( getMetaText("dc:language"), false));
1598 SfxDocumentMetaData::setLanguage(const css::lang::Locale
& the_value
)
1600 OUString
text( LanguageTag::convertToBcp47( the_value
, false));
1601 setMetaTextAndNotify("dc:language", text
);
1605 SfxDocumentMetaData::getModifiedBy()
1607 ::osl::MutexGuard
g(m_aMutex
);
1608 return getMetaText("dc:creator");
1612 SfxDocumentMetaData::setModifiedBy(const OUString
& the_value
)
1614 setMetaTextAndNotify("dc:creator", the_value
);
1617 css::util::DateTime SAL_CALL
1618 SfxDocumentMetaData::getModificationDate()
1620 ::osl::MutexGuard
g(m_aMutex
);
1621 return textToDateTimeDefault(getMetaText("dc:date"));
1625 SfxDocumentMetaData::setModificationDate(const css::util::DateTime
& the_value
)
1627 setMetaTextAndNotify("dc:date", dateTimeToText(the_value
));
1631 SfxDocumentMetaData::getPrintedBy()
1633 ::osl::MutexGuard
g(m_aMutex
);
1634 return getMetaText("meta:printed-by");
1638 SfxDocumentMetaData::setPrintedBy(const OUString
& the_value
)
1640 setMetaTextAndNotify("meta:printed-by", the_value
);
1643 css::util::DateTime SAL_CALL
1644 SfxDocumentMetaData::getPrintDate()
1646 ::osl::MutexGuard
g(m_aMutex
);
1647 return textToDateTimeDefault(getMetaText("meta:print-date"));
1651 SfxDocumentMetaData::setPrintDate(const css::util::DateTime
& the_value
)
1653 setMetaTextAndNotify("meta:print-date", dateTimeToText(the_value
));
1657 SfxDocumentMetaData::getTemplateName()
1659 ::osl::MutexGuard
g(m_aMutex
);
1661 return m_TemplateName
;
1665 SfxDocumentMetaData::setTemplateName(const OUString
& the_value
)
1667 ::osl::ClearableMutexGuard
g(m_aMutex
);
1669 if (m_TemplateName
!= the_value
) {
1670 m_TemplateName
= the_value
;
1677 SfxDocumentMetaData::getTemplateURL()
1679 ::osl::MutexGuard
g(m_aMutex
);
1681 return m_TemplateURL
;
1685 SfxDocumentMetaData::setTemplateURL(const OUString
& the_value
)
1687 ::osl::ClearableMutexGuard
g(m_aMutex
);
1689 if (m_TemplateURL
!= the_value
) {
1690 m_TemplateURL
= the_value
;
1696 css::util::DateTime SAL_CALL
1697 SfxDocumentMetaData::getTemplateDate()
1699 ::osl::MutexGuard
g(m_aMutex
);
1701 return m_TemplateDate
;
1705 SfxDocumentMetaData::setTemplateDate(const css::util::DateTime
& the_value
)
1707 ::osl::ClearableMutexGuard
g(m_aMutex
);
1709 if (m_TemplateDate
!= the_value
) {
1710 m_TemplateDate
= the_value
;
1717 SfxDocumentMetaData::getAutoloadURL()
1719 ::osl::MutexGuard
g(m_aMutex
);
1721 return m_AutoloadURL
;
1725 SfxDocumentMetaData::setAutoloadURL(const OUString
& the_value
)
1727 ::osl::ClearableMutexGuard
g(m_aMutex
);
1729 if (m_AutoloadURL
!= the_value
) {
1730 m_AutoloadURL
= the_value
;
1736 ::sal_Int32 SAL_CALL
1737 SfxDocumentMetaData::getAutoloadSecs()
1739 ::osl::MutexGuard
g(m_aMutex
);
1741 return m_AutoloadSecs
;
1745 SfxDocumentMetaData::setAutoloadSecs(::sal_Int32 the_value
)
1748 throw css::lang::IllegalArgumentException(
1749 "SfxDocumentMetaData::setAutoloadSecs: argument is negative",
1751 ::osl::ClearableMutexGuard
g(m_aMutex
);
1753 if (m_AutoloadSecs
!= the_value
) {
1754 m_AutoloadSecs
= the_value
;
1761 SfxDocumentMetaData::getDefaultTarget()
1763 ::osl::MutexGuard
g(m_aMutex
);
1765 return m_DefaultTarget
;
1769 SfxDocumentMetaData::setDefaultTarget(const OUString
& the_value
)
1771 ::osl::ClearableMutexGuard
g(m_aMutex
);
1773 if (m_DefaultTarget
!= the_value
) {
1774 m_DefaultTarget
= the_value
;
1780 css::uno::Sequence
< css::beans::NamedValue
> SAL_CALL
1781 SfxDocumentMetaData::getDocumentStatistics()
1783 ::osl::MutexGuard
g(m_aMutex
);
1785 ::std::vector
<css::beans::NamedValue
> stats
;
1786 for (size_t i
= 0; s_stdStats
[i
] != nullptr; ++i
) {
1787 OUString text
= getMetaAttr("meta:document-statistic", s_stdStatAttrs
[i
]);
1788 if (text
.isEmpty()) continue;
1789 css::beans::NamedValue stat
;
1790 stat
.Name
= OUString::createFromAscii(s_stdStats
[i
]);
1793 if (!::sax::Converter::convertNumber(val
, text
, 0) || (val
< 0)) {
1795 SAL_WARN("sfx.doc", "Invalid number: " << text
);
1799 stats
.push_back(stat
);
1802 return ::comphelper::containerToSequence(stats
);
1806 SfxDocumentMetaData::setDocumentStatistics(
1807 const css::uno::Sequence
< css::beans::NamedValue
> & the_value
)
1810 osl::MutexGuard
g(m_aMutex
);
1812 std::vector
<std::pair
<OUString
, OUString
> > attributes
;
1813 for (const auto& rValue
: the_value
) {
1814 const OUString name
= rValue
.Name
;
1815 // inefficiently search for matching attribute
1816 for (size_t j
= 0; s_stdStats
[j
] != nullptr; ++j
) {
1817 if (name
.equalsAscii(s_stdStats
[j
])) {
1818 const css::uno::Any any
= rValue
.Value
;
1821 attributes
.emplace_back(s_stdStatAttrs
[j
],
1822 OUString::number(val
));
1825 SAL_WARN("sfx.doc", "Invalid statistic: " << name
);
1831 updateElement("meta:document-statistic", &attributes
);
1836 ::sal_Int16 SAL_CALL
1837 SfxDocumentMetaData::getEditingCycles()
1839 ::osl::MutexGuard
g(m_aMutex
);
1840 OUString text
= getMetaText("meta:editing-cycles");
1842 if (::sax::Converter::convertNumber(ret
, text
,
1843 0, std::numeric_limits
<sal_Int16
>::max())) {
1844 return static_cast<sal_Int16
>(ret
);
1851 SfxDocumentMetaData::setEditingCycles(::sal_Int16 the_value
)
1854 throw css::lang::IllegalArgumentException(
1855 "SfxDocumentMetaData::setEditingCycles: argument is negative",
1857 setMetaTextAndNotify("meta:editing-cycles", OUString::number(the_value
));
1860 ::sal_Int32 SAL_CALL
1861 SfxDocumentMetaData::getEditingDuration()
1863 ::osl::MutexGuard
g(m_aMutex
);
1864 return textToDuration(getMetaText("meta:editing-duration"));
1868 SfxDocumentMetaData::setEditingDuration(::sal_Int32 the_value
)
1871 throw css::lang::IllegalArgumentException(
1872 "SfxDocumentMetaData::setEditingDuration: argument is negative",
1874 setMetaTextAndNotify("meta:editing-duration", durationToText(the_value
));
1878 SfxDocumentMetaData::resetUserData(const OUString
& the_value
)
1880 ::osl::ClearableMutexGuard
g(m_aMutex
);
1882 bool bModified( false );
1883 bModified
|= setMetaText("meta:initial-creator", the_value
);
1884 ::DateTime
now( ::DateTime::SYSTEM
);
1885 css::util::DateTime
uDT(now
.GetUNODateTime());
1886 bModified
|= setMetaText("meta:creation-date", dateTimeToText(uDT
));
1887 bModified
|= setMetaText("dc:creator", OUString());
1888 bModified
|= setMetaText("meta:printed-by", OUString());
1889 bModified
|= setMetaText("dc:date", dateTimeToText(css::util::DateTime()));
1890 bModified
|= setMetaText("meta:print-date",
1891 dateTimeToText(css::util::DateTime()));
1892 bModified
|= setMetaText("meta:editing-duration", durationToText(0));
1893 bModified
|= setMetaText("meta:editing-cycles",
1903 css::uno::Reference
< css::beans::XPropertyContainer
> SAL_CALL
1904 SfxDocumentMetaData::getUserDefinedProperties()
1906 ::osl::MutexGuard
g(m_aMutex
);
1908 createUserDefined();
1909 return m_xUserDefined
;
1914 SfxDocumentMetaData::loadFromStorage(
1915 const css::uno::Reference
< css::embed::XStorage
> & xStorage
,
1916 const css::uno::Sequence
< css::beans::PropertyValue
> & Medium
)
1919 throw css::lang::IllegalArgumentException("SfxDocumentMetaData::loadFromStorage: argument is null", *this, 0);
1920 ::osl::MutexGuard
g(m_aMutex
);
1922 // open meta data file
1923 css::uno::Reference
<css::io::XStream
> xStream(
1924 xStorage
->openStreamElement(
1926 css::embed::ElementModes::READ
) );
1927 if (!xStream
.is()) throw css::uno::RuntimeException();
1928 css::uno::Reference
<css::io::XInputStream
> xInStream
=
1929 xStream
->getInputStream();
1930 if (!xInStream
.is()) throw css::uno::RuntimeException();
1932 // create DOM parser service
1933 css::uno::Reference
<css::lang::XMultiComponentFactory
> xMsf (
1934 m_xContext
->getServiceManager());
1935 css::xml::sax::InputSource input
;
1936 input
.aInputStream
= xInStream
;
1938 sal_uInt64 version
= SotStorage::GetVersion( xStorage
);
1939 // Oasis is also the default (0)
1940 bool bOasis
= ( version
> SOFFICE_FILEFORMAT_60
|| version
== 0 );
1941 const char *pServiceName
= bOasis
1942 ? "com.sun.star.document.XMLOasisMetaImporter"
1943 : "com.sun.star.document.XMLMetaImporter";
1946 css::uno::Reference
<css::beans::XPropertySet
> xPropArg
=
1947 getURLProperties(Medium
);
1949 xPropArg
->getPropertyValue("BaseURI")
1950 >>= input
.sSystemId
;
1951 input
.sSystemId
+= OUString::Concat("/") + s_meta
;
1952 } catch (const css::uno::Exception
&) {
1953 input
.sSystemId
= s_meta
;
1955 css::uno::Sequence
< css::uno::Any
> args
{ css::uno::Any(xPropArg
) };
1957 // the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler
1958 css::uno::Reference
<XInterface
> xFilter
=
1959 xMsf
->createInstanceWithArgumentsAndContext(
1960 OUString::createFromAscii(pServiceName
), args
, m_xContext
);
1962 css::uno::Reference
<css::xml::sax::XFastParser
> xFastParser(xFilter
, css::uno::UNO_QUERY
);
1963 css::uno::Reference
<css::document::XImporter
> xImp(xFilter
, css::uno::UNO_QUERY_THROW
);
1964 xImp
->setTargetDocument(css::uno::Reference
<css::lang::XComponent
>(this));
1967 xFastParser
->parseStream(input
);
1970 css::uno::Reference
<css::xml::sax::XDocumentHandler
> xDocHandler(xFilter
, css::uno::UNO_QUERY_THROW
);
1971 css::uno::Reference
<css::xml::sax::XParser
> xParser
= css::xml::sax::Parser::create(m_xContext
);
1972 xParser
->setDocumentHandler(xDocHandler
);
1973 xParser
->parseStream(input
);
1975 } catch (const css::xml::sax::SAXException
&) {
1976 throw css::io::WrongFormatException(
1977 "SfxDocumentMetaData::loadFromStorage:"
1978 " XML parsing exception", *this);
1980 // NB: the implementation of XMLOasisMetaImporter calls initialize
1985 SfxDocumentMetaData::storeToStorage(
1986 const css::uno::Reference
< css::embed::XStorage
> & xStorage
,
1987 const css::uno::Sequence
< css::beans::PropertyValue
> & Medium
)
1990 throw css::lang::IllegalArgumentException(
1991 "SfxDocumentMetaData::storeToStorage: argument is null", *this, 0);
1992 ::osl::MutexGuard
g(m_aMutex
);
1995 // update user-defined meta data in DOM tree
1996 // updateUserDefinedAndAttributes(); // this will be done in serialize!
1998 // write into storage
1999 css::uno::Reference
<css::io::XStream
> xStream
=
2000 xStorage
->openStreamElement(s_meta
,
2001 css::embed::ElementModes::WRITE
2002 | css::embed::ElementModes::TRUNCATE
);
2003 if (!xStream
.is()) throw css::uno::RuntimeException();
2004 css::uno::Reference
< css::beans::XPropertySet
> xStreamProps(xStream
,
2005 css::uno::UNO_QUERY_THROW
);
2006 xStreamProps
->setPropertyValue(
2008 css::uno::Any(OUString("text/xml")));
2009 xStreamProps
->setPropertyValue(
2011 css::uno::Any(false));
2012 xStreamProps
->setPropertyValue(
2013 "UseCommonStoragePasswordEncryption",
2014 css::uno::Any(false));
2015 css::uno::Reference
<css::io::XOutputStream
> xOutStream
=
2016 xStream
->getOutputStream();
2017 if (!xOutStream
.is()) throw css::uno::RuntimeException();
2018 css::uno::Reference
<css::lang::XMultiComponentFactory
> xMsf (
2019 m_xContext
->getServiceManager());
2020 css::uno::Reference
<css::xml::sax::XWriter
> xSaxWriter(
2021 css::xml::sax::Writer::create(m_xContext
));
2022 xSaxWriter
->setOutputStream(xOutStream
);
2024 const sal_uInt64 version
= SotStorage::GetVersion( xStorage
);
2025 // Oasis is also the default (0)
2026 const bool bOasis
= ( version
> SOFFICE_FILEFORMAT_60
|| version
== 0 );
2027 const char *pServiceName
= bOasis
2028 ? "com.sun.star.document.XMLOasisMetaExporter"
2029 : "com.sun.star.document.XMLMetaExporter";
2032 css::uno::Reference
<css::beans::XPropertySet
> xPropArg
=
2033 getURLProperties(Medium
);
2034 css::uno::Sequence
< css::uno::Any
> args
{ css::uno::Any(xSaxWriter
), css::uno::Any(xPropArg
) };
2036 css::uno::Reference
<css::document::XExporter
> xExp(
2037 xMsf
->createInstanceWithArgumentsAndContext(
2038 OUString::createFromAscii(pServiceName
), args
, m_xContext
),
2039 css::uno::UNO_QUERY_THROW
);
2040 xExp
->setSourceDocument(css::uno::Reference
<css::lang::XComponent
>(this));
2041 css::uno::Reference
<css::document::XFilter
> xFilter(xExp
,
2042 css::uno::UNO_QUERY_THROW
);
2043 if (!xFilter
->filter(css::uno::Sequence
< css::beans::PropertyValue
>())) {
2044 throw css::io::IOException(
2045 "SfxDocumentMetaData::storeToStorage: cannot filter", *this);
2047 css::uno::Reference
<css::embed::XTransactedObject
> xTransaction(
2048 xStorage
, css::uno::UNO_QUERY
);
2049 if (xTransaction
.is()) {
2050 xTransaction
->commit();
2055 SfxDocumentMetaData::loadFromMedium(const OUString
& URL
,
2056 const css::uno::Sequence
< css::beans::PropertyValue
> & Medium
)
2058 css::uno::Reference
<css::io::XInputStream
> xIn
;
2059 utl::MediaDescriptor
md(Medium
);
2060 // if we have a URL parameter, it replaces the one in the media descriptor
2061 if (!URL
.isEmpty()) {
2062 md
[ utl::MediaDescriptor::PROP_URL
] <<= URL
;
2063 md
[ utl::MediaDescriptor::PROP_READONLY
] <<= true;
2065 if (md
.addInputStream()) {
2066 md
[ utl::MediaDescriptor::PROP_INPUTSTREAM
] >>= xIn
;
2068 css::uno::Reference
<css::embed::XStorage
> xStorage
;
2071 xStorage
= ::comphelper::OStorageHelper::GetStorageFromInputStream(
2073 } else { // fallback to url parameter
2074 xStorage
= ::comphelper::OStorageHelper::GetStorageFromURL(
2075 URL
, css::embed::ElementModes::READ
, m_xContext
);
2077 } catch (const css::uno::RuntimeException
&) {
2079 } catch (const css::io::IOException
&) {
2081 } catch (const css::uno::Exception
&) {
2082 css::uno::Any anyEx
= cppu::getCaughtException();
2083 throw css::lang::WrappedTargetException(
2084 "SfxDocumentMetaData::loadFromMedium: exception",
2085 css::uno::Reference
<css::uno::XInterface
>(*this),
2088 if (!xStorage
.is()) {
2089 throw css::uno::RuntimeException(
2090 "SfxDocumentMetaData::loadFromMedium: cannot get Storage",
2093 loadFromStorage(xStorage
, md
.getAsConstPropertyValueList());
2097 SfxDocumentMetaData::storeToMedium(const OUString
& URL
,
2098 const css::uno::Sequence
< css::beans::PropertyValue
> & Medium
)
2100 utl::MediaDescriptor
md(Medium
);
2101 if (!URL
.isEmpty()) {
2102 md
[ utl::MediaDescriptor::PROP_URL
] <<= URL
;
2104 SfxMedium
aMedium(md
.getAsConstPropertyValueList());
2105 css::uno::Reference
<css::embed::XStorage
> xStorage
2106 = aMedium
.GetOutputStorage();
2109 if (!xStorage
.is()) {
2110 throw css::uno::RuntimeException(
2111 "SfxDocumentMetaData::storeToMedium: cannot get Storage",
2114 // set MIME type of the storage
2115 utl::MediaDescriptor::const_iterator iter
2116 = md
.find(utl::MediaDescriptor::PROP_MEDIATYPE
);
2117 if (iter
!= md
.end()) {
2118 css::uno::Reference
< css::beans::XPropertySet
> xProps(xStorage
,
2119 css::uno::UNO_QUERY_THROW
);
2120 xProps
->setPropertyValue(
2121 utl::MediaDescriptor::PROP_MEDIATYPE
,
2124 storeToStorage(xStorage
, md
.getAsConstPropertyValueList());
2127 const bool bOk
= aMedium
.Commit();
2130 ErrCodeMsg nError
= aMedium
.GetErrorIgnoreWarning();
2131 if ( nError
== ERRCODE_NONE
) {
2132 nError
= ERRCODE_IO_GENERAL
;
2135 throw css::task::ErrorCodeIOException(
2136 "SfxDocumentMetaData::storeToMedium <" + URL
+ "> Commit failed: " + nError
.toString(),
2137 css::uno::Reference
< css::uno::XInterface
>(), sal_uInt32(nError
.GetCode()));
2142 // css::lang::XInitialization:
2143 void SAL_CALL
SfxDocumentMetaData::initialize( const css::uno::Sequence
< css::uno::Any
> & aArguments
)
2145 // possible arguments:
2146 // - no argument: default initialization (empty DOM)
2147 // - 1 argument, XDocument: initialize with given DOM and empty base URL
2148 // NB: links in document must be absolute
2150 ::osl::MutexGuard
g(m_aMutex
);
2151 css::uno::Reference
<css::xml::dom::XDocument
> xDoc
;
2153 for (sal_Int32 i
= 0; i
< aArguments
.getLength(); ++i
) {
2154 const css::uno::Any any
= aArguments
[i
];
2155 if (!(any
>>= xDoc
)) {
2156 throw css::lang::IllegalArgumentException(
2157 "SfxDocumentMetaData::initialize: argument must be XDocument",
2158 *this, static_cast<sal_Int16
>(i
));
2161 throw css::lang::IllegalArgumentException(
2162 "SfxDocumentMetaData::initialize: argument is null",
2163 *this, static_cast<sal_Int16
>(i
));
2168 // For a new document, we create a new DOM tree here.
2175 // css::util::XCloneable:
2176 css::uno::Reference
<css::util::XCloneable
> SAL_CALL
2177 SfxDocumentMetaData::createClone()
2179 ::osl::MutexGuard
g(m_aMutex
);
2182 rtl::Reference
<SfxDocumentMetaData
> pNew
= createMe(m_xContext
);
2184 // NB: do not copy the modification listeners, only DOM
2185 css::uno::Reference
<css::xml::dom::XDocument
> xDoc
= createDOM();
2187 updateUserDefinedAndAttributes();
2188 // deep copy of root node
2189 css::uno::Reference
<css::xml::dom::XNode
> xRoot(
2190 m_xDoc
->getDocumentElement(), css::uno::UNO_QUERY_THROW
);
2191 css::uno::Reference
<css::xml::dom::XNode
> xRootNew(
2192 xDoc
->importNode(xRoot
, true));
2193 xDoc
->appendChild(xRootNew
);
2195 } catch (const css::uno::RuntimeException
&) {
2197 } catch (const css::uno::Exception
&) {
2198 css::uno::Any anyEx
= cppu::getCaughtException();
2199 throw css::lang::WrappedTargetRuntimeException(
2200 "SfxDocumentMetaData::createClone: exception",
2201 css::uno::Reference
<css::uno::XInterface
>(*this), anyEx
);
2203 return css::uno::Reference
<css::util::XCloneable
> (pNew
);
2206 // css::util::XModifiable:
2207 sal_Bool SAL_CALL
SfxDocumentMetaData::isModified( )
2209 ::osl::MutexGuard
g(m_aMutex
);
2211 css::uno::Reference
<css::util::XModifiable
> xMB(m_xUserDefined
,
2212 css::uno::UNO_QUERY
);
2213 return m_isModified
|| (xMB
.is() && xMB
->isModified());
2216 void SAL_CALL
SfxDocumentMetaData::setModified( sal_Bool bModified
)
2218 css::uno::Reference
<css::util::XModifiable
> xMB
;
2219 { // do not lock mutex while notifying (#i93514#) to prevent deadlock
2220 ::osl::MutexGuard
g(m_aMutex
);
2222 m_isModified
= bModified
;
2223 if ( !bModified
&& m_xUserDefined
.is() )
2225 xMB
.set(m_xUserDefined
, css::uno::UNO_QUERY
);
2227 "SfxDocumentMetaData::setModified: PropertyBag not Modifiable?");
2232 css::uno::Reference
<css::uno::XInterface
> xThis(*this);
2233 css::lang::EventObject
event(xThis
);
2234 m_NotifyListeners
.notifyEach(&css::util::XModifyListener::modified
,
2236 } catch (const css::uno::RuntimeException
&) {
2238 } catch (const css::uno::Exception
&) {
2240 TOOLS_WARN_EXCEPTION("sfx.doc", "setModified");
2244 xMB
->setModified(false);
2249 // css::util::XModifyBroadcaster:
2250 void SAL_CALL
SfxDocumentMetaData::addModifyListener(
2251 const css::uno::Reference
< css::util::XModifyListener
> & xListener
)
2253 ::osl::MutexGuard
g(m_aMutex
);
2255 m_NotifyListeners
.addInterface(xListener
);
2256 css::uno::Reference
<css::util::XModifyBroadcaster
> xMB(m_xUserDefined
,
2257 css::uno::UNO_QUERY
);
2259 xMB
->addModifyListener(xListener
);
2263 void SAL_CALL
SfxDocumentMetaData::removeModifyListener(
2264 const css::uno::Reference
< css::util::XModifyListener
> & xListener
)
2266 ::osl::MutexGuard
g(m_aMutex
);
2268 m_NotifyListeners
.removeInterface(xListener
);
2269 css::uno::Reference
<css::util::XModifyBroadcaster
> xMB(m_xUserDefined
,
2270 css::uno::UNO_QUERY
);
2272 xMB
->removeModifyListener(xListener
);
2276 // css::xml::sax::XSAXSerializable
2277 void SAL_CALL
SfxDocumentMetaData::serialize(
2278 const css::uno::Reference
<css::xml::sax::XDocumentHandler
>& i_xHandler
,
2279 const css::uno::Sequence
< css::beans::StringPair
>& i_rNamespaces
)
2281 ::osl::MutexGuard
g(m_aMutex
);
2283 updateUserDefinedAndAttributes();
2284 css::uno::Reference
<css::xml::sax::XSAXSerializable
> xSAXable(m_xDoc
,
2285 css::uno::UNO_QUERY_THROW
);
2286 xSAXable
->serialize(i_xHandler
, i_rNamespaces
);
2289 void SfxDocumentMetaData::createUserDefined()
2291 // user-defined meta data: create PropertyBag which only accepts property
2292 // values of allowed types
2293 if ( m_xUserDefined
.is() )
2296 css::uno::Sequence
<css::uno::Type
> types
{
2297 ::cppu::UnoType
<bool>::get(),
2298 ::cppu::UnoType
< OUString
>::get(),
2299 ::cppu::UnoType
<css::util::DateTime
>::get(),
2300 ::cppu::UnoType
<css::util::Date
>::get(),
2301 ::cppu::UnoType
<css::util::DateTimeWithTimezone
>::get(),
2302 ::cppu::UnoType
<css::util::DateWithTimezone
>::get(),
2303 ::cppu::UnoType
<css::util::Duration
>::get(),
2304 ::cppu::UnoType
<float>::get(),
2305 ::cppu::UnoType
<double>::get(),
2306 ::cppu::UnoType
<sal_Int16
>::get(),
2307 ::cppu::UnoType
<sal_Int32
>::get(),
2308 ::cppu::UnoType
<sal_Int64
>::get(),
2309 // Time is supported for backward compatibility with OOo 3.x, x<=2
2310 ::cppu::UnoType
<css::util::Time
>::get()
2312 // #i94175#: ODF allows empty user-defined property names!
2314 css::beans::PropertyBag::createWithTypes( m_xContext
, types
, true/*AllowEmptyPropertyName*/, false/*AutomaticAddition*/ ),
2315 css::uno::UNO_QUERY_THROW
);
2317 const css::uno::Reference
<css::util::XModifyBroadcaster
> xMB(
2318 m_xUserDefined
, css::uno::UNO_QUERY
);
2321 const std::vector
<css::uno::Reference
<css::util::XModifyListener
> >
2322 listeners(m_NotifyListeners
.getElements());
2323 for (const auto& l
: listeners
) {
2324 xMB
->addModifyListener(l
);
2329 } // closing anonymous implementation namespace
2331 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
2332 CompatWriterDocPropsImpl_get_implementation(
2333 css::uno::XComponentContext
*context
,
2334 css::uno::Sequence
<css::uno::Any
> const &)
2336 return cppu::acquire(new CompatWriterDocPropsImpl(context
));
2339 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
2340 SfxDocumentMetaData_get_implementation(
2341 css::uno::XComponentContext
*context
,
2342 css::uno::Sequence
<css::uno::Any
> const &)
2344 return cppu::acquire(new SfxDocumentMetaData(context
));
2347 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */