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