1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <comphelper/sequence.hxx>
21 #include <comphelper/string.hxx>
22 #include <svl/zforlist.hxx>
23 #include <svl/zformat.hxx>
24 #include <svl/numuno.hxx>
25 #include <i18nlangtag/mslangid.hxx>
26 #include <i18nlangtag/languagetag.hxx>
27 #include <tools/debug.hxx>
28 #include <rtl/math.hxx>
29 #include <unotools/calendarwrapper.hxx>
30 #include <unotools/charclass.hxx>
31 #include <com/sun/star/lang/Locale.hpp>
32 #include <rtl/ustrbuf.hxx>
33 #include <sal/log.hxx>
34 #include <osl/diagnose.h>
35 #include <tools/color.hxx>
36 #include <sax/tools/converter.hxx>
37 
38 #include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>
39 
40 #include <xmloff/xmlnumfe.hxx>
41 #include <xmloff/xmlnmspe.hxx>
42 #include <xmloff/xmlnumfi.hxx>
43 
44 #include <svl/nfsymbol.hxx>
45 #include <xmloff/xmltoken.hxx>
46 #include <xmloff/xmlexp.hxx>
47 
48 #include <float.h>
49 #include <set>
50 #include <vector>
51 
52 using namespace ::com::sun::star;
53 using namespace ::xmloff::token;
54 using namespace ::svt;
55 
56 typedef std::set< sal_uInt32 >  SvXMLuInt32Set;
57 
58 struct SvXMLEmbeddedTextEntry
59 {
60     sal_uInt16 const  nSourcePos;     // position in NumberFormat (to skip later)
61     sal_Int32 const   nFormatPos;     // resulting position in embedded-text element
62     OUString const    aText;
63 
SvXMLEmbeddedTextEntrySvXMLEmbeddedTextEntry64     SvXMLEmbeddedTextEntry( sal_uInt16 nSP, sal_Int32 nFP, const OUString& rT ) :
65         nSourcePos(nSP), nFormatPos(nFP), aText(rT) {}
66 };
67 
68 class SvXMLEmbeddedTextEntryArr
69 {
70     typedef std::vector<SvXMLEmbeddedTextEntry> DataType;
71     DataType maData;
72 
73 public:
74 
push_back(SvXMLEmbeddedTextEntry const & r)75     void push_back( SvXMLEmbeddedTextEntry const& r )
76     {
77         maData.push_back(r);
78     }
79 
operator [](size_t i) const80     const SvXMLEmbeddedTextEntry& operator[] ( size_t i ) const
81     {
82         return maData[i];
83     }
84 
size() const85     size_t size() const
86     {
87         return maData.size();
88     }
89 };
90 
91 class SvXMLNumUsedList_Impl
92 {
93     SvXMLuInt32Set              aUsed;
94     SvXMLuInt32Set              aWasUsed;
95     SvXMLuInt32Set::iterator    aCurrentUsedPos;
96     sal_uInt32                  nUsedCount;
97     sal_uInt32                  nWasUsedCount;
98 
99 public:
100             SvXMLNumUsedList_Impl();
101 
102     void    SetUsed( sal_uInt32 nKey );
103     bool    IsUsed( sal_uInt32 nKey ) const;
104     bool    IsWasUsed( sal_uInt32 nKey ) const;
105     void    Export();
106 
107     bool    GetFirstUsed(sal_uInt32& nKey);
108     bool    GetNextUsed(sal_uInt32& nKey);
109 
110     uno::Sequence<sal_Int32> GetWasUsed() const;
111     void SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed);
112 };
113 
114 //! SvXMLNumUsedList_Impl should be optimized!
115 
SvXMLNumUsedList_Impl()116 SvXMLNumUsedList_Impl::SvXMLNumUsedList_Impl() :
117     nUsedCount(0),
118     nWasUsedCount(0)
119 {
120 }
121 
SetUsed(sal_uInt32 nKey)122 void SvXMLNumUsedList_Impl::SetUsed( sal_uInt32 nKey )
123 {
124     if ( !IsWasUsed(nKey) )
125     {
126         std::pair<SvXMLuInt32Set::iterator, bool> aPair = aUsed.insert( nKey );
127         if (aPair.second)
128             nUsedCount++;
129     }
130 }
131 
IsUsed(sal_uInt32 nKey) const132 bool SvXMLNumUsedList_Impl::IsUsed( sal_uInt32 nKey ) const
133 {
134     SvXMLuInt32Set::const_iterator aItr = aUsed.find(nKey);
135     return (aItr != aUsed.end());
136 }
137 
IsWasUsed(sal_uInt32 nKey) const138 bool SvXMLNumUsedList_Impl::IsWasUsed( sal_uInt32 nKey ) const
139 {
140     SvXMLuInt32Set::const_iterator aItr = aWasUsed.find(nKey);
141     return (aItr != aWasUsed.end());
142 }
143 
Export()144 void SvXMLNumUsedList_Impl::Export()
145 {
146     SvXMLuInt32Set::const_iterator aItr = aUsed.begin();
147     while (aItr != aUsed.end())
148     {
149         std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( *aItr );
150         if (aPair.second)
151             nWasUsedCount++;
152         ++aItr;
153     }
154     aUsed.clear();
155     nUsedCount = 0;
156 }
157 
GetFirstUsed(sal_uInt32 & nKey)158 bool SvXMLNumUsedList_Impl::GetFirstUsed(sal_uInt32& nKey)
159 {
160     bool bRet(false);
161     aCurrentUsedPos = aUsed.begin();
162     if(nUsedCount)
163     {
164         DBG_ASSERT(aCurrentUsedPos != aUsed.end(), "something went wrong");
165         nKey = *aCurrentUsedPos;
166         bRet = true;
167     }
168     return bRet;
169 }
170 
GetNextUsed(sal_uInt32 & nKey)171 bool SvXMLNumUsedList_Impl::GetNextUsed(sal_uInt32& nKey)
172 {
173     bool bRet(false);
174     if (aCurrentUsedPos != aUsed.end())
175     {
176         ++aCurrentUsedPos;
177         if (aCurrentUsedPos != aUsed.end())
178         {
179             nKey = *aCurrentUsedPos;
180             bRet = true;
181         }
182     }
183     return bRet;
184 }
185 
GetWasUsed() const186 uno::Sequence<sal_Int32> SvXMLNumUsedList_Impl::GetWasUsed() const
187 {
188     return comphelper::containerToSequence<sal_Int32>(aWasUsed);
189 }
190 
SetWasUsed(const uno::Sequence<sal_Int32> & rWasUsed)191 void SvXMLNumUsedList_Impl::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
192 {
193     DBG_ASSERT(nWasUsedCount == 0, "WasUsed should be empty");
194     for (const auto nWasUsed : rWasUsed)
195     {
196         std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( nWasUsed );
197         if (aPair.second)
198             nWasUsedCount++;
199     }
200 }
201 
SvXMLNumFmtExport(SvXMLExport & rExp,const uno::Reference<util::XNumberFormatsSupplier> & rSupp)202 SvXMLNumFmtExport::SvXMLNumFmtExport(
203             SvXMLExport& rExp,
204             const uno::Reference< util::XNumberFormatsSupplier >& rSupp ) :
205     rExport( rExp ),
206     sPrefix( OUString("N") ),
207     pFormatter( nullptr )
208 {
209     //  supplier must be SvNumberFormatsSupplierObj
210     SvNumberFormatsSupplierObj* pObj =
211                     comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( rSupp );
212     if (pObj)
213         pFormatter = pObj->GetNumberFormatter();
214 
215     if ( pFormatter )
216     {
217         pCharClass.reset( new CharClass( pFormatter->GetComponentContext(),
218             pFormatter->GetLanguageTag() ) );
219         pLocaleData.reset( new LocaleDataWrapper( pFormatter->GetComponentContext(),
220             pFormatter->GetLanguageTag() ) );
221     }
222     else
223     {
224         LanguageTag aLanguageTag( MsLangId::getSystemLanguage() );
225 
226         pCharClass.reset( new CharClass( rExport.getComponentContext(), aLanguageTag ) );
227         pLocaleData.reset( new LocaleDataWrapper( rExport.getComponentContext(), aLanguageTag ) );
228     }
229 
230     pUsedList.reset(new SvXMLNumUsedList_Impl);
231 }
232 
SvXMLNumFmtExport(SvXMLExport & rExp,const css::uno::Reference<css::util::XNumberFormatsSupplier> & rSupp,const OUString & rPrefix)233 SvXMLNumFmtExport::SvXMLNumFmtExport(
234                        SvXMLExport& rExp,
235                        const css::uno::Reference< css::util::XNumberFormatsSupplier >& rSupp,
236                        const OUString& rPrefix ) :
237     rExport( rExp ),
238     sPrefix( rPrefix ),
239     pFormatter( nullptr )
240 {
241     //  supplier must be SvNumberFormatsSupplierObj
242     SvNumberFormatsSupplierObj* pObj =
243                     comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( rSupp );
244     if (pObj)
245         pFormatter = pObj->GetNumberFormatter();
246 
247     if ( pFormatter )
248     {
249         pCharClass.reset( new CharClass( pFormatter->GetComponentContext(),
250             pFormatter->GetLanguageTag() ) );
251         pLocaleData.reset( new LocaleDataWrapper( pFormatter->GetComponentContext(),
252             pFormatter->GetLanguageTag() ) );
253     }
254     else
255     {
256         LanguageTag aLanguageTag( MsLangId::getSystemLanguage() );
257 
258         pCharClass.reset( new CharClass( rExport.getComponentContext(), aLanguageTag ) );
259         pLocaleData.reset( new LocaleDataWrapper( rExport.getComponentContext(), aLanguageTag ) );
260     }
261 
262     pUsedList.reset(new SvXMLNumUsedList_Impl);
263 }
264 
~SvXMLNumFmtExport()265 SvXMLNumFmtExport::~SvXMLNumFmtExport()
266 {
267 }
268 
269 //  helper methods
270 
lcl_CreateStyleName(sal_Int32 nKey,sal_Int32 nPart,bool bDefPart,const OUString & rPrefix)271 static OUString lcl_CreateStyleName( sal_Int32 nKey, sal_Int32 nPart, bool bDefPart, const OUString& rPrefix )
272 {
273     OUStringBuffer aFmtName(10);
274     aFmtName.append( rPrefix );
275     aFmtName.append( nKey );
276     if (!bDefPart)
277     {
278         aFmtName.append( 'P' );
279         aFmtName.append( nPart );
280     }
281     return aFmtName.makeStringAndClear();
282 }
283 
AddCalendarAttr_Impl(const OUString & rCalendar)284 void SvXMLNumFmtExport::AddCalendarAttr_Impl( const OUString& rCalendar )
285 {
286     if ( !rCalendar.isEmpty() )
287     {
288         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_CALENDAR, rCalendar );
289     }
290 }
291 
AddStyleAttr_Impl(bool bLong)292 void SvXMLNumFmtExport::AddStyleAttr_Impl( bool bLong )
293 {
294     if ( bLong )            // short is default
295     {
296         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_STYLE, XML_LONG );
297     }
298 }
299 
AddLanguageAttr_Impl(LanguageType nLang)300 void SvXMLNumFmtExport::AddLanguageAttr_Impl( LanguageType nLang )
301 {
302     if ( nLang != LANGUAGE_SYSTEM )
303     {
304         rExport.AddLanguageTagAttributes( XML_NAMESPACE_NUMBER, XML_NAMESPACE_NUMBER,
305                 LanguageTag( nLang), false);
306     }
307 }
308 
309 //  methods to write individual elements within a format
310 
AddToTextElement_Impl(const OUString & rString)311 void SvXMLNumFmtExport::AddToTextElement_Impl( const OUString& rString )
312 {
313     //  append to sTextContent, write element in FinishTextElement_Impl
314     //  to avoid several text elements following each other
315 
316     sTextContent.append( rString );
317 }
318 
FinishTextElement_Impl(bool bUseExtensionNS)319 void SvXMLNumFmtExport::FinishTextElement_Impl(bool bUseExtensionNS)
320 {
321     if ( !sTextContent.isEmpty() )
322     {
323         sal_uInt16 nNS = bUseExtensionNS ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER;
324         SvXMLElementExport aElem( rExport, nNS, XML_TEXT,
325                                   true, false );
326         rExport.Characters( sTextContent.makeStringAndClear() );
327     }
328 }
329 
WriteColorElement_Impl(const Color & rColor)330 void SvXMLNumFmtExport::WriteColorElement_Impl( const Color& rColor )
331 {
332     FinishTextElement_Impl();
333 
334     OUStringBuffer aColStr( 7 );
335     ::sax::Converter::convertColor( aColStr, rColor );
336     rExport.AddAttribute( XML_NAMESPACE_FO, XML_COLOR,
337                           aColStr.makeStringAndClear() );
338 
339     SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, XML_TEXT_PROPERTIES,
340                               true, false );
341 }
342 
WriteCurrencyElement_Impl(const OUString & rString,const OUString & rExt)343 void SvXMLNumFmtExport::WriteCurrencyElement_Impl( const OUString& rString,
344                                                     const OUString& rExt )
345 {
346     FinishTextElement_Impl();
347 
348     if ( !rExt.isEmpty() )
349     {
350         // rExt should be a 16-bit hex value max FFFF which may contain a
351         // leading "-" separator (that is not a minus sign, but toInt32 can be
352         // used to parse it, with post-processing as necessary):
353         sal_Int32 nLang = rExt.toInt32(16);
354         if ( nLang < 0 )
355             nLang = -nLang;
356         AddLanguageAttr_Impl( LanguageType(nLang) );          // adds to pAttrList
357     }
358 
359     SvXMLElementExport aElem( rExport,
360                               XML_NAMESPACE_NUMBER, XML_CURRENCY_SYMBOL,
361                               true, false );
362     rExport.Characters( rString );
363 }
364 
WriteBooleanElement_Impl()365 void SvXMLNumFmtExport::WriteBooleanElement_Impl()
366 {
367     FinishTextElement_Impl();
368 
369     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_BOOLEAN,
370                               true, false );
371 }
372 
WriteTextContentElement_Impl()373 void SvXMLNumFmtExport::WriteTextContentElement_Impl()
374 {
375     FinishTextElement_Impl();
376 
377     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_TEXT_CONTENT,
378                               true, false );
379 }
380 
381 //  date elements
382 
WriteDayElement_Impl(const OUString & rCalendar,bool bLong)383 void SvXMLNumFmtExport::WriteDayElement_Impl( const OUString& rCalendar, bool bLong )
384 {
385     FinishTextElement_Impl();
386 
387     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
388     AddStyleAttr_Impl( bLong );     // adds to pAttrList
389 
390     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_DAY,
391                               true, false );
392 }
393 
WriteMonthElement_Impl(const OUString & rCalendar,bool bLong,bool bText)394 void SvXMLNumFmtExport::WriteMonthElement_Impl( const OUString& rCalendar, bool bLong, bool bText )
395 {
396     FinishTextElement_Impl();
397 
398     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
399     AddStyleAttr_Impl( bLong );     // adds to pAttrList
400     if ( bText )
401     {
402         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TEXTUAL, XML_TRUE );
403     }
404 
405     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_MONTH,
406                               true, false );
407 }
408 
WriteYearElement_Impl(const OUString & rCalendar,bool bLong)409 void SvXMLNumFmtExport::WriteYearElement_Impl( const OUString& rCalendar, bool bLong )
410 {
411     FinishTextElement_Impl();
412 
413     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
414     AddStyleAttr_Impl( bLong );     // adds to pAttrList
415 
416     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_YEAR,
417                               true, false );
418 }
419 
WriteEraElement_Impl(const OUString & rCalendar,bool bLong)420 void SvXMLNumFmtExport::WriteEraElement_Impl( const OUString& rCalendar, bool bLong )
421 {
422     FinishTextElement_Impl();
423 
424     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
425     AddStyleAttr_Impl( bLong );     // adds to pAttrList
426 
427     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_ERA,
428                               true, false );
429 }
430 
WriteDayOfWeekElement_Impl(const OUString & rCalendar,bool bLong)431 void SvXMLNumFmtExport::WriteDayOfWeekElement_Impl( const OUString& rCalendar, bool bLong )
432 {
433     FinishTextElement_Impl();
434 
435     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
436     AddStyleAttr_Impl( bLong );     // adds to pAttrList
437 
438     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_DAY_OF_WEEK,
439                               true, false );
440 }
441 
WriteWeekElement_Impl(const OUString & rCalendar)442 void SvXMLNumFmtExport::WriteWeekElement_Impl( const OUString& rCalendar )
443 {
444     FinishTextElement_Impl();
445 
446     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
447 
448     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_WEEK_OF_YEAR,
449                               true, false );
450 }
451 
WriteQuarterElement_Impl(const OUString & rCalendar,bool bLong)452 void SvXMLNumFmtExport::WriteQuarterElement_Impl( const OUString& rCalendar, bool bLong )
453 {
454     FinishTextElement_Impl();
455 
456     AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
457     AddStyleAttr_Impl( bLong );     // adds to pAttrList
458 
459     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_QUARTER,
460                               true, false );
461 }
462 
463 //  time elements
464 
WriteHoursElement_Impl(bool bLong)465 void SvXMLNumFmtExport::WriteHoursElement_Impl( bool bLong )
466 {
467     FinishTextElement_Impl();
468 
469     AddStyleAttr_Impl( bLong );     // adds to pAttrList
470 
471     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_HOURS,
472                               true, false );
473 }
474 
WriteMinutesElement_Impl(bool bLong)475 void SvXMLNumFmtExport::WriteMinutesElement_Impl( bool bLong )
476 {
477     FinishTextElement_Impl();
478 
479     AddStyleAttr_Impl( bLong );     // adds to pAttrList
480 
481     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_MINUTES,
482                               true, false );
483 }
484 
WriteRepeatedElement_Impl(sal_Unicode nChar)485 void SvXMLNumFmtExport::WriteRepeatedElement_Impl( sal_Unicode nChar )
486 {
487     // Export only for 1.2 with extensions or 1.3 and later.
488     SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
489     if (eVersion > SvtSaveOptions::ODFSVER_012)
490     {
491         FinishTextElement_Impl(true);
492         // For 1.2+ use loext namespace, for 1.3 use number namespace.
493         SvXMLElementExport aElem( rExport,
494                                   ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
495                                   XML_FILL_CHARACTER, true, false );
496         rExport.Characters( OUString( nChar ) );
497     }
498 }
499 
WriteSecondsElement_Impl(bool bLong,sal_uInt16 nDecimals)500 void SvXMLNumFmtExport::WriteSecondsElement_Impl( bool bLong, sal_uInt16 nDecimals )
501 {
502     FinishTextElement_Impl();
503 
504     AddStyleAttr_Impl( bLong );     // adds to pAttrList
505     if ( nDecimals > 0 )
506     {
507         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
508                               OUString::number(  nDecimals ) );
509     }
510 
511     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_SECONDS,
512                               true, false );
513 }
514 
WriteAMPMElement_Impl()515 void SvXMLNumFmtExport::WriteAMPMElement_Impl()
516 {
517     FinishTextElement_Impl();
518 
519     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_AM_PM,
520                               true, false );
521 }
522 
523 //  numbers
524 
WriteNumberElement_Impl(sal_Int32 nDecimals,sal_Int32 nMinDecimals,sal_Int32 nInteger,const OUString & rDashStr,bool bGrouping,sal_Int32 nTrailingThousands,const SvXMLEmbeddedTextEntryArr & rEmbeddedEntries)525 void SvXMLNumFmtExport::WriteNumberElement_Impl(
526                             sal_Int32 nDecimals, sal_Int32 nMinDecimals,
527                             sal_Int32 nInteger, const OUString& rDashStr,
528                             bool bGrouping, sal_Int32 nTrailingThousands,
529                             const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
530 {
531     FinishTextElement_Impl();
532 
533     //  decimals
534     if ( nDecimals >= 0 )   // negative = automatic
535     {
536         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
537                               OUString::number( nDecimals ) );
538     }
539 
540     if ( nMinDecimals >= 0 )   // negative = automatic
541     {
542         // Export only for 1.2 with extensions or 1.3 and later.
543         SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
544         if (eVersion > SvtSaveOptions::ODFSVER_012)
545         {
546             // For 1.2+ use loext namespace, for 1.3 use number namespace.
547             rExport.AddAttribute(
548                 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
549                                  XML_MIN_DECIMAL_PLACES,
550                                  OUString::number( nMinDecimals ) );
551         }
552     }
553 
554     //  integer digits
555     if ( nInteger >= 0 )    // negative = automatic
556     {
557         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
558                               OUString::number( nInteger ) );
559     }
560 
561     //  decimal replacement (dashes) or variable decimals (#)
562     if ( !rDashStr.isEmpty() ||  nMinDecimals < nDecimals )
563     {
564         // full variable decimals means an empty replacement string
565         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_REPLACEMENT,
566                               rDashStr );
567     }
568 
569     //  (automatic) grouping separator
570     if ( bGrouping )
571     {
572         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
573     }
574 
575     //  display-factor if there are trailing thousands separators
576     if ( nTrailingThousands )
577     {
578         //  each separator character removes three digits
579         double fFactor = ::rtl::math::pow10Exp( 1.0, 3 * nTrailingThousands );
580 
581         OUStringBuffer aFactStr;
582         ::sax::Converter::convertDouble( aFactStr, fFactor );
583         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DISPLAY_FACTOR, aFactStr.makeStringAndClear() );
584     }
585 
586     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_NUMBER,
587                               true, true );
588 
589     //  number:embedded-text as child elements
590 
591     auto nEntryCount = rEmbeddedEntries.size();
592     for (decltype(nEntryCount) nEntry=0; nEntry < nEntryCount; ++nEntry)
593     {
594         const SvXMLEmbeddedTextEntry *const pObj = &rEmbeddedEntries[nEntry];
595 
596         //  position attribute
597         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_POSITION,
598                                 OUString::number( pObj->nFormatPos ) );
599         SvXMLElementExport aChildElem( rExport, XML_NAMESPACE_NUMBER, XML_EMBEDDED_TEXT,
600                                           true, false );
601 
602         //  text as element content
603         OUStringBuffer aContent( pObj->aText );
604         while ( nEntry+1 < nEntryCount && rEmbeddedEntries[nEntry+1].nFormatPos == pObj->nFormatPos )
605         {
606             // The array can contain several elements for the same position in the number
607             // (for example, literal text and space from underscores). They must be merged
608             // into a single embedded-text element.
609             aContent.append(rEmbeddedEntries[nEntry+1].aText);
610             ++nEntry;
611         }
612         rExport.Characters( aContent.makeStringAndClear() );
613     }
614 }
615 
WriteScientificElement_Impl(sal_Int32 nDecimals,sal_Int32 nMinDecimals,sal_Int32 nInteger,bool bGrouping,sal_Int32 nExp,sal_Int32 nExpInterval,bool bExpSign)616 void SvXMLNumFmtExport::WriteScientificElement_Impl(
617                             sal_Int32 nDecimals, sal_Int32 nMinDecimals, sal_Int32 nInteger,
618                             bool bGrouping, sal_Int32 nExp, sal_Int32 nExpInterval, bool bExpSign )
619 {
620     FinishTextElement_Impl();
621 
622     //  decimals
623     if ( nDecimals >= 0 )   // negative = automatic
624     {
625         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
626                               OUString::number( nDecimals ) );
627     }
628 
629     SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
630     if ( nMinDecimals >= 0 )   // negative = automatic
631     {
632         // Export only for 1.2 with extensions or 1.3 and later.
633         if (eVersion > SvtSaveOptions::ODFSVER_012)
634         {
635             // For 1.2+ use loext namespace, for 1.3 use number namespace.
636             rExport.AddAttribute(
637                 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
638                                  XML_MIN_DECIMAL_PLACES,
639                                  OUString::number( nMinDecimals ) );
640         }
641     }
642 
643     //  integer digits
644     if ( nInteger >= 0 )    // negative = automatic
645     {
646         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
647                               OUString::number( nInteger ) );
648     }
649 
650     //  (automatic) grouping separator
651     if ( bGrouping )
652     {
653         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
654     }
655 
656     //  exponent digits
657     if ( nExp >= 0 )
658     {
659         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_EXPONENT_DIGITS,
660                               OUString::number( nExp ) );
661     }
662 
663     //  exponent interval for engineering notation
664     if ( nExpInterval >= 0 )
665     {
666         // Export only for 1.2 with extensions or 1.3 and later.
667         if (eVersion > SvtSaveOptions::ODFSVER_012)
668         {
669             // For 1.2+ use loext namespace, for 1.3 use number namespace.
670             rExport.AddAttribute(
671                     ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
672                     XML_EXPONENT_INTERVAL, OUString::number( nExpInterval ) );
673         }
674     }
675 
676     //  exponent sign
677     // Export only for 1.2 with extensions or 1.3 and later.
678     if (eVersion > SvtSaveOptions::ODFSVER_012)
679     {
680         // For 1.2+ use loext namespace, for 1.3 use number namespace.
681         rExport.AddAttribute(
682             ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
683                              XML_FORCED_EXPONENT_SIGN,
684                              bExpSign? XML_TRUE : XML_FALSE );
685     }
686 
687     SvXMLElementExport aElem( rExport,
688                               XML_NAMESPACE_NUMBER, XML_SCIENTIFIC_NUMBER,
689                               true, false );
690 }
691 
WriteFractionElement_Impl(sal_Int32 nInteger,bool bGrouping,const SvNumberformat & rFormat,sal_uInt16 nPart)692 void SvXMLNumFmtExport::WriteFractionElement_Impl(
693                             sal_Int32 nInteger, bool bGrouping,
694                             const SvNumberformat& rFormat, sal_uInt16 nPart )
695 {
696     FinishTextElement_Impl();
697     const OUString aNumeratorString = rFormat.GetNumeratorString( nPart );
698     const OUString aDenominatorString = rFormat.GetDenominatorString( nPart );
699     const OUString aIntegerFractionDelimiterString = rFormat.GetIntegerFractionDelimiterString( nPart );
700     sal_Int32 nMaxNumeratorDigits = aNumeratorString.getLength();
701     // Count '0' as '?'
702     sal_Int32 nMinNumeratorDigits = aNumeratorString.replaceAll("0","?").indexOf('?');
703     sal_Int32 nZerosNumeratorDigits = aNumeratorString.indexOf('0');
704     if ( nMinNumeratorDigits >= 0 )
705         nMinNumeratorDigits = nMaxNumeratorDigits - nMinNumeratorDigits;
706     else
707         nMinNumeratorDigits = 0;
708     if ( nZerosNumeratorDigits >= 0 )
709         nZerosNumeratorDigits = nMaxNumeratorDigits - nZerosNumeratorDigits;
710     else
711         nZerosNumeratorDigits = 0;
712     sal_Int32 nMaxDenominatorDigits = aDenominatorString.getLength();
713     sal_Int32 nMinDenominatorDigits = aDenominatorString.replaceAll("0","?").indexOf('?');
714     sal_Int32 nZerosDenominatorDigits = aDenominatorString.indexOf('0');
715     if ( nMinDenominatorDigits >= 0 )
716         nMinDenominatorDigits = nMaxDenominatorDigits - nMinDenominatorDigits;
717     else
718         nMinDenominatorDigits = 0;
719     if ( nZerosDenominatorDigits >= 0 )
720         nZerosDenominatorDigits = nMaxDenominatorDigits - nZerosDenominatorDigits;
721     else
722         nZerosDenominatorDigits = 0;
723     sal_Int32 nDenominator = aDenominatorString.toInt32();
724 
725     //  integer digits
726     if ( nInteger >= 0 )        // negative = default (no integer part)
727     {
728         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
729                               OUString::number( nInteger ) );
730     }
731 
732     //  (automatic) grouping separator
733     if ( bGrouping )
734     {
735         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
736     }
737 
738     // integer/fraction delimiter
739     SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
740     if ( !aIntegerFractionDelimiterString.isEmpty() && aIntegerFractionDelimiterString != " "
741         && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
742     {   // Export only for 1.2 with extensions or 1.3 and later.
743         rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_INTEGER_FRACTION_DELIMITER,
744                               aIntegerFractionDelimiterString );
745     }
746 
747     //  numerator digits
748     if ( nMinNumeratorDigits == 0 ) // at least one digit to keep compatibility with previous versions
749         nMinNumeratorDigits++;
750     rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_NUMERATOR_DIGITS,
751                           OUString::number( nMinNumeratorDigits ) );
752     // Export only for 1.2 with extensions or 1.3 and later.
753     if ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0)
754     {
755         // For extended ODF use loext namespace
756         rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_MAX_NUMERATOR_DIGITS,
757                               OUString::number( nMaxNumeratorDigits ) );
758     }
759     if ( nZerosNumeratorDigits && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
760         rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_ZEROS_NUMERATOR_DIGITS,
761                               OUString::number( nZerosNumeratorDigits ) );
762 
763     if ( nDenominator )
764     {
765         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DENOMINATOR_VALUE,
766                               OUString::number( nDenominator) );
767     }
768     //  it's not necessary to export nDenominatorDigits
769     //  if we have a forced denominator
770     else
771     {
772         if ( nMinDenominatorDigits == 0 ) // at least one digit to keep compatibility with previous versions
773             nMinDenominatorDigits++;
774         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_DENOMINATOR_DIGITS,
775                               OUString::number( nMinDenominatorDigits ) );
776         if (eVersion > SvtSaveOptions::ODFSVER_012)
777         {
778             // For 1.2+ use loext namespace, for 1.3 use number namespace.
779             rExport.AddAttribute(
780                 ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
781                                  XML_MAX_DENOMINATOR_VALUE,
782                                  OUString::number( pow ( 10.0, nMaxDenominatorDigits ) - 1 ) ); // 9, 99 or 999
783         }
784         if ( nZerosDenominatorDigits && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
785             rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_ZEROS_DENOMINATOR_DIGITS,
786                                   OUString::number( nZerosDenominatorDigits ) );
787     }
788 
789     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, XML_FRACTION,
790                               true, false );
791 }
792 
793 //  mapping (condition)
794 
WriteMapElement_Impl(sal_Int32 nOp,double fLimit,sal_Int32 nKey,sal_Int32 nPart)795 void SvXMLNumFmtExport::WriteMapElement_Impl( sal_Int32 nOp, double fLimit,
796                                                 sal_Int32 nKey, sal_Int32 nPart )
797 {
798     FinishTextElement_Impl();
799 
800     if ( nOp != NUMBERFORMAT_OP_NO )
801     {
802         // style namespace
803 
804         OUStringBuffer aCondStr(20);
805         aCondStr.append( "value()" );          //! define constant
806         switch ( nOp )
807         {
808             case NUMBERFORMAT_OP_EQ: aCondStr.append( '=' );  break;
809             case NUMBERFORMAT_OP_NE: aCondStr.append( "!=" );          break;
810             case NUMBERFORMAT_OP_LT: aCondStr.append( '<' );  break;
811             case NUMBERFORMAT_OP_LE: aCondStr.append( "<=" );          break;
812             case NUMBERFORMAT_OP_GT: aCondStr.append( '>' );  break;
813             case NUMBERFORMAT_OP_GE: aCondStr.append( ">=" );          break;
814             default:
815                 OSL_FAIL("unknown operator");
816         }
817         ::rtl::math::doubleToUStringBuffer( aCondStr, fLimit,
818                 rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
819                 '.', true );
820 
821         rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_CONDITION,
822                               aCondStr.makeStringAndClear() );
823 
824         rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME,
825                               rExport.EncodeStyleName( lcl_CreateStyleName( nKey, nPart, false,
826                                                    sPrefix ) ) );
827 
828         SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, XML_MAP,
829                                   true, false );
830     }
831 }
832 
833 //  for old (automatic) currency formats: parse currency symbol from text
834 
lcl_FindSymbol(const OUString & sUpperStr,const OUString & sCurString)835 static sal_Int32 lcl_FindSymbol( const OUString& sUpperStr, const OUString& sCurString )
836 {
837     //  search for currency symbol
838     //  Quoting as in ImpSvNumberformatScan::Symbol_Division
839 
840     sal_Int32 nCPos = 0;
841     while (nCPos >= 0)
842     {
843         nCPos = sUpperStr.indexOf( sCurString, nCPos );
844         if (nCPos >= 0)
845         {
846             // in Quotes?
847             sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sUpperStr, nCPos );
848             if ( nQ < 0 )
849             {
850                 //  dm can be escaped as "dm or \d
851                 sal_Unicode c;
852                 if ( nCPos == 0 ||
853                     ((c = sUpperStr[nCPos-1]) != '"'
854                      && c != '\\') )
855                 {
856                     return nCPos;                   // found
857                 }
858                 else
859                 {
860                     nCPos++;                        // continue
861                 }
862             }
863             else
864             {
865                 nCPos = nQ + 1;                     // continue after quote end
866             }
867         }
868     }
869     return -1;
870 }
871 
WriteTextWithCurrency_Impl(const OUString & rString,const css::lang::Locale & rLocale)872 bool SvXMLNumFmtExport::WriteTextWithCurrency_Impl( const OUString& rString,
873                             const css::lang::Locale& rLocale )
874 {
875     //  returns true if currency element was written
876 
877     bool bRet = false;
878 
879     LanguageTag aLanguageTag( rLocale );
880     pFormatter->ChangeIntl( aLanguageTag.getLanguageType( false) );
881     OUString sCurString, sDummy;
882     pFormatter->GetCompatibilityCurrency( sCurString, sDummy );
883 
884     pCharClass->setLanguageTag( aLanguageTag );
885     OUString sUpperStr = pCharClass->uppercase(rString);
886     sal_Int32 nPos = lcl_FindSymbol( sUpperStr, sCurString );
887     if ( nPos >= 0 )
888     {
889         sal_Int32 nLength = rString.getLength();
890         sal_Int32 nCurLen = sCurString.getLength();
891         sal_Int32 nCont = nPos + nCurLen;
892 
893         //  text before currency symbol
894         if ( nPos > 0 )
895         {
896             AddToTextElement_Impl( rString.copy( 0, nPos ) );
897         }
898         //  currency symbol (empty string -> default)
899         WriteCurrencyElement_Impl( "", "" );
900         bRet = true;
901 
902         //  text after currency symbol
903         if ( nCont < nLength )
904         {
905             AddToTextElement_Impl( rString.copy( nCont, nLength-nCont ) );
906         }
907     }
908     else
909     {
910         AddToTextElement_Impl( rString );       // simple text
911     }
912 
913     return bRet;        // true: currency element written
914 }
915 
lcl_GetDefaultCalendar(SvNumberFormatter const * pFormatter,LanguageType nLang)916 static OUString lcl_GetDefaultCalendar( SvNumberFormatter const * pFormatter, LanguageType nLang )
917 {
918     //  get name of first non-gregorian calendar for the language
919 
920     OUString aCalendar;
921     CalendarWrapper* pCalendar = pFormatter->GetCalendar();
922     if (pCalendar)
923     {
924         lang::Locale aLocale( LanguageTag::convertToLocale( nLang ) );
925 
926         uno::Sequence<OUString> aCals = pCalendar->getAllCalendars( aLocale );
927         auto pCal = std::find_if(aCals.begin(), aCals.end(),
928             [](const OUString& rCal) { return rCal != "gregorian"; });
929         if (pCal != aCals.end())
930             aCalendar = *pCal;
931     }
932     return aCalendar;
933 }
934 
lcl_IsInEmbedded(const SvXMLEmbeddedTextEntryArr & rEmbeddedEntries,sal_uInt16 nPos)935 static bool lcl_IsInEmbedded( const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries, sal_uInt16 nPos )
936 {
937     auto nCount = rEmbeddedEntries.size();
938     for (decltype(nCount) i=0; i<nCount; i++)
939         if ( rEmbeddedEntries[i].nSourcePos == nPos )
940             return true;
941 
942     return false;       // not found
943 }
944 
lcl_IsDefaultDateFormat(const SvNumberformat & rFormat,bool bSystemDate,NfIndexTableOffset eBuiltIn)945 static bool lcl_IsDefaultDateFormat( const SvNumberformat& rFormat, bool bSystemDate, NfIndexTableOffset eBuiltIn )
946 {
947     //  make an extra loop to collect date elements, to check if it is a default format
948     //  before adding the automatic-order attribute
949 
950     SvXMLDateElementAttributes eDateDOW = XML_DEA_NONE;
951     SvXMLDateElementAttributes eDateDay = XML_DEA_NONE;
952     SvXMLDateElementAttributes eDateMonth = XML_DEA_NONE;
953     SvXMLDateElementAttributes eDateYear = XML_DEA_NONE;
954     SvXMLDateElementAttributes eDateHours = XML_DEA_NONE;
955     SvXMLDateElementAttributes eDateMins = XML_DEA_NONE;
956     SvXMLDateElementAttributes eDateSecs = XML_DEA_NONE;
957     bool bDateNoDefault = false;
958 
959     sal_uInt16 nPos = 0;
960     bool bEnd = false;
961     short nLastType = 0;
962     while (!bEnd)
963     {
964         short nElemType = rFormat.GetNumForType( 0, nPos );
965         switch ( nElemType )
966         {
967             case 0:
968                 if ( nLastType == NF_SYMBOLTYPE_STRING )
969                     bDateNoDefault = true;  // text at the end -> no default date format
970                 bEnd = true;                // end of format reached
971                 break;
972             case NF_SYMBOLTYPE_STRING:
973             case NF_SYMBOLTYPE_DATESEP:
974             case NF_SYMBOLTYPE_TIMESEP:
975             case NF_SYMBOLTYPE_TIME100SECSEP:
976                 // text is ignored, except at the end
977                 break;
978             // same mapping as in SvXMLNumFormatContext::AddNfKeyword:
979             case NF_KEY_NN:     eDateDOW = XML_DEA_SHORT;       break;
980             case NF_KEY_NNN:
981             case NF_KEY_NNNN:   eDateDOW = XML_DEA_LONG;        break;
982             case NF_KEY_D:      eDateDay = XML_DEA_SHORT;       break;
983             case NF_KEY_DD:     eDateDay = XML_DEA_LONG;        break;
984             case NF_KEY_M:      eDateMonth = XML_DEA_SHORT;     break;
985             case NF_KEY_MM:     eDateMonth = XML_DEA_LONG;      break;
986             case NF_KEY_MMM:    eDateMonth = XML_DEA_TEXTSHORT; break;
987             case NF_KEY_MMMM:   eDateMonth = XML_DEA_TEXTLONG;  break;
988             case NF_KEY_YY:     eDateYear = XML_DEA_SHORT;      break;
989             case NF_KEY_YYYY:   eDateYear = XML_DEA_LONG;       break;
990             case NF_KEY_H:      eDateHours = XML_DEA_SHORT;     break;
991             case NF_KEY_HH:     eDateHours = XML_DEA_LONG;      break;
992             case NF_KEY_MI:     eDateMins = XML_DEA_SHORT;      break;
993             case NF_KEY_MMI:    eDateMins = XML_DEA_LONG;       break;
994             case NF_KEY_S:      eDateSecs = XML_DEA_SHORT;      break;
995             case NF_KEY_SS:     eDateSecs = XML_DEA_LONG;       break;
996             case NF_KEY_AP:
997             case NF_KEY_AMPM:   break;          // AM/PM may or may not be in date/time formats -> ignore by itself
998             default:
999                 bDateNoDefault = true;      // any other element -> no default format
1000         }
1001         nLastType = nElemType;
1002         ++nPos;
1003     }
1004 
1005     if ( bDateNoDefault )
1006         return false;                       // additional elements
1007     else
1008     {
1009         NfIndexTableOffset eFound = static_cast<NfIndexTableOffset>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
1010                 eDateDOW, eDateDay, eDateMonth, eDateYear, eDateHours, eDateMins, eDateSecs, bSystemDate ));
1011 
1012         return ( eFound == eBuiltIn );
1013     }
1014 }
1015 
1016 //  export one part (condition)
1017 
ExportPart_Impl(const SvNumberformat & rFormat,sal_uInt32 nKey,sal_uInt32 nRealKey,sal_uInt16 nPart,bool bDefPart)1018 void SvXMLNumFmtExport::ExportPart_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, sal_uInt32 nRealKey,
1019                                             sal_uInt16 nPart, bool bDefPart )
1020 {
1021     //! for the default part, pass the conditions from the other parts!
1022 
1023     //  element name
1024 
1025     NfIndexTableOffset eBuiltIn = pFormatter->GetIndexTableOffset( nRealKey );
1026 
1027     SvNumFormatType nFmtType = SvNumFormatType::ALL;
1028     bool bThousand = false;
1029     sal_uInt16 nPrecision = 0;
1030     sal_uInt16 nLeading = 0;
1031     rFormat.GetNumForInfo( nPart, nFmtType, bThousand, nPrecision, nLeading);
1032     nFmtType &= ~SvNumFormatType::DEFINED;
1033 
1034     //  special treatment of builtin formats that aren't detected by normal parsing
1035     //  (the same formats that get the type set in SvNumberFormatter::ImpGenerateFormats)
1036     if ( eBuiltIn == NF_NUMBER_STANDARD )
1037         nFmtType = SvNumFormatType::NUMBER;
1038     else if ( eBuiltIn == NF_BOOLEAN )
1039         nFmtType = SvNumFormatType::LOGICAL;
1040     else if ( eBuiltIn == NF_TEXT )
1041         nFmtType = SvNumFormatType::TEXT;
1042 
1043     // #101606# An empty subformat is a valid number-style resulting in an
1044     // empty display string for the condition of the subformat.
1045 
1046     XMLTokenEnum eType = XML_TOKEN_INVALID;
1047     switch ( nFmtType )
1048     {
1049         // Type UNDEFINED likely is a crappy format string for that we could
1050         // not decide on any format type (and maybe could try harder?), but the
1051         // resulting XMLTokenEnum should be something valid, so make that
1052         // number-style.
1053         case SvNumFormatType::UNDEFINED:
1054             SAL_WARN("xmloff.style","UNDEFINED number format: '" << rFormat.GetFormatstring() << "'");
1055             [[fallthrough]];
1056         // Type is 0 if a format contains no recognized elements
1057         // (like text only) - this is handled as a number-style.
1058         case SvNumFormatType::ALL:
1059         case SvNumFormatType::EMPTY:
1060         case SvNumFormatType::NUMBER:
1061         case SvNumFormatType::SCIENTIFIC:
1062         case SvNumFormatType::FRACTION:
1063             eType = XML_NUMBER_STYLE;
1064             break;
1065         case SvNumFormatType::PERCENT:
1066             eType = XML_PERCENTAGE_STYLE;
1067             break;
1068         case SvNumFormatType::CURRENCY:
1069             eType = XML_CURRENCY_STYLE;
1070             break;
1071         case SvNumFormatType::DATE:
1072         case SvNumFormatType::DATETIME:
1073             eType = XML_DATE_STYLE;
1074             break;
1075         case SvNumFormatType::TIME:
1076             eType = XML_TIME_STYLE;
1077             break;
1078         case SvNumFormatType::TEXT:
1079             eType = XML_TEXT_STYLE;
1080             break;
1081         case SvNumFormatType::LOGICAL:
1082             eType = XML_BOOLEAN_STYLE;
1083             break;
1084         default: break;
1085     }
1086     SAL_WARN_IF( eType == XML_TOKEN_INVALID, "xmloff.style", "unknown format type" );
1087 
1088     OUString sAttrValue;
1089     bool bUserDef( rFormat.GetType() & SvNumFormatType::DEFINED );
1090 
1091     //  common attributes for format
1092 
1093     //  format name (generated from key) - style namespace
1094     rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
1095                         lcl_CreateStyleName( nKey, nPart, bDefPart, sPrefix ) );
1096 
1097     //  "volatile" attribute for styles used only in maps
1098     if ( !bDefPart )
1099         rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_VOLATILE, XML_TRUE );
1100 
1101     //  language / country
1102     LanguageType nLang = rFormat.GetLanguage();
1103     AddLanguageAttr_Impl( nLang );                  // adds to pAttrList
1104 
1105     //  title (comment)
1106     //  titles for builtin formats are not written
1107     sAttrValue = rFormat.GetComment();
1108     if ( !sAttrValue.isEmpty() && bUserDef && bDefPart )
1109     {
1110         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TITLE, sAttrValue );
1111     }
1112 
1113     //  automatic ordering for currency and date formats
1114     //  only used for some built-in formats
1115     bool bAutoOrder = ( eBuiltIn == NF_CURRENCY_1000INT     || eBuiltIn == NF_CURRENCY_1000DEC2 ||
1116                         eBuiltIn == NF_CURRENCY_1000INT_RED || eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
1117                         eBuiltIn == NF_CURRENCY_1000DEC2_DASHED ||
1118                         eBuiltIn == NF_DATE_SYSTEM_SHORT    || eBuiltIn == NF_DATE_SYSTEM_LONG ||
1119                         eBuiltIn == NF_DATE_SYS_MMYY        || eBuiltIn == NF_DATE_SYS_DDMMM ||
1120                         eBuiltIn == NF_DATE_SYS_DDMMYYYY    || eBuiltIn == NF_DATE_SYS_DDMMYY ||
1121                         eBuiltIn == NF_DATE_SYS_DMMMYY      || eBuiltIn == NF_DATE_SYS_DMMMYYYY ||
1122                         eBuiltIn == NF_DATE_SYS_DMMMMYYYY   || eBuiltIn == NF_DATE_SYS_NNDMMMYY ||
1123                         eBuiltIn == NF_DATE_SYS_NNDMMMMYYYY || eBuiltIn == NF_DATE_SYS_NNNNDMMMMYYYY ||
1124                         eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM || eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMMSS );
1125 
1126     //  format source (for date and time formats)
1127     //  only used for some built-in formats
1128     bool bSystemDate = ( eBuiltIn == NF_DATE_SYSTEM_SHORT ||
1129                          eBuiltIn == NF_DATE_SYSTEM_LONG  ||
1130                          eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM );
1131     bool bLongSysDate = ( eBuiltIn == NF_DATE_SYSTEM_LONG );
1132 
1133     // check if the format definition matches the key
1134     if ( bAutoOrder && ( nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) &&
1135             !lcl_IsDefaultDateFormat( rFormat, bSystemDate, eBuiltIn ) )
1136     {
1137         bAutoOrder = bSystemDate = bLongSysDate = false;        // don't write automatic-order attribute then
1138     }
1139 
1140     if ( bAutoOrder &&
1141         ( nFmtType == SvNumFormatType::CURRENCY || nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) )
1142     {
1143         //  #85109# format type must be checked to avoid dtd errors if
1144         //  locale data contains other format types at the built-in positions
1145 
1146         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_AUTOMATIC_ORDER,
1147                               XML_TRUE );
1148     }
1149 
1150     if ( bSystemDate && bAutoOrder &&
1151         ( nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) )
1152     {
1153         //  #85109# format type must be checked to avoid dtd errors if
1154         //  locale data contains other format types at the built-in positions
1155 
1156         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_FORMAT_SOURCE,
1157                               XML_LANGUAGE );
1158     }
1159 
1160     //  overflow for time formats as in [hh]:mm
1161     //  controlled by bThousand from number format info
1162     //  default for truncate-on-overflow is true
1163     if ( nFmtType == SvNumFormatType::TIME && bThousand )
1164     {
1165         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRUNCATE_ON_OVERFLOW,
1166                               XML_FALSE );
1167     }
1168 
1169     // Native number transliteration
1170     css::i18n::NativeNumberXmlAttributes2 aAttr;
1171     rFormat.GetNatNumXml( aAttr, nPart );
1172     if ( !aAttr.Format.isEmpty() )
1173     {
1174         assert(aAttr.Spellout.isEmpty());   // mutually exclusive
1175 
1176         /* FIXME-BCP47: ODF defines no transliteration-script or
1177          * transliteration-rfc-language-tag */
1178         LanguageTag aLanguageTag( aAttr.Locale);
1179         OUString aLanguage, aScript, aCountry;
1180         aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
1181         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_FORMAT,
1182                               aAttr.Format );
1183         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
1184                               aLanguage );
1185         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1186                               aCountry );
1187         rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_STYLE,
1188                               aAttr.Style );
1189     }
1190 
1191     if ( !aAttr.Spellout.isEmpty() )
1192     {
1193         const bool bWriteSpellout = aAttr.Format.isEmpty();
1194         assert(bWriteSpellout);     // mutually exclusive
1195 
1196         // Export only for 1.2 with extensions or 1.3 and later.
1197         SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
1198         // Also ensure that duplicated transliteration-language and
1199         // transliteration-country attributes never escape into the wild with
1200         // releases.
1201         if (eVersion > SvtSaveOptions::ODFSVER_012 && bWriteSpellout)
1202         {
1203             /* FIXME-BCP47: ODF defines no transliteration-script or
1204              * transliteration-rfc-language-tag */
1205             LanguageTag aLanguageTag( aAttr.Locale);
1206             OUString aLanguage, aScript, aCountry;
1207             aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
1208             // For 1.2+ use loext namespace, for 1.3 use number namespace.
1209             rExport.AddAttribute( ((eVersion < SvtSaveOptions::ODFSVER_013) ?
1210                         XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
1211                     XML_TRANSLITERATION_SPELLOUT, aAttr.Spellout );
1212             rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
1213                                   aLanguage );
1214             rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1215                                   aCountry );
1216         }
1217     }
1218 
1219     // The element
1220     SvXMLElementExport aElem( rExport, XML_NAMESPACE_NUMBER, eType,
1221                               true, true );
1222 
1223     //  color (properties element)
1224 
1225     const Color* pCol = rFormat.GetColor( nPart );
1226     if (pCol)
1227         WriteColorElement_Impl(*pCol);
1228 
1229     //  detect if there is "real" content, excluding color and maps
1230     //! move to implementation of Write... methods?
1231     bool bAnyContent = false;
1232 
1233     //  format elements
1234 
1235     SvXMLEmbeddedTextEntryArr aEmbeddedEntries;
1236     if ( eBuiltIn == NF_NUMBER_STANDARD )
1237     {
1238         //  default number format contains just one number element
1239         WriteNumberElement_Impl( -1, -1, 1, OUString(), false, 0, aEmbeddedEntries );
1240         bAnyContent = true;
1241     }
1242     else if ( eBuiltIn == NF_BOOLEAN )
1243     {
1244         //  boolean format contains just one boolean element
1245         WriteBooleanElement_Impl();
1246         bAnyContent = true;
1247     }
1248     else
1249     {
1250         //  first loop to collect attributes
1251 
1252         bool bDecDashes  = false;
1253         bool bExpFound   = false;
1254         bool bCurrFound  = false;
1255         bool bInInteger  = true;
1256         bool bExpSign = true;
1257         bool bDecAlign   = false;               // decimal alignment with "?"
1258         sal_Int32 nExpDigits = 0;
1259         sal_Int32 nIntegerSymbols = 0;          // for embedded-text, including "#"
1260         sal_Int32 nTrailingThousands = 0;       // thousands-separators after all digits
1261         sal_Int32 nMinDecimals = nPrecision;
1262         OUString sCurrExt;
1263         OUString aCalendar;
1264         sal_uInt16 nPos = 0;
1265         bool bEnd = false;
1266         while (!bEnd)
1267         {
1268             short nElemType = rFormat.GetNumForType( nPart, nPos );
1269             const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1270 
1271             switch ( nElemType )
1272             {
1273                 case 0:
1274                     bEnd = true;                // end of format reached
1275                     break;
1276                 case NF_SYMBOLTYPE_DIGIT:
1277                     if ( bExpFound && pElemStr )
1278                         nExpDigits += pElemStr->getLength();
1279                     else if ( !bDecDashes && pElemStr && (*pElemStr)[0] == '-' )
1280                     {
1281                         bDecDashes = true;
1282                         nMinDecimals = 0;
1283                     }
1284                     else if ( !bInInteger && pElemStr )
1285                     {
1286                         for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
1287                         {
1288                             sal_Unicode aChar = (*pElemStr)[i];
1289                             if ( aChar == '#' || aChar == '?' )
1290                             {
1291                                 nMinDecimals --;
1292                                 if ( aChar == '?' )
1293                                     bDecAlign = true;
1294                             }
1295                             else
1296                                 break;
1297                         }
1298                     }
1299                     if ( bInInteger && pElemStr )
1300                         nIntegerSymbols += pElemStr->getLength();
1301                     nTrailingThousands = 0;
1302                     break;
1303                 case NF_SYMBOLTYPE_DECSEP:
1304                     bInInteger = false;
1305                     break;
1306                 case NF_SYMBOLTYPE_THSEP:
1307                     if (pElemStr)
1308                         nTrailingThousands += pElemStr->getLength();      // is reset to 0 if digits follow
1309                     break;
1310                 case NF_SYMBOLTYPE_EXP:
1311                     bExpFound = true;           // following digits are exponent digits
1312                     bInInteger = false;
1313                     if ( pElemStr && ( pElemStr->getLength() == 1
1314                                   || ( pElemStr->getLength() == 2 && (*pElemStr)[1] == '-' ) ) )
1315                         bExpSign = false;       // for 0.00E0 or 0.00E-00
1316                     break;
1317                 case NF_SYMBOLTYPE_CURRENCY:
1318                     bCurrFound = true;
1319                     break;
1320                 case NF_SYMBOLTYPE_CURREXT:
1321                     if (pElemStr)
1322                         sCurrExt = *pElemStr;
1323                     break;
1324 
1325                 // E, EE, R, RR: select non-gregorian calendar
1326                 // AAA, AAAA: calendar is switched at the position of the element
1327                 case NF_KEY_EC:
1328                 case NF_KEY_EEC:
1329                 case NF_KEY_R:
1330                 case NF_KEY_RR:
1331                     if (aCalendar.isEmpty())
1332                         aCalendar = lcl_GetDefaultCalendar( pFormatter, nLang );
1333                     break;
1334             }
1335             ++nPos;
1336         }
1337 
1338         //  collect strings for embedded-text (must be known before number element is written)
1339 
1340         bool bAllowEmbedded = ( nFmtType == SvNumFormatType::ALL || nFmtType == SvNumFormatType::NUMBER ||
1341                                         nFmtType == SvNumFormatType::CURRENCY ||
1342                                         nFmtType == SvNumFormatType::PERCENT );
1343         if ( bAllowEmbedded )
1344         {
1345             sal_Int32 nDigitsPassed = 0;
1346             nPos = 0;
1347             bEnd = false;
1348             while (!bEnd)
1349             {
1350                 short nElemType = rFormat.GetNumForType( nPart, nPos );
1351                 const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1352 
1353                 switch ( nElemType )
1354                 {
1355                     case 0:
1356                         bEnd = true;                // end of format reached
1357                         break;
1358                     case NF_SYMBOLTYPE_DIGIT:
1359                         if ( pElemStr )
1360                             nDigitsPassed += pElemStr->getLength();
1361                         break;
1362                     case NF_SYMBOLTYPE_STRING:
1363                     case NF_SYMBOLTYPE_BLANK:
1364                     case NF_SYMBOLTYPE_PERCENT:
1365                         if ( nDigitsPassed > 0 && nDigitsPassed < nIntegerSymbols && pElemStr )
1366                         {
1367                             //  text (literal or underscore) within the integer part of a number:number element
1368 
1369                             OUString aEmbeddedStr;
1370                             if ( nElemType == NF_SYMBOLTYPE_STRING || nElemType == NF_SYMBOLTYPE_PERCENT )
1371                             {
1372                                 aEmbeddedStr = *pElemStr;
1373                             }
1374                             else if (pElemStr->getLength() >= 2)
1375                             {
1376                                 SvNumberformat::InsertBlanks( aEmbeddedStr, 0, (*pElemStr)[1] );
1377                             }
1378                             sal_Int32 nEmbedPos = nIntegerSymbols - nDigitsPassed;
1379 
1380                             aEmbeddedEntries.push_back(
1381                                 SvXMLEmbeddedTextEntry(nPos, nEmbedPos, aEmbeddedStr));
1382                         }
1383                         break;
1384                 }
1385                 ++nPos;
1386             }
1387         }
1388 
1389         //  final loop to write elements
1390 
1391         bool bNumWritten = false;
1392         bool bCurrencyWritten = false;
1393         short nPrevType = 0;
1394         nPos = 0;
1395         bEnd = false;
1396         while (!bEnd)
1397         {
1398             short nElemType = rFormat.GetNumForType( nPart, nPos );
1399             const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1400 
1401             switch ( nElemType )
1402             {
1403                 case 0:
1404                     bEnd = true;                // end of format reached
1405                     break;
1406                 case NF_SYMBOLTYPE_STRING:
1407                 case NF_SYMBOLTYPE_DATESEP:
1408                 case NF_SYMBOLTYPE_TIMESEP:
1409                 case NF_SYMBOLTYPE_TIME100SECSEP:
1410                 case NF_SYMBOLTYPE_PERCENT:
1411                     if (pElemStr)
1412                     {
1413                         if ( ( nPrevType == NF_KEY_S || nPrevType == NF_KEY_SS ) &&
1414                              ( nElemType == NF_SYMBOLTYPE_TIME100SECSEP ) &&
1415                              nPrecision > 0 )
1416                         {
1417                             //  decimal separator after seconds is implied by
1418                             //  "decimal-places" attribute and must not be written
1419                             //  as text element
1420                             //! difference between '.' and ',' is lost here
1421                         }
1422                         else if ( lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
1423                         {
1424                             //  text is written as embedded-text child of the number,
1425                             //  don't create a text element
1426                         }
1427                         else if ( nFmtType == SvNumFormatType::CURRENCY && !bCurrFound && !bCurrencyWritten )
1428                         {
1429                             //  automatic currency symbol is implemented as part of
1430                             //  normal text -> search for the symbol
1431                             bCurrencyWritten = WriteTextWithCurrency_Impl( *pElemStr,
1432                                 LanguageTag::convertToLocale( nLang ) );
1433                             bAnyContent = true;
1434                         }
1435                         else
1436                             AddToTextElement_Impl( *pElemStr );
1437                     }
1438                     break;
1439                 case NF_SYMBOLTYPE_BLANK:
1440                     if ( pElemStr && !lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
1441                     {
1442                         //  turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat
1443                         //  (#i20396# the spaces may also be in embedded-text elements)
1444 
1445                         OUString aBlanks;
1446                         if (pElemStr->getLength() >= 2)
1447                             SvNumberformat::InsertBlanks( aBlanks, 0, (*pElemStr)[1] );
1448                         AddToTextElement_Impl( aBlanks );
1449                     }
1450                     break;
1451                 case NF_KEY_GENERAL :
1452                         WriteNumberElement_Impl( -1, -1, 1, OUString(), false, 0, aEmbeddedEntries );
1453                     break;
1454                 case NF_KEY_CCC:
1455                     if (pElemStr)
1456                     {
1457                         if ( bCurrencyWritten )
1458                             AddToTextElement_Impl( *pElemStr );     // never more than one currency element
1459                         else
1460                         {
1461                             //! must be different from short automatic format
1462                             //! but should still be empty (meaning automatic)
1463                             //  pElemStr is "CCC"
1464 
1465                             WriteCurrencyElement_Impl( *pElemStr, OUString() );
1466                             bAnyContent = true;
1467                             bCurrencyWritten = true;
1468                         }
1469                     }
1470                     break;
1471                 case NF_SYMBOLTYPE_CURRENCY:
1472                     if (pElemStr)
1473                     {
1474                         if ( bCurrencyWritten )
1475                             AddToTextElement_Impl( *pElemStr );     // never more than one currency element
1476                         else
1477                         {
1478                             WriteCurrencyElement_Impl( *pElemStr, sCurrExt );
1479                             bAnyContent = true;
1480                             bCurrencyWritten = true;
1481                         }
1482                     }
1483                     break;
1484                 case NF_SYMBOLTYPE_DIGIT:
1485                     if (!bNumWritten)           // write number part
1486                     {
1487                         switch ( nFmtType )
1488                         {
1489                             // for type 0 (not recognized as a special type),
1490                             // write a "normal" number
1491                             case SvNumFormatType::ALL:
1492                             case SvNumFormatType::NUMBER:
1493                             case SvNumFormatType::CURRENCY:
1494                             case SvNumFormatType::PERCENT:
1495                                 {
1496                                     //  decimals
1497                                     //  only some built-in formats have automatic decimals
1498                                     sal_Int32 nDecimals = nPrecision;   // from GetFormatSpecialInfo
1499                                     if ( eBuiltIn == NF_NUMBER_STANDARD ||
1500                                          eBuiltIn == NF_CURRENCY_1000DEC2 ||
1501                                          eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
1502                                          eBuiltIn == NF_CURRENCY_1000DEC2_CCC ||
1503                                          eBuiltIn == NF_CURRENCY_1000DEC2_DASHED )
1504                                         nDecimals = -1;
1505 
1506                                     //  integer digits
1507                                     //  only one built-in format has automatic integer digits
1508                                     sal_Int32 nInteger = nLeading;
1509                                     if ( eBuiltIn == NF_NUMBER_SYSTEM )
1510                                         nInteger = -1;
1511 
1512                                     //  string for decimal replacement
1513                                     //  has to be taken from nPrecision
1514                                     //  (positive number even for automatic decimals)
1515                                     OUStringBuffer sDashStr;
1516                                     if (bDecDashes && nPrecision > 0)
1517                                         comphelper::string::padToLength(sDashStr, nPrecision, '-');
1518                                     // "?" in decimal part are replaced by space character
1519                                     if (bDecAlign && nPrecision > 0)
1520                                         sDashStr = " ";
1521 
1522                                     WriteNumberElement_Impl(nDecimals, nMinDecimals, nInteger, sDashStr.makeStringAndClear(),
1523                                         bThousand, nTrailingThousands, aEmbeddedEntries);
1524                                     bAnyContent = true;
1525                                 }
1526                                 break;
1527                             case SvNumFormatType::SCIENTIFIC:
1528                                 // #i43959# for scientific numbers, count all integer symbols ("0" and "#")
1529                                 // as integer digits: use nIntegerSymbols instead of nLeading
1530                                 // nIntegerSymbols represents exponent interval (for engineering notation)
1531                                 WriteScientificElement_Impl( nPrecision, nMinDecimals, nLeading, bThousand, nExpDigits, nIntegerSymbols, bExpSign );
1532                                 bAnyContent = true;
1533                                 break;
1534                             case SvNumFormatType::FRACTION:
1535                                 {
1536                                     sal_Int32 nInteger = nLeading;
1537                                     if ( rFormat.GetNumForNumberElementCount( nPart ) == 3 )
1538                                     {
1539                                         //  If there is only two numbers + fraction in format string
1540                                         //  the fraction doesn't have an integer part, and no
1541                                         //  min-integer-digits attribute must be written.
1542                                         nInteger = -1;
1543                                     }
1544                                     WriteFractionElement_Impl( nInteger, bThousand,  rFormat, nPart );
1545                                     bAnyContent = true;
1546                                 }
1547                                 break;
1548                             default: break;
1549                         }
1550 
1551                         bNumWritten = true;
1552                     }
1553                     break;
1554                 case NF_SYMBOLTYPE_DECSEP:
1555                     if ( pElemStr && nPrecision == 0 )
1556                     {
1557                         //  A decimal separator after the number, without following decimal digits,
1558                         //  isn't modelled as part of the number element, so it's written as text
1559                         //  (the distinction between a quoted and non-quoted, locale-dependent
1560                         //  character is lost here).
1561 
1562                         AddToTextElement_Impl( *pElemStr );
1563                     }
1564                     break;
1565                 case NF_SYMBOLTYPE_DEL:
1566                     if ( pElemStr && *pElemStr == "@" )
1567                     {
1568                         WriteTextContentElement_Impl();
1569                         bAnyContent = true;
1570                     }
1571                     break;
1572 
1573                 case NF_SYMBOLTYPE_CALENDAR:
1574                     if ( pElemStr )
1575                         aCalendar = *pElemStr;
1576                     break;
1577 
1578                 // date elements:
1579 
1580                 case NF_KEY_D:
1581                 case NF_KEY_DD:
1582                     {
1583                         bool bLong = ( nElemType == NF_KEY_DD );
1584                         WriteDayElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1585                         bAnyContent = true;
1586                     }
1587                     break;
1588                 case NF_KEY_DDD:
1589                 case NF_KEY_DDDD:
1590                 case NF_KEY_NN:
1591                 case NF_KEY_NNN:
1592                 case NF_KEY_NNNN:
1593                 case NF_KEY_AAA:
1594                 case NF_KEY_AAAA:
1595                     {
1596                         OUString aCalAttr = aCalendar;
1597                         if ( nElemType == NF_KEY_AAA || nElemType == NF_KEY_AAAA )
1598                         {
1599                             //  calendar attribute for AAA and AAAA is switched only for this element
1600                             if (aCalAttr.isEmpty())
1601                                 aCalAttr = lcl_GetDefaultCalendar( pFormatter, nLang );
1602                         }
1603 
1604                         bool bLong = ( nElemType == NF_KEY_NNN || nElemType == NF_KEY_NNNN ||
1605                                            nElemType == NF_KEY_DDDD || nElemType == NF_KEY_AAAA );
1606                         WriteDayOfWeekElement_Impl( aCalAttr, ( bSystemDate ? bLongSysDate : bLong ) );
1607                         bAnyContent = true;
1608                         if ( nElemType == NF_KEY_NNNN )
1609                         {
1610                             //  write additional text element for separator
1611                             pLocaleData->setLanguageTag( LanguageTag( nLang ) );
1612                             AddToTextElement_Impl( pLocaleData->getLongDateDayOfWeekSep() );
1613                         }
1614                     }
1615                     break;
1616                 case NF_KEY_M:
1617                 case NF_KEY_MM:
1618                 case NF_KEY_MMM:
1619                 case NF_KEY_MMMM:
1620                 case NF_KEY_MMMMM:      //! first letter of month name, no attribute available
1621                     {
1622                         bool bLong = ( nElemType == NF_KEY_MM  || nElemType == NF_KEY_MMMM );
1623                         bool bText = ( nElemType == NF_KEY_MMM || nElemType == NF_KEY_MMMM ||
1624                                             nElemType == NF_KEY_MMMMM );
1625                         WriteMonthElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ), bText );
1626                         bAnyContent = true;
1627                     }
1628                     break;
1629                 case NF_KEY_YY:
1630                 case NF_KEY_YYYY:
1631                 case NF_KEY_EC:
1632                 case NF_KEY_EEC:
1633                 case NF_KEY_R:      //! R acts as EE, no attribute available
1634                     {
1635                         //! distinguish EE and R
1636                         //  calendar attribute for E and EE and R is set in first loop
1637                         bool bLong = ( nElemType == NF_KEY_YYYY || nElemType == NF_KEY_EEC ||
1638                                             nElemType == NF_KEY_R );
1639                         WriteYearElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1640                         bAnyContent = true;
1641                     }
1642                     break;
1643                 case NF_KEY_G:
1644                 case NF_KEY_GG:
1645                 case NF_KEY_GGG:
1646                 case NF_KEY_RR:     //! RR acts as GGGEE, no attribute available
1647                     {
1648                         //! distinguish GG and GGG and RR
1649                         bool bLong = ( nElemType == NF_KEY_GGG || nElemType == NF_KEY_RR );
1650                         WriteEraElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1651                         bAnyContent = true;
1652                         if ( nElemType == NF_KEY_RR )
1653                         {
1654                             //  calendar attribute for RR is set in first loop
1655                             WriteYearElement_Impl( aCalendar, ( bSystemDate || bLongSysDate ) );
1656                         }
1657                     }
1658                     break;
1659                 case NF_KEY_Q:
1660                 case NF_KEY_QQ:
1661                     {
1662                         bool bLong = ( nElemType == NF_KEY_QQ );
1663                         WriteQuarterElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1664                         bAnyContent = true;
1665                     }
1666                     break;
1667                 case NF_KEY_WW:
1668                     WriteWeekElement_Impl( aCalendar );
1669                     bAnyContent = true;
1670                     break;
1671 
1672                 // time elements (bSystemDate is not used):
1673 
1674                 case NF_KEY_H:
1675                 case NF_KEY_HH:
1676                     WriteHoursElement_Impl( nElemType == NF_KEY_HH );
1677                     bAnyContent = true;
1678                     break;
1679                 case NF_KEY_MI:
1680                 case NF_KEY_MMI:
1681                     WriteMinutesElement_Impl( nElemType == NF_KEY_MMI );
1682                     bAnyContent = true;
1683                     break;
1684                 case NF_KEY_S:
1685                 case NF_KEY_SS:
1686                     WriteSecondsElement_Impl( ( nElemType == NF_KEY_SS ), nPrecision );
1687                     bAnyContent = true;
1688                     break;
1689                 case NF_KEY_AMPM:
1690                 case NF_KEY_AP:
1691                     WriteAMPMElement_Impl();        // short/long?
1692                     bAnyContent = true;
1693                     break;
1694                 case NF_SYMBOLTYPE_STAR :
1695                     // export only if ODF 1.2 extensions are enabled
1696                     if( rExport.getDefaultVersion() > SvtSaveOptions::ODFVER_012 )
1697                     {
1698                         if ( pElemStr && pElemStr->getLength() > 1 )
1699                             WriteRepeatedElement_Impl( (*pElemStr)[1] );
1700                     }
1701                     break;
1702             }
1703             nPrevType = nElemType;
1704             ++nPos;
1705         }
1706     }
1707 
1708     if ( !sTextContent.isEmpty() )
1709         bAnyContent = true;     // element written in FinishTextElement_Impl
1710 
1711     FinishTextElement_Impl();       // final text element - before maps
1712 
1713     if ( !bAnyContent )
1714     {
1715         //  for an empty format, write an empty text element
1716         SvXMLElementExport aTElem( rExport, XML_NAMESPACE_NUMBER, XML_TEXT,
1717                                    true, false );
1718     }
1719 
1720     //  mapping (conditions) must be last elements
1721 
1722     if (bDefPart)
1723     {
1724         SvNumberformatLimitOps eOp1, eOp2;
1725         double fLimit1, fLimit2;
1726         rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
1727 
1728         WriteMapElement_Impl( eOp1, fLimit1, nKey, 0 );
1729         WriteMapElement_Impl( eOp2, fLimit2, nKey, 1 );
1730 
1731         if ( rFormat.HasTextFormat() )
1732         {
1733             //  4th part is for text -> make an "all other numbers" condition for the 3rd part
1734             //  by reversing the 2nd condition.
1735             //  For a trailing text format like  0;@  that has no conditions
1736             //  use a "less or equal than biggest" condition for the number
1737             //  part, ODF can't store subformats (style maps) without
1738             //  conditions.
1739 
1740             SvNumberformatLimitOps eOp3 = NUMBERFORMAT_OP_NO;
1741             double fLimit3 = fLimit2;
1742             sal_uInt16 nLastPart = 2;
1743             SvNumberformatLimitOps eOpLast = eOp2;
1744             if (eOp2 == NUMBERFORMAT_OP_NO)
1745             {
1746                 eOpLast = eOp1;
1747                 fLimit3 = fLimit1;
1748                 nLastPart = (eOp1 == NUMBERFORMAT_OP_NO) ? 0 : 1;
1749             }
1750             switch ( eOpLast )
1751             {
1752                 case NUMBERFORMAT_OP_EQ: eOp3 = NUMBERFORMAT_OP_NE; break;
1753                 case NUMBERFORMAT_OP_NE: eOp3 = NUMBERFORMAT_OP_EQ; break;
1754                 case NUMBERFORMAT_OP_LT: eOp3 = NUMBERFORMAT_OP_GE; break;
1755                 case NUMBERFORMAT_OP_LE: eOp3 = NUMBERFORMAT_OP_GT; break;
1756                 case NUMBERFORMAT_OP_GT: eOp3 = NUMBERFORMAT_OP_LE; break;
1757                 case NUMBERFORMAT_OP_GE: eOp3 = NUMBERFORMAT_OP_LT; break;
1758                 case NUMBERFORMAT_OP_NO: eOp3 = NUMBERFORMAT_OP_LE; fLimit3 = DBL_MAX; break;
1759             }
1760 
1761             if ( fLimit1 == fLimit2 &&
1762                     ( ( eOp1 == NUMBERFORMAT_OP_LT && eOp2 == NUMBERFORMAT_OP_GT ) ||
1763                       ( eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_LT ) ) )
1764             {
1765                 //  For <x and >x, add =x as last condition
1766                 //  (just for readability, <=x would be valid, too)
1767 
1768                 eOp3 = NUMBERFORMAT_OP_EQ;
1769             }
1770 
1771             WriteMapElement_Impl( eOp3, fLimit3, nKey, nLastPart );
1772         }
1773     }
1774 }
1775 
1776 //  export one format
1777 
ExportFormat_Impl(const SvNumberformat & rFormat,sal_uInt32 nKey,sal_uInt32 nRealKey)1778 void SvXMLNumFmtExport::ExportFormat_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, sal_uInt32 nRealKey )
1779 {
1780     const sal_uInt16 XMLNUM_MAX_PARTS = 4;
1781     bool bParts[XMLNUM_MAX_PARTS] = { false, false, false, false };
1782     sal_uInt16 nUsedParts = 0;
1783     for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
1784     {
1785         if (rFormat.GetNumForInfoScannedType( nPart) != SvNumFormatType::UNDEFINED)
1786         {
1787             bParts[nPart] = true;
1788             nUsedParts = nPart + 1;
1789         }
1790     }
1791 
1792     SvNumberformatLimitOps eOp1, eOp2;
1793     double fLimit1, fLimit2;
1794     rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
1795 
1796     //  if conditions are set, even empty formats must be written
1797 
1798     if ( eOp1 != NUMBERFORMAT_OP_NO )
1799     {
1800         bParts[1] = true;
1801         if (nUsedParts < 2)
1802             nUsedParts = 2;
1803     }
1804     if ( eOp2 != NUMBERFORMAT_OP_NO )
1805     {
1806         bParts[2] = true;
1807         if (nUsedParts < 3)
1808             nUsedParts = 3;
1809     }
1810     if ( rFormat.HasTextFormat() )
1811     {
1812         bParts[3] = true;
1813         if (nUsedParts < 4)
1814             nUsedParts = 4;
1815     }
1816 
1817     for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
1818     {
1819         if (bParts[nPart])
1820         {
1821             bool bDefault = ( nPart+1 == nUsedParts );          // last = default
1822             ExportPart_Impl( rFormat, nKey, nRealKey, nPart, bDefault );
1823         }
1824     }
1825 }
1826 
1827 //  export method called by application
1828 
Export(bool bIsAutoStyle)1829 void SvXMLNumFmtExport::Export( bool bIsAutoStyle )
1830 {
1831     if ( !pFormatter )
1832         return;                         // no formatter -> no entries
1833 
1834     sal_uInt32 nKey;
1835     const SvNumberformat* pFormat = nullptr;
1836     bool bNext(pUsedList->GetFirstUsed(nKey));
1837     while(bNext)
1838     {
1839         // ODF has its notation of system formats, so obtain the "real" already
1840         // substituted format but use the original key for style name.
1841         sal_uInt32 nRealKey = nKey;
1842         pFormat = pFormatter->GetSubstitutedEntry( nKey, nRealKey);
1843         if(pFormat)
1844             ExportFormat_Impl( *pFormat, nKey, nRealKey );
1845         bNext = pUsedList->GetNextUsed(nKey);
1846     }
1847     if (!bIsAutoStyle)
1848     {
1849         std::vector<LanguageType> aLanguages;
1850         pFormatter->GetUsedLanguages( aLanguages );
1851         for (const auto& nLang : aLanguages)
1852         {
1853             sal_uInt32 nDefaultIndex = 0;
1854             SvNumberFormatTable& rTable = pFormatter->GetEntryTable(
1855                                          SvNumFormatType::DEFINED, nDefaultIndex, nLang );
1856             for (const auto& rTableEntry : rTable)
1857             {
1858                 nKey = rTableEntry.first;
1859                 pFormat = rTableEntry.second;
1860                 if (!pUsedList->IsUsed(nKey))
1861                 {
1862                     DBG_ASSERT((pFormat->GetType() & SvNumFormatType::DEFINED), "a not user defined numberformat found");
1863                     sal_uInt32 nRealKey = nKey;
1864                     if (pFormat->IsSubstituted())
1865                     {
1866                         pFormat = pFormatter->GetSubstitutedEntry( nKey, nRealKey); // export the "real" format
1867                         assert(pFormat);
1868                     }
1869                     //  user-defined and used formats are exported
1870                     ExportFormat_Impl( *pFormat, nKey, nRealKey );
1871                     // if it is a user-defined Format it will be added else nothing will happen
1872                     pUsedList->SetUsed(nKey);
1873                 }
1874             }
1875         }
1876     }
1877     pUsedList->Export();
1878 }
1879 
GetStyleName(sal_uInt32 nKey)1880 OUString SvXMLNumFmtExport::GetStyleName( sal_uInt32 nKey )
1881 {
1882     if(pUsedList->IsUsed(nKey) || pUsedList->IsWasUsed(nKey))
1883         return lcl_CreateStyleName( nKey, 0, true, sPrefix );
1884     else
1885     {
1886         OSL_FAIL("There is no written Data-Style");
1887         return OUString();
1888     }
1889 }
1890 
SetUsed(sal_uInt32 nKey)1891 void SvXMLNumFmtExport::SetUsed( sal_uInt32 nKey )
1892 {
1893     SAL_WARN_IF( pFormatter == nullptr, "xmloff.style", "missing formatter" );
1894     if( !pFormatter )
1895         return;
1896 
1897     if (pFormatter->GetEntry(nKey))
1898         pUsedList->SetUsed( nKey );
1899     else {
1900         OSL_FAIL("no existing Numberformat found with this key");
1901     }
1902 }
1903 
GetWasUsed() const1904 uno::Sequence<sal_Int32> SvXMLNumFmtExport::GetWasUsed() const
1905 {
1906     if (pUsedList)
1907         return pUsedList->GetWasUsed();
1908     return uno::Sequence<sal_Int32>();
1909 }
1910 
SetWasUsed(const uno::Sequence<sal_Int32> & rWasUsed)1911 void SvXMLNumFmtExport::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
1912 {
1913     if (pUsedList)
1914         pUsedList->SetWasUsed(rWasUsed);
1915 }
1916 
lcl_GetFormat(SvNumberFormatter const * pFormatter,sal_uInt32 nKey)1917 static const SvNumberformat* lcl_GetFormat( SvNumberFormatter const * pFormatter,
1918                            sal_uInt32 nKey )
1919 {
1920     return ( pFormatter != nullptr ) ? pFormatter->GetEntry( nKey ) : nullptr;
1921 }
1922 
ForceSystemLanguage(sal_uInt32 nKey)1923 sal_uInt32 SvXMLNumFmtExport::ForceSystemLanguage( sal_uInt32 nKey )
1924 {
1925     sal_uInt32 nRet = nKey;
1926 
1927     const SvNumberformat* pFormat = lcl_GetFormat( pFormatter, nKey );
1928     if( pFormat != nullptr )
1929     {
1930         SAL_WARN_IF( pFormatter == nullptr, "xmloff.style", "format without formatter?" );
1931 
1932         sal_Int32 nErrorPos;
1933         SvNumFormatType nType = pFormat->GetType();
1934 
1935         sal_uInt32 nNewKey = pFormatter->GetFormatForLanguageIfBuiltIn(
1936                        nKey, LANGUAGE_SYSTEM );
1937 
1938         if( nNewKey != nKey )
1939         {
1940             nRet = nNewKey;
1941         }
1942         else
1943         {
1944             OUString aFormatString( pFormat->GetFormatstring() );
1945             pFormatter->PutandConvertEntry(
1946                             aFormatString,
1947                             nErrorPos, nType, nNewKey,
1948                             pFormat->GetLanguage(), LANGUAGE_SYSTEM, true);
1949 
1950             // success? Then use new key.
1951             if( nErrorPos == 0 )
1952                 nRet = nNewKey;
1953         }
1954     }
1955 
1956     return nRet;
1957 }
1958 
1959 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1960