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