Update ooo320-m1
[ooovba.git] / xmloff / source / meta / xmlmetae.cxx
blobfb2893201fa5383341406489f19c6c935d1f1db5
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: xmlmetae.cxx,v $
10 * $Revision: 1.27 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_xmloff.hxx"
34 #include <tools/debug.hxx>
35 #include <tools/inetdef.hxx>
36 #include <i18npool/mslangid.hxx>
37 #include <tools/urlobj.hxx>
38 #include <tools/time.hxx>
39 #include <rtl/ustrbuf.hxx>
41 #include <xmloff/xmlmetae.hxx>
42 #include <xmloff/xmlexp.hxx>
43 #include <xmloff/xmluconv.hxx>
44 #include <xmloff/nmspmap.hxx>
45 #include "xmlnmspe.hxx"
47 #include <com/sun/star/beans/XPropertyAccess.hpp>
48 #include <com/sun/star/beans/StringPair.hpp>
49 #include <com/sun/star/xml/dom/XDocument.hpp>
50 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
52 #include <comphelper/sequenceasvector.hxx>
53 #include <unotools/docinfohelper.hxx>
55 #include <string.h>
58 using namespace com::sun::star;
59 using namespace ::xmloff::token;
62 //-------------------------------------------------------------------------
64 void lcl_AddTwoDigits( rtl::OUStringBuffer& rStr, sal_Int32 nVal )
66 if ( nVal < 10 )
67 rStr.append( sal_Unicode('0') );
68 rStr.append( nVal );
71 rtl::OUString
72 SvXMLMetaExport::GetISODateTimeString( const util::DateTime& rDateTime )
74 // return ISO date string "YYYY-MM-DDThh:mm:ss"
76 rtl::OUStringBuffer sTmp;
77 sTmp.append( (sal_Int32) rDateTime.Year );
78 sTmp.append( sal_Unicode('-') );
79 lcl_AddTwoDigits( sTmp, rDateTime.Month );
80 sTmp.append( sal_Unicode('-') );
81 lcl_AddTwoDigits( sTmp, rDateTime.Day );
82 sTmp.append( sal_Unicode('T') );
83 lcl_AddTwoDigits( sTmp, rDateTime.Hours );
84 sTmp.append( sal_Unicode(':') );
85 lcl_AddTwoDigits( sTmp, rDateTime.Minutes );
86 sTmp.append( sal_Unicode(':') );
87 lcl_AddTwoDigits( sTmp, rDateTime.Seconds );
89 return sTmp.makeStringAndClear();
92 //-------------------------------------------------------------------------
94 void SvXMLMetaExport::SimpleStringElement( const rtl::OUString& rText,
95 sal_uInt16 nNamespace, enum XMLTokenEnum eElementName )
97 if ( rText.getLength() ) {
98 SvXMLElementExport aElem( mrExport, nNamespace, eElementName,
99 sal_True, sal_False );
100 mrExport.Characters( rText );
104 void SvXMLMetaExport::SimpleDateTimeElement( const util::DateTime & rDate,
105 sal_uInt16 nNamespace, enum XMLTokenEnum eElementName )
107 if (rDate.Month != 0) { // invalid dates are 0-0-0
108 rtl::OUString sValue = GetISODateTimeString( rDate );
109 if ( sValue.getLength() ) {
110 SvXMLElementExport aElem( mrExport, nNamespace, eElementName,
111 sal_True, sal_False );
112 mrExport.Characters( sValue );
117 void SvXMLMetaExport::_MExport()
119 // generator
121 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_GENERATOR,
122 sal_True, sal_True );
123 mrExport.Characters( ::utl::DocInfoHelper::GetGeneratorString() );
126 // document title
127 SimpleStringElement ( mxDocProps->getTitle(),
128 XML_NAMESPACE_DC, XML_TITLE );
130 // description
131 SimpleStringElement ( mxDocProps->getDescription(),
132 XML_NAMESPACE_DC, XML_DESCRIPTION );
134 // subject
135 SimpleStringElement ( mxDocProps->getSubject(),
136 XML_NAMESPACE_DC, XML_SUBJECT );
138 // created...
139 SimpleStringElement ( mxDocProps->getAuthor(),
140 XML_NAMESPACE_META, XML_INITIAL_CREATOR );
141 SimpleDateTimeElement( mxDocProps->getCreationDate(),
142 XML_NAMESPACE_META, XML_CREATION_DATE );
144 // modified...
145 SimpleStringElement ( mxDocProps->getModifiedBy(),
146 XML_NAMESPACE_DC, XML_CREATOR );
147 SimpleDateTimeElement( mxDocProps->getModificationDate(),
148 XML_NAMESPACE_DC, XML_DATE );
150 // printed...
151 SimpleStringElement ( mxDocProps->getPrintedBy(),
152 XML_NAMESPACE_META, XML_PRINTED_BY );
153 SimpleDateTimeElement( mxDocProps->getPrintDate(),
154 XML_NAMESPACE_META, XML_PRINT_DATE );
156 // keywords
157 const uno::Sequence< ::rtl::OUString > keywords = mxDocProps->getKeywords();
158 for (sal_Int32 i = 0; i < keywords.getLength(); ++i) {
159 SvXMLElementExport aKwElem( mrExport, XML_NAMESPACE_META, XML_KEYWORD,
160 sal_True, sal_False );
161 mrExport.Characters( keywords[i] );
164 // document language
166 const lang::Locale aLocale = mxDocProps->getLanguage();
167 ::rtl::OUString sValue = aLocale.Language;
168 if (sValue.getLength()) {
169 if ( aLocale.Country.getLength() )
171 sValue += rtl::OUString::valueOf((sal_Unicode)'-');
172 sValue += aLocale.Country;
174 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DC, XML_LANGUAGE,
175 sal_True, sal_False );
176 mrExport.Characters( sValue );
180 // editing cycles
182 SvXMLElementExport aElem( mrExport,
183 XML_NAMESPACE_META, XML_EDITING_CYCLES,
184 sal_True, sal_False );
185 mrExport.Characters( ::rtl::OUString::valueOf(
186 static_cast<sal_Int32>(mxDocProps->getEditingCycles()) ) );
189 // editing duration
190 // property is a int32 (seconds)
192 sal_Int32 secs = mxDocProps->getEditingDuration();
193 SvXMLElementExport aElem( mrExport,
194 XML_NAMESPACE_META, XML_EDITING_DURATION,
195 sal_True, sal_False );
196 mrExport.Characters( SvXMLUnitConverter::convertTimeDuration(
197 Time(secs/3600, (secs%3600)/60, secs%60)) );
200 // default target
201 const ::rtl::OUString sDefTarget = mxDocProps->getDefaultTarget();
202 if ( sDefTarget.getLength() )
204 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME,
205 sDefTarget );
207 //! define strings for xlink:show values
208 const XMLTokenEnum eShow =
209 sDefTarget.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_blank"))
210 ? XML_NEW : XML_REPLACE;
211 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, eShow );
213 SvXMLElementExport aElem( mrExport,
214 XML_NAMESPACE_META,XML_HYPERLINK_BEHAVIOUR,
215 sal_True, sal_False );
218 // auto-reload
219 const ::rtl::OUString sReloadURL = mxDocProps->getAutoloadURL();
220 const sal_Int32 sReloadDelay = mxDocProps->getAutoloadSecs();
221 if (sReloadDelay != 0 || sReloadURL.getLength() != 0)
223 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF,
224 mrExport.GetRelativeReference( sReloadURL ) );
226 mrExport.AddAttribute( XML_NAMESPACE_META, XML_DELAY,
227 SvXMLUnitConverter::convertTimeDuration(
228 Time(sReloadDelay/3600, (sReloadDelay%3600)/60,
229 sReloadDelay%60 )) );
231 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_AUTO_RELOAD,
232 sal_True, sal_False );
235 // template
236 const rtl::OUString sTplPath = mxDocProps->getTemplateURL();
237 if ( sTplPath.getLength() )
239 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
240 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONREQUEST );
242 // template URL
243 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF,
244 mrExport.GetRelativeReference(sTplPath) );
246 // template name
247 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TITLE,
248 mxDocProps->getTemplateName() );
250 // template date
251 mrExport.AddAttribute( XML_NAMESPACE_META, XML_DATE,
252 GetISODateTimeString( mxDocProps->getTemplateDate() ) );
254 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_TEMPLATE,
255 sal_True, sal_False );
258 // user defined fields
259 uno::Reference< beans::XPropertyAccess > xUserDefined(
260 mxDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
261 const uno::Sequence< beans::PropertyValue > props =
262 xUserDefined->getPropertyValues();
263 for (sal_Int32 i = 0; i < props.getLength(); ++i) {
264 ::rtl::OUStringBuffer sValueBuffer;
265 ::rtl::OUStringBuffer sType;
266 if (!SvXMLUnitConverter::convertAny(
267 sValueBuffer, sType, props[i].Value)) {
268 continue;
270 mrExport.AddAttribute( XML_NAMESPACE_META, XML_NAME, props[i].Name );
271 mrExport.AddAttribute( XML_NAMESPACE_META, XML_VALUE_TYPE,
272 sType.makeStringAndClear() );
273 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META,
274 XML_USER_DEFINED, sal_True, sal_False );
275 mrExport.Characters( sValueBuffer.makeStringAndClear() );
278 const uno::Sequence< beans::NamedValue > aDocStatistic =
279 mxDocProps->getDocumentStatistics();
280 // write document statistic if there is any provided
281 if ( aDocStatistic.getLength() )
283 for ( sal_Int32 nInd = 0; nInd < aDocStatistic.getLength(); nInd++ )
285 sal_Int32 nValue = 0;
286 if ( aDocStatistic[nInd].Value >>= nValue )
288 ::rtl::OUString aValue = rtl::OUString::valueOf( nValue );
289 if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
290 RTL_CONSTASCII_USTRINGPARAM( "TableCount" ) ) ) )
291 mrExport.AddAttribute(
292 XML_NAMESPACE_META, XML_TABLE_COUNT, aValue );
293 else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
294 RTL_CONSTASCII_USTRINGPARAM( "ObjectCount" ) ) ) )
295 mrExport.AddAttribute(
296 XML_NAMESPACE_META, XML_OBJECT_COUNT, aValue );
297 else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
298 RTL_CONSTASCII_USTRINGPARAM( "ImageCount" ) ) ) )
299 mrExport.AddAttribute(
300 XML_NAMESPACE_META, XML_IMAGE_COUNT, aValue );
301 else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
302 RTL_CONSTASCII_USTRINGPARAM( "PageCount" ) ) ) )
303 mrExport.AddAttribute(
304 XML_NAMESPACE_META, XML_PAGE_COUNT, aValue );
305 else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
306 RTL_CONSTASCII_USTRINGPARAM( "ParagraphCount" ) ) ) )
307 mrExport.AddAttribute(
308 XML_NAMESPACE_META, XML_PARAGRAPH_COUNT, aValue );
309 else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
310 RTL_CONSTASCII_USTRINGPARAM( "WordCount" ) ) ) )
311 mrExport.AddAttribute(
312 XML_NAMESPACE_META, XML_WORD_COUNT, aValue );
313 else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
314 RTL_CONSTASCII_USTRINGPARAM( "CharacterCount" ) ) ) )
315 mrExport.AddAttribute(
316 XML_NAMESPACE_META, XML_CHARACTER_COUNT, aValue );
317 else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
318 RTL_CONSTASCII_USTRINGPARAM( "CellCount" ) ) ) )
319 mrExport.AddAttribute(
320 XML_NAMESPACE_META, XML_CELL_COUNT, aValue );
321 else
323 DBG_ASSERT( sal_False, "Unknown statistic value!\n" );
327 SvXMLElementExport aElem( mrExport,
328 XML_NAMESPACE_META, XML_DOCUMENT_STATISTIC, sal_True, sal_True );
332 //-------------------------------------------------------------------------
334 static const char *s_xmlns = "xmlns";
335 static const char *s_xmlns2 = "xmlns:";
336 static const char *s_meta = "meta:";
337 static const char *s_href = "xlink:href";
339 SvXMLMetaExport::SvXMLMetaExport(
340 SvXMLExport& i_rExp,
341 const uno::Reference<document::XDocumentProperties>& i_rDocProps ) :
342 mrExport( i_rExp ),
343 mxDocProps( i_rDocProps ),
344 m_level( 0 ),
345 m_preservedNSs()
347 DBG_ASSERT( mxDocProps.is(), "no document properties" );
350 SvXMLMetaExport::~SvXMLMetaExport()
354 void SvXMLMetaExport::Export()
356 // exportDom(xDOM, mrExport); // this would not work (root node, namespaces)
357 uno::Reference< xml::sax::XSAXSerializable> xSAXable(mxDocProps,
358 uno::UNO_QUERY);
359 if (xSAXable.is()) {
360 ::comphelper::SequenceAsVector< beans::StringPair > namespaces;
361 const SvXMLNamespaceMap & rNsMap(mrExport.GetNamespaceMap());
362 for (sal_uInt16 key = rNsMap.GetFirstKey();
363 key != USHRT_MAX; key = rNsMap.GetNextKey(key)) {
364 beans::StringPair ns;
365 const ::rtl::OUString attrname = rNsMap.GetAttrNameByKey(key);
366 if (attrname.matchAsciiL(s_xmlns2, strlen(s_xmlns2))) {
367 ns.First = attrname.copy(strlen(s_xmlns2));
368 } else if (attrname.equalsAsciiL(s_xmlns, strlen(s_xmlns))) {
369 // default initialized empty string
370 } else {
371 DBG_ERROR("namespace attribute not starting with xmlns unexpected");
373 ns.Second = rNsMap.GetNameByKey(key);
374 namespaces.push_back(ns);
376 xSAXable->serialize(this, namespaces.getAsConstList());
377 } else {
378 // office:meta
379 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_OFFICE, XML_META,
380 sal_True, sal_True );
381 // fall back to using public interface of XDocumentProperties
382 _MExport();
386 // ::com::sun::star::xml::sax::XDocumentHandler:
387 void SAL_CALL
388 SvXMLMetaExport::startDocument()
389 throw (uno::RuntimeException, xml::sax::SAXException)
391 // ignore: has already been done by SvXMLExport::exportDoc
392 DBG_ASSERT( m_level == 0, "SvXMLMetaExport: level error" );
395 void SAL_CALL
396 SvXMLMetaExport::endDocument()
397 throw (uno::RuntimeException, xml::sax::SAXException)
399 // ignore: will be done by SvXMLExport::exportDoc
400 DBG_ASSERT( m_level == 0, "SvXMLMetaExport: level error" );
403 // unfortunately, this method contains far too much ugly namespace mangling.
404 void SAL_CALL
405 SvXMLMetaExport::startElement(const ::rtl::OUString & i_rName,
406 const uno::Reference< xml::sax::XAttributeList > & i_xAttribs)
407 throw (uno::RuntimeException, xml::sax::SAXException)
410 if (m_level == 0) {
411 // namepace decls: default ones have been written at the root element
412 // non-default ones must be preserved here
413 const sal_Int16 nCount = i_xAttribs->getLength();
414 for (sal_Int16 i = 0; i < nCount; ++i) {
415 const ::rtl::OUString name(i_xAttribs->getNameByIndex(i));
416 if (name.matchAsciiL(s_xmlns, strlen(s_xmlns))) {
417 bool found(false);
418 const SvXMLNamespaceMap & rNsMap(mrExport.GetNamespaceMap());
419 for (sal_uInt16 key = rNsMap.GetFirstKey();
420 key != USHRT_MAX; key = rNsMap.GetNextKey(key)) {
421 if (name.equals(rNsMap.GetAttrNameByKey(key))) {
422 found = true;
423 break;
426 if (!found) {
427 m_preservedNSs.push_back(beans::StringPair(name,
428 i_xAttribs->getValueByIndex(i)));
432 // ignore the root: it has been written already
433 ++m_level;
434 return;
437 if (m_level == 1) {
438 // attach preserved namespace decls from root node here
439 for (std::vector<beans::StringPair>::const_iterator iter =
440 m_preservedNSs.begin(); iter != m_preservedNSs.end(); ++iter) {
441 const ::rtl::OUString ns(iter->First);
442 bool found(false);
443 // but only if it is not already there
444 const sal_Int16 nCount = i_xAttribs->getLength();
445 for (sal_Int16 i = 0; i < nCount; ++i) {
446 const ::rtl::OUString name(i_xAttribs->getNameByIndex(i));
447 if (ns.equals(name)) {
448 found = true;
449 break;
452 if (!found) {
453 mrExport.AddAttribute(ns, iter->Second);
458 // attach the attributes
459 if (i_rName.matchAsciiL(s_meta, strlen(s_meta))) {
460 // special handling for all elements that may have
461 // xlink:href attributes; these must be made relative
462 const sal_Int16 nLength = i_xAttribs->getLength();
463 for (sal_Int16 i = 0; i < nLength; ++i) {
464 const ::rtl::OUString name (i_xAttribs->getNameByIndex (i));
465 ::rtl::OUString value(i_xAttribs->getValueByIndex(i));
466 if (name.matchAsciiL(s_href, strlen(s_href))) {
467 value = mrExport.GetRelativeReference(value);
469 mrExport.AddAttribute(name, value);
471 } else {
472 const sal_Int16 nLength = i_xAttribs->getLength();
473 for (sal_Int16 i = 0; i < nLength; ++i) {
474 const ::rtl::OUString name (i_xAttribs->getNameByIndex(i));
475 const ::rtl::OUString value (i_xAttribs->getValueByIndex(i));
476 mrExport.AddAttribute(name, value);
480 // finally, start the element
481 mrExport.StartElement(i_rName, sal_True); //FIXME:whitespace?
482 ++m_level;
485 void SAL_CALL
486 SvXMLMetaExport::endElement(const ::rtl::OUString & i_rName)
487 throw (uno::RuntimeException, xml::sax::SAXException)
489 --m_level;
490 if (m_level == 0) {
491 // ignore the root; see startElement
492 return;
494 DBG_ASSERT( m_level >= 0, "SvXMLMetaExport: level error" );
495 mrExport.EndElement(i_rName, sal_False);
498 void SAL_CALL
499 SvXMLMetaExport::characters(const ::rtl::OUString & i_rChars)
500 throw (uno::RuntimeException, xml::sax::SAXException)
502 mrExport.Characters(i_rChars);
505 void SAL_CALL
506 SvXMLMetaExport::ignorableWhitespace(const ::rtl::OUString & /*i_rWhitespaces*/)
507 throw (uno::RuntimeException, xml::sax::SAXException)
509 mrExport.IgnorableWhitespace(/*i_rWhitespaces*/);
512 void SAL_CALL
513 SvXMLMetaExport::processingInstruction(const ::rtl::OUString & i_rTarget,
514 const ::rtl::OUString & i_rData)
515 throw (uno::RuntimeException, xml::sax::SAXException)
517 // ignore; the exporter cannot handle these
518 (void) i_rTarget;
519 (void) i_rData;
522 void SAL_CALL
523 SvXMLMetaExport::setDocumentLocator(const uno::Reference<xml::sax::XLocator>&)
524 throw (uno::RuntimeException, xml::sax::SAXException)
526 // nothing to do here, move along...