xref: /core/svl/source/numbers/zformat.cxx (revision bd2ffb6f)
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 <string_view>
21 
22 #include <o3tl/sprintf.hxx>
23 #include <o3tl/string_view.hxx>
24 #include <comphelper/string.hxx>
25 #include <sal/log.hxx>
26 #include <tools/debug.hxx>
27 #include <tools/long.hxx>
28 #include <i18nlangtag/mslangid.hxx>
29 #include <rtl/math.hxx>
30 #include <unotools/charclass.hxx>
31 #include <unotools/calendarwrapper.hxx>
32 #include <unotools/nativenumberwrapper.hxx>
33 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
34 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
35 #include <com/sun/star/i18n/CalendarDisplayCode.hpp>
36 #include <com/sun/star/i18n/AmPmValue.hpp>
37 #include <com/sun/star/i18n/NativeNumberMode.hpp>
38 #include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>
39 
40 #include <svl/zformat.hxx>
41 #include "zforscan.hxx"
42 
43 #include "zforfind.hxx"
44 #include <svl/zforlist.hxx>
45 #include <unotools/digitgroupingiterator.hxx>
46 #include <svl/nfsymbol.hxx>
47 
48 #include <cmath>
49 #include <array>
50 
51 using namespace svt;
52 
53 namespace {
54 
55 constexpr OUString GREGORIAN = u"gregorian"_ustr;
56 
57 const sal_uInt16 UPPER_PRECISION = 300; // entirely arbitrary...
58 const double EXP_LOWER_BOUND = 1.0E-4; // prefer scientific notation below this value.
59 const double EXP_ABS_UPPER_BOUND = 1.0E15;  // use exponential notation above that absolute value.
60                                             // Back in time was E16 that lead
61                                             // to display rounding errors, see
62                                             // also sal/rtl/math.cxx
63                                             // doubleToString()
64 
65 constexpr sal_Int32 kTimeSignificantRound = 7;  // Round (date+)time at 7 decimals
66                                                 // (+5 of 86400 == 12 significant digits).
67 
68 const sal_Unicode cBlankDigit = 0x2007;     // tdf#158890 use figure space for '?'
69 } // namespace
70 
71 const double D_MAX_U_INT32 = double(0xffffffff);      // 4294967295.0
72 constexpr double D_MAX_INTEGER = (sal_uInt64(1) << 53) - 1;
73 
74 const double D_MAX_D_BY_100  = 1.7E306;
75 const double D_MIN_M_BY_1000 = 2.3E-305;
76 
77 const sal_uInt8 cCharWidths[ 128-32 ] = {
78     1,1,1,2,2,3,2,1,1,1,1,2,1,1,1,1,
79     2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,
80     3,2,2,2,2,2,2,3,2,1,2,2,2,3,3,3,
81     2,3,2,2,2,2,2,3,2,2,2,1,1,1,2,2,
82     1,2,2,2,2,2,1,2,2,1,1,2,1,3,2,2,
83     2,2,1,2,1,2,2,2,2,2,2,1,1,1,2,1
84 };
85 
86 // static
InsertBlanks(OUStringBuffer & r,sal_Int32 nPos,sal_Unicode c)87 sal_Int32 SvNumberformat::InsertBlanks( OUStringBuffer& r, sal_Int32 nPos, sal_Unicode c )
88 {
89     if( c >= 32 )
90     {
91         int n = 2;   // Default for chars > 128 (HACK!)
92         if( c <= 127 )
93         {
94             n = static_cast<int>(cCharWidths[ c - 32 ]);
95         }
96         while( n-- )
97         {
98             r.insert( nPos++, ' ');
99         }
100     }
101     return nPos;
102 }
103 
GetPrecExp(double fAbsVal)104 static tools::Long GetPrecExp( double fAbsVal )
105 {
106     DBG_ASSERT( fAbsVal > 0.0, "GetPrecExp: fAbsVal <= 0.0" );
107     if ( fAbsVal < 1e-7 || fAbsVal > 1e7 )
108     {
109         // Shear: whether it's faster or not, falls in between 1e6 and 1e7
110         return static_cast<tools::Long>(floor( log10( fAbsVal ) )) + 1;
111     }
112     else
113     {
114         tools::Long nPrecExp = 1;
115         while( fAbsVal < 1 )
116         {
117             fAbsVal *= 10;
118             nPrecExp--;
119         }
120         while( fAbsVal >= 10 )
121         {
122             fAbsVal /= 10;
123             nPrecExp++;
124         }
125         return nPrecExp;
126     }
127 }
128 
129 /**
130  * SvNumberformatInfo
131  * */
132 
Copy(const ImpSvNumberformatInfo & rNumFor,sal_uInt16 nCnt)133 void ImpSvNumberformatInfo::Copy( const ImpSvNumberformatInfo& rNumFor, sal_uInt16 nCnt )
134 {
135     for (sal_uInt16 i = 0; i < nCnt; ++i)
136     {
137         sStrArray[i]  = rNumFor.sStrArray[i];
138         nTypeArray[i] = rNumFor.nTypeArray[i];
139     }
140     eScannedType = rNumFor.eScannedType;
141     bThousand    = rNumFor.bThousand;
142     nThousand    = rNumFor.nThousand;
143     nCntPre      = rNumFor.nCntPre;
144     nCntPost     = rNumFor.nCntPost;
145     nCntExp      = rNumFor.nCntExp;
146 }
147 
148 const std::map<LanguageType, std::array<sal_uInt8, 4>> tblDBNumToNatNum
149     = { { primary(LANGUAGE_CHINESE),    { 4, 5, 3, 0 } },
150         { primary(LANGUAGE_JAPANESE),   { 4, 5, 3, 0 } },
151         { primary(LANGUAGE_KOREAN),     { 4, 5, 6, 10 } } };
152 
153 // static
MapDBNumToNatNum(sal_uInt8 nDBNum,LanguageType eLang,bool bDate)154 sal_uInt8 SvNumberNatNum::MapDBNumToNatNum( sal_uInt8 nDBNum, LanguageType eLang, bool bDate )
155 {
156     sal_uInt8 nNatNum = 0;
157     eLang = MsLangId::getRealLanguage( eLang );  // resolve SYSTEM etc.
158     eLang = primary(eLang);    // 10 bit primary language
159     if ( bDate )
160     {
161         if ( nDBNum == 4 && eLang == primary(LANGUAGE_KOREAN) )
162         {
163             nNatNum = 10;
164         }
165         else if ( nDBNum <= 3 )
166         {
167             nNatNum = nDBNum;   // known to be good for: zh,ja,ko / 1,2,3
168         }
169     }
170     else
171     {
172         if (1 <= nDBNum && nDBNum <= 4)
173         {
174             auto const it = tblDBNumToNatNum.find(eLang);
175             if (it != tblDBNumToNatNum.end())
176                 nNatNum = it->second[nDBNum - 1];
177 
178         }
179     }
180     return nNatNum;
181 }
182 
183 const std::map<LanguageType, std::array<sal_uInt8, 9>> tblNatNumToDBNum
184     = { { primary(LANGUAGE_CHINESE),    { 1, 0, 0, 1, 2, 3, 0, 0, 0 } },
185         { primary(LANGUAGE_JAPANESE),   { 1, 2, 3, 1, 2, 3, 1, 2, 0 } },
186         { primary(LANGUAGE_KOREAN),     { 1, 2, 3, 1, 2, 3, 1, 2, 4 } } };
187 
188 // static
MapNatNumToDBNum(sal_uInt8 nNatNum,LanguageType eLang,bool bDate)189 sal_uInt8 SvNumberNatNum::MapNatNumToDBNum( sal_uInt8 nNatNum, LanguageType eLang, bool bDate )
190 {
191     sal_uInt8 nDBNum = 0;
192     eLang = MsLangId::getRealLanguage( eLang );  // resolve SYSTEM etc.
193     eLang = primary(eLang);    // 10 bit primary language
194     if ( bDate )
195     {
196         if ( nNatNum == 10 && eLang == primary(LANGUAGE_KOREAN) )
197         {
198             nDBNum = 4;
199         }
200         else if ( nNatNum <= 3 )
201         {
202             nDBNum = nNatNum;   // known to be good for: zh,ja,ko / 1,2,3
203         }
204     }
205     else
206     {
207         if (1 <= nNatNum && nNatNum <= 9)
208         {
209             auto const it = tblNatNumToDBNum.find(eLang);
210             if (it != tblNatNumToDBNum.end())
211                 nDBNum = it->second[nNatNum - 1];
212         }
213     }
214     return nDBNum;
215 }
216 
217 /**
218  * SvNumFor
219  */
220 
ImpSvNumFor()221 ImpSvNumFor::ImpSvNumFor()
222 {
223     nStringsCnt = 0;
224     aI.eScannedType = SvNumFormatType::UNDEFINED;
225     aI.bThousand = false;
226     aI.nThousand = 0;
227     aI.nCntPre = 0;
228     aI.nCntPost = 0;
229     aI.nCntExp = 0;
230     pColor = nullptr;
231 }
232 
~ImpSvNumFor()233 ImpSvNumFor::~ImpSvNumFor()
234 {
235 }
236 
Enlarge(sal_uInt16 nCnt)237 void ImpSvNumFor::Enlarge(sal_uInt16 nCnt)
238 {
239     if ( nStringsCnt != nCnt )
240     {
241         nStringsCnt = nCnt;
242         aI.nTypeArray.resize(nCnt);
243         aI.sStrArray.resize(nCnt);
244     }
245 }
246 
Copy(const ImpSvNumFor & rNumFor,const ImpSvNumberformatScan * pSc)247 void ImpSvNumFor::Copy( const ImpSvNumFor& rNumFor, const ImpSvNumberformatScan* pSc )
248 {
249     Enlarge( rNumFor.nStringsCnt );
250     aI.Copy( rNumFor.aI, nStringsCnt );
251     sColorName = rNumFor.sColorName;
252     if ( pSc )
253     {
254         pColor = pSc->GetColor( sColorName );   // #121103# don't copy pointer between documents
255     }
256     else
257     {
258         pColor = rNumFor.pColor;
259     }
260     aNatNum = rNumFor.aNatNum;
261 }
262 
HasNewCurrency() const263 bool ImpSvNumFor::HasNewCurrency() const
264 {
265     for ( sal_uInt16 j=0; j<nStringsCnt; j++ )
266     {
267         if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY )
268         {
269             return true;
270         }
271     }
272     return false;
273 }
274 
GetNewCurrencySymbol(OUString & rSymbol,OUString & rExtension) const275 bool ImpSvNumFor::GetNewCurrencySymbol( OUString& rSymbol,
276                                         OUString& rExtension ) const
277 {
278     for ( sal_uInt16 j=0; j<nStringsCnt; j++ )
279     {
280         if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY )
281         {
282             rSymbol = aI.sStrArray[j];
283             if ( j < nStringsCnt-1 && aI.nTypeArray[j+1] == NF_SYMBOLTYPE_CURREXT )
284             {
285                 rExtension = aI.sStrArray[j+1];
286             }
287             else
288             {
289                 rExtension.clear();
290             }
291             return true;
292         }
293     }
294     //! No Erase at rSymbol, rExtension
295     return false;
296 }
297 
298 /**
299  * SvNumberformat
300  */
301 
302 namespace {
303 
304 enum BracketFormatSymbolType
305 {
306     BRACKET_SYMBOLTYPE_FORMAT   = -1,   // subformat string
307     BRACKET_SYMBOLTYPE_COLOR    = -2,   // color
308     BRACKET_SYMBOLTYPE_ERROR    = -3,   // error
309     BRACKET_SYMBOLTYPE_DBNUM1   = -4,   // DoubleByteNumber, represent numbers
310     BRACKET_SYMBOLTYPE_DBNUM2   = -5,   // using CJK characters, Excel compatible
311     BRACKET_SYMBOLTYPE_DBNUM3   = -6,
312     BRACKET_SYMBOLTYPE_DBNUM4   = -7,
313     BRACKET_SYMBOLTYPE_DBNUM5   = -8,
314     BRACKET_SYMBOLTYPE_DBNUM6   = -9,
315     BRACKET_SYMBOLTYPE_DBNUM7   = -10,
316     BRACKET_SYMBOLTYPE_DBNUM8   = -11,
317     BRACKET_SYMBOLTYPE_DBNUM9   = -12,
318     BRACKET_SYMBOLTYPE_LOCALE   = -13,
319     BRACKET_SYMBOLTYPE_NATNUM0  = -14,  // Our NativeNumber support, ASCII
320     BRACKET_SYMBOLTYPE_NATNUM1  = -15,  // Our NativeNumber support, represent
321     BRACKET_SYMBOLTYPE_NATNUM2  = -16,  // numbers using CJK, CTL, ...
322     BRACKET_SYMBOLTYPE_NATNUM3  = -17,
323     BRACKET_SYMBOLTYPE_NATNUM4  = -18,
324     BRACKET_SYMBOLTYPE_NATNUM5  = -19,
325     BRACKET_SYMBOLTYPE_NATNUM6  = -20,
326     BRACKET_SYMBOLTYPE_NATNUM7  = -21,
327     BRACKET_SYMBOLTYPE_NATNUM8  = -22,
328     BRACKET_SYMBOLTYPE_NATNUM9  = -23,
329     BRACKET_SYMBOLTYPE_NATNUM10 = -24,
330     BRACKET_SYMBOLTYPE_NATNUM11 = -25,
331     BRACKET_SYMBOLTYPE_NATNUM12 = -26,
332     BRACKET_SYMBOLTYPE_NATNUM13 = -27,
333     BRACKET_SYMBOLTYPE_NATNUM14 = -28,
334     BRACKET_SYMBOLTYPE_NATNUM15 = -29,
335     BRACKET_SYMBOLTYPE_NATNUM16 = -30,
336     BRACKET_SYMBOLTYPE_NATNUM17 = -31,
337     BRACKET_SYMBOLTYPE_NATNUM18 = -32,
338     BRACKET_SYMBOLTYPE_NATNUM19 = -33
339 };
340 
341 }
342 
ImpCopyNumberformat(const SvNumberformat & rFormat)343 void SvNumberformat::ImpCopyNumberformat( const SvNumberformat& rFormat )
344 {
345     sFormatstring = rFormat.sFormatstring;
346     eType         = rFormat.eType;
347     maLocale      = rFormat.maLocale;
348     fLimit1       = rFormat.fLimit1;
349     fLimit2       = rFormat.fLimit2;
350     eOp1          = rFormat.eOp1;
351     eOp2          = rFormat.eOp2;
352     bStandard     = rFormat.bStandard;
353     bIsUsed       = rFormat.bIsUsed;
354     sComment      = rFormat.sComment;
355     bAdditionalBuiltin = rFormat.bAdditionalBuiltin;
356 
357     // #121103# when copying between documents, get color pointers from own scanner
358     ImpSvNumberformatScan* pColorSc = ( &rScan != &rFormat.rScan ) ? &rScan : nullptr;
359 
360     for (sal_uInt16 i = 0; i < 4; i++)
361     {
362         NumFor[i].Copy(rFormat.NumFor[i], pColorSc);
363     }
364 }
365 
SvNumberformat(SvNumberformat const & rFormat)366 SvNumberformat::SvNumberformat( SvNumberformat const & rFormat )
367     : rScan(rFormat.rScan)
368 {
369     ImpCopyNumberformat( rFormat );
370 }
371 
SvNumberformat(SvNumberformat const & rFormat,ImpSvNumberformatScan & rSc)372 SvNumberformat::SvNumberformat( SvNumberformat const & rFormat, ImpSvNumberformatScan& rSc )
373     : rScan(rSc)
374 {
375     ImpCopyNumberformat( rFormat );
376 }
377 
lcl_SvNumberformat_IsBracketedPrefix(short nSymbolType)378 static bool lcl_SvNumberformat_IsBracketedPrefix( short nSymbolType )
379 {
380     if ( nSymbolType > 0  )
381     {
382         return true; // conditions
383     }
384     switch ( nSymbolType )
385     {
386     case BRACKET_SYMBOLTYPE_COLOR :
387     case BRACKET_SYMBOLTYPE_DBNUM1 :
388     case BRACKET_SYMBOLTYPE_DBNUM2 :
389     case BRACKET_SYMBOLTYPE_DBNUM3 :
390     case BRACKET_SYMBOLTYPE_DBNUM4 :
391     case BRACKET_SYMBOLTYPE_DBNUM5 :
392     case BRACKET_SYMBOLTYPE_DBNUM6 :
393     case BRACKET_SYMBOLTYPE_DBNUM7 :
394     case BRACKET_SYMBOLTYPE_DBNUM8 :
395     case BRACKET_SYMBOLTYPE_DBNUM9 :
396     case BRACKET_SYMBOLTYPE_LOCALE :
397     case BRACKET_SYMBOLTYPE_NATNUM0 :
398     case BRACKET_SYMBOLTYPE_NATNUM1 :
399     case BRACKET_SYMBOLTYPE_NATNUM2 :
400     case BRACKET_SYMBOLTYPE_NATNUM3 :
401     case BRACKET_SYMBOLTYPE_NATNUM4 :
402     case BRACKET_SYMBOLTYPE_NATNUM5 :
403     case BRACKET_SYMBOLTYPE_NATNUM6 :
404     case BRACKET_SYMBOLTYPE_NATNUM7 :
405     case BRACKET_SYMBOLTYPE_NATNUM8 :
406     case BRACKET_SYMBOLTYPE_NATNUM9 :
407     case BRACKET_SYMBOLTYPE_NATNUM10 :
408     case BRACKET_SYMBOLTYPE_NATNUM11 :
409     case BRACKET_SYMBOLTYPE_NATNUM12 :
410     case BRACKET_SYMBOLTYPE_NATNUM13 :
411     case BRACKET_SYMBOLTYPE_NATNUM14 :
412     case BRACKET_SYMBOLTYPE_NATNUM15 :
413     case BRACKET_SYMBOLTYPE_NATNUM16 :
414     case BRACKET_SYMBOLTYPE_NATNUM17 :
415     case BRACKET_SYMBOLTYPE_NATNUM18 :
416     case BRACKET_SYMBOLTYPE_NATNUM19 :
417         return true;
418     }
419     return false;
420 }
421 
422 /** Import extended LCID from Excel
423  */
ImpObtainCalendarAndNumerals(OUStringBuffer & rString,sal_Int32 nPos,LanguageType & nLang,const LocaleType & aTmpLocale)424 OUString SvNumberformat::ImpObtainCalendarAndNumerals( OUStringBuffer& rString, sal_Int32 nPos,
425                                                        LanguageType& nLang, const LocaleType& aTmpLocale )
426 {
427     OUString sCalendar;
428     sal_uInt16 nNatNum = 0;
429     LanguageType nLocaleLang = MsLangId::getRealLanguage( maLocale.meLanguage );
430     LanguageType nTmpLocaleLang = MsLangId::getRealLanguage( aTmpLocale.meLanguage );
431     /* NOTE: enhancement to allow other possible locale dependent
432      * calendars and numerals. BUT only if our locale data allows it! For LCID
433      * numerals and calendars see
434      * http://office.microsoft.com/en-us/excel/HA010346351033.aspx
435      * Calendar is inserted after
436      * all prefixes have been consumed as it is actually a format modifier
437      * and not a prefix.
438      * Currently calendars are tied to the locale of the entire number
439      * format, e.g. [~buddhist] in en_US doesn't work.
440      * => Having different locales in sub formats does not work!
441      * */
442     /* TODO: calendars could be tied to a sub format's NatNum info
443      * instead, or even better be available for any locale. Needs a
444      * different implementation of GetCal() and locale data calendars.
445      * */
446     switch ( aTmpLocale.mnCalendarType & 0x7F )
447     {
448         case 0x03 : // Gengou calendar
449             // Only Japanese language support Gengou calendar.
450             // It is an implicit "other" calendar where E, EE, R and RR
451             // automatically switch to and YY and YYYY switch to Gregorian. Do
452             // not add the "[~gengou]" modifier.
453             if ( nLocaleLang != LANGUAGE_JAPANESE )
454             {
455                 nLang = maLocale.meLanguage = LANGUAGE_JAPANESE;
456             }
457             break;
458         case 0x05 : // Korean Dangi calendar
459             sCalendar = "[~dangi]";
460             // Only Korean language support dangi calendar
461             if ( nLocaleLang != LANGUAGE_KOREAN )
462             {
463                 nLang = maLocale.meLanguage = LANGUAGE_KOREAN;
464             }
465             break;
466         case 0x06 : // Hijri calendar
467         case 0x17 : // same?
468             sCalendar = "[~hijri]";
469             // Only Arabic or Farsi languages support Hijri calendar
470             if ( ( primary( nLocaleLang ) != LANGUAGE_ARABIC_PRIMARY_ONLY )
471                   && nLocaleLang != LANGUAGE_FARSI )
472             {
473                 if ( ( primary( nTmpLocaleLang ) == LANGUAGE_ARABIC_PRIMARY_ONLY )
474                       || nTmpLocaleLang == LANGUAGE_FARSI )
475                 {
476                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
477                 }
478                 else
479                 {
480                     nLang = maLocale.meLanguage = LANGUAGE_ARABIC_SAUDI_ARABIA;
481                 }
482             }
483             break;
484         case 0x07 : // Buddhist calendar
485             sCalendar="[~buddhist]";
486             // Only Thai or Lao languages support Buddhist calendar
487             if ( nLocaleLang != LANGUAGE_THAI && nLocaleLang != LANGUAGE_LAO )
488             {
489                 if ( nTmpLocaleLang == LANGUAGE_THAI || nTmpLocaleLang == LANGUAGE_LAO )
490                 {
491                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
492                 }
493                 else
494                 {
495                     nLang = maLocale.meLanguage = LANGUAGE_THAI;
496                 }
497             }
498             break;
499         case 0x08 : // Hebrew calendar
500             sCalendar = "[~jewish]";
501             // Many languages (but not all) support Jewish calendar
502             // Unable to find any logic => keep same language
503             break;
504         case 0x0E : // unknown calendar
505         case 0x0F : // unknown calendar
506         case 0x10 : // Indian calendar (unsupported)
507         case 0x11 : // unknown calendar
508         case 0x12 : // unknown calendar
509         case 0x13 : // unknown calendar
510         default : // other calendars (see tdf#36038) are not handle by LibO
511             break;
512     }
513     /** Reference language for each numeral ID */
514     static const LanguageType aNumeralIDtoLanguage []=
515     {
516         LANGUAGE_DONTKNOW,              // 0x00
517         LANGUAGE_ENGLISH_US,            // 0x01
518         LANGUAGE_ARABIC_SAUDI_ARABIA,   // 0x02 + all Arabic
519         LANGUAGE_FARSI,                 // 0x03
520         LANGUAGE_HINDI,                 // 0x04 + Devanagari
521         LANGUAGE_BENGALI,               // 0x05
522         LANGUAGE_PUNJABI,               // 0x06
523         LANGUAGE_GUJARATI,              // 0x07
524         LANGUAGE_ODIA,                  // 0x08
525         LANGUAGE_TAMIL,                 // 0x09
526         LANGUAGE_TELUGU,                // 0x0A
527         LANGUAGE_KANNADA,               // 0x0B
528         LANGUAGE_MALAYALAM,             // 0x0C
529         LANGUAGE_THAI,                  // 0x0D
530         LANGUAGE_LAO,                   // 0x0E
531         LANGUAGE_TIBETAN,               // 0x0F
532         LANGUAGE_BURMESE,               // 0x10
533         LANGUAGE_TIGRIGNA_ETHIOPIA,     // 0x11
534         LANGUAGE_KHMER,                 // 0x12
535         LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA, // 0x13
536         LANGUAGE_DONTKNOW,              // 0x14
537         LANGUAGE_DONTKNOW,              // 0x15
538         LANGUAGE_DONTKNOW,              // 0x16
539         LANGUAGE_DONTKNOW,              // 0x17
540         LANGUAGE_DONTKNOW,              // 0x18
541         LANGUAGE_DONTKNOW,              // 0x19
542         LANGUAGE_DONTKNOW,              // 0x1A
543         LANGUAGE_JAPANESE,              // 0x1B
544         LANGUAGE_JAPANESE,              // 0x1C
545         LANGUAGE_JAPANESE,              // 0x1D
546         LANGUAGE_CHINESE_SIMPLIFIED,    // 0x1E
547         LANGUAGE_CHINESE_SIMPLIFIED,    // 0x1F
548         LANGUAGE_CHINESE_SIMPLIFIED,    // 0x20
549         LANGUAGE_CHINESE_TRADITIONAL,   // 0x21
550         LANGUAGE_CHINESE_TRADITIONAL,   // 0x22
551         LANGUAGE_CHINESE_TRADITIONAL,   // 0x23
552         LANGUAGE_KOREAN,                // 0x24
553         LANGUAGE_KOREAN,                // 0x25
554         LANGUAGE_KOREAN,                // 0x26
555         LANGUAGE_KOREAN                 // 0x27
556     };
557 
558     sal_uInt16 nNumeralID = aTmpLocale.mnNumeralShape & 0x7F;
559     LanguageType nReferenceLanguage = nNumeralID <= 0x27 ? aNumeralIDtoLanguage[nNumeralID] : LANGUAGE_DONTKNOW;
560 
561     switch ( nNumeralID )
562     {
563         // Regular cases: all languages with same primary mask use same numerals
564         case 0x03 : // Perso-Arabic (Farsi) numerals
565         case 0x05 : // Bengali numerals
566         case 0x06 : // Punjabi numerals
567         case 0x07 : // Gujarati numerals
568         case 0x08 : // Odia (Orya) numerals
569         case 0x09 : // Tamil numerals
570         case 0x0A : // Telugu numerals
571         case 0x0B : // Kannada numerals
572         case 0x0C : // Malayalam numerals
573         case 0x0D : // Thai numerals
574         case 0x0E : // Lao numerals
575         case 0x0F : // Tibetan numerals
576         case 0x10 : // Burmese (Myanmar) numerals
577         case 0x11 : // Tigrigna (Ethiopia) numerals
578         case 0x12 : // Khmer numerals
579             if ( primary( nLocaleLang ) != primary( nReferenceLanguage ) )
580             {
581                 if ( primary( nTmpLocaleLang ) == primary( nReferenceLanguage ) )
582                 {
583                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
584                 }
585                 else
586                 {
587                     nLang = maLocale.meLanguage = nReferenceLanguage;
588                 }
589             }
590             break;
591         // Special cases
592         case 0x04 : // Devanagari (Hindi) numerals
593             // same numerals (Devanagari) for languages with different primary masks
594             if ( nLocaleLang != LANGUAGE_HINDI    && nLocaleLang != LANGUAGE_MARATHI
595             && primary( nLocaleLang ) != primary( LANGUAGE_NEPALI ) )
596             {
597                 if ( nTmpLocaleLang == LANGUAGE_HINDI || nTmpLocaleLang == LANGUAGE_MARATHI
598                 || primary( nTmpLocaleLang ) == primary( LANGUAGE_NEPALI ) )
599                 {
600                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
601                 }
602                 else
603                 {
604                     nLang = maLocale.meLanguage = LANGUAGE_HINDI;
605                 }
606             }
607             break;
608         case 0x13 : // Mongolian numerals
609             // not all Mongolian languages use Mongolian numerals
610             if ( nLocaleLang != LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
611               && nLocaleLang != LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
612               && nLocaleLang != LANGUAGE_MONGOLIAN_MONGOLIAN_LSO )
613             {
614                 if ( nTmpLocaleLang == LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
615                   || nTmpLocaleLang == LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
616                   || nTmpLocaleLang == LANGUAGE_MONGOLIAN_MONGOLIAN_LSO )
617                 {
618                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
619                 }
620                 else
621                 {
622                     nLang = maLocale.meLanguage = LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA;
623                 }
624             }
625             break;
626         case 0x02 : // Eastern-Arabic numerals
627             // all arabic primary mask + LANGUAGE_PUNJABI_ARABIC_LSO
628             if ( primary( nLocaleLang ) != LANGUAGE_ARABIC_PRIMARY_ONLY
629                 && nLocaleLang != LANGUAGE_PUNJABI_ARABIC_LSO )
630             {
631                 if ( primary( nTmpLocaleLang ) == LANGUAGE_ARABIC_PRIMARY_ONLY
632                     || nTmpLocaleLang != LANGUAGE_PUNJABI_ARABIC_LSO )
633                 {
634                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
635                 }
636                 else
637                 {
638                     nLang = maLocale.meLanguage = nReferenceLanguage;
639                 }
640             }
641             break;
642         // CJK numerals
643         case 0x1B : // simple Asian numerals, Japanese
644         case 0x1C : // financial Asian numerals, Japanese
645         case 0x1D : // Arabic fullwidth numerals, Japanese
646         case 0x24 : // simple Asian numerals, Korean
647         case 0x25 : // financial Asian numerals, Korean
648         case 0x26 : // Arabic fullwidth numerals, Korean
649         case 0x27 : // Korean Hangul numerals
650             // Japanese and Korean are regular
651             if ( primary( nLocaleLang ) != primary( nReferenceLanguage ) )
652             {
653                 if ( primary( nTmpLocaleLang ) == primary( nReferenceLanguage ) )
654                 {
655                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
656                 }
657                 else
658                 {
659                     nLang = maLocale.meLanguage = nReferenceLanguage;
660                 }
661             }
662             [[fallthrough]];
663         case 0x1E : // simple Asian numerals, Chinese-PRC
664         case 0x1F : // financial Asian numerals, Chinese-PRC
665         case 0x20 : // Arabic fullwidth numerals, Chinese-PRC
666         case 0x21 : // simple Asian numerals, Chinese-Taiwan
667         case 0x22 : // financial Asian numerals, Chinese-Taiwan
668         case 0x23 : // Arabic fullwidth numerals, Chinese-Taiwan
669             nNatNum = nNumeralID == 0x27 ? 9 : ( ( nNumeralID - 0x1B ) % 3 ) + 1;
670             // [NatNum1] simple numerals
671             // [natNum2] financial numerals
672             // [NatNum3] Arabic fullwidth numerals
673             // Chinese simplified and Chinese traditional have same primary mask
674             // Chinese-PRC
675             if ( nReferenceLanguage == LANGUAGE_CHINESE_SIMPLIFIED
676               && nLocaleLang != LANGUAGE_CHINESE_SIMPLIFIED
677               && nLocaleLang != LANGUAGE_CHINESE_SINGAPORE
678               && nLocaleLang != LANGUAGE_CHINESE_LSO )
679             {
680                 if ( nTmpLocaleLang == LANGUAGE_CHINESE_SIMPLIFIED
681                   || nTmpLocaleLang == LANGUAGE_CHINESE_SINGAPORE
682                   || nTmpLocaleLang == LANGUAGE_CHINESE_LSO )
683                 {
684                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
685                 }
686                 else
687                 {
688                     nLang = maLocale.meLanguage = LANGUAGE_CHINESE_SIMPLIFIED;
689                 }
690             }
691             // Chinese-Taiwan
692             else if ( nReferenceLanguage == LANGUAGE_CHINESE_TRADITIONAL
693                    && nLocaleLang != LANGUAGE_CHINESE_TRADITIONAL
694                    && nLocaleLang != LANGUAGE_CHINESE_HONGKONG
695                    && nLocaleLang != LANGUAGE_CHINESE_MACAU )
696             {
697                 if ( nTmpLocaleLang == LANGUAGE_CHINESE_TRADITIONAL
698                   || nTmpLocaleLang == LANGUAGE_CHINESE_HONGKONG
699                   || nTmpLocaleLang == LANGUAGE_CHINESE_MACAU )
700                 {
701                     nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
702                 }
703                 else
704                 {
705                     nLang = maLocale.meLanguage = LANGUAGE_CHINESE_TRADITIONAL;
706                 }
707             }
708             break;
709     }
710     if ( nNumeralID >= 0x02 && nNumeralID <= 0x13 )
711         nNatNum = 1;
712     if ( nNatNum )
713         rString.insert(nPos, "[NatNum" + OUString::number(nNatNum) + "]");
714     return sCalendar;
715 }
716 
717 namespace
718 {
NatNumTakesParameters(sal_Int16 nNum)719 bool NatNumTakesParameters(sal_Int16 nNum)
720 {
721     return (nNum == css::i18n::NativeNumberMode::NATNUM12);
722 }
723 }
724 
725 // is there a 3-letter bank code in NatNum12 param (but not
726 // followed by an equal mark, like in the date code "NNN=")?
lcl_isNatNum12Currency(std::u16string_view sParam)727 static bool lcl_isNatNum12Currency( std::u16string_view sParam )
728 {
729     sal_Int32 nUpper = 0;
730     sal_Int32 nLen = sParam.size();
731     for (sal_Int32 n = 0; n < nLen; ++n)
732     {
733         sal_Unicode c = sParam[n];
734         if ( 'A' <= c && c <= 'Z' )
735         {
736             ++nUpper;
737         }
738         else if ( c == ' ' && nUpper == 3 && (n == 3 || sParam[n - 4] == ' ') )
739         {
740             return true;
741         }
742         else
743         {
744             nUpper = 0;
745         }
746     }
747 
748     return nUpper == 3 && (nLen == 3 || sParam[nLen - 4] == ' ');
749 }
750 
SvNumberformat(OUString & rString,ImpSvNumberformatScan * pSc,ImpSvNumberInputScan * pISc,const NativeNumberWrapper & rNatNum,sal_Int32 & nCheckPos,LanguageType & eLan,bool bReplaceBooleanEquivalent)751 SvNumberformat::SvNumberformat(OUString& rString,
752                                ImpSvNumberformatScan* pSc,
753                                ImpSvNumberInputScan* pISc,
754                                const NativeNumberWrapper& rNatNum,
755                                sal_Int32& nCheckPos,
756                                LanguageType& eLan,
757                                bool bReplaceBooleanEquivalent)
758         : rScan(*pSc)
759         , bAdditionalBuiltin( false )
760 {
761     if (bReplaceBooleanEquivalent)
762         rScan.ReplaceBooleanEquivalent( rString);
763 
764     OUStringBuffer sBuff(rString);
765 
766     // If the group (AKA thousand) separator is a No-Break Space (French)
767     // replace all occurrences by a simple space.
768     // The same for Narrow No-Break Space just in case some locale uses it.
769     // The tokens will be changed to the LocaleData separator again later on.
770     const OUString& rThSep = GetCurrentLanguageData().GetNumThousandSep();
771     if ( rThSep.getLength() == 1)
772     {
773         const sal_Unicode cNBSp = 0xA0;
774         const sal_Unicode cNNBSp = 0x202F;
775         if (rThSep[0] == cNBSp )
776             sBuff.replace( cNBSp, ' ');
777         else if (rThSep[0] == cNNBSp )
778             sBuff.replace( cNNBSp, ' ');
779     }
780 
781     OUString aConvertFromDecSep;
782     OUString aConvertToDecSep;
783     if (rScan.GetConvertMode())
784     {
785         aConvertFromDecSep = GetCurrentLanguageData().GetNumDecimalSep();
786         maLocale.meLanguage = rScan.GetNewLnge();
787         eLan = maLocale.meLanguage; // Make sure to return switch
788     }
789     else
790     {
791         maLocale.meLanguage = eLan;
792     }
793     bStandard = false;
794     bIsUsed = false;
795     fLimit1 = 0.0;
796     fLimit2 = 0.0;
797     eOp1 = NUMBERFORMAT_OP_NO;
798     eOp2 = NUMBERFORMAT_OP_NO;
799     eType = SvNumFormatType::DEFINED;
800 
801     bool bCancel = false;
802     bool bCondition = false;
803     short eSymbolType;
804     sal_Int32 nPos = 0;
805     sal_Int32 nPosOld;
806     nCheckPos = 0;
807 
808     // Split into 4 sub formats
809     sal_uInt16 nIndex;
810     for ( nIndex = 0; nIndex < 4 && !bCancel; nIndex++ )
811     {
812         // Original language/country may have to be reestablished
813         if (rScan.GetConvertMode())
814         {
815             rScan.GetCurrentLanguageData().ChangeIntl(rScan.GetTmpLnge());
816         }
817         OUString sInsertCalendar; // a calendar resulting from parsing LCID
818         OUString sStr;
819         nPosOld = nPos; // Start position of substring
820         // first get bracketed prefixes; e.g. conditions, color
821         do
822         {
823             eSymbolType = ImpNextSymbol(sBuff, nPos, sStr);
824             if (eSymbolType > 0) // condition
825             {
826                 if ( nIndex == 0 && !bCondition )
827                 {
828                     bCondition = true;
829                     eOp1 = static_cast<SvNumberformatLimitOps>(eSymbolType);
830                 }
831                 else if ( nIndex == 1 && bCondition )
832                 {
833                     eOp2 = static_cast<SvNumberformatLimitOps>(eSymbolType);
834                 }
835                 else                                // error
836                 {
837                     bCancel = true;                 // break for
838                     nCheckPos = nPosOld;
839                 }
840                 if (!bCancel)
841                 {
842                     double fNumber;
843                     sal_Int32 nCntChars = ImpGetNumber(sBuff, nPos, sStr);
844                     if (nCntChars > 0)
845                     {
846                         sal_Int32 nDecPos;
847                         SvNumFormatType F_Type = SvNumFormatType::UNDEFINED;
848                         if (!pISc->IsNumberFormat(sStr, F_Type, fNumber, nullptr, rNatNum, SvNumInputOptions::NONE) ||
849                             ( F_Type != SvNumFormatType::NUMBER &&
850                               F_Type != SvNumFormatType::SCIENTIFIC) )
851                         {
852                             fNumber = 0.0;
853                             nPos = nPos - nCntChars;
854                             sBuff.remove(nPos, nCntChars);
855                             sBuff.insert(nPos, '0');
856                             nPos++;
857                         }
858                         else if (rScan.GetConvertMode() && ((nDecPos = sStr.indexOf( aConvertFromDecSep)) >= 0))
859                         {
860                             if (aConvertToDecSep.isEmpty())
861                                 aConvertToDecSep = rScan.GetCurrentLanguageData().GetLangDecimalSep( rScan.GetNewLnge());
862                             if (aConvertToDecSep != aConvertFromDecSep)
863                             {
864                                 const OUString aStr( sStr.replaceAt( nDecPos,
865                                             aConvertFromDecSep.getLength(), aConvertToDecSep));
866                                 nPos = nPos - nCntChars;
867                                 sBuff.remove(nPos, nCntChars);
868                                 sBuff.insert(nPos, aStr);
869                                 nPos += aStr.getLength();
870                             }
871                         }
872                     }
873                     else
874                     {
875                         fNumber = 0.0;
876                         sBuff.insert(nPos++, '0');
877                     }
878                     if (nIndex == 0)
879                     {
880                         fLimit1 = fNumber;
881                     }
882                     else
883                     {
884                         fLimit2 = fNumber;
885                     }
886                     if ( nPos < sBuff.getLength() && sBuff[nPos] == ']' )
887                     {
888                         nPos++;
889                     }
890                     else
891                     {
892                         bCancel = true;             // break for
893                         nCheckPos = nPos;
894                     }
895                 }
896                 nPosOld = nPos;                     // position before string
897             }
898             else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) )
899             {
900                 OUString sSymbol( sStr);
901                 switch ( eSymbolType )
902                 {
903                 case BRACKET_SYMBOLTYPE_COLOR :
904                     if ( NumFor[nIndex].GetColor() != nullptr )
905                     {                           // error, more than one color
906                         bCancel = true;         // break for
907                         nCheckPos = nPosOld;
908                     }
909                     else
910                     {
911                         const Color* pColor = pSc->GetColor( sStr);
912                         NumFor[nIndex].SetColor( pColor, sStr);
913                         if (pColor == nullptr)
914                         {                       // error
915                             bCancel = true;     // break for
916                             nCheckPos = nPosOld;
917                         }
918                     }
919                     break;
920                 case BRACKET_SYMBOLTYPE_NATNUM0 :
921                 case BRACKET_SYMBOLTYPE_NATNUM1 :
922                 case BRACKET_SYMBOLTYPE_NATNUM2 :
923                 case BRACKET_SYMBOLTYPE_NATNUM3 :
924                 case BRACKET_SYMBOLTYPE_NATNUM4 :
925                 case BRACKET_SYMBOLTYPE_NATNUM5 :
926                 case BRACKET_SYMBOLTYPE_NATNUM6 :
927                 case BRACKET_SYMBOLTYPE_NATNUM7 :
928                 case BRACKET_SYMBOLTYPE_NATNUM8 :
929                 case BRACKET_SYMBOLTYPE_NATNUM9 :
930                 case BRACKET_SYMBOLTYPE_NATNUM10 :
931                 case BRACKET_SYMBOLTYPE_NATNUM11 :
932                 case BRACKET_SYMBOLTYPE_NATNUM12 :
933                 case BRACKET_SYMBOLTYPE_NATNUM13 :
934                 case BRACKET_SYMBOLTYPE_NATNUM14 :
935                 case BRACKET_SYMBOLTYPE_NATNUM15 :
936                 case BRACKET_SYMBOLTYPE_NATNUM16 :
937                 case BRACKET_SYMBOLTYPE_NATNUM17 :
938                 case BRACKET_SYMBOLTYPE_NATNUM18 :
939                 case BRACKET_SYMBOLTYPE_NATNUM19 :
940                     if ( NumFor[nIndex].GetNatNum().IsSet() )
941                     {
942                         bCancel = true;         // break for
943                         nCheckPos = nPosOld;
944                     }
945                     else
946                     {
947                         OUString sParams;
948                         sal_Int32 nSpacePos = sStr.indexOf(' ');
949                         if (nSpacePos >= 0)
950                         {
951                             sParams = o3tl::trim(sStr.subView(nSpacePos+1));
952                         }
953                         //! eSymbolType is negative
954                         sal_uInt8 nNum = static_cast<sal_uInt8>(0 - (eSymbolType - BRACKET_SYMBOLTYPE_NATNUM0));
955                         if (!sParams.isEmpty() && !NatNumTakesParameters(nNum))
956                         {
957                             bCancel = true; // break for
958                             nCheckPos = nPosOld;
959                             break;
960                         }
961                         sStr = "NatNum" + OUString::number(nNum);
962                         NumFor[nIndex].SetNatNumNum( nNum, false );
963                         // NatNum12 supports arguments
964                         if (nNum == 12)
965                         {
966                             if (sParams.isEmpty())
967                                 sParams = "cardinal"; // default NatNum12 format is "cardinal"
968                             else if (sParams.indexOf("CURRENCY") >= 0)
969                                 sParams = sParams.replaceAll("CURRENCY",
970                                     rLoc().getCurrBankSymbol());
971                             NumFor[nIndex].SetNatNumParams(sParams);
972                             sStr += " " + sParams;
973                         }
974                     }
975                     break;
976                 case BRACKET_SYMBOLTYPE_DBNUM1 :
977                 case BRACKET_SYMBOLTYPE_DBNUM2 :
978                 case BRACKET_SYMBOLTYPE_DBNUM3 :
979                 case BRACKET_SYMBOLTYPE_DBNUM4 :
980                 case BRACKET_SYMBOLTYPE_DBNUM5 :
981                 case BRACKET_SYMBOLTYPE_DBNUM6 :
982                 case BRACKET_SYMBOLTYPE_DBNUM7 :
983                 case BRACKET_SYMBOLTYPE_DBNUM8 :
984                 case BRACKET_SYMBOLTYPE_DBNUM9 :
985                     if ( NumFor[nIndex].GetNatNum().IsSet() )
986                     {
987                         bCancel = true;         // break for
988                         nCheckPos = nPosOld;
989                     }
990                     else
991                     {
992                         //! eSymbolType is negative
993                         sal_uInt8 nNum = static_cast<sal_uInt8>(1 - (eSymbolType - BRACKET_SYMBOLTYPE_DBNUM1));
994                         sStr = "DBNum" + OUStringChar(sal_Unicode('0' + nNum));
995                         NumFor[nIndex].SetNatNumNum( nNum, true );
996                     }
997                     break;
998                 case BRACKET_SYMBOLTYPE_LOCALE :
999                     if ( NumFor[nIndex].GetNatNum().GetLang() != LANGUAGE_DONTKNOW ||
1000                          sBuff[nPos-1] != ']' )
1001                         // Check also for ']' to avoid pulling in
1002                         // locale data for the preview string for not
1003                         // yet completed LCIDs in the dialog.
1004                     {
1005                         bCancel = true;         // break for
1006                         nCheckPos = nPosOld;
1007                     }
1008                     else
1009                     {
1010                         sal_Int32 nTmp = 2;
1011                         LocaleType aTmpLocale( ImpGetLocaleType( sStr, nTmp));
1012                         if (aTmpLocale.meLanguage == LANGUAGE_DONTKNOW)
1013                         {
1014                             bCancel = true;         // break for
1015                             nCheckPos = nPosOld;
1016                         }
1017                         else
1018                         {
1019                             // Only the first sub format's locale will be
1020                             // used as the format's overall locale.
1021                             // Sorts this also under the corresponding
1022                             // locale for the dialog.
1023                             // If we don't support the locale this would
1024                             // result in an unknown (empty) language
1025                             // listbox entry and the user would never see
1026                             // this format.
1027                             if (nIndex == 0 && (aTmpLocale.meLanguage == LANGUAGE_SYSTEM ||
1028                                                 SvNumberFormatter::IsLocaleInstalled( aTmpLocale.meLanguage)))
1029                             {
1030                                 maLocale = aTmpLocale;
1031                                 eLan = aTmpLocale.meLanguage;   // return to caller
1032 
1033                                 // Set new target locale also at scanner.
1034                                 // We have to do this because switching locale
1035                                 // may make replacing keywords and separators
1036                                 // necessary.
1037                                 // We can do this because it's the first
1038                                 // subformat and we're still at parsing the
1039                                 // modifiers, not keywords.
1040                                 rScan.SetNewLnge( eLan);
1041                                 // We can not force conversion though because
1042                                 // the caller may have explicitly not set it.
1043                                 // In the usual case the target locale is the
1044                                 // originating locale the conversion is not
1045                                 // necessary, when reading alien documents
1046                                 // conversion is enabled anyway.
1047 
1048                                 /* TODO: fiddle with scanner to make this
1049                                  * known? A change in the locale may affect
1050                                  * separators and keywords. On the other
1051                                  * hand they may have been entered as used
1052                                  * in the originating locale, there's no
1053                                  * way to predict other than analyzing the
1054                                  * format code, we assume here the current
1055                                  * context is used, which is most likely
1056                                  * the case.
1057                                  * */
1058 
1059                                 // Strip a plain locale identifier if locale
1060                                 // data is available to avoid duplicated
1061                                 // formats with and without LCID for the same
1062                                 // locale. Besides it looks ugly and confusing
1063                                 // and is unnecessary as the format will be
1064                                 // listed for the resulting locale.
1065                                 if (aTmpLocale.isPlainLocale())
1066                                     sStr.clear();
1067                                 else
1068                                     sStr = "$-" + aTmpLocale.generateCode();
1069                             }
1070                             else
1071                             {
1072                                 if (nIndex == 0)
1073                                     // Locale data not available, remember.
1074                                     maLocale.meLanguageWithoutLocaleData = aTmpLocale.meLanguage;
1075 
1076                                 sStr = "$-" + aTmpLocale.generateCode();
1077                             }
1078                             NumFor[nIndex].SetNatNumLang( MsLangId::getRealLanguage( aTmpLocale.meLanguage));
1079 
1080                             // "$-NNCCLLLL" Numerals and Calendar
1081                             if (sSymbol.getLength() > 6)
1082                             {
1083                                 sInsertCalendar = ImpObtainCalendarAndNumerals( sBuff, nPos, eLan, aTmpLocale);
1084                             }
1085                             /* NOTE: there can be only one calendar
1086                              * inserted so the last one wins, though
1087                              * our own calendar modifiers support
1088                              * multiple calendars within one sub format
1089                              * code if at different positions. */
1090                         }
1091                     }
1092                     break;
1093                 }
1094                 if ( !bCancel )
1095                 {
1096                     if (sStr == sSymbol)
1097                     {
1098                         nPosOld = nPos;
1099                     }
1100                     else
1101                     {
1102                         sBuff.remove(nPosOld, nPos - nPosOld);
1103                         if (!sStr.isEmpty())
1104                         {
1105                             sBuff.insert(nPosOld, "[" + sStr + "]");
1106                             nPos = nPosOld + sStr.getLength() + 2;
1107                             nPosOld = nPos;     // position before string
1108                         }
1109                         else
1110                         {
1111                             nPos = nPosOld;     // prefix removed for whatever reason
1112                         }
1113                     }
1114                 }
1115             }
1116         }
1117         while ( !bCancel && lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) );
1118 
1119         // The remaining format code string
1120         if ( !bCancel )
1121         {
1122             if (eSymbolType == BRACKET_SYMBOLTYPE_FORMAT)
1123             {
1124                 if (nIndex == 1 && eOp1 == NUMBERFORMAT_OP_NO)
1125                 {
1126                     eOp1 = NUMBERFORMAT_OP_GT;  // undefined condition, default: > 0
1127                 }
1128                 else if (nIndex == 2 && eOp2 == NUMBERFORMAT_OP_NO)
1129                 {
1130                     eOp2 = NUMBERFORMAT_OP_LT;  // undefined condition, default: < 0
1131                 }
1132                 if (sStr.isEmpty())
1133                 {
1134                     // Empty sub format.
1135                     NumFor[nIndex].Info().eScannedType = SvNumFormatType::EMPTY;
1136                 }
1137                 else
1138                 {
1139                     if (!sInsertCalendar.isEmpty())
1140                     {
1141                         sStr = sInsertCalendar + sStr;
1142                     }
1143                     sal_Int32 nStrPos = pSc->ScanFormat( sStr);
1144                     sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1145                     if (nCnt == 0 && nStrPos == 0)  // error
1146                     {
1147                         nStrPos = 1;
1148                     }
1149                     if (nStrPos == 0)               // ok
1150                     {
1151                         // e.g. Thai T speciality
1152                         if (pSc->GetNatNumModifier() && !NumFor[nIndex].GetNatNum().IsSet())
1153                         {
1154                             sStr = "[NatNum"  + OUString::number( pSc->GetNatNumModifier()) + "]" + sStr;
1155                             NumFor[nIndex].SetNatNumNum( pSc->GetNatNumModifier(), false );
1156                         }
1157                         // #i53826# #i42727# For the Thai T speciality we need
1158                         // to freeze the locale and immunize it against
1159                         // conversions during exports, just in case we want to
1160                         // save to Xcl. This disables the feature of being able
1161                         // to convert a NatNum to another locale. You can't
1162                         // have both.
1163                         // FIXME: implement a specialized export conversion
1164                         // that works on tokens (have to tokenize all first)
1165                         // and doesn't use the format string and
1166                         // PutandConvertEntry() to LANGUAGE_ENGLISH_US in
1167                         // sc/source/filter/excel/xestyle.cxx
1168                         // XclExpNumFmtBuffer::WriteFormatRecord().
1169                         LanguageType eLanguage;
1170                         if (NumFor[nIndex].GetNatNum().GetNatNum() == 1 &&
1171                             ((eLanguage = MsLangId::getRealLanguage( eLan)) == LANGUAGE_THAI) &&
1172                             NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW)
1173                         {
1174                             sStr = "[$-" + OUString::number( sal_uInt16(eLanguage), 16 ).toAsciiUpperCase() + "]" + sStr;
1175                             NumFor[nIndex].SetNatNumLang( eLanguage);
1176                         }
1177                         sBuff.remove(nPosOld, nPos - nPosOld);
1178                         sBuff.insert(nPosOld, sStr);
1179                         nPos = nPosOld + sStr.getLength();
1180                         if (nPos < sBuff.getLength())
1181                         {
1182                             sBuff.insert(nPos, ";");
1183                             nPos++;
1184                         }
1185                         else if (nIndex > 0)
1186                         {
1187                             // The last subformat. If it is a trailing text
1188                             // format the omitted subformats act like they were
1189                             // not specified and "inherited" the first format,
1190                             // e.g.  0;@  behaves like  0;-0;0;@
1191                             if (pSc->GetScannedType() == SvNumFormatType::TEXT)
1192                             {
1193                                 // Reset conditions, reverting any set above.
1194                                 if (nIndex == 1)
1195                                     eOp1 = NUMBERFORMAT_OP_NO;
1196                                 else if (nIndex == 2)
1197                                     eOp2 = NUMBERFORMAT_OP_NO;
1198                                 nIndex = 3;
1199                             }
1200                         }
1201                         NumFor[nIndex].Enlarge(nCnt);
1202                         pSc->CopyInfo(&(NumFor[nIndex].Info()), nCnt);
1203                         // type check
1204                         if (nIndex == 0)
1205                         {
1206                             if ( NumFor[nIndex].GetNatNum().GetNatNum() == 12 &&
1207                                     lcl_isNatNum12Currency(NumFor[nIndex].GetNatNum().GetParams()) )
1208                                 eType = SvNumFormatType::CURRENCY;
1209                             else
1210                                 eType = NumFor[nIndex].Info().eScannedType;
1211                         }
1212                         else if (nIndex == 3)
1213                         {   // #77026# Everything recognized IS text
1214                             NumFor[nIndex].Info().eScannedType = SvNumFormatType::TEXT;
1215                         }
1216                         else if ( NumFor[nIndex].Info().eScannedType != eType)
1217                         {
1218                             eType = SvNumFormatType::DEFINED;
1219                         }
1220                     }
1221                     else
1222                     {
1223                         nCheckPos = nPosOld + nStrPos;  // error in string
1224                         bCancel = true;                 // break for
1225                     }
1226                 }
1227             }
1228             else if (eSymbolType == BRACKET_SYMBOLTYPE_ERROR)   // error
1229             {
1230                 nCheckPos = nPosOld;
1231                 bCancel = true;
1232             }
1233             else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) )
1234             {
1235                 nCheckPos = nPosOld + 1;                // error, prefix in string
1236                 bCancel = true;                         // break for
1237             }
1238         }
1239         if ( bCancel && !nCheckPos )
1240         {
1241             nCheckPos = 1;      // nCheckPos is used as an error condition
1242         }
1243         if ( !bCancel )
1244         {
1245             if ( NumFor[nIndex].GetNatNum().IsSet() &&
1246                  NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW )
1247             {
1248                  NumFor[nIndex].SetNatNumLang( eLan );
1249             }
1250         }
1251         if (sBuff.getLength() == nPos)
1252         {
1253             if (nIndex < 3 && rString[rString.getLength()-1] == ';')
1254             {
1255                 // A trailing ';' is significant and specifies the following
1256                 // subformat to be empty. We don't enter the scanning loop
1257                 // above again though.
1258                 // Note that the operators apply to the current last scanned
1259                 // subformat.
1260                 if (nIndex == 0 && eOp1 == NUMBERFORMAT_OP_NO)
1261                 {
1262                     eOp1 = NUMBERFORMAT_OP_GT;  // undefined condition, default: > 0
1263                 }
1264                 else if (nIndex == 1 && eOp2 == NUMBERFORMAT_OP_NO)
1265                 {
1266                     eOp2 = NUMBERFORMAT_OP_LT;  // undefined condition, default: < 0
1267                 }
1268                 NumFor[nIndex+1].Info().eScannedType = SvNumFormatType::EMPTY;
1269                 if (sBuff[nPos-1] != ';')
1270                     sBuff.insert( nPos++, ';');
1271             }
1272             if (nIndex == 2 && eSymbolType == BRACKET_SYMBOLTYPE_FORMAT && sBuff[nPos-1] == ';')
1273             {
1274                 // #83510# A 4th subformat explicitly specified to be empty
1275                 // hides any text. Need the type here for HasTextFormat()
1276                 NumFor[3].Info().eScannedType = SvNumFormatType::TEXT;
1277             }
1278             bCancel = true;
1279         }
1280         if ( NumFor[nIndex].GetNatNum().IsSet() )
1281         {
1282             NumFor[nIndex].SetNatNumDate( bool(NumFor[nIndex].Info().eScannedType & SvNumFormatType::DATE) );
1283         }
1284     }
1285 
1286     if (!nCheckPos && IsSubstituted())
1287     {
1288         // For to be substituted formats the scanned type must match the
1289         // substitute type.
1290         if (IsSystemTimeFormat())
1291         {
1292             if ((eType & ~SvNumFormatType::DEFINED) != SvNumFormatType::TIME)
1293                 nCheckPos = std::max<sal_Int32>( sBuff.indexOf(']') + 1, 1);
1294         }
1295         else if (IsSystemLongDateFormat())
1296         {
1297             if ((eType & ~SvNumFormatType::DEFINED) != SvNumFormatType::DATE)
1298                 nCheckPos = std::max<sal_Int32>( sBuff.indexOf(']') + 1, 1);
1299         }
1300         else
1301             assert(!"unhandled substitute");
1302     }
1303 
1304     if ( bCondition && !nCheckPos )
1305     {
1306         if ( nIndex == 1 && NumFor[0].GetCount() == 0 &&
1307              sBuff[sBuff.getLength() - 1] != ';' )
1308         {
1309             // No format code => GENERAL but not if specified empty
1310             OUString aAdd( pSc->GetStandardName() );
1311             if ( !pSc->ScanFormat( aAdd ) )
1312             {
1313                 sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1314                 if ( nCnt )
1315                 {
1316                     NumFor[0].Enlarge(nCnt);
1317                     pSc->CopyInfo( &(NumFor[0].Info()), nCnt );
1318                     sBuff.append(aAdd);
1319                 }
1320             }
1321         }
1322         else if ( nIndex == 1 && NumFor[nIndex].GetCount() == 0 &&
1323                   sBuff[sBuff.getLength() - 1] != ';' &&
1324                   (NumFor[0].GetCount() > 1 ||
1325                    (NumFor[0].GetCount() == 1 &&
1326                     NumFor[0].Info().nTypeArray[0] != NF_KEY_GENERAL)) )
1327         {
1328             // No trailing second subformat => GENERAL but not if specified empty
1329             // and not if first subformat is GENERAL
1330             OUString aAdd( pSc->GetStandardName() );
1331             if ( !pSc->ScanFormat( aAdd ) )
1332             {
1333                 sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1334                 if ( nCnt )
1335                 {
1336                     NumFor[nIndex].Enlarge(nCnt);
1337                     pSc->CopyInfo( &(NumFor[nIndex].Info()), nCnt );
1338                     sBuff.append(";" + aAdd);
1339                 }
1340             }
1341         }
1342         else if ( nIndex == 2 && NumFor[nIndex].GetCount() == 0 &&
1343                   sBuff[sBuff.getLength() - 1] != ';' &&
1344                   eOp2 != NUMBERFORMAT_OP_NO )
1345         {
1346             // No trailing third subformat => GENERAL but not if specified empty
1347             OUString aAdd( pSc->GetStandardName() );
1348             if ( !pSc->ScanFormat( aAdd ) )
1349             {
1350                 sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1351                 if ( nCnt )
1352                 {
1353                     NumFor[nIndex].Enlarge(nCnt);
1354                     pSc->CopyInfo( &(NumFor[nIndex].Info()), nCnt );
1355                     sBuff.append(";" + aAdd);
1356                 }
1357             }
1358         }
1359     }
1360     rString = sBuff.makeStringAndClear();
1361     sFormatstring = rString;
1362 
1363     if (NumFor[2].GetCount() == 0 && // No third partial string
1364         eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_NO &&
1365         fLimit1 == 0.0 && fLimit2 == 0.0)
1366     {
1367         eOp1 = NUMBERFORMAT_OP_GE; // Add 0 to the first format
1368     }
1369 
1370 }
1371 
~SvNumberformat()1372 SvNumberformat::~SvNumberformat()
1373 {
1374 }
1375 
1376 /**
1377  * Next_Symbol
1378  *
1379  * Splits up the symbols for further processing (by the Turing machine)
1380  *
1381  * Start state = SsStart, * = Special state
1382  * ---------------+-------------------+----------------------------+---------------
1383  *  Old State     | Symbol read       | Event                      | New state
1384  * ---------------+-------------------+----------------------------+---------------
1385  *  SsStart       | "                 | Symbol += Character        | SsGetQuoted
1386  *                | ;                 | Pos--                      | SsGetString
1387  *                | [                 | Symbol += Character        | SsGetBracketed
1388  *                | ]                 | Error                      | SsStop
1389  *                | BLANK             |                            |
1390  *                | Else              | Symbol += Character        | SsGetString
1391  * ---------------+-------------------+----------------------------+---------------
1392  *  SsGetString   | "                 | Symbol += Character        | SsGetQuoted
1393  *                | ;                 |                            | SsStop
1394  *                | Else              | Symbol += Character        |
1395  * ---------------+-------------------+----------------------------+---------------
1396  *  SsGetQuoted   | "                 | Symbol += Character        | SsGetString
1397  *                | Else              | Symbol += Character        |
1398  * ---------------+-------------------+----------------------------+---------------
1399  * SsGetBracketed | <, > =            | del [                      |
1400  *                |                   | Symbol += Character        | SsGetCon
1401  *                | BLANK             |                            |
1402  *                | h, H, m, M, s, S  | Symbol += Character        | SsGetTime
1403  *                | Else              | del [                      |
1404  *                |                   | Symbol += Character        | SsGetPrefix
1405  * ---------------+-------------------+----------------------------+---------------
1406  *  SsGetTime     | ]                 | Symbol += Character        | SsGetString
1407  *                | h, H, m, M, s, S  | Symbol += Character, *     | SsGetString
1408  *                | Else              | del [; Symbol += Character | SsGetPrefix
1409  * ---------------+-------------------+----------------------------+---------------
1410  *  SsGetPrefix   | ]                 |                            | SsStop
1411  *                | Else              | Symbol += Character        |
1412  * ---------------+-------------------+----------------------------+---------------
1413  *  SsGetCon      | >, =              | Symbol += Character        |
1414  *                | ]                 |                            | SsStop
1415  *                | Else              | Error                      | SsStop
1416  * ---------------+-------------------+----------------------------+---------------
1417  */
1418 
1419 namespace {
1420 
1421 enum ScanState
1422 {
1423     SsStop,
1424     SsStart,
1425     SsGetCon,           // condition
1426     SsGetString,        // format string
1427     SsGetPrefix,        // color or NatNumN
1428     SsGetTime,          // [HH] for time
1429     SsGetBracketed,     // any [...] not decided yet
1430     SsGetQuoted         // quoted text
1431 };
1432 
1433 }
1434 
1435 // read a string until ']' and delete spaces in input
1436 // static
ImpGetNumber(OUStringBuffer & rString,sal_Int32 & nPos,OUString & sSymbol)1437 sal_Int32 SvNumberformat::ImpGetNumber(OUStringBuffer& rString,
1438                                        sal_Int32& nPos,
1439                                        OUString& sSymbol)
1440 {
1441     sal_Int32 nStartPos = nPos;
1442     sal_Unicode cToken;
1443     sal_Int32 nLen = rString.getLength();
1444     OUStringBuffer sBuffSymbol;
1445     while ( nPos < nLen )
1446     {
1447         cToken = rString[nPos];
1448         if (cToken == ']')
1449             break;
1450         if (cToken == ' ')
1451         {                                               // delete spaces
1452             rString.remove(nPos,1);
1453             nLen--;
1454         }
1455         else
1456         {
1457             nPos++;
1458             sBuffSymbol.append(cToken);
1459         }
1460     }
1461     sSymbol = sBuffSymbol.makeStringAndClear();
1462     return nPos - nStartPos;
1463 }
1464 
1465 namespace {
1466 
toUniChar(sal_uInt8 n)1467 sal_Unicode toUniChar(sal_uInt8 n)
1468 {
1469     char c;
1470     if (n < 10)
1471     {
1472         c = '0' + n;
1473     }
1474     else
1475     {
1476         c = 'A' + n - 10;
1477     }
1478     return sal_Unicode(c);
1479 }
1480 
IsCombiningSymbol(OUStringBuffer & rStringBuffer,sal_Int32 nPos)1481 bool IsCombiningSymbol( OUStringBuffer& rStringBuffer, sal_Int32 nPos )
1482 {
1483     bool bRet = false;
1484     while (nPos >= 0)
1485     {
1486         switch (rStringBuffer[nPos])
1487         {
1488             case '*':
1489             case '\\':
1490             case '_':
1491                 bRet = !bRet;
1492                 --nPos;
1493                 break;
1494             default:
1495                 return bRet;
1496         }
1497     }
1498     return bRet;
1499 }
1500 
1501 } // namespace
1502 
generateCode() const1503 OUString SvNumberformat::LocaleType::generateCode() const
1504 {
1505     OUStringBuffer aBuf;
1506 #if 0
1507     // TODO: We may re-enable this later. Don't remove it! --Kohei
1508     if (mnNumeralShape)
1509     {
1510         sal_uInt8 nVal = mnNumeralShape;
1511         for (sal_uInt8 i = 0; i < 2; ++i)
1512         {
1513             sal_uInt8 n = (nVal & 0xF0) >> 4;
1514             if (n || aBuf.getLength())
1515             {
1516                 aBuf.append(toUniChar(n));
1517             }
1518             nVal = nVal << 4;
1519         }
1520     }
1521 
1522     if (mnNumeralShape || mnCalendarType)
1523     {
1524         sal_uInt8 nVal = mnCalendarType;
1525         for (sal_uInt8 i = 0; i < 2; ++i)
1526         {
1527             sal_uInt8 n = (nVal & 0xF0) >> 4;
1528             if (n || aBuf.getLength())
1529             {
1530                 aBuf.append(toUniChar(n));
1531             }
1532             nVal = nVal << 4;
1533         }
1534     }
1535 #endif
1536 
1537     sal_uInt16 n16 = static_cast<sal_uInt16>(
1538             (meLanguageWithoutLocaleData == LANGUAGE_DONTKNOW) ? meLanguage :
1539             meLanguageWithoutLocaleData);
1540     if (meLanguage == LANGUAGE_SYSTEM)
1541     {
1542         switch (meSubstitute)
1543         {
1544             case Substitute::NONE:
1545                 ;   // nothing
1546                 break;
1547             case Substitute::TIME:
1548                 n16 = static_cast<sal_uInt16>(LANGUAGE_NF_SYSTEM_TIME);
1549                 break;
1550             case Substitute::LONGDATE:
1551                 n16 = static_cast<sal_uInt16>(LANGUAGE_NF_SYSTEM_DATE);
1552                 break;
1553         }
1554     }
1555     for (sal_uInt8 i = 0; i < 4; ++i)
1556     {
1557         sal_uInt8 n = static_cast<sal_uInt8>((n16 & 0xF000) >> 12);
1558         // Omit leading zeros for consistency.
1559         if (n || !aBuf.isEmpty() || i == 3)
1560         {
1561             aBuf.append(toUniChar(n));
1562         }
1563         n16 = n16 << 4;
1564     }
1565 
1566     return aBuf.makeStringAndClear();
1567 }
1568 
LocaleType()1569 SvNumberformat::LocaleType::LocaleType()
1570     : meLanguage(LANGUAGE_DONTKNOW)
1571     , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW)
1572     , meSubstitute(Substitute::NONE)
1573     , mnNumeralShape(0)
1574     , mnCalendarType(0)
1575 {
1576 }
1577 
LocaleType(sal_uInt32 nRawNum)1578 SvNumberformat::LocaleType::LocaleType(sal_uInt32 nRawNum)
1579     : meLanguage(LANGUAGE_DONTKNOW)
1580     , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW)
1581     , meSubstitute(Substitute::NONE)
1582     , mnNumeralShape(0)
1583     , mnCalendarType(0)
1584 {
1585     meLanguage = static_cast<LanguageType>(nRawNum & 0x0000FFFF);
1586     if (meLanguage == LANGUAGE_NF_SYSTEM_TIME)
1587     {
1588         meSubstitute = Substitute::TIME;
1589         meLanguage = LANGUAGE_SYSTEM;
1590     }
1591     else if (meLanguage == LANGUAGE_NF_SYSTEM_DATE)
1592     {
1593         meSubstitute = Substitute::LONGDATE;
1594         meLanguage = LANGUAGE_SYSTEM;
1595     }
1596     nRawNum = (nRawNum >> 16);
1597     mnCalendarType = static_cast<sal_uInt8>(nRawNum & 0xFF);
1598     nRawNum = (nRawNum >> 8);
1599     mnNumeralShape = static_cast<sal_uInt8>(nRawNum & 0xFF);
1600 }
1601 
isPlainLocale() const1602 bool SvNumberformat::LocaleType::isPlainLocale() const
1603 {
1604     return meSubstitute == Substitute::NONE && !mnCalendarType && !mnNumeralShape;
1605 }
1606 
1607 // static
ImpGetLocaleType(std::u16string_view rString,sal_Int32 & nPos)1608 SvNumberformat::LocaleType SvNumberformat::ImpGetLocaleType(std::u16string_view rString, sal_Int32& nPos )
1609 {
1610     sal_uInt32 nNum = 0;
1611     sal_Unicode cToken = 0;
1612     sal_Int32 nStart = nPos;
1613     sal_Int32 nLen = rString.size();
1614     while ( nPos < nLen && (nPos - nStart < 8) )
1615     {
1616         cToken = rString[nPos];
1617         if (cToken == ']')
1618             break;
1619         if ( '0' <= cToken && cToken <= '9' )
1620         {
1621             nNum *= 16;
1622             nNum += cToken - '0';
1623         }
1624         else if ( 'a' <= cToken && cToken <= 'f' )
1625         {
1626             nNum *= 16;
1627             nNum += cToken - 'a' + 10;
1628         }
1629         else if ( 'A' <= cToken && cToken <= 'F' )
1630         {
1631             nNum *= 16;
1632             nNum += cToken - 'A' + 10;
1633         }
1634         else
1635         {
1636             return LocaleType(); // LANGUAGE_DONTKNOW;
1637         }
1638         ++nPos;
1639     }
1640 
1641     return (cToken == ']' || nPos == nLen) ? LocaleType(nNum) : LocaleType();
1642 }
1643 
lcl_matchKeywordAndGetNumber(std::u16string_view rString,const sal_Int32 nPos,std::u16string_view rKeyword,sal_Int32 & nNumber)1644 static bool lcl_matchKeywordAndGetNumber( std::u16string_view rString, const sal_Int32 nPos,
1645         std::u16string_view rKeyword, sal_Int32 & nNumber )
1646 {
1647     if (0 <= nPos && nPos + static_cast<sal_Int32>(rKeyword.size()) < static_cast<sal_Int32>(rString.size()) && o3tl::matchIgnoreAsciiCase( rString, rKeyword, nPos))
1648     {
1649         nNumber = o3tl::toInt32(rString.substr( nPos + rKeyword.size()));
1650         return true;
1651     }
1652     else
1653     {
1654         nNumber = 0;
1655         return false;
1656     }
1657 }
1658 
ImpNextSymbol(OUStringBuffer & rString,sal_Int32 & nPos,OUString & sSymbol) const1659 short SvNumberformat::ImpNextSymbol(OUStringBuffer& rString,
1660                                     sal_Int32& nPos,
1661                                     OUString& sSymbol) const
1662 {
1663     short eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1664     sal_Unicode cToken;
1665     sal_Unicode cLetter = ' '; // Preliminary result
1666     sal_Int32 nLen = rString.getLength();
1667     ScanState eState = SsStart;
1668     OUStringBuffer sBuffSymbol(128);
1669 
1670     const NfKeywordTable & rKeywords = rScan.GetKeywords();
1671     while (nPos < nLen && eState != SsStop)
1672     {
1673         cToken = rString[nPos];
1674         nPos++;
1675         switch (eState)
1676         {
1677         case SsStart:
1678             if (cToken == '\"')
1679             {
1680                 eState = SsGetQuoted;
1681                 sBuffSymbol.append(cToken);
1682             }
1683             else if (cToken == '[')
1684             {
1685                 eState = SsGetBracketed;
1686                 sBuffSymbol.append(cToken);
1687             }
1688             else if (cToken == ';')
1689             {
1690                 eState = SsGetString;
1691                 nPos--;
1692                 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1693             }
1694             else if (cToken == ']')
1695             {
1696                 eState = SsStop;
1697                 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1698             }
1699             else if (cToken == ' ') // Skip Blanks
1700             {
1701                 nPos--;
1702                 rString.remove(nPos, 1);
1703                 nLen--;
1704             }
1705             else
1706             {
1707                 sBuffSymbol.append(cToken);
1708                 eState = SsGetString;
1709                 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1710             }
1711             break;
1712         case SsGetBracketed:
1713             switch (cToken)
1714             {
1715             case '<':
1716             case '>':
1717             case '=':
1718                 sBuffSymbol.stripStart('[');
1719                 sBuffSymbol.append(cToken);
1720                 cLetter = cToken;
1721                 eState = SsGetCon;
1722                 switch (cToken)
1723                 {
1724                 case '<':
1725                     eSymbolType = NUMBERFORMAT_OP_LT;
1726                     break;
1727                 case '>':
1728                     eSymbolType = NUMBERFORMAT_OP_GT;
1729                     break;
1730                 case '=':
1731                     eSymbolType = NUMBERFORMAT_OP_EQ;
1732                     break;
1733                 }
1734                 break;
1735             case ' ':
1736                 nPos--;
1737                 rString.remove(nPos, 1);
1738                 nLen--;
1739                 break;
1740             case '$' :
1741                 if ( nPos < nLen && rString[nPos] == '-' )
1742                 {
1743                     // [$-xxx] locale
1744                     sBuffSymbol.stripStart('[');
1745                     eSymbolType = BRACKET_SYMBOLTYPE_LOCALE;
1746                     eState = SsGetPrefix;
1747                 }
1748                 else
1749                 {   // currency
1750                     eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1751                     eState = SsGetString;
1752                 }
1753                 sBuffSymbol.append(cToken);
1754                 break;
1755             case '~' :
1756                 // calendarID
1757                 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1758                 sBuffSymbol.append(cToken);
1759                 eState = SsGetString;
1760                 break;
1761             default:
1762             {
1763                 static constexpr OUString aNatNum(u"NATNUM"_ustr);
1764                 static constexpr OUString aDBNum(u"DBNUM"_ustr);
1765                 const OUString aBufStr( rString.toString());
1766                 sal_Int32 nNatNumNum;
1767                 sal_Int32 nDBNum;
1768                 if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aNatNum, nNatNumNum) &&
1769                         0 <= nNatNumNum && nNatNumNum <= 19 )
1770                 {
1771                     sBuffSymbol.stripStart('[');
1772                     sBuffSymbol.append( aBufStr.subView(--nPos, aNatNum.getLength()+1) );
1773                     nPos += aNatNum.getLength()+1;
1774                     //! SymbolType is negative
1775                     eSymbolType = static_cast<short>(BRACKET_SYMBOLTYPE_NATNUM0 - nNatNumNum);
1776                     eState = SsGetPrefix;
1777                 }
1778                 else if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aDBNum, nDBNum) &&
1779                         1 <= nDBNum && nDBNum <= 9 )
1780                 {
1781                     sBuffSymbol.stripStart('[');
1782                     sBuffSymbol.append( aBufStr.subView(--nPos, aDBNum.getLength()+1) );
1783                     nPos += aDBNum.getLength()+1;
1784                     //! SymbolType is negative
1785                     eSymbolType = sal::static_int_cast< short >( BRACKET_SYMBOLTYPE_DBNUM1 - (nDBNum - 1) );
1786                     eState = SsGetPrefix;
1787                 }
1788                 else
1789                 {
1790                     sal_Unicode cUpper = rChrCls().uppercase( aBufStr, nPos-1, 1)[0];
1791                     if (    cUpper == rKeywords[NF_KEY_H][0] ||     // H
1792                             cUpper == rKeywords[NF_KEY_MI][0] ||    // M
1793                             cUpper == rKeywords[NF_KEY_S][0] )      // S
1794                     {
1795                         sBuffSymbol.append(cToken);
1796                         eState = SsGetTime;
1797                         cLetter = cToken;
1798                     }
1799                     else
1800                     {
1801                         sBuffSymbol.stripStart('[');
1802                         sBuffSymbol.append(cToken);
1803                         eSymbolType = BRACKET_SYMBOLTYPE_COLOR;
1804                         eState = SsGetPrefix;
1805                     }
1806                 }
1807             }
1808             }
1809             break;
1810         case SsGetString:
1811             if (cToken == '\"')
1812             {
1813                 eState = SsGetQuoted;
1814                 sBuffSymbol.append(cToken);
1815             }
1816             else if (cToken == ';' && (nPos < 2 || !IsCombiningSymbol( rString, nPos-2)))
1817             {
1818                 eState = SsStop;
1819             }
1820             else
1821             {
1822                 sBuffSymbol.append(cToken);
1823             }
1824             break;
1825         case SsGetQuoted:
1826             if (cToken == '\"')
1827             {
1828                 eState = SsGetString;
1829                 sBuffSymbol.append(cToken);
1830             }
1831             else
1832             {
1833                 sBuffSymbol.append(cToken);
1834             }
1835             break;
1836         case SsGetTime:
1837             if (cToken == ']')
1838             {
1839                 sBuffSymbol.append(cToken);
1840                 eState = SsGetString;
1841                 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1842             }
1843             else
1844             {
1845                 sal_Unicode cUpper = rChrCls().uppercase(rString.toString(), nPos-1, 1)[0];
1846                 if (cUpper == rKeywords[NF_KEY_H][0] ||   // H
1847                     cUpper == rKeywords[NF_KEY_MI][0] ||  // M
1848                     cUpper == rKeywords[NF_KEY_S][0] )    // S
1849                 {
1850                     if (cLetter == cToken)
1851                     {
1852                         sBuffSymbol.append(cToken);
1853                         cLetter = ' ';
1854                     }
1855                     else
1856                     {
1857                         sBuffSymbol.stripStart('[');
1858                         sBuffSymbol.append(cToken);
1859                         eState = SsGetPrefix;
1860                     }
1861                 }
1862                 else
1863                 {
1864                     sBuffSymbol.stripStart('[');
1865                     sBuffSymbol.append(cToken);
1866                     eSymbolType = BRACKET_SYMBOLTYPE_COLOR;
1867                     eState = SsGetPrefix;
1868                 }
1869             }
1870             break;
1871         case SsGetCon:
1872             switch (cToken)
1873             {
1874             case '<':
1875                 eState = SsStop;
1876                 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1877                 break;
1878             case '>':
1879                 if (cLetter == '<')
1880                 {
1881                     sBuffSymbol.append(cToken);
1882                     cLetter = ' ';
1883                     eState = SsStop;
1884                     eSymbolType = NUMBERFORMAT_OP_NE;
1885                 }
1886                 else
1887                 {
1888                     eState = SsStop;
1889                     eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1890                 }
1891                 break;
1892             case '=':
1893                 if (cLetter == '<')
1894                 {
1895                     sBuffSymbol.append(cToken);
1896                     cLetter = ' ';
1897                     eSymbolType = NUMBERFORMAT_OP_LE;
1898                 }
1899                 else if (cLetter == '>')
1900                 {
1901                     sBuffSymbol.append(cToken);
1902                     cLetter = ' ';
1903                     eSymbolType = NUMBERFORMAT_OP_GE;
1904                 }
1905                 else
1906                 {
1907                     eState = SsStop;
1908                     eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1909                 }
1910                 break;
1911             case ' ':
1912                 nPos--;
1913                 rString.remove(nPos,1);
1914                 nLen--;
1915                 break;
1916             default:
1917                 eState = SsStop;
1918                 nPos--;
1919                 break;
1920             }
1921             break;
1922         case SsGetPrefix:
1923             if (cToken == ']')
1924             {
1925                 eState = SsStop;
1926             }
1927             else
1928             {
1929                 sBuffSymbol.append(cToken);
1930             }
1931             break;
1932         default:
1933             break;
1934         } // of switch
1935     } // of while
1936     sSymbol = sBuffSymbol.makeStringAndClear();
1937     return eSymbolType;
1938 }
1939 
ConvertLanguage(SvNumberFormatter & rConverter,LanguageType eConvertFrom,LanguageType eConvertTo)1940 void SvNumberformat::ConvertLanguage( SvNumberFormatter& rConverter,
1941                                       LanguageType eConvertFrom,
1942                                       LanguageType eConvertTo )
1943 {
1944     sal_Int32 nCheckPos;
1945     sal_uInt32 nKey;
1946     SvNumFormatType nType = eType;
1947     OUString aFormatString( sFormatstring );
1948     rConverter.PutandConvertEntry( aFormatString, nCheckPos, nType,
1949                                    nKey, eConvertFrom, eConvertTo, false);
1950     const SvNumberformat* pFormat = rConverter.GetEntry( nKey );
1951     DBG_ASSERT( pFormat, "SvNumberformat::ConvertLanguage: Conversion without format" );
1952     if ( pFormat )
1953     {
1954         ImpCopyNumberformat( *pFormat );
1955         // Reset values taken over from Formatter/Scanner
1956         // pColor still points to table in temporary Formatter/Scanner
1957         for (ImpSvNumFor & rFormatter : NumFor)
1958         {
1959             OUString aColorName = rFormatter.GetColorName();
1960             const Color* pColor = rScan.GetColor( aColorName );
1961             rFormatter.SetColor( pColor, aColorName );
1962         }
1963     }
1964 }
1965 
HasNewCurrency() const1966 bool SvNumberformat::HasNewCurrency() const
1967 {
1968     for (const auto & j : NumFor)
1969     {
1970         if ( j.HasNewCurrency() )
1971         {
1972             return true;
1973         }
1974     }
1975     return false;
1976 }
1977 
GetNewCurrencySymbol(OUString & rSymbol,OUString & rExtension) const1978 bool SvNumberformat::GetNewCurrencySymbol( OUString& rSymbol,
1979                                            OUString& rExtension ) const
1980 {
1981     for (const auto & j : NumFor)
1982     {
1983         if ( j.GetNewCurrencySymbol( rSymbol, rExtension ) )
1984         {
1985             return true;
1986         }
1987     }
1988     rSymbol.clear();
1989     rExtension.clear();
1990     return false;
1991 }
1992 
1993 // static
StripNewCurrencyDelimiters(const OUString & rStr)1994 OUString SvNumberformat::StripNewCurrencyDelimiters( const OUString& rStr )
1995 {
1996     OUStringBuffer aTmp(rStr.getLength());
1997     sal_Int32 nStartPos, nPos, nLen;
1998     nLen = rStr.getLength();
1999     nStartPos = 0;
2000     while ( (nPos = rStr.indexOf( "[$", nStartPos )) >= 0 )
2001     {
2002         sal_Int32 nEnd;
2003         if ( (nEnd = GetQuoteEnd( rStr, nPos )) >= 0 )
2004         {
2005             aTmp.append(rStr.subView( nStartPos, ++nEnd - nStartPos ));
2006             nStartPos = nEnd;
2007         }
2008         else
2009         {
2010             aTmp.append(rStr.subView(nStartPos, nPos - nStartPos) );
2011             nStartPos = nPos + 2;
2012             sal_Int32 nDash;
2013             nEnd = nStartPos - 1;
2014             do
2015             {
2016                 nDash = rStr.indexOf( '-', ++nEnd );
2017                 nEnd = GetQuoteEnd( rStr, nDash );
2018             }
2019             while ( nEnd >= 0 );
2020             sal_Int32 nClose;
2021             nEnd = nStartPos - 1;
2022             do
2023             {
2024                 nClose = rStr.indexOf( ']', ++nEnd );
2025                 nEnd = GetQuoteEnd( rStr, nClose );
2026             }
2027             while ( nEnd >= 0 );
2028 
2029             if(nClose < 0)
2030             {
2031                 /* there should always be a closing ]
2032                  * but the old String class would have hidden
2033                  * that. so be conservative too
2034                  */
2035                 nClose = nLen;
2036             }
2037 
2038             nPos = nClose;
2039             if(nDash >= 0 && nDash < nClose)
2040             {
2041                 nPos = nDash;
2042             }
2043             aTmp.append(rStr.subView(nStartPos, nPos - nStartPos) );
2044             nStartPos = nClose + 1;
2045         }
2046     }
2047     if ( nLen > nStartPos )
2048     {
2049         aTmp.append(rStr.subView(nStartPos, nLen - nStartPos) );
2050     }
2051     return aTmp.makeStringAndClear();
2052 }
2053 
ImpGetOutputStandard(double & fNumber,OUStringBuffer & rOutString,const NativeNumberWrapper & rNatNum) const2054 void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUStringBuffer& rOutString,
2055                                           const NativeNumberWrapper& rNatNum) const
2056 {
2057     OUString sTemp;
2058     ImpGetOutputStandard(fNumber, sTemp, rNatNum);
2059     rOutString = sTemp;
2060 }
2061 
ImpGetOutputStandard(double & fNumber,OUString & rOutString,const NativeNumberWrapper & rNatNum) const2062 void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUString& rOutString,
2063                                           const NativeNumberWrapper& rNatNum) const
2064 {
2065     sal_uInt16 nStandardPrec = rScan.GetStandardPrec();
2066 
2067     if ( fabs(fNumber) > EXP_ABS_UPPER_BOUND )
2068     {
2069         nStandardPrec = ::std::min(nStandardPrec, static_cast<sal_uInt16>(14)); // limits to 14 decimals
2070         rOutString = ::rtl::math::doubleToUString( fNumber,
2071                                                   rtl_math_StringFormat_E2, nStandardPrec /*2*/,
2072                                                   GetCurrentLanguageData().GetNumDecimalSep()[0]);
2073     }
2074     else
2075     {
2076         ImpGetOutputStdToPrecision(fNumber, rOutString, nStandardPrec, rNatNum);
2077     }
2078 }
2079 
2080 namespace
2081 {
2082 
2083 template<typename T>
checkForAll0s(const T & rString,sal_Int32 nIdx=0)2084 bool checkForAll0s(const T& rString, sal_Int32 nIdx=0)
2085 {
2086     if (nIdx>=rString.getLength())
2087         return false;
2088 
2089     do
2090     {
2091         if (rString[nIdx]!='0')
2092             return false;
2093     }
2094     while (++nIdx<rString.getLength());
2095 
2096     return true;
2097 }
2098 
impTransliterateImpl(const OUString & rStr,const SvNumberNatNum & rNum,const NativeNumberWrapper & rNatNum)2099 OUString impTransliterateImpl(const OUString& rStr,
2100                               const SvNumberNatNum& rNum,
2101                               const NativeNumberWrapper& rNatNum)
2102 {
2103     css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
2104     return rNatNum.getNativeNumberStringParams(rStr, aLocale, rNum.GetNatNum(), rNum.GetParams());
2105 }
2106 
impTransliterateImpl(OUStringBuffer & rStr,const SvNumberNatNum & rNum,const NativeNumberWrapper & rNatNum)2107 void impTransliterateImpl(OUStringBuffer& rStr,
2108                           const SvNumberNatNum& rNum,
2109                           const NativeNumberWrapper& rNatNum)
2110 {
2111     css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
2112 
2113     rStr = rNatNum.getNativeNumberStringParams(
2114         OUString::unacquired(rStr), aLocale, rNum.GetNatNum(), rNum.GetParams());
2115 }
2116 
impTransliterate(const OUString & rStr,const SvNumberNatNum & rNum,const NativeNumberWrapper & rNatNum)2117 OUString impTransliterate(const OUString& rStr, const SvNumberNatNum& rNum, const NativeNumberWrapper& rNatNum)
2118 {
2119     return rNum.IsComplete() ? impTransliterateImpl(rStr, rNum, rNatNum) : rStr;
2120 }
2121 
impTransliterate(OUStringBuffer & rStr,const SvNumberNatNum & rNum,const NativeNumberWrapper & rNatNum)2122 void impTransliterate(OUStringBuffer& rStr, const SvNumberNatNum& rNum, const NativeNumberWrapper& rNatNum)
2123 {
2124     if(rNum.IsComplete())
2125     {
2126         impTransliterateImpl(rStr, rNum, rNatNum);
2127     }
2128 }
2129 
2130 }
2131 
ImpGetOutputStdToPrecision(double & rNumber,OUString & rOutString,sal_uInt16 nPrecision,const NativeNumberWrapper & rNatNum) const2132 void SvNumberformat::ImpGetOutputStdToPrecision(double& rNumber, OUString& rOutString, sal_uInt16 nPrecision,
2133                                                 const NativeNumberWrapper& rNatNum) const
2134 {
2135     // Make sure the precision doesn't go over the maximum allowable precision.
2136     nPrecision = ::std::min(UPPER_PRECISION, nPrecision);
2137 
2138     // We decided to strip trailing zeros unconditionally, since binary
2139     // double-precision rounding error makes it impossible to determine e.g.
2140     // whether 844.10000000000002273737 is what the user has typed, or the
2141     // user has typed 844.1 but IEEE 754 represents it that way internally.
2142 
2143     rOutString = ::rtl::math::doubleToUString( rNumber,
2144                                                rtl_math_StringFormat_F, nPrecision /*2*/,
2145                                                GetCurrentLanguageData().GetNumDecimalSep()[0], true );
2146     if (rOutString[0] == '-' && checkForAll0s(rOutString, 1))
2147     {
2148         rOutString = comphelper::string::stripStart(rOutString, '-'); // not -0
2149     }
2150     rOutString = ::impTransliterate(rOutString, NumFor[0].GetNatNum(), rNatNum);
2151 }
2152 
ImpGetOutputInputLine(double fNumber,OUString & OutString) const2153 void SvNumberformat::ImpGetOutputInputLine(double fNumber, OUString& OutString) const
2154 {
2155     bool bModified = false;
2156     if ( (eType & SvNumFormatType::PERCENT) && (fabs(fNumber) < D_MAX_D_BY_100))
2157     {
2158         if (fNumber == 0.0)
2159         {
2160             OutString = "0%";
2161             return;
2162         }
2163         fNumber *= 100;
2164         bModified = true;
2165     }
2166 
2167     if (fNumber == 0.0)
2168     {
2169         OutString = "0";
2170         return;
2171     }
2172 
2173     OutString = ::rtl::math::doubleToUString( fNumber,
2174                                               rtl_math_StringFormat_Automatic,
2175                                               rtl_math_DecimalPlaces_Max,
2176                                               GetCurrentLanguageData().GetNumDecimalSep()[0], true );
2177 
2178     if ( eType & SvNumFormatType::PERCENT && bModified)
2179     {
2180         OutString += "%";
2181     }
2182 }
2183 
ImpCheckCondition(double fNumber,double fLimit,SvNumberformatLimitOps eOp)2184 short SvNumberformat::ImpCheckCondition(double fNumber,
2185                                         double fLimit,
2186                                         SvNumberformatLimitOps eOp)
2187 {
2188     switch(eOp)
2189     {
2190     case NUMBERFORMAT_OP_NO:
2191         return -1;
2192     case NUMBERFORMAT_OP_EQ:
2193         return static_cast<short>(fNumber == fLimit);
2194     case NUMBERFORMAT_OP_NE:
2195         return static_cast<short>(fNumber != fLimit);
2196     case NUMBERFORMAT_OP_LT:
2197         return static_cast<short>(fNumber <  fLimit);
2198     case NUMBERFORMAT_OP_LE:
2199         return static_cast<short>(fNumber <= fLimit);
2200     case NUMBERFORMAT_OP_GT:
2201         return static_cast<short>(fNumber >  fLimit);
2202     case NUMBERFORMAT_OP_GE:
2203         return static_cast<short>(fNumber >= fLimit);
2204     default:
2205         return -1;
2206     }
2207 }
2208 
lcl_appendStarFillChar(OUStringBuffer & rBuf,std::u16string_view rStr)2209 static bool lcl_appendStarFillChar( OUStringBuffer& rBuf, std::u16string_view rStr )
2210 {
2211     // Right during user input the star symbol is the very
2212     // last character before the user enters another one.
2213     if (rStr.size() > 1)
2214     {
2215         rBuf.append(u'\x001B');
2216         rBuf.append(rStr[1]);
2217         return true;
2218     }
2219     return false;
2220 }
2221 
lcl_insertStarFillChar(OUStringBuffer & rBuf,sal_Int32 nPos,std::u16string_view rStr)2222 static bool lcl_insertStarFillChar( OUStringBuffer& rBuf, sal_Int32 nPos, std::u16string_view rStr )
2223 {
2224     if (rStr.size() > 1)
2225     {
2226         rBuf.insert( nPos, rStr[1]);
2227         rBuf.insert( nPos, u'\x001B');
2228         return true;
2229     }
2230     return false;
2231 }
2232 
GetOutputString(std::u16string_view sString,OUString & OutString,const Color ** ppColor,bool bStarFlag) const2233 void SvNumberformat::GetOutputString(std::u16string_view sString,
2234                                      OUString& OutString,
2235                                      const Color** ppColor,
2236                                      bool bStarFlag) const
2237 {
2238     OUStringBuffer sOutBuff;
2239     sal_uInt16 nIx;
2240     if (eType & SvNumFormatType::TEXT)
2241     {
2242         nIx = 0;
2243     }
2244     else if (NumFor[3].GetCount() > 0)
2245     {
2246         nIx = 3;
2247     }
2248     else
2249     {
2250         *ppColor = nullptr; // no change of color
2251         return;
2252     }
2253     *ppColor = NumFor[nIx].GetColor();
2254     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2255     if (rInfo.eScannedType == SvNumFormatType::TEXT)
2256     {
2257         const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2258         for (sal_uInt16 i = 0; i < nCnt; i++)
2259         {
2260             switch (rInfo.nTypeArray[i])
2261             {
2262             case NF_SYMBOLTYPE_STAR:
2263                 if( bStarFlag )
2264                 {
2265                     lcl_appendStarFillChar( sOutBuff, rInfo.sStrArray[i]);
2266                 }
2267                 break;
2268             case NF_SYMBOLTYPE_BLANK:
2269                 if (rInfo.sStrArray[i].getLength() >= 2)
2270                     InsertBlanks( sOutBuff, sOutBuff.getLength(), rInfo.sStrArray[i][1] );
2271                 break;
2272             case NF_KEY_GENERAL :   // #77026# "General" is the same as "@"
2273             case NF_SYMBOLTYPE_DEL :
2274                 sOutBuff.append(sString);
2275                 break;
2276             default:
2277                 sOutBuff.append(rInfo.sStrArray[i]);
2278             }
2279         }
2280     }
2281     OutString = sOutBuff.makeStringAndClear();
2282 }
2283 
2284 namespace {
2285 
lcl_GetOutputStringScientific(double fNumber,sal_uInt16 nCharCount,const SvNFLanguageData & rLanguageData,OUString & rOutString)2286 void lcl_GetOutputStringScientific(double fNumber, sal_uInt16 nCharCount,
2287                                    const SvNFLanguageData& rLanguageData, OUString& rOutString)
2288 {
2289     bool bSign = std::signbit(fNumber);
2290 
2291     // 1.000E+015 (one digit and the decimal point, and the two chars +
2292     // nExpDigit for the exponential part, totalling 6 or 7).
2293     double fExp = log10( fabs(fNumber) );
2294     if( fExp < 0.0 )
2295       fExp = 1.0 - fExp;
2296     sal_uInt16 nCharFormat = 6 + (fExp >= 100.0 ? 1 : 0);
2297     sal_uInt16 nPrec = nCharCount > nCharFormat ? nCharCount - nCharFormat : 0;
2298     if (nPrec && bSign)
2299     {
2300         // Make room for the negative sign.
2301         --nPrec;
2302     }
2303     nPrec = ::std::min(nPrec, static_cast<sal_uInt16>(14)); // limit to 14 decimals.
2304 
2305     rOutString = ::rtl::math::doubleToUString(fNumber, rtl_math_StringFormat_E2,
2306                                               nPrec, rLanguageData.GetNumDecimalSep()[0], true );
2307 }
2308 
lcl_GetPercentString(const ImpSvNumberformatInfo & rInfo,sal_uInt16 nCnt)2309 OUString lcl_GetPercentString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2310 {
2311     sal_Int32 i;
2312     OUStringBuffer aPercentString;
2313     for( i = 0; i < nCnt; i++ )
2314     {
2315         if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_PERCENT )
2316         {
2317             aPercentString.append( rInfo.sStrArray[i] );
2318             bool bStringFound = false;
2319             for( i--; i >= 0 && rInfo.nTypeArray[i] == NF_SYMBOLTYPE_STRING ; i-- )
2320             {
2321                 if( !bStringFound )
2322                 {
2323                     bStringFound = true;
2324                     aPercentString.insert( 0, "\"" );
2325                 }
2326                 aPercentString.insert( 0, rInfo.sStrArray[i] );
2327             }
2328             i = nCnt;
2329             if( bStringFound )
2330                 aPercentString.insert( 0, "\"" );
2331         }
2332     }
2333     return aPercentString.makeStringAndClear();
2334 }
2335 
lcl_GetDenominatorString(const ImpSvNumberformatInfo & rInfo,sal_uInt16 nCnt)2336 OUString lcl_GetDenominatorString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2337 {
2338     sal_Int32 i;
2339     OUStringBuffer aDenominatorString;
2340     for( i = 0; i < nCnt; i++ )
2341     {
2342         if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC )
2343         {
2344             while ( ( ++i < nCnt ) && rInfo.nTypeArray[i] != NF_SYMBOLTYPE_FRAC_FDIV
2345                                    && rInfo.nTypeArray[i] != NF_SYMBOLTYPE_DIGIT );
2346             for( ; i < nCnt; i++ )
2347             {
2348                 if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC_FDIV || rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT )
2349                     aDenominatorString.append( rInfo.sStrArray[i] );
2350                 else
2351                     i = nCnt;
2352             }
2353         }
2354     }
2355     return aDenominatorString.makeStringAndClear();
2356 }
2357 
lcl_GetNumeratorString(const ImpSvNumberformatInfo & rInfo,sal_uInt16 nCnt)2358 OUString lcl_GetNumeratorString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2359 {
2360     sal_Int32 i;
2361     OUStringBuffer aNumeratorString;
2362     for( i = 0; i < nCnt; i++ )
2363     {
2364         if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC )
2365         {
2366             for( i--; i >= 0 && rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT ; i-- )
2367             {
2368                 aNumeratorString.insert( 0, rInfo.sStrArray[i] );
2369             }
2370             i = nCnt;
2371         }
2372     }
2373     return aNumeratorString.makeStringAndClear();
2374 }
2375 
lcl_GetFractionIntegerString(const ImpSvNumberformatInfo & rInfo,sal_uInt16 nCnt)2376 OUString lcl_GetFractionIntegerString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2377 {
2378     sal_Int32 i;
2379     OUStringBuffer aIntegerString;
2380     for( i = 0; i < nCnt; i++ )
2381     {
2382         if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRACBLANK )
2383         {
2384             for( i--; i >= 0 && ( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT
2385                                || rInfo.nTypeArray[i] == NF_SYMBOLTYPE_THSEP ); i-- )
2386             {
2387                 aIntegerString.insert( 0, rInfo.sStrArray[i] );
2388             }
2389             i = nCnt;
2390         }
2391     }
2392     return aIntegerString.makeStringAndClear();
2393 }
2394 
lcl_GetIntegerFractionDelimiterString(const ImpSvNumberformatInfo & rInfo,sal_uInt16 nCnt)2395 OUString lcl_GetIntegerFractionDelimiterString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2396 {
2397     sal_uInt16 i;
2398     for( i = 0; i < nCnt; i++ )
2399     {
2400         if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRACBLANK )
2401         {
2402             return rInfo.sStrArray[i];
2403         }
2404     }
2405     return OUString();
2406 }
2407 
2408 }
2409 
GetPercentString(sal_uInt16 nNumFor) const2410 OUString SvNumberformat::GetPercentString( sal_uInt16 nNumFor ) const
2411 {
2412     const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2413     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2414     return lcl_GetPercentString( rInfo, nCnt );
2415 }
2416 
GetDenominatorString(sal_uInt16 nNumFor) const2417 OUString SvNumberformat::GetDenominatorString( sal_uInt16 nNumFor ) const
2418 {
2419     const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2420     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2421     return lcl_GetDenominatorString( rInfo, nCnt );
2422 }
2423 
GetNumeratorString(sal_uInt16 nNumFor) const2424 OUString SvNumberformat::GetNumeratorString( sal_uInt16 nNumFor ) const
2425 {
2426     const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2427     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2428     return lcl_GetNumeratorString( rInfo, nCnt );
2429 }
2430 
GetIntegerFractionDelimiterString(sal_uInt16 nNumFor) const2431 OUString SvNumberformat::GetIntegerFractionDelimiterString( sal_uInt16 nNumFor ) const
2432 {
2433     const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2434     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2435     return lcl_GetIntegerFractionDelimiterString( rInfo, nCnt );
2436 }
2437 
GetOutputString(double fNumber,sal_uInt16 nCharCount,OUString & rOutString,const NativeNumberWrapper & rNatNum) const2438 bool SvNumberformat::GetOutputString(double fNumber, sal_uInt16 nCharCount, OUString& rOutString, const NativeNumberWrapper& rNatNum) const
2439 {
2440     if (eType != SvNumFormatType::NUMBER)
2441     {
2442         return false;
2443     }
2444     double fTestNum = fNumber;
2445     bool bSign = std::signbit(fTestNum);
2446     if (bSign)
2447     {
2448         fTestNum = -fTestNum;
2449     }
2450     if (fTestNum < EXP_LOWER_BOUND)
2451     {
2452         lcl_GetOutputStringScientific(fNumber, nCharCount, GetCurrentLanguageData(), rOutString);
2453         return true;
2454     }
2455 
2456     double fExp = log10(fTestNum);
2457     // Values < 1.0 always have one digit before the decimal point.
2458     sal_uInt16 nDigitPre = fExp >= 0.0 ? static_cast<sal_uInt16>(ceil(fExp)) : 1;
2459 
2460     if (nDigitPre > 15)
2461     {
2462         lcl_GetOutputStringScientific(fNumber, nCharCount, GetCurrentLanguageData(), rOutString);
2463         return true;
2464     }
2465 
2466     sal_uInt16 nPrec = nCharCount >= nDigitPre ? nCharCount - nDigitPre : 0;
2467     if (nPrec && bSign)
2468     {
2469         // Subtract the negative sign.
2470         --nPrec;
2471     }
2472     if (nPrec)
2473     {
2474         // Subtract the decimal point.
2475         --nPrec;
2476     }
2477     ImpGetOutputStdToPrecision(fNumber, rOutString, nPrec, rNatNum);
2478     if (rOutString.getLength() > nCharCount)
2479     {
2480         // String still wider than desired.  Switch to scientific notation.
2481         lcl_GetOutputStringScientific(fNumber, nCharCount, GetCurrentLanguageData(), rOutString);
2482     }
2483     return true;
2484 }
2485 
GetSubformatIndex(double fNumber) const2486 sal_uInt16 SvNumberformat::GetSubformatIndex (double fNumber ) const
2487 {
2488     sal_uInt16 nIx; // Index of the partial format
2489     double fLimit_1 = fLimit1;
2490     short nCheck = ImpCheckCondition(fNumber, fLimit_1, eOp1);
2491     if (nCheck == -1 || nCheck == 1) // Only 1 String or True
2492     {
2493         nIx = 0;
2494     }
2495     else
2496     {
2497         double fLimit_2 = fLimit2;
2498         nCheck = ImpCheckCondition(fNumber, fLimit_2, eOp2);
2499         if (nCheck == -1 || nCheck == 1)
2500         {
2501             nIx = 1;
2502         }
2503         else
2504         {
2505             nIx = 2;
2506         }
2507     }
2508     return nIx;
2509 }
2510 
GetOutputString(double fNumber,OUString & OutString,const Color ** ppColor,const NativeNumberWrapper & rNatNum,const SvNFLanguageData & rCurrentLang,bool bStarFlag) const2511 bool SvNumberformat::GetOutputString(double fNumber,
2512                                      OUString& OutString,
2513                                      const Color** ppColor,
2514                                      const NativeNumberWrapper& rNatNum,
2515                                      const SvNFLanguageData& rCurrentLang,
2516                                      bool bStarFlag) const
2517 {
2518     bool bRes = false;
2519     OutString.clear();
2520     *ppColor = nullptr; // No color change
2521     if (eType & SvNumFormatType::LOGICAL && sFormatstring == rScan.GetKeywords()[NF_KEY_BOOLEAN])
2522     {
2523         if (fNumber)
2524         {
2525             OutString = rScan.GetTrueString();
2526         }
2527         else
2528         {
2529             OutString = rScan.GetFalseString();
2530         }
2531         return false;
2532     }
2533     OUStringBuffer sBuff(64);
2534     if (eType & SvNumFormatType::TEXT)
2535     {
2536         ImpGetOutputStandard(fNumber, sBuff, rNatNum);
2537         OutString = sBuff.makeStringAndClear();
2538         return false;
2539     }
2540     bool bHadStandard = false;
2541     if (bStandard) // Individual standard formats
2542     {
2543         if (rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION) // All number format InputLine
2544         {
2545             ImpGetOutputInputLine(fNumber, OutString);
2546             return false;
2547         }
2548         switch (eType)
2549         {
2550         case SvNumFormatType::NUMBER: // Standard number format
2551             if (rScan.GetStandardPrec() == SvNumberFormatter::UNLIMITED_PRECISION)
2552             {
2553                 if (std::signbit(fNumber))
2554                 {
2555                     if (!(fNumber < 0.0))
2556                         fNumber = -fNumber;     // do not display -0.0
2557                 }
2558                 if (fNumber == 0.0)
2559                 {
2560                     OutString = "0";
2561                 }
2562                 else if (fNumber < 1.0 && fNumber > -1.0)
2563                 {
2564                     // Decide whether to display as 0.000000123... or 1.23...e-07
2565                     bool bFix = (fNumber < -EXP_LOWER_BOUND || EXP_LOWER_BOUND < fNumber);
2566                     if (!bFix)
2567                     {
2568                         // Arbitrary, not too many 0s visually, start E2 at 1E-10.
2569                         constexpr sal_Int32 kMaxExp = 9;
2570                         const sal_Int32 nExp = static_cast<sal_Int32>(ceil( -log10( fabs( fNumber))));
2571                         if (nExp <= kMaxExp && rtl::math::approxEqual(
2572                                     rtl::math::round( fNumber, 16), rtl::math::round( fNumber, nExp + 16)))
2573                         {
2574                             // Not too many significant digits or accuracy
2575                             // artefacts, otherwise leave everything to E2
2576                             // format.
2577                             bFix = true;
2578                         }
2579                     }
2580                     if (bFix)
2581                         OutString = ::rtl::math::doubleToUString( fNumber,
2582                                 rtl_math_StringFormat_F,
2583                                 rtl_math_DecimalPlaces_Max,
2584                                 GetCurrentLanguageData().GetNumDecimalSep()[0], true);
2585                     else
2586                         OutString = ::rtl::math::doubleToUString( fNumber,
2587                                 rtl_math_StringFormat_E2,
2588                                 rtl_math_DecimalPlaces_Max,
2589                                 GetCurrentLanguageData().GetNumDecimalSep()[0], true);
2590                 }
2591                 else
2592                 {
2593                     OutString = ::rtl::math::doubleToUString( fNumber,
2594                                 rtl_math_StringFormat_Automatic,
2595                                 rtl_math_DecimalPlaces_Max,
2596                                 GetCurrentLanguageData().GetNumDecimalSep()[0], true);
2597                 }
2598                 return false;
2599             }
2600             ImpGetOutputStandard(fNumber, sBuff, rNatNum);
2601             bHadStandard = true;
2602             break;
2603         case SvNumFormatType::DATE:
2604             bRes |= ImpGetDateOutput(fNumber, 0, bStarFlag, rNatNum, rCurrentLang, sBuff);
2605             bHadStandard = true;
2606             break;
2607         case SvNumFormatType::TIME:
2608             bRes |= ImpGetTimeOutput(fNumber, 0, bStarFlag, rNatNum, rCurrentLang, sBuff);
2609             bHadStandard = true;
2610             break;
2611         case SvNumFormatType::DATETIME:
2612             bRes |= ImpGetDateTimeOutput(fNumber, 0, bStarFlag, rNatNum, rCurrentLang, sBuff);
2613             bHadStandard = true;
2614             break;
2615         default: break;
2616         }
2617     }
2618     if ( !bHadStandard )
2619     {
2620         sal_uInt16 nIx = GetSubformatIndex ( fNumber ); // Index of the partial format
2621         if (fNumber < 0.0 &&
2622                 ((nIx == 0 && IsFirstSubformatRealNegative()) || // 1st, usually positive subformat
2623                  (nIx == 1 && IsSecondSubformatRealNegative()))) // 2nd, usually negative subformat
2624         {
2625             fNumber = -fNumber; // eliminate sign
2626         }
2627         *ppColor = NumFor[nIx].GetColor();
2628         const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2629         const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2630         if (nCnt == 0 && rInfo.eScannedType == SvNumFormatType::EMPTY)
2631         {
2632             return false; // Empty => nothing
2633         }
2634         else if (nCnt == 0) // Else Standard Format
2635         {
2636             ImpGetOutputStandard(fNumber, sBuff, rNatNum);
2637             OutString = sBuff.makeStringAndClear();
2638             return false;
2639         }
2640         switch (rInfo.eScannedType)
2641         {
2642         case SvNumFormatType::TEXT:
2643         case SvNumFormatType::DEFINED:
2644             for (sal_uInt16 i = 0; i < nCnt; i++)
2645             {
2646                 switch (rInfo.nTypeArray[i])
2647                 {
2648                 case NF_SYMBOLTYPE_STAR:
2649                     if( bStarFlag )
2650                     {
2651                         bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
2652                     }
2653                     break;
2654                 case NF_SYMBOLTYPE_BLANK:
2655                     if (rInfo.sStrArray[i].getLength() >= 2)
2656                         InsertBlanks(sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
2657                     break;
2658                 case NF_SYMBOLTYPE_STRING:
2659                 case NF_SYMBOLTYPE_CURRENCY:
2660                     sBuff.append(rInfo.sStrArray[i]);
2661                     break;
2662                 case NF_SYMBOLTYPE_THSEP:
2663                     if (rInfo.nThousand == 0)
2664                     {
2665                         sBuff.append(rInfo.sStrArray[i]);
2666                     }
2667                     break;
2668                 default:
2669                     break;
2670                 }
2671             }
2672             break;
2673         case SvNumFormatType::DATE:
2674             bRes |= ImpGetDateOutput(fNumber, nIx, bStarFlag, rNatNum, rCurrentLang, sBuff);
2675             break;
2676         case SvNumFormatType::TIME:
2677             bRes |= ImpGetTimeOutput(fNumber, nIx, bStarFlag, rNatNum, rCurrentLang, sBuff);
2678                 break;
2679         case SvNumFormatType::DATETIME:
2680             bRes |= ImpGetDateTimeOutput(fNumber, nIx, bStarFlag, rNatNum, rCurrentLang, sBuff);
2681             break;
2682         case SvNumFormatType::NUMBER:
2683         case SvNumFormatType::PERCENT:
2684         case SvNumFormatType::CURRENCY:
2685             bRes |= ImpGetNumberOutput(fNumber, nIx, bStarFlag, rNatNum, sBuff);
2686             break;
2687         case SvNumFormatType::LOGICAL:
2688             bRes |= ImpGetLogicalOutput(fNumber, nIx, rNatNum, sBuff);
2689             break;
2690         case SvNumFormatType::FRACTION:
2691             bRes |= ImpGetFractionOutput(fNumber, nIx, bStarFlag, rNatNum, sBuff);
2692             break;
2693         case SvNumFormatType::SCIENTIFIC:
2694             bRes |= ImpGetScientificOutput(fNumber, nIx, bStarFlag, rNatNum, sBuff);
2695             break;
2696         default: break;
2697         }
2698     }
2699     OutString = sBuff.makeStringAndClear();
2700     return bRes;
2701 }
2702 
ImpGetScientificOutput(double fNumber,sal_uInt16 nIx,bool bStarFlag,const NativeNumberWrapper & rNatNum,OUStringBuffer & sStr) const2703 bool SvNumberformat::ImpGetScientificOutput(double fNumber,
2704                                             sal_uInt16 nIx,
2705                                             bool bStarFlag,
2706                                             const NativeNumberWrapper& rNatNum,
2707                                             OUStringBuffer& sStr) const
2708 {
2709     bool bRes = false;
2710     bool bSign = false;
2711 
2712     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2713     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2714 
2715     if (fNumber < 0)
2716     {
2717         if (nIx == 0) // Not in the ones at the end
2718         {
2719             bSign = true; // Formats
2720         }
2721         fNumber = -fNumber;
2722     }
2723 
2724     sStr = ::rtl::math::doubleToUString( fNumber,
2725                                          rtl_math_StringFormat_E,
2726                                          rInfo.nCntPre + rInfo.nCntPost - 1, '.' );
2727     OUStringBuffer ExpStr;
2728     short nExpSign = 1;
2729     sal_Int32 nExPos = sStr.indexOf('E');
2730     sal_Int32 nDecPos = -1;
2731 
2732     if ( nExPos >= 0 )
2733     {
2734         // split into mantissa and exponent and get rid of "E+" or "E-"
2735         sal_Int32 nExpStart = nExPos + 1;
2736 
2737         switch ( sStr[ nExpStart ] )
2738         {
2739         case '-' :
2740             nExpSign = -1;
2741             [[fallthrough]];
2742         case '+' :
2743             ++nExpStart;
2744             break;
2745         }
2746         ExpStr = sStr.subView( nExpStart );    // part following the "E+"
2747         sStr.truncate( nExPos );
2748 
2749         if ( rInfo.nCntPre != 1 ) // rescale Exp
2750         {
2751             sal_Int32 nExp = OUString::unacquired(ExpStr).toInt32() * nExpSign;
2752             sal_Int32 nRescale = (rInfo.nCntPre != 0) ? nExp % static_cast<sal_Int32>(rInfo.nCntPre) : -1;
2753             if( nRescale < 0 && rInfo.nCntPre != 0 )
2754                 nRescale += static_cast<sal_Int32>(rInfo.nCntPre);
2755             nExp -= nRescale;
2756             if ( nExp < 0 )
2757             {
2758                 nExpSign = -1;
2759                 nExp = -nExp;
2760             }
2761             else
2762             {
2763                 nExpSign = 1;
2764             }
2765             ExpStr = OUString::number( nExp );
2766             const sal_Unicode cFirstDigit = sStr[0];
2767             // rescale mantissa
2768             sStr = ::rtl::math::doubleToUString( fNumber,
2769                                          rtl_math_StringFormat_E,
2770                                          nRescale + rInfo.nCntPost, '.' );
2771 
2772             // sStr now may contain a rounded-up value shifted into the next
2773             // magnitude, for example 1.000E+02 (4 digits) for fNumber 99.995
2774             // (9.9995E+02 rounded to 3 decimals) but we want the final result
2775             // to be 100.00E+00 (5 digits), so for the following fill routines
2776             // below to work correctly append a zero decimal.
2777             /* TODO: this is awkward, could an engineering notation mode be
2778              * introduced to rtl_math_doubleToUString()? */
2779             sStr.truncate( sStr.indexOf('E') );
2780             if (sStr[0] == '1' && cFirstDigit != '1')
2781                 sStr.append('0');
2782         }
2783 
2784         // cut any decimal delimiter
2785         sal_Int32 index = 0;
2786 
2787         while((index = sStr.indexOf('.', index)) >= 0)
2788         {
2789             if (nDecPos < 0)
2790                 nDecPos = index;
2791             sStr.remove(index, 1);
2792         }
2793     }
2794 
2795     sal_uInt16 j = nCnt-1;  // Last symbol
2796     sal_Int32 k = ExpStr.getLength() - 1;  // Position in ExpStr
2797     sal_Int32 nZeros = 0; // Erase leading zeros
2798 
2799     // erase all leading zeros except last one
2800     while (nZeros < k && ExpStr[nZeros] == '0')
2801     {
2802         ++nZeros;
2803     }
2804     if (nZeros)
2805     {
2806         ExpStr.remove( 0, nZeros);
2807     }
2808 
2809     // restore leading zeros or blanks according to format '0' or '?' tdf#156449
2810     bRes |= ImpNumberFill(rNatNum, ExpStr, fNumber, k, j, nIx, NF_SYMBOLTYPE_EXP, bStarFlag);
2811 
2812     bool bCont = true;
2813 
2814     if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_EXP)
2815     {
2816         const OUString& rStr = rInfo.sStrArray[j];
2817         if (nExpSign == -1)
2818         {
2819             ExpStr.insert(0, '-');
2820         }
2821         else if (rStr.getLength() > 1 && rStr[1] == '+')
2822         {
2823             ExpStr.insert(0, '+');
2824         }
2825         ExpStr.insert(0, rStr[0]);
2826         if ( j )
2827         {
2828             j--;
2829         }
2830         else
2831         {
2832             bCont = false;
2833         }
2834     }
2835     // Continue main number:
2836     if ( !bCont )
2837     {
2838         sStr.truncate();
2839     }
2840     else
2841     {
2842         bRes |= ImpDecimalFill(rNatNum, sStr, fNumber, nDecPos, j, nIx, false, bStarFlag);
2843     }
2844 
2845     if (bSign)
2846     {
2847         sStr.insert(0, '-');
2848     }
2849     sStr.append(ExpStr);
2850 
2851     return bRes;
2852 }
2853 
GetRoundFractionValue(double fNumber) const2854 double SvNumberformat::GetRoundFractionValue ( double fNumber ) const
2855 {
2856     sal_uInt16 nIx = GetSubformatIndex ( fNumber );
2857     double fIntPart = 0.0;           // integer part of fraction
2858     sal_Int64 nFrac = 0, nDiv = 1;  // numerator and denominator
2859     double fSign = (fNumber < 0.0) ? -1.0 : 1.0;
2860     // fNumber is modified in ImpGetFractionElements to absolute fractional part
2861     ImpGetFractionElements ( fNumber, nIx, fIntPart, nFrac, nDiv );
2862     if ( nDiv > 0 )
2863         return fSign * ( fIntPart + static_cast<double>(nFrac) / static_cast<double>(nDiv) );
2864     else
2865         return fSign * fIntPart;
2866 }
2867 
ImpGetFractionElements(double & fNumber,sal_uInt16 nIx,double & fIntPart,sal_Int64 & nFrac,sal_Int64 & nDiv) const2868 void SvNumberformat::ImpGetFractionElements ( double& fNumber, sal_uInt16 nIx,
2869                                               double& fIntPart, sal_Int64& nFrac, sal_Int64& nDiv ) const
2870 {
2871     if ( fNumber < 0.0 )
2872         fNumber = -fNumber;
2873     fIntPart = floor(fNumber); // Integral part
2874     fNumber -= fIntPart;         // Fractional part
2875     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2876     sal_Int64 nForcedDiv = lcl_GetDenominatorString( rInfo, NumFor[nIx].GetCount() ).toInt32();
2877     if( nForcedDiv > 0 )
2878     {   // Forced Denominator
2879         nDiv = nForcedDiv;
2880         nFrac = static_cast<sal_Int64>(floor ( fNumber * nDiv ));
2881         double fFracNew = static_cast<double>(nFrac) / static_cast<double>(nDiv);
2882         double fFracNew1 = static_cast<double>(nFrac + 1) / static_cast<double>(nDiv);
2883         double fDiff = fNumber - fFracNew;
2884         if( fDiff > ( fFracNew1 - fNumber ) )
2885         {
2886             nFrac++;
2887         }
2888     }
2889     else // Calculated Denominator
2890     {
2891         nDiv = 1;
2892         sal_Int64 nBasis = static_cast<sal_Int64>(floor( pow(10.0,rInfo.nCntExp))) - 1; // 9, 99, 999 ,...
2893         sal_Int64 nFracPrev = 1, nDivPrev = 0, nFracNext, nDivNext, nPartialDenom;
2894         double fRemainder = fNumber;
2895 
2896         // Use continued fraction representation of fNumber
2897         // See https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations
2898         while ( fRemainder > 0.0 )
2899         {
2900             double fTemp = 1.0 / fRemainder;             // 64bits precision required when fRemainder is very weak
2901             nPartialDenom = static_cast<sal_Int64>(floor(fTemp));   // due to floating point notation with double precision
2902             fRemainder = fTemp - static_cast<double>(nPartialDenom);
2903             nDivNext = nPartialDenom * nDiv + nDivPrev;
2904             if ( nDivNext <= nBasis )  // continue loop
2905             {
2906                 nFracNext = nPartialDenom * nFrac + nFracPrev;
2907                 nFracPrev = nFrac;
2908                 nFrac = nFracNext;
2909                 nDivPrev = nDiv;
2910                 nDiv = nDivNext;
2911             }
2912             else // calculate collateral fraction and exit
2913             {
2914                 sal_Int64 nCollat = (nBasis - nDivPrev) / nDiv;
2915                 if ( 2 * nCollat >= nPartialDenom )
2916                 {
2917                     sal_Int64 nFracTest = nCollat * nFrac + nFracPrev;
2918                     sal_Int64 nDivTest  = nCollat * nDiv  + nDivPrev;
2919                     double fSign = (static_cast<double>(nFrac) > fNumber * static_cast<double>(nDiv))?1.0:-1.0;
2920                     if ( fSign * ( double(nFrac * nDivTest + nDiv * nFracTest) - 2.0 * double(nDiv * nDivTest) * fNumber ) > 0.0 )
2921                     {
2922                         nFrac = nFracTest;
2923                         nDiv  = nDivTest;
2924                     }
2925                 }
2926                 fRemainder = 0.0; // exit while loop
2927             }
2928         }
2929     }
2930     if (nFrac >= nDiv)
2931     {
2932         ++fIntPart;
2933         nFrac = 0;
2934         nDiv = ( nForcedDiv > 0 ) ? nForcedDiv : 1;
2935     }
2936 }
2937 
ImpGetFractionOutput(double fNumber,sal_uInt16 nIx,bool bStarFlag,const NativeNumberWrapper & rNatNum,OUStringBuffer & sBuff) const2938 bool SvNumberformat::ImpGetFractionOutput(double fNumber,
2939                                           sal_uInt16 nIx,
2940                                           bool bStarFlag,
2941                                           const NativeNumberWrapper& rNatNum,
2942                                           OUStringBuffer& sBuff) const
2943 {
2944     bool bRes = false;
2945     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2946     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2947     OUStringBuffer sStr, sFrac, sDiv; // Strings, value for Integral part Numerator and denominator
2948     bool bSign = ( (fNumber < 0) && (nIx == 0) ); // sign Not in the ones at the end
2949     const OUString sIntegerFormat = lcl_GetFractionIntegerString(rInfo, nCnt);
2950     const OUString sNumeratorFormat = lcl_GetNumeratorString(rInfo, nCnt);
2951     const OUString sDenominatorFormat = lcl_GetDenominatorString(rInfo, nCnt);
2952 
2953     sal_Int64 nFrac = 0, nDiv = 1;
2954     double fNum = floor(fNumber); // Integral part
2955 
2956     if (fNum > D_MAX_U_INT32 || rInfo.nCntExp > 9) // Too large
2957     {
2958         sBuff = ImpSvNumberformatScan::sErrStr;
2959         return false;
2960     }
2961     if (rInfo.nCntExp == 0)
2962     {
2963         SAL_WARN( "svl.numbers", "SvNumberformat:: Fraction, nCntExp == 0");
2964         sBuff.truncate();
2965         return false;
2966     }
2967 
2968     ImpGetFractionElements( fNumber, nIx, fNum, nFrac, nDiv);
2969 
2970     if (rInfo.nCntPre == 0) // Improper fraction
2971     {
2972         double fNum1 = fNum * static_cast<double>(nDiv) + static_cast<double>(nFrac);
2973 
2974         if (fNum1 > D_MAX_INTEGER)
2975         {
2976             sBuff = ImpSvNumberformatScan::sErrStr;
2977             return false;
2978         }
2979         nFrac = static_cast<sal_Int64>(floor(fNum1));
2980     }
2981     else if (fNum == 0.0 && nFrac != 0)
2982     {
2983     }
2984     else
2985     {
2986         char aBuf[100];
2987         o3tl::sprintf( aBuf, "%.f", fNum ); // simple rounded integer
2988         sStr.appendAscii( aBuf );
2989         ::impTransliterate(sStr, NumFor[nIx].GetNatNum(), rNatNum);
2990     }
2991     bool bHideFraction = (rInfo.nCntPre > 0 && nFrac == 0
2992                         && (sNumeratorFormat.indexOf('0') < 0)
2993                         && (sDenominatorFormat.indexOf('0') < 0
2994                         || sDenominatorFormat.toInt32() > 0) );
2995     if ( bHideFraction )
2996     {
2997         sDiv.truncate();
2998     }
2999     else  // if there are some '0' in format, force display of fraction
3000     {
3001         sFrac = ImpIntToString(rNatNum, nIx, nFrac);
3002         sDiv = ImpIntToString(rNatNum, nIx, nDiv);
3003     }
3004 
3005     sal_uInt16 j = nCnt-1; // Last symbol -> backwards
3006     sal_Int32 k;           // Denominator
3007 
3008     bRes |= ImpNumberFill(rNatNum, sDiv, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRAC, bStarFlag, true);
3009 
3010     bool bCont = true;
3011     if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRAC)
3012     {
3013         if ( bHideFraction )
3014         {   // do not insert blank for fraction if there is no '?'
3015             if ( sNumeratorFormat.indexOf('?') >= 0
3016               || sDenominatorFormat.indexOf('?') >= 0 )
3017                 sDiv.insert(0, ' ');
3018         }
3019         else
3020         {
3021             sDiv.insert(0, rInfo.sStrArray[j][0]);
3022         }
3023         if ( j )
3024         {
3025             j--;
3026         }
3027         else
3028         {
3029             bCont = false;
3030         }
3031     }
3032     // Further numerators:
3033     if ( !bCont )
3034     {
3035         sFrac.truncate();
3036     }
3037     else
3038     {
3039         bRes |= ImpNumberFill(rNatNum, sFrac, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRACBLANK, bStarFlag);
3040         bCont = false;  // there is no integer part?
3041         if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRACBLANK)
3042         {
3043             if ( j )
3044             {
3045                 if ( bHideFraction )
3046                 {   // '?' in any format force display of blank as delimiter
3047                     if ( sIntegerFormat.indexOf('?') >= 0
3048                       || sNumeratorFormat.indexOf('?') >= 0
3049                       || sDenominatorFormat.indexOf('?') >= 0 )
3050                     {
3051                         for (sal_Int32 i = 0; i < rInfo.sStrArray[j].getLength(); i++)
3052                             sFrac.insert(0, ' ');
3053                     }
3054                 }
3055                 else
3056                 {
3057                     if ( fNum != 0.0 || sIntegerFormat.indexOf('0') >= 0 )
3058                         sFrac.insert(0, rInfo.sStrArray[j]); // insert Blank string only if there are both integer and fraction
3059                     else
3060                     {
3061                         if ( sIntegerFormat.indexOf('?') >= 0
3062                           || sNumeratorFormat.indexOf('?') >= 0 )
3063                         {
3064                             for (sal_Int32 i = 0; i < rInfo.sStrArray[j].getLength(); i++)
3065                                 sFrac.insert(0, ' ');
3066                         }
3067                     }
3068                 }
3069                 j--;
3070                 bCont = true;  // Yes, there is an integer
3071             }
3072             else
3073                 sFrac.insert(0, rInfo.sStrArray[j]);
3074         }
3075     }
3076     // Continue integer part
3077     if ( !bCont )
3078     {
3079         sStr.truncate();
3080     }
3081     else
3082     {
3083         k = sStr.getLength(); // After last figure
3084         bRes |= ImpNumberFillWithThousands(rNatNum, sStr, fNumber, k, j, nIx,
3085                                            rInfo.nCntPre, bStarFlag);
3086     }
3087     if (bSign && (nFrac != 0 || fNum != 0.0))
3088     {
3089         sBuff.insert(0, '-'); // Not -0
3090     }
3091     sBuff.append(sStr);
3092     sBuff.append(sFrac);
3093     sBuff.append(sDiv);
3094     return bRes;
3095 }
3096 
ImpGetFractionOfSecondString(OUStringBuffer & rBuf,double fFractionOfSecond,int nFractionDecimals,bool bAddOneRoundingDecimal,sal_uInt16 nIx,sal_uInt16 nMinimumInputLineDecimals,const NativeNumberWrapper & rNatNum) const3097 sal_uInt16 SvNumberformat::ImpGetFractionOfSecondString( OUStringBuffer& rBuf, double fFractionOfSecond,
3098         int nFractionDecimals, bool bAddOneRoundingDecimal, sal_uInt16 nIx, sal_uInt16 nMinimumInputLineDecimals,
3099         const NativeNumberWrapper& rNatNum) const
3100 {
3101     if (!nFractionDecimals)
3102         return 0;
3103 
3104     // nFractionDecimals+1 to not round up what Time::GetClock() carefully
3105     // truncated.
3106     rBuf.append( rtl::math::doubleToUString( fFractionOfSecond, rtl_math_StringFormat_F,
3107                 (bAddOneRoundingDecimal ? nFractionDecimals + 1 : nFractionDecimals), '.'));
3108     rBuf.stripStart('0');
3109     rBuf.stripStart('.');
3110     if (bAddOneRoundingDecimal && rBuf.getLength() > nFractionDecimals)
3111         rBuf.truncate( nFractionDecimals); // the digit appended because of nFractionDecimals+1
3112     if (nMinimumInputLineDecimals)
3113     {
3114         rBuf.stripEnd('0');
3115         for (sal_Int32 index = rBuf.getLength(); index < nMinimumInputLineDecimals; ++index)
3116         {
3117             rBuf.append('0');
3118         }
3119         ::impTransliterate(rBuf, NumFor[nIx].GetNatNum(), rNatNum);
3120         nFractionDecimals = rBuf.getLength();
3121     }
3122     else
3123     {
3124         ::impTransliterate(rBuf, NumFor[nIx].GetNatNum(), rNatNum);
3125     }
3126     return static_cast<sal_uInt16>(nFractionDecimals);
3127 }
3128 
ImpGetTimeOutput(double fNumber,sal_uInt16 nIx,bool bStarFlag,const NativeNumberWrapper & rNatNum,const SvNFLanguageData & rCurrentLang,OUStringBuffer & sBuff) const3129 bool SvNumberformat::ImpGetTimeOutput(double fNumber,
3130                                       sal_uInt16 nIx,
3131                                       bool bStarFlag,
3132                                       const NativeNumberWrapper& rNatNum,
3133                                       const SvNFLanguageData& rCurrentLang,
3134                                       OUStringBuffer& sBuff) const
3135 {
3136     using namespace ::com::sun::star::i18n;
3137     bool bCalendarSet = false;
3138     const double fNumberOrig = fNumber;
3139     bool bRes = false;
3140     bool bSign = false;
3141     if (fNumber < 0.0)
3142     {
3143         fNumber = -fNumber;
3144         if (nIx == 0)
3145         {
3146             bSign = true;
3147         }
3148     }
3149     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3150     bool bInputLine;
3151     sal_Int32 nCntPost;
3152     if ( rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION &&
3153          0 < rInfo.nCntPost && rInfo.nCntPost < kTimeSignificantRound )
3154     {
3155         bInputLine = true;
3156         nCntPost = kTimeSignificantRound;
3157     }
3158     else
3159     {
3160         bInputLine = false;
3161         nCntPost = rInfo.nCntPost;
3162     }
3163 
3164     OUStringBuffer sSecStr;
3165     sal_Int32 nSecPos = 0; // For figure by figure processing
3166     sal_uInt32 nHour, nMin, nSec;
3167     if (!rInfo.bThousand) // No [] format
3168     {
3169         sal_uInt16 nCHour, nCMinute, nCSecond;
3170         double fFractionOfSecond;
3171         tools::Time::GetClock( fNumberOrig, nCHour, nCMinute, nCSecond, fFractionOfSecond, nCntPost);
3172         nHour = nCHour;
3173         nMin = nCMinute;
3174         nSec = nCSecond;
3175         nCntPost = ImpGetFractionOfSecondString( sSecStr, fFractionOfSecond, nCntPost, true, nIx,
3176                 (bInputLine ? rInfo.nCntPost : 0), rNatNum );
3177     }
3178     else
3179     {
3180         const double fTime = rtl::math::round( fNumber * 86400.0, int(nCntPost));
3181         if (bSign && fTime == 0.0)
3182         {
3183             bSign = false; // Not -00:00:00
3184         }
3185         if (fTime > D_MAX_U_INT32)
3186         {
3187             sBuff = ImpSvNumberformatScan::sErrStr;
3188             return false;
3189         }
3190         sal_uInt32 nSeconds = static_cast<sal_uInt32>(fTime);
3191 
3192         nCntPost = ImpGetFractionOfSecondString( sSecStr, fTime - nSeconds, nCntPost, false, nIx,
3193                 (bInputLine ? rInfo.nCntPost : 0), rNatNum );
3194 
3195         if (rInfo.nThousand == 3) // [ss]
3196         {
3197             nHour = 0;
3198             nMin = 0;
3199             nSec = nSeconds;
3200         }
3201         else if (rInfo.nThousand == 2) // [mm]:ss
3202         {
3203             nHour = 0;
3204             nMin = nSeconds / 60;
3205             nSec = nSeconds % 60;
3206         }
3207         else if (rInfo.nThousand == 1) // [hh]:mm:ss
3208         {
3209             nHour = nSeconds / 3600;
3210             nMin = (nSeconds%3600) / 60;
3211             nSec = nSeconds%60;
3212         }
3213         else
3214         {
3215             // TODO  What should these be set to?
3216             nHour = 0;
3217             nMin  = 0;
3218             nSec  = 0;
3219         }
3220     }
3221 
3222     sal_Unicode cAmPm = ' '; // a or p
3223     if (rInfo.nCntExp) // AM/PM
3224     {
3225         if (nHour == 0)
3226         {
3227             nHour = 12;
3228             cAmPm = 'a';
3229         }
3230         else if (nHour < 12)
3231         {
3232             cAmPm = 'a';
3233         }
3234         else
3235         {
3236             cAmPm = 'p';
3237             if (nHour > 12)
3238             {
3239                 nHour -= 12;
3240             }
3241         }
3242     }
3243     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
3244     for (sal_uInt16 i = 0; i < nCnt; i++)
3245     {
3246         sal_Int32 nLen;
3247         switch (rInfo.nTypeArray[i])
3248         {
3249         case NF_SYMBOLTYPE_STAR:
3250             if( bStarFlag )
3251             {
3252                 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
3253             }
3254             break;
3255         case NF_SYMBOLTYPE_BLANK:
3256             if (rInfo.sStrArray[i].getLength() >= 2)
3257                 InsertBlanks(sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
3258             break;
3259         case NF_SYMBOLTYPE_STRING:
3260         case NF_SYMBOLTYPE_CURRENCY:
3261         case NF_SYMBOLTYPE_DATESEP:
3262         case NF_SYMBOLTYPE_TIMESEP:
3263         case NF_SYMBOLTYPE_TIME100SECSEP:
3264             sBuff.append(rInfo.sStrArray[i]);
3265             break;
3266         case NF_SYMBOLTYPE_DIGIT:
3267             nLen = ( bInputLine && i > 0 &&
3268                      (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING ||
3269                       rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ?
3270                      nCntPost : rInfo.sStrArray[i].getLength() );
3271             for (sal_Int32 j = 0; j < nLen && nSecPos < nCntPost && nSecPos < sSecStr.getLength(); ++j)
3272             {
3273                 sBuff.append(sSecStr[nSecPos]);
3274                 nSecPos++;
3275             }
3276             break;
3277         case NF_KEY_AMPM:               // AM/PM
3278         {
3279             CalendarWrapper& rCal = *rCurrentLang.GetCalendar();
3280             if ( !bCalendarSet )
3281             {
3282                 double fDiff = DateTime::Sub( DateTime(rScan.GetNullDate()), rCal.getEpochStart());
3283                 fDiff += fNumberOrig;
3284                 rCal.setLocalDateTime( fDiff );
3285                 bCalendarSet = true;
3286             }
3287             if (cAmPm == 'a')
3288             {
3289                 sBuff.append(rCal.getDisplayName(
3290                                  CalendarDisplayIndex::AM_PM, AmPmValue::AM, 0 ));
3291             }
3292             else
3293             {
3294                 sBuff.append(rCal.getDisplayName(
3295                                  CalendarDisplayIndex::AM_PM, AmPmValue::PM, 0 ));
3296             }
3297             break;
3298         }
3299         case NF_KEY_AP:                 // A/P
3300             if (cAmPm == 'a')
3301             {
3302                 sBuff.append('a');
3303             }
3304             else
3305             {
3306                 sBuff.append('p');
3307             }
3308             break;
3309         case NF_KEY_MI:                 // M
3310             sBuff.append(ImpIntToString(rNatNum, nIx, nMin ));
3311             break;
3312         case NF_KEY_MMI:                // MM
3313             sBuff.append(ImpIntToString(rNatNum, nIx, nMin, 2 ));
3314             break;
3315         case NF_KEY_H:                  // H
3316             sBuff.append(ImpIntToString(rNatNum, nIx, nHour ));
3317             break;
3318         case NF_KEY_HH:                 // HH
3319             sBuff.append(ImpIntToString(rNatNum, nIx, nHour, 2 ));
3320             break;
3321         case NF_KEY_S:                  // S
3322             sBuff.append(ImpIntToString(rNatNum, nIx, nSec ));
3323             break;
3324         case NF_KEY_SS:                 // SS
3325             sBuff.append(ImpIntToString(rNatNum, nIx, nSec, 2 ));
3326             break;
3327         default:
3328             break;
3329         }
3330     }
3331     if (bSign && rInfo.bThousand)
3332     {
3333         sBuff.insert(0, '-');
3334     }
3335     return bRes;
3336 }
3337 
3338 
3339 /** If a day of month occurs within the format, the month name is in possessive
3340     genitive case if the day follows the month, and partitive case if the day
3341     precedes the month. If there is no day of month the nominative case (noun)
3342     is returned. Also if the month is immediately preceded or followed by a
3343     literal string other than space and not followed by a comma, the nominative
3344     name is used, this prevents duplicated casing for MMMM\t\a and such in
3345     documents imported from (e.g. Finnish) Excel or older LibO/OOo releases.
3346  */
3347 
3348 // IDEA: instead of eCodeType pass the index to nTypeArray and restrict
3349 // inspection of month name around that one, that would enable different month
3350 // cases in one format. Though probably the most rare use case ever...
3351 
ImpUseMonthCase(int & io_nState,const ImpSvNumFor & rNumFor,NfKeywordIndex eCodeType)3352 sal_Int32 SvNumberformat::ImpUseMonthCase( int & io_nState, const ImpSvNumFor& rNumFor, NfKeywordIndex eCodeType )
3353 {
3354     using namespace ::com::sun::star::i18n;
3355     if (!io_nState)
3356     {
3357         bool bMonthSeen = false;
3358         bool bDaySeen = false;
3359         const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3360         const sal_uInt16 nCount = rNumFor.GetCount();
3361         for (sal_uInt16 i = 0; i < nCount && io_nState == 0; ++i)
3362         {
3363             sal_Int32 nLen;
3364             switch (rInfo.nTypeArray[i])
3365             {
3366             case NF_KEY_D :
3367             case NF_KEY_DD :
3368                 if (bMonthSeen)
3369                 {
3370                     io_nState = 2;
3371                 }
3372                 else
3373                 {
3374                     bDaySeen = true;
3375                 }
3376                 break;
3377             case NF_KEY_MMM:
3378             case NF_KEY_MMMM:
3379             case NF_KEY_MMMMM:
3380                 if ((i < nCount-1 &&
3381                      rInfo.nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
3382                      // Literal following, not empty, space nor comma.
3383                      !rInfo.sStrArray[i+1].isEmpty() &&
3384                      rInfo.sStrArray[i+1][0] != ' ' && rInfo.sStrArray[i+1][0] != ',') ||
3385                     (i > 0 && rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING &&
3386                      ((nLen = rInfo.sStrArray[i-1].getLength()) > 0) &&
3387                      // Literal preceding, not space.
3388                      rInfo.sStrArray[i-1][nLen-1] != ' '))
3389                 {
3390                     io_nState = 1;
3391                 }
3392                 else if (bDaySeen)
3393                 {
3394                     io_nState = 3;
3395                 }
3396                 else
3397                 {
3398                     bMonthSeen = true;
3399                 }
3400                 break;
3401             }
3402         }
3403         if (io_nState == 0)
3404         {
3405             io_nState = 1; // No day of month
3406         }
3407     }
3408     switch (io_nState)
3409     {
3410     case 1:
3411         // No day of month or forced nominative
3412         switch (eCodeType)
3413         {
3414         case NF_KEY_MMM:
3415             return CalendarDisplayCode::SHORT_MONTH_NAME;
3416         case NF_KEY_MMMM:
3417             return CalendarDisplayCode::LONG_MONTH_NAME;
3418         case NF_KEY_MMMMM:
3419             return CalendarDisplayCode::NARROW_MONTH_NAME;
3420         default:
3421             ;   // nothing
3422         }
3423         break;
3424     case 2:
3425         // Day of month follows month (the month's 17th)
3426         switch (eCodeType)
3427         {
3428         case NF_KEY_MMM:
3429             return CalendarDisplayCode::SHORT_GENITIVE_MONTH_NAME;
3430         case NF_KEY_MMMM:
3431             return CalendarDisplayCode::LONG_GENITIVE_MONTH_NAME;
3432         case NF_KEY_MMMMM:
3433             return CalendarDisplayCode::NARROW_GENITIVE_MONTH_NAME;
3434         default:
3435             ;   // Nothing
3436         }
3437         break;
3438     case 3:
3439         // Day of month precedes month (17 of month)
3440         switch (eCodeType)
3441         {
3442         case NF_KEY_MMM:
3443             return CalendarDisplayCode::SHORT_PARTITIVE_MONTH_NAME;
3444         case NF_KEY_MMMM:
3445             return CalendarDisplayCode::LONG_PARTITIVE_MONTH_NAME;
3446         case NF_KEY_MMMMM:
3447             return CalendarDisplayCode::NARROW_PARTITIVE_MONTH_NAME;
3448         default:
3449             ;   // nothing
3450         }
3451         break;
3452     }
3453     SAL_WARN( "svl.numbers", "ImpUseMonthCase: unhandled keyword index eCodeType");
3454     return CalendarDisplayCode::LONG_MONTH_NAME;
3455 }
3456 
3457 namespace {
3458 
ImpIsOtherCalendar(const ImpSvNumFor & rNumFor,const CalendarWrapper & rCal)3459 bool ImpIsOtherCalendar( const ImpSvNumFor& rNumFor, const CalendarWrapper& rCal )
3460 {
3461     if ( rCal.getUniqueID() != GREGORIAN )
3462     {
3463         return false;
3464     }
3465     const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3466     const sal_uInt16 nCnt = rNumFor.GetCount();
3467     sal_uInt16 i;
3468     for ( i = 0; i < nCnt; i++ )
3469     {
3470         switch ( rInfo.nTypeArray[i] )
3471         {
3472         case NF_SYMBOLTYPE_CALENDAR :
3473             return false;
3474         case NF_KEY_EC :
3475         case NF_KEY_EEC :
3476         case NF_KEY_R :
3477         case NF_KEY_RR :
3478         case NF_KEY_AAA :
3479         case NF_KEY_AAAA :
3480         case NF_KEY_G :
3481         case NF_KEY_GG :
3482         case NF_KEY_GGG :
3483             return true;
3484         }
3485     }
3486     return false;
3487 }
3488 
3489 }
3490 
SwitchToOtherCalendar(OUString & rOrgCalendar,double & fOrgDateTime,CalendarWrapper & rCal) const3491 void SvNumberformat::SwitchToOtherCalendar( OUString& rOrgCalendar,
3492                                             double& fOrgDateTime,
3493                                             CalendarWrapper& rCal ) const
3494 {
3495     if ( rCal.getUniqueID() != GREGORIAN )
3496         return;
3497 
3498     using namespace ::com::sun::star::i18n;
3499     const css::uno::Sequence< OUString > xCals = rCal.getAllCalendars(
3500             rLoc().getLanguageTag().getLocale() );
3501     sal_Int32 nCnt = xCals.getLength();
3502     if ( nCnt <= 1 )
3503         return;
3504 
3505     auto pCal = std::find_if(xCals.begin(), xCals.end(),
3506         [](const OUString& rCalName) { return rCalName != GREGORIAN; });
3507     if (pCal == xCals.end())
3508         return;
3509 
3510     if ( !rOrgCalendar.getLength() )
3511     {
3512         rOrgCalendar = rCal.getUniqueID();
3513         fOrgDateTime = rCal.getDateTime();
3514     }
3515     rCal.loadCalendar( *pCal, rLoc().getLanguageTag().getLocale() );
3516     rCal.setDateTime( fOrgDateTime );
3517 }
3518 
SwitchToGregorianCalendar(std::u16string_view rOrgCalendar,double fOrgDateTime,CalendarWrapper & rCal) const3519 void SvNumberformat::SwitchToGregorianCalendar( std::u16string_view rOrgCalendar,
3520                                                 double fOrgDateTime,
3521                                                 CalendarWrapper& rCal ) const
3522 {
3523     if ( rOrgCalendar.size() && rCal.getUniqueID() != GREGORIAN )
3524     {
3525         rCal.loadCalendar( GREGORIAN, rLoc().getLanguageTag().getLocale() );
3526         rCal.setDateTime( fOrgDateTime );
3527     }
3528 }
3529 
ImpFallBackToGregorianCalendar(OUString & rOrgCalendar,double & fOrgDateTime,CalendarWrapper & rCal) const3530 bool SvNumberformat::ImpFallBackToGregorianCalendar( OUString& rOrgCalendar,
3531                                                      double& fOrgDateTime,
3532                                                      CalendarWrapper& rCal ) const
3533 {
3534     using namespace ::com::sun::star::i18n;
3535     if ( rCal.getUniqueID() != GREGORIAN )
3536     {
3537         sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA );
3538         if ( nVal == 0 && rCal.getLoadedCalendar().Eras[0].ID == "Dummy" )
3539         {
3540             if ( !rOrgCalendar.getLength() )
3541             {
3542                 rOrgCalendar = rCal.getUniqueID();
3543                 fOrgDateTime = rCal.getDateTime();
3544             }
3545             else if ( rOrgCalendar == GREGORIAN )
3546             {
3547                 rOrgCalendar.clear();
3548             }
3549             rCal.loadCalendar( GREGORIAN, rLoc().getLanguageTag().getLocale() );
3550             rCal.setDateTime( fOrgDateTime );
3551             return true;
3552         }
3553     }
3554     return false;
3555 }
3556 
3557 
3558 #ifdef THE_FUTURE
3559 /* XXX NOTE: even if the ImpSwitchToSpecifiedCalendar method is currently
3560  * unused please don't remove it, it would be needed by
3561  * SwitchToSpecifiedCalendar(), see comment in
3562  * ImpSvNumberInputScan::GetDateRef() */
3563 
ImpSwitchToSpecifiedCalendar(OUString & rOrgCalendar,double & fOrgDateTime,const ImpSvNumFor & rNumFor) const3564 bool SvNumberformat::ImpSwitchToSpecifiedCalendar( OUString& rOrgCalendar,
3565                                                    double& fOrgDateTime,
3566                                                    const ImpSvNumFor& rNumFor ) const
3567 {
3568     const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3569     const sal_uInt16 nCnt = rNumFor.GetCount();
3570     for ( sal_uInt16 i = 0; i < nCnt; i++ )
3571     {
3572         if ( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_CALENDAR )
3573         {
3574             CalendarWrapper& rCal = GetCal();
3575             if ( !rOrgCalendar.getLength() )
3576             {
3577                 rOrgCalendar = rCal.getUniqueID();
3578                 fOrgDateTime = rCal.getDateTime();
3579             }
3580             rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLocale() );
3581             rCal.setDateTime( fOrgDateTime );
3582             return true;
3583         }
3584     }
3585     return false;
3586 }
3587 #endif
3588 
3589 // static
ImpAppendEraG(OUStringBuffer & OutString,const CalendarWrapper & rCal,sal_Int16 nNatNum)3590 void SvNumberformat::ImpAppendEraG( OUStringBuffer& OutString,
3591                                     const CalendarWrapper& rCal,
3592                                     sal_Int16 nNatNum )
3593 {
3594     using namespace ::com::sun::star::i18n;
3595     if ( rCal.getUniqueID() == "gengou" )
3596     {
3597         sal_Unicode cEra;
3598         sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA );
3599         switch ( nVal )
3600         {
3601         case 1:
3602             cEra = 'M';
3603             break;
3604         case 2:
3605             cEra = 'T';
3606             break;
3607         case 3:
3608             cEra = 'S';
3609             break;
3610         case 4:
3611             cEra = 'H';
3612             break;
3613         case 5:
3614             cEra = 'R';
3615             break;
3616         default:
3617             cEra = '?';
3618             break;
3619         }
3620         OutString.append(cEra);
3621     }
3622     else
3623     {
3624         OutString.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
3625     }
3626 }
3627 
ImpIsIso8601(const ImpSvNumFor & rNumFor) const3628 bool SvNumberformat::ImpIsIso8601( const ImpSvNumFor& rNumFor ) const
3629 {
3630     bool bIsIso = false;
3631     if (eType & SvNumFormatType::DATE)
3632     {
3633         enum State
3634         {
3635             eNone,
3636             eAtYear,
3637             eAtSep1,
3638             eAtMonth,
3639             eAtSep2,
3640             eNotIso
3641         };
3642         State eState = eNone;
3643         auto & rTypeArray = rNumFor.Info().nTypeArray;
3644         sal_uInt16 nCnt = rNumFor.GetCount();
3645         for (sal_uInt16 i=0; i < nCnt && !bIsIso && eState != eNotIso; ++i)
3646         {
3647             switch ( rTypeArray[i] )
3648             {
3649             case NF_KEY_YY:     // two digits not strictly ISO 8601
3650             case NF_KEY_YYYY:
3651                 if (eState != eNone)
3652                 {
3653                     eState = eNotIso;
3654                 }
3655                 else
3656                 {
3657                     eState = eAtYear;
3658                 }
3659                 break;
3660             case NF_KEY_M:      // single digit not strictly ISO 8601
3661             case NF_KEY_MM:
3662                 if (eState != eAtSep1)
3663                 {
3664                     eState = eNotIso;
3665                 }
3666                 else
3667                 {
3668                     eState = eAtMonth;
3669                 }
3670                 break;
3671             case NF_KEY_D:      // single digit not strictly ISO 8601
3672             case NF_KEY_DD:
3673                 if (eState != eAtSep2)
3674                 {
3675                     eState = eNotIso;
3676                 }
3677                 else
3678                 {
3679                     bIsIso = true;
3680                 }
3681                 break;
3682             case NF_SYMBOLTYPE_STRING:
3683             case NF_SYMBOLTYPE_DATESEP:
3684                 if (rNumFor.Info().sStrArray[i] == "-")
3685                 {
3686                     if (eState == eAtYear)
3687                     {
3688                         eState = eAtSep1;
3689                     }
3690                     else if (eState == eAtMonth)
3691                     {
3692                         eState = eAtSep2;
3693                     }
3694                     else
3695                     {
3696                         eState = eNotIso;
3697                     }
3698                 }
3699                 else
3700                 {
3701                     eState = eNotIso;
3702                 }
3703                 break;
3704             default:
3705                 eState = eNotIso;
3706             }
3707         }
3708     }
3709     else
3710     {
3711        SAL_WARN( "svl.numbers", "SvNumberformat::ImpIsIso8601: no date" );
3712     }
3713     return bIsIso;
3714 }
3715 
lcl_hasEra(const ImpSvNumFor & rNumFor)3716 static bool lcl_hasEra( const ImpSvNumFor& rNumFor )
3717 {
3718     const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3719     const sal_uInt16 nCnt = rNumFor.GetCount();
3720     for ( sal_uInt16 i = 0; i < nCnt; i++ )
3721     {
3722         switch ( rInfo.nTypeArray[i] )
3723         {
3724             case NF_KEY_RR :
3725             case NF_KEY_G :
3726             case NF_KEY_GG :
3727             case NF_KEY_GGG :
3728                 return true;
3729         }
3730     }
3731     return false;
3732 }
3733 
lcl_isSignedYear(const CalendarWrapper & rCal,const ImpSvNumFor & rNumFor)3734 static bool lcl_isSignedYear( const CalendarWrapper& rCal, const ImpSvNumFor& rNumFor )
3735 {
3736     return rCal.getValue( css::i18n::CalendarFieldIndex::ERA ) == 0 &&
3737         rCal.getUniqueID() == GREGORIAN && !lcl_hasEra( rNumFor );
3738 }
3739 
3740 /* XXX: if needed this could be stripped from rEpochStart and diff adding and
3741  * moved to tools' DateTime to be reused elsewhere. */
lcl_getValidDate(const DateTime & rNullDate,const DateTime & rEpochStart,double & fNumber)3742 static bool lcl_getValidDate( const DateTime& rNullDate, const DateTime& rEpochStart, double& fNumber )
3743 {
3744     static const DateTime aCE( Date(1,1,1));
3745     static const DateTime aMin( Date(1,1, SAL_MIN_INT16));
3746     static const DateTime aMax( Date(31,12, SAL_MAX_INT16), tools::Time(23,59,59, tools::Time::nanoSecPerSec - 1));
3747     static const double fMin = DateTime::Sub( aMin, aCE);
3748     static const double fMax = DateTime::Sub( aMax, aCE);
3749     // Value must be representable in our tools::Date proleptic Gregorian
3750     // calendar as well.
3751     const double fOff = DateTime::Sub( rNullDate, aCE) + fNumber;
3752     // Add diff between epochs to serial date number.
3753     const double fDiff = DateTime::Sub( rNullDate, rEpochStart);
3754     fNumber += fDiff;
3755     return fMin <= fOff && fOff <= fMax;
3756 }
3757 
ImpGetDateOutput(double fNumber,sal_uInt16 nIx,bool bStarFlag,const NativeNumberWrapper & rNatNum,const SvNFLanguageData & rCurrentLang,OUStringBuffer & sBuff) const3758 bool SvNumberformat::ImpGetDateOutput(double fNumber,
3759                                       sal_uInt16 nIx,
3760                                       bool bStarFlag,
3761                                       const NativeNumberWrapper& rNatNum,
3762                                       const SvNFLanguageData& rCurrentLang,
3763                                       OUStringBuffer& sBuff) const
3764 {
3765     using namespace ::com::sun::star::i18n;
3766     bool bRes = false;
3767 
3768     CalendarWrapper& rCal = *rCurrentLang.GetCalendar();
3769     if (!lcl_getValidDate( rScan.GetNullDate(), rCal.getEpochStart(), fNumber))
3770     {
3771         sBuff = ImpSvNumberformatScan::sErrStr;
3772         return false;
3773     }
3774     rCal.setLocalDateTime( fNumber );
3775     int nUseMonthCase = 0; // Not decided yet
3776     OUString aOrgCalendar; // empty => not changed yet
3777 
3778     double fOrgDateTime(0.0);
3779     bool bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx], *rCurrentLang.GetCalendar() );
3780     if ( bOtherCalendar )
3781     {
3782         SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3783     }
3784     if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() ) )
3785     {
3786         bOtherCalendar = false;
3787     }
3788     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3789     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
3790     sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum();
3791     OUString aStr;
3792 
3793     // NatNum12: if the date format contains more than a date
3794     // field, it needs to specify in NatNum12 argument
3795     // which date element needs special formatting:
3796     //
3797     // '[NatNum12 ordinal-number]D'              -> "1st"
3798     // '[NatNum12 D=ordinal-number]D" of "MMMM'  -> "1st of April"
3799     // '[NatNum12 D=ordinal]D" of "MMMM'         -> "first of April"
3800     // '[NatNum12 YYYY=year,D=ordinal]D" of "MMMM", "YYYY' -> "first of April, nineteen ninety"
3801     //
3802     // Note: set only for YYYY, MMMM, M, DDDD, D and NNN/AAAA in date formats.
3803     // Additionally for MMMMM, MMM, DDD and NN/AA to support at least
3804     // capitalize, upper, lower, title.
3805     // XXX It's possible to extend this for other keywords and date + time
3806     // combinations, as required.
3807 
3808     bool bUseSpellout = NatNumTakesParameters(nNatNum) &&
3809             (nCnt == 1 || NumFor[nIx].GetNatNum().GetParams().indexOf('=') > -1);
3810 
3811     for (sal_uInt16 i = 0; i < nCnt; i++)
3812     {
3813         switch (rInfo.nTypeArray[i])
3814         {
3815         case NF_SYMBOLTYPE_CALENDAR :
3816             if ( !aOrgCalendar.getLength() )
3817             {
3818                 aOrgCalendar = rCal.getUniqueID();
3819                 fOrgDateTime = rCal.getDateTime();
3820             }
3821             rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLanguageTag().getLocale() );
3822             rCal.setDateTime( fOrgDateTime );
3823             ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3824             break;
3825         case NF_SYMBOLTYPE_STAR:
3826             if( bStarFlag )
3827             {
3828                 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
3829             }
3830             break;
3831         case NF_SYMBOLTYPE_BLANK:
3832             if (rInfo.sStrArray[i].getLength() >= 2)
3833                 InsertBlanks( sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
3834             break;
3835         case NF_SYMBOLTYPE_STRING:
3836         case NF_SYMBOLTYPE_CURRENCY:
3837         case NF_SYMBOLTYPE_DATESEP:
3838         case NF_SYMBOLTYPE_TIMESEP:
3839         case NF_SYMBOLTYPE_TIME100SECSEP:
3840             sBuff.append(rInfo.sStrArray[i]);
3841             break;
3842         case NF_KEY_M:                  // M
3843             aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_MONTH, nNatNum );
3844             // NatNum12: support variants of preposition, suffixation or article
3845             // for example, Catalan "de març", but "d'abril" etc.
3846             if ( bUseSpellout )
3847             {
3848                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3849             }
3850             sBuff.append(aStr);
3851             break;
3852         case NF_KEY_MM:                 // MM
3853             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_MONTH, nNatNum ));
3854             break;
3855         case NF_KEY_MMM:                // MMM
3856         case NF_KEY_MMMM:               // MMMM
3857         case NF_KEY_MMMMM:              // MMMMM
3858             // NatNum12: support variants of preposition, suffixation or
3859             // article, or capitalize, upper, lower, title.
3860             // Note: result of the "spell out" conversion can depend from the optional
3861             // PartitiveMonths or GenitiveMonths defined in the locale data,
3862             // see description of ImpUseMonthCase(), and locale data in
3863             // i18npool/source/localedata/data/ and libnumbertext
3864             aStr = rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3865                                                            static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3866                                                            nNatNum);
3867             if ( bUseSpellout )
3868             {
3869                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3870             }
3871             sBuff.append(aStr);
3872             break;
3873         case NF_KEY_Q:                  // Q
3874             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_QUARTER, nNatNum ));
3875             break;
3876         case NF_KEY_QQ:                 // QQ
3877             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_QUARTER, nNatNum ));
3878             break;
3879         case NF_KEY_D:                  // D
3880             aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY, nNatNum );
3881             // NatNum12: support variants of preposition, suffixation or article
3882             if ( bUseSpellout )
3883             {
3884                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3885             }
3886             sBuff.append(aStr);
3887             break;
3888         case NF_KEY_DD:                 // DD
3889             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY, nNatNum ));
3890             break;
3891         case NF_KEY_DDD:                // DDD
3892             if ( bOtherCalendar )
3893             {
3894                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3895             }
3896             aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum );
3897             // NatNum12: support at least capitalize, upper, lower, title
3898             if ( bUseSpellout )
3899             {
3900                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3901             }
3902             sBuff.append(aStr);
3903             if ( bOtherCalendar )
3904             {
3905                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3906             }
3907             break;
3908         case NF_KEY_DDDD:               // DDDD
3909             if ( bOtherCalendar )
3910             {
3911                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3912             }
3913             aStr = rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum );
3914             // NatNum12: support variants of preposition, suffixation or article
3915             if ( bUseSpellout )
3916             {
3917                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3918             }
3919             sBuff.append(aStr);
3920             if ( bOtherCalendar )
3921             {
3922                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3923             }
3924             break;
3925         case NF_KEY_YY:                 // YY
3926             if ( bOtherCalendar )
3927             {
3928                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3929             }
3930             // Prepend a minus sign if Gregorian BCE and era is not displayed.
3931             if (lcl_isSignedYear( rCal, NumFor[nIx] ))
3932             {
3933                 sBuff.append('-');
3934             }
3935             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3936             if ( bOtherCalendar )
3937             {
3938                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3939             }
3940             break;
3941         case NF_KEY_YYYY:               // YYYY
3942             if ( bOtherCalendar )
3943             {
3944                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3945             }
3946             // Prepend a minus sign if Gregorian BCE and era is not displayed.
3947             if (lcl_isSignedYear( rCal, NumFor[nIx] ))
3948             {
3949                 sBuff.append('-');
3950             }
3951             aStr = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
3952             if (aStr.getLength() < 4 && !lcl_hasEra(NumFor[nIx]))
3953             {
3954                 using namespace comphelper::string;
3955                 // Ensure that year consists of at least 4 digits, so it
3956                 // can be distinguished from 2 digits display and edited
3957                 // without suddenly being hit by the 2-digit year magic.
3958                 OUStringBuffer aBuf;
3959                 padToLength(aBuf, 4 - aStr.getLength(), '0');
3960                 ::impTransliterate(aBuf, NumFor[nIx].GetNatNum(), rNatNum);
3961                 aBuf.append(aStr);
3962                 aStr = aBuf.makeStringAndClear();
3963             }
3964             // NatNum12: support variants of preposition, suffixation or article
3965             if ( bUseSpellout )
3966             {
3967                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3968             }
3969             sBuff.append(aStr);
3970             if ( bOtherCalendar )
3971             {
3972                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3973             }
3974             break;
3975         case NF_KEY_EC:                 // E
3976             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3977             break;
3978         case NF_KEY_EEC:                // EE
3979         case NF_KEY_R:                  // R
3980             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum ));
3981             break;
3982         case NF_KEY_NN:                 // NN
3983         case NF_KEY_AAA:                // AAA
3984             aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum );
3985             // NatNum12: support at least capitalize, upper, lower, title
3986             if ( bUseSpellout )
3987             {
3988                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3989             }
3990             sBuff.append(aStr);
3991             break;
3992         case NF_KEY_NNN:                // NNN
3993         case NF_KEY_AAAA:               // AAAA
3994             aStr = rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum );
3995             // NatNum12: support variants of preposition, suffixation or article
3996             if ( bUseSpellout )
3997             {
3998                 aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3999             }
4000             sBuff.append(aStr);
4001             break;
4002         case NF_KEY_NNNN:               // NNNN
4003             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4004             sBuff.append(rLoc().getLongDateDayOfWeekSep());
4005             break;
4006         case NF_KEY_WW :                // WW
4007             sBuff.append(ImpIntToString(rNatNum, nIx,
4008                                          rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR )));
4009             break;
4010         case NF_KEY_G:                  // G
4011             ImpAppendEraG(sBuff, rCal, nNatNum );
4012             break;
4013         case NF_KEY_GG:                 // GG
4014             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
4015             break;
4016         case NF_KEY_GGG:                // GGG
4017             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_ERA, nNatNum ));
4018             break;
4019         case NF_KEY_RR:                 // RR => GGGEE
4020             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ));
4021             break;
4022         }
4023     }
4024     if ( aOrgCalendar.getLength() )
4025     {
4026         rCal.loadCalendar( aOrgCalendar, rLoc().getLanguageTag().getLocale() );  // restore calendar
4027     }
4028     return bRes;
4029 }
4030 
ImpGetDateTimeOutput(double fNumber,sal_uInt16 nIx,bool bStarFlag,const NativeNumberWrapper & rNatNum,const SvNFLanguageData & rCurrentLang,OUStringBuffer & sBuff) const4031 bool SvNumberformat::ImpGetDateTimeOutput(double fNumber,
4032                                           sal_uInt16 nIx,
4033                                           bool bStarFlag,
4034                                           const NativeNumberWrapper& rNatNum,
4035                                           const SvNFLanguageData& rCurrentLang,
4036                                           OUStringBuffer& sBuff) const
4037 {
4038     using namespace ::com::sun::star::i18n;
4039     bool bRes = false;
4040 
4041     CalendarWrapper& rCal = *rCurrentLang.GetCalendar();
4042     if (!lcl_getValidDate( rScan.GetNullDate(), rCal.getEpochStart(), fNumber))
4043     {
4044         sBuff = ImpSvNumberformatScan::sErrStr;
4045         return false;
4046     }
4047 
4048     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4049     bool bInputLine;
4050     sal_Int32 nCntPost, nFirstRounding;
4051     if ( rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION &&
4052          0 < rInfo.nCntPost && rInfo.nCntPost < kTimeSignificantRound )
4053     {
4054         bInputLine = true;
4055         nCntPost = nFirstRounding = kTimeSignificantRound;
4056     }
4057     else
4058     {
4059         bInputLine = false;
4060         nCntPost = rInfo.nCntPost;
4061         // For clock format (not []) do not round up to seconds and thus days.
4062         nFirstRounding = (rInfo.bThousand ? nCntPost : kTimeSignificantRound);
4063     }
4064     double fTime = (fNumber - floor( fNumber )) * 86400.0;
4065     fTime = ::rtl::math::round( fTime, int(nFirstRounding) );
4066     if (fTime >= 86400.0)
4067     {
4068         // result of fNumber==x.999999999... rounded up, use correct date/time
4069         fTime -= 86400.0;
4070         fNumber = floor( fNumber + 0.5) + fTime;
4071     }
4072     rCal.setLocalDateTime( fNumber );
4073 
4074     int nUseMonthCase = 0; // Not decided yet
4075     OUString aOrgCalendar; // empty => not changed yet
4076     double fOrgDateTime(0.0);
4077     bool bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx], *rCurrentLang.GetCalendar() );
4078     if ( bOtherCalendar )
4079     {
4080         SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4081     }
4082     if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() ) )
4083     {
4084         bOtherCalendar = false;
4085     }
4086     sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum();
4087 
4088     OUStringBuffer sSecStr;
4089     sal_Int32 nSecPos = 0; // For figure by figure processing
4090     sal_uInt32 nHour, nMin, nSec;
4091     if (!rInfo.bThousand) // No [] format
4092     {
4093         sal_uInt16 nCHour, nCMinute, nCSecond;
4094         double fFractionOfSecond;
4095         tools::Time::GetClock( fNumber, nCHour, nCMinute, nCSecond, fFractionOfSecond, nCntPost);
4096         nHour = nCHour;
4097         nMin = nCMinute;
4098         nSec = nCSecond;
4099         nCntPost = ImpGetFractionOfSecondString( sSecStr, fFractionOfSecond, nCntPost, true, nIx,
4100                 (bInputLine ? rInfo.nCntPost : 0), rNatNum );
4101     }
4102     else
4103     {
4104         sal_uInt32 nSeconds = static_cast<sal_uInt32>(floor( fTime ));
4105 
4106         nCntPost = ImpGetFractionOfSecondString( sSecStr, fTime - nSeconds, nCntPost, false, nIx,
4107                 (bInputLine ? rInfo.nCntPost : 0), rNatNum );
4108 
4109         if (rInfo.nThousand == 3) // [ss]
4110         {
4111             nHour = 0;
4112             nMin = 0;
4113             nSec = nSeconds;
4114         }
4115         else if (rInfo.nThousand == 2) // [mm]:ss
4116         {
4117             nHour = 0;
4118             nMin = nSeconds / 60;
4119             nSec = nSeconds % 60;
4120         }
4121         else if (rInfo.nThousand == 1) // [hh]:mm:ss
4122         {
4123             nHour = nSeconds / 3600;
4124             nMin = (nSeconds%3600) / 60;
4125             nSec = nSeconds%60;
4126         }
4127         else
4128         {
4129             nHour = 0;  // TODO What should these values be?
4130             nMin  = 0;
4131             nSec  = 0;
4132         }
4133     }
4134     sal_Unicode cAmPm = ' '; // a or p
4135     if (rInfo.nCntExp) // AM/PM
4136     {
4137         if (nHour == 0)
4138         {
4139             nHour = 12;
4140             cAmPm = 'a';
4141         }
4142         else if (nHour < 12)
4143         {
4144             cAmPm = 'a';
4145         }
4146         else
4147         {
4148             cAmPm = 'p';
4149             if (nHour > 12)
4150             {
4151                 nHour -= 12;
4152             }
4153         }
4154     }
4155     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
4156     sal_Int32 nLen;
4157     OUString aYear;
4158     for (sal_uInt16 i = 0; i < nCnt; i++)
4159     {
4160         switch (rInfo.nTypeArray[i])
4161         {
4162         case NF_SYMBOLTYPE_CALENDAR :
4163             if ( !aOrgCalendar.getLength() )
4164             {
4165                 aOrgCalendar = rCal.getUniqueID();
4166                 fOrgDateTime = rCal.getDateTime();
4167             }
4168             rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLanguageTag().getLocale() );
4169             rCal.setDateTime( fOrgDateTime );
4170             ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4171             break;
4172         case NF_SYMBOLTYPE_STAR:
4173             if( bStarFlag )
4174             {
4175                 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
4176             }
4177             break;
4178         case NF_SYMBOLTYPE_BLANK:
4179             if (rInfo.sStrArray[i].getLength() >= 2)
4180                 InsertBlanks( sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
4181             break;
4182         case NF_SYMBOLTYPE_STRING:
4183         case NF_SYMBOLTYPE_CURRENCY:
4184         case NF_SYMBOLTYPE_DATESEP:
4185         case NF_SYMBOLTYPE_TIMESEP:
4186         case NF_SYMBOLTYPE_TIME100SECSEP:
4187             sBuff.append(rInfo.sStrArray[i]);
4188             break;
4189         case NF_SYMBOLTYPE_DIGIT:
4190             nLen = ( bInputLine && i > 0 &&
4191                      (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING ||
4192                       rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ?
4193                      nCntPost : rInfo.sStrArray[i].getLength() );
4194             for (sal_Int32 j = 0; j < nLen && nSecPos < nCntPost && nSecPos < sSecStr.getLength(); ++j)
4195             {
4196                 sBuff.append(sSecStr[ nSecPos ]);
4197                 nSecPos++;
4198             }
4199             break;
4200         case NF_KEY_AMPM:               // AM/PM
4201             if (cAmPm == 'a')
4202             {
4203                 sBuff.append(rCal.getDisplayName( CalendarDisplayIndex::AM_PM,
4204                                                   AmPmValue::AM, 0 ));
4205             }
4206             else
4207             {
4208                 sBuff.append(rCal.getDisplayName( CalendarDisplayIndex::AM_PM,
4209                                                   AmPmValue::PM, 0 ));
4210             }
4211             break;
4212         case NF_KEY_AP:                 // A/P
4213             if (cAmPm == 'a')
4214             {
4215                 sBuff.append('a');
4216             }
4217             else
4218             {
4219                 sBuff.append('p');
4220             }
4221             break;
4222         case NF_KEY_MI:                 // M
4223             sBuff.append(ImpIntToString(rNatNum, nIx, nMin ));
4224             break;
4225         case NF_KEY_MMI:                // MM
4226             sBuff.append(ImpIntToString(rNatNum, nIx, nMin, 2 ));
4227             break;
4228         case NF_KEY_H:                  // H
4229             sBuff.append(ImpIntToString(rNatNum, nIx, nHour ));
4230             break;
4231         case NF_KEY_HH:                 // HH
4232             sBuff.append(ImpIntToString(rNatNum, nIx, nHour, 2 ));
4233             break;
4234         case NF_KEY_S:                  // S
4235             sBuff.append(ImpIntToString(rNatNum, nIx, nSec ));
4236             break;
4237         case NF_KEY_SS:                 // SS
4238             sBuff.append(ImpIntToString(rNatNum, nIx, nSec, 2 ));
4239             break;
4240         case NF_KEY_M:                  // M
4241             sBuff.append(rCal.getDisplayString(
4242                              CalendarDisplayCode::SHORT_MONTH, nNatNum ));
4243             break;
4244         case NF_KEY_MM:                 // MM
4245             sBuff.append(rCal.getDisplayString(
4246                              CalendarDisplayCode::LONG_MONTH, nNatNum ));
4247             break;
4248         case NF_KEY_MMM:                // MMM
4249             sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4250                                                                  static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4251                                                 nNatNum));
4252             break;
4253         case NF_KEY_MMMM:               // MMMM
4254             sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4255                                                                  static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4256                                                 nNatNum));
4257             break;
4258         case NF_KEY_MMMMM:              // MMMMM
4259             sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4260                                                                  static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4261                                                 nNatNum));
4262             break;
4263         case NF_KEY_Q:                  // Q
4264             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_QUARTER, nNatNum ));
4265             break;
4266         case NF_KEY_QQ:                 // QQ
4267             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_QUARTER, nNatNum ));
4268             break;
4269         case NF_KEY_D:                  // D
4270             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY, nNatNum ));
4271             break;
4272         case NF_KEY_DD:                 // DD
4273             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY, nNatNum ));
4274             break;
4275         case NF_KEY_DDD:                // DDD
4276             if ( bOtherCalendar )
4277             {
4278                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4279             }
4280             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
4281             if ( bOtherCalendar )
4282             {
4283                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4284             }
4285             break;
4286         case NF_KEY_DDDD:               // DDDD
4287             if ( bOtherCalendar )
4288             {
4289                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4290             }
4291             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4292             if ( bOtherCalendar )
4293             {
4294                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4295             }
4296             break;
4297         case NF_KEY_YY:                 // YY
4298             if ( bOtherCalendar )
4299             {
4300                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4301             }
4302             // Prepend a minus sign if Gregorian BCE and era is not displayed.
4303             if (lcl_isSignedYear( rCal, NumFor[nIx] ))
4304             {
4305                 sBuff.append('-');
4306             }
4307             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
4308             if ( bOtherCalendar )
4309             {
4310                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4311             }
4312             break;
4313         case NF_KEY_YYYY:               // YYYY
4314             if ( bOtherCalendar )
4315             {
4316                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4317             }
4318             // Prepend a minus sign if Gregorian BCE and era is not displayed.
4319             if (lcl_isSignedYear( rCal, NumFor[nIx] ))
4320             {
4321                 sBuff.append('-');
4322             }
4323             aYear = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
4324             if (aYear.getLength() < 4 && !lcl_hasEra(NumFor[nIx]))
4325             {
4326                 using namespace comphelper::string;
4327                 // Ensure that year consists of at least 4 digits, so it
4328                 // can be distinguished from 2 digits display and edited
4329                 // without suddenly being hit by the 2-digit year magic.
4330                 OUStringBuffer aBuf;
4331                 padToLength(aBuf, 4 - aYear.getLength(), '0');
4332                 ::impTransliterate(aBuf, NumFor[nIx].GetNatNum(), rNatNum);
4333                 aBuf.append(aYear);
4334                 sBuff.append(aBuf);
4335             }
4336             else
4337             {
4338                 sBuff.append(aYear);
4339             }
4340             if ( bOtherCalendar )
4341             {
4342                 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4343             }
4344             break;
4345         case NF_KEY_EC:                 // E
4346             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
4347             break;
4348         case NF_KEY_EEC:                // EE
4349         case NF_KEY_R:                  // R
4350             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum ));
4351             break;
4352         case NF_KEY_NN:                 // NN
4353         case NF_KEY_AAA:                // AAA
4354             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
4355             break;
4356         case NF_KEY_NNN:                // NNN
4357         case NF_KEY_AAAA:               // AAAA
4358             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4359             break;
4360         case NF_KEY_NNNN:               // NNNN
4361             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4362             sBuff.append(rLoc().getLongDateDayOfWeekSep());
4363             break;
4364         case NF_KEY_WW :                // WW
4365             sBuff.append(ImpIntToString(rNatNum, nIx, rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR )));
4366             break;
4367         case NF_KEY_G:                  // G
4368             ImpAppendEraG( sBuff, rCal, nNatNum );
4369             break;
4370         case NF_KEY_GG:                 // GG
4371             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
4372             break;
4373         case NF_KEY_GGG:                // GGG
4374             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_ERA, nNatNum ));
4375             break;
4376         case NF_KEY_RR:                 // RR => GGGEE
4377             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ));
4378             break;
4379         }
4380     }
4381     if ( aOrgCalendar.getLength() )
4382     {
4383         rCal.loadCalendar( aOrgCalendar, rLoc().getLanguageTag().getLocale() );  // restore calendar
4384     }
4385     return bRes;
4386 }
4387 
ImpGetLogicalOutput(double fNumber,sal_uInt16 nIx,const NativeNumberWrapper & rNatNum,OUStringBuffer & sStr) const4388 bool SvNumberformat::ImpGetLogicalOutput(double fNumber,
4389                                          sal_uInt16 nIx,
4390                                          const NativeNumberWrapper& rNatNum,
4391                                          OUStringBuffer& sStr) const
4392 {
4393     bool bRes = false;
4394     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4395     const sal_uInt16 nCnt = NumFor[nIx].GetCount();
4396     for (sal_uInt16 j = 0; j < nCnt; ++j)
4397     {
4398         switch (rInfo.nTypeArray[j])
4399         {
4400             case NF_KEY_BOOLEAN:
4401                 sStr.append( fNumber ? rScan.GetTrueString() : rScan.GetFalseString());
4402             break;
4403             case NF_SYMBOLTYPE_STRING:
4404                 sStr.append( rInfo.sStrArray[j]);
4405             break;
4406         }
4407     }
4408     ::impTransliterate(sStr, NumFor[nIx].GetNatNum(), rNatNum);
4409     return bRes;
4410 }
4411 
ImpGetNumberOutput(double fNumber,sal_uInt16 nIx,bool bStarFlag,const NativeNumberWrapper & rNatNum,OUStringBuffer & sStr) const4412 bool SvNumberformat::ImpGetNumberOutput(double fNumber,
4413                                         sal_uInt16 nIx,
4414                                         bool bStarFlag,
4415                                         const NativeNumberWrapper& rNatNum,
4416                                         OUStringBuffer& sStr) const
4417 {
4418     bool bRes = false;
4419     bool bSign;
4420     if (fNumber < 0.0)
4421     {
4422         bSign = (nIx == 0); // Not in the ones at the back;
4423         fNumber = -fNumber;
4424     }
4425     else
4426     {
4427         bSign = false;
4428         if ( std::signbit( fNumber ) )
4429         {
4430             fNumber = -fNumber; // yes, -0.0 is possible, eliminate '-'
4431         }
4432     }
4433     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4434     if (rInfo.eScannedType == SvNumFormatType::PERCENT)
4435     {
4436         if (fNumber < D_MAX_D_BY_100)
4437         {
4438             fNumber *= 100.0;
4439         }
4440         else
4441         {
4442             sStr = ImpSvNumberformatScan::sErrStr;
4443             return false;
4444         }
4445     }
4446     sal_uInt16 i, j;
4447     sal_Int32 nDecPos = -1;
4448     bool bInteger = false;
4449     if ( rInfo.nThousand != FLAG_STANDARD_IN_FORMAT )
4450     {
4451         // Special formatting only if no GENERAL keyword in format code
4452         const sal_uInt16 nThousand = rInfo.nThousand;
4453         tools::Long nPrecExp;
4454         for (i = 0; i < nThousand; i++)
4455         {
4456            if (fNumber > D_MIN_M_BY_1000)
4457            {
4458                fNumber /= 1000.0;
4459            }
4460            else
4461            {
4462                fNumber = 0.0;
4463            }
4464         }
4465         if (fNumber > 0.0)
4466         {
4467             nPrecExp = GetPrecExp( fNumber );
4468         }
4469         else
4470         {
4471             nPrecExp = 0;
4472         }
4473         // Make sure that Calc's ROUND and formatted output agree
4474         fNumber = rtl_math_round(fNumber, rInfo.nCntPost, rtl_math_RoundingMode_Corrected);
4475         if (rInfo.nCntPost) // Decimal places
4476         {
4477             if ((rInfo.nCntPost + nPrecExp) > 15 && nPrecExp < 15)
4478             {
4479                 sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, 15-nPrecExp, '.');
4480                 for (tools::Long l = 15-nPrecExp; l < static_cast<tools::Long>(rInfo.nCntPost); l++)
4481                 {
4482                     sStr.append('0');
4483                 }
4484             }
4485             else
4486             {
4487                 sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, rInfo.nCntPost, '.' );
4488             }
4489             sStr.stripStart('0'); // Strip leading zeros
4490         }
4491         else if (fNumber == 0.0) // Null
4492         {
4493             // Nothing to be done here, keep empty string sStr,
4494             // ImpNumberFillWithThousands does the rest
4495         }
4496         else // Integer
4497         {
4498             sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, 0, '.');
4499             sStr.stripStart('0'); // Strip leading zeros
4500         }
4501         nDecPos = sStr.indexOf('.' );
4502         if ( nDecPos >= 0)
4503         {
4504             const sal_Unicode* p = sStr.getStr() + nDecPos;
4505             while ( *++p == '0' )
4506                 ;
4507             if ( !*p )
4508             {
4509                 bInteger = true;
4510             }
4511             sStr.remove( nDecPos, 1 ); //  Remove .
4512         }
4513         if (bSign && (sStr.isEmpty() || checkForAll0s(sStr)))   // Only 00000
4514         {
4515             bSign = false;              // Not -0.00
4516         }
4517     }                                   // End of != FLAG_STANDARD_IN_FORMAT
4518 
4519                                         // Edit backwards:
4520     j = NumFor[nIx].GetCount()-1;       // Last symbol
4521                                         // Decimal places:
4522     bRes |= ImpDecimalFill(rNatNum, sStr, fNumber, nDecPos, j, nIx, bInteger, bStarFlag);
4523     if (bSign)
4524     {
4525         sStr.insert(0, '-');
4526     }
4527     ::impTransliterate(sStr, NumFor[nIx].GetNatNum(), rNatNum);
4528     return bRes;
4529 }
4530 
ImpDecimalFill(const NativeNumberWrapper & rNatNum,OUStringBuffer & sStr,double & rNumber,sal_Int32 nDecPos,sal_uInt16 j,sal_uInt16 nIx,bool bInteger,bool bStarFlag) const4531 bool SvNumberformat::ImpDecimalFill(const NativeNumberWrapper& rNatNum,
4532                                    OUStringBuffer& sStr,  // number string
4533                                    double& rNumber,       // number
4534                                    sal_Int32 nDecPos,     // decimals start
4535                                    sal_uInt16 j,          // symbol index within format code
4536                                    sal_uInt16 nIx,        // subformat index
4537                                    bool bInteger,         // is integer
4538                                    bool bStarFlag) const
4539 {
4540     bool bRes = false;
4541     bool bFilled = false;               // Was filled?
4542     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4543     sal_Int32 k = sStr.getLength();     // After last figure
4544                                         // Decimal places:
4545     if (rInfo.nCntPost > 0)
4546     {
4547         bool bTrailing = true;          // Trailing zeros?
4548         short nType;
4549         while (j > 0 &&                 // Backwards
4550                (nType = rInfo.nTypeArray[j]) != NF_SYMBOLTYPE_DECSEP)
4551         {
4552             switch ( nType )
4553             {
4554             case NF_SYMBOLTYPE_STAR:
4555                 if( bStarFlag )
4556                 {
4557                     bRes = lcl_insertStarFillChar( sStr, k, rInfo.sStrArray[j]);
4558                 }
4559                 break;
4560             case NF_SYMBOLTYPE_BLANK:
4561                 if (rInfo.sStrArray[j].getLength() >= 2)
4562                     /*k = */ InsertBlanks(sStr, k, rInfo.sStrArray[j][1] );
4563                 break;
4564             case NF_SYMBOLTYPE_STRING:
4565             case NF_SYMBOLTYPE_CURRENCY:
4566             case NF_SYMBOLTYPE_PERCENT:
4567                 sStr.insert(k, rInfo.sStrArray[j]);
4568                 break;
4569             case NF_SYMBOLTYPE_THSEP:
4570                 if (rInfo.nThousand == 0)
4571                 {
4572                     sStr.insert(k, rInfo.sStrArray[j]);
4573                 }
4574                 break;
4575             case NF_SYMBOLTYPE_DIGIT:
4576             {
4577                 const OUString& rStr = rInfo.sStrArray[j];
4578                 const sal_Unicode* p1 = rStr.getStr();
4579                 const sal_Unicode* p = p1 + rStr.getLength();
4580                 // In case the number of decimals passed are less than the
4581                 // "digits" given, append trailing '0' characters, which here
4582                 // means insert them because literal strings may have been
4583                 // appended already. If they weren't to be '0' characters
4584                 // they'll be changed below, as if decimals with trailing zeros
4585                 // were passed.
4586                 if (nDecPos >= 0 && nDecPos <= k)
4587                 {
4588                     sal_Int32 nAppend = rStr.getLength() - (k - nDecPos);
4589                     while (nAppend-- > 0)
4590                     {
4591                         sStr.insert( k++, '0');
4592                     }
4593                 }
4594                 while (k && p1 < p--)
4595                 {
4596                     const sal_Unicode c = *p;
4597                     k--;
4598                     if ( sStr[k] != '0' )
4599                     {
4600                         bTrailing = false;
4601                         bFilled = true;
4602                     }
4603                     if (bTrailing)
4604                     {
4605                         if ( c == '0' )
4606                         {
4607                             bFilled = true;
4608                         }
4609                         else if ( c == '-' )
4610                         {
4611                             if ( bInteger )
4612                             {
4613                                 sStr[ k ] = '-';
4614                             }
4615                             bFilled = true;
4616                         }
4617                         else if ( c == '?' )
4618                         {
4619                             sStr[ k ] = cBlankDigit;
4620                             bFilled = true;
4621                         }
4622                         else if ( !bFilled ) // #
4623                         {
4624                             sStr.remove(k,1);
4625                         }
4626                     }
4627                 } // of for
4628                 break;
4629             } // of case digi
4630             case NF_KEY_CCC: // CCC currency
4631                 sStr.insert(k, rScan.GetCurAbbrev());
4632                 break;
4633             case NF_KEY_GENERAL: // Standard in the String
4634             {
4635                 OUStringBuffer sNum;
4636                 ImpGetOutputStandard(rNumber, sNum, rNatNum);
4637                 sNum.stripStart('-');
4638                 sStr.insert(k, sNum);
4639                 break;
4640             }
4641             default:
4642                 break;
4643             } // of switch
4644             j--;
4645         } // of while
4646     } // of decimal places
4647 
4648     bRes |= ImpNumberFillWithThousands(rNatNum, sStr, rNumber, k, j, nIx, // Fill with . if needed
4649                                        rInfo.nCntPre, bStarFlag, bFilled );
4650 
4651     return bRes;
4652 }
4653 
ImpNumberFillWithThousands(const NativeNumberWrapper & rNatNum,OUStringBuffer & sBuff,double & rNumber,sal_Int32 k,sal_uInt16 j,sal_uInt16 nIx,sal_Int32 nDigCnt,bool bStarFlag,bool bAddDecSep) const4654 bool SvNumberformat::ImpNumberFillWithThousands( const NativeNumberWrapper& rNatNum,
4655                                                  OUStringBuffer& sBuff,  // number string
4656                                                  double& rNumber,       // number
4657                                                  sal_Int32 k,           // position within string
4658                                                  sal_uInt16 j,          // symbol index within format code
4659                                                  sal_uInt16 nIx,        // subformat index
4660                                                  sal_Int32 nDigCnt,     // count of integer digits in format
4661                                                  bool bStarFlag,
4662                                                  bool bAddDecSep) const // add decimal separator if necessary
4663 {
4664     bool bRes = false;
4665     sal_Int32 nLeadingStringChars = 0; // inserted StringChars before number
4666     sal_Int32 nDigitCount = 0;         // count of integer digits from the right
4667     bool bStop = false;
4668     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4669     // no normal thousands separators if number divided by thousands
4670     bool bDoThousands = (rInfo.nThousand == 0);
4671     utl::DigitGroupingIterator aGrouping( GetCurrentLanguageData().GetLocaleData()->getDigitGrouping());
4672 
4673     while (!bStop) // backwards
4674     {
4675         if (j == 0)
4676         {
4677             bStop = true;
4678         }
4679         switch (rInfo.nTypeArray[j])
4680         {
4681         case NF_SYMBOLTYPE_DECSEP:
4682             aGrouping.reset();
4683             [[fallthrough]];
4684         case NF_SYMBOLTYPE_STRING:
4685             if ( rInfo.nTypeArray[j] == NF_SYMBOLTYPE_STRING && nDigCnt == 0 )
4686             {
4687                 // tdf#159930 no integer in format ".###"
4688                 k = 0; // insert string at the beginning
4689             }
4690             [[fallthrough]];
4691         case NF_SYMBOLTYPE_CURRENCY:
4692         case NF_SYMBOLTYPE_PERCENT:
4693             if ( rInfo.nTypeArray[j] != NF_SYMBOLTYPE_DECSEP || bAddDecSep )
4694                 sBuff.insert(k, rInfo.sStrArray[j]);
4695             if ( k == 0 )
4696             {
4697                 nLeadingStringChars = nLeadingStringChars + rInfo.sStrArray[j].getLength();
4698             }
4699             break;
4700         case NF_SYMBOLTYPE_STAR:
4701             if( bStarFlag )
4702             {
4703                 bRes = lcl_insertStarFillChar( sBuff, k, rInfo.sStrArray[j]);
4704             }
4705             break;
4706         case NF_SYMBOLTYPE_BLANK:
4707             if (rInfo.sStrArray[j].getLength() >= 2)
4708                 /*k = */ InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] );
4709             break;
4710         case NF_SYMBOLTYPE_THSEP:
4711             // #i7284# #102685# Insert separator also if number is divided
4712             // by thousands and the separator is specified somewhere in
4713             // between and not only at the end.
4714             // #i12596# But do not insert if it's a parenthesized negative
4715             // format like (#,)
4716             // In fact, do not insert if divided and regex [0#,],[^0#] and
4717             // no other digit symbol follows (which was already detected
4718             // during scan of format code, otherwise there would be no
4719             // division), else do insert. Same in ImpNumberFill() below.
4720             if ( !bDoThousands && j < NumFor[nIx].GetCount()-1 )
4721             {
4722                 bDoThousands = ((j == 0) ||
4723                                 (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT &&
4724                                  rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) ||
4725                                 (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT));
4726             }
4727             if ( bDoThousands )
4728             {
4729                 if (k > 0)
4730                 {
4731                     sBuff.insert(k, rInfo.sStrArray[j]);
4732                 }
4733                 else if (nDigitCount < nDigCnt)
4734                 {
4735                     // Leading '#' displays nothing (e.g. no leading
4736                     // separator for numbers <1000 with #,##0 format).
4737                     // Leading '?' displays blank.
4738                     // Everything else, including nothing, displays the
4739                     // separator.
4740                     sal_Unicode cLeader = 0;
4741                     if (j > 0 && rInfo.nTypeArray[j-1] == NF_SYMBOLTYPE_DIGIT)
4742                     {
4743                         const OUString& rStr = rInfo.sStrArray[j-1];
4744                         sal_Int32 nLen = rStr.getLength();
4745                         if (nLen)
4746                         {
4747                             cLeader = rStr[ nLen - 1 ];
4748                         }
4749                     }
4750                     switch (cLeader)
4751                     {
4752                     case '#':
4753                         ;   // nothing
4754                         break;
4755                     case '?':
4756                         // replace thousand separator with blank
4757                         sBuff.insert(k, ' ');
4758                         break;
4759                     default:
4760                         sBuff.insert(k, rInfo.sStrArray[j]);
4761                     }
4762                 }
4763                 aGrouping.advance();
4764             }
4765             break;
4766         case NF_SYMBOLTYPE_DIGIT:
4767         {
4768             const OUString& rStr = rInfo.sStrArray[j];
4769             const sal_Unicode* p1 = rStr.getStr();
4770             const sal_Unicode* p = p1 + rStr.getLength();
4771             while ( p1 < p-- )
4772             {
4773                 nDigitCount++;
4774                 if (k > 0)
4775                 {
4776                     k--;
4777                 }
4778                 else
4779                 {
4780                     switch (*p)
4781                     {
4782                     case '0':
4783                         sBuff.insert(0, '0');
4784                         break;
4785                     case '?':
4786                         sBuff.insert(0, cBlankDigit);
4787                         break;
4788                     }
4789                 }
4790                 if (nDigitCount == nDigCnt && k > 0)
4791                 {
4792                     // more digits than specified
4793                     ImpDigitFill(sBuff, 0, k, nIx, nDigitCount, aGrouping);
4794                 }
4795             }
4796             break;
4797         }
4798         case NF_KEY_CCC: // CCC currency
4799             sBuff.insert(k, rScan.GetCurAbbrev());
4800             break;
4801         case NF_KEY_GENERAL: // "General" in string
4802         {
4803             OUStringBuffer sNum;
4804             ImpGetOutputStandard(rNumber, sNum, rNatNum);
4805             sNum.stripStart('-');
4806             sBuff.insert(k, sNum);
4807             break;
4808         }
4809         default:
4810             break;
4811         } // switch
4812         j--; // next format code string
4813     } // while
4814 
4815     k = k + nLeadingStringChars;    // MSC converts += to int and then warns, so ...
4816     if (k > nLeadingStringChars)
4817     {
4818         ImpDigitFill(sBuff, nLeadingStringChars, k, nIx, nDigitCount, aGrouping);
4819     }
4820     return bRes;
4821 }
4822 
ImpDigitFill(OUStringBuffer & sStr,sal_Int32 nStart,sal_Int32 & k,sal_uInt16 nIx,sal_Int32 & nDigitCount,utl::DigitGroupingIterator & rGrouping) const4823 void SvNumberformat::ImpDigitFill(OUStringBuffer& sStr,     // number string
4824                                   sal_Int32 nStart,         // start of digits
4825                                   sal_Int32 & k,            // position within string
4826                                   sal_uInt16 nIx,           // subformat index
4827                                   sal_Int32 & nDigitCount,  // count of integer digits from the right so far
4828                                   utl::DigitGroupingIterator & rGrouping ) const // current grouping
4829 {
4830     if (NumFor[nIx].Info().bThousand) // Only if grouping fill in separators
4831     {
4832         const OUString& rThousandSep = GetCurrentLanguageData().GetNumThousandSep();
4833         while (k > nStart)
4834         {
4835             if (nDigitCount == rGrouping.getPos())
4836             {
4837                 sStr.insert( k, rThousandSep );
4838                 rGrouping.advance();
4839             }
4840             nDigitCount++;
4841             k--;
4842         }
4843     }
4844     else // simply skip
4845     {
4846         k = nStart;
4847     }
4848 }
4849 
ImpNumberFill(const NativeNumberWrapper & rNatNum,OUStringBuffer & sBuff,double & rNumber,sal_Int32 & k,sal_uInt16 & j,sal_uInt16 nIx,short eSymbolType,bool bStarFlag,bool bInsertRightBlank) const4850 bool SvNumberformat::ImpNumberFill( const NativeNumberWrapper& rNatNum,
4851                                     OUStringBuffer& sBuff, // number string
4852                                     double& rNumber,       // number for "General" format
4853                                     sal_Int32& k,          // position within string
4854                                     sal_uInt16& j,         // symbol index within format code
4855                                     sal_uInt16 nIx,        // subformat index
4856                                     short eSymbolType,     // type of stop condition
4857                                     bool bStarFlag,
4858                                     bool bInsertRightBlank) const // insert blank on right for denominator (default = false)
4859 {
4860     bool bRes = false;
4861     bool bStop = false;
4862     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4863     // no normal thousands separators if number divided by thousands
4864     bool bDoThousands = (rInfo.nThousand == 0);
4865     bool bFoundNumber = false;
4866     short nType;
4867 
4868     k = sBuff.getLength(); // behind last digit
4869 
4870     while (!bStop && (nType = rInfo.nTypeArray[j]) != eSymbolType ) // Backwards
4871     {
4872         switch ( nType )
4873         {
4874         case NF_SYMBOLTYPE_STAR:
4875             if( bStarFlag )
4876             {
4877                 if ( bFoundNumber && eSymbolType != NF_SYMBOLTYPE_EXP )
4878                     k = 0; // tdf#100842 jump to beginning of number before inserting something else
4879                 bRes = lcl_insertStarFillChar( sBuff, k, rInfo.sStrArray[j]);
4880             }
4881             break;
4882         case NF_SYMBOLTYPE_BLANK:
4883             if (rInfo.sStrArray[j].getLength() >= 2)
4884             {
4885                 if ( bFoundNumber && eSymbolType != NF_SYMBOLTYPE_EXP )
4886                     k = 0; // tdf#100842 jump to beginning of number before inserting something else
4887                 k = InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] );
4888             }
4889             break;
4890         case NF_SYMBOLTYPE_THSEP:
4891             // Same as in ImpNumberFillWithThousands() above, do not insert
4892             // if divided and regex [0#,],[^0#] and no other digit symbol
4893             // follows (which was already detected during scan of format
4894             // code, otherwise there would be no division), else do insert.
4895             if ( !bDoThousands && j < NumFor[nIx].GetCount()-1 )
4896             {
4897                 bDoThousands = ((j == 0) ||
4898                                 (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT &&
4899                                  rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) ||
4900                                 (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT));
4901             }
4902             if ( bDoThousands && k > 0 )
4903             {
4904                 sBuff.insert(k, rInfo.sStrArray[j]);
4905             }
4906             break;
4907         case NF_SYMBOLTYPE_DIGIT:
4908         {
4909             bFoundNumber = true;
4910             sal_uInt16 nPosInsertBlank = bInsertRightBlank ? k : 0; // left alignment of denominator
4911             const OUString& rStr = rInfo.sStrArray[j];
4912             const sal_Unicode* p1 = rStr.getStr();
4913             const sal_Unicode* p = p1 + rStr.getLength();
4914             while ( p1 < p-- )
4915             {
4916                 if (k > 0)
4917                 {
4918                     k--;
4919                 }
4920                 else
4921                 {
4922                     switch (*p)
4923                     {
4924                     case '0':
4925                         sBuff.insert(0, '0');
4926                         break;
4927                     case '?':
4928                         sBuff.insert(nPosInsertBlank, cBlankDigit);
4929                         break;
4930                     }
4931                 }
4932             }
4933         }
4934         break;
4935         case NF_KEY_CCC:                // CCC currency
4936             sBuff.insert(k, rScan.GetCurAbbrev());
4937             break;
4938         case NF_KEY_GENERAL: // Standard in the String
4939         {
4940             OUStringBuffer sNum;
4941             bFoundNumber = true;
4942             ImpGetOutputStandard(rNumber, sNum, rNatNum);
4943             sNum.stripStart('-');
4944             sBuff.insert(k, sNum);
4945         }
4946         break;
4947         case NF_SYMBOLTYPE_FRAC_FDIV: // Do Nothing
4948             if (k > 0)
4949             {
4950                 k--;
4951             }
4952             break;
4953 
4954         default:
4955             if ( bFoundNumber && eSymbolType != NF_SYMBOLTYPE_EXP )
4956                 k = 0; // tdf#100842 jump to beginning of number before inserting something else
4957             sBuff.insert(k, rInfo.sStrArray[j]);
4958             break;
4959         } // of switch
4960         if ( j )
4961             j--; // Next String
4962         else
4963             bStop = true;
4964     } // of while
4965     return bRes;
4966 }
4967 
GetFormatSpecialInfo(bool & bThousand,bool & IsRed,sal_uInt16 & nPrecision,sal_uInt16 & nLeadingCnt) const4968 void SvNumberformat::GetFormatSpecialInfo(bool& bThousand,
4969                                           bool& IsRed,
4970                                           sal_uInt16& nPrecision,
4971                                           sal_uInt16& nLeadingCnt) const
4972 {
4973     // as before: take info from nNumFor=0 for whole format (for dialog etc.)
4974 
4975     SvNumFormatType nDummyType;
4976     GetNumForInfo( 0, nDummyType, bThousand, nPrecision, nLeadingCnt );
4977 
4978     // "negative in red" is only useful for the whole format
4979 
4980     const Color* pColor = NumFor[1].GetColor();
4981     IsRed = fLimit1 == 0.0 && fLimit2 == 0.0 && pColor
4982         && (*pColor == ImpSvNumberformatScan::GetRedColor());
4983 }
4984 
GetNumForInfo(sal_uInt16 nNumFor,SvNumFormatType & rScannedType,bool & bThousand,sal_uInt16 & nPrecision,sal_uInt16 & nLeadingCnt) const4985 void SvNumberformat::GetNumForInfo( sal_uInt16 nNumFor, SvNumFormatType& rScannedType,
4986                     bool& bThousand, sal_uInt16& nPrecision, sal_uInt16& nLeadingCnt ) const
4987 {
4988     // take info from a specified sub-format (for XML export)
4989 
4990     if ( nNumFor > 3 )
4991     {
4992         return; // invalid
4993     }
4994 
4995     const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
4996     rScannedType = rInfo.eScannedType;
4997     bThousand = rInfo.bThousand;
4998     nPrecision = (rInfo.eScannedType == SvNumFormatType::FRACTION)
4999                     ? rInfo.nCntExp  // number of denominator digits for fraction
5000                     : rInfo.nCntPost;
5001     sal_Int32 nPosHash = 1;
5002     if ( rInfo.eScannedType == SvNumFormatType::FRACTION &&
5003             ( (nPosHash += GetDenominatorString(nNumFor).indexOf('#')) > 0 ) )
5004         nPrecision -= nPosHash;
5005     if (bStandard && rInfo.eScannedType == SvNumFormatType::NUMBER)
5006     {
5007         // StandardFormat
5008         nLeadingCnt = 1;
5009     }
5010     else
5011     {
5012         nLeadingCnt = 0;
5013         bool bStop = false;
5014         sal_uInt16 i = 0;
5015         const sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
5016         while (!bStop && i < nCnt)
5017         {
5018             short nType = rInfo.nTypeArray[i];
5019             if ( nType == NF_SYMBOLTYPE_DIGIT)
5020             {
5021                 const sal_Unicode* p = rInfo.sStrArray[i].getStr();
5022                 while ( *p == '#' )
5023                 {
5024                     p++;
5025                 }
5026                 while ( *p == '0' || *p == '?' )
5027                 {
5028                     nLeadingCnt++;
5029                     p++;
5030                 }
5031             }
5032             else if (nType == NF_SYMBOLTYPE_DECSEP
5033                   || nType == NF_SYMBOLTYPE_EXP
5034                   || nType == NF_SYMBOLTYPE_FRACBLANK)  // Fraction: stop after integer part,
5035             {                                           // do not count '0' of fraction
5036                 bStop = true;
5037             }
5038             i++;
5039         }
5040     }
5041 }
5042 
GetNumForString(sal_uInt16 nNumFor,sal_uInt16 nPos,bool bString) const5043 const OUString* SvNumberformat::GetNumForString( sal_uInt16 nNumFor, sal_uInt16 nPos,
5044             bool bString /* = false */ ) const
5045 {
5046     if ( nNumFor > 3 )
5047     {
5048         return nullptr;
5049     }
5050     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
5051     if ( !nCnt )
5052     {
5053         return nullptr;
5054     }
5055     if ( nPos == 0xFFFF )
5056     {
5057         nPos = nCnt - 1;
5058         if ( bString )
5059         {   // Backwards
5060             short const * pType = NumFor[nNumFor].Info().nTypeArray.data() + nPos;
5061             while ( nPos > 0 && (*pType != NF_SYMBOLTYPE_STRING) &&
5062                     (*pType != NF_SYMBOLTYPE_CURRENCY) )
5063             {
5064                 pType--;
5065                 nPos--;
5066             }
5067             if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) )
5068             {
5069                 return nullptr;
5070             }
5071         }
5072     }
5073     else if ( nPos > nCnt - 1 )
5074     {
5075         return nullptr;
5076     }
5077     else if ( bString )
5078     {
5079         // forward
5080         short const * pType = NumFor[nNumFor].Info().nTypeArray.data() + nPos;
5081         while ( nPos < nCnt && (*pType != NF_SYMBOLTYPE_STRING) &&
5082                 (*pType != NF_SYMBOLTYPE_CURRENCY) )
5083         {
5084             pType++;
5085             nPos++;
5086         }
5087         if ( nPos >= nCnt || ((*pType != NF_SYMBOLTYPE_STRING) &&
5088                               (*pType != NF_SYMBOLTYPE_CURRENCY)) )
5089         {
5090             return nullptr;
5091         }
5092     }
5093     return &NumFor[nNumFor].Info().sStrArray[nPos];
5094 }
5095 
GetNumForType(sal_uInt16 nNumFor,sal_uInt16 nPos) const5096 short SvNumberformat::GetNumForType( sal_uInt16 nNumFor, sal_uInt16 nPos ) const
5097 {
5098     if ( nNumFor > 3 )
5099     {
5100         return 0;
5101     }
5102     sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
5103     if ( !nCnt )
5104     {
5105         return 0;
5106     }
5107     if ( nPos == 0xFFFF )
5108     {
5109         nPos = nCnt - 1;
5110     }
5111     else if ( nPos > nCnt - 1 )
5112     {
5113         return 0;
5114     }
5115     return NumFor[nNumFor].Info().nTypeArray[nPos];
5116 }
5117 
IsNegativeWithoutSign() const5118 bool SvNumberformat::IsNegativeWithoutSign() const
5119 {
5120     if ( IsSecondSubformatRealNegative() )
5121     {
5122         const OUString* pStr = GetNumForString( 1, 0, true );
5123         if ( pStr )
5124         {
5125             return !HasStringNegativeSign( *pStr );
5126         }
5127     }
5128     return false;
5129 }
5130 
IsNegativeInBracket() const5131 bool SvNumberformat::IsNegativeInBracket() const
5132 {
5133     sal_uInt16 nCnt = NumFor[1].GetCount();
5134     if (!nCnt)
5135     {
5136         return false;
5137     }
5138     auto& tmp = NumFor[1].Info().sStrArray;
5139     return tmp[0] == "(" && tmp[nCnt-1] == ")";
5140 }
5141 
HasPositiveBracketPlaceholder() const5142 bool SvNumberformat::HasPositiveBracketPlaceholder() const
5143 {
5144     sal_uInt16 nCnt = NumFor[0].GetCount();
5145     return NumFor[0].Info().sStrArray[nCnt-1] == "_)";
5146 }
5147 
GetDateOrder() const5148 DateOrder SvNumberformat::GetDateOrder() const
5149 {
5150     if ( eType & SvNumFormatType::DATE )
5151     {
5152         auto& rTypeArray = NumFor[0].Info().nTypeArray;
5153         sal_uInt16 nCnt = NumFor[0].GetCount();
5154         for ( sal_uInt16 j=0; j<nCnt; j++ )
5155         {
5156             switch ( rTypeArray[j] )
5157             {
5158             case NF_KEY_D :
5159             case NF_KEY_DD :
5160                 return DateOrder::DMY;
5161             case NF_KEY_M :
5162             case NF_KEY_MM :
5163             case NF_KEY_MMM :
5164             case NF_KEY_MMMM :
5165             case NF_KEY_MMMMM :
5166                 return DateOrder::MDY;
5167             case NF_KEY_YY :
5168             case NF_KEY_YYYY :
5169             case NF_KEY_EC :
5170             case NF_KEY_EEC :
5171             case NF_KEY_R :
5172             case NF_KEY_RR :
5173                 return DateOrder::YMD;
5174             }
5175         }
5176     }
5177     else
5178     {
5179        SAL_WARN( "svl.numbers", "SvNumberformat::GetDateOrder: no date" );
5180     }
5181     return rLoc().getDateOrder();
5182 }
5183 
GetExactDateOrder() const5184 sal_uInt32 SvNumberformat::GetExactDateOrder() const
5185 {
5186     sal_uInt32 nRet = 0;
5187     if ( !(eType & SvNumFormatType::DATE) )
5188     {
5189         SAL_WARN( "svl.numbers", "SvNumberformat::GetExactDateOrder: no date" );
5190         return nRet;
5191     }
5192     auto& rTypeArray = NumFor[0].Info().nTypeArray;
5193     sal_uInt16 nCnt = NumFor[0].GetCount();
5194     int nShift = 0;
5195     for ( sal_uInt16 j=0; j<nCnt && nShift < 3; j++ )
5196     {
5197         switch ( rTypeArray[j] )
5198         {
5199         case NF_KEY_D :
5200         case NF_KEY_DD :
5201             nRet = (nRet << 8) | 'D';
5202             ++nShift;
5203             break;
5204         case NF_KEY_M :
5205         case NF_KEY_MM :
5206         case NF_KEY_MMM :
5207         case NF_KEY_MMMM :
5208         case NF_KEY_MMMMM :
5209             nRet = (nRet << 8) | 'M';
5210             ++nShift;
5211             break;
5212         case NF_KEY_YY :
5213         case NF_KEY_YYYY :
5214         case NF_KEY_EC :
5215         case NF_KEY_EEC :
5216         case NF_KEY_R :
5217         case NF_KEY_RR :
5218             nRet = (nRet << 8) | 'Y';
5219             ++nShift;
5220             break;
5221         }
5222     }
5223     return nRet;
5224 }
5225 
GetConditions(SvNumberformatLimitOps & rOper1,double & rVal1,SvNumberformatLimitOps & rOper2,double & rVal2) const5226 void SvNumberformat::GetConditions( SvNumberformatLimitOps& rOper1, double& rVal1,
5227                                     SvNumberformatLimitOps& rOper2, double& rVal2 ) const
5228 {
5229     rOper1 = eOp1;
5230     rOper2 = eOp2;
5231     rVal1  = fLimit1;
5232     rVal2  = fLimit2;
5233 }
5234 
GetColor(sal_uInt16 nNumFor) const5235 const Color* SvNumberformat::GetColor( sal_uInt16 nNumFor ) const
5236 {
5237     if ( nNumFor > 3 )
5238     {
5239         return nullptr;
5240     }
5241     return NumFor[nNumFor].GetColor();
5242 }
5243 
lcl_SvNumberformat_AddLimitStringImpl(OUString & rStr,SvNumberformatLimitOps eOp,double fLimit,std::u16string_view rDecSep)5244 static void lcl_SvNumberformat_AddLimitStringImpl( OUString& rStr,
5245                                                    SvNumberformatLimitOps eOp,
5246                                                    double fLimit, std::u16string_view rDecSep )
5247 {
5248     if ( eOp == NUMBERFORMAT_OP_NO )
5249         return;
5250 
5251     switch ( eOp )
5252     {
5253     case NUMBERFORMAT_OP_EQ :
5254         rStr = "[=";
5255         break;
5256     case NUMBERFORMAT_OP_NE :
5257         rStr = "[<>";
5258         break;
5259     case NUMBERFORMAT_OP_LT :
5260         rStr = "[<";
5261         break;
5262     case NUMBERFORMAT_OP_LE :
5263         rStr = "[<=";
5264         break;
5265     case NUMBERFORMAT_OP_GT :
5266         rStr = "[>";
5267         break;
5268     case NUMBERFORMAT_OP_GE :
5269         rStr = "[>=";
5270         break;
5271     default:
5272         SAL_WARN( "svl.numbers", "unsupported number format" );
5273         break;
5274     }
5275     rStr +=  ::rtl::math::doubleToUString( fLimit,
5276                                            rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
5277                                            rDecSep[0], true);
5278     rStr += "]";
5279 }
5280 
lcl_insertLCID(OUStringBuffer & rFormatStr,sal_uInt32 nLCID,sal_Int32 nPosInsertLCID,bool bDBNumInserted)5281 static void lcl_insertLCID( OUStringBuffer& rFormatStr, sal_uInt32 nLCID, sal_Int32 nPosInsertLCID, bool bDBNumInserted )
5282 {
5283     if ( nLCID == 0 )
5284         return;
5285     if (nPosInsertLCID == rFormatStr.getLength() && !bDBNumInserted)
5286         // No format code, no locale.
5287         return;
5288 
5289     auto aLCIDString = OUString::number( nLCID , 16 ).toAsciiUpperCase();
5290     // Search for only last DBNum which is the last element before insertion position
5291     if ( bDBNumInserted && nPosInsertLCID >= 8
5292         && aLCIDString.length > 4
5293         && OUString::unacquired(rFormatStr).match( "[DBNum", nPosInsertLCID-8) )
5294     {   // remove DBNumX code if long LCID
5295         nPosInsertLCID -= 8;
5296         rFormatStr.remove( nPosInsertLCID, 8 );
5297     }
5298     rFormatStr.insert( nPosInsertLCID, "[$-" + aLCIDString + "]" );
5299 }
5300 
5301 /** Increment nAlphabetID for CJK numerals
5302  * +1 for financial numerals [NatNum2]
5303  * +2 for Arabic fullwidth numerals [NatNum3]
5304  * */
lcl_incrementAlphabetWithNatNum(sal_uInt32 & nAlphabetID,sal_uInt32 nNatNum)5305 static void lcl_incrementAlphabetWithNatNum ( sal_uInt32& nAlphabetID, sal_uInt32 nNatNum )
5306 {
5307     if ( nNatNum == 2) // financial
5308         nAlphabetID += 1;
5309     else if ( nNatNum == 3)
5310         nAlphabetID += 2;
5311     nAlphabetID = nAlphabetID << 24;
5312 }
5313 
GetMappedFormatstring(const NfKeywordTable & rKeywords,const LocaleDataWrapper & rLocWrp,LanguageType nOriginalLang,bool bSystemLanguage) const5314 OUString SvNumberformat::GetMappedFormatstring( const NfKeywordTable& rKeywords,
5315                                                 const LocaleDataWrapper& rLocWrp,
5316                                                 LanguageType nOriginalLang /* =LANGUAGE_DONTKNOW */,
5317                                                 bool bSystemLanguage /* =false */ ) const
5318 {
5319     OUStringBuffer aStr;
5320     if (maLocale.meSubstitute != LocaleType::Substitute::NONE)
5321     {
5322         // XXX: theoretically this could clash with the first subformat's
5323         // lcl_insertLCID() below, in practice as long as it is used for system
5324         // time and date modifiers it shouldn't (i.e. there is no calendar or
5325         // numeral specified as well).
5326         aStr.append("[$-" + maLocale.generateCode() + "]");
5327     }
5328     bool bDefault[4];
5329     // 1 subformat matches all if no condition specified,
5330     bDefault[0] = ( NumFor[1].GetCount() == 0 && eOp1 == NUMBERFORMAT_OP_NO );
5331     // with 2 subformats [>=0];[<0] is implied if no condition specified
5332     bDefault[1] = ( !bDefault[0] && NumFor[2].GetCount() == 0 &&
5333                     eOp1 == NUMBERFORMAT_OP_GE && fLimit1 == 0.0 &&
5334                     eOp2 == NUMBERFORMAT_OP_NO && fLimit2 == 0.0 );
5335     // with 3 or more subformats [>0];[<0];[=0] is implied if no condition specified,
5336     // note that subformats may be empty (;;;) and NumFor[2].GetCount()>0 is not checked.
5337     bDefault[2] = ( !bDefault[0] && !bDefault[1] &&
5338                     eOp1 == NUMBERFORMAT_OP_GT && fLimit1 == 0.0 &&
5339                     eOp2 == NUMBERFORMAT_OP_LT && fLimit2 == 0.0 );
5340     bool bDefaults = bDefault[0] || bDefault[1] || bDefault[2];
5341     // from now on bDefault[] values are used to append empty subformats at the end
5342     bDefault[3] = false;
5343     if ( !bDefaults )
5344     {
5345         // conditions specified
5346         if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 == NUMBERFORMAT_OP_NO )
5347         {
5348             bDefault[0] = bDefault[1] = true;                               // [];x
5349         }
5350         else if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 != NUMBERFORMAT_OP_NO &&
5351                   NumFor[2].GetCount() == 0 )
5352         {
5353             bDefault[0] = bDefault[1] = bDefault[2] = bDefault[3] = true;   // [];[];;
5354         }
5355         // nothing to do if conditions specified for every subformat
5356     }
5357     else if ( bDefault[0] )
5358     {
5359         bDefault[0] = false; // a single unconditional subformat is never delimited
5360     }
5361     else
5362     {
5363         if ( bDefault[2] && NumFor[2].GetCount() == 0 && NumFor[1].GetCount() > 0 )
5364         {
5365             bDefault[3] = true; // special cases x;x;; and ;x;;
5366         }
5367         for ( int i=0; i<3 && !bDefault[i]; ++i )
5368         {
5369             bDefault[i] = true;
5370         }
5371     }
5372     int nSem = 0; // needed ';' delimiters
5373     int nSub = 0; // subformats delimited so far
5374     for ( int n=0; n<4; n++ )
5375     {
5376         if ( n > 0 && NumFor[n].Info().eScannedType != SvNumFormatType::UNDEFINED )
5377         {
5378             nSem++;
5379         }
5380         OUString aPrefix;
5381 
5382         if ( !bDefaults )
5383         {
5384             switch ( n )
5385             {
5386             case 0 :
5387                 lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp1,
5388                                                        fLimit1, rLocWrp.getNumDecimalSep() );
5389                 break;
5390             case 1 :
5391                 lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp2,
5392                                                        fLimit2, rLocWrp.getNumDecimalSep() );
5393                 break;
5394             }
5395         }
5396 
5397         const OUString& rColorName = NumFor[n].GetColorName();
5398         if ( !rColorName.isEmpty() )
5399         {
5400             const NfKeywordTable & rKey = rScan.GetKeywords();
5401             for ( int j = NF_KEY_FIRSTCOLOR; j <= NF_KEY_LASTCOLOR; j++ )
5402             {
5403                 if ( rKey[j] == rColorName )
5404                 {
5405                     aPrefix += "[" + rKeywords[j] + "]";
5406                     break;  // for
5407                 }
5408             }
5409         }
5410 
5411         SvNumberNatNum aNatNum = NumFor[n].GetNatNum();
5412         bool bDBNumInserted = false;
5413         if (aNatNum.IsComplete() && (aNatNum.GetDBNum() > 0 || nOriginalLang != LANGUAGE_DONTKNOW))
5414         {   // GetFormatStringForExcel() may have changed language to en_US
5415             if (aNatNum.GetLang() == LANGUAGE_ENGLISH_US && nOriginalLang != LANGUAGE_DONTKNOW)
5416                 aNatNum.SetLang( nOriginalLang );
5417             if ( aNatNum.GetDBNum() > 0 )
5418             {
5419                 aPrefix += "[DBNum" + OUString::number( aNatNum.GetDBNum() ) + "]";
5420                 bDBNumInserted = true;
5421             }
5422         }
5423 
5424         sal_uInt16 nCnt = NumFor[n].GetCount();
5425         if ( nSem && (nCnt || !aPrefix.isEmpty()) )
5426         {
5427             for ( ; nSem; --nSem )
5428             {
5429                 aStr.append( ';' );
5430             }
5431             for ( ; nSub <= n; ++nSub )
5432             {
5433                 bDefault[nSub] = false;
5434             }
5435         }
5436 
5437         if ( !aPrefix.isEmpty() )
5438         {
5439             aStr.append( aPrefix );
5440         }
5441         sal_Int32 nPosHaveLCID = -1;
5442         sal_Int32 nPosInsertLCID = aStr.getLength();
5443         sal_uInt32 nCalendarID = 0x0000000; // Excel ID of calendar used in sub-format see tdf#36038
5444         constexpr sal_uInt32 kCalGengou = 0x0030000;
5445         if ( nCnt )
5446         {
5447             auto& rTypeArray = NumFor[n].Info().nTypeArray;
5448             auto& rStrArray = NumFor[n].Info().sStrArray;
5449             for ( sal_uInt16 j=0; j<nCnt; j++ )
5450             {
5451                 if ( 0 <= rTypeArray[j] && rTypeArray[j] < NF_KEYWORD_ENTRIES_COUNT )
5452                 {
5453                     aStr.append( rKeywords[rTypeArray[j]] );
5454                     if( NF_KEY_NNNN == rTypeArray[j] )
5455                     {
5456                         aStr.append( rLocWrp.getLongDateDayOfWeekSep() );
5457                     }
5458                     switch (rTypeArray[j])
5459                     {
5460                         case NF_KEY_EC:
5461                         case NF_KEY_EEC:
5462                         case NF_KEY_R:
5463                         case NF_KEY_RR:
5464                             // Implicit secondary (non-gregorian) calendar.
5465                             // Currently only for ja-JP.
5466                             /* TODO: same for all locales in
5467                              * LocaleDataWrapper::doesSecondaryCalendarUseEC() ?
5468                              * Should split the locales off that then. */
5469                             if (!nCalendarID)
5470                             {
5471                                 const LanguageType nLang = MsLangId::getRealLanguage( nOriginalLang);
5472                                 if (nLang == LANGUAGE_JAPANESE)
5473                                     nCalendarID = kCalGengou;
5474                             }
5475                         break;
5476                         default:
5477                             ;   // nothing
5478                     }
5479                 }
5480                 else
5481                 {
5482                     switch ( rTypeArray[j] )
5483                     {
5484                     case NF_SYMBOLTYPE_DECSEP :
5485                         aStr.append( rLocWrp.getNumDecimalSep() );
5486                         break;
5487                     case NF_SYMBOLTYPE_THSEP :
5488                         aStr.append( rLocWrp.getNumThousandSep() );
5489                         break;
5490                     case NF_SYMBOLTYPE_EXP :
5491                         aStr.append( rKeywords[NF_KEY_E] );
5492                         if ( rStrArray[j].getLength() > 1 && rStrArray[j][1] == '+' )
5493                             aStr.append( "+" );
5494                         else
5495                         // tdf#102370: Excel code for exponent without sign
5496                             aStr.append( "-" );
5497                         break;
5498                     case NF_SYMBOLTYPE_DATESEP :
5499                         aStr.append( rLocWrp.getDateSep() );
5500                         break;
5501                     case NF_SYMBOLTYPE_TIMESEP :
5502                         aStr.append( rLocWrp.getTimeSep() );
5503                         break;
5504                     case NF_SYMBOLTYPE_TIME100SECSEP :
5505                         aStr.append( rLocWrp.getTime100SecSep() );
5506                         break;
5507                     case NF_SYMBOLTYPE_FRACBLANK :
5508                     case NF_SYMBOLTYPE_STRING :
5509                         if ( rStrArray[j].getLength() == 1 )
5510                         {
5511                             if ( rTypeArray[j] == NF_SYMBOLTYPE_STRING )
5512                                 aStr.append( '\\' );
5513                             aStr.append( rStrArray[j] );
5514                         }
5515                         else
5516                         {
5517                             aStr.append( "\"" + rStrArray[j] + "\"" );
5518                         }
5519                         break;
5520                     case NF_SYMBOLTYPE_CALDEL :
5521                         if (j + 1 >= nCnt)
5522                             break;
5523                         if ( rStrArray[j+1] == "gengou" )
5524                         {
5525                             nCalendarID = kCalGengou;
5526                         }
5527                         else if ( rStrArray[j+1] == "hijri" )
5528                         {
5529                             nCalendarID = 0x0060000;
5530                         }
5531                         else if ( rStrArray[j+1] == "buddhist" )
5532                         {
5533                             nCalendarID = 0x0070000;
5534                         }
5535                         else if ( rStrArray[j+1] == "jewish" )
5536                         {
5537                             nCalendarID = 0x0080000;
5538                         }
5539                         // Other calendars (see tdf#36038) not corresponding between LibO and XL.
5540                         // However, skip any calendar modifier and don't write
5541                         // as format code (if not as literal string).
5542                         j += 2;
5543                         break;
5544                     case NF_SYMBOLTYPE_CURREXT :
5545                         nPosHaveLCID = aStr.getLength();
5546                         aStr.append( rStrArray[j] );
5547                         break;
5548                     default:
5549                         aStr.append( rStrArray[j] );
5550                     }
5551                 }
5552             }
5553         }
5554         sal_uInt32 nAlphabetID = 0x0000000; // Excel ID of alphabet used for numerals see tdf#36038
5555         LanguageType nLanguageID = LANGUAGE_SYSTEM;
5556         if ( aNatNum.IsComplete() )
5557         {
5558             nLanguageID = MsLangId::getRealLanguage( aNatNum.GetLang());
5559             if ( aNatNum.GetNatNum() == 0 )
5560             {
5561                 nAlphabetID = 0x01000000;  // Arabic-european numerals
5562             }
5563             else if ( nCalendarID > 0 || aNatNum.GetDBNum() == 0 || aNatNum.GetDBNum() == aNatNum.GetNatNum() )
5564             {   // if no DBNum code then use long LCID
5565                 // if DBNum value != NatNum value, use DBNum and not extended LCID
5566                 // if calendar, then DBNum will be removed
5567                 LanguageType pri = primary(nLanguageID);
5568                 if ( pri == LANGUAGE_ARABIC_PRIMARY_ONLY )
5569                         nAlphabetID = 0x02000000;  // Arabic-indic numerals
5570                 else if ( pri == primary(LANGUAGE_FARSI) )
5571                         nAlphabetID = 0x03000000;  // Farsi numerals
5572                 else if ( pri.anyOf(
5573                     primary(LANGUAGE_HINDI),
5574                     primary(LANGUAGE_MARATHI),
5575                     primary(LANGUAGE_NEPALI) ))
5576                         nAlphabetID = 0x04000000;  // Devanagari numerals
5577                 else if ( pri == primary(LANGUAGE_BENGALI) )
5578                         nAlphabetID = 0x05000000;  // Bengali numerals
5579                 else if ( pri == primary(LANGUAGE_PUNJABI) )
5580                 {
5581                     if ( nLanguageID == LANGUAGE_PUNJABI_ARABIC_LSO )
5582                         nAlphabetID =  0x02000000;  // Arabic-indic numerals
5583                     else
5584                         nAlphabetID = 0x06000000;  // Punjabi numerals
5585                 }
5586                 else if ( pri == primary(LANGUAGE_GUJARATI) )
5587                         nAlphabetID = 0x07000000;  // Gujarati numerals
5588                 else if ( pri == primary(LANGUAGE_ODIA))
5589                         nAlphabetID = 0x08000000;  // Odia (Oriya) numerals
5590                 else if ( pri == primary(LANGUAGE_TAMIL))
5591                         nAlphabetID = 0x09000000;  // Tamil numerals
5592                 else if ( pri == primary(LANGUAGE_TELUGU))
5593                         nAlphabetID = 0x0A000000;  // Telugu numerals
5594                 else if ( pri == primary(LANGUAGE_KANNADA))
5595                         nAlphabetID = 0x0B000000;  // Kannada numerals
5596                 else if ( pri == primary(LANGUAGE_MALAYALAM))
5597                         nAlphabetID = 0x0C000000;  // Malayalam numerals
5598                 else if ( pri == primary(LANGUAGE_THAI))
5599                 {
5600                     // The Thai T NatNum modifier during Xcl export.
5601                     if ( rKeywords[NF_KEY_THAI_T] == "T" )
5602                         nAlphabetID = 0x0D000000;  // Thai numerals
5603                 }
5604                 else if ( pri == primary(LANGUAGE_LAO))
5605                         nAlphabetID = 0x0E000000;  // Lao numerals
5606                 else if ( pri == primary(LANGUAGE_TIBETAN))
5607                         nAlphabetID = 0x0F000000;  // Tibetan numerals
5608                 else if ( pri == primary(LANGUAGE_BURMESE))
5609                         nAlphabetID = 0x10000000;  // Burmese numerals
5610                 else if ( pri == primary(LANGUAGE_TIGRIGNA_ETHIOPIA))
5611                         nAlphabetID = 0x11000000;  // Tigrigna numerals
5612                 else if ( pri == primary(LANGUAGE_KHMER))
5613                         nAlphabetID = 0x12000000;  // Khmer numerals
5614                 else if ( pri == primary(LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA))
5615                 {
5616                     if ( nLanguageID != LANGUAGE_MONGOLIAN_CYRILLIC_MONGOLIA
5617                       && nLanguageID != LANGUAGE_MONGOLIAN_CYRILLIC_LSO )
5618                         nAlphabetID = 0x13000000;  // Mongolian numerals
5619                 }
5620                     // CJK numerals
5621                 else if ( pri == primary(LANGUAGE_JAPANESE))
5622                 {
5623                     nAlphabetID = 0x1B;
5624                     lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5625                 }
5626                 else if ( pri == primary(LANGUAGE_CHINESE))
5627                 {
5628                     if ( nLanguageID == LANGUAGE_CHINESE_TRADITIONAL
5629                       || nLanguageID == LANGUAGE_CHINESE_HONGKONG
5630                       || nLanguageID == LANGUAGE_CHINESE_MACAU )
5631                     {
5632                         nAlphabetID = 0x21;
5633                         lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5634                     }
5635                     else // LANGUAGE_CHINESE_SIMPLIFIED
5636                     {
5637                         nAlphabetID = 0x1E;
5638                         lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5639                     }
5640                 }
5641                 else if ( pri == primary(LANGUAGE_KOREAN))
5642                 {
5643                     if ( aNatNum.GetNatNum() == 9 ) // Hangul
5644                     {
5645                         nAlphabetID = 0x27000000;
5646                     }
5647                     else
5648                     {
5649                         nAlphabetID = 0x24;
5650                         lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5651                     }
5652                 }
5653             }
5654             // Add LCID to DBNum
5655             if ( aNatNum.GetDBNum() > 0 && nLanguageID == LANGUAGE_SYSTEM )
5656                 nLanguageID = MsLangId::getRealLanguage( aNatNum.GetLang());
5657         }
5658         else if (nPosHaveLCID < 0)
5659         {
5660             // Do not insert a duplicated LCID that was already given with a
5661             // currency format as [$R-1C09]
5662             if (!bSystemLanguage && nOriginalLang != LANGUAGE_DONTKNOW)
5663             {
5664                 // Explicit locale, write only to the first subformat.
5665                 if (n == 0)
5666                     nLanguageID = MsLangId::getRealLanguage( nOriginalLang);
5667             }
5668             else if (bSystemLanguage && maLocale.meLanguageWithoutLocaleData != LANGUAGE_DONTKNOW)
5669             {
5670                 // Explicit locale but no locale data thus assigned to system
5671                 // locale, preserve for roundtrip, write only to the first
5672                 // subformat.
5673                 if (n == 0)
5674                     nLanguageID = maLocale.meLanguageWithoutLocaleData;
5675             }
5676         }
5677         if ( nCalendarID > 0 )
5678         {   // Add alphabet and language to calendar
5679             if ( nAlphabetID == 0 )
5680                 nAlphabetID = 0x01000000;
5681             if ( nLanguageID == LANGUAGE_SYSTEM && nOriginalLang != LANGUAGE_DONTKNOW )
5682                 nLanguageID = nOriginalLang;
5683         }
5684         lcl_insertLCID( aStr, nAlphabetID + nCalendarID + static_cast<sal_uInt16>(nLanguageID), nPosInsertLCID,
5685                 bDBNumInserted);
5686     }
5687     for ( ; nSub<4 && bDefault[nSub]; ++nSub )
5688     {   // append empty subformats
5689         aStr.append( ';' );
5690     }
5691     return aStr.makeStringAndClear();
5692 }
5693 
5694 // static
ImpGetNatNumString(const SvNumberNatNum & rNum,sal_Int64 nVal,sal_uInt16 nMinDigits,const NativeNumberWrapper & rNatNum)5695 OUString SvNumberformat::ImpGetNatNumString(const SvNumberNatNum& rNum,
5696                                             sal_Int64 nVal, sal_uInt16 nMinDigits,
5697                                             const NativeNumberWrapper& rNatNum)
5698 {
5699     OUString aStr;
5700     if ( nMinDigits )
5701     {
5702         if ( nMinDigits == 2 )
5703         {
5704             // speed up the most common case
5705             if ( 0 <= nVal && nVal < 10 )
5706             {
5707                 sal_Unicode aBuf[2];
5708                 aBuf[0] = '0';
5709                 aBuf[1] = '0' + nVal;
5710                 aStr = OUString(aBuf, SAL_N_ELEMENTS(aBuf));
5711             }
5712             else
5713             {
5714                 aStr = OUString::number( nVal );
5715             }
5716         }
5717         else
5718         {
5719             OUString aValStr( OUString::number( nVal ) );
5720             if ( aValStr.getLength() >= nMinDigits )
5721             {
5722                 aStr = aValStr;
5723             }
5724             else
5725             {
5726                 OUStringBuffer aBuf;
5727                 for(sal_Int32 index = 0; index < nMinDigits - aValStr.getLength(); ++index)
5728                 {
5729                     aBuf.append('0');
5730                 }
5731                 aBuf.append(aValStr);
5732                 aStr = aBuf.makeStringAndClear();
5733             }
5734         }
5735     }
5736     else
5737     {
5738         aStr = OUString::number( nVal );
5739     }
5740     return ::impTransliterate(aStr, rNum, rNatNum);
5741 }
5742 
impTransliterateImpl(const OUString & rStr,const SvNumberNatNum & rNum,const sal_uInt16 nDateKey,const NativeNumberWrapper & rNatNum) const5743 OUString SvNumberformat::impTransliterateImpl(const OUString& rStr,
5744                                               const SvNumberNatNum& rNum,
5745                                               const sal_uInt16 nDateKey,
5746                                               const NativeNumberWrapper& rNatNum) const
5747 {
5748     // no KEYWORD=argument list in NatNum12
5749     if (rNum.GetParams().indexOf('=') == -1)
5750         return ::impTransliterateImpl( rStr, rNum, rNatNum);
5751 
5752     const NfKeywordTable & rKeywords = rScan.GetKeywords();
5753 
5754     // Format: KEYWORD=numbertext_prefix, ..., for example:
5755     // [NatNum12 YYYY=title ordinal,MMMM=article, D=ordinal-number]
5756     sal_Int32 nField = -1;
5757     do
5758     {
5759         nField = rNum.GetParams().indexOf(Concat2View(rKeywords[nDateKey] + "="), ++nField);
5760     }
5761     while (nField != -1 && nField != 0 &&
5762             (rNum.GetParams()[nField - 1] != ',' &&
5763               rNum.GetParams()[nField - 1] != ' '));
5764 
5765     // no format specified for actual keyword
5766     if (nField == -1)
5767         return rStr;
5768 
5769     sal_Int32 nKeywordLen = rKeywords[nDateKey].getLength() + 1;
5770     sal_Int32 nFieldEnd = rNum.GetParams().indexOf(',', nField);
5771 
5772     if (nFieldEnd == -1)
5773         nFieldEnd = rNum.GetParams().getLength();
5774 
5775     css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
5776 
5777     return rNatNum.getNativeNumberStringParams(
5778         rStr, aLocale, rNum.GetNatNum(),
5779         rNum.GetParams().copy(nField + nKeywordLen, nFieldEnd - nField - nKeywordLen));
5780 }
5781 
GetNatNumXml(css::i18n::NativeNumberXmlAttributes2 & rAttr,sal_uInt16 nNumFor,const NativeNumberWrapper & rNatNum) const5782 void SvNumberformat::GetNatNumXml( css::i18n::NativeNumberXmlAttributes2& rAttr,
5783                                    sal_uInt16 nNumFor, const NativeNumberWrapper& rNatNum ) const
5784 {
5785     if ( nNumFor <= 3 )
5786     {
5787         const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum();
5788         if ( rNum.IsSet() )
5789         {
5790             css::lang::Locale aLocale(
5791                     LanguageTag( rNum.GetLang() ).getLocale() );
5792 
5793             /* TODO: a new XNativeNumberSupplier2::convertToXmlAttributes()
5794              * should rather return NativeNumberXmlAttributes2 and places
5795              * adapted, and whether to fill Spellout or something different
5796              * should be internal there. */
5797             css::i18n::NativeNumberXmlAttributes aTmp(
5798                     rNatNum.convertToXmlAttributes(
5799                         aLocale, rNum.GetNatNum()));
5800             rAttr.Locale = aTmp.Locale;
5801             rAttr.Format = aTmp.Format;
5802             rAttr.Style = aTmp.Style;
5803             if ( NatNumTakesParameters(rNum.GetNatNum()) )
5804             {
5805                 // NatNum12 spell out numbers, dates and money amounts
5806                 rAttr.Spellout = rNum.GetParams();
5807                 // Mutually exclusive.
5808                 rAttr.Format.clear();
5809                 rAttr.Style.clear();
5810             }
5811             else
5812             {
5813                 rAttr.Spellout.clear();
5814             }
5815         }
5816         else
5817         {
5818             rAttr = css::i18n::NativeNumberXmlAttributes2();
5819         }
5820     }
5821     else
5822     {
5823         rAttr = css::i18n::NativeNumberXmlAttributes2();
5824     }
5825 }
5826 
GetNatNumModifierString(sal_uInt16 nNumFor) const5827 OUString SvNumberformat::GetNatNumModifierString( sal_uInt16 nNumFor ) const
5828 {
5829     if ( nNumFor > 3 )
5830         return u""_ustr;
5831     const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum();
5832     if ( !rNum.IsSet() )
5833         return u""_ustr;
5834     const sal_Int32 nNum = rNum.GetNatNum();
5835     OUStringBuffer sNatNumModifier = "[NatNum" + OUString::number( nNum );
5836     if ( NatNumTakesParameters( nNum ) )
5837     {
5838         sNatNumModifier.append( " " + rNum.GetParams() );
5839     }
5840     sNatNumModifier.append( "]" );
5841 
5842     return sNatNumModifier.makeStringAndClear();
5843 }
5844 
5845 // static
HasStringNegativeSign(const OUString & rStr)5846 bool SvNumberformat::HasStringNegativeSign( const OUString& rStr )
5847 {
5848     // For Sign '-' needs to be at the start or at the end of the string (blanks ignored)
5849     sal_Int32 nLen = rStr.getLength();
5850     if ( !nLen )
5851     {
5852         return false;
5853     }
5854     const sal_Unicode* const pBeg = rStr.getStr();
5855     const sal_Unicode* const pEnd = pBeg + nLen;
5856     const sal_Unicode* p = pBeg;
5857     do
5858     {   // Start
5859         if ( *p == '-' )
5860         {
5861             return true;
5862         }
5863     }
5864     while ( *p == ' ' && ++p < pEnd );
5865 
5866     p = pEnd - 1;
5867 
5868     do
5869     {   // End
5870         if ( *p == '-' )
5871         {
5872             return true;
5873         }
5874     }
5875     while ( *p == ' ' && pBeg < --p );
5876     return false;
5877 }
5878 
5879 // static
IsInQuote(const OUString & rStr,sal_Int32 nPos,sal_Unicode cQuote,sal_Unicode cEscIn,sal_Unicode cEscOut)5880 bool SvNumberformat::IsInQuote( const OUString& rStr, sal_Int32 nPos,
5881                                 sal_Unicode cQuote, sal_Unicode cEscIn, sal_Unicode cEscOut )
5882 {
5883     sal_Int32 nLen = rStr.getLength();
5884     if ( nPos >= nLen )
5885     {
5886         return false;
5887     }
5888     const sal_Unicode* p0 = rStr.getStr();
5889     const sal_Unicode* p = p0;
5890     const sal_Unicode* p1 = p0 + nPos;
5891     bool bQuoted = false;
5892     while ( p <= p1 )
5893     {
5894         if ( *p == cQuote )
5895         {
5896             if ( p == p0 )
5897             {
5898                 bQuoted = true;
5899             }
5900             else if ( bQuoted )
5901             {
5902                 if ( *(p-1) != cEscIn )
5903                 {
5904                     bQuoted = false;
5905                 }
5906             }
5907             else
5908             {
5909                 if ( *(p-1) != cEscOut )
5910                 {
5911                     bQuoted = true;
5912                 }
5913             }
5914         }
5915         p++;
5916     }
5917     return bQuoted;
5918 }
5919 
5920 // static
GetQuoteEnd(const OUString & rStr,sal_Int32 nPos,sal_Unicode cQuote,sal_Unicode cEscIn)5921 sal_Int32 SvNumberformat::GetQuoteEnd( const OUString& rStr, sal_Int32 nPos,
5922                                        sal_Unicode cQuote, sal_Unicode cEscIn )
5923 {
5924     if ( nPos < 0 )
5925     {
5926         return -1;
5927     }
5928     sal_Int32 nLen = rStr.getLength();
5929     if ( nPos >= nLen )
5930     {
5931         return -1;
5932     }
5933     if ( !IsInQuote( rStr, nPos, cQuote, cEscIn ) )
5934     {
5935         if ( rStr[ nPos ] == cQuote )
5936         {
5937             return nPos; // Closing cQuote
5938         }
5939         return -1;
5940     }
5941     const sal_Unicode* p0 = rStr.getStr();
5942     const sal_Unicode* p = p0 + nPos;
5943     const sal_Unicode* p1 = p0 + nLen;
5944     while ( p < p1 )
5945     {
5946         if ( *p == cQuote && p > p0 && *(p-1) != cEscIn )
5947         {
5948             return sal::static_int_cast< sal_Int32 >(p - p0);
5949         }
5950         p++;
5951     }
5952     return nLen; // End of String
5953 }
5954 
GetNumForNumberElementCount(sal_uInt16 nNumFor) const5955 sal_uInt16 SvNumberformat::GetNumForNumberElementCount( sal_uInt16 nNumFor ) const
5956 {
5957     if ( nNumFor < 4 )
5958     {
5959         sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
5960         return nCnt - ImpGetNumForStringElementCount( nNumFor );
5961     }
5962     return 0;
5963 }
5964 
ImpGetNumForStringElementCount(sal_uInt16 nNumFor) const5965 sal_uInt16 SvNumberformat::ImpGetNumForStringElementCount( sal_uInt16 nNumFor ) const
5966 {
5967     sal_uInt16 nCnt = 0;
5968     sal_uInt16 nNumForCnt = NumFor[nNumFor].GetCount();
5969     auto& rTypeArray = NumFor[nNumFor].Info().nTypeArray;
5970     for ( sal_uInt16 j=0; j<nNumForCnt; ++j )
5971     {
5972         switch ( rTypeArray[j] )
5973         {
5974         case NF_SYMBOLTYPE_STRING:
5975         case NF_SYMBOLTYPE_CURRENCY:
5976         case NF_SYMBOLTYPE_DATESEP:
5977         case NF_SYMBOLTYPE_TIMESEP:
5978         case NF_SYMBOLTYPE_TIME100SECSEP:
5979         case NF_SYMBOLTYPE_PERCENT:
5980             ++nCnt;
5981             break;
5982         }
5983     }
5984     return nCnt;
5985 }
5986 
IsMinuteSecondFormat() const5987 bool SvNumberformat::IsMinuteSecondFormat() const
5988 {
5989     if (GetMaskedType() != SvNumFormatType::TIME)
5990         return false;
5991 
5992     constexpr sal_uInt16 k00 = 0x00;    // Nada, Nilch
5993     constexpr sal_uInt16 kLB = 0x01;    // '[' Left Bracket
5994     constexpr sal_uInt16 kRB = 0x02;    // ']' Right Bracket
5995     constexpr sal_uInt16 kMM = 0x04;    // M or MM
5996     constexpr sal_uInt16 kTS = 0x08;    // Time Separator
5997     constexpr sal_uInt16 kSS = 0x10;    // S or SS
5998 #define HAS_MINUTE_SECOND(state) ((state) == (kMM|kTS|kSS) || (state) == (kLB|kMM|kRB|kTS|kSS))
5999     // Also (kMM|kTS|kLB|kSS|kRB) but those are the same bits.
6000 
6001     sal_uInt16 nState = k00;
6002     bool bSep = false;
6003     sal_uInt16 nNumForCnt = NumFor[0].GetCount();
6004     auto const & rTypeArray = NumFor[0].Info().nTypeArray;
6005     for (sal_uInt16 j=0; j < nNumForCnt; ++j)
6006     {
6007         switch (rTypeArray[j])
6008         {
6009             case NF_SYMBOLTYPE_DEL:
6010                 {
6011                     // '[' or ']' before/after MM or SS
6012                     const OUString& rStr = NumFor[0].Info().sStrArray[j];
6013                     if (rStr == "[")
6014                     {
6015                         if (nState != k00 && nState != (kMM|kTS))
6016                             return false;
6017                         nState |= kLB;
6018                     }
6019                     else if (rStr == "]")
6020                     {
6021                         if (nState != (kLB|kMM) && nState != (kMM|kTS|kLB|kSS))
6022                             return false;
6023                         nState |= kRB;
6024                     }
6025                     else
6026                         return false;
6027                 }
6028             break;
6029             case NF_KEY_MI:
6030             case NF_KEY_MMI:
6031                 if (nState != k00 && nState != kLB)
6032                     return false;
6033                 nState |= kMM;
6034             break;
6035             case NF_SYMBOLTYPE_TIMESEP:
6036                 if (nState != kMM && nState != (kLB|kMM|kRB))
6037                     return false;
6038                 nState |= kTS;
6039             break;
6040             case NF_KEY_S:
6041             case NF_KEY_SS:
6042                 if (nState != (kMM|kTS) && nState != (kLB|kMM|kRB|kTS) && nState != (kMM|kTS|kLB))
6043                     return false;
6044                 nState |= kSS;
6045             break;
6046             case NF_SYMBOLTYPE_TIME100SECSEP:
6047                 // Trailing fraction of seconds allowed.
6048                 if (!HAS_MINUTE_SECOND(nState))
6049                     return false;
6050                 bSep = true;
6051             break;
6052             case NF_SYMBOLTYPE_DIGIT:
6053                 if (!bSep)
6054                     return false;
6055             break;
6056             case NF_SYMBOLTYPE_STRING:
6057                 // nothing, display literal
6058             break;
6059             default:
6060                 return false;
6061         }
6062     }
6063     return HAS_MINUTE_SECOND(nState);
6064 #undef HAS_MINUTE_SECOND
6065 }
6066 
GetFormatStringForTimePrecision(int nPrecision) const6067 OUString SvNumberformat::GetFormatStringForTimePrecision( int nPrecision ) const
6068 {
6069     OUStringBuffer sString;
6070     using comphelper::string::padToLength;
6071 
6072     sal_uInt16 nNumForCnt = NumFor[0].GetCount();
6073     auto const & rTypeArray = NumFor[0].Info().nTypeArray;
6074     for (sal_uInt16 j=0; j < nNumForCnt; ++j)
6075     {
6076         switch (rTypeArray[j])
6077         {
6078             case NF_KEY_S :
6079             case NF_KEY_SS:
6080                 sString.append( NumFor[0].Info().sStrArray[j] );
6081                 if ( j > 0 && rTypeArray[j-1] == NF_SYMBOLTYPE_DEL && j < nNumForCnt-1 )
6082                 {
6083                     j++;
6084                     sString.append( NumFor[0].Info().sStrArray[j] );
6085                 }
6086                 if (nPrecision > 0)
6087                 {
6088                     sString.append( rLoc().getTime100SecSep() );
6089                     padToLength(sString, sString.getLength() + nPrecision, '0');
6090                 }
6091                 break;
6092             case NF_SYMBOLTYPE_TIME100SECSEP:
6093             case NF_SYMBOLTYPE_DIGIT:
6094                 break;
6095             case NF_SYMBOLTYPE_STRING:
6096                 sString.append( "\"" );
6097                 [[fallthrough]];
6098             default:
6099                 sString.append( NumFor[0].Info().sStrArray[j] );
6100                 if (rTypeArray[j] == NF_SYMBOLTYPE_STRING)
6101                 {
6102                     sString.append( "\"" );
6103                 }
6104         }
6105     }
6106 
6107     return sString.makeStringAndClear();
6108 }
6109 
GetThousandDivisorPrecision(sal_uInt16 nIx) const6110 sal_uInt16 SvNumberformat::GetThousandDivisorPrecision( sal_uInt16 nIx ) const
6111 {
6112     if (nIx >= 4)
6113         return 0;
6114 
6115     const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
6116 
6117     if (rInfo.eScannedType != SvNumFormatType::NUMBER && rInfo.eScannedType != SvNumFormatType::CURRENCY)
6118         return 0;
6119 
6120     if (rInfo.nThousand == FLAG_STANDARD_IN_FORMAT)
6121         return SvNumberFormatter::UNLIMITED_PRECISION;
6122 
6123     return rInfo.nThousand * 3;
6124 }
6125 
rChrCls() const6126 const CharClass& SvNumberformat::rChrCls() const
6127 {
6128     return rScan.GetChrCls();
6129 }
6130 
rLoc() const6131 const LocaleDataWrapper& SvNumberformat::rLoc() const
6132 {
6133     return rScan.GetLoc();
6134 }
6135 
GetCurrentLanguageData() const6136 const SvNFLanguageData& SvNumberformat::GetCurrentLanguageData() const
6137 {
6138     return rScan.GetCurrentLanguageData();
6139 }
6140 
6141 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
6142