aa25ddc572
Change-Id: Ia14f70bf4b09109e987fd7b432dcc1a5a9b10ad2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/125972 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
2152 lines
76 KiB
C++
2152 lines
76 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <sal/config.h>
|
|
#include <sal/log.hxx>
|
|
|
|
#include <cppuhelper/compbase.hxx>
|
|
#include <cppuhelper/exc_hlp.hxx>
|
|
#include <com/sun/star/lang/XServiceInfo.hpp>
|
|
#include <com/sun/star/document/XDocumentProperties.hpp>
|
|
#include <com/sun/star/lang/XInitialization.hpp>
|
|
#include <com/sun/star/util/XCloneable.hpp>
|
|
#include <com/sun/star/util/XModifiable.hpp>
|
|
#include <com/sun/star/xml/sax/SAXException.hpp>
|
|
#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
|
|
|
|
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
|
|
#include <com/sun/star/lang/EventObject.hpp>
|
|
#include <com/sun/star/beans/IllegalTypeException.hpp>
|
|
#include <com/sun/star/beans/PropertyExistException.hpp>
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/beans/XPropertySetInfo.hpp>
|
|
#include <com/sun/star/beans/PropertyAttribute.hpp>
|
|
#include <com/sun/star/task/ErrorCodeIOException.hpp>
|
|
#include <com/sun/star/embed/XStorage.hpp>
|
|
#include <com/sun/star/embed/XTransactedObject.hpp>
|
|
#include <com/sun/star/embed/ElementModes.hpp>
|
|
#include <com/sun/star/io/WrongFormatException.hpp>
|
|
#include <com/sun/star/io/XStream.hpp>
|
|
#include <com/sun/star/document/XImporter.hpp>
|
|
#include <com/sun/star/document/XExporter.hpp>
|
|
#include <com/sun/star/document/XFilter.hpp>
|
|
#include <com/sun/star/xml/sax/Writer.hpp>
|
|
#include <com/sun/star/xml/sax/Parser.hpp>
|
|
#include <com/sun/star/xml/sax/XFastParser.hpp>
|
|
#include <com/sun/star/xml/dom/DOMException.hpp>
|
|
#include <com/sun/star/xml/dom/XDocument.hpp>
|
|
#include <com/sun/star/xml/dom/XElement.hpp>
|
|
#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
|
|
#include <com/sun/star/xml/dom/NodeType.hpp>
|
|
#include <com/sun/star/xml/xpath/XPathAPI.hpp>
|
|
#include <com/sun/star/util/Date.hpp>
|
|
#include <com/sun/star/util/Time.hpp>
|
|
#include <com/sun/star/util/DateWithTimezone.hpp>
|
|
#include <com/sun/star/util/DateTimeWithTimezone.hpp>
|
|
#include <com/sun/star/util/Duration.hpp>
|
|
|
|
#include <rtl/ustrbuf.hxx>
|
|
#include <tools/datetime.hxx>
|
|
#include <tools/diagnose_ex.h>
|
|
#include <osl/mutex.hxx>
|
|
#include <comphelper/fileformat.h>
|
|
#include <cppuhelper/basemutex.hxx>
|
|
#include <comphelper/interfacecontainer3.hxx>
|
|
#include <comphelper/storagehelper.hxx>
|
|
#include <unotools/mediadescriptor.hxx>
|
|
#include <comphelper/sequence.hxx>
|
|
#include <sot/storage.hxx>
|
|
#include <sfx2/docfile.hxx>
|
|
#include <sax/tools/converter.hxx>
|
|
#include <i18nlangtag/languagetag.hxx>
|
|
#include <optional>
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <cstring>
|
|
#include <limits>
|
|
|
|
|
|
#include <cppuhelper/implbase.hxx>
|
|
#include <cppuhelper/supportsservice.hxx>
|
|
#include <com/sun/star/document/XCompatWriterDocProperties.hpp>
|
|
#include <com/sun/star/beans/PropertyBag.hpp>
|
|
|
|
/**
|
|
* This file contains the implementation of the service
|
|
* com.sun.star.document.DocumentProperties.
|
|
* This service enables access to the meta-data stored in documents.
|
|
* Currently, this service only handles documents in ODF format.
|
|
*
|
|
* The implementation uses an XML DOM to store the properties.
|
|
* This approach was taken because it allows for preserving arbitrary XML data
|
|
* in loaded documents, which will be stored unmodified when saving the
|
|
* document again.
|
|
*
|
|
* Upon access, some properties are directly read from and updated in the DOM.
|
|
* Exception: it seems impossible to get notified upon addition of a property
|
|
* to a com.sun.star.beans.PropertyBag, which is used for storing user-defined
|
|
* properties; because of this, user-defined properties are updated in the
|
|
* XML DOM only when storing the document.
|
|
* Exception 2: when setting certain properties which correspond to attributes
|
|
* in the XML DOM, we want to remove the corresponding XML element. Detecting
|
|
* this condition can get messy, so we store all such properties as members,
|
|
* and update the DOM tree only when storing the document (in
|
|
* <method>updateUserDefinedAndAttributes</method>).
|
|
*
|
|
*/
|
|
|
|
/// anonymous implementation namespace
|
|
namespace {
|
|
|
|
/// a list of attribute-lists, where attribute means name and content
|
|
typedef std::vector<std::vector<std::pair<const char*, OUString> > >
|
|
AttrVector;
|
|
|
|
typedef ::cppu::WeakComponentImplHelper<
|
|
css::lang::XServiceInfo,
|
|
css::document::XDocumentProperties,
|
|
css::lang::XInitialization,
|
|
css::util::XCloneable,
|
|
css::util::XModifiable,
|
|
css::xml::sax::XSAXSerializable>
|
|
SfxDocumentMetaData_Base;
|
|
|
|
class SfxDocumentMetaData:
|
|
private ::cppu::BaseMutex,
|
|
public SfxDocumentMetaData_Base
|
|
{
|
|
public:
|
|
explicit SfxDocumentMetaData(
|
|
css::uno::Reference< css::uno::XComponentContext > const & context);
|
|
SfxDocumentMetaData(const SfxDocumentMetaData&) = delete;
|
|
SfxDocumentMetaData& operator=(const SfxDocumentMetaData&) = delete;
|
|
|
|
// css::lang::XServiceInfo:
|
|
virtual OUString SAL_CALL getImplementationName() override;
|
|
virtual sal_Bool SAL_CALL supportsService(
|
|
const OUString & ServiceName) override;
|
|
virtual css::uno::Sequence< OUString > SAL_CALL
|
|
getSupportedServiceNames() override;
|
|
|
|
// css::lang::XComponent:
|
|
virtual void SAL_CALL dispose() override;
|
|
|
|
// css::document::XDocumentProperties:
|
|
virtual OUString SAL_CALL getAuthor() override;
|
|
virtual void SAL_CALL setAuthor(const OUString & the_value) override;
|
|
virtual OUString SAL_CALL getGenerator() override;
|
|
virtual void SAL_CALL setGenerator(const OUString & the_value) override;
|
|
virtual css::util::DateTime SAL_CALL getCreationDate() override;
|
|
virtual void SAL_CALL setCreationDate(const css::util::DateTime & the_value) override;
|
|
virtual OUString SAL_CALL getTitle() override;
|
|
virtual void SAL_CALL setTitle(const OUString & the_value) override;
|
|
virtual OUString SAL_CALL getSubject() override;
|
|
virtual void SAL_CALL setSubject(const OUString & the_value) override;
|
|
virtual OUString SAL_CALL getDescription() override;
|
|
virtual void SAL_CALL setDescription(const OUString & the_value) override;
|
|
virtual css::uno::Sequence< OUString > SAL_CALL getKeywords() override;
|
|
virtual void SAL_CALL setKeywords(
|
|
const css::uno::Sequence< OUString > & the_value) override;
|
|
virtual css::lang::Locale SAL_CALL getLanguage() override;
|
|
virtual void SAL_CALL setLanguage(const css::lang::Locale & the_value) override;
|
|
virtual OUString SAL_CALL getModifiedBy() override;
|
|
virtual void SAL_CALL setModifiedBy(const OUString & the_value) override;
|
|
virtual css::util::DateTime SAL_CALL getModificationDate() override;
|
|
virtual void SAL_CALL setModificationDate(
|
|
const css::util::DateTime & the_value) override;
|
|
virtual OUString SAL_CALL getPrintedBy() override;
|
|
virtual void SAL_CALL setPrintedBy(const OUString & the_value) override;
|
|
virtual css::util::DateTime SAL_CALL getPrintDate() override;
|
|
virtual void SAL_CALL setPrintDate(const css::util::DateTime & the_value) override;
|
|
virtual OUString SAL_CALL getTemplateName() override;
|
|
virtual void SAL_CALL setTemplateName(const OUString & the_value) override;
|
|
virtual OUString SAL_CALL getTemplateURL() override;
|
|
virtual void SAL_CALL setTemplateURL(const OUString & the_value) override;
|
|
virtual css::util::DateTime SAL_CALL getTemplateDate() override;
|
|
virtual void SAL_CALL setTemplateDate(const css::util::DateTime & the_value) override;
|
|
virtual OUString SAL_CALL getAutoloadURL() override;
|
|
virtual void SAL_CALL setAutoloadURL(const OUString & the_value) override;
|
|
virtual ::sal_Int32 SAL_CALL getAutoloadSecs() override;
|
|
virtual void SAL_CALL setAutoloadSecs(::sal_Int32 the_value) override;
|
|
virtual OUString SAL_CALL getDefaultTarget() override;
|
|
virtual void SAL_CALL setDefaultTarget(const OUString & the_value) override;
|
|
virtual css::uno::Sequence< css::beans::NamedValue > SAL_CALL
|
|
getDocumentStatistics() override;
|
|
virtual void SAL_CALL setDocumentStatistics(
|
|
const css::uno::Sequence< css::beans::NamedValue > & the_value) override;
|
|
virtual ::sal_Int16 SAL_CALL getEditingCycles() override;
|
|
virtual void SAL_CALL setEditingCycles(::sal_Int16 the_value) override;
|
|
virtual ::sal_Int32 SAL_CALL getEditingDuration() override;
|
|
virtual void SAL_CALL setEditingDuration(::sal_Int32 the_value) override;
|
|
virtual void SAL_CALL resetUserData(const OUString & the_value) override;
|
|
virtual css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
|
|
getUserDefinedProperties() override;
|
|
virtual void SAL_CALL loadFromStorage(
|
|
const css::uno::Reference< css::embed::XStorage > & Storage,
|
|
const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
|
|
virtual void SAL_CALL loadFromMedium(const OUString & URL,
|
|
const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
|
|
virtual void SAL_CALL storeToStorage(
|
|
const css::uno::Reference< css::embed::XStorage > & Storage,
|
|
const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
|
|
virtual void SAL_CALL storeToMedium(const OUString & URL,
|
|
const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
|
|
|
|
// css::lang::XInitialization:
|
|
virtual void SAL_CALL initialize(
|
|
const css::uno::Sequence< css::uno::Any > & aArguments) override;
|
|
|
|
// css::util::XCloneable:
|
|
virtual css::uno::Reference<css::util::XCloneable> SAL_CALL createClone() override;
|
|
|
|
// css::util::XModifiable:
|
|
virtual sal_Bool SAL_CALL isModified( ) override;
|
|
virtual void SAL_CALL setModified( sal_Bool bModified ) override;
|
|
|
|
// css::util::XModifyBroadcaster:
|
|
virtual void SAL_CALL addModifyListener(
|
|
const css::uno::Reference< css::util::XModifyListener > & xListener) override;
|
|
virtual void SAL_CALL removeModifyListener(
|
|
const css::uno::Reference< css::util::XModifyListener > & xListener) override;
|
|
|
|
// css::xml::sax::XSAXSerializable
|
|
virtual void SAL_CALL serialize(
|
|
const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
|
|
const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces) override;
|
|
|
|
protected:
|
|
virtual ~SfxDocumentMetaData() override {}
|
|
virtual rtl::Reference<SfxDocumentMetaData> createMe( css::uno::Reference< css::uno::XComponentContext > const & context ) { return new SfxDocumentMetaData( context ); };
|
|
const css::uno::Reference< css::uno::XComponentContext > m_xContext;
|
|
|
|
/// for notification
|
|
::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener> m_NotifyListeners;
|
|
/// flag: false means not initialized yet, or disposed
|
|
bool m_isInitialized;
|
|
/// flag
|
|
bool m_isModified;
|
|
/// meta-data DOM tree
|
|
css::uno::Reference< css::xml::dom::XDocument > m_xDoc;
|
|
/// meta-data super node in the meta-data DOM tree
|
|
css::uno::Reference< css::xml::dom::XNode> m_xParent;
|
|
/// standard meta data (single occurrence)
|
|
std::map< OUString, css::uno::Reference<css::xml::dom::XNode> >
|
|
m_meta;
|
|
/// standard meta data (multiple occurrences)
|
|
std::map< OUString,
|
|
std::vector<css::uno::Reference<css::xml::dom::XNode> > > m_metaList;
|
|
/// user-defined meta data (meta:user-defined) @ATTENTION may be null!
|
|
css::uno::Reference<css::beans::XPropertyContainer> m_xUserDefined;
|
|
// now for some meta-data attributes; these are not updated directly in the
|
|
// DOM because updates (detecting "empty" elements) would be quite messy
|
|
OUString m_TemplateName;
|
|
OUString m_TemplateURL;
|
|
css::util::DateTime m_TemplateDate;
|
|
OUString m_AutoloadURL;
|
|
sal_Int32 m_AutoloadSecs;
|
|
OUString m_DefaultTarget;
|
|
|
|
/// check if we are initialized properly
|
|
void checkInit() const;
|
|
/// initialize state from given DOM tree
|
|
void init(const css::uno::Reference<css::xml::dom::XDocument>& i_xDom);
|
|
/// update element in DOM tree
|
|
void updateElement(const char *i_name,
|
|
std::vector<std::pair<const char *, OUString> >* i_pAttrs = nullptr);
|
|
/// update user-defined meta data and attributes in DOM tree
|
|
void updateUserDefinedAndAttributes();
|
|
/// create empty DOM tree (XDocument)
|
|
css::uno::Reference<css::xml::dom::XDocument> createDOM() const;
|
|
/// extract base URL (necessary for converting relative links)
|
|
css::uno::Reference<css::beans::XPropertySet> getURLProperties(
|
|
const css::uno::Sequence<css::beans::PropertyValue> & i_rMedium) const;
|
|
/// get text of standard meta data element
|
|
OUString getMetaText(const char* i_name) const;
|
|
/// set text of standard meta data element iff not equal to existing text
|
|
bool setMetaText(const char* i_name,
|
|
const OUString & i_rValue);
|
|
/// set text of standard meta data element iff not equal to existing text
|
|
void setMetaTextAndNotify(const char* i_name,
|
|
const OUString & i_rValue);
|
|
/// get text of standard meta data element's attribute
|
|
OUString getMetaAttr(const char* i_name,
|
|
const char* i_attr) const;
|
|
/// get text of a list of standard meta data elements (multiple occ.)
|
|
css::uno::Sequence< OUString > getMetaList(
|
|
const char* i_name) const;
|
|
/// set text of a list of standard meta data elements (multiple occ.)
|
|
bool setMetaList(const char* i_name,
|
|
const css::uno::Sequence< OUString > & i_rValue,
|
|
AttrVector const*);
|
|
void createUserDefined();
|
|
};
|
|
|
|
typedef ::cppu::ImplInheritanceHelper< SfxDocumentMetaData, css::document::XCompatWriterDocProperties > CompatWriterDocPropsImpl_BASE;
|
|
|
|
class CompatWriterDocPropsImpl : public CompatWriterDocPropsImpl_BASE
|
|
{
|
|
OUString msManager;
|
|
OUString msCategory;
|
|
OUString msCompany;
|
|
protected:
|
|
virtual rtl::Reference<SfxDocumentMetaData> createMe( css::uno::Reference< css::uno::XComponentContext > const & context ) override { return new CompatWriterDocPropsImpl( context ); };
|
|
public:
|
|
explicit CompatWriterDocPropsImpl( css::uno::Reference< css::uno::XComponentContext > const & context) : CompatWriterDocPropsImpl_BASE( context ) {}
|
|
|
|
// XCompatWriterDocPropsImpl
|
|
virtual OUString SAL_CALL getManager() override { return msManager; }
|
|
virtual void SAL_CALL setManager( const OUString& _manager ) override { msManager = _manager; }
|
|
virtual OUString SAL_CALL getCategory() override { return msCategory; }
|
|
virtual void SAL_CALL setCategory( const OUString& _category ) override { msCategory = _category; }
|
|
virtual OUString SAL_CALL getCompany() override { return msCompany; }
|
|
virtual void SAL_CALL setCompany( const OUString& _company ) override { msCompany = _company; }
|
|
|
|
// XServiceInfo
|
|
virtual OUString SAL_CALL getImplementationName( ) override
|
|
{
|
|
return "CompatWriterDocPropsImpl";
|
|
}
|
|
|
|
virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override
|
|
{
|
|
return cppu::supportsService(this, ServiceName);
|
|
}
|
|
|
|
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override
|
|
{
|
|
css::uno::Sequence<OUString> aServiceNames { "com.sun.star.writer.DocumentProperties" };
|
|
return aServiceNames;
|
|
}
|
|
};
|
|
|
|
// NB: keep these two arrays in sync!
|
|
const char* s_stdStatAttrs[] = {
|
|
"meta:page-count",
|
|
"meta:table-count",
|
|
"meta:draw-count",
|
|
"meta:image-count",
|
|
"meta:object-count",
|
|
"meta:ole-object-count",
|
|
"meta:paragraph-count",
|
|
"meta:word-count",
|
|
"meta:character-count",
|
|
"meta:row-count",
|
|
"meta:frame-count",
|
|
"meta:sentence-count",
|
|
"meta:syllable-count",
|
|
"meta:non-whitespace-character-count",
|
|
"meta:cell-count",
|
|
nullptr
|
|
};
|
|
|
|
// NB: keep these two arrays in sync!
|
|
const char* s_stdStats[] = {
|
|
"PageCount",
|
|
"TableCount",
|
|
"DrawCount",
|
|
"ImageCount",
|
|
"ObjectCount",
|
|
"OLEObjectCount",
|
|
"ParagraphCount",
|
|
"WordCount",
|
|
"CharacterCount",
|
|
"RowCount",
|
|
"FrameCount",
|
|
"SentenceCount",
|
|
"SyllableCount",
|
|
"NonWhitespaceCharacterCount",
|
|
"CellCount",
|
|
nullptr
|
|
};
|
|
|
|
const char* s_stdMeta[] = {
|
|
"meta:generator", // string
|
|
"dc:title", // string
|
|
"dc:description", // string
|
|
"dc:subject", // string
|
|
"meta:initial-creator", // string
|
|
"dc:creator", // string
|
|
"meta:printed-by", // string
|
|
"meta:creation-date", // dateTime
|
|
"dc:date", // dateTime
|
|
"meta:print-date", // dateTime
|
|
"meta:template", // XLink
|
|
"meta:auto-reload",
|
|
"meta:hyperlink-behaviour",
|
|
"dc:language", // language
|
|
"meta:editing-cycles", // nonNegativeInteger
|
|
"meta:editing-duration", // duration
|
|
"meta:document-statistic", // ... // note: statistic is singular, no s!
|
|
nullptr
|
|
};
|
|
|
|
const char* s_stdMetaList[] = {
|
|
"meta:keyword", // string*
|
|
"meta:user-defined", // ...*
|
|
nullptr
|
|
};
|
|
|
|
constexpr OUStringLiteral s_nsXLink = u"http://www.w3.org/1999/xlink";
|
|
constexpr OUStringLiteral s_nsDC = u"http://purl.org/dc/elements/1.1/";
|
|
constexpr OUStringLiteral s_nsODF = u"urn:oasis:names:tc:opendocument:xmlns:office:1.0";
|
|
constexpr OUStringLiteral s_nsODFMeta = u"urn:oasis:names:tc:opendocument:xmlns:meta:1.0";
|
|
// constexpr OUStringLiteral s_nsOOo = "http://openoffice.org/2004/office"; // not used (yet?)
|
|
|
|
constexpr OUStringLiteral s_meta = u"meta.xml";
|
|
|
|
bool isValidDate(const css::util::Date & i_rDate)
|
|
{
|
|
return i_rDate.Month > 0;
|
|
}
|
|
|
|
bool isValidDateTime(const css::util::DateTime & i_rDateTime)
|
|
{
|
|
return i_rDateTime.Month > 0;
|
|
}
|
|
|
|
std::pair< OUString, OUString >
|
|
getQualifier(const char* i_name) {
|
|
OUString nm = OUString::createFromAscii(i_name);
|
|
sal_Int32 ix = nm.indexOf(u':');
|
|
if (ix == -1) {
|
|
return std::make_pair(OUString(), nm);
|
|
} else {
|
|
return std::make_pair(nm.copy(0,ix), nm.copy(ix+1));
|
|
}
|
|
}
|
|
|
|
// get namespace for standard qualified names
|
|
// NB: only call this with statically known strings!
|
|
OUString getNameSpace(const char* i_qname) noexcept
|
|
{
|
|
assert(i_qname);
|
|
OUString ns;
|
|
OUString n = getQualifier(i_qname).first;
|
|
if ( n == "xlink" ) ns = s_nsXLink;
|
|
if ( n == "dc" ) ns = s_nsDC;
|
|
if ( n == "office" ) ns = s_nsODF;
|
|
if ( n == "meta" ) ns = s_nsODFMeta;
|
|
assert(!ns.isEmpty());
|
|
return ns;
|
|
}
|
|
|
|
bool
|
|
textToDateOrDateTime(css::util::Date & io_rd, css::util::DateTime & io_rdt,
|
|
bool & o_rIsDateTime, std::optional<sal_Int16> & o_rTimeZone,
|
|
const OUString& i_text) noexcept
|
|
{
|
|
if (::sax::Converter::parseDateOrDateTime(
|
|
&io_rd, io_rdt, o_rIsDateTime, &o_rTimeZone, i_text)) {
|
|
return true;
|
|
} else {
|
|
SAL_WARN_IF(!i_text.isEmpty(), "sfx.doc", "Invalid date: " << i_text);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// convert string to date/time
|
|
bool
|
|
textToDateTime(css::util::DateTime & io_rdt, const OUString& i_text) noexcept
|
|
{
|
|
if (::sax::Converter::parseDateTime(io_rdt, i_text)) {
|
|
return true;
|
|
} else {
|
|
SAL_WARN_IF(!i_text.isEmpty(), "sfx.doc", "Invalid date: " << i_text);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// convert string to date/time with default return value
|
|
css::util::DateTime
|
|
textToDateTimeDefault(const OUString& i_text) noexcept
|
|
{
|
|
css::util::DateTime dt;
|
|
static_cast<void> (textToDateTime(dt, i_text));
|
|
// on conversion error: return default value (unchanged)
|
|
return dt;
|
|
}
|
|
|
|
// convert date to string
|
|
OUString
|
|
dateToText(css::util::Date const& i_rd,
|
|
sal_Int16 const*const pTimeZone) noexcept
|
|
{
|
|
if (isValidDate(i_rd)) {
|
|
OUStringBuffer buf;
|
|
::sax::Converter::convertDate(buf, i_rd, pTimeZone);
|
|
return buf.makeStringAndClear();
|
|
} else {
|
|
return OUString();
|
|
}
|
|
}
|
|
|
|
|
|
// convert date/time to string
|
|
OUString
|
|
dateTimeToText(css::util::DateTime const& i_rdt,
|
|
sal_Int16 const*const pTimeZone = nullptr) noexcept
|
|
{
|
|
if (isValidDateTime(i_rdt)) {
|
|
OUStringBuffer buf(32);
|
|
::sax::Converter::convertDateTime(buf, i_rdt, pTimeZone, true);
|
|
return buf.makeStringAndClear();
|
|
} else {
|
|
return OUString();
|
|
}
|
|
}
|
|
|
|
// convert string to duration
|
|
bool
|
|
textToDuration(css::util::Duration& io_rDur, OUString const& i_rText)
|
|
noexcept
|
|
{
|
|
if (::sax::Converter::convertDuration(io_rDur, i_rText)) {
|
|
return true;
|
|
} else {
|
|
SAL_WARN_IF(!i_rText.isEmpty(), "sfx.doc", "Invalid duration: " << i_rText);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
sal_Int32 textToDuration(OUString const& i_rText) noexcept
|
|
{
|
|
css::util::Duration d;
|
|
if (textToDuration(d, i_rText)) {
|
|
// #i107372#: approximate years/months
|
|
const sal_Int32 days( (d.Years * 365) + (d.Months * 30) + d.Days );
|
|
return (days * (24*3600))
|
|
+ (d.Hours * 3600) + (d.Minutes * 60) + d.Seconds;
|
|
} else {
|
|
return 0; // default
|
|
}
|
|
}
|
|
|
|
// convert duration to string
|
|
OUString durationToText(css::util::Duration const& i_rDur) noexcept
|
|
{
|
|
OUStringBuffer buf;
|
|
::sax::Converter::convertDuration(buf, i_rDur);
|
|
return buf.makeStringAndClear();
|
|
}
|
|
|
|
// convert duration to string
|
|
OUString durationToText(sal_Int32 i_value) noexcept
|
|
{
|
|
css::util::Duration ud;
|
|
ud.Days = static_cast<sal_Int16>(i_value / (24 * 3600));
|
|
ud.Hours = static_cast<sal_Int16>((i_value % (24 * 3600)) / 3600);
|
|
ud.Minutes = static_cast<sal_Int16>((i_value % 3600) / 60);
|
|
ud.Seconds = static_cast<sal_Int16>(i_value % 60);
|
|
ud.NanoSeconds = 0;
|
|
return durationToText(ud);
|
|
}
|
|
|
|
// extract base URL (necessary for converting relative links)
|
|
css::uno::Reference< css::beans::XPropertySet >
|
|
SfxDocumentMetaData::getURLProperties(
|
|
const css::uno::Sequence< css::beans::PropertyValue > & i_rMedium) const
|
|
{
|
|
css::uno::Reference< css::beans::XPropertyBag> xPropArg = css::beans::PropertyBag::createDefault( m_xContext );
|
|
try {
|
|
css::uno::Any baseUri;
|
|
for (const auto& rProp : i_rMedium) {
|
|
if (rProp.Name == "DocumentBaseURL") {
|
|
baseUri = rProp.Value;
|
|
} else if (rProp.Name == "URL") {
|
|
if (!baseUri.hasValue()) {
|
|
baseUri = rProp.Value;
|
|
}
|
|
} else if (rProp.Name == "HierarchicalDocumentName") {
|
|
xPropArg->addProperty(
|
|
"StreamRelPath",
|
|
css::beans::PropertyAttribute::MAYBEVOID,
|
|
rProp.Value);
|
|
}
|
|
}
|
|
if (baseUri.hasValue()) {
|
|
xPropArg->addProperty(
|
|
"BaseURI", css::beans::PropertyAttribute::MAYBEVOID,
|
|
baseUri);
|
|
}
|
|
xPropArg->addProperty("StreamName",
|
|
css::beans::PropertyAttribute::MAYBEVOID,
|
|
css::uno::makeAny(OUString(s_meta)));
|
|
} catch (const css::uno::Exception &) {
|
|
// ignore
|
|
}
|
|
return css::uno::Reference< css::beans::XPropertySet>(xPropArg,
|
|
css::uno::UNO_QUERY_THROW);
|
|
}
|
|
|
|
// return the text of the (hopefully unique, i.e., normalize first!) text
|
|
// node _below_ the given node
|
|
/// @throws css::uno::RuntimeException
|
|
OUString
|
|
getNodeText(const css::uno::Reference<css::xml::dom::XNode>& i_xNode)
|
|
{
|
|
if (!i_xNode.is())
|
|
throw css::uno::RuntimeException("SfxDocumentMetaData::getNodeText: argument is null", i_xNode);
|
|
for (css::uno::Reference<css::xml::dom::XNode> c = i_xNode->getFirstChild();
|
|
c.is();
|
|
c = c->getNextSibling()) {
|
|
if (c->getNodeType() == css::xml::dom::NodeType_TEXT_NODE) {
|
|
try {
|
|
return c->getNodeValue();
|
|
} catch (const css::xml::dom::DOMException &) { // too big?
|
|
return OUString();
|
|
}
|
|
}
|
|
}
|
|
return OUString();
|
|
}
|
|
|
|
OUString
|
|
SfxDocumentMetaData::getMetaText(const char* i_name) const
|
|
// throw (css::uno::RuntimeException)
|
|
{
|
|
checkInit();
|
|
|
|
const OUString name( OUString::createFromAscii(i_name) );
|
|
assert(m_meta.find(name) != m_meta.end());
|
|
css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
|
|
return (xNode.is()) ? getNodeText(xNode) : OUString();
|
|
}
|
|
|
|
bool
|
|
SfxDocumentMetaData::setMetaText(const char* i_name,
|
|
const OUString & i_rValue)
|
|
// throw (css::uno::RuntimeException)
|
|
{
|
|
checkInit();
|
|
|
|
const OUString name( OUString::createFromAscii(i_name) );
|
|
assert(m_meta.find(name) != m_meta.end());
|
|
css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
|
|
|
|
try {
|
|
if (i_rValue.isEmpty()) {
|
|
if (xNode.is()) { // delete
|
|
m_xParent->removeChild(xNode);
|
|
xNode.clear();
|
|
m_meta[name] = xNode;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (xNode.is()) { // update
|
|
for (css::uno::Reference<css::xml::dom::XNode> c =
|
|
xNode->getFirstChild();
|
|
c.is();
|
|
c = c->getNextSibling()) {
|
|
if (c->getNodeType() == css::xml::dom::NodeType_TEXT_NODE) {
|
|
if (c->getNodeValue() != i_rValue) {
|
|
c->setNodeValue(i_rValue);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
} else { // insert
|
|
xNode.set(m_xDoc->createElementNS(getNameSpace(i_name), name),
|
|
css::uno::UNO_QUERY_THROW);
|
|
m_xParent->appendChild(xNode);
|
|
m_meta[name] = xNode;
|
|
}
|
|
css::uno::Reference<css::xml::dom::XNode> xTextNode(
|
|
m_xDoc->createTextNode(i_rValue), css::uno::UNO_QUERY_THROW);
|
|
xNode->appendChild(xTextNode);
|
|
return true;
|
|
}
|
|
} catch (const css::xml::dom::DOMException &) {
|
|
css::uno::Any anyEx = cppu::getCaughtException();
|
|
throw css::lang::WrappedTargetRuntimeException(
|
|
"SfxDocumentMetaData::setMetaText: DOM exception",
|
|
css::uno::Reference<css::uno::XInterface>(*this), anyEx);
|
|
}
|
|
}
|
|
|
|
void
|
|
SfxDocumentMetaData::setMetaTextAndNotify(const char* i_name,
|
|
const OUString & i_rValue)
|
|
// throw (css::uno::RuntimeException)
|
|
{
|
|
::osl::ClearableMutexGuard g(m_aMutex);
|
|
if (setMetaText(i_name, i_rValue)) {
|
|
g.clear();
|
|
setModified(true);
|
|
}
|
|
}
|
|
|
|
OUString
|
|
SfxDocumentMetaData::getMetaAttr(const char* i_name, const char* i_attr) const
|
|
// throw (css::uno::RuntimeException)
|
|
{
|
|
OUString name = OUString::createFromAscii(i_name);
|
|
assert(m_meta.find(name) != m_meta.end());
|
|
css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
|
|
if (xNode.is()) {
|
|
css::uno::Reference<css::xml::dom::XElement> xElem(xNode,
|
|
css::uno::UNO_QUERY_THROW);
|
|
return xElem->getAttributeNS(getNameSpace(i_attr),
|
|
getQualifier(i_attr).second);
|
|
} else {
|
|
return OUString();
|
|
}
|
|
}
|
|
|
|
css::uno::Sequence< OUString>
|
|
SfxDocumentMetaData::getMetaList(const char* i_name) const
|
|
// throw (css::uno::RuntimeException)
|
|
{
|
|
checkInit();
|
|
OUString name = OUString::createFromAscii(i_name);
|
|
assert(m_metaList.find(name) != m_metaList.end());
|
|
std::vector<css::uno::Reference<css::xml::dom::XNode> > const & vec =
|
|
m_metaList.find(name)->second;
|
|
css::uno::Sequence< OUString> ret(vec.size());
|
|
std::transform(vec.begin(), vec.end(), ret.getArray(),
|
|
[](const auto& node) { return getNodeText(node); });
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
SfxDocumentMetaData::setMetaList(const char* i_name,
|
|
const css::uno::Sequence<OUString> & i_rValue,
|
|
AttrVector const* i_pAttrs)
|
|
// throw (css::uno::RuntimeException)
|
|
{
|
|
checkInit();
|
|
assert((i_pAttrs == nullptr) ||
|
|
(static_cast<size_t>(i_rValue.getLength()) == i_pAttrs->size()));
|
|
|
|
try {
|
|
OUString name = OUString::createFromAscii(i_name);
|
|
assert(m_metaList.find(name) != m_metaList.end());
|
|
std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
|
|
m_metaList[name];
|
|
|
|
// if nothing changed, do nothing
|
|
// alas, this does not check for permutations, or attributes...
|
|
if (nullptr == i_pAttrs) {
|
|
if (static_cast<size_t>(i_rValue.getLength()) == vec.size()) {
|
|
bool isEqual(true);
|
|
for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
|
|
css::uno::Reference<css::xml::dom::XNode> xNode(vec.at(i));
|
|
if (xNode.is()) {
|
|
OUString val = getNodeText(xNode);
|
|
if (val != i_rValue[i]) {
|
|
isEqual = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (isEqual) return false;
|
|
}
|
|
}
|
|
|
|
// remove old meta data nodes
|
|
{
|
|
std::vector<css::uno::Reference<css::xml::dom::XNode> >
|
|
::reverse_iterator it(vec.rbegin());
|
|
try {
|
|
for ( ;it != vec.rend(); ++it)
|
|
{
|
|
m_xParent->removeChild(*it);
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
// Clean up already removed nodes
|
|
vec.erase(it.base(), vec.end());
|
|
throw;
|
|
}
|
|
vec.clear();
|
|
}
|
|
|
|
// insert new meta data nodes into DOM tree
|
|
for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
|
|
css::uno::Reference<css::xml::dom::XElement> xElem(
|
|
m_xDoc->createElementNS(getNameSpace(i_name), name),
|
|
css::uno::UNO_SET_THROW);
|
|
css::uno::Reference<css::xml::dom::XNode> xNode(xElem,
|
|
css::uno::UNO_QUERY_THROW);
|
|
css::uno::Reference<css::xml::dom::XNode> xTextNode(
|
|
m_xDoc->createTextNode(i_rValue[i]), css::uno::UNO_QUERY_THROW);
|
|
// set attributes
|
|
if (i_pAttrs != nullptr) {
|
|
for (auto const& elem : (*i_pAttrs)[i])
|
|
{
|
|
xElem->setAttributeNS(getNameSpace(elem.first),
|
|
OUString::createFromAscii(elem.first),
|
|
elem.second);
|
|
}
|
|
}
|
|
xNode->appendChild(xTextNode);
|
|
m_xParent->appendChild(xNode);
|
|
vec.push_back(xNode);
|
|
}
|
|
|
|
return true;
|
|
} catch (const css::xml::dom::DOMException &) {
|
|
css::uno::Any anyEx = cppu::getCaughtException();
|
|
throw css::lang::WrappedTargetRuntimeException(
|
|
"SfxDocumentMetaData::setMetaList: DOM exception",
|
|
css::uno::Reference<css::uno::XInterface>(*this), anyEx);
|
|
}
|
|
}
|
|
|
|
// convert property list to string list and attribute list
|
|
std::pair<css::uno::Sequence< OUString>, AttrVector>
|
|
propsToStrings(css::uno::Reference<css::beans::XPropertySet> const & i_xPropSet)
|
|
{
|
|
::std::vector< OUString > values;
|
|
AttrVector attrs;
|
|
|
|
css::uno::Reference<css::beans::XPropertySetInfo> xSetInfo
|
|
= i_xPropSet->getPropertySetInfo();
|
|
css::uno::Sequence<css::beans::Property> props = xSetInfo->getProperties();
|
|
|
|
for (sal_Int32 i = 0; i < props.getLength(); ++i) {
|
|
if (props[i].Attributes & css::beans::PropertyAttribute::TRANSIENT) {
|
|
continue;
|
|
}
|
|
const OUString name = props[i].Name;
|
|
css::uno::Any any;
|
|
try {
|
|
any = i_xPropSet->getPropertyValue(name);
|
|
} catch (const css::uno::Exception &) {
|
|
// ignore
|
|
}
|
|
const css::uno::Type & type = any.getValueType();
|
|
std::vector<std::pair<const char*, OUString> > as;
|
|
as.emplace_back("meta:name", name);
|
|
const char* vt = "meta:value-type";
|
|
|
|
// convert according to type
|
|
if (type == ::cppu::UnoType<bool>::get()) {
|
|
bool b = false;
|
|
any >>= b;
|
|
OUStringBuffer buf;
|
|
::sax::Converter::convertBool(buf, b);
|
|
values.push_back(buf.makeStringAndClear());
|
|
as.emplace_back(vt, OUString("boolean"));
|
|
} else if (type == ::cppu::UnoType< OUString>::get()) {
|
|
OUString s;
|
|
any >>= s;
|
|
values.push_back(s);
|
|
// #i90847# OOo 2.x does stupid things if value-type="string";
|
|
// fortunately string is default anyway, so we can just omit it
|
|
// #i107502#: however, OOo 2.x only reads 4 user-defined without @value-type
|
|
// => best backward compatibility: first 4 without @value-type, rest with
|
|
if (4 <= i)
|
|
{
|
|
as.emplace_back(vt, OUString("string"));
|
|
}
|
|
} else if (type == ::cppu::UnoType<css::util::DateTime>::get()) {
|
|
css::util::DateTime dt;
|
|
any >>= dt;
|
|
values.push_back(dateTimeToText(dt));
|
|
as.emplace_back(vt, OUString("date"));
|
|
} else if (type == ::cppu::UnoType<css::util::Date>::get()) {
|
|
css::util::Date d;
|
|
any >>= d;
|
|
values.push_back(dateToText(d, nullptr));
|
|
as.emplace_back(vt,OUString("date"));
|
|
} else if (type == ::cppu::UnoType<css::util::DateTimeWithTimezone>::get()) {
|
|
css::util::DateTimeWithTimezone dttz;
|
|
any >>= dttz;
|
|
values.push_back(dateTimeToText(dttz.DateTimeInTZ, &dttz.Timezone));
|
|
as.emplace_back(vt, OUString("date"));
|
|
} else if (type == ::cppu::UnoType<css::util::DateWithTimezone>::get()) {
|
|
css::util::DateWithTimezone dtz;
|
|
any >>= dtz;
|
|
values.push_back(dateToText(dtz.DateInTZ, &dtz.Timezone));
|
|
as.emplace_back(vt, OUString("date"));
|
|
} else if (type == ::cppu::UnoType<css::util::Time>::get()) {
|
|
// #i97029#: replaced by Duration
|
|
// Time is supported for backward compatibility with OOo 3.x, x<=2
|
|
css::util::Time ut;
|
|
any >>= ut;
|
|
css::util::Duration ud;
|
|
ud.Hours = ut.Hours;
|
|
ud.Minutes = ut.Minutes;
|
|
ud.Seconds = ut.Seconds;
|
|
ud.NanoSeconds = ut.NanoSeconds;
|
|
values.push_back(durationToText(ud));
|
|
as.emplace_back(vt, OUString("time"));
|
|
} else if (type == ::cppu::UnoType<css::util::Duration>::get()) {
|
|
css::util::Duration ud;
|
|
any >>= ud;
|
|
values.push_back(durationToText(ud));
|
|
as.emplace_back(vt, OUString("time"));
|
|
} else if (::cppu::UnoType<double>::get().isAssignableFrom(type)) {
|
|
// support not just double, but anything that can be converted
|
|
double d = 0;
|
|
any >>= d;
|
|
OUStringBuffer buf;
|
|
::sax::Converter::convertDouble(buf, d);
|
|
values.push_back(buf.makeStringAndClear());
|
|
as.emplace_back(vt, OUString("float"));
|
|
} else {
|
|
SAL_WARN("sfx.doc", "Unsupported property type: " << any.getValueTypeName() );
|
|
continue;
|
|
}
|
|
attrs.push_back(as);
|
|
}
|
|
|
|
return std::make_pair(comphelper::containerToSequence(values), attrs);
|
|
}
|
|
|
|
// remove the given element from the DOM, and iff i_pAttrs != 0 insert new one
|
|
void
|
|
SfxDocumentMetaData::updateElement(const char *i_name,
|
|
std::vector<std::pair<const char *, OUString> >* i_pAttrs)
|
|
{
|
|
OUString name = OUString::createFromAscii(i_name);
|
|
try {
|
|
// remove old element
|
|
css::uno::Reference<css::xml::dom::XNode> xNode =
|
|
m_meta.find(name)->second;
|
|
if (xNode.is()) {
|
|
m_xParent->removeChild(xNode);
|
|
xNode.clear();
|
|
}
|
|
// add new element
|
|
if (nullptr != i_pAttrs) {
|
|
css::uno::Reference<css::xml::dom::XElement> xElem(
|
|
m_xDoc->createElementNS(getNameSpace(i_name), name),
|
|
css::uno::UNO_SET_THROW);
|
|
xNode.set(xElem, css::uno::UNO_QUERY_THROW);
|
|
// set attributes
|
|
for (auto const& elem : *i_pAttrs)
|
|
{
|
|
xElem->setAttributeNS(getNameSpace(elem.first),
|
|
OUString::createFromAscii(elem.first), elem.second);
|
|
}
|
|
m_xParent->appendChild(xNode);
|
|
}
|
|
m_meta[name] = xNode;
|
|
} catch (const css::xml::dom::DOMException &) {
|
|
css::uno::Any anyEx = cppu::getCaughtException();
|
|
throw css::lang::WrappedTargetRuntimeException(
|
|
"SfxDocumentMetaData::updateElement: DOM exception",
|
|
css::uno::Reference<css::uno::XInterface>(*this), anyEx);
|
|
}
|
|
}
|
|
|
|
// update user-defined meta data in DOM tree
|
|
void SfxDocumentMetaData::updateUserDefinedAndAttributes()
|
|
{
|
|
createUserDefined();
|
|
const css::uno::Reference<css::beans::XPropertySet> xPSet(m_xUserDefined,
|
|
css::uno::UNO_QUERY_THROW);
|
|
const std::pair<css::uno::Sequence< OUString>, AttrVector>
|
|
udStringsAttrs( propsToStrings(xPSet) );
|
|
(void) setMetaList("meta:user-defined", udStringsAttrs.first,
|
|
&udStringsAttrs.second);
|
|
|
|
// update elements with attributes
|
|
std::vector<std::pair<const char *, OUString> > attributes;
|
|
if (!m_TemplateName.isEmpty() || !m_TemplateURL.isEmpty()
|
|
|| isValidDateTime(m_TemplateDate)) {
|
|
attributes.emplace_back("xlink:type", OUString("simple"));
|
|
attributes.emplace_back("xlink:actuate", OUString("onRequest"));
|
|
attributes.emplace_back("xlink:title", m_TemplateName);
|
|
attributes.emplace_back("xlink:href", m_TemplateURL );
|
|
if (isValidDateTime(m_TemplateDate)) {
|
|
attributes.emplace_back(
|
|
"meta:date", dateTimeToText(m_TemplateDate));
|
|
}
|
|
updateElement("meta:template", &attributes);
|
|
} else {
|
|
updateElement("meta:template");
|
|
}
|
|
attributes.clear();
|
|
|
|
if (!m_AutoloadURL.isEmpty() || (0 != m_AutoloadSecs)) {
|
|
attributes.emplace_back("xlink:href", m_AutoloadURL );
|
|
attributes.emplace_back("meta:delay",
|
|
durationToText(m_AutoloadSecs));
|
|
updateElement("meta:auto-reload", &attributes);
|
|
} else {
|
|
updateElement("meta:auto-reload");
|
|
}
|
|
attributes.clear();
|
|
|
|
if (!m_DefaultTarget.isEmpty()) {
|
|
attributes.emplace_back(
|
|
"office:target-frame-name",
|
|
m_DefaultTarget);
|
|
// xlink:show: _blank -> new, any other value -> replace
|
|
const char* show = m_DefaultTarget == "_blank" ? "new" : "replace";
|
|
attributes.emplace_back(
|
|
"xlink:show",
|
|
OUString::createFromAscii(show));
|
|
updateElement("meta:hyperlink-behaviour", &attributes);
|
|
} else {
|
|
updateElement("meta:hyperlink-behaviour");
|
|
}
|
|
attributes.clear();
|
|
}
|
|
|
|
// create empty DOM tree (XDocument)
|
|
css::uno::Reference<css::xml::dom::XDocument>
|
|
SfxDocumentMetaData::createDOM() const // throw (css::uno::RuntimeException)
|
|
{
|
|
css::uno::Reference<css::xml::dom::XDocumentBuilder> xBuilder( css::xml::dom::DocumentBuilder::create(m_xContext) );
|
|
css::uno::Reference<css::xml::dom::XDocument> xDoc = xBuilder->newDocument();
|
|
if (!xDoc.is())
|
|
throw css::uno::RuntimeException(
|
|
"SfxDocumentMetaData::createDOM: cannot create new document",
|
|
*const_cast<SfxDocumentMetaData*>(this));
|
|
return xDoc;
|
|
}
|
|
|
|
void
|
|
SfxDocumentMetaData::checkInit() const // throw (css::uno::RuntimeException)
|
|
{
|
|
if (!m_isInitialized) {
|
|
throw css::uno::RuntimeException(
|
|
"SfxDocumentMetaData::checkInit: not initialized",
|
|
*const_cast<SfxDocumentMetaData*>(this));
|
|
}
|
|
assert(m_xDoc.is() && m_xParent.is());
|
|
}
|
|
|
|
// initialize state from DOM tree
|
|
void SfxDocumentMetaData::init(
|
|
const css::uno::Reference<css::xml::dom::XDocument>& i_xDoc)
|
|
{
|
|
if (!i_xDoc.is())
|
|
throw css::uno::RuntimeException("SfxDocumentMetaData::init: no DOM tree given", *this);
|
|
|
|
css::uno::Reference<css::xml::xpath::XXPathAPI> xPath = css::xml::xpath::XPathAPI::create(m_xContext);
|
|
|
|
m_isInitialized = false;
|
|
m_xDoc = i_xDoc;
|
|
|
|
// select nodes for standard meta data stuff
|
|
xPath->registerNS("xlink", s_nsXLink);
|
|
xPath->registerNS("dc", s_nsDC);
|
|
xPath->registerNS("office", s_nsODF);
|
|
xPath->registerNS("meta", s_nsODFMeta);
|
|
// NB: we do not handle the single-XML-file ODF variant, which would
|
|
// have the root element office:document.
|
|
// The root of such documents must be converted in the importer!
|
|
css::uno::Reference<css::xml::dom::XNode> xDocNode(
|
|
m_xDoc, css::uno::UNO_QUERY_THROW);
|
|
m_xParent.clear();
|
|
try {
|
|
m_xParent = xPath->selectSingleNode(xDocNode, "/child::office:document-meta/child::office:meta");
|
|
} catch (const css::uno::Exception &) {
|
|
}
|
|
|
|
if (!m_xParent.is()) {
|
|
// all this create/append stuff may throw DOMException
|
|
try {
|
|
css::uno::Reference<css::xml::dom::XElement> xRElem;
|
|
css::uno::Reference<css::xml::dom::XNode> xNode(
|
|
i_xDoc->getFirstChild());
|
|
while (xNode.is()) {
|
|
if (css::xml::dom::NodeType_ELEMENT_NODE ==xNode->getNodeType())
|
|
{
|
|
if ( xNode->getNamespaceURI() == s_nsODF && xNode->getLocalName() == "document-meta" )
|
|
{
|
|
xRElem.set(xNode, css::uno::UNO_QUERY_THROW);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
SAL_INFO("sfx.doc", "SfxDocumentMetaData::init(): "
|
|
"deleting unexpected root element: "
|
|
<< xNode->getLocalName());
|
|
i_xDoc->removeChild(xNode);
|
|
xNode = i_xDoc->getFirstChild(); // start over
|
|
}
|
|
} else {
|
|
xNode = xNode->getNextSibling();
|
|
}
|
|
}
|
|
if (!xRElem.is()) {
|
|
xRElem = i_xDoc->createElementNS(
|
|
s_nsODF, "office:document-meta");
|
|
css::uno::Reference<css::xml::dom::XNode> xRNode(xRElem,
|
|
css::uno::UNO_QUERY_THROW);
|
|
i_xDoc->appendChild(xRNode);
|
|
}
|
|
xRElem->setAttributeNS(s_nsODF, "office:version", "1.0");
|
|
// does not exist, otherwise m_xParent would not be null
|
|
css::uno::Reference<css::xml::dom::XNode> xParent (
|
|
i_xDoc->createElementNS(s_nsODF, "office:meta"),
|
|
css::uno::UNO_QUERY_THROW);
|
|
xRElem->appendChild(xParent);
|
|
m_xParent = xParent;
|
|
} catch (const css::xml::dom::DOMException &) {
|
|
css::uno::Any anyEx = cppu::getCaughtException();
|
|
throw css::lang::WrappedTargetRuntimeException(
|
|
"SfxDocumentMetaData::init: DOM exception",
|
|
css::uno::Reference<css::uno::XInterface>(*this), anyEx);
|
|
}
|
|
}
|
|
|
|
|
|
// select nodes for elements of which we only handle one occurrence
|
|
for (const char **pName = s_stdMeta; *pName != nullptr; ++pName) {
|
|
OUString name = OUString::createFromAscii(*pName);
|
|
// NB: If a document contains more than one occurrence of a
|
|
// meta-data element, we arbitrarily pick one of them here.
|
|
// We do not remove the others, i.e., when we write the
|
|
// document, it will contain the duplicates unchanged.
|
|
// The ODF spec says that handling multiple occurrences is
|
|
// application-specific.
|
|
css::uno::Reference<css::xml::dom::XNode> xNode =
|
|
xPath->selectSingleNode(m_xParent, "child::" + name);
|
|
// Do not create an empty element if it is missing;
|
|
// for certain elements, such as dateTime, this would be invalid
|
|
m_meta[name] = xNode;
|
|
}
|
|
|
|
// select nodes for elements of which we handle all occurrences
|
|
for (const char **pName = s_stdMetaList; *pName != nullptr; ++pName) {
|
|
OUString name = OUString::createFromAscii(*pName);
|
|
css::uno::Reference<css::xml::dom::XNodeList> nodes =
|
|
xPath->selectNodeList(m_xParent, "child::" + name);
|
|
std::vector<css::uno::Reference<css::xml::dom::XNode> > v;
|
|
v.reserve(nodes->getLength());
|
|
for (sal_Int32 i = 0; i < nodes->getLength(); ++i)
|
|
{
|
|
v.push_back(nodes->item(i));
|
|
}
|
|
m_metaList[name] = v;
|
|
}
|
|
|
|
// initialize members corresponding to attributes from DOM nodes
|
|
m_TemplateName = getMetaAttr("meta:template", "xlink:title");
|
|
m_TemplateURL = getMetaAttr("meta:template", "xlink:href");
|
|
m_TemplateDate =
|
|
textToDateTimeDefault(getMetaAttr("meta:template", "meta:date"));
|
|
m_AutoloadURL = getMetaAttr("meta:auto-reload", "xlink:href");
|
|
m_AutoloadSecs =
|
|
textToDuration(getMetaAttr("meta:auto-reload", "meta:delay"));
|
|
m_DefaultTarget =
|
|
getMetaAttr("meta:hyperlink-behaviour", "office:target-frame-name");
|
|
|
|
|
|
std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
|
|
m_metaList[OUString("meta:user-defined")];
|
|
m_xUserDefined.clear(); // #i105826#: reset (may be re-initialization)
|
|
if ( !vec.empty() )
|
|
{
|
|
createUserDefined();
|
|
}
|
|
|
|
// user-defined meta data: initialize PropertySet from DOM nodes
|
|
for (auto const& elem : vec)
|
|
{
|
|
css::uno::Reference<css::xml::dom::XElement> xElem(elem,
|
|
css::uno::UNO_QUERY_THROW);
|
|
css::uno::Any any;
|
|
OUString name = xElem->getAttributeNS(s_nsODFMeta, "name");
|
|
OUString type = xElem->getAttributeNS(s_nsODFMeta, "value-type");
|
|
OUString text = getNodeText(elem);
|
|
if ( type == "float" ) {
|
|
double d;
|
|
if (::sax::Converter::convertDouble(d, text)) {
|
|
any <<= d;
|
|
} else {
|
|
SAL_WARN("sfx.doc", "Invalid float: " << text);
|
|
continue;
|
|
}
|
|
} else if ( type == "date" ) {
|
|
bool isDateTime;
|
|
css::util::Date d;
|
|
css::util::DateTime dt;
|
|
std::optional<sal_Int16> nTimeZone;
|
|
if (textToDateOrDateTime(d, dt, isDateTime, nTimeZone, text)) {
|
|
if (isDateTime) {
|
|
if (nTimeZone) {
|
|
any <<= css::util::DateTimeWithTimezone(dt,
|
|
*nTimeZone);
|
|
} else {
|
|
any <<= dt;
|
|
}
|
|
} else {
|
|
if (nTimeZone) {
|
|
any <<= css::util::DateWithTimezone(d, *nTimeZone);
|
|
} else {
|
|
any <<= d;
|
|
}
|
|
}
|
|
} else {
|
|
SAL_WARN("sfx.doc", "Invalid date: " << text);
|
|
continue;
|
|
}
|
|
} else if ( type == "time" ) {
|
|
css::util::Duration ud;
|
|
if (textToDuration(ud, text)) {
|
|
any <<= ud;
|
|
} else {
|
|
SAL_WARN("sfx.doc", "Invalid time: " << text);
|
|
continue;
|
|
}
|
|
} else if ( type == "boolean" ) {
|
|
bool b;
|
|
if (::sax::Converter::convertBool(b, text)) {
|
|
any <<= b;
|
|
} else {
|
|
SAL_WARN("sfx.doc", "Invalid boolean: " << text);
|
|
continue;
|
|
}
|
|
} else { // default
|
|
any <<= text;
|
|
}
|
|
try {
|
|
m_xUserDefined->addProperty(name,
|
|
css::beans::PropertyAttribute::REMOVABLE, any);
|
|
} catch (const css::beans::PropertyExistException &) {
|
|
SAL_WARN("sfx.doc", "Duplicate: " << name);
|
|
// ignore; duplicate
|
|
} catch (const css::beans::IllegalTypeException &) {
|
|
SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal type: " << name);
|
|
} catch (const css::lang::IllegalArgumentException &) {
|
|
SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal arg: " << name);
|
|
}
|
|
}
|
|
|
|
m_isModified = false;
|
|
m_isInitialized = true;
|
|
}
|
|
|
|
|
|
SfxDocumentMetaData::SfxDocumentMetaData(
|
|
css::uno::Reference< css::uno::XComponentContext > const & context)
|
|
: BaseMutex()
|
|
, SfxDocumentMetaData_Base(m_aMutex)
|
|
, m_xContext(context)
|
|
, m_NotifyListeners(m_aMutex)
|
|
, m_isInitialized(false)
|
|
, m_isModified(false)
|
|
, m_AutoloadSecs(0)
|
|
{
|
|
assert(context.is());
|
|
assert(context->getServiceManager().is());
|
|
init(createDOM());
|
|
}
|
|
|
|
// com.sun.star.uno.XServiceInfo:
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getImplementationName()
|
|
{
|
|
return "SfxDocumentMetaData";
|
|
}
|
|
|
|
sal_Bool SAL_CALL
|
|
SfxDocumentMetaData::supportsService(OUString const & serviceName)
|
|
{
|
|
return cppu::supportsService(this, serviceName);
|
|
}
|
|
|
|
css::uno::Sequence< OUString > SAL_CALL
|
|
SfxDocumentMetaData::getSupportedServiceNames()
|
|
{
|
|
css::uno::Sequence< OUString > s { "com.sun.star.document.DocumentProperties" };
|
|
return s;
|
|
}
|
|
|
|
|
|
// css::lang::XComponent:
|
|
void SAL_CALL SfxDocumentMetaData::dispose()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
if (!m_isInitialized) {
|
|
return;
|
|
}
|
|
WeakComponentImplHelperBase::dispose(); // superclass
|
|
m_NotifyListeners.disposeAndClear(css::lang::EventObject(
|
|
static_cast< ::cppu::OWeakObject* >(this)));
|
|
m_isInitialized = false;
|
|
m_meta.clear();
|
|
m_metaList.clear();
|
|
m_xParent.clear();
|
|
m_xDoc.clear();
|
|
m_xUserDefined.clear();
|
|
}
|
|
|
|
|
|
// css::document::XDocumentProperties:
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getAuthor()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return getMetaText("meta:initial-creator");
|
|
}
|
|
|
|
void SAL_CALL SfxDocumentMetaData::setAuthor(const OUString & the_value)
|
|
{
|
|
setMetaTextAndNotify("meta:initial-creator", the_value);
|
|
}
|
|
|
|
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getGenerator()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return getMetaText("meta:generator");
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setGenerator(const OUString & the_value)
|
|
{
|
|
setMetaTextAndNotify("meta:generator", the_value);
|
|
}
|
|
|
|
css::util::DateTime SAL_CALL
|
|
SfxDocumentMetaData::getCreationDate()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return textToDateTimeDefault(getMetaText("meta:creation-date"));
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setCreationDate(const css::util::DateTime & the_value)
|
|
{
|
|
setMetaTextAndNotify("meta:creation-date", dateTimeToText(the_value));
|
|
}
|
|
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getTitle()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return getMetaText("dc:title");
|
|
}
|
|
|
|
void SAL_CALL SfxDocumentMetaData::setTitle(const OUString & the_value)
|
|
{
|
|
setMetaTextAndNotify("dc:title", the_value);
|
|
}
|
|
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getSubject()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return getMetaText("dc:subject");
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setSubject(const OUString & the_value)
|
|
{
|
|
setMetaTextAndNotify("dc:subject", the_value);
|
|
}
|
|
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getDescription()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return getMetaText("dc:description");
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setDescription(const OUString & the_value)
|
|
{
|
|
setMetaTextAndNotify("dc:description", the_value);
|
|
}
|
|
|
|
css::uno::Sequence< OUString >
|
|
SAL_CALL SfxDocumentMetaData::getKeywords()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return getMetaList("meta:keyword");
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setKeywords(
|
|
const css::uno::Sequence< OUString > & the_value)
|
|
{
|
|
::osl::ClearableMutexGuard g(m_aMutex);
|
|
if (setMetaList("meta:keyword", the_value, nullptr)) {
|
|
g.clear();
|
|
setModified(true);
|
|
}
|
|
}
|
|
|
|
css::lang::Locale SAL_CALL
|
|
SfxDocumentMetaData::getLanguage()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
css::lang::Locale loc( LanguageTag::convertToLocale( getMetaText("dc:language"), false));
|
|
return loc;
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setLanguage(const css::lang::Locale & the_value)
|
|
{
|
|
OUString text( LanguageTag::convertToBcp47( the_value, false));
|
|
setMetaTextAndNotify("dc:language", text);
|
|
}
|
|
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getModifiedBy()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return getMetaText("dc:creator");
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setModifiedBy(const OUString & the_value)
|
|
{
|
|
setMetaTextAndNotify("dc:creator", the_value);
|
|
}
|
|
|
|
css::util::DateTime SAL_CALL
|
|
SfxDocumentMetaData::getModificationDate()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return textToDateTimeDefault(getMetaText("dc:date"));
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setModificationDate(const css::util::DateTime & the_value)
|
|
{
|
|
setMetaTextAndNotify("dc:date", dateTimeToText(the_value));
|
|
}
|
|
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getPrintedBy()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return getMetaText("meta:printed-by");
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setPrintedBy(const OUString & the_value)
|
|
{
|
|
setMetaTextAndNotify("meta:printed-by", the_value);
|
|
}
|
|
|
|
css::util::DateTime SAL_CALL
|
|
SfxDocumentMetaData::getPrintDate()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return textToDateTimeDefault(getMetaText("meta:print-date"));
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setPrintDate(const css::util::DateTime & the_value)
|
|
{
|
|
setMetaTextAndNotify("meta:print-date", dateTimeToText(the_value));
|
|
}
|
|
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getTemplateName()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
return m_TemplateName;
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setTemplateName(const OUString & the_value)
|
|
{
|
|
::osl::ClearableMutexGuard g(m_aMutex);
|
|
checkInit();
|
|
if (m_TemplateName != the_value) {
|
|
m_TemplateName = the_value;
|
|
g.clear();
|
|
setModified(true);
|
|
}
|
|
}
|
|
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getTemplateURL()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
return m_TemplateURL;
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setTemplateURL(const OUString & the_value)
|
|
{
|
|
::osl::ClearableMutexGuard g(m_aMutex);
|
|
checkInit();
|
|
if (m_TemplateURL != the_value) {
|
|
m_TemplateURL = the_value;
|
|
g.clear();
|
|
setModified(true);
|
|
}
|
|
}
|
|
|
|
css::util::DateTime SAL_CALL
|
|
SfxDocumentMetaData::getTemplateDate()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
return m_TemplateDate;
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setTemplateDate(const css::util::DateTime & the_value)
|
|
{
|
|
::osl::ClearableMutexGuard g(m_aMutex);
|
|
checkInit();
|
|
if (m_TemplateDate != the_value) {
|
|
m_TemplateDate = the_value;
|
|
g.clear();
|
|
setModified(true);
|
|
}
|
|
}
|
|
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getAutoloadURL()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
return m_AutoloadURL;
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setAutoloadURL(const OUString & the_value)
|
|
{
|
|
::osl::ClearableMutexGuard g(m_aMutex);
|
|
checkInit();
|
|
if (m_AutoloadURL != the_value) {
|
|
m_AutoloadURL = the_value;
|
|
g.clear();
|
|
setModified(true);
|
|
}
|
|
}
|
|
|
|
::sal_Int32 SAL_CALL
|
|
SfxDocumentMetaData::getAutoloadSecs()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
return m_AutoloadSecs;
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setAutoloadSecs(::sal_Int32 the_value)
|
|
{
|
|
if (the_value < 0)
|
|
throw css::lang::IllegalArgumentException(
|
|
"SfxDocumentMetaData::setAutoloadSecs: argument is negative",
|
|
*this, 0);
|
|
::osl::ClearableMutexGuard g(m_aMutex);
|
|
checkInit();
|
|
if (m_AutoloadSecs != the_value) {
|
|
m_AutoloadSecs = the_value;
|
|
g.clear();
|
|
setModified(true);
|
|
}
|
|
}
|
|
|
|
OUString SAL_CALL
|
|
SfxDocumentMetaData::getDefaultTarget()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
return m_DefaultTarget;
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setDefaultTarget(const OUString & the_value)
|
|
{
|
|
::osl::ClearableMutexGuard g(m_aMutex);
|
|
checkInit();
|
|
if (m_DefaultTarget != the_value) {
|
|
m_DefaultTarget = the_value;
|
|
g.clear();
|
|
setModified(true);
|
|
}
|
|
}
|
|
|
|
css::uno::Sequence< css::beans::NamedValue > SAL_CALL
|
|
SfxDocumentMetaData::getDocumentStatistics()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
::std::vector<css::beans::NamedValue> stats;
|
|
for (size_t i = 0; s_stdStats[i] != nullptr; ++i) {
|
|
const char * aName = s_stdStatAttrs[i];
|
|
OUString text = getMetaAttr("meta:document-statistic", aName);
|
|
if (text.isEmpty()) continue;
|
|
css::beans::NamedValue stat;
|
|
stat.Name = OUString::createFromAscii(s_stdStats[i]);
|
|
sal_Int32 val;
|
|
css::uno::Any any;
|
|
if (!::sax::Converter::convertNumber(val, text, 0) || (val < 0)) {
|
|
val = 0;
|
|
SAL_WARN("sfx.doc", "Invalid number: " << text);
|
|
}
|
|
any <<= val;
|
|
stat.Value = any;
|
|
stats.push_back(stat);
|
|
}
|
|
|
|
return ::comphelper::containerToSequence(stats);
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setDocumentStatistics(
|
|
const css::uno::Sequence< css::beans::NamedValue > & the_value)
|
|
{
|
|
{
|
|
osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
std::vector<std::pair<const char *, OUString> > attributes;
|
|
for (const auto& rValue : the_value) {
|
|
const OUString name = rValue.Name;
|
|
// inefficiently search for matching attribute
|
|
for (size_t j = 0; s_stdStats[j] != nullptr; ++j) {
|
|
if (name.equalsAscii(s_stdStats[j])) {
|
|
const css::uno::Any any = rValue.Value;
|
|
sal_Int32 val = 0;
|
|
if (any >>= val) {
|
|
attributes.emplace_back(s_stdStatAttrs[j],
|
|
OUString::number(val));
|
|
}
|
|
else {
|
|
SAL_WARN("sfx.doc", "Invalid statistic: " << name);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
updateElement("meta:document-statistic", &attributes);
|
|
}
|
|
setModified(true);
|
|
}
|
|
|
|
::sal_Int16 SAL_CALL
|
|
SfxDocumentMetaData::getEditingCycles()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
OUString text = getMetaText("meta:editing-cycles");
|
|
sal_Int32 ret;
|
|
if (::sax::Converter::convertNumber(ret, text,
|
|
0, std::numeric_limits<sal_Int16>::max())) {
|
|
return static_cast<sal_Int16>(ret);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setEditingCycles(::sal_Int16 the_value)
|
|
{
|
|
if (the_value < 0)
|
|
throw css::lang::IllegalArgumentException(
|
|
"SfxDocumentMetaData::setEditingCycles: argument is negative",
|
|
*this, 0);
|
|
setMetaTextAndNotify("meta:editing-cycles", OUString::number(the_value));
|
|
}
|
|
|
|
::sal_Int32 SAL_CALL
|
|
SfxDocumentMetaData::getEditingDuration()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
return textToDuration(getMetaText("meta:editing-duration"));
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::setEditingDuration(::sal_Int32 the_value)
|
|
{
|
|
if (the_value < 0)
|
|
throw css::lang::IllegalArgumentException(
|
|
"SfxDocumentMetaData::setEditingDuration: argument is negative",
|
|
*this, 0);
|
|
setMetaTextAndNotify("meta:editing-duration", durationToText(the_value));
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::resetUserData(const OUString & the_value)
|
|
{
|
|
::osl::ClearableMutexGuard g(m_aMutex);
|
|
|
|
bool bModified( false );
|
|
bModified |= setMetaText("meta:initial-creator", the_value);
|
|
::DateTime now( ::DateTime::SYSTEM );
|
|
css::util::DateTime uDT(now.GetUNODateTime());
|
|
bModified |= setMetaText("meta:creation-date", dateTimeToText(uDT));
|
|
bModified |= setMetaText("dc:creator", OUString());
|
|
bModified |= setMetaText("meta:printed-by", OUString());
|
|
bModified |= setMetaText("dc:date", dateTimeToText(css::util::DateTime()));
|
|
bModified |= setMetaText("meta:print-date",
|
|
dateTimeToText(css::util::DateTime()));
|
|
bModified |= setMetaText("meta:editing-duration", durationToText(0));
|
|
bModified |= setMetaText("meta:editing-cycles",
|
|
"1");
|
|
|
|
if (bModified) {
|
|
g.clear();
|
|
setModified(true);
|
|
}
|
|
}
|
|
|
|
|
|
css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
|
|
SfxDocumentMetaData::getUserDefinedProperties()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
createUserDefined();
|
|
return m_xUserDefined;
|
|
}
|
|
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::loadFromStorage(
|
|
const css::uno::Reference< css::embed::XStorage > & xStorage,
|
|
const css::uno::Sequence< css::beans::PropertyValue > & Medium)
|
|
{
|
|
if (!xStorage.is())
|
|
throw css::lang::IllegalArgumentException("SfxDocumentMetaData::loadFromStorage: argument is null", *this, 0);
|
|
::osl::MutexGuard g(m_aMutex);
|
|
|
|
// open meta data file
|
|
css::uno::Reference<css::io::XStream> xStream(
|
|
xStorage->openStreamElement(
|
|
s_meta,
|
|
css::embed::ElementModes::READ) );
|
|
if (!xStream.is()) throw css::uno::RuntimeException();
|
|
css::uno::Reference<css::io::XInputStream> xInStream =
|
|
xStream->getInputStream();
|
|
if (!xInStream.is()) throw css::uno::RuntimeException();
|
|
|
|
// create DOM parser service
|
|
css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
|
|
m_xContext->getServiceManager());
|
|
css::xml::sax::InputSource input;
|
|
input.aInputStream = xInStream;
|
|
|
|
sal_uInt64 version = SotStorage::GetVersion( xStorage );
|
|
// Oasis is also the default (0)
|
|
bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
|
|
const char *pServiceName = bOasis
|
|
? "com.sun.star.document.XMLOasisMetaImporter"
|
|
: "com.sun.star.document.XMLMetaImporter";
|
|
|
|
// set base URL
|
|
css::uno::Reference<css::beans::XPropertySet> xPropArg =
|
|
getURLProperties(Medium);
|
|
try {
|
|
xPropArg->getPropertyValue("BaseURI")
|
|
>>= input.sSystemId;
|
|
input.sSystemId += OUString::Concat("/") + s_meta;
|
|
} catch (const css::uno::Exception &) {
|
|
input.sSystemId = s_meta;
|
|
}
|
|
css::uno::Sequence< css::uno::Any > args{ css::uno::Any(xPropArg) };
|
|
|
|
// the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler
|
|
css::uno::Reference<XInterface> xFilter =
|
|
xMsf->createInstanceWithArgumentsAndContext(
|
|
OUString::createFromAscii(pServiceName), args, m_xContext);
|
|
assert(xFilter);
|
|
css::uno::Reference<css::xml::sax::XFastParser> xFastParser(xFilter, css::uno::UNO_QUERY);
|
|
css::uno::Reference<css::document::XImporter> xImp(xFilter, css::uno::UNO_QUERY_THROW);
|
|
xImp->setTargetDocument(css::uno::Reference<css::lang::XComponent>(this));
|
|
try {
|
|
if (xFastParser)
|
|
xFastParser->parseStream(input);
|
|
else
|
|
{
|
|
css::uno::Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, css::uno::UNO_QUERY_THROW);
|
|
css::uno::Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(m_xContext);
|
|
xParser->setDocumentHandler(xDocHandler);
|
|
xParser->parseStream(input);
|
|
}
|
|
} catch (const css::xml::sax::SAXException &) {
|
|
throw css::io::WrongFormatException(
|
|
"SfxDocumentMetaData::loadFromStorage:"
|
|
" XML parsing exception", *this);
|
|
}
|
|
// NB: the implementation of XMLOasisMetaImporter calls initialize
|
|
checkInit();
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::storeToStorage(
|
|
const css::uno::Reference< css::embed::XStorage > & xStorage,
|
|
const css::uno::Sequence< css::beans::PropertyValue > & Medium)
|
|
{
|
|
if (!xStorage.is())
|
|
throw css::lang::IllegalArgumentException(
|
|
"SfxDocumentMetaData::storeToStorage: argument is null", *this, 0);
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
|
|
// update user-defined meta data in DOM tree
|
|
// updateUserDefinedAndAttributes(); // this will be done in serialize!
|
|
|
|
// write into storage
|
|
css::uno::Reference<css::io::XStream> xStream =
|
|
xStorage->openStreamElement(s_meta,
|
|
css::embed::ElementModes::WRITE
|
|
| css::embed::ElementModes::TRUNCATE);
|
|
if (!xStream.is()) throw css::uno::RuntimeException();
|
|
css::uno::Reference< css::beans::XPropertySet > xStreamProps(xStream,
|
|
css::uno::UNO_QUERY_THROW);
|
|
xStreamProps->setPropertyValue(
|
|
"MediaType",
|
|
css::uno::makeAny(OUString("text/xml")));
|
|
xStreamProps->setPropertyValue(
|
|
"Compressed",
|
|
css::uno::makeAny(false));
|
|
xStreamProps->setPropertyValue(
|
|
"UseCommonStoragePasswordEncryption",
|
|
css::uno::makeAny(false));
|
|
css::uno::Reference<css::io::XOutputStream> xOutStream =
|
|
xStream->getOutputStream();
|
|
if (!xOutStream.is()) throw css::uno::RuntimeException();
|
|
css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
|
|
m_xContext->getServiceManager());
|
|
css::uno::Reference<css::xml::sax::XWriter> xSaxWriter(
|
|
css::xml::sax::Writer::create(m_xContext));
|
|
xSaxWriter->setOutputStream(xOutStream);
|
|
|
|
const sal_uInt64 version = SotStorage::GetVersion( xStorage );
|
|
// Oasis is also the default (0)
|
|
const bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
|
|
const char *pServiceName = bOasis
|
|
? "com.sun.star.document.XMLOasisMetaExporter"
|
|
: "com.sun.star.document.XMLMetaExporter";
|
|
|
|
// set base URL
|
|
css::uno::Reference<css::beans::XPropertySet> xPropArg =
|
|
getURLProperties(Medium);
|
|
css::uno::Sequence< css::uno::Any > args{ css::uno::Any(xSaxWriter), css::uno::Any(xPropArg) };
|
|
|
|
css::uno::Reference<css::document::XExporter> xExp(
|
|
xMsf->createInstanceWithArgumentsAndContext(
|
|
OUString::createFromAscii(pServiceName), args, m_xContext),
|
|
css::uno::UNO_QUERY_THROW);
|
|
xExp->setSourceDocument(css::uno::Reference<css::lang::XComponent>(this));
|
|
css::uno::Reference<css::document::XFilter> xFilter(xExp,
|
|
css::uno::UNO_QUERY_THROW);
|
|
if (!xFilter->filter(css::uno::Sequence< css::beans::PropertyValue >())) {
|
|
throw css::io::IOException(
|
|
"SfxDocumentMetaData::storeToStorage: cannot filter", *this);
|
|
}
|
|
css::uno::Reference<css::embed::XTransactedObject> xTransaction(
|
|
xStorage, css::uno::UNO_QUERY);
|
|
if (xTransaction.is()) {
|
|
xTransaction->commit();
|
|
}
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::loadFromMedium(const OUString & URL,
|
|
const css::uno::Sequence< css::beans::PropertyValue > & Medium)
|
|
{
|
|
css::uno::Reference<css::io::XInputStream> xIn;
|
|
utl::MediaDescriptor md(Medium);
|
|
// if we have a URL parameter, it replaces the one in the media descriptor
|
|
if (!URL.isEmpty()) {
|
|
md[ utl::MediaDescriptor::PROP_URL ] <<= URL;
|
|
md[ utl::MediaDescriptor::PROP_READONLY ] <<= true;
|
|
}
|
|
if (md.addInputStream()) {
|
|
md[ utl::MediaDescriptor::PROP_INPUTSTREAM ] >>= xIn;
|
|
}
|
|
css::uno::Reference<css::embed::XStorage> xStorage;
|
|
try {
|
|
if (xIn.is()) {
|
|
xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
|
|
xIn, m_xContext);
|
|
} else { // fallback to url parameter
|
|
xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(
|
|
URL, css::embed::ElementModes::READ, m_xContext);
|
|
}
|
|
} catch (const css::uno::RuntimeException &) {
|
|
throw;
|
|
} catch (const css::io::IOException &) {
|
|
throw;
|
|
} catch (const css::uno::Exception &) {
|
|
css::uno::Any anyEx = cppu::getCaughtException();
|
|
throw css::lang::WrappedTargetException(
|
|
"SfxDocumentMetaData::loadFromMedium: exception",
|
|
css::uno::Reference<css::uno::XInterface>(*this),
|
|
anyEx);
|
|
}
|
|
if (!xStorage.is()) {
|
|
throw css::uno::RuntimeException(
|
|
"SfxDocumentMetaData::loadFromMedium: cannot get Storage",
|
|
*this);
|
|
}
|
|
loadFromStorage(xStorage, md.getAsConstPropertyValueList());
|
|
}
|
|
|
|
void SAL_CALL
|
|
SfxDocumentMetaData::storeToMedium(const OUString & URL,
|
|
const css::uno::Sequence< css::beans::PropertyValue > & Medium)
|
|
{
|
|
utl::MediaDescriptor md(Medium);
|
|
if (!URL.isEmpty()) {
|
|
md[ utl::MediaDescriptor::PROP_URL ] <<= URL;
|
|
}
|
|
SfxMedium aMedium(md.getAsConstPropertyValueList());
|
|
css::uno::Reference<css::embed::XStorage> xStorage
|
|
= aMedium.GetOutputStorage();
|
|
|
|
|
|
if (!xStorage.is()) {
|
|
throw css::uno::RuntimeException(
|
|
"SfxDocumentMetaData::storeToMedium: cannot get Storage",
|
|
*this);
|
|
}
|
|
// set MIME type of the storage
|
|
utl::MediaDescriptor::const_iterator iter
|
|
= md.find(utl::MediaDescriptor::PROP_MEDIATYPE);
|
|
if (iter != md.end()) {
|
|
css::uno::Reference< css::beans::XPropertySet > xProps(xStorage,
|
|
css::uno::UNO_QUERY_THROW);
|
|
xProps->setPropertyValue(
|
|
utl::MediaDescriptor::PROP_MEDIATYPE,
|
|
iter->second);
|
|
}
|
|
storeToStorage(xStorage, md.getAsConstPropertyValueList());
|
|
|
|
|
|
const bool bOk = aMedium.Commit();
|
|
aMedium.Close();
|
|
if ( !bOk ) {
|
|
ErrCode nError = aMedium.GetError();
|
|
if ( nError == ERRCODE_NONE ) {
|
|
nError = ERRCODE_IO_GENERAL;
|
|
}
|
|
|
|
throw css::task::ErrorCodeIOException(
|
|
"SfxDocumentMetaData::storeToMedium <" + URL + "> Commit failed: " + nError.toHexString(),
|
|
css::uno::Reference< css::uno::XInterface >(), sal_uInt32(nError));
|
|
|
|
}
|
|
}
|
|
|
|
// css::lang::XInitialization:
|
|
void SAL_CALL SfxDocumentMetaData::initialize( const css::uno::Sequence< css::uno::Any > & aArguments)
|
|
{
|
|
// possible arguments:
|
|
// - no argument: default initialization (empty DOM)
|
|
// - 1 argument, XDocument: initialize with given DOM and empty base URL
|
|
// NB: links in document must be absolute
|
|
|
|
::osl::MutexGuard g(m_aMutex);
|
|
css::uno::Reference<css::xml::dom::XDocument> xDoc;
|
|
|
|
for (sal_Int32 i = 0; i < aArguments.getLength(); ++i) {
|
|
const css::uno::Any any = aArguments[i];
|
|
if (!(any >>= xDoc)) {
|
|
throw css::lang::IllegalArgumentException(
|
|
"SfxDocumentMetaData::initialize: argument must be XDocument",
|
|
*this, static_cast<sal_Int16>(i));
|
|
}
|
|
if (!xDoc.is()) {
|
|
throw css::lang::IllegalArgumentException(
|
|
"SfxDocumentMetaData::initialize: argument is null",
|
|
*this, static_cast<sal_Int16>(i));
|
|
}
|
|
}
|
|
|
|
if (!xDoc.is()) {
|
|
// For a new document, we create a new DOM tree here.
|
|
xDoc = createDOM();
|
|
}
|
|
|
|
init(xDoc);
|
|
}
|
|
|
|
// css::util::XCloneable:
|
|
css::uno::Reference<css::util::XCloneable> SAL_CALL
|
|
SfxDocumentMetaData::createClone()
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
|
|
rtl::Reference<SfxDocumentMetaData> pNew = createMe(m_xContext);
|
|
|
|
// NB: do not copy the modification listeners, only DOM
|
|
css::uno::Reference<css::xml::dom::XDocument> xDoc = createDOM();
|
|
try {
|
|
updateUserDefinedAndAttributes();
|
|
// deep copy of root node
|
|
css::uno::Reference<css::xml::dom::XNode> xRoot(
|
|
m_xDoc->getDocumentElement(), css::uno::UNO_QUERY_THROW);
|
|
css::uno::Reference<css::xml::dom::XNode> xRootNew(
|
|
xDoc->importNode(xRoot, true));
|
|
xDoc->appendChild(xRootNew);
|
|
pNew->init(xDoc);
|
|
} catch (const css::uno::RuntimeException &) {
|
|
throw;
|
|
} catch (const css::uno::Exception &) {
|
|
css::uno::Any anyEx = cppu::getCaughtException();
|
|
throw css::lang::WrappedTargetRuntimeException(
|
|
"SfxDocumentMetaData::createClone: exception",
|
|
css::uno::Reference<css::uno::XInterface>(*this), anyEx);
|
|
}
|
|
return css::uno::Reference<css::util::XCloneable> (pNew);
|
|
}
|
|
|
|
// css::util::XModifiable:
|
|
sal_Bool SAL_CALL SfxDocumentMetaData::isModified( )
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
css::uno::Reference<css::util::XModifiable> xMB(m_xUserDefined,
|
|
css::uno::UNO_QUERY);
|
|
return m_isModified || (xMB.is() && xMB->isModified());
|
|
}
|
|
|
|
void SAL_CALL SfxDocumentMetaData::setModified( sal_Bool bModified )
|
|
{
|
|
css::uno::Reference<css::util::XModifiable> xMB;
|
|
{ // do not lock mutex while notifying (#i93514#) to prevent deadlock
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
m_isModified = bModified;
|
|
if ( !bModified && m_xUserDefined.is() )
|
|
{
|
|
xMB.set(m_xUserDefined, css::uno::UNO_QUERY);
|
|
assert(xMB.is() &&
|
|
"SfxDocumentMetaData::setModified: PropertyBag not Modifiable?");
|
|
}
|
|
}
|
|
if (bModified) {
|
|
try {
|
|
css::uno::Reference<css::uno::XInterface> xThis(*this);
|
|
css::lang::EventObject event(xThis);
|
|
m_NotifyListeners.notifyEach(&css::util::XModifyListener::modified,
|
|
event);
|
|
} catch (const css::uno::RuntimeException &) {
|
|
throw;
|
|
} catch (const css::uno::Exception &) {
|
|
// ignore
|
|
TOOLS_WARN_EXCEPTION("sfx.doc", "setModified");
|
|
}
|
|
} else {
|
|
if (xMB.is()) {
|
|
xMB->setModified(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// css::util::XModifyBroadcaster:
|
|
void SAL_CALL SfxDocumentMetaData::addModifyListener(
|
|
const css::uno::Reference< css::util::XModifyListener > & xListener)
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
m_NotifyListeners.addInterface(xListener);
|
|
css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
|
|
css::uno::UNO_QUERY);
|
|
if (xMB.is()) {
|
|
xMB->addModifyListener(xListener);
|
|
}
|
|
}
|
|
|
|
void SAL_CALL SfxDocumentMetaData::removeModifyListener(
|
|
const css::uno::Reference< css::util::XModifyListener > & xListener)
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
m_NotifyListeners.removeInterface(xListener);
|
|
css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
|
|
css::uno::UNO_QUERY);
|
|
if (xMB.is()) {
|
|
xMB->removeModifyListener(xListener);
|
|
}
|
|
}
|
|
|
|
// css::xml::sax::XSAXSerializable
|
|
void SAL_CALL SfxDocumentMetaData::serialize(
|
|
const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
|
|
const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces)
|
|
{
|
|
::osl::MutexGuard g(m_aMutex);
|
|
checkInit();
|
|
updateUserDefinedAndAttributes();
|
|
css::uno::Reference<css::xml::sax::XSAXSerializable> xSAXable(m_xDoc,
|
|
css::uno::UNO_QUERY_THROW);
|
|
xSAXable->serialize(i_xHandler, i_rNamespaces);
|
|
}
|
|
|
|
void SfxDocumentMetaData::createUserDefined()
|
|
{
|
|
// user-defined meta data: create PropertyBag which only accepts property
|
|
// values of allowed types
|
|
if ( m_xUserDefined.is() )
|
|
return;
|
|
|
|
css::uno::Sequence<css::uno::Type> types{
|
|
::cppu::UnoType<bool>::get(),
|
|
::cppu::UnoType< OUString>::get(),
|
|
::cppu::UnoType<css::util::DateTime>::get(),
|
|
::cppu::UnoType<css::util::Date>::get(),
|
|
::cppu::UnoType<css::util::DateTimeWithTimezone>::get(),
|
|
::cppu::UnoType<css::util::DateWithTimezone>::get(),
|
|
::cppu::UnoType<css::util::Duration>::get(),
|
|
::cppu::UnoType<float>::get(),
|
|
::cppu::UnoType<double>::get(),
|
|
::cppu::UnoType<sal_Int16>::get(),
|
|
::cppu::UnoType<sal_Int32>::get(),
|
|
::cppu::UnoType<sal_Int64>::get(),
|
|
// Time is supported for backward compatibility with OOo 3.x, x<=2
|
|
::cppu::UnoType<css::util::Time>::get()
|
|
};
|
|
// #i94175#: ODF allows empty user-defined property names!
|
|
m_xUserDefined.set(
|
|
css::beans::PropertyBag::createWithTypes( m_xContext, types, true/*AllowEmptyPropertyName*/, false/*AutomaticAddition*/ ),
|
|
css::uno::UNO_QUERY_THROW);
|
|
|
|
const css::uno::Reference<css::util::XModifyBroadcaster> xMB(
|
|
m_xUserDefined, css::uno::UNO_QUERY);
|
|
if (xMB.is())
|
|
{
|
|
const std::vector<css::uno::Reference<css::util::XModifyListener> >
|
|
listeners(m_NotifyListeners.getElements());
|
|
for (const auto& l : listeners) {
|
|
xMB->addModifyListener(l);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // closing anonymous implementation namespace
|
|
|
|
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
|
|
CompatWriterDocPropsImpl_get_implementation(
|
|
css::uno::XComponentContext *context,
|
|
css::uno::Sequence<css::uno::Any> const &)
|
|
{
|
|
return cppu::acquire(new CompatWriterDocPropsImpl(context));
|
|
}
|
|
|
|
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
|
|
SfxDocumentMetaData_get_implementation(
|
|
css::uno::XComponentContext *context,
|
|
css::uno::Sequence<css::uno::Any> const &)
|
|
{
|
|
return cppu::acquire(new SfxDocumentMetaData(context));
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|