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