bump product version to 5.0.4.1
[LibreOffice.git] / xmloff / source / style / xmlnumfe.cxx
blob0d160add959844b47ab8c50e7320510b63f0ace7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/string.hxx>
21 #include <svl/zforlist.hxx>
22 #include <svl/zformat.hxx>
23 #include <svl/numuno.hxx>
24 #include <i18nlangtag/mslangid.hxx>
25 #include <i18nlangtag/languagetag.hxx>
26 #include <tools/debug.hxx>
27 #include <rtl/math.hxx>
28 #include <unotools/calendarwrapper.hxx>
29 #include <unotools/charclass.hxx>
30 #include <com/sun/star/lang/Locale.hpp>
31 #include <rtl/ustrbuf.hxx>
32 #include <tools/color.hxx>
33 #include <sax/tools/converter.hxx>
35 #include <com/sun/star/i18n/NativeNumberXmlAttributes.hpp>
37 #include <xmloff/xmlnumfe.hxx>
38 #include <xmloff/xmlnmspe.hxx>
39 #include <xmloff/attrlist.hxx>
40 #include <xmloff/nmspmap.hxx>
41 #include <xmloff/families.hxx>
42 #include <xmloff/xmlnumfi.hxx>
44 #include <svl/nfsymbol.hxx>
45 #include <xmloff/xmltoken.hxx>
46 #include <xmloff/xmlexp.hxx>
48 #include <set>
49 #include <boost/ptr_container/ptr_vector.hpp>
51 using namespace ::com::sun::star;
52 using namespace ::xmloff::token;
53 using namespace ::svt;
55 #define XMLNUM_MAX_PARTS 3
57 struct LessuInt32
59 bool operator() (const sal_uInt32 rValue1, const sal_uInt32 rValue2) const
61 return rValue1 < rValue2;
65 typedef std::set< sal_uInt32, LessuInt32 > SvXMLuInt32Set;
67 class SvXMLEmbeddedTextEntryArr
69 typedef boost::ptr_vector<SvXMLEmbeddedTextEntry> DataType;
70 DataType maData;
72 public:
74 void push_back( SvXMLEmbeddedTextEntry* p )
76 maData.push_back(p);
79 const SvXMLEmbeddedTextEntry& operator[] ( size_t i ) const
81 return maData[i];
84 size_t size() const
86 return maData.size();
90 class SvXMLNumUsedList_Impl
92 SvXMLuInt32Set aUsed;
93 SvXMLuInt32Set aWasUsed;
94 SvXMLuInt32Set::iterator aCurrentUsedPos;
95 sal_uInt32 nUsedCount;
96 sal_uInt32 nWasUsedCount;
98 public:
99 SvXMLNumUsedList_Impl();
100 ~SvXMLNumUsedList_Impl();
102 void SetUsed( sal_uInt32 nKey );
103 bool IsUsed( sal_uInt32 nKey ) const;
104 bool IsWasUsed( sal_uInt32 nKey ) const;
105 void Export();
107 bool GetFirstUsed(sal_uInt32& nKey);
108 bool GetNextUsed(sal_uInt32& nKey);
110 void GetWasUsed(uno::Sequence<sal_Int32>& rWasUsed);
111 void SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed);
114 struct SvXMLEmbeddedTextEntry
116 sal_uInt16 nSourcePos; // position in NumberFormat (to skip later)
117 sal_Int32 nFormatPos; // resulting position in embedded-text element
118 OUString aText;
120 SvXMLEmbeddedTextEntry( sal_uInt16 nSP, sal_Int32 nFP, const OUString& rT ) :
121 nSourcePos(nSP), nFormatPos(nFP), aText(rT) {}
124 //! SvXMLNumUsedList_Impl should be optimized!
126 SvXMLNumUsedList_Impl::SvXMLNumUsedList_Impl() :
127 nUsedCount(0),
128 nWasUsedCount(0)
132 SvXMLNumUsedList_Impl::~SvXMLNumUsedList_Impl()
136 void SvXMLNumUsedList_Impl::SetUsed( sal_uInt32 nKey )
138 if ( !IsWasUsed(nKey) )
140 std::pair<SvXMLuInt32Set::iterator, bool> aPair = aUsed.insert( nKey );
141 if (aPair.second)
142 nUsedCount++;
146 bool SvXMLNumUsedList_Impl::IsUsed( sal_uInt32 nKey ) const
148 SvXMLuInt32Set::const_iterator aItr = aUsed.find(nKey);
149 return (aItr != aUsed.end());
152 bool SvXMLNumUsedList_Impl::IsWasUsed( sal_uInt32 nKey ) const
154 SvXMLuInt32Set::const_iterator aItr = aWasUsed.find(nKey);
155 return (aItr != aWasUsed.end());
158 void SvXMLNumUsedList_Impl::Export()
160 SvXMLuInt32Set::const_iterator aItr = aUsed.begin();
161 while (aItr != aUsed.end())
163 std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( *aItr );
164 if (aPair.second)
165 nWasUsedCount++;
166 ++aItr;
168 aUsed.clear();
169 nUsedCount = 0;
172 bool SvXMLNumUsedList_Impl::GetFirstUsed(sal_uInt32& nKey)
174 bool bRet(false);
175 aCurrentUsedPos = aUsed.begin();
176 if(nUsedCount)
178 DBG_ASSERT(aCurrentUsedPos != aUsed.end(), "something went wrong");
179 nKey = *aCurrentUsedPos;
180 bRet = true;
182 return bRet;
185 bool SvXMLNumUsedList_Impl::GetNextUsed(sal_uInt32& nKey)
187 bool bRet(false);
188 if (aCurrentUsedPos != aUsed.end())
190 ++aCurrentUsedPos;
191 if (aCurrentUsedPos != aUsed.end())
193 nKey = *aCurrentUsedPos;
194 bRet = true;
197 return bRet;
200 void SvXMLNumUsedList_Impl::GetWasUsed(uno::Sequence<sal_Int32>& rWasUsed)
202 rWasUsed.realloc(nWasUsedCount);
203 sal_Int32* pWasUsed = rWasUsed.getArray();
204 if (pWasUsed)
206 SvXMLuInt32Set::const_iterator aItr = aWasUsed.begin();
207 while (aItr != aWasUsed.end())
209 *pWasUsed = *aItr;
210 ++aItr;
211 ++pWasUsed;
216 void SvXMLNumUsedList_Impl::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
218 DBG_ASSERT(nWasUsedCount == 0, "WasUsed should be empty");
219 sal_Int32 nCount(rWasUsed.getLength());
220 const sal_Int32* pWasUsed = rWasUsed.getConstArray();
221 for (sal_uInt16 i = 0; i < nCount; i++, pWasUsed++)
223 std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( *pWasUsed );
224 if (aPair.second)
225 nWasUsedCount++;
229 SvXMLNumFmtExport::SvXMLNumFmtExport(
230 SvXMLExport& rExp,
231 const uno::Reference< util::XNumberFormatsSupplier >& rSupp ) :
232 rExport( rExp ),
233 sPrefix( OUString("N") ),
234 pFormatter( NULL ),
235 pCharClass( NULL ),
236 pLocaleData( NULL )
238 // supplier must be SvNumberFormatsSupplierObj
239 SvNumberFormatsSupplierObj* pObj =
240 SvNumberFormatsSupplierObj::getImplementation( rSupp );
241 if (pObj)
242 pFormatter = pObj->GetNumberFormatter();
244 if ( pFormatter )
246 pCharClass = new CharClass( pFormatter->GetComponentContext(),
247 pFormatter->GetLanguageTag() );
248 pLocaleData = new LocaleDataWrapper( pFormatter->GetComponentContext(),
249 pFormatter->GetLanguageTag() );
251 else
253 LanguageTag aLanguageTag( MsLangId::getSystemLanguage() );
255 pCharClass = new CharClass( rExport.getComponentContext(), aLanguageTag );
256 pLocaleData = new LocaleDataWrapper( rExport.getComponentContext(), aLanguageTag );
259 pUsedList = new SvXMLNumUsedList_Impl;
262 SvXMLNumFmtExport::SvXMLNumFmtExport(
263 SvXMLExport& rExp,
264 const ::com::sun::star::uno::Reference<
265 ::com::sun::star::util::XNumberFormatsSupplier >& rSupp,
266 const OUString& rPrefix ) :
267 rExport( rExp ),
268 sPrefix( rPrefix ),
269 pFormatter( NULL ),
270 pCharClass( NULL ),
271 pLocaleData( NULL )
273 // supplier must be SvNumberFormatsSupplierObj
274 SvNumberFormatsSupplierObj* pObj =
275 SvNumberFormatsSupplierObj::getImplementation( rSupp );
276 if (pObj)
277 pFormatter = pObj->GetNumberFormatter();
279 if ( pFormatter )
281 pCharClass = new CharClass( pFormatter->GetComponentContext(),
282 pFormatter->GetLanguageTag() );
283 pLocaleData = new LocaleDataWrapper( pFormatter->GetComponentContext(),
284 pFormatter->GetLanguageTag() );
286 else
288 LanguageTag aLanguageTag( MsLangId::getSystemLanguage() );
290 pCharClass = new CharClass( rExport.getComponentContext(), aLanguageTag );
291 pLocaleData = new LocaleDataWrapper( rExport.getComponentContext(), aLanguageTag );
294 pUsedList = new SvXMLNumUsedList_Impl;
297 SvXMLNumFmtExport::~SvXMLNumFmtExport()
299 delete pUsedList;
300 delete pLocaleData;
301 delete pCharClass;
304 // helper methods
306 static OUString lcl_CreateStyleName( sal_Int32 nKey, sal_Int32 nPart, bool bDefPart, const OUString& rPrefix )
308 OUStringBuffer aFmtName(10);
309 aFmtName.append( rPrefix );
310 aFmtName.append( nKey );
311 if (!bDefPart)
313 aFmtName.append( 'P' );
314 aFmtName.append( nPart );
316 return aFmtName.makeStringAndClear();
319 void SvXMLNumFmtExport::AddCalendarAttr_Impl( const OUString& rCalendar )
321 if ( !rCalendar.isEmpty() )
323 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_CALENDAR, rCalendar );
327 void SvXMLNumFmtExport::AddTextualAttr_Impl( bool bText )
329 if ( bText ) // non-textual
331 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TEXTUAL, XML_TRUE );
335 void SvXMLNumFmtExport::AddStyleAttr_Impl( bool bLong )
337 if ( bLong ) // short is default
339 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_STYLE, XML_LONG );
343 void SvXMLNumFmtExport::AddLanguageAttr_Impl( sal_Int32 nLang )
345 if ( nLang != LANGUAGE_SYSTEM )
347 rExport.AddLanguageTagAttributes( XML_NAMESPACE_NUMBER, XML_NAMESPACE_NUMBER,
348 LanguageTag( (LanguageType)nLang), false);
352 // methods to write individual elements within a format
354 void SvXMLNumFmtExport::AddToTextElement_Impl( const OUString& rString )
356 // append to sTextContent, write element in FinishTextElement_Impl
357 // to avoid several text elements following each other
359 sTextContent.append( rString );
362 void SvXMLNumFmtExport::FinishTextElement_Impl(bool bUseExtensionNS)
364 if ( !sTextContent.isEmpty() )
366 sal_uInt16 nNS = bUseExtensionNS ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER;
367 SvXMLElementExport aElem( rExport, nNS, XML_TEXT,
368 true, false );
369 rExport.Characters( sTextContent.makeStringAndClear() );
373 void SvXMLNumFmtExport::WriteColorElement_Impl( const Color& rColor )
375 FinishTextElement_Impl();
377 OUStringBuffer aColStr( 7 );
378 ::sax::Converter::convertColor( aColStr, rColor.GetColor() );
379 rExport.AddAttribute( XML_NAMESPACE_FO, XML_COLOR,
380 aColStr.makeStringAndClear() );
382 SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, XML_TEXT_PROPERTIES,
383 true, false );
386 void SvXMLNumFmtExport::WriteCurrencyElement_Impl( const OUString& rString,
387 const OUString& rExt )
389 FinishTextElement_Impl();
391 if ( !rExt.isEmpty() )
393 // rExt should be a 16-bit hex value max FFFF which may contain a
394 // leading "-" separator (that is not a minus sign, but toInt32 can be
395 // used to parse it, with post-processing as necessary):
396 sal_Int32 nLang = rExt.toInt32(16);
397 if ( nLang < 0 )
398 nLang = -nLang;
399 AddLanguageAttr_Impl( nLang ); // adds to pAttrList
402 SvXMLElementExport aElem( rExport,
403 XML_NAMESPACE_NUMBER, XML_CURRENCY_SYMBOL,
404 true, false );
405 rExport.Characters( rString );
408 void SvXMLNumFmtExport::WriteBooleanElement_Impl()
410 FinishTextElement_Impl();
412 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_BOOLEAN,
413 true, false );
416 void SvXMLNumFmtExport::WriteTextContentElement_Impl()
418 FinishTextElement_Impl();
420 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_TEXT_CONTENT,
421 true, false );
424 // date elements
426 void SvXMLNumFmtExport::WriteDayElement_Impl( const OUString& rCalendar, bool bLong )
428 FinishTextElement_Impl();
430 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
431 AddStyleAttr_Impl( bLong ); // adds to pAttrList
433 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_DAY,
434 true, false );
437 void SvXMLNumFmtExport::WriteMonthElement_Impl( const OUString& rCalendar, bool bLong, bool bText )
439 FinishTextElement_Impl();
441 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
442 AddStyleAttr_Impl( bLong ); // adds to pAttrList
443 AddTextualAttr_Impl( bText ); // adds to pAttrList
445 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_MONTH,
446 true, false );
449 void SvXMLNumFmtExport::WriteYearElement_Impl( const OUString& rCalendar, bool bLong )
451 FinishTextElement_Impl();
453 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
454 AddStyleAttr_Impl( bLong ); // adds to pAttrList
456 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_YEAR,
457 true, false );
460 void SvXMLNumFmtExport::WriteEraElement_Impl( const OUString& rCalendar, bool bLong )
462 FinishTextElement_Impl();
464 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
465 AddStyleAttr_Impl( bLong ); // adds to pAttrList
467 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_ERA,
468 true, false );
471 void SvXMLNumFmtExport::WriteDayOfWeekElement_Impl( const OUString& rCalendar, bool bLong )
473 FinishTextElement_Impl();
475 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
476 AddStyleAttr_Impl( bLong ); // adds to pAttrList
478 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_DAY_OF_WEEK,
479 true, false );
482 void SvXMLNumFmtExport::WriteWeekElement_Impl( const OUString& rCalendar )
484 FinishTextElement_Impl();
486 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
488 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_WEEK_OF_YEAR,
489 true, false );
492 void SvXMLNumFmtExport::WriteQuarterElement_Impl( const OUString& rCalendar, bool bLong )
494 FinishTextElement_Impl();
496 AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
497 AddStyleAttr_Impl( bLong ); // adds to pAttrList
499 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_QUARTER,
500 true, false );
503 // time elements
505 void SvXMLNumFmtExport::WriteHoursElement_Impl( bool bLong )
507 FinishTextElement_Impl();
509 AddStyleAttr_Impl( bLong ); // adds to pAttrList
511 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_HOURS,
512 true, false );
515 void SvXMLNumFmtExport::WriteMinutesElement_Impl( bool bLong )
517 FinishTextElement_Impl();
519 AddStyleAttr_Impl( bLong ); // adds to pAttrList
521 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_MINUTES,
522 true, false );
525 void SvXMLNumFmtExport::WriteRepeatedElement_Impl( sal_Unicode nChar )
527 FinishTextElement_Impl(true);
528 SvXMLElementExport aElem( rExport, XML_NAMESPACE_LO_EXT, XML_FILL_CHARACTER,
529 true, false );
530 rExport.Characters( OUString( nChar ) );
533 void SvXMLNumFmtExport::WriteSecondsElement_Impl( bool bLong, sal_uInt16 nDecimals )
535 FinishTextElement_Impl();
537 AddStyleAttr_Impl( bLong ); // adds to pAttrList
538 if ( nDecimals > 0 )
540 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
541 OUString::number( nDecimals ) );
544 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_SECONDS,
545 true, false );
548 void SvXMLNumFmtExport::WriteAMPMElement_Impl()
550 FinishTextElement_Impl();
552 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_AM_PM,
553 true, false );
556 // numbers
558 void SvXMLNumFmtExport::WriteNumberElement_Impl(
559 sal_Int32 nDecimals, sal_Int32 nMinDecimals,
560 sal_Int32 nInteger, const OUString& rDashStr,
561 bool bGrouping, sal_Int32 nTrailingThousands,
562 const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
564 FinishTextElement_Impl();
566 // decimals
567 if ( nDecimals >= 0 ) // negative = automatic
569 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
570 OUString::number( nDecimals ) );
573 if ( nMinDecimals >= 0 ) // negative = automatic
575 rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_MIN_DECIMAL_PLACES,
576 OUString::number( nMinDecimals ) );
579 // integer digits
580 if ( nInteger >= 0 ) // negative = automatic
582 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
583 OUString::number( nInteger ) );
586 // decimal replacement (dashes) or variable decimals (#)
587 if ( !rDashStr.isEmpty() || nMinDecimals < nDecimals )
589 // full variable decimals means an empty replacement string
590 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_REPLACEMENT,
591 rDashStr );
594 // (automatic) grouping separator
595 if ( bGrouping )
597 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
600 // display-factor if there are trailing thousands separators
601 if ( nTrailingThousands )
603 // each separator character removes three digits
604 double fFactor = ::rtl::math::pow10Exp( 1.0, 3 * nTrailingThousands );
606 OUStringBuffer aFactStr;
607 ::sax::Converter::convertDouble( aFactStr, fFactor );
608 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DISPLAY_FACTOR, aFactStr.makeStringAndClear() );
611 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_NUMBER,
612 true, true );
614 // number:embedded-text as child elements
616 sal_uInt16 nEntryCount = rEmbeddedEntries.size();
617 for (sal_uInt16 nEntry=0; nEntry<nEntryCount; nEntry++)
619 const SvXMLEmbeddedTextEntry* pObj = &rEmbeddedEntries[nEntry];
621 // position attribute
622 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_POSITION,
623 OUString::number( pObj->nFormatPos ) );
624 SvXMLElementExport aChildElem( rExport, XML_NAMESPACE_NUMBER, XML_EMBEDDED_TEXT,
625 true, false );
627 // text as element content
628 OUString aContent( pObj->aText );
629 while ( nEntry+1 < nEntryCount && rEmbeddedEntries[nEntry+1].nFormatPos == pObj->nFormatPos )
631 // The array can contain several elements for the same position in the number
632 // (for example, literal text and space from underscores). They must be merged
633 // into a single embedded-text element.
634 aContent += rEmbeddedEntries[nEntry+1].aText;
635 ++nEntry;
637 rExport.Characters( aContent );
641 void SvXMLNumFmtExport::WriteScientificElement_Impl(
642 sal_Int32 nDecimals, sal_Int32 nMinDecimals, sal_Int32 nInteger,
643 bool bGrouping, sal_Int32 nExp, sal_Int32 nExpInterval, bool bExpSign )
645 FinishTextElement_Impl();
647 // decimals
648 if ( nDecimals >= 0 ) // negative = automatic
650 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
651 OUString::number( nDecimals ) );
654 if ( nMinDecimals >= 0 ) // negative = automatic
656 rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_MIN_DECIMAL_PLACES,
657 OUString::number( nMinDecimals ) );
660 // integer digits
661 if ( nInteger >= 0 ) // negative = automatic
663 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
664 OUString::number( nInteger ) );
667 // (automatic) grouping separator
668 if ( bGrouping )
670 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
673 // exponent digits
674 if ( nExp >= 0 )
676 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_EXPONENT_DIGITS,
677 OUString::number( nExp ) );
680 // exponent interval for engineering notation
681 if ( nExpInterval >= 0 )
683 // Export only for 1.2 with extensions or 1.3 and later.
684 SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
685 if (eVersion > SvtSaveOptions::ODFSVER_012)
687 // For 1.2+ use loext namespace, for 1.3 use number namespace.
688 rExport.AddAttribute(
689 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
690 XML_EXPONENT_INTERVAL, OUString::number( nExpInterval ) );
694 // exponent sign
695 if ( bExpSign )
697 rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_FORCED_EXPONENT_SIGN, XML_TRUE );
699 else
701 rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_FORCED_EXPONENT_SIGN, XML_FALSE );
704 SvXMLElementExport aElem( rExport,
705 XML_NAMESPACE_NUMBER, XML_SCIENTIFIC_NUMBER,
706 true, false );
709 void SvXMLNumFmtExport::WriteFractionElement_Impl(
710 sal_Int32 nInteger, bool bGrouping,
711 sal_Int32 nNumeratorDigits, sal_Int32 nDenominatorDigits, sal_Int32 nDenominator )
713 FinishTextElement_Impl();
715 // integer digits
716 if ( nInteger >= 0 ) // negative = default (no integer part)
718 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
719 OUString::number( nInteger ) );
722 // (automatic) grouping separator
723 if ( bGrouping )
725 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
728 // numerator digits
729 if ( nNumeratorDigits >= 0 )
731 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_NUMERATOR_DIGITS,
732 OUString::number( nNumeratorDigits ) );
735 if ( nDenominator )
737 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DENOMINATOR_VALUE,
738 OUString::number( nDenominator) );
740 // I guess it's not necessary to export nDenominatorDigits
741 // if we have a forced denominator ( remove ? )
742 if ( nDenominatorDigits >= 0 )
744 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_DENOMINATOR_DIGITS,
745 OUString::number( nDenominatorDigits ) );
748 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_FRACTION,
749 true, false );
752 // mapping (condition)
754 void SvXMLNumFmtExport::WriteMapElement_Impl( sal_Int32 nOp, double fLimit,
755 sal_Int32 nKey, sal_Int32 nPart )
757 FinishTextElement_Impl();
759 if ( nOp != NUMBERFORMAT_OP_NO )
761 // style namespace
763 OUStringBuffer aCondStr(20);
764 aCondStr.appendAscii( "value()" ); //! define constant
765 switch ( nOp )
767 case NUMBERFORMAT_OP_EQ: aCondStr.append( '=' ); break;
768 case NUMBERFORMAT_OP_NE: aCondStr.appendAscii( "!=" ); break;
769 case NUMBERFORMAT_OP_LT: aCondStr.append( '<' ); break;
770 case NUMBERFORMAT_OP_LE: aCondStr.appendAscii( "<=" ); break;
771 case NUMBERFORMAT_OP_GT: aCondStr.append( '>' ); break;
772 case NUMBERFORMAT_OP_GE: aCondStr.appendAscii( ">=" ); break;
773 default:
774 OSL_FAIL("unknown operator");
776 ::rtl::math::doubleToUStringBuffer( aCondStr, fLimit,
777 rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
778 '.', true );
780 rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_CONDITION,
781 aCondStr.makeStringAndClear() );
783 rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME,
784 rExport.EncodeStyleName( lcl_CreateStyleName( nKey, nPart, false,
785 sPrefix ) ) );
787 SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, XML_MAP,
788 true, false );
792 // for old (automatic) currency formats: parse currency symbol from text
794 sal_Int32 lcl_FindSymbol( const OUString& sUpperStr, const OUString& sCurString )
796 // search for currency symbol
797 // Quoting as in ImpSvNumberformatScan::Symbol_Division
799 sal_Int32 nCPos = 0;
800 while (nCPos >= 0)
802 nCPos = sUpperStr.indexOf( sCurString, nCPos );
803 if (nCPos >= 0)
805 // in Quotes?
806 sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sUpperStr, nCPos );
807 if ( nQ < 0 )
809 // dm can be escaped as "dm or \d
810 sal_Unicode c;
811 if ( nCPos == 0 ||
812 ((c = sUpperStr[nCPos-1]) != '"'
813 && c != '\\') )
815 return nCPos; // found
817 else
819 nCPos++; // continue
822 else
824 nCPos = nQ + 1; // continue after quote end
828 return -1;
831 bool SvXMLNumFmtExport::WriteTextWithCurrency_Impl( const OUString& rString,
832 const ::com::sun::star::lang::Locale& rLocale )
834 // returns true if currency element was written
836 bool bRet = false;
838 LanguageTag aLanguageTag( rLocale );
839 pFormatter->ChangeIntl( aLanguageTag.getLanguageType( false) );
840 OUString sCurString, sDummy;
841 pFormatter->GetCompatibilityCurrency( sCurString, sDummy );
843 pCharClass->setLanguageTag( aLanguageTag );
844 OUString sUpperStr = pCharClass->uppercase(rString);
845 sal_Int32 nPos = lcl_FindSymbol( sUpperStr, sCurString );
846 if ( nPos >= 0 )
848 sal_Int32 nLength = rString.getLength();
849 sal_Int32 nCurLen = sCurString.getLength();
850 sal_Int32 nCont = nPos + nCurLen;
852 // text before currency symbol
853 if ( nPos > 0 )
855 AddToTextElement_Impl( rString.copy( 0, nPos ) );
857 // currency symbol (empty string -> default)
858 OUString sEmpty;
859 WriteCurrencyElement_Impl( sEmpty, sEmpty );
860 bRet = true;
862 // text after currency symbol
863 if ( nCont < nLength )
865 AddToTextElement_Impl( rString.copy( nCont, nLength-nCont ) );
868 else
870 AddToTextElement_Impl( rString ); // simple text
873 return bRet; // true: currency element written
876 static OUString lcl_GetDefaultCalendar( SvNumberFormatter* pFormatter, LanguageType nLang )
878 // get name of first non-gregorian calendar for the language
880 OUString aCalendar;
881 CalendarWrapper* pCalendar = pFormatter->GetCalendar();
882 if (pCalendar)
884 lang::Locale aLocale( LanguageTag::convertToLocale( nLang ) );
886 uno::Sequence<OUString> aCals = pCalendar->getAllCalendars( aLocale );
887 sal_Int32 nCnt = aCals.getLength();
888 bool bFound = false;
889 for ( sal_Int32 j=0; j < nCnt && !bFound; j++ )
891 if ( aCals[j] != "gregorian" )
893 aCalendar = aCals[j];
894 bFound = true;
898 return aCalendar;
901 static bool lcl_IsInEmbedded( const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries, sal_uInt16 nPos )
903 sal_uInt16 nCount = rEmbeddedEntries.size();
904 for (sal_uInt16 i=0; i<nCount; i++)
905 if ( rEmbeddedEntries[i].nSourcePos == nPos )
906 return true;
908 return false; // not found
911 static bool lcl_IsDefaultDateFormat( const SvNumberformat& rFormat, bool bSystemDate, NfIndexTableOffset eBuiltIn )
913 // make an extra loop to collect date elements, to check if it is a default format
914 // before adding the automatic-order attribute
916 SvXMLDateElementAttributes eDateDOW = XML_DEA_NONE;
917 SvXMLDateElementAttributes eDateDay = XML_DEA_NONE;
918 SvXMLDateElementAttributes eDateMonth = XML_DEA_NONE;
919 SvXMLDateElementAttributes eDateYear = XML_DEA_NONE;
920 SvXMLDateElementAttributes eDateHours = XML_DEA_NONE;
921 SvXMLDateElementAttributes eDateMins = XML_DEA_NONE;
922 SvXMLDateElementAttributes eDateSecs = XML_DEA_NONE;
923 bool bDateNoDefault = false;
925 sal_uInt16 nPos = 0;
926 bool bEnd = false;
927 short nLastType = 0;
928 while (!bEnd)
930 short nElemType = rFormat.GetNumForType( 0, nPos, false );
931 switch ( nElemType )
933 case 0:
934 if ( nLastType == NF_SYMBOLTYPE_STRING )
935 bDateNoDefault = true; // text at the end -> no default date format
936 bEnd = true; // end of format reached
937 break;
938 case NF_SYMBOLTYPE_STRING:
939 case NF_SYMBOLTYPE_DATESEP:
940 case NF_SYMBOLTYPE_TIMESEP:
941 case NF_SYMBOLTYPE_TIME100SECSEP:
942 // text is ignored, except at the end
943 break;
944 // same mapping as in SvXMLNumFormatContext::AddNfKeyword:
945 case NF_KEY_NN: eDateDOW = XML_DEA_SHORT; break;
946 case NF_KEY_NNN:
947 case NF_KEY_NNNN: eDateDOW = XML_DEA_LONG; break;
948 case NF_KEY_D: eDateDay = XML_DEA_SHORT; break;
949 case NF_KEY_DD: eDateDay = XML_DEA_LONG; break;
950 case NF_KEY_M: eDateMonth = XML_DEA_SHORT; break;
951 case NF_KEY_MM: eDateMonth = XML_DEA_LONG; break;
952 case NF_KEY_MMM: eDateMonth = XML_DEA_TEXTSHORT; break;
953 case NF_KEY_MMMM: eDateMonth = XML_DEA_TEXTLONG; break;
954 case NF_KEY_YY: eDateYear = XML_DEA_SHORT; break;
955 case NF_KEY_YYYY: eDateYear = XML_DEA_LONG; break;
956 case NF_KEY_H: eDateHours = XML_DEA_SHORT; break;
957 case NF_KEY_HH: eDateHours = XML_DEA_LONG; break;
958 case NF_KEY_MI: eDateMins = XML_DEA_SHORT; break;
959 case NF_KEY_MMI: eDateMins = XML_DEA_LONG; break;
960 case NF_KEY_S: eDateSecs = XML_DEA_SHORT; break;
961 case NF_KEY_SS: eDateSecs = XML_DEA_LONG; break;
962 case NF_KEY_AP:
963 case NF_KEY_AMPM: break; // AM/PM may or may not be in date/time formats -> ignore by itself
964 default:
965 bDateNoDefault = true; // any other element -> no default format
967 nLastType = nElemType;
968 ++nPos;
971 if ( bDateNoDefault )
972 return false; // additional elements
973 else
975 NfIndexTableOffset eFound = (NfIndexTableOffset) SvXMLNumFmtDefaults::GetDefaultDateFormat(
976 eDateDOW, eDateDay, eDateMonth, eDateYear, eDateHours, eDateMins, eDateSecs, bSystemDate );
978 return ( eFound == eBuiltIn );
982 // export one part (condition)
984 void SvXMLNumFmtExport::ExportPart_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey,
985 sal_uInt16 nPart, bool bDefPart )
987 //! for the default part, pass the coditions from the other parts!
989 // element name
991 NfIndexTableOffset eBuiltIn = pFormatter->GetIndexTableOffset( nKey );
993 short nFmtType = 0;
994 bool bThousand = false;
995 sal_uInt16 nPrecision = 0;
996 sal_uInt16 nLeading = 0;
997 rFormat.GetNumForInfo( nPart, nFmtType, bThousand, nPrecision, nLeading);
998 nFmtType &= ~css::util::NumberFormat::DEFINED;
1000 // special treatment of builtin formats that aren't detected by normal parsing
1001 // (the same formats that get the type set in SvNumberFormatter::ImpGenerateFormats)
1002 if ( eBuiltIn == NF_NUMBER_STANDARD )
1003 nFmtType = css::util::NumberFormat::NUMBER;
1004 else if ( eBuiltIn == NF_BOOLEAN )
1005 nFmtType = css::util::NumberFormat::LOGICAL;
1006 else if ( eBuiltIn == NF_TEXT )
1007 nFmtType = css::util::NumberFormat::TEXT;
1009 // #101606# An empty subformat is a valid number-style resulting in an
1010 // empty display string for the condition of the subformat.
1011 if ( nFmtType == css::util::NumberFormat::UNDEFINED && rFormat.GetNumForType( nPart,
1012 0, false ) == 0 )
1013 nFmtType = 0;
1015 XMLTokenEnum eType = XML_TOKEN_INVALID;
1016 switch ( nFmtType )
1018 // type is 0 if a format contains no recognized elements
1019 // (like text only) - this is handled as a number-style.
1020 case 0:
1021 case css::util::NumberFormat::NUMBER:
1022 case css::util::NumberFormat::SCIENTIFIC:
1023 case css::util::NumberFormat::FRACTION:
1024 eType = XML_NUMBER_STYLE;
1025 break;
1026 case css::util::NumberFormat::PERCENT:
1027 eType = XML_PERCENTAGE_STYLE;
1028 break;
1029 case css::util::NumberFormat::CURRENCY:
1030 eType = XML_CURRENCY_STYLE;
1031 break;
1032 case css::util::NumberFormat::DATE:
1033 case css::util::NumberFormat::DATETIME:
1034 eType = XML_DATE_STYLE;
1035 break;
1036 case css::util::NumberFormat::TIME:
1037 eType = XML_TIME_STYLE;
1038 break;
1039 case css::util::NumberFormat::TEXT:
1040 eType = XML_TEXT_STYLE;
1041 break;
1042 case css::util::NumberFormat::LOGICAL:
1043 eType = XML_BOOLEAN_STYLE;
1044 break;
1046 DBG_ASSERT( eType != XML_TOKEN_INVALID, "unknown format type" );
1048 OUString sAttrValue;
1049 bool bUserDef = ( rFormat.GetType() & css::util::NumberFormat::DEFINED );
1051 // common attributes for format
1053 // format name (generated from key) - style namespace
1054 rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
1055 lcl_CreateStyleName( nKey, nPart, bDefPart, sPrefix ) );
1057 // "volatile" attribute for styles used only in maps
1058 if ( !bDefPart )
1059 rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_VOLATILE, XML_TRUE );
1061 // language / country
1062 LanguageType nLang = rFormat.GetLanguage();
1063 AddLanguageAttr_Impl( nLang ); // adds to pAttrList
1065 // title (comment)
1066 // titles for builtin formats are not written
1067 sAttrValue = rFormat.GetComment();
1068 if ( !sAttrValue.isEmpty() && bUserDef && bDefPart )
1070 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TITLE, sAttrValue );
1073 // automatic ordering for currency and date formats
1074 // only used for some built-in formats
1075 bool bAutoOrder = ( eBuiltIn == NF_CURRENCY_1000INT || eBuiltIn == NF_CURRENCY_1000DEC2 ||
1076 eBuiltIn == NF_CURRENCY_1000INT_RED || eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
1077 eBuiltIn == NF_CURRENCY_1000DEC2_DASHED ||
1078 eBuiltIn == NF_DATE_SYSTEM_SHORT || eBuiltIn == NF_DATE_SYSTEM_LONG ||
1079 eBuiltIn == NF_DATE_SYS_MMYY || eBuiltIn == NF_DATE_SYS_DDMMM ||
1080 eBuiltIn == NF_DATE_SYS_DDMMYYYY || eBuiltIn == NF_DATE_SYS_DDMMYY ||
1081 eBuiltIn == NF_DATE_SYS_DMMMYY || eBuiltIn == NF_DATE_SYS_DMMMYYYY ||
1082 eBuiltIn == NF_DATE_SYS_DMMMMYYYY || eBuiltIn == NF_DATE_SYS_NNDMMMYY ||
1083 eBuiltIn == NF_DATE_SYS_NNDMMMMYYYY || eBuiltIn == NF_DATE_SYS_NNNNDMMMMYYYY ||
1084 eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM || eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMMSS );
1086 // format source (for date and time formats)
1087 // only used for some built-in formats
1088 bool bSystemDate = ( eBuiltIn == NF_DATE_SYSTEM_SHORT ||
1089 eBuiltIn == NF_DATE_SYSTEM_LONG ||
1090 eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM );
1091 bool bLongSysDate = ( eBuiltIn == NF_DATE_SYSTEM_LONG );
1093 // check if the format definition matches the key
1094 if ( bAutoOrder && ( nFmtType == css::util::NumberFormat::DATE || nFmtType == css::util::NumberFormat::DATETIME ) &&
1095 !lcl_IsDefaultDateFormat( rFormat, bSystemDate, eBuiltIn ) )
1097 bAutoOrder = bSystemDate = bLongSysDate = false; // don't write automatic-order attribute then
1100 if ( bAutoOrder &&
1101 ( nFmtType == css::util::NumberFormat::CURRENCY || nFmtType == css::util::NumberFormat::DATE || nFmtType == css::util::NumberFormat::DATETIME ) )
1103 // #85109# format type must be checked to avoid dtd errors if
1104 // locale data contains other format types at the built-in positions
1106 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_AUTOMATIC_ORDER,
1107 XML_TRUE );
1110 if ( bSystemDate && bAutoOrder &&
1111 ( nFmtType == css::util::NumberFormat::DATE || nFmtType == css::util::NumberFormat::DATETIME ) )
1113 // #85109# format type must be checked to avoid dtd errors if
1114 // locale data contains other format types at the built-in positions
1116 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_FORMAT_SOURCE,
1117 XML_LANGUAGE );
1120 // overflow for time formats as in [hh]:mm
1121 // controlled by bThousand from number format info
1122 // default for truncate-on-overflow is true
1123 if ( nFmtType == css::util::NumberFormat::TIME && bThousand )
1125 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRUNCATE_ON_OVERFLOW,
1126 XML_FALSE );
1129 // Native number transliteration
1130 ::com::sun::star::i18n::NativeNumberXmlAttributes aAttr;
1131 rFormat.GetNatNumXml( aAttr, nPart );
1132 if ( !aAttr.Format.isEmpty() )
1134 /* FIXME-BCP47: ODF defines no transliteration-script or
1135 * transliteration-rfc-language-tag */
1136 LanguageTag aLanguageTag( aAttr.Locale);
1137 OUString aLanguage, aScript, aCountry;
1138 aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
1139 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_FORMAT,
1140 aAttr.Format );
1141 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
1142 aLanguage );
1143 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1144 aCountry );
1145 rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_STYLE,
1146 aAttr.Style );
1149 // The element
1150 SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, eType,
1151 true, true );
1153 // color (properties element)
1155 const Color* pCol = rFormat.GetColor( nPart );
1156 if (pCol)
1157 WriteColorElement_Impl(*pCol);
1159 // detect if there is "real" content, excluding color and maps
1160 //! move to implementation of Write... methods?
1161 bool bAnyContent = false;
1163 // format elements
1165 SvXMLEmbeddedTextEntryArr aEmbeddedEntries;
1166 if ( eBuiltIn == NF_NUMBER_STANDARD )
1168 // default number format contains just one number element
1169 WriteNumberElement_Impl( -1, -1, 1, OUString(), false, 0, aEmbeddedEntries );
1170 bAnyContent = true;
1172 else if ( eBuiltIn == NF_BOOLEAN )
1174 // boolean format contains just one boolean element
1175 WriteBooleanElement_Impl();
1176 bAnyContent = true;
1178 else
1180 // first loop to collect attributes
1182 bool bDecDashes = false;
1183 bool bExpFound = false;
1184 bool bCurrFound = false;
1185 bool bInInteger = true;
1186 bool bExpSign = true;
1187 sal_Int32 nExpDigits = 0;
1188 sal_Int32 nIntegerSymbols = 0; // for embedded-text, including "#"
1189 sal_Int32 nTrailingThousands = 0; // thousands-separators after all digits
1190 sal_Int32 nMinDecimals = nPrecision;
1191 OUString sCurrExt;
1192 OUString aCalendar;
1193 sal_uInt16 nPos = 0;
1194 bool bEnd = false;
1195 while (!bEnd)
1197 short nElemType = rFormat.GetNumForType( nPart, nPos, false );
1198 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos, false );
1200 switch ( nElemType )
1202 case 0:
1203 bEnd = true; // end of format reached
1204 break;
1205 case NF_SYMBOLTYPE_DIGIT:
1206 if ( bExpFound && pElemStr )
1207 nExpDigits += pElemStr->getLength();
1208 else if ( !bDecDashes && pElemStr && (*pElemStr)[0] == '-' )
1210 bDecDashes = true;
1211 nMinDecimals = 0;
1213 else if ( !bInInteger && pElemStr )
1215 for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 && (*pElemStr)[i] == '#'; i-- )
1217 nMinDecimals --;
1220 if ( bInInteger && pElemStr )
1221 nIntegerSymbols += pElemStr->getLength();
1222 nTrailingThousands = 0;
1223 break;
1224 case NF_SYMBOLTYPE_DECSEP:
1225 bInInteger = false;
1226 break;
1227 case NF_SYMBOLTYPE_THSEP:
1228 if (pElemStr)
1229 nTrailingThousands += pElemStr->getLength(); // is reset to 0 if digits follow
1230 break;
1231 case NF_SYMBOLTYPE_EXP:
1232 bExpFound = true; // following digits are exponent digits
1233 bInInteger = false;
1234 if ( pElemStr && pElemStr->getLength() == 1 )
1235 bExpSign = false; // for 0.00E0
1236 break;
1237 case NF_SYMBOLTYPE_CURRENCY:
1238 bCurrFound = true;
1239 break;
1240 case NF_SYMBOLTYPE_CURREXT:
1241 if (pElemStr)
1242 sCurrExt = *pElemStr;
1243 break;
1245 // E, EE, R, RR: select non-gregorian calendar
1246 // AAA, AAAA: calendar is switched at the position of the element
1247 case NF_KEY_EC:
1248 case NF_KEY_EEC:
1249 case NF_KEY_R:
1250 case NF_KEY_RR:
1251 if (aCalendar.isEmpty())
1252 aCalendar = lcl_GetDefaultCalendar( pFormatter, nLang );
1253 break;
1255 ++nPos;
1258 // collect strings for embedded-text (must be known before number element is written)
1260 bool bAllowEmbedded = ( nFmtType == 0 || nFmtType == css::util::NumberFormat::NUMBER ||
1261 nFmtType == css::util::NumberFormat::CURRENCY ||
1262 nFmtType == css::util::NumberFormat::PERCENT );
1263 if ( bAllowEmbedded )
1265 sal_Int32 nDigitsPassed = 0;
1266 nPos = 0;
1267 bEnd = false;
1268 while (!bEnd)
1270 short nElemType = rFormat.GetNumForType( nPart, nPos, false );
1271 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos, false );
1273 switch ( nElemType )
1275 case 0:
1276 bEnd = true; // end of format reached
1277 break;
1278 case NF_SYMBOLTYPE_DIGIT:
1279 if ( pElemStr )
1280 nDigitsPassed += pElemStr->getLength();
1281 break;
1282 case NF_SYMBOLTYPE_STRING:
1283 case NF_SYMBOLTYPE_BLANK:
1284 case NF_SYMBOLTYPE_PERCENT:
1285 if ( nDigitsPassed > 0 && nDigitsPassed < nIntegerSymbols && pElemStr )
1287 // text (literal or underscore) within the integer part of a number:number element
1289 OUString aEmbeddedStr;
1290 if ( nElemType == NF_SYMBOLTYPE_STRING || nElemType == NF_SYMBOLTYPE_PERCENT )
1292 aEmbeddedStr = *pElemStr;
1294 else
1296 SvNumberformat::InsertBlanks( aEmbeddedStr, 0, (*pElemStr)[1] );
1298 sal_Int32 nEmbedPos = nIntegerSymbols - nDigitsPassed;
1300 SvXMLEmbeddedTextEntry* pObj = new SvXMLEmbeddedTextEntry( nPos, nEmbedPos, aEmbeddedStr );
1301 aEmbeddedEntries.push_back( pObj );
1303 break;
1305 ++nPos;
1309 // final loop to write elements
1311 bool bNumWritten = false;
1312 bool bCurrencyWritten = false;
1313 short nPrevType = 0;
1314 nPos = 0;
1315 bEnd = false;
1316 while (!bEnd)
1318 short nElemType = rFormat.GetNumForType( nPart, nPos, false );
1319 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos, false );
1321 switch ( nElemType )
1323 case 0:
1324 bEnd = true; // end of format reached
1325 break;
1326 case NF_SYMBOLTYPE_STRING:
1327 case NF_SYMBOLTYPE_DATESEP:
1328 case NF_SYMBOLTYPE_TIMESEP:
1329 case NF_SYMBOLTYPE_TIME100SECSEP:
1330 case NF_SYMBOLTYPE_PERCENT:
1331 if (pElemStr)
1333 if ( ( nPrevType == NF_KEY_S || nPrevType == NF_KEY_SS ) &&
1334 ( nElemType == NF_SYMBOLTYPE_TIME100SECSEP ) &&
1335 nPrecision > 0 )
1337 // decimal separator after seconds is implied by
1338 // "decimal-places" attribute and must not be written
1339 // as text element
1340 //! difference between '.' and ',' is lost here
1342 else if ( lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
1344 // text is written as embedded-text child of the number,
1345 // don't create a text element
1347 else if ( nFmtType == css::util::NumberFormat::CURRENCY && !bCurrFound && !bCurrencyWritten )
1349 // automatic currency symbol is implemented as part of
1350 // normal text -> search for the symbol
1351 bCurrencyWritten = WriteTextWithCurrency_Impl( *pElemStr,
1352 LanguageTag::convertToLocale( nLang ) );
1353 bAnyContent = true;
1355 else
1356 AddToTextElement_Impl( *pElemStr );
1358 break;
1359 case NF_SYMBOLTYPE_BLANK:
1360 if ( pElemStr && !lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
1362 // turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat
1363 // (#i20396# the spaces may also be in embedded-text elements)
1365 OUString aBlanks;
1366 SvNumberformat::InsertBlanks( aBlanks, 0, (*pElemStr)[1] );
1367 AddToTextElement_Impl( aBlanks );
1369 break;
1370 case NF_KEY_GENERAL :
1371 WriteNumberElement_Impl( -1, -1, 1, OUString(), false, 0, aEmbeddedEntries );
1372 break;
1373 case NF_KEY_CCC:
1374 if (pElemStr)
1376 if ( bCurrencyWritten )
1377 AddToTextElement_Impl( *pElemStr ); // never more than one currency element
1378 else
1380 //! must be different from short automatic format
1381 //! but should still be empty (meaning automatic)
1382 // pElemStr is "CCC"
1384 WriteCurrencyElement_Impl( *pElemStr, OUString() );
1385 bAnyContent = true;
1386 bCurrencyWritten = true;
1389 break;
1390 case NF_SYMBOLTYPE_CURRENCY:
1391 if (pElemStr)
1393 if ( bCurrencyWritten )
1394 AddToTextElement_Impl( *pElemStr ); // never more than one currency element
1395 else
1397 WriteCurrencyElement_Impl( *pElemStr, sCurrExt );
1398 bAnyContent = true;
1399 bCurrencyWritten = true;
1402 break;
1403 case NF_SYMBOLTYPE_DIGIT:
1404 if (!bNumWritten) // write number part
1406 switch ( nFmtType )
1408 // for type 0 (not recognized as a special type),
1409 // write a "normal" number
1410 case 0:
1411 case css::util::NumberFormat::NUMBER:
1412 case css::util::NumberFormat::CURRENCY:
1413 case css::util::NumberFormat::PERCENT:
1415 // decimals
1416 // only some built-in formats have automatic decimals
1417 sal_Int32 nDecimals = nPrecision; // from GetFormatSpecialInfo
1418 if ( eBuiltIn == NF_NUMBER_STANDARD ||
1419 eBuiltIn == NF_CURRENCY_1000DEC2 ||
1420 eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
1421 eBuiltIn == NF_CURRENCY_1000DEC2_CCC ||
1422 eBuiltIn == NF_CURRENCY_1000DEC2_DASHED )
1423 nDecimals = -1;
1425 // integer digits
1426 // only one built-in format has automatic integer digits
1427 sal_Int32 nInteger = nLeading;
1428 if ( eBuiltIn == NF_NUMBER_SYSTEM )
1429 nInteger = -1;
1431 // string for decimal replacement
1432 // has to be taken from nPrecision
1433 // (positive number even for automatic decimals)
1434 OUStringBuffer sDashStr;
1435 if (bDecDashes && nPrecision > 0)
1436 comphelper::string::padToLength(sDashStr, nPrecision, '-');
1438 WriteNumberElement_Impl(nDecimals, nMinDecimals, nInteger, sDashStr.makeStringAndClear(),
1439 bThousand, nTrailingThousands, aEmbeddedEntries);
1440 bAnyContent = true;
1442 break;
1443 case css::util::NumberFormat::SCIENTIFIC:
1444 // #i43959# for scientific numbers, count all integer symbols ("0" and "#")
1445 // as integer digits: use nIntegerSymbols instead of nLeading
1446 // nIntegerSymbols represents exponent interval (for engineering notation)
1447 WriteScientificElement_Impl( nPrecision, nMinDecimals, nLeading, bThousand, nExpDigits, nIntegerSymbols, bExpSign );
1448 bAnyContent = true;
1449 break;
1450 case css::util::NumberFormat::FRACTION:
1452 sal_Int32 nInteger = nLeading;
1453 if ( pElemStr && (*pElemStr)[0] == '?' )
1455 // If the first digit character is a question mark,
1456 // the fraction doesn't have an integer part, and no
1457 // min-integer-digits attribute must be written.
1458 nInteger = -1;
1460 sal_Int32 nDenominator = rFormat.GetForcedDenominatorForType( nPart );
1461 WriteFractionElement_Impl( nInteger, bThousand, nPrecision, nPrecision, nDenominator );
1462 bAnyContent = true;
1464 break;
1467 bNumWritten = true;
1469 break;
1470 case NF_SYMBOLTYPE_DECSEP:
1471 if ( pElemStr && nPrecision == 0 )
1473 // A decimal separator after the number, without following decimal digits,
1474 // isn't modelled as part of the number element, so it's written as text
1475 // (the distinction between a quoted and non-quoted, locale-dependent
1476 // character is lost here).
1478 AddToTextElement_Impl( *pElemStr );
1480 break;
1481 case NF_SYMBOLTYPE_DEL:
1482 if ( pElemStr && comphelper::string::equals(*pElemStr, '@') )
1484 WriteTextContentElement_Impl();
1485 bAnyContent = true;
1487 break;
1489 case NF_SYMBOLTYPE_CALENDAR:
1490 if ( pElemStr )
1491 aCalendar = *pElemStr;
1492 break;
1494 // date elements:
1496 case NF_KEY_D:
1497 case NF_KEY_DD:
1499 bool bLong = ( nElemType == NF_KEY_DD );
1500 WriteDayElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1501 bAnyContent = true;
1503 break;
1504 case NF_KEY_DDD:
1505 case NF_KEY_DDDD:
1506 case NF_KEY_NN:
1507 case NF_KEY_NNN:
1508 case NF_KEY_NNNN:
1509 case NF_KEY_AAA:
1510 case NF_KEY_AAAA:
1512 OUString aCalAttr = aCalendar;
1513 if ( nElemType == NF_KEY_AAA || nElemType == NF_KEY_AAAA )
1515 // calendar attribute for AAA and AAAA is switched only for this element
1516 if (aCalAttr.isEmpty())
1517 aCalAttr = lcl_GetDefaultCalendar( pFormatter, nLang );
1520 bool bLong = ( nElemType == NF_KEY_NNN || nElemType == NF_KEY_NNNN ||
1521 nElemType == NF_KEY_DDDD || nElemType == NF_KEY_AAAA );
1522 WriteDayOfWeekElement_Impl( aCalAttr, ( bSystemDate ? bLongSysDate : bLong ) );
1523 bAnyContent = true;
1524 if ( nElemType == NF_KEY_NNNN )
1526 // write additional text element for separator
1527 pLocaleData->setLanguageTag( LanguageTag( nLang ) );
1528 AddToTextElement_Impl( pLocaleData->getLongDateDayOfWeekSep() );
1531 break;
1532 case NF_KEY_M:
1533 case NF_KEY_MM:
1534 case NF_KEY_MMM:
1535 case NF_KEY_MMMM:
1536 case NF_KEY_MMMMM: //! first letter of month name, no attribute available
1538 bool bLong = ( nElemType == NF_KEY_MM || nElemType == NF_KEY_MMMM );
1539 bool bText = ( nElemType == NF_KEY_MMM || nElemType == NF_KEY_MMMM ||
1540 nElemType == NF_KEY_MMMMM );
1541 WriteMonthElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ), bText );
1542 bAnyContent = true;
1544 break;
1545 case NF_KEY_YY:
1546 case NF_KEY_YYYY:
1547 case NF_KEY_EC:
1548 case NF_KEY_EEC:
1549 case NF_KEY_R: //! R acts as EE, no attribute available
1551 //! distinguish EE and R
1552 // calendar attribute for E and EE and R is set in first loop
1553 bool bLong = ( nElemType == NF_KEY_YYYY || nElemType == NF_KEY_EEC ||
1554 nElemType == NF_KEY_R );
1555 WriteYearElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1556 bAnyContent = true;
1558 break;
1559 case NF_KEY_G:
1560 case NF_KEY_GG:
1561 case NF_KEY_GGG:
1562 case NF_KEY_RR: //! RR acts as GGGEE, no attribute available
1564 //! distinguish GG and GGG and RR
1565 bool bLong = ( nElemType == NF_KEY_GGG || nElemType == NF_KEY_RR );
1566 WriteEraElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1567 bAnyContent = true;
1568 if ( nElemType == NF_KEY_RR )
1570 // calendar attribute for RR is set in first loop
1571 WriteYearElement_Impl( aCalendar, ( bSystemDate || bLongSysDate ) );
1574 break;
1575 case NF_KEY_Q:
1576 case NF_KEY_QQ:
1578 bool bLong = ( nElemType == NF_KEY_QQ );
1579 WriteQuarterElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1580 bAnyContent = true;
1582 break;
1583 case NF_KEY_WW:
1584 WriteWeekElement_Impl( aCalendar );
1585 bAnyContent = true;
1586 break;
1588 // time elements (bSystemDate is not used):
1590 case NF_KEY_H:
1591 case NF_KEY_HH:
1592 WriteHoursElement_Impl( nElemType == NF_KEY_HH );
1593 bAnyContent = true;
1594 break;
1595 case NF_KEY_MI:
1596 case NF_KEY_MMI:
1597 WriteMinutesElement_Impl( nElemType == NF_KEY_MMI );
1598 bAnyContent = true;
1599 break;
1600 case NF_KEY_S:
1601 case NF_KEY_SS:
1602 WriteSecondsElement_Impl( ( nElemType == NF_KEY_SS ), nPrecision );
1603 bAnyContent = true;
1604 break;
1605 case NF_KEY_AMPM:
1606 case NF_KEY_AP:
1607 WriteAMPMElement_Impl(); // short/long?
1608 bAnyContent = true;
1609 break;
1610 case NF_SYMBOLTYPE_STAR :
1611 // export only if ODF 1.2 extensions are enabled
1612 if( rExport.getDefaultVersion() > SvtSaveOptions::ODFVER_012 )
1614 if ( pElemStr && pElemStr->getLength() > 1 )
1615 WriteRepeatedElement_Impl( (*pElemStr)[1] );
1617 break;
1619 nPrevType = nElemType;
1620 ++nPos;
1624 if ( !sTextContent.isEmpty() )
1625 bAnyContent = true; // element written in FinishTextElement_Impl
1627 FinishTextElement_Impl(); // final text element - before maps
1629 if ( !bAnyContent )
1631 // for an empty format, write an empty text element
1632 SvXMLElementExport aTElem( rExport, XML_NAMESPACE_NUMBER, XML_TEXT,
1633 true, false );
1636 // mapping (conditions) must be last elements
1638 if (bDefPart)
1640 SvNumberformatLimitOps eOp1, eOp2;
1641 double fLimit1, fLimit2;
1642 rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
1644 WriteMapElement_Impl( eOp1, fLimit1, nKey, 0 );
1645 WriteMapElement_Impl( eOp2, fLimit2, nKey, 1 );
1647 if ( rFormat.HasTextFormat() )
1649 // 4th part is for text -> make an "all other numbers" condition for the 3rd part
1650 // by reversing the 2nd condition
1652 SvNumberformatLimitOps eOp3 = NUMBERFORMAT_OP_NO;
1653 double fLimit3 = fLimit2;
1654 switch ( eOp2 )
1656 case NUMBERFORMAT_OP_EQ: eOp3 = NUMBERFORMAT_OP_NE; break;
1657 case NUMBERFORMAT_OP_NE: eOp3 = NUMBERFORMAT_OP_EQ; break;
1658 case NUMBERFORMAT_OP_LT: eOp3 = NUMBERFORMAT_OP_GE; break;
1659 case NUMBERFORMAT_OP_LE: eOp3 = NUMBERFORMAT_OP_GT; break;
1660 case NUMBERFORMAT_OP_GT: eOp3 = NUMBERFORMAT_OP_LE; break;
1661 case NUMBERFORMAT_OP_GE: eOp3 = NUMBERFORMAT_OP_LT; break;
1662 default:
1663 break;
1666 if ( fLimit1 == fLimit2 &&
1667 ( ( eOp1 == NUMBERFORMAT_OP_LT && eOp2 == NUMBERFORMAT_OP_GT ) ||
1668 ( eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_LT ) ) )
1670 // For <x and >x, add =x as last condition
1671 // (just for readability, <=x would be valid, too)
1673 eOp3 = NUMBERFORMAT_OP_EQ;
1676 WriteMapElement_Impl( eOp3, fLimit3, nKey, 2 );
1681 // export one format
1683 void SvXMLNumFmtExport::ExportFormat_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey )
1685 sal_uInt16 nUsedParts = 0;
1686 sal_uInt16 nPart;
1687 for (nPart=0; nPart<XMLNUM_MAX_PARTS; nPart++)
1688 if (rFormat.GetNumForType( nPart, 0, false ) != 0)
1689 nUsedParts = nPart+1;
1691 SvNumberformatLimitOps eOp1, eOp2;
1692 double fLimit1, fLimit2;
1693 rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
1695 // if conditions are set, even empty formats must be written
1697 if ( eOp1 != NUMBERFORMAT_OP_NO && nUsedParts < 2 )
1698 nUsedParts = 2;
1699 if ( eOp2 != NUMBERFORMAT_OP_NO && nUsedParts < 3 )
1700 nUsedParts = 3;
1701 if ( rFormat.HasTextFormat() && nUsedParts < 4 )
1702 nUsedParts = 4;
1704 for (nPart=0; nPart<nUsedParts; nPart++)
1706 bool bDefault = ( nPart+1 == nUsedParts ); // last = default
1707 ExportPart_Impl( rFormat, nKey, nPart, bDefault );
1711 // export method called by application
1713 void SvXMLNumFmtExport::Export( bool bIsAutoStyle )
1715 if ( !pFormatter )
1716 return; // no formatter -> no entries
1718 sal_uInt32 nKey;
1719 const SvNumberformat* pFormat = NULL;
1720 bool bNext(pUsedList->GetFirstUsed(nKey));
1721 while(bNext)
1723 pFormat = pFormatter->GetEntry(nKey);
1724 if(pFormat)
1725 ExportFormat_Impl( *pFormat, nKey );
1726 bNext = pUsedList->GetNextUsed(nKey);
1728 if (!bIsAutoStyle)
1730 std::vector<sal_uInt16> aLanguages;
1731 pFormatter->GetUsedLanguages( aLanguages );
1732 for (std::vector<sal_uInt16>::const_iterator it(aLanguages.begin()); it != aLanguages.end(); ++it)
1734 LanguageType nLang = *it;
1736 sal_uInt32 nDefaultIndex = 0;
1737 SvNumberFormatTable& rTable = pFormatter->GetEntryTable(
1738 css::util::NumberFormat::DEFINED, nDefaultIndex, nLang );
1739 SvNumberFormatTable::iterator it2 = rTable.begin();
1740 while (it2 != rTable.end())
1742 nKey = it2->first;
1743 pFormat = it2->second;
1744 if (!pUsedList->IsUsed(nKey))
1746 DBG_ASSERT((pFormat->GetType() & css::util::NumberFormat::DEFINED), "a not user defined numberformat found");
1747 // user-defined and used formats are exported
1748 ExportFormat_Impl( *pFormat, nKey );
1749 // if it is a user-defined Format it will be added else nothing will happen
1750 pUsedList->SetUsed(nKey);
1753 ++it2;
1757 pUsedList->Export();
1760 OUString SvXMLNumFmtExport::GetStyleName( sal_uInt32 nKey )
1762 if(pUsedList->IsUsed(nKey) || pUsedList->IsWasUsed(nKey))
1763 return lcl_CreateStyleName( nKey, 0, true, sPrefix );
1764 else
1766 OSL_FAIL("There is no written Data-Style");
1767 return OUString();
1771 void SvXMLNumFmtExport::SetUsed( sal_uInt32 nKey )
1773 DBG_ASSERT( pFormatter != NULL, "missing formatter" );
1774 if( !pFormatter )
1775 return;
1777 if (pFormatter->GetEntry(nKey))
1778 pUsedList->SetUsed( nKey );
1779 else {
1780 OSL_FAIL("no existing Numberformat found with this key");
1784 void SvXMLNumFmtExport::GetWasUsed(uno::Sequence<sal_Int32>& rWasUsed)
1786 if (pUsedList)
1787 pUsedList->GetWasUsed(rWasUsed);
1790 void SvXMLNumFmtExport::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
1792 if (pUsedList)
1793 pUsedList->SetWasUsed(rWasUsed);
1796 static const SvNumberformat* lcl_GetFormat( SvNumberFormatter* pFormatter,
1797 sal_uInt32 nKey )
1799 return ( pFormatter != NULL ) ? pFormatter->GetEntry( nKey ) : NULL;
1802 sal_uInt32 SvXMLNumFmtExport::ForceSystemLanguage( sal_uInt32 nKey )
1804 sal_uInt32 nRet = nKey;
1806 const SvNumberformat* pFormat = lcl_GetFormat( pFormatter, nKey );
1807 if( pFormat != NULL )
1809 DBG_ASSERT( pFormatter != NULL, "format without formatter?" );
1811 sal_Int32 nErrorPos;
1812 short nType = pFormat->GetType();
1814 sal_uInt32 nNewKey = pFormatter->GetFormatForLanguageIfBuiltIn(
1815 nKey, LANGUAGE_SYSTEM );
1817 if( nNewKey != nKey )
1819 nRet = nNewKey;
1821 else
1823 OUString aFormatString( pFormat->GetFormatstring() );
1824 pFormatter->PutandConvertEntry(
1825 aFormatString,
1826 nErrorPos, nType, nNewKey,
1827 pFormat->GetLanguage(), LANGUAGE_SYSTEM );
1829 // success? Then use new key.
1830 if( nErrorPos == 0 )
1831 nRet = nNewKey;
1835 return nRet;
1838 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */