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