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 
21 #include <stdlib.h>
22 #include <comphelper/string.hxx>
23 #include <sal/log.hxx>
24 #include <tools/debug.hxx>
25 #include <i18nlangtag/mslangid.hxx>
26 #include <unotools/charclass.hxx>
27 #include <unotools/localedatawrapper.hxx>
28 #include <com/sun/star/i18n/NumberFormatCode.hpp>
29 #include <com/sun/star/i18n/NumberFormatMapper.hpp>
30 
31 #include <svl/zforlist.hxx>
32 #include <svl/zformat.hxx>
33 #include <unotools/digitgroupingiterator.hxx>
34 
35 #include "zforscan.hxx"
36 
37 #include <svl/nfsymbol.hxx>
38 using namespace svt;
39 
40 const sal_Unicode cNoBreakSpace = 0xA0;
41 const sal_Unicode cNarrowNoBreakSpace = 0x202F;
42 
43 const int MaxCntPost = 20; //max dec places allow by rtl_math_round
44 
45 const NfKeywordTable ImpSvNumberformatScan::sEnglishKeyword =
46 {             // Syntax keywords in English (USA)
47     //! All keywords MUST be UPPERCASE! In same order as NfKeywordIndex
48     "",        // NF_KEY_NONE 0
49     "E",       // NF_KEY_E Exponent
50     "AM/PM",   // NF_KEY_AMPM AM/PM
51     "A/P",     // NF_KEY_AP AM/PM short
52     "M",       // NF_KEY_MI Minute
53     "MM",      // NF_KEY_MMI Minute 02
54     "M",       // NF_KEY_M month        (!)
55     "MM",      // NF_KEY_MM month 02     (!)
56     "MMM",     // NF_KEY_MMM month short name
57     "MMMM",    // NF_KEY_MMMM month long name
58     "MMMMM",   // NF_KEY_MMMMM first letter of month name
59     "H",       // NF_KEY_H hour
60     "HH",      // NF_KEY_HH hour 02
61     "S",       // NF_KEY_S Second
62     "SS",      // NF_KEY_SS Second 02
63     "Q",       // NF_KEY_Q Quarter short 'Q'
64     "QQ",      // NF_KEY_QQ Quarter long
65     "D",       // NF_KEY_D day of month
66     "DD",      // NF_KEY_DD day of month 02
67     "DDD",     // NF_KEY_DDD day of week short
68     "DDDD",    // NF_KEY_DDDD day of week long
69     "YY",      // NF_KEY_YY year two digits
70     "YYYY",    // NF_KEY_YYYY year four digits
71     "NN",      // NF_KEY_NN Day of week short
72     "NNN",     // NF_KEY_NNN Day of week long
73     "NNNN",    // NF_KEY_NNNN Day of week long incl. separator
74     "AAA",     // NF_KEY_AAA
75     "AAAA",    // NF_KEY_AAAA
76     "E",       // NF_KEY_EC
77     "EE",      // NF_KEY_EEC
78     "G",       // NF_KEY_G
79     "GG",      // NF_KEY_GG
80     "GGG",     // NF_KEY_GGG
81     "R",       // NF_KEY_R
82     "RR",      // NF_KEY_RR
83     "WW",      // NF_KEY_WW Week of year
84     "t",       // NF_KEY_THAI_T Thai T modifier, speciality of Thai Excel, only
85                 // used with Thai locale and converted to [NatNum1], only
86                 // exception as lowercase
87     "CCC",     // NF_KEY_CCC Currency abbreviation
88     "BOOLEAN", // NF_KEY_BOOLEAN boolean
89     "GENERAL", // NF_KEY_GENERAL General / Standard
90 
91     // Reserved words translated and color names follow:
92     "TRUE",    // NF_KEY_TRUE boolean true
93     "FALSE",   // NF_KEY_FALSE boolean false
94     "COLOR",   // NF_KEY_COLOR color
95         // colours
96     "BLACK",   // NF_KEY_BLACK
97     "BLUE",    // NF_KEY_BLUE
98     "GREEN",   // NF_KEY_GREEN
99     "CYAN",    // NF_KEY_CYAN
100     "RED",     // NF_KEY_RED
101     "MAGENTA", // NF_KEY_MAGENTA
102     "BROWN",   // NF_KEY_BROWN
103     "GREY",    // NF_KEY_GREY
104     "YELLOW",  // NF_KEY_YELLOW
105     "WHITE"    // NF_KEY_WHITE
106 };
107 
108 const ::std::vector<Color> ImpSvNumberformatScan::StandardColor{
109     COL_BLACK,        COL_LIGHTBLUE, COL_LIGHTGREEN, COL_LIGHTCYAN, COL_LIGHTRED,
110     COL_LIGHTMAGENTA, COL_BROWN,     COL_GRAY,       COL_YELLOW,    COL_WHITE
111 };
112 
113 // This vector will hold *only* the color names in German language.
GermanColorName(size_t i)114 static const std::u16string_view& GermanColorName(size_t i)
115 {
116     static const std::u16string_view sGermanColorNames[]{ u"FARBE",   u"SCHWARZ", u"BLAU",
117                                                           u"GRÜN",    u"CYAN",    u"ROT",
118                                                           u"MAGENTA", u"BRAUN",   u"GRAU",
119                                                           u"GELB",    u"WEISS" };
120     assert(i < SAL_N_ELEMENTS(sGermanColorNames));
121     return sGermanColorNames[i];
122 }
123 
ImpSvNumberformatScan(SvNumberFormatter * pFormatterP)124 ImpSvNumberformatScan::ImpSvNumberformatScan( SvNumberFormatter* pFormatterP )
125     : maNullDate( 30, 12, 1899)
126     , eNewLnge(LANGUAGE_DONTKNOW)
127     , eTmpLnge(LANGUAGE_DONTKNOW)
128     , nCurrPos(-1)
129     , meKeywordLocalization(KeywordLocalization::AllowEnglish)
130 {
131     pFormatter = pFormatterP;
132     xNFC = css::i18n::NumberFormatMapper::create( pFormatter->GetComponentContext() );
133     bConvertMode = false;
134     mbConvertDateOrder = false;
135     bConvertSystemToSystem = false;
136     bKeywordsNeedInit = true;            // locale dependent and not locale dependent keywords
137     bCompatCurNeedInit = true;           // locale dependent compatibility currency strings
138 
139     static_assert( NF_KEY_BLACK - NF_KEY_COLOR == 1,        "bad FARBE(COLOR), SCHWARZ(BLACK) sequence");
140     static_assert( NF_KEY_FIRSTCOLOR - NF_KEY_COLOR == 1,   "bad color sequence");
141     static_assert( NF_MAX_DEFAULT_COLORS + 1 == 11,         "bad color count");
142     static_assert( NF_KEY_WHITE - NF_KEY_COLOR + 1 == 11,   "bad color sequence count");
143 
144     nStandardPrec = 2;
145 
146     Reset();
147 }
148 
~ImpSvNumberformatScan()149 ImpSvNumberformatScan::~ImpSvNumberformatScan()
150 {
151     Reset();
152 }
153 
ChangeIntl(KeywordLocalization eKeywordLocalization)154 void ImpSvNumberformatScan::ChangeIntl( KeywordLocalization eKeywordLocalization )
155 {
156     meKeywordLocalization = eKeywordLocalization;
157     bKeywordsNeedInit = true;
158     bCompatCurNeedInit = true;
159     // may be initialized by InitSpecialKeyword()
160     sKeyword[NF_KEY_TRUE].clear();
161     sKeyword[NF_KEY_FALSE].clear();
162 }
163 
InitSpecialKeyword(NfKeywordIndex eIdx) const164 void ImpSvNumberformatScan::InitSpecialKeyword( NfKeywordIndex eIdx ) const
165 {
166     switch ( eIdx )
167     {
168     case NF_KEY_TRUE :
169         const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_TRUE] =
170             pFormatter->GetCharClass()->uppercase( pFormatter->GetLocaleData()->getTrueWord() );
171         if ( sKeyword[NF_KEY_TRUE].isEmpty() )
172         {
173             SAL_WARN( "svl.numbers", "InitSpecialKeyword: TRUE_WORD?" );
174             const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_TRUE] = sEnglishKeyword[NF_KEY_TRUE];
175         }
176         break;
177     case NF_KEY_FALSE :
178         const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_FALSE] =
179             pFormatter->GetCharClass()->uppercase( pFormatter->GetLocaleData()->getFalseWord() );
180         if ( sKeyword[NF_KEY_FALSE].isEmpty() )
181         {
182             SAL_WARN( "svl.numbers", "InitSpecialKeyword: FALSE_WORD?" );
183             const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_FALSE] = sEnglishKeyword[NF_KEY_FALSE];
184         }
185         break;
186     default:
187         SAL_WARN( "svl.numbers", "InitSpecialKeyword: unknown request" );
188     }
189 }
190 
InitCompatCur() const191 void ImpSvNumberformatScan::InitCompatCur() const
192 {
193     ImpSvNumberformatScan* pThis = const_cast<ImpSvNumberformatScan*>(this);
194     // currency symbol for old style ("automatic") compatibility format codes
195     pFormatter->GetCompatibilityCurrency( pThis->sCurSymbol, pThis->sCurAbbrev );
196     // currency symbol upper case
197     pThis->sCurString = pFormatter->GetCharClass()->uppercase( sCurSymbol );
198     bCompatCurNeedInit = false;
199 }
200 
InitKeywords() const201 void ImpSvNumberformatScan::InitKeywords() const
202 {
203     if ( !bKeywordsNeedInit )
204         return ;
205     const_cast<ImpSvNumberformatScan*>(this)->SetDependentKeywords();
206     bKeywordsNeedInit = false;
207 }
208 
209 /** Extract the name of General, Standard, Whatever, ignoring leading modifiers
210     such as [NatNum1]. */
lcl_extractStandardGeneralName(const OUString & rCode)211 static OUString lcl_extractStandardGeneralName( const OUString & rCode )
212 {
213     OUString aStr;
214     const sal_Unicode* p = rCode.getStr();
215     const sal_Unicode* const pStop = p + rCode.getLength();
216     const sal_Unicode* pBeg = p;    // name begins here
217     bool bMod = false;
218     bool bDone = false;
219     while (p < pStop && !bDone)
220     {
221         switch (*p)
222         {
223         case '[':
224             bMod = true;
225             break;
226         case ']':
227             if (bMod)
228             {
229                 bMod = false;
230                 pBeg = p+1;
231             }
232             // else: would be a locale data error, easily to be spotted in
233             // UI dialog
234             break;
235         case ';':
236             if (!bMod)
237             {
238                 bDone = true;
239                 --p;    // put back, increment by one follows
240             }
241             break;
242         }
243         ++p;
244         if (bMod)
245         {
246             pBeg = p;
247         }
248     }
249     if (pBeg < p)
250     {
251         aStr = rCode.copy( pBeg - rCode.getStr(), p - pBeg);
252     }
253     return aStr;
254 }
255 
SetDependentKeywords()256 void ImpSvNumberformatScan::SetDependentKeywords()
257 {
258     using namespace ::com::sun::star;
259     using namespace ::com::sun::star::uno;
260 
261     const CharClass* pCharClass = pFormatter->GetCharClass();
262     const LocaleDataWrapper* pLocaleData = pFormatter->GetLocaleData();
263     // #80023# be sure to generate keywords for the loaded Locale, not for the
264     // requested Locale, otherwise number format codes might not match
265     const LanguageTag& rLoadedLocale = pLocaleData->getLoadedLanguageTag();
266     LanguageType eLang = rLoadedLocale.getLanguageType( false);
267 
268     bool bL10n = (meKeywordLocalization != KeywordLocalization::EnglishOnly);
269     if (bL10n)
270     {
271         // Check if this actually is a locale that uses any localized keywords,
272         // if not then disable localized keywords completely.
273         if ( !eLang.anyOf( LANGUAGE_GERMAN,
274                     LANGUAGE_GERMAN_SWISS,
275                     LANGUAGE_GERMAN_AUSTRIAN,
276                     LANGUAGE_GERMAN_LUXEMBOURG,
277                     LANGUAGE_GERMAN_LIECHTENSTEIN,
278                     LANGUAGE_DUTCH,
279                     LANGUAGE_DUTCH_BELGIAN,
280                     LANGUAGE_FRENCH,
281                     LANGUAGE_FRENCH_BELGIAN,
282                     LANGUAGE_FRENCH_CANADIAN,
283                     LANGUAGE_FRENCH_SWISS,
284                     LANGUAGE_FRENCH_LUXEMBOURG,
285                     LANGUAGE_FRENCH_MONACO,
286                     LANGUAGE_FINNISH,
287                     LANGUAGE_ITALIAN,
288                     LANGUAGE_ITALIAN_SWISS,
289                     LANGUAGE_DANISH,
290                     LANGUAGE_NORWEGIAN,
291                     LANGUAGE_NORWEGIAN_BOKMAL,
292                     LANGUAGE_NORWEGIAN_NYNORSK,
293                     LANGUAGE_SWEDISH,
294                     LANGUAGE_SWEDISH_FINLAND,
295                     LANGUAGE_PORTUGUESE,
296                     LANGUAGE_PORTUGUESE_BRAZILIAN,
297                     LANGUAGE_SPANISH_MODERN,
298                     LANGUAGE_SPANISH_DATED,
299                     LANGUAGE_SPANISH_MEXICAN,
300                     LANGUAGE_SPANISH_GUATEMALA,
301                     LANGUAGE_SPANISH_COSTARICA,
302                     LANGUAGE_SPANISH_PANAMA,
303                     LANGUAGE_SPANISH_DOMINICAN_REPUBLIC,
304                     LANGUAGE_SPANISH_VENEZUELA,
305                     LANGUAGE_SPANISH_COLOMBIA,
306                     LANGUAGE_SPANISH_PERU,
307                     LANGUAGE_SPANISH_ARGENTINA,
308                     LANGUAGE_SPANISH_ECUADOR,
309                     LANGUAGE_SPANISH_CHILE,
310                     LANGUAGE_SPANISH_URUGUAY,
311                     LANGUAGE_SPANISH_PARAGUAY,
312                     LANGUAGE_SPANISH_BOLIVIA,
313                     LANGUAGE_SPANISH_EL_SALVADOR,
314                     LANGUAGE_SPANISH_HONDURAS,
315                     LANGUAGE_SPANISH_NICARAGUA,
316                     LANGUAGE_SPANISH_PUERTO_RICO ))
317         {
318             bL10n = false;
319             meKeywordLocalization = KeywordLocalization::EnglishOnly;
320         }
321     }
322 
323     // Init the current NfKeywordTable with English keywords.
324     sKeyword = sEnglishKeyword;
325 
326     // Set the uppercase localized General name, e.g. Standard -> STANDARD
327     i18n::NumberFormatCode aFormat = xNFC->getFormatCode( NF_NUMBER_STANDARD, rLoadedLocale.getLocale() );
328     sNameStandardFormat = lcl_extractStandardGeneralName( aFormat.Code );
329     sKeyword[NF_KEY_GENERAL] = pCharClass->uppercase( sNameStandardFormat );
330 
331     // Thai T NatNum special. Other locale's small letter 't' results in upper
332     // case comparison not matching but length does in conversion mode. Ugly.
333     if (eLang == LANGUAGE_THAI)
334     {
335         sKeyword[NF_KEY_THAI_T] = "T";
336     }
337     else
338     {
339         sKeyword[NF_KEY_THAI_T] = sEnglishKeyword[NF_KEY_THAI_T];
340     }
341 
342     // boolean keywords
343     InitSpecialKeyword( NF_KEY_TRUE );
344     InitSpecialKeyword( NF_KEY_FALSE );
345 
346     // Boolean equivalent format codes that are written to Excel files, may
347     // have been written to ODF as well, specifically if such loaded Excel file
348     // was saved as ODF, and shall result in proper Boolean again.
349     // "TRUE";"TRUE";"FALSE"
350     sBooleanEquivalent1 = "\"" + sKeyword[NF_KEY_TRUE] + "\";\"" +
351         sKeyword[NF_KEY_TRUE] + "\";\"" + sKeyword[NF_KEY_FALSE] + "\"";
352     // [>0]"TRUE";[<0]"TRUE";"FALSE"
353     sBooleanEquivalent2 = "[>0]\"" + sKeyword[NF_KEY_TRUE] + "\";[<0]\"" +
354         sKeyword[NF_KEY_TRUE] + "\";\"" + sKeyword[NF_KEY_FALSE] + "\"";
355 
356     // compatibility currency strings
357     InitCompatCur();
358 
359     if (!bL10n)
360         return;
361 
362     // All locale dependent keywords overrides follow.
363 
364     if ( eLang.anyOf(
365             LANGUAGE_GERMAN,
366             LANGUAGE_GERMAN_SWISS,
367             LANGUAGE_GERMAN_AUSTRIAN,
368             LANGUAGE_GERMAN_LUXEMBOURG,
369             LANGUAGE_GERMAN_LIECHTENSTEIN))
370     {
371         //! all capital letters
372         sKeyword[NF_KEY_M] =         "M";     // month 1
373         sKeyword[NF_KEY_MM] =        "MM";    // month 01
374         sKeyword[NF_KEY_MMM] =       "MMM";   // month Jan
375         sKeyword[NF_KEY_MMMM] =      "MMMM";  // month Januar
376         sKeyword[NF_KEY_MMMMM] =     "MMMMM"; // month J
377         sKeyword[NF_KEY_H] =         "H";     // hour 2
378         sKeyword[NF_KEY_HH] =        "HH";    // hour 02
379         sKeyword[NF_KEY_D] =         "T";
380         sKeyword[NF_KEY_DD] =        "TT";
381         sKeyword[NF_KEY_DDD] =       "TTT";
382         sKeyword[NF_KEY_DDDD] =      "TTTT";
383         sKeyword[NF_KEY_YY] =        "JJ";
384         sKeyword[NF_KEY_YYYY] =      "JJJJ";
385         sKeyword[NF_KEY_BOOLEAN] =   "LOGISCH";
386         sKeyword[NF_KEY_COLOR] =     GermanColorName(NF_KEY_COLOR - NF_KEY_COLOR);
387         sKeyword[NF_KEY_BLACK] =     GermanColorName(NF_KEY_BLACK - NF_KEY_COLOR);
388         sKeyword[NF_KEY_BLUE] =      GermanColorName(NF_KEY_BLUE - NF_KEY_COLOR);
389         sKeyword[NF_KEY_GREEN] =     GermanColorName(NF_KEY_GREEN - NF_KEY_COLOR);
390         sKeyword[NF_KEY_CYAN] =      GermanColorName(NF_KEY_CYAN - NF_KEY_COLOR);
391         sKeyword[NF_KEY_RED] =       GermanColorName(NF_KEY_RED - NF_KEY_COLOR);
392         sKeyword[NF_KEY_MAGENTA] =   GermanColorName(NF_KEY_MAGENTA - NF_KEY_COLOR);
393         sKeyword[NF_KEY_BROWN] =     GermanColorName(NF_KEY_BROWN - NF_KEY_COLOR);
394         sKeyword[NF_KEY_GREY] =      GermanColorName(NF_KEY_GREY - NF_KEY_COLOR);
395         sKeyword[NF_KEY_YELLOW] =    GermanColorName(NF_KEY_YELLOW - NF_KEY_COLOR);
396         sKeyword[NF_KEY_WHITE] =     GermanColorName(NF_KEY_WHITE - NF_KEY_COLOR);
397     }
398     else
399     {
400         // day
401         if ( eLang.anyOf(
402                 LANGUAGE_ITALIAN,
403                 LANGUAGE_ITALIAN_SWISS))
404         {
405             sKeyword[NF_KEY_D] = "G";
406             sKeyword[NF_KEY_DD] = "GG";
407             sKeyword[NF_KEY_DDD] = "GGG";
408             sKeyword[NF_KEY_DDDD] = "GGGG";
409             // must exchange the era code, same as Xcl
410             sKeyword[NF_KEY_G] = "X";
411             sKeyword[NF_KEY_GG] = "XX";
412             sKeyword[NF_KEY_GGG] = "XXX";
413         }
414         else if ( eLang.anyOf(
415                  LANGUAGE_FRENCH,
416                  LANGUAGE_FRENCH_BELGIAN,
417                  LANGUAGE_FRENCH_CANADIAN,
418                  LANGUAGE_FRENCH_SWISS,
419                  LANGUAGE_FRENCH_LUXEMBOURG,
420                  LANGUAGE_FRENCH_MONACO))
421         {
422             sKeyword[NF_KEY_D] = "J";
423             sKeyword[NF_KEY_DD] = "JJ";
424             sKeyword[NF_KEY_DDD] = "JJJ";
425             sKeyword[NF_KEY_DDDD] = "JJJJ";
426         }
427         else if ( eLang == LANGUAGE_FINNISH )
428         {
429             sKeyword[NF_KEY_D] = "P";
430             sKeyword[NF_KEY_DD] = "PP";
431             sKeyword[NF_KEY_DDD] = "PPP";
432             sKeyword[NF_KEY_DDDD] = "PPPP";
433         }
434 
435         // month
436         if ( eLang == LANGUAGE_FINNISH )
437         {
438             sKeyword[NF_KEY_M] = "K";
439             sKeyword[NF_KEY_MM] = "KK";
440             sKeyword[NF_KEY_MMM] = "KKK";
441             sKeyword[NF_KEY_MMMM] = "KKKK";
442             sKeyword[NF_KEY_MMMMM] = "KKKKK";
443         }
444 
445         // year
446         if ( eLang.anyOf(
447             LANGUAGE_ITALIAN,
448             LANGUAGE_ITALIAN_SWISS,
449             LANGUAGE_FRENCH,
450             LANGUAGE_FRENCH_BELGIAN,
451             LANGUAGE_FRENCH_CANADIAN,
452             LANGUAGE_FRENCH_SWISS,
453             LANGUAGE_FRENCH_LUXEMBOURG,
454             LANGUAGE_FRENCH_MONACO,
455             LANGUAGE_PORTUGUESE,
456             LANGUAGE_PORTUGUESE_BRAZILIAN,
457             LANGUAGE_SPANISH_MODERN,
458             LANGUAGE_SPANISH_DATED,
459             LANGUAGE_SPANISH_MEXICAN,
460             LANGUAGE_SPANISH_GUATEMALA,
461             LANGUAGE_SPANISH_COSTARICA,
462             LANGUAGE_SPANISH_PANAMA,
463             LANGUAGE_SPANISH_DOMINICAN_REPUBLIC,
464             LANGUAGE_SPANISH_VENEZUELA,
465             LANGUAGE_SPANISH_COLOMBIA,
466             LANGUAGE_SPANISH_PERU,
467             LANGUAGE_SPANISH_ARGENTINA,
468             LANGUAGE_SPANISH_ECUADOR,
469             LANGUAGE_SPANISH_CHILE,
470             LANGUAGE_SPANISH_URUGUAY,
471             LANGUAGE_SPANISH_PARAGUAY,
472             LANGUAGE_SPANISH_BOLIVIA,
473             LANGUAGE_SPANISH_EL_SALVADOR,
474             LANGUAGE_SPANISH_HONDURAS,
475             LANGUAGE_SPANISH_NICARAGUA,
476             LANGUAGE_SPANISH_PUERTO_RICO))
477         {
478             sKeyword[NF_KEY_YY] = "AA";
479             sKeyword[NF_KEY_YYYY] = "AAAA";
480             // must exchange the day of week name code, same as Xcl
481             sKeyword[NF_KEY_AAA] =   "OOO";
482             sKeyword[NF_KEY_AAAA] =  "OOOO";
483         }
484         else if ( eLang.anyOf(
485              LANGUAGE_DUTCH,
486              LANGUAGE_DUTCH_BELGIAN))
487         {
488             sKeyword[NF_KEY_YY] = "JJ";
489             sKeyword[NF_KEY_YYYY] = "JJJJ";
490         }
491         else if ( eLang == LANGUAGE_FINNISH )
492         {
493             sKeyword[NF_KEY_YY] = "VV";
494             sKeyword[NF_KEY_YYYY] = "VVVV";
495         }
496 
497         // hour
498         if ( eLang.anyOf(
499              LANGUAGE_DUTCH,
500              LANGUAGE_DUTCH_BELGIAN))
501         {
502             sKeyword[NF_KEY_H] = "U";
503             sKeyword[NF_KEY_HH] = "UU";
504         }
505         else if ( eLang.anyOf(
506             LANGUAGE_FINNISH,
507             LANGUAGE_SWEDISH,
508             LANGUAGE_SWEDISH_FINLAND,
509             LANGUAGE_DANISH,
510             LANGUAGE_NORWEGIAN,
511             LANGUAGE_NORWEGIAN_BOKMAL,
512             LANGUAGE_NORWEGIAN_NYNORSK))
513         {
514             sKeyword[NF_KEY_H] = "T";
515             sKeyword[NF_KEY_HH] = "TT";
516         }
517     }
518 }
519 
ChangeNullDate(sal_uInt16 nDay,sal_uInt16 nMonth,sal_Int16 nYear)520 void ImpSvNumberformatScan::ChangeNullDate(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
521 {
522     maNullDate = Date(nDay, nMonth, nYear);
523     if (!maNullDate.IsValidDate())
524     {
525         maNullDate.Normalize();
526         SAL_WARN("svl.numbers","ImpSvNumberformatScan::ChangeNullDate - not valid"
527                 " d: " << nDay << " m: " << nMonth << " y: " << nYear << " normalized to"
528                 " d: " << maNullDate.GetDay() << " m: " << maNullDate.GetMonth() << " y: " << maNullDate.GetYear());
529     }
530 }
531 
ChangeStandardPrec(sal_uInt16 nPrec)532 void ImpSvNumberformatScan::ChangeStandardPrec(sal_uInt16 nPrec)
533 {
534     nStandardPrec = nPrec;
535 }
536 
GetColor(OUString & sStr) const537 const Color* ImpSvNumberformatScan::GetColor(OUString& sStr) const
538 {
539     OUString sString = pFormatter->GetCharClass()->uppercase(sStr);
540     const NfKeywordTable & rKeyword = GetKeywords();
541     size_t i = 0;
542     while (i < NF_MAX_DEFAULT_COLORS && sString != rKeyword[NF_KEY_FIRSTCOLOR+i] )
543     {
544         i++;
545     }
546     if (i >= NF_MAX_DEFAULT_COLORS && meKeywordLocalization == KeywordLocalization::AllowEnglish)
547     {
548         LanguageType eLang = pFormatter->GetLocaleData()->getLoadedLanguageTag().getLanguageType( false);
549         if ( eLang.anyOf(
550                     LANGUAGE_GERMAN,
551                     LANGUAGE_GERMAN_SWISS,
552                     LANGUAGE_GERMAN_AUSTRIAN,
553                     LANGUAGE_GERMAN_LUXEMBOURG,
554                     LANGUAGE_GERMAN_LIECHTENSTEIN )) // only German uses localized color names
555         {
556             size_t j = 0;
557             while ( j < NF_MAX_DEFAULT_COLORS && sString != sEnglishKeyword[NF_KEY_FIRSTCOLOR + j] )
558             {
559                 ++j;
560             }
561             if ( j < NF_MAX_DEFAULT_COLORS )
562             {
563                 i = j;
564             }
565         }
566     }
567 
568     enum ColorKeywordConversion
569     {
570         None,
571         GermanToEnglish,
572         EnglishToGerman
573     } eColorKeywordConversion(None);
574 
575     if (bConvertMode)
576     {
577         const bool bFromGerman = eTmpLnge.anyOf(
578                 LANGUAGE_GERMAN,
579                 LANGUAGE_GERMAN_SWISS,
580                 LANGUAGE_GERMAN_AUSTRIAN,
581                 LANGUAGE_GERMAN_LUXEMBOURG,
582                 LANGUAGE_GERMAN_LIECHTENSTEIN);
583         const bool bToGerman = eNewLnge.anyOf(
584                 LANGUAGE_GERMAN,
585                 LANGUAGE_GERMAN_SWISS,
586                 LANGUAGE_GERMAN_AUSTRIAN,
587                 LANGUAGE_GERMAN_LUXEMBOURG,
588                 LANGUAGE_GERMAN_LIECHTENSTEIN);
589         if (bFromGerman && !bToGerman)
590             eColorKeywordConversion = ColorKeywordConversion::GermanToEnglish;
591         else if (!bFromGerman && bToGerman)
592             eColorKeywordConversion = ColorKeywordConversion::EnglishToGerman;
593     }
594 
595     const Color* pResult = nullptr;
596     if (i >= NF_MAX_DEFAULT_COLORS)
597     {
598         const OUString& rColorWord = rKeyword[NF_KEY_COLOR];
599         bool bL10n = true;
600         if ((bL10n = sString.startsWith(rColorWord)) ||
601                 ((meKeywordLocalization == KeywordLocalization::AllowEnglish) &&
602                  sString.startsWith(sEnglishKeyword[NF_KEY_COLOR])))
603         {
604             sal_Int32 nPos = (bL10n ? rColorWord.getLength() : sEnglishKeyword[NF_KEY_COLOR].getLength());
605             sStr = sStr.copy(nPos);
606             sStr = comphelper::string::strip(sStr, ' ');
607             switch (eColorKeywordConversion)
608             {
609                 case ColorKeywordConversion::None:
610                     sStr = rColorWord + sStr;
611                 break;
612                 case ColorKeywordConversion::GermanToEnglish:
613                     sStr = sEnglishKeyword[NF_KEY_COLOR] + sStr;                    // Farbe -> COLOR
614                 break;
615                 case ColorKeywordConversion::EnglishToGerman:
616                     sStr = GermanColorName(NF_KEY_COLOR - NF_KEY_COLOR) + sStr;   // Color -> FARBE
617                 break;
618             }
619             sString = sString.copy(nPos);
620             sString = comphelper::string::strip(sString, ' ');
621 
622             if ( CharClass::isAsciiNumeric( sString ) )
623             {
624                 sal_Int32 nIndex = sString.toInt32();
625                 if (nIndex > 0 && nIndex <= 64)
626                 {
627                     pResult = pFormatter->GetUserDefColor(static_cast<sal_uInt16>(nIndex)-1);
628                 }
629             }
630         }
631     }
632     else
633     {
634         sStr.clear();
635         switch (eColorKeywordConversion)
636         {
637             case ColorKeywordConversion::None:
638                 sStr = rKeyword[NF_KEY_FIRSTCOLOR+i];
639             break;
640             case ColorKeywordConversion::GermanToEnglish:
641                 sStr = sEnglishKeyword[NF_KEY_FIRSTCOLOR + i];                  // Rot -> RED
642             break;
643             case ColorKeywordConversion::EnglishToGerman:
644                 sStr = GermanColorName(NF_KEY_FIRSTCOLOR - NF_KEY_COLOR + i); // Red -> ROT
645             break;
646         }
647         pResult = &(StandardColor[i]);
648     }
649     return pResult;
650 }
651 
GetKeyWord(const OUString & sSymbol,sal_Int32 nPos,bool & rbFoundEnglish) const652 short ImpSvNumberformatScan::GetKeyWord( const OUString& sSymbol, sal_Int32 nPos, bool& rbFoundEnglish ) const
653 {
654     OUString sString = pFormatter->GetCharClass()->uppercase( sSymbol, nPos, sSymbol.getLength() - nPos );
655     const NfKeywordTable & rKeyword = GetKeywords();
656     // #77026# for the Xcl perverts: the GENERAL keyword is recognized anywhere
657     if (sString.startsWith( rKeyword[NF_KEY_GENERAL] ))
658     {
659         return NF_KEY_GENERAL;
660     }
661     if ((meKeywordLocalization == KeywordLocalization::AllowEnglish) &&
662             sString.startsWith( sEnglishKeyword[NF_KEY_GENERAL]))
663     {
664         rbFoundEnglish = true;
665         return NF_KEY_GENERAL;
666     }
667 
668     // MUST be a reverse search to find longer strings first,
669     // new keywords take precedence over old keywords,
670     // skip colors et al after keywords.
671     short i = NF_KEY_LASTKEYWORD;
672     while (i > 0 && !sString.startsWith( rKeyword[i]))
673     {
674         i--;
675     }
676     if (i == 0 && meKeywordLocalization == KeywordLocalization::AllowEnglish)
677     {
678         // No localized (if so) keyword, try English keywords if keywords
679         // are localized. That was already checked in SetDependentKeywords().
680         i = NF_KEY_LASTKEYWORD;
681         while (i > 0 && !sString.startsWith( sEnglishKeyword[i]))
682         {
683             i--;
684         }
685     }
686 
687     // The Thai T NatNum modifier during Xcl import.
688     if (i == 0 && bConvertMode &&
689         sString[0] == 'T' &&
690         eTmpLnge == LANGUAGE_ENGLISH_US &&
691         MsLangId::getRealLanguage( eNewLnge) == LANGUAGE_THAI)
692     {
693         i = NF_KEY_THAI_T;
694     }
695     return i; // 0 => not found
696 }
697 
698 /**
699  * Next_Symbol
700  *
701  * Splits up the input for further processing (by the Turing machine).
702  *
703  * Starting state = SsStar
704  *
705  * ---------------+-------------------+---------------------------+---------------
706  * Old state      | Character read    | Event                     | New state
707  * ---------------+-------------------+---------------------------+---------------
708  * SsStart        | Character         | Symbol = Character        | SsGetWord
709  *                |    "              | Type = String             | SsGetString
710  *                |    \              | Type = String             | SsGetChar
711  *                |    *              | Type = Star               | SsGetStar
712  *                |    _              | Type = Blank              | SsGetBlank
713  *                | @ # 0 ? / . , % [ | Symbol = Character;       |
714  *                | ] ' Blank         | Type = Control character  | SsStop
715  *                | $ - + ( ) :       | Type  = String;           |
716  *                | Else              | Symbol = Character        | SsStop
717  * ---------------|-------------------+---------------------------+---------------
718  * SsGetChar      | Else              | Symbol = Character        | SsStop
719  * ---------------+-------------------+---------------------------+---------------
720  * GetString      | "                 |                           | SsStop
721  *                | Else              | Symbol += Character       | GetString
722  * ---------------+-------------------+---------------------------+---------------
723  * SsGetWord      | Character         | Symbol += Character       |
724  *                | + -        (E+ E-)| Symbol += Character       | SsStop
725  *                | /          (AM/PM)| Symbol += Character       |
726  *                | Else              | Pos--, if Key Type = Word | SsStop
727  * ---------------+-------------------+---------------------------+---------------
728  * SsGetStar      | Else              | Symbol += Character       | SsStop
729  *                |                   | Mark special case *       |
730  * ---------------+-------------------+---------------------------+---------------
731  * SsGetBlank     | Else              | Symbol + =Character       | SsStop
732  *                |                   | Mark special case  _      |
733  * ---------------------------------------------------------------+--------------
734  *
735  * If we recognize a keyword in the state SsGetWord (even as the symbol's start text)
736  * we write back the rest of the characters!
737  */
738 
739 namespace {
740 
741 enum ScanState
742 {
743     SsStop      = 0,
744     SsStart     = 1,
745     SsGetChar   = 2,
746     SsGetString = 3,
747     SsGetWord   = 4,
748     SsGetStar   = 5,
749     SsGetBlank  = 6
750 };
751 
752 }
753 
Next_Symbol(const OUString & rStr,sal_Int32 & nPos,OUString & sSymbol) const754 short ImpSvNumberformatScan::Next_Symbol( const OUString& rStr,
755                                           sal_Int32& nPos,
756                                           OUString& sSymbol ) const
757 {
758     InitKeywords();
759     const CharClass* pChrCls = pFormatter->GetCharClass();
760     const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
761     short eType = 0;
762     ScanState eState = SsStart;
763     OUStringBuffer sSymbolBuffer;
764     while ( nPos < rStr.getLength() && eState != SsStop )
765     {
766         sal_Unicode cToken = rStr[nPos++];
767         switch (eState)
768         {
769         case SsStart:
770             // Fetch any currency longer than one character and don't get
771             // confused later on by "E/" or other combinations of letters
772             // and meaningful symbols. Necessary for old automatic currency.
773             // #96158# But don't do it if we're starting a "[...]" section,
774             // for example a "[$...]" new currency symbol to not parse away
775             // "$U" (symbol) of "[$UYU]" (abbreviation).
776             if ( nCurrPos >= 0 && sCurString.getLength() > 1 &&
777                  nPos-1 + sCurString.getLength() <= rStr.getLength() &&
778                  (nPos <= 1 || rStr[nPos-2] != '[') )
779             {
780                 OUString aTest = pChrCls->uppercase( rStr.copy( nPos-1, sCurString.getLength() ) );
781                 if ( aTest == sCurString )
782                 {
783                     sSymbol = rStr.copy( --nPos, sCurString.getLength() );
784                     nPos = nPos + sSymbol.getLength();
785                     eType = NF_SYMBOLTYPE_STRING;
786                     return eType;
787                 }
788             }
789             switch (cToken)
790             {
791             case '#':
792             case '0':
793             case '?':
794             case '%':
795             case '@':
796             case '[':
797             case ']':
798             case ',':
799             case '.':
800             case '/':
801             case '\'':
802             case ' ':
803             case ':':
804             case '-':
805                 eType = NF_SYMBOLTYPE_DEL;
806                 sSymbolBuffer.append(OUStringChar(cToken));
807                 eState = SsStop;
808                 break;
809             case '*':
810                 eType = NF_SYMBOLTYPE_STAR;
811                 sSymbolBuffer.append(OUStringChar(cToken));
812                 eState = SsGetStar;
813                 break;
814             case '_':
815                 eType = NF_SYMBOLTYPE_BLANK;
816                 sSymbolBuffer.append(OUStringChar(cToken));
817                 eState = SsGetBlank;
818                 break;
819             case '"':
820                 eType = NF_SYMBOLTYPE_STRING;
821                 eState = SsGetString;
822                 sSymbolBuffer.append(OUStringChar(cToken));
823                 break;
824             case '\\':
825                 eType = NF_SYMBOLTYPE_STRING;
826                 eState = SsGetChar;
827                 sSymbolBuffer.append(OUStringChar(cToken));
828                 break;
829             case '$':
830             case '+':
831             case '(':
832             case ')':
833                 eType = NF_SYMBOLTYPE_STRING;
834                 eState = SsStop;
835                 sSymbolBuffer.append(OUStringChar(cToken));
836                 break;
837             default :
838                 if (StringEqualsChar( pFormatter->GetNumDecimalSep(), cToken) ||
839                     StringEqualsChar( pFormatter->GetNumThousandSep(), cToken) ||
840                     StringEqualsChar( pFormatter->GetDateSep(), cToken) ||
841                     StringEqualsChar( pLoc->getTimeSep(), cToken) ||
842                     StringEqualsChar( pLoc->getTime100SecSep(), cToken))
843                 {
844                     // Another separator than pre-known ASCII
845                     eType = NF_SYMBOLTYPE_DEL;
846                     sSymbolBuffer.append(OUStringChar(cToken));
847                     eState = SsStop;
848                 }
849                 else if ( pChrCls->isLetter( rStr, nPos-1 ) )
850                 {
851                     bool bFoundEnglish = false;
852                     short nTmpType = GetKeyWord( rStr, nPos-1, bFoundEnglish);
853                     if ( nTmpType )
854                     {
855                         bool bCurrency = false;
856                         // "Automatic" currency may start with keyword,
857                         // like "R" (Rand) and 'R' (era)
858                         if ( nCurrPos >= 0 &&
859                              nPos-1 + sCurString.getLength() <= rStr.getLength() &&
860                              sCurString.startsWith( bFoundEnglish ? sEnglishKeyword[nTmpType] : sKeyword[nTmpType]))
861                         {
862                             OUString aTest = pChrCls->uppercase( rStr.copy( nPos-1, sCurString.getLength() ) );
863                             if ( aTest == sCurString )
864                             {
865                                 bCurrency = true;
866                             }
867                         }
868                         if ( bCurrency )
869                         {
870                             eState = SsGetWord;
871                             sSymbolBuffer.append(OUStringChar(cToken));
872                         }
873                         else
874                         {
875                             eType = nTmpType;
876                             // The code to be advanced is the detected keyword,
877                             // not necessarily the locale's keyword, but the
878                             // symbol is to be the locale's keyword.
879                             sal_Int32 nLen;
880                             if (bFoundEnglish)
881                             {
882                                 nLen = sEnglishKeyword[eType].getLength();
883                                 // Use the locale's General keyword name, not uppercase.
884                                 sSymbolBuffer = (eType == NF_KEY_GENERAL ? sNameStandardFormat : sKeyword[eType]);
885                             }
886                             else
887                             {
888                                 nLen = sKeyword[eType].getLength();
889                                 // Preserve a locale's keyword's case as entered.
890                                 sSymbolBuffer = rStr.subView( nPos-1, nLen);
891                             }
892                             if ((eType == NF_KEY_E || IsAmbiguousE(eType)) && nPos < rStr.getLength())
893                             {
894                                 sal_Unicode cNext = rStr[nPos];
895                                 switch ( cNext )
896                                 {
897                                 case '+' :
898                                 case '-' :  // E+ E- combine to one symbol
899                                     sSymbolBuffer.append(OUStringChar(cNext));
900                                     eType = NF_KEY_E;
901                                     nPos++;
902                                     break;
903                                 case '0' :
904                                 case '#' :  // scientific E without sign
905                                     eType = NF_KEY_E;
906                                     break;
907                                 }
908                             }
909                             nPos--;
910                             nPos = nPos + nLen;
911                             eState = SsStop;
912                         }
913                     }
914                     else
915                     {
916                         eState = SsGetWord;
917                         sSymbolBuffer.append(OUStringChar(cToken));
918                     }
919                 }
920                 else
921                 {
922                     eType = NF_SYMBOLTYPE_STRING;
923                     eState = SsStop;
924                     sSymbolBuffer.append(OUStringChar(cToken));
925                 }
926                 break;
927             }
928             break;
929         case SsGetChar:
930             sSymbolBuffer.append(OUStringChar(cToken));
931             eState = SsStop;
932             break;
933         case SsGetString:
934             if (cToken == '"')
935             {
936                 eState = SsStop;
937             }
938             sSymbolBuffer.append(OUStringChar(cToken));
939             break;
940         case SsGetWord:
941             if ( pChrCls->isLetter( rStr, nPos-1 ) )
942             {
943                 bool bFoundEnglish = false;
944                 short nTmpType = GetKeyWord( rStr, nPos-1, bFoundEnglish);
945                 if ( nTmpType )
946                 {
947                     // beginning of keyword, stop scan and put back
948                     eType = NF_SYMBOLTYPE_STRING;
949                     eState = SsStop;
950                     nPos--;
951                 }
952                 else
953                 {
954                     sSymbolBuffer.append(OUStringChar(cToken));
955                 }
956             }
957             else
958             {
959                 bool bDontStop = false;
960                 sal_Unicode cNext;
961                 switch (cToken)
962                 {
963                 case '/': // AM/PM, A/P
964                     if (nPos < rStr.getLength())
965                     {
966                         cNext = rStr[nPos];
967                         if ( cNext == 'P' || cNext == 'p' )
968                         {
969                             sal_Int32 nLen = sSymbolBuffer.getLength();
970                             if ( 1 <= nLen &&
971                                     (sSymbolBuffer[0] == 'A' || sSymbolBuffer[0] == 'a') &&
972                                     (nLen == 1 ||
973                                      (nLen == 2 && (sSymbolBuffer[1] == 'M' || sSymbolBuffer[1] == 'm')
974                                       && (rStr[nPos + 1] == 'M' || rStr[nPos + 1] == 'm'))))
975                             {
976                                 sSymbolBuffer.append(OUStringChar(cToken));
977                                 bDontStop = true;
978                             }
979                         }
980                     }
981                     break;
982                 }
983                 // anything not recognized will stop the scan
984                 if (!bDontStop)
985                 {
986                     eState = SsStop;
987                     nPos--;
988                     eType = NF_SYMBOLTYPE_STRING;
989                 }
990             }
991             break;
992         case SsGetStar:
993             eState = SsStop;
994             sSymbolBuffer.append(OUStringChar(cToken));
995             break;
996         case SsGetBlank:
997             eState = SsStop;
998             sSymbolBuffer.append(OUStringChar(cToken));
999             break;
1000         default:
1001             break;
1002         } // of switch
1003     } // of while
1004     if (eState == SsGetWord)
1005     {
1006         eType = NF_SYMBOLTYPE_STRING;
1007     }
1008     sSymbol = sSymbolBuffer.makeStringAndClear();
1009     return eType;
1010 }
1011 
Symbol_Division(const OUString & rString)1012 sal_Int32 ImpSvNumberformatScan::Symbol_Division(const OUString& rString)
1013 {
1014     nCurrPos = -1;
1015     // Do we have some sort of currency?
1016     OUString sString = pFormatter->GetCharClass()->uppercase(rString);
1017     sal_Int32 nCPos = 0;
1018     while (nCPos >= 0 && nCPos < sString.getLength())
1019     {
1020         nCPos = sString.indexOf(GetCurString(),nCPos);
1021         if (nCPos >= 0)
1022         {
1023             // In Quotes?
1024             sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sString, nCPos );
1025             if ( nQ < 0 )
1026             {
1027                 sal_Unicode c;
1028                 if ( nCPos == 0 ||
1029                     ((c = sString[nCPos-1]) != '"'
1030                             && c != '\\') ) // dm can be protected by "dm \d
1031                 {
1032                     nCurrPos = nCPos;
1033                     nCPos = -1;
1034                 }
1035                 else
1036                 {
1037                     nCPos++; // Continue search
1038                 }
1039             }
1040             else
1041             {
1042                 nCPos = nQ + 1; // Continue search
1043             }
1044         }
1045     }
1046     nStringsCnt = 0;
1047     bool bStar = false; // Is set on detecting '*'
1048     Reset();
1049 
1050     sal_Int32 nPos = 0;
1051     const sal_Int32 nLen = rString.getLength();
1052     while (nPos < nLen && nStringsCnt < NF_MAX_FORMAT_SYMBOLS)
1053     {
1054         nTypeArray[nStringsCnt] = Next_Symbol(rString, nPos, sStrArray[nStringsCnt]);
1055         if (nTypeArray[nStringsCnt] == NF_SYMBOLTYPE_STAR)
1056         { // Monitoring the '*'
1057             if (bStar)
1058             {
1059                 return nPos; // Error: double '*'
1060             }
1061             else
1062             {
1063                 // Valid only if there is a character following, else we are
1064                 // at the end of a code that does not have a fill character
1065                 // (yet?).
1066                 if (sStrArray[nStringsCnt].getLength() < 2)
1067                     return nPos;
1068                 bStar = true;
1069             }
1070         }
1071         nStringsCnt++;
1072     }
1073 
1074     return 0; // 0 => ok
1075 }
1076 
SkipStrings(sal_uInt16 & i,sal_Int32 & nPos) const1077 void ImpSvNumberformatScan::SkipStrings(sal_uInt16& i, sal_Int32& nPos) const
1078 {
1079     while (i < nStringsCnt && (   nTypeArray[i] == NF_SYMBOLTYPE_STRING
1080                                || nTypeArray[i] == NF_SYMBOLTYPE_BLANK
1081                                || nTypeArray[i] == NF_SYMBOLTYPE_STAR) )
1082     {
1083         nPos = nPos + sStrArray[i].getLength();
1084         i++;
1085     }
1086 }
1087 
PreviousKeyword(sal_uInt16 i) const1088 sal_uInt16 ImpSvNumberformatScan::PreviousKeyword(sal_uInt16 i) const
1089 {
1090     short res = 0;
1091     if (i > 0 && i < nStringsCnt)
1092     {
1093         i--;
1094         while (i > 0 && nTypeArray[i] <= 0)
1095         {
1096             i--;
1097         }
1098         if (nTypeArray[i] > 0)
1099         {
1100             res = nTypeArray[i];
1101         }
1102     }
1103     return res;
1104 }
1105 
NextKeyword(sal_uInt16 i) const1106 sal_uInt16 ImpSvNumberformatScan::NextKeyword(sal_uInt16 i) const
1107 {
1108     short res = 0;
1109     if (i < nStringsCnt-1)
1110     {
1111         i++;
1112         while (i < nStringsCnt-1 && nTypeArray[i] <= 0)
1113         {
1114             i++;
1115         }
1116         if (nTypeArray[i] > 0)
1117         {
1118             res = nTypeArray[i];
1119         }
1120     }
1121     return res;
1122 }
1123 
PreviousType(sal_uInt16 i) const1124 short ImpSvNumberformatScan::PreviousType( sal_uInt16 i ) const
1125 {
1126     if ( i > 0 && i < nStringsCnt )
1127     {
1128         do
1129         {
1130             i--;
1131         }
1132         while ( i > 0 && nTypeArray[i] == NF_SYMBOLTYPE_EMPTY );
1133         return nTypeArray[i];
1134     }
1135     return 0;
1136 }
1137 
PreviousChar(sal_uInt16 i) const1138 sal_Unicode ImpSvNumberformatScan::PreviousChar(sal_uInt16 i) const
1139 {
1140     sal_Unicode res = ' ';
1141     if (i > 0 && i < nStringsCnt)
1142     {
1143         i--;
1144         while (i > 0 &&
1145                ( nTypeArray[i] == NF_SYMBOLTYPE_EMPTY ||
1146                  nTypeArray[i] == NF_SYMBOLTYPE_STRING ||
1147                  nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
1148                  nTypeArray[i] == NF_SYMBOLTYPE_BLANK ))
1149         {
1150             i--;
1151         }
1152         if (sStrArray[i].getLength() > 0)
1153         {
1154             res = sStrArray[i][sStrArray[i].getLength()-1];
1155         }
1156     }
1157     return res;
1158 }
1159 
NextChar(sal_uInt16 i) const1160 sal_Unicode ImpSvNumberformatScan::NextChar(sal_uInt16 i) const
1161 {
1162     sal_Unicode res = ' ';
1163     if (i < nStringsCnt-1)
1164     {
1165         i++;
1166         while (i < nStringsCnt-1 &&
1167                ( nTypeArray[i] == NF_SYMBOLTYPE_EMPTY ||
1168                  nTypeArray[i] == NF_SYMBOLTYPE_STRING ||
1169                  nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
1170                  nTypeArray[i] == NF_SYMBOLTYPE_BLANK))
1171         {
1172             i++;
1173         }
1174         if (sStrArray[i].getLength() > 0)
1175         {
1176             res = sStrArray[i][0];
1177         }
1178     }
1179     return res;
1180 }
1181 
IsLastBlankBeforeFrac(sal_uInt16 i) const1182 bool ImpSvNumberformatScan::IsLastBlankBeforeFrac(sal_uInt16 i) const
1183 {
1184     bool res = true;
1185     if (i < nStringsCnt-1)
1186     {
1187         bool bStop = false;
1188         i++;
1189         while (i < nStringsCnt-1 && !bStop)
1190         {
1191             i++;
1192             if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL &&
1193                  sStrArray[i][0] == '/')
1194             {
1195                 bStop = true;
1196             }
1197             else if ( ( nTypeArray[i] == NF_SYMBOLTYPE_DEL  &&
1198                         sStrArray[i][0] == ' ')             ||
1199                         nTypeArray[i] == NF_SYMBOLTYPE_STRING ) // integer/fraction delimiter can also be a string
1200             {
1201                 res = false;
1202             }
1203         }
1204         if (!bStop) // no '/'{
1205         {
1206             res = false;
1207         }
1208     }
1209     else
1210     {
1211         res = false; // no '/' any more
1212     }
1213     return res;
1214 }
1215 
Reset()1216 void ImpSvNumberformatScan::Reset()
1217 {
1218     nStringsCnt = 0;
1219     nResultStringsCnt = 0;
1220     eScannedType = SvNumFormatType::UNDEFINED;
1221     bExp = false;
1222     bThousand = false;
1223     nThousand = 0;
1224     bDecSep = false;
1225     nDecPos = sal_uInt16(-1);
1226     nExpPos = sal_uInt16(-1);
1227     nBlankPos = sal_uInt16(-1);
1228     nCntPre = 0;
1229     nCntPost = 0;
1230     nCntExp = 0;
1231     bFrac = false;
1232     bBlank = false;
1233     nNatNumModifier = 0;
1234 }
1235 
Is100SecZero(sal_uInt16 i,bool bHadDecSep) const1236 bool ImpSvNumberformatScan::Is100SecZero( sal_uInt16 i, bool bHadDecSep ) const
1237 {
1238     sal_uInt16 nIndexPre = PreviousKeyword( i );
1239     return (nIndexPre == NF_KEY_S || nIndexPre == NF_KEY_SS) &&
1240             (bHadDecSep ||
1241              ( i > 0 && nTypeArray[i-1] == NF_SYMBOLTYPE_STRING));
1242               // SS"any"00  take "any" as a valid decimal separator
1243 }
1244 
ScanType()1245 sal_Int32 ImpSvNumberformatScan::ScanType()
1246 {
1247     const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
1248 
1249     sal_Int32 nPos = 0;
1250     sal_uInt16 i = 0;
1251     SvNumFormatType eNewType;
1252     bool bMatchBracket = false;
1253     bool bHaveGeneral = false; // if General/Standard encountered
1254     bool bIsTimeDetected =false;   // hour or second found in format
1255     bool bHaveMinute = false;
1256 
1257     SkipStrings(i, nPos);
1258     while (i < nStringsCnt)
1259     {
1260         if (nTypeArray[i] > 0)
1261         {   // keyword
1262             sal_uInt16 nIndexPre;
1263             sal_uInt16 nIndexNex;
1264 
1265             switch (nTypeArray[i])
1266             {
1267             case NF_KEY_E:                          // E
1268                 eNewType = SvNumFormatType::SCIENTIFIC;
1269                 break;
1270             case NF_KEY_H:                          // H
1271             case NF_KEY_HH:                         // HH
1272                 bIsTimeDetected = true;
1273                 [[fallthrough]];
1274             case NF_KEY_S:                          // S
1275             case NF_KEY_SS:                         // SS
1276                 if ( !bHaveMinute )
1277                     bIsTimeDetected = true;
1278                 [[fallthrough]];
1279             case NF_KEY_AMPM:                       // AM,A,PM,P
1280             case NF_KEY_AP:
1281                 eNewType = SvNumFormatType::TIME;
1282                 break;
1283             case NF_KEY_M:                          // M
1284             case NF_KEY_MM:                         // MM
1285             case NF_KEY_MI:                         // M  minute detected in Finnish
1286             case NF_KEY_MMI:                        // MM
1287                 /* Minute or month.
1288                    Minute if one of:
1289                    * preceded by time keyword H (ignoring separators)
1290                    * followed by time keyword S (ignoring separators)
1291                    * H or S was detected and this is the first M following
1292                    * preceded by '[' amount bracket
1293                    Else month.
1294                    That are the Excel rules. BUT, we break it because certainly
1295                    in something like {HH YYYY-MM-DD} the MM is NOT meant to be
1296                    minute, so not if MM is between YY and DD or DD and YY.
1297                    Actually not if any date specific keyword followed a time
1298                    setting keyword.
1299                 */
1300                 nIndexPre = PreviousKeyword(i);
1301                 nIndexNex = NextKeyword(i);
1302                 if (nIndexPre == NF_KEY_H   ||      // H
1303                     nIndexPre == NF_KEY_HH  ||      // HH
1304                     nIndexNex == NF_KEY_S   ||      // S
1305                     nIndexNex == NF_KEY_SS  ||      // SS
1306                     bIsTimeDetected         ||      // tdf#101147
1307                     PreviousChar(i) == '['  )       // [M
1308                 {
1309                     eNewType = SvNumFormatType::TIME;
1310                     if ( nTypeArray[i] == NF_KEY_M || nTypeArray[i] == NF_KEY_MM )
1311                     {
1312                         nTypeArray[i] -= 2;             // 6 -> 4, 7 -> 5
1313                     }
1314                     bIsTimeDetected = false;        // next M should be month
1315                     bHaveMinute = true;
1316                 }
1317                 else
1318                 {
1319                     eNewType = SvNumFormatType::DATE;
1320                     if ( nTypeArray[i] == NF_KEY_MI || nTypeArray[i] == NF_KEY_MMI )
1321                     {   // follow resolution of tdf#33689 for Finnish
1322                         nTypeArray[i] += 2;             // 4 -> 6, 5 -> 7
1323                     }
1324                 }
1325                 break;
1326             case NF_KEY_MMM:                        // MMM
1327             case NF_KEY_MMMM:                       // MMMM
1328             case NF_KEY_MMMMM:                      // MMMMM
1329             case NF_KEY_Q:                          // Q
1330             case NF_KEY_QQ:                         // QQ
1331             case NF_KEY_D:                          // D
1332             case NF_KEY_DD:                         // DD
1333             case NF_KEY_DDD:                        // DDD
1334             case NF_KEY_DDDD:                       // DDDD
1335             case NF_KEY_YY:                         // YY
1336             case NF_KEY_YYYY:                       // YYYY
1337             case NF_KEY_NN:                         // NN
1338             case NF_KEY_NNN:                        // NNN
1339             case NF_KEY_NNNN:                       // NNNN
1340             case NF_KEY_WW :                        // WW
1341             case NF_KEY_AAA :                       // AAA
1342             case NF_KEY_AAAA :                      // AAAA
1343             case NF_KEY_EC :                        // E
1344             case NF_KEY_EEC :                       // EE
1345             case NF_KEY_G :                         // G
1346             case NF_KEY_GG :                        // GG
1347             case NF_KEY_GGG :                       // GGG
1348             case NF_KEY_R :                         // R
1349             case NF_KEY_RR :                        // RR
1350                 eNewType = SvNumFormatType::DATE;
1351                 bIsTimeDetected = false;
1352                 break;
1353             case NF_KEY_CCC:                        // CCC
1354                 eNewType = SvNumFormatType::CURRENCY;
1355                 break;
1356             case NF_KEY_BOOLEAN:                    // BOOLEAN
1357                 eNewType = SvNumFormatType::LOGICAL;
1358                 break;
1359             case NF_KEY_GENERAL:                    // General
1360                 eNewType = SvNumFormatType::NUMBER;
1361                 bHaveGeneral = true;
1362                 break;
1363             default:
1364                 eNewType = SvNumFormatType::UNDEFINED;
1365                 break;
1366             }
1367         }
1368         else
1369         {                                           // control character
1370             switch ( sStrArray[i][0] )
1371             {
1372             case '#':
1373             case '?':
1374                 eNewType = SvNumFormatType::NUMBER;
1375                 break;
1376             case '0':
1377                 if ( eScannedType & SvNumFormatType::TIME )
1378                 {
1379                     if ( Is100SecZero( i, bDecSep ) )
1380                     {
1381                         bDecSep = true;                 // subsequent 0's
1382                         eNewType = SvNumFormatType::TIME;
1383                     }
1384                     else
1385                     {
1386                         return nPos;                    // Error
1387                     }
1388                 }
1389                 else
1390                 {
1391                     eNewType = SvNumFormatType::NUMBER;
1392                 }
1393                 break;
1394             case '%':
1395                 eNewType = SvNumFormatType::PERCENT;
1396                 break;
1397             case '/':
1398                 eNewType = SvNumFormatType::FRACTION;
1399                 break;
1400             case '[':
1401                 if ( i < nStringsCnt-1 &&
1402                      nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
1403                      sStrArray[i+1][0] == '$' )
1404                 {
1405                     eNewType = SvNumFormatType::CURRENCY;
1406                     bMatchBracket = true;
1407                 }
1408                 else if ( i < nStringsCnt-1 &&
1409                           nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
1410                           sStrArray[i+1][0] == '~' )
1411                 {
1412                     eNewType = SvNumFormatType::DATE;
1413                     bMatchBracket = true;
1414                 }
1415                 else
1416                 {
1417                     sal_uInt16 nIndexNex = NextKeyword(i);
1418                     if (nIndexNex == NF_KEY_H   ||  // H
1419                         nIndexNex == NF_KEY_HH  ||  // HH
1420                         nIndexNex == NF_KEY_M   ||  // M
1421                         nIndexNex == NF_KEY_MM  ||  // MM
1422                         nIndexNex == NF_KEY_S   ||  // S
1423                         nIndexNex == NF_KEY_SS   )  // SS
1424                         eNewType = SvNumFormatType::TIME;
1425                     else
1426                     {
1427                         return nPos;                // Error
1428                     }
1429                 }
1430                 break;
1431             case '@':
1432                 eNewType = SvNumFormatType::TEXT;
1433                 break;
1434             default:
1435                 if (pLoc->getTime100SecSep() == sStrArray[i])
1436                 {
1437                     bDecSep = true;                  // for SS,0
1438                 }
1439                 eNewType = SvNumFormatType::UNDEFINED;
1440                 break;
1441             }
1442         }
1443         if (eScannedType == SvNumFormatType::UNDEFINED)
1444         {
1445             eScannedType = eNewType;
1446         }
1447         else if (eScannedType == SvNumFormatType::TEXT || eNewType == SvNumFormatType::TEXT)
1448         {
1449             eScannedType = SvNumFormatType::TEXT; // Text always remains text
1450         }
1451         else if (eNewType == SvNumFormatType::UNDEFINED)
1452         { // Remains as is
1453         }
1454         else if (eScannedType != eNewType)
1455         {
1456             switch (eScannedType)
1457             {
1458             case SvNumFormatType::DATE:
1459                 switch (eNewType)
1460                 {
1461                 case SvNumFormatType::TIME:
1462                     eScannedType = SvNumFormatType::DATETIME;
1463                     break;
1464                 case SvNumFormatType::FRACTION:         // DD/MM
1465                     break;
1466                 default:
1467                     if (nCurrPos >= 0)
1468                     {
1469                         eScannedType = SvNumFormatType::UNDEFINED;
1470                     }
1471                     else if ( sStrArray[i] != pFormatter->GetDateSep() )
1472                     {
1473                         return nPos;
1474                     }
1475                 }
1476                 break;
1477             case SvNumFormatType::TIME:
1478                 switch (eNewType)
1479                 {
1480                 case SvNumFormatType::DATE:
1481                     eScannedType = SvNumFormatType::DATETIME;
1482                     break;
1483                 case SvNumFormatType::FRACTION:         // MM/SS
1484                     break;
1485                 default:
1486                     if (nCurrPos >= 0)
1487                     {
1488                         eScannedType = SvNumFormatType::UNDEFINED;
1489                     }
1490                     else if (pLoc->getTimeSep() != sStrArray[i])
1491                     {
1492                         return nPos;
1493                     }
1494                     break;
1495                 }
1496                 break;
1497             case SvNumFormatType::DATETIME:
1498                 switch (eNewType)
1499                 {
1500                 case SvNumFormatType::TIME:
1501                 case SvNumFormatType::DATE:
1502                     break;
1503                 case SvNumFormatType::FRACTION:         // DD/MM
1504                     break;
1505                 default:
1506                     if (nCurrPos >= 0)
1507                     {
1508                         eScannedType = SvNumFormatType::UNDEFINED;
1509                     }
1510                     else if ( pFormatter->GetDateSep() != sStrArray[i] &&
1511                               pLoc->getTimeSep() != sStrArray[i] )
1512                     {
1513                         return nPos;
1514                     }
1515                 }
1516                 break;
1517             case SvNumFormatType::PERCENT:
1518                 switch (eNewType)
1519                 {
1520                 case SvNumFormatType::NUMBER:   // Only number to percent
1521                     break;
1522                 default:
1523                     return nPos;
1524                 }
1525                 break;
1526             case SvNumFormatType::SCIENTIFIC:
1527                 switch (eNewType)
1528                 {
1529                 case SvNumFormatType::NUMBER:   // Only number to E
1530                     break;
1531                 default:
1532                     return nPos;
1533                 }
1534                 break;
1535             case SvNumFormatType::NUMBER:
1536                 switch (eNewType)
1537                 {
1538                 case SvNumFormatType::SCIENTIFIC:
1539                 case SvNumFormatType::PERCENT:
1540                 case SvNumFormatType::FRACTION:
1541                 case SvNumFormatType::CURRENCY:
1542                     eScannedType = eNewType;
1543                     break;
1544                 default:
1545                     if (nCurrPos >= 0)
1546                     {
1547                         eScannedType = SvNumFormatType::UNDEFINED;
1548                     }
1549                     else
1550                     {
1551                         return nPos;
1552                     }
1553                 }
1554                 break;
1555             case SvNumFormatType::FRACTION:
1556                 switch (eNewType)
1557                 {
1558                 case SvNumFormatType::NUMBER:   // Only number to fraction
1559                     break;
1560                 default:
1561                     return nPos;
1562                 }
1563                 break;
1564             default:
1565                 break;
1566             }
1567         }
1568         nPos = nPos + sStrArray[i].getLength(); // Position of correction
1569         i++;
1570         if ( bMatchBracket )
1571         {   // no type detection inside of matching brackets if [$...], [~...]
1572             while ( bMatchBracket && i < nStringsCnt )
1573             {
1574                 if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL
1575                      && sStrArray[i][0] == ']' )
1576                 {
1577                     bMatchBracket = false;
1578                 }
1579                 else
1580                 {
1581                     nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1582                 }
1583                 nPos = nPos + sStrArray[i].getLength();
1584                 i++;
1585             }
1586             if ( bMatchBracket )
1587             {
1588                 return nPos; // missing closing bracket at end of code
1589             }
1590         }
1591         SkipStrings(i, nPos);
1592     }
1593 
1594     if ((eScannedType == SvNumFormatType::NUMBER ||
1595          eScannedType == SvNumFormatType::UNDEFINED) &&
1596         nCurrPos >= 0 && !bHaveGeneral)
1597     {
1598         eScannedType = SvNumFormatType::CURRENCY; // old "automatic" currency
1599     }
1600     if (eScannedType == SvNumFormatType::UNDEFINED)
1601     {
1602         eScannedType = SvNumFormatType::DEFINED;
1603     }
1604     return 0; // All is fine
1605 }
1606 
InsertSymbol(sal_uInt16 & nPos,svt::NfSymbolType eType,const OUString & rStr)1607 bool ImpSvNumberformatScan::InsertSymbol( sal_uInt16 & nPos, svt::NfSymbolType eType, const OUString& rStr )
1608 {
1609     if (nStringsCnt >= NF_MAX_FORMAT_SYMBOLS || nPos > nStringsCnt)
1610     {
1611         return false;
1612     }
1613     if (nPos > 0 && nTypeArray[nPos-1] == NF_SYMBOLTYPE_EMPTY)
1614     {
1615         --nPos; // reuse position
1616     }
1617     else
1618     {
1619         if (nStringsCnt >= NF_MAX_FORMAT_SYMBOLS - 1)
1620         {
1621             return false;
1622         }
1623         ++nStringsCnt;
1624         for (size_t i = nStringsCnt; i > nPos; --i)
1625         {
1626             nTypeArray[i] = nTypeArray[i-1];
1627             sStrArray[i] = sStrArray[i-1];
1628         }
1629     }
1630     ++nResultStringsCnt;
1631     nTypeArray[nPos] = static_cast<short>(eType);
1632     sStrArray[nPos] = rStr;
1633     return true;
1634 }
1635 
FinalScanGetCalendar(sal_Int32 & nPos,sal_uInt16 & i,sal_uInt16 & rResultStringsCnt)1636 int ImpSvNumberformatScan::FinalScanGetCalendar( sal_Int32& nPos, sal_uInt16& i,
1637                                                  sal_uInt16& rResultStringsCnt )
1638 {
1639     if ( i < nStringsCnt-1 &&
1640          sStrArray[i][0] == '[' &&
1641          nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
1642          sStrArray[i+1][0] == '~' )
1643     {
1644         // [~calendarID]
1645         nPos = nPos + sStrArray[i].getLength();           // [
1646         nTypeArray[i] = NF_SYMBOLTYPE_CALDEL;
1647         nPos = nPos + sStrArray[++i].getLength();         // ~
1648         sStrArray[i-1] += sStrArray[i];                   // [~
1649         nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
1650         rResultStringsCnt--;
1651         if ( ++i >= nStringsCnt )
1652         {
1653             return -1; // error
1654         }
1655         nPos = nPos + sStrArray[i].getLength();           // calendarID
1656         OUString& rStr = sStrArray[i];
1657         nTypeArray[i] = NF_SYMBOLTYPE_CALENDAR;          // convert
1658         i++;
1659         while ( i < nStringsCnt && sStrArray[i][0] != ']' )
1660         {
1661             nPos = nPos + sStrArray[i].getLength();
1662             rStr += sStrArray[i];
1663             nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
1664             rResultStringsCnt--;
1665             i++;
1666         }
1667         if ( rStr.getLength() && i < nStringsCnt &&
1668              sStrArray[i][0] == ']' )
1669         {
1670             nTypeArray[i] = NF_SYMBOLTYPE_CALDEL;
1671             nPos = nPos + sStrArray[i].getLength();
1672             i++;
1673         }
1674         else
1675         {
1676             return -1; // error
1677         }
1678         return 1;
1679     }
1680     return 0;
1681 }
1682 
IsDateFragment(size_t nPos1,size_t nPos2) const1683 bool ImpSvNumberformatScan::IsDateFragment( size_t nPos1, size_t nPos2 ) const
1684 {
1685     return nPos2 - nPos1 == 2 && nTypeArray[nPos1+1] == NF_SYMBOLTYPE_DATESEP;
1686 }
1687 
SwapArrayElements(size_t nPos1,size_t nPos2)1688 void ImpSvNumberformatScan::SwapArrayElements( size_t nPos1, size_t nPos2 )
1689 {
1690     std::swap( nTypeArray[nPos1], nTypeArray[nPos2]);
1691     std::swap( sStrArray[nPos1], sStrArray[nPos2]);
1692 }
1693 
FinalScan(OUString & rString)1694 sal_Int32 ImpSvNumberformatScan::FinalScan( OUString& rString )
1695 {
1696     const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
1697 
1698     // save values for convert mode
1699     OUString sOldDecSep       = pFormatter->GetNumDecimalSep();
1700     OUString sOldThousandSep  = pFormatter->GetNumThousandSep();
1701     OUString sOldDateSep      = pFormatter->GetDateSep();
1702     OUString sOldTimeSep      = pLoc->getTimeSep();
1703     OUString sOldTime100SecSep= pLoc->getTime100SecSep();
1704     OUString sOldCurSymbol    = GetCurSymbol();
1705     OUString sOldCurString = GetCurString();
1706     sal_Unicode cOldKeyH    = sKeyword[NF_KEY_H][0];
1707     sal_Unicode cOldKeyMI   = sKeyword[NF_KEY_MI][0];
1708     sal_Unicode cOldKeyS    = sKeyword[NF_KEY_S][0];
1709     DateOrder eOldDateOrder = pLoc->getDateOrder();
1710     sal_uInt16 nDayPos, nMonthPos, nYearPos;
1711     nDayPos = nMonthPos = nYearPos = SAL_MAX_UINT16;
1712 
1713     // If the group separator is a No-Break Space (French) continue with a
1714     // normal space instead so queries on space work correctly.
1715     // The same for Narrow No-Break Space just in case some locale uses it.
1716     // The format string is adjusted to allow both.
1717     // For output of the format code string the LocaleData characters are used.
1718     if ( (sOldThousandSep[0] == cNoBreakSpace || sOldThousandSep[0] == cNarrowNoBreakSpace) &&
1719             sOldThousandSep.getLength() == 1 )
1720     {
1721         sOldThousandSep = " ";
1722     }
1723     bool bNewDateOrder = false;
1724     // change locale data et al
1725     if (bConvertMode)
1726     {
1727         pFormatter->ChangeIntl(eNewLnge);
1728         //! pointer may have changed
1729         pLoc = pFormatter->GetLocaleData();
1730         //! init new keywords
1731         InitKeywords();
1732         // Adapt date order to target locale, but Excel does not handle date
1733         // particle re-ordering for the target locale when loading documents,
1734         // though it does exchange separators, tdf#113889
1735         bNewDateOrder = (mbConvertDateOrder && eOldDateOrder != pLoc->getDateOrder());
1736     }
1737     const CharClass* pChrCls = pFormatter->GetCharClass();
1738 
1739     sal_Int32 nPos = 0;                    // error correction position
1740     sal_uInt16 i = 0;                      // symbol loop counter
1741     sal_uInt16 nCounter = 0;               // counts digits
1742     nResultStringsCnt = nStringsCnt;       // counts remaining symbols
1743     bDecSep = false;                       // reset in case already used in TypeCheck
1744     bool bThaiT = false;                   // Thai T NatNum modifier present
1745     bool bTimePart = false;
1746     bool bDenomin = false;                 // Set when reading end of denominator
1747 
1748     switch (eScannedType)
1749     {
1750     case SvNumFormatType::TEXT:
1751     case SvNumFormatType::DEFINED:
1752         while (i < nStringsCnt)
1753         {
1754             switch (nTypeArray[i])
1755             {
1756             case NF_SYMBOLTYPE_BLANK:
1757             case NF_SYMBOLTYPE_STAR:
1758                 break;
1759             case NF_KEY_GENERAL : // #77026# "General" is the same as "@"
1760                 break;
1761             default:
1762                 if ( nTypeArray[i] != NF_SYMBOLTYPE_DEL ||
1763                      sStrArray[i][0] != '@' )
1764                 {
1765                     nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1766                 }
1767                 break;
1768             }
1769             nPos = nPos + sStrArray[i].getLength();
1770             i++;
1771         } // of while
1772         break;
1773 
1774     case SvNumFormatType::NUMBER:
1775     case SvNumFormatType::PERCENT:
1776     case SvNumFormatType::CURRENCY:
1777     case SvNumFormatType::SCIENTIFIC:
1778     case SvNumFormatType::FRACTION:
1779         while (i < nStringsCnt)
1780         {
1781             // TODO: rechecking eScannedType is unnecessary.
1782             // This switch-case is for eScannedType == SvNumFormatType::FRACTION anyway
1783             if (eScannedType == SvNumFormatType::FRACTION &&        // special case
1784                 nTypeArray[i] == NF_SYMBOLTYPE_DEL &&           // # ### #/#
1785                 StringEqualsChar( sOldThousandSep, ' ' ) &&     // e.g. France or Sweden
1786                 StringEqualsChar( sStrArray[i], ' ' ) &&
1787                 !bFrac                          &&
1788                 IsLastBlankBeforeFrac(i) )
1789             {
1790                 nTypeArray[i] = NF_SYMBOLTYPE_STRING;           // del->string
1791             }                                                   // No thousands marker
1792 
1793             if (nTypeArray[i] == NF_SYMBOLTYPE_BLANK    ||
1794                 nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
1795                 nTypeArray[i] == NF_KEY_CCC         ||          // CCC
1796                 nTypeArray[i] == NF_KEY_GENERAL )               // Standard
1797             {
1798                 if (nTypeArray[i] == NF_KEY_GENERAL)
1799                 {
1800                     nThousand = FLAG_STANDARD_IN_FORMAT;
1801                     if ( bConvertMode )
1802                     {
1803                         sStrArray[i] = sNameStandardFormat;
1804                     }
1805                 }
1806                 nPos = nPos + sStrArray[i].getLength();
1807                 i++;
1808             }
1809             else if (nTypeArray[i] == NF_SYMBOLTYPE_STRING ||   // No Strings or
1810                      nTypeArray[i] > 0)                         // Keywords
1811             {
1812                 if (eScannedType == SvNumFormatType::SCIENTIFIC &&
1813                     nTypeArray[i] == NF_KEY_E)                  // E+
1814                 {
1815                     if (bExp)                                   // Double
1816                     {
1817                         return nPos;
1818                     }
1819                     bExp = true;
1820                     nExpPos = i;
1821                     if (bDecSep)
1822                     {
1823                         nCntPost = nCounter;
1824                     }
1825                     else
1826                     {
1827                         nCntPre = nCounter;
1828                     }
1829                     nCounter = 0;
1830                     nTypeArray[i] = NF_SYMBOLTYPE_EXP;
1831                 }
1832                 else if (eScannedType == SvNumFormatType::FRACTION &&
1833                     (sStrArray[i][0] == ' ' || ( nTypeArray[i] == NF_SYMBOLTYPE_STRING && (sStrArray[i][0] < '0' || sStrArray[i][0] > '9') ) ) )
1834                 {
1835                     if (!bBlank && !bFrac) // Not double or after a /
1836                     {
1837                         if (bDecSep && nCounter > 0) // Decimal places
1838                         {
1839                             return nPos; // Error
1840                         }
1841                         if (sStrArray[i][0] == ' ' ||  nCounter > 0 )   // treat string as integer/fraction delimiter only if there is integer
1842                         {
1843                             bBlank = true;
1844                             nBlankPos = i;
1845                             nCntPre = nCounter;
1846                             nCounter = 0;
1847                             nTypeArray[i] = NF_SYMBOLTYPE_FRACBLANK;
1848                         }
1849                     }
1850                     else if ( sStrArray[i][0] == ' ' )
1851                         nTypeArray[i] = NF_SYMBOLTYPE_FRACBLANK;
1852                     else if ( bFrac && ( nCounter > 0 ) )
1853                         bDenomin = true; // following elements are no more part of denominator
1854                 }
1855                 else if (nTypeArray[i] == NF_KEY_THAI_T)
1856                 {
1857                     bThaiT = true;
1858                     sStrArray[i] = sKeyword[nTypeArray[i]];
1859                 }
1860                 else if (sStrArray[i][0] >= '0' &&
1861                          sStrArray[i][0] <= '9' && !bDenomin) // denominator was not yet found
1862                 {
1863                     OUString sDiv;
1864                     sal_uInt16 j = i;
1865                     while(j < nStringsCnt && sStrArray[j][0] >= '0' && sStrArray[j][0] <= '9')
1866                     {
1867                         sDiv += sStrArray[j++];
1868                     }
1869                     assert(j > 0 && "if i is 0, first iteration through loop is guaranteed by surrounding if condition");
1870                     if (std::u16string_view(OUString::number(sDiv.toInt32())) == sDiv)
1871                     {
1872                         // Found a Divisor
1873                         while (i < j)
1874                         {
1875                             nTypeArray[i++] = NF_SYMBOLTYPE_FRAC_FDIV;
1876                         }
1877                         i = j - 1; // Stop the loop
1878                         if (nCntPost)
1879                         {
1880                             nCounter = nCntPost;
1881                         }
1882                         else if (nCntPre)
1883                         {
1884                             nCounter = nCntPre;
1885                         }
1886                         // don't artificially increment nCntPre for forced denominator
1887                         if ( ( eScannedType != SvNumFormatType::FRACTION ) && (!nCntPre) )
1888                         {
1889                             nCntPre++;
1890                         }
1891                         if ( bFrac )
1892                             bDenomin = true; // next content should be treated as outside denominator
1893                     }
1894                 }
1895                 else
1896                 {
1897                     if ( bFrac && ( nCounter > 0 ) )
1898                         bDenomin = true;    // next content should be treated as outside denominator
1899                     nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1900                 }
1901                 nPos = nPos + sStrArray[i].getLength();
1902                 i++;
1903             }
1904             else if (nTypeArray[i] == NF_SYMBOLTYPE_DEL)
1905             {
1906                 sal_Unicode cHere = sStrArray[i][0];
1907                 sal_Unicode cSaved = cHere;
1908                 // Handle not pre-known separators in switch.
1909                 sal_Unicode cSimplified;
1910                 if (StringEqualsChar( pFormatter->GetNumThousandSep(), cHere))
1911                 {
1912                     cSimplified = ',';
1913                 }
1914                 else if (StringEqualsChar( pFormatter->GetNumDecimalSep(), cHere))
1915                 {
1916                     cSimplified = '.';
1917                 }
1918                 else
1919                 {
1920                     cSimplified = cHere;
1921                 }
1922 
1923                 OUString& rStr = sStrArray[i];
1924 
1925                 switch ( cSimplified )
1926                 {
1927                 case '#':
1928                 case '0':
1929                 case '?':
1930                     if (nThousand > 0)                  // #... #
1931                     {
1932                         return nPos;                    // Error
1933                     }
1934                     if ( !bDenomin )
1935                     {
1936                         nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
1937                         nPos = nPos + rStr.getLength();
1938                         i++;
1939                         nCounter++;
1940                         while (i < nStringsCnt &&
1941                               (sStrArray[i][0] == '#' ||
1942                                sStrArray[i][0] == '0' ||
1943                                sStrArray[i][0] == '?'))
1944                         {
1945                             nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
1946                             nPos = nPos + sStrArray[i].getLength();
1947                             nCounter++;
1948                             i++;
1949                         }
1950                     }
1951                     else // after denominator, treat any character as text
1952                     {
1953                         nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1954                         nPos = nPos + sStrArray[i].getLength();
1955                     }
1956                     break;
1957                 case '-':
1958                     if ( bDecSep && nDecPos+1 == i &&
1959                          nTypeArray[nDecPos] == NF_SYMBOLTYPE_DECSEP )
1960                     {
1961                         // "0.--"
1962                         nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
1963                         nPos = nPos + rStr.getLength();
1964                         i++;
1965                         nCounter++;
1966                         while (i < nStringsCnt &&
1967                                (sStrArray[i][0] == '-') )
1968                         {
1969                             // If more than two dashes are present in
1970                             // currency formats the last dash will be
1971                             // interpreted literally as a minus sign.
1972                             // Has to be this ugly. Period.
1973                             if ( eScannedType == SvNumFormatType::CURRENCY
1974                                  && rStr.getLength() >= 2 &&
1975                                  (i == nStringsCnt-1 ||
1976                                   sStrArray[i+1][0] != '-') )
1977                             {
1978                                 break;
1979                             }
1980                             rStr += sStrArray[i];
1981                             nPos = nPos + sStrArray[i].getLength();
1982                             nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
1983                             nResultStringsCnt--;
1984                             nCounter++;
1985                             i++;
1986                         }
1987                     }
1988                     else
1989                     {
1990                         nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1991                         nPos = nPos + sStrArray[i].getLength();
1992                         i++;
1993                     }
1994                     break;
1995                 case '.':
1996                 case ',':
1997                 case '\'':
1998                 case ' ':
1999                     if ( StringEqualsChar( sOldThousandSep, cSaved ) )
2000                     {
2001                         // previous char with skip empty
2002                         sal_Unicode cPre = PreviousChar(i);
2003                         sal_Unicode cNext;
2004                         if (bExp || bBlank || bFrac)
2005                         {
2006                             // after E, / or ' '
2007                             if ( !StringEqualsChar( sOldThousandSep, ' ' ) )
2008                             {
2009                                 nPos = nPos + sStrArray[i].getLength();
2010                                 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2011                                 nResultStringsCnt--;
2012                                 i++; // eat it
2013                             }
2014                             else
2015                             {
2016                                 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2017                                 if ( bFrac && (nCounter > 0) )
2018                                     bDenomin = true; // end of denominator
2019                             }
2020                         }
2021                         else if (i > 0 && i < nStringsCnt-1   &&
2022                                  (cPre == '#' || cPre == '0' || cPre == '?')      &&
2023                                  ((cNext = NextChar(i)) == '#' || cNext == '0' || cNext == '?')) // #,#
2024                         {
2025                             nPos = nPos + sStrArray[i].getLength();
2026                             if (!bThousand) // only once
2027                             {
2028                                 bThousand = true;
2029                             }
2030                             // Eat it, will be reinserted at proper grouping positions further down.
2031                             nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2032                             nResultStringsCnt--;
2033                             i++;
2034                         }
2035                         else if (i > 0 && (cPre == '#' || cPre == '0' || cPre == '?')
2036                                  && PreviousType(i) == NF_SYMBOLTYPE_DIGIT
2037                                  && nThousand < FLAG_STANDARD_IN_FORMAT )
2038                         {   // #,,,,
2039                             if ( StringEqualsChar( sOldThousandSep, ' ' ) )
2040                             {
2041                                 // strange, those French...
2042                                 bool bFirst = true;
2043                                 //  set a hard No-Break Space or ConvertMode
2044                                 const OUString& rSepF = pFormatter->GetNumThousandSep();
2045                                 while ( i < nStringsCnt &&
2046                                         sStrArray[i] == sOldThousandSep &&
2047                                         StringEqualsChar( sOldThousandSep, NextChar(i) ) )
2048                                 {   // last was a space or another space
2049                                     // is following => separator
2050                                     nPos = nPos + sStrArray[i].getLength();
2051                                     if ( bFirst )
2052                                     {
2053                                         bFirst = false;
2054                                         rStr = rSepF;
2055                                         nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
2056                                     }
2057                                     else
2058                                     {
2059                                         rStr += rSepF;
2060                                         nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2061                                         nResultStringsCnt--;
2062                                     }
2063                                     nThousand++;
2064                                     i++;
2065                                 }
2066                                 if ( i < nStringsCnt-1 &&
2067                                      sStrArray[i] == sOldThousandSep )
2068                                 {
2069                                     // something following last space
2070                                     // => space if currency contained,
2071                                     // else separator
2072                                     nPos = nPos + sStrArray[i].getLength();
2073                                     if ( (nPos <= nCurrPos &&
2074                                           nCurrPos < nPos + sStrArray[i+1].getLength()) ||
2075                                          nTypeArray[i+1] == NF_KEY_CCC ||
2076                                          (i < nStringsCnt-2 &&
2077                                           sStrArray[i+1][0] == '[' &&
2078                                           sStrArray[i+2][0] == '$') )
2079                                     {
2080                                         nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2081                                     }
2082                                     else
2083                                     {
2084                                         if ( bFirst )
2085                                         {
2086                                             rStr = rSepF;
2087                                             nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
2088                                         }
2089                                         else
2090                                         {
2091                                             rStr += rSepF;
2092                                             nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2093                                             nResultStringsCnt--;
2094                                         }
2095                                         nThousand++;
2096                                     }
2097                                     i++;
2098                                 }
2099                             }
2100                             else
2101                             {
2102                                 do
2103                                 {
2104                                     nThousand++;
2105                                     nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
2106                                     nPos = nPos + sStrArray[i].getLength();
2107                                     sStrArray[i] = pFormatter->GetNumThousandSep();
2108                                     i++;
2109                                 }
2110                                 while (i < nStringsCnt && sStrArray[i] == sOldThousandSep);
2111                             }
2112                         }
2113                         else // any grsep
2114                         {
2115                             nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2116                             nPos = nPos + rStr.getLength();
2117                             i++;
2118                             while ( i < nStringsCnt && sStrArray[i] == sOldThousandSep )
2119                             {
2120                                 rStr += sStrArray[i];
2121                                 nPos = nPos + sStrArray[i].getLength();
2122                                 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2123                                 nResultStringsCnt--;
2124                                 i++;
2125                             }
2126                         }
2127                     }
2128                     else if ( StringEqualsChar( sOldDecSep, cSaved ) )
2129                     {
2130                         if (bBlank || bFrac)    // . behind / or ' '
2131                         {
2132                             return nPos;        // error
2133                         }
2134                         else if (bExp)          // behind E
2135                         {
2136                             nPos = nPos + sStrArray[i].getLength();
2137                             nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2138                             nResultStringsCnt--;
2139                             i++;                // eat it
2140                         }
2141                         else if (bDecSep)       // any .
2142                         {
2143                             nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2144                             nPos = nPos + rStr.getLength();
2145                             i++;
2146                             while ( i < nStringsCnt && sStrArray[i] == sOldDecSep )
2147                             {
2148                                 rStr += sStrArray[i];
2149                                 nPos = nPos + sStrArray[i].getLength();
2150                                 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2151                                 nResultStringsCnt--;
2152                                 i++;
2153                             }
2154                         }
2155                         else
2156                         {
2157                             nPos = nPos + sStrArray[i].getLength();
2158                             nTypeArray[i] = NF_SYMBOLTYPE_DECSEP;
2159                             sStrArray[i] = pFormatter->GetNumDecimalSep();
2160                             bDecSep = true;
2161                             nDecPos = i;
2162                             nCntPre = nCounter;
2163                             nCounter = 0;
2164 
2165                             i++;
2166                         }
2167                     } // of else = DecSep
2168                     else // . without meaning
2169                     {
2170                         if (cSaved == ' ' &&
2171                             eScannedType == SvNumFormatType::FRACTION &&
2172                             StringEqualsChar( sStrArray[i], ' ' ) )
2173                         {
2174                             if (!bBlank && !bFrac)  // no dups
2175                             {                       // or behind /
2176                                 if (bDecSep && nCounter > 0) // dec.
2177                                 {
2178                                     return nPos; // error
2179                                 }
2180                                 bBlank = true;
2181                                 nBlankPos = i;
2182                                 nCntPre = nCounter;
2183                                 nCounter = 0;
2184                             }
2185                             if ( bFrac && (nCounter > 0) )
2186                                 bDenomin = true; // next content is not part of denominator
2187                             nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2188                             nPos = nPos + sStrArray[i].getLength();
2189                         }
2190                         else
2191                         {
2192                             nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2193                             if ( bFrac && (nCounter > 0) )
2194                                 bDenomin = true; // next content is not part of denominator
2195                             nPos = nPos + rStr.getLength();
2196                             i++;
2197                             while (i < nStringsCnt && StringEqualsChar( sStrArray[i], cSaved ) )
2198                             {
2199                                 rStr += sStrArray[i];
2200                                 nPos = nPos + sStrArray[i].getLength();
2201                                 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2202                                 nResultStringsCnt--;
2203                                 i++;
2204                             }
2205                         }
2206                     }
2207                     break;
2208                 case '/':
2209                     if (eScannedType == SvNumFormatType::FRACTION)
2210                     {
2211                         if ( i == 0 ||
2212                              (nTypeArray[i-1] != NF_SYMBOLTYPE_DIGIT &&
2213                               nTypeArray[i-1] != NF_SYMBOLTYPE_EMPTY) )
2214                         {
2215                             return nPos ? nPos : 1; // /? not allowed
2216                         }
2217                         else if (!bFrac || (bDecSep && nCounter > 0))
2218                         {
2219                             bFrac = true;
2220                             nCntPost = nCounter;
2221                             nCounter = 0;
2222                             nTypeArray[i] = NF_SYMBOLTYPE_FRAC;
2223                             nPos = nPos + sStrArray[i].getLength();
2224                             i++;
2225                         }
2226                         else // / double or in , in the denominator
2227                         {
2228                             return nPos; // Error
2229                         }
2230                     }
2231                     else
2232                     {
2233                         nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2234                         nPos = nPos + sStrArray[i].getLength();
2235                         i++;
2236                     }
2237                     break;
2238                 case '[' :
2239                     if ( eScannedType == SvNumFormatType::CURRENCY &&
2240                          i < nStringsCnt-1 &&
2241                          nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
2242                          sStrArray[i+1][0] == '$' )
2243                     {
2244                         // [$DM-xxx]
2245                         nPos = nPos + sStrArray[i].getLength();     // [
2246                         nTypeArray[i] = NF_SYMBOLTYPE_CURRDEL;
2247                         nPos = nPos + sStrArray[++i].getLength();   // $
2248                         sStrArray[i-1] += sStrArray[i];             // [$
2249                         nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2250                         nResultStringsCnt--;
2251                         if ( ++i >= nStringsCnt )
2252                         {
2253                             return nPos; // Error
2254                         }
2255                         nPos = nPos + sStrArray[i].getLength();     // DM
2256                         OUString* pStr = &sStrArray[i];
2257                         nTypeArray[i] = NF_SYMBOLTYPE_CURRENCY; // convert
2258                         bool bHadDash = false;
2259                         i++;
2260                         while ( i < nStringsCnt && sStrArray[i][0] != ']' )
2261                         {
2262                             nPos = nPos + sStrArray[i].getLength();
2263                             if ( bHadDash )
2264                             {
2265                                 *pStr += sStrArray[i];
2266                                 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2267                                 nResultStringsCnt--;
2268                             }
2269                             else
2270                             {
2271                                 if ( sStrArray[i][0] == '-' )
2272                                 {
2273                                     bHadDash = true;
2274                                     pStr = &sStrArray[i];
2275                                     nTypeArray[i] = NF_SYMBOLTYPE_CURREXT;
2276                                 }
2277                                 else
2278                                 {
2279                                     *pStr += sStrArray[i];
2280                                     nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2281                                     nResultStringsCnt--;
2282                                 }
2283                             }
2284                             i++;
2285                         }
2286                         if ( rStr.getLength() && i < nStringsCnt && sStrArray[i][0] == ']' )
2287                         {
2288                             nTypeArray[i] = NF_SYMBOLTYPE_CURRDEL;
2289                             nPos = nPos + sStrArray[i].getLength();
2290                             i++;
2291                         }
2292                         else
2293                         {
2294                             return nPos; // Error
2295                         }
2296                     }
2297                     else
2298                     {
2299                         nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2300                         nPos = nPos + sStrArray[i].getLength();
2301                         i++;
2302                     }
2303                     break;
2304                 default: // Other Dels
2305                     if (eScannedType == SvNumFormatType::PERCENT && cHere == '%')
2306                     {
2307                         nTypeArray[i] = NF_SYMBOLTYPE_PERCENT;
2308                     }
2309                     else
2310                     {
2311                         nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2312                     }
2313                     nPos = nPos + sStrArray[i].getLength();
2314                     i++;
2315                     break;
2316                 } // of switch (Del)
2317             } // of else Del
2318             else
2319             {
2320                 SAL_WARN( "svl.numbers", "unknown NF_SYMBOLTYPE_..." );
2321                 nPos = nPos + sStrArray[i].getLength();
2322                 i++;
2323             }
2324         } // of while
2325         if (eScannedType == SvNumFormatType::FRACTION)
2326         {
2327             if (bFrac)
2328             {
2329                 nCntExp = nCounter;
2330             }
2331             else if (bBlank)
2332             {
2333                 nCntPost = nCounter;
2334             }
2335             else
2336             {
2337                 nCntPre = nCounter;
2338             }
2339         }
2340         else
2341         {
2342             if (bExp)
2343             {
2344                 nCntExp = nCounter;
2345             }
2346             else if (bDecSep)
2347             {
2348                 nCntPost = nCounter;
2349             }
2350             else
2351             {
2352                 nCntPre = nCounter;
2353             }
2354         }
2355         if (bThousand) // Expansion of grouping separators
2356         {
2357             sal_uInt16 nMaxPos;
2358             if (bFrac)
2359             {
2360                 if (bBlank)
2361                 {
2362                     nMaxPos = nBlankPos;
2363                 }
2364                 else
2365                 {
2366                     nMaxPos = 0;                // no grouping
2367                 }
2368             }
2369             else if (bDecSep)                   // decimal separator present
2370             {
2371                 nMaxPos = nDecPos;
2372             }
2373             else if (bExp)                      // 'E' exponent present
2374             {
2375                 nMaxPos = nExpPos;
2376             }
2377             else                                // up to end
2378             {
2379                 nMaxPos = i;
2380             }
2381             // Insert separators at proper positions.
2382             sal_Int32 nCount = 0;
2383             utl::DigitGroupingIterator aGrouping( pLoc->getDigitGrouping());
2384             size_t nFirstDigitSymbol = nMaxPos;
2385             size_t nFirstGroupingSymbol = nMaxPos;
2386             i = nMaxPos;
2387             while (i-- > 0)
2388             {
2389                 if (nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
2390                 {
2391                     nFirstDigitSymbol = i;
2392                     nCount = nCount + sStrArray[i].getLength(); // MSC converts += to int and then warns, so ...
2393                     // Insert separator only if not leftmost symbol.
2394                     if (i > 0 && nCount >= aGrouping.getPos())
2395                     {
2396                         DBG_ASSERT( sStrArray[i].getLength() == 1,
2397                                     "ImpSvNumberformatScan::FinalScan: combined digits in group separator insertion");
2398                         if (!InsertSymbol( i, NF_SYMBOLTYPE_THSEP, pFormatter->GetNumThousandSep()))
2399                         {
2400                             // nPos isn't correct here, but signals error
2401                             return nPos;
2402                         }
2403                         // i may have been decremented by 1
2404                         nFirstDigitSymbol = i + 1;
2405                         nFirstGroupingSymbol = i;
2406                         aGrouping.advance();
2407                     }
2408                 }
2409             }
2410             // Generated something like "string",000; remove separator again.
2411             if (nFirstGroupingSymbol < nFirstDigitSymbol)
2412             {
2413                 nTypeArray[nFirstGroupingSymbol] = NF_SYMBOLTYPE_EMPTY;
2414                 nResultStringsCnt--;
2415             }
2416         }
2417         // Combine digits into groups to save memory (Info will be copied
2418         // later, taking only non-empty symbols).
2419         for (i = 0; i < nStringsCnt; ++i)
2420         {
2421             if (nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
2422             {
2423                 OUString& rStr = sStrArray[i];
2424                 while (++i < nStringsCnt && nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
2425                 {
2426                     rStr += sStrArray[i];
2427                     nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2428                     nResultStringsCnt--;
2429                 }
2430             }
2431         }
2432         break; // of SvNumFormatType::NUMBER
2433     case SvNumFormatType::DATE:
2434         while (i < nStringsCnt)
2435         {
2436             switch (nTypeArray[i])
2437             {
2438             case NF_SYMBOLTYPE_BLANK:
2439             case NF_SYMBOLTYPE_STAR:
2440             case NF_SYMBOLTYPE_STRING:
2441                 nPos = nPos + sStrArray[i].getLength();
2442                 i++;
2443                 break;
2444             case NF_SYMBOLTYPE_DEL:
2445                 int nCalRet;
2446                 if (sStrArray[i] == sOldDateSep)
2447                 {
2448                     nTypeArray[i] = NF_SYMBOLTYPE_DATESEP;
2449                     nPos = nPos + sStrArray[i].getLength();
2450                     if (bConvertMode)
2451                     {
2452                         sStrArray[i] = pFormatter->GetDateSep();
2453                     }
2454                     i++;
2455                 }
2456                 else if ( (nCalRet = FinalScanGetCalendar( nPos, i, nResultStringsCnt )) != 0 )
2457                 {
2458                     if ( nCalRet < 0  )
2459                     {
2460                         return nPos; // error
2461                     }
2462                 }
2463                 else
2464                 {
2465                     nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2466                     nPos = nPos + sStrArray[i].getLength();
2467                     i++;
2468                 }
2469                 break;
2470             case NF_KEY_THAI_T :
2471                 bThaiT = true;
2472                 [[fallthrough]];
2473             case NF_KEY_M:                          // M
2474             case NF_KEY_MM:                         // MM
2475             case NF_KEY_MMM:                        // MMM
2476             case NF_KEY_MMMM:                       // MMMM
2477             case NF_KEY_MMMMM:                      // MMMMM
2478             case NF_KEY_Q:                          // Q
2479             case NF_KEY_QQ:                         // QQ
2480             case NF_KEY_D:                          // D
2481             case NF_KEY_DD:                         // DD
2482             case NF_KEY_DDD:                        // DDD
2483             case NF_KEY_DDDD:                       // DDDD
2484             case NF_KEY_YY:                         // YY
2485             case NF_KEY_YYYY:                       // YYYY
2486             case NF_KEY_NN:                         // NN
2487             case NF_KEY_NNN:                        // NNN
2488             case NF_KEY_NNNN:                       // NNNN
2489             case NF_KEY_WW :                        // WW
2490             case NF_KEY_AAA :                       // AAA
2491             case NF_KEY_AAAA :                      // AAAA
2492             case NF_KEY_EC :                        // E
2493             case NF_KEY_EEC :                       // EE
2494             case NF_KEY_G :                         // G
2495             case NF_KEY_GG :                        // GG
2496             case NF_KEY_GGG :                       // GGG
2497             case NF_KEY_R :                         // R
2498             case NF_KEY_RR :                        // RR
2499                 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2500                 nPos = nPos + sStrArray[i].getLength();
2501                 if (bNewDateOrder)
2502                 {
2503                     // For simple numeric date formats record date order and
2504                     // later rearrange.
2505                     switch (nTypeArray[i])
2506                     {
2507                         case NF_KEY_M:
2508                         case NF_KEY_MM:
2509                             if (nMonthPos == SAL_MAX_UINT16)
2510                                 nMonthPos = i;
2511                             else
2512                                 bNewDateOrder = false;
2513                         break;
2514                         case NF_KEY_D:
2515                         case NF_KEY_DD:
2516                             if (nDayPos == SAL_MAX_UINT16)
2517                                 nDayPos = i;
2518                             else
2519                                 bNewDateOrder = false;
2520                         break;
2521                         case NF_KEY_YY:
2522                         case NF_KEY_YYYY:
2523                             if (nYearPos == SAL_MAX_UINT16)
2524                                 nYearPos = i;
2525                             else
2526                                 bNewDateOrder = false;
2527                         break;
2528                         default:
2529                             ;   // nothing
2530                     }
2531                 }
2532                 i++;
2533                 break;
2534             default: // Other keywords
2535                 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2536                 nPos = nPos + sStrArray[i].getLength();
2537                 i++;
2538                 break;
2539             }
2540         } // of while
2541         break; // of SvNumFormatType::DATE
2542     case SvNumFormatType::TIME:
2543         while (i < nStringsCnt)
2544         {
2545             sal_Unicode cChar;
2546 
2547             switch (nTypeArray[i])
2548             {
2549             case NF_SYMBOLTYPE_BLANK:
2550             case NF_SYMBOLTYPE_STAR:
2551                 nPos = nPos + sStrArray[i].getLength();
2552                 i++;
2553                 break;
2554             case NF_SYMBOLTYPE_DEL:
2555                 switch( sStrArray[i][0] )
2556                 {
2557                 case '0':
2558                     if ( Is100SecZero( i, bDecSep ) )
2559                     {
2560                         bDecSep = true;
2561                         nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
2562                         OUString& rStr = sStrArray[i];
2563                         nCounter++;
2564                         i++;
2565                         while (i < nStringsCnt &&
2566                                sStrArray[i][0] == '0')
2567                         {
2568                             rStr += sStrArray[i];
2569                             nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2570                             nResultStringsCnt--;
2571                             nCounter++;
2572                             i++;
2573                         }
2574                         nPos += rStr.getLength();
2575                     }
2576                     else
2577                     {
2578                         return nPos;
2579                     }
2580                     break;
2581                 case '#':
2582                 case '?':
2583                     return nPos;
2584                 case '[':
2585                     if (bThousand) // Double
2586                     {
2587                         return nPos;
2588                     }
2589                     bThousand = true; // Empty for Time
2590                     cChar = pChrCls->uppercase(OUString(NextChar(i)))[0];
2591                     if ( cChar == cOldKeyH )
2592                     {
2593                         nThousand = 1;      // H
2594                     }
2595                     else if ( cChar == cOldKeyMI )
2596                     {
2597                         nThousand = 2;      // M
2598                     }
2599                     else if ( cChar == cOldKeyS )
2600                     {
2601                         nThousand = 3;      // S
2602                     }
2603                     else
2604                     {
2605                         return nPos;
2606                     }
2607                     nPos = nPos + sStrArray[i].getLength();
2608                     i++;
2609                     break;
2610                 case ']':
2611                     if (!bThousand) // No preceding [
2612                     {
2613                         return nPos;
2614                     }
2615                     nPos = nPos + sStrArray[i].getLength();
2616                     i++;
2617                     break;
2618                 default:
2619                     nPos = nPos + sStrArray[i].getLength();
2620                     if ( sStrArray[i] == sOldTimeSep )
2621                     {
2622                         nTypeArray[i] = NF_SYMBOLTYPE_TIMESEP;
2623                         if ( bConvertMode )
2624                         {
2625                             sStrArray[i] = pLoc->getTimeSep();
2626                         }
2627                     }
2628                     else if ( sStrArray[i] == sOldTime100SecSep )
2629                     {
2630                         bDecSep = true;
2631                         nTypeArray[i] = NF_SYMBOLTYPE_TIME100SECSEP;
2632                         if ( bConvertMode )
2633                         {
2634                             sStrArray[i] = pLoc->getTime100SecSep();
2635                         }
2636                     }
2637                     else
2638                     {
2639                         nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2640                     }
2641                     i++;
2642                     break;
2643                 }
2644                 break;
2645             case NF_SYMBOLTYPE_STRING:
2646                 nPos = nPos + sStrArray[i].getLength();
2647                 i++;
2648                 break;
2649             case NF_KEY_AMPM:                       // AM/PM
2650             case NF_KEY_AP:                         // A/P
2651                 bExp = true;                        // Abuse for A/P
2652                 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2653                 nPos = nPos + sStrArray[i].getLength();
2654                 i++;
2655                 break;
2656             case NF_KEY_THAI_T :
2657                 bThaiT = true;
2658                 [[fallthrough]];
2659             case NF_KEY_MI:                         // M
2660             case NF_KEY_MMI:                        // MM
2661             case NF_KEY_H:                          // H
2662             case NF_KEY_HH:                         // HH
2663             case NF_KEY_S:                          // S
2664             case NF_KEY_SS:                         // SS
2665                 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2666                 nPos = nPos + sStrArray[i].getLength();
2667                 i++;
2668                 break;
2669             default: // Other keywords
2670                 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2671                 nPos = nPos + sStrArray[i].getLength();
2672                 i++;
2673                 break;
2674             }
2675         }                                       // of while
2676         nCntPost = nCounter;                    // Zero counter
2677         if (bExp)
2678         {
2679             nCntExp = 1;                        // Remembers AM/PM
2680         }
2681         break;                                 // of SvNumFormatType::TIME
2682     case SvNumFormatType::DATETIME:
2683         while (i < nStringsCnt)
2684         {
2685             int nCalRet;
2686             switch (nTypeArray[i])
2687             {
2688             case NF_SYMBOLTYPE_BLANK:
2689             case NF_SYMBOLTYPE_STAR:
2690             case NF_SYMBOLTYPE_STRING:
2691                 nPos = nPos + sStrArray[i].getLength();
2692                 i++;
2693                 break;
2694             case NF_SYMBOLTYPE_DEL:
2695                 if ( (nCalRet = FinalScanGetCalendar( nPos, i, nResultStringsCnt )) != 0 )
2696                 {
2697                     if ( nCalRet < 0  )
2698                     {
2699                         return nPos; // Error
2700                     }
2701                 }
2702                 else
2703                 {
2704                     switch( sStrArray[i][0] )
2705                     {
2706                     case '0':
2707                         if (bTimePart && Is100SecZero(i, bDecSep) && nCounter < MaxCntPost)
2708                         {
2709                             bDecSep = true;
2710                             nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
2711                             OUString& rStr = sStrArray[i];
2712                             nCounter++;
2713                             i++;
2714                             while (i < nStringsCnt &&
2715                                    sStrArray[i][0] == '0' && nCounter < MaxCntPost)
2716                             {
2717                                 rStr += sStrArray[i];
2718                                 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2719                                 nResultStringsCnt--;
2720                                 nCounter++;
2721                                 i++;
2722                             }
2723                             nPos += rStr.getLength();
2724                         }
2725                         else
2726                         {
2727                             return nPos;
2728                         }
2729                         break;
2730                     case '#':
2731                     case '?':
2732                         return nPos;
2733                     default:
2734                         nPos = nPos + sStrArray[i].getLength();
2735                         if (bTimePart)
2736                         {
2737                             if ( sStrArray[i] == sOldTimeSep )
2738                             {
2739                                 nTypeArray[i] = NF_SYMBOLTYPE_TIMESEP;
2740                                 if ( bConvertMode )
2741                                 {
2742                                     sStrArray[i] = pLoc->getTimeSep();
2743                                 }
2744                             }
2745                             else if ( sStrArray[i] == sOldTime100SecSep )
2746                             {
2747                                 bDecSep = true;
2748                                 nTypeArray[i] = NF_SYMBOLTYPE_TIME100SECSEP;
2749                                 if ( bConvertMode )
2750                                 {
2751                                     sStrArray[i] = pLoc->getTime100SecSep();
2752                                 }
2753                             }
2754                             else
2755                             {
2756                                 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2757                             }
2758                         }
2759                         else
2760                         {
2761                             if ( sStrArray[i] == sOldDateSep )
2762                             {
2763                                 nTypeArray[i] = NF_SYMBOLTYPE_DATESEP;
2764                                 if (bConvertMode)
2765                                     sStrArray[i] = pFormatter->GetDateSep();
2766                             }
2767                             else
2768                             {
2769                                 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2770                             }
2771                         }
2772                         i++;
2773                         break;
2774                     }
2775                 }
2776                 break;
2777             case NF_KEY_AMPM:                       // AM/PM
2778             case NF_KEY_AP:                         // A/P
2779                 bTimePart = true;
2780                 bExp = true;                        // Abuse for A/P
2781                 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2782                 nPos = nPos + sStrArray[i].getLength();
2783                 i++;
2784                 break;
2785             case NF_KEY_MI:                         // M
2786             case NF_KEY_MMI:                        // MM
2787             case NF_KEY_H:                          // H
2788             case NF_KEY_HH:                         // HH
2789             case NF_KEY_S:                          // S
2790             case NF_KEY_SS:                         // SS
2791                 bTimePart = true;
2792                 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2793                 nPos = nPos + sStrArray[i].getLength();
2794                 i++;
2795                 break;
2796             case NF_KEY_M:                          // M
2797             case NF_KEY_MM:                         // MM
2798             case NF_KEY_MMM:                        // MMM
2799             case NF_KEY_MMMM:                       // MMMM
2800             case NF_KEY_MMMMM:                      // MMMMM
2801             case NF_KEY_Q:                          // Q
2802             case NF_KEY_QQ:                         // QQ
2803             case NF_KEY_D:                          // D
2804             case NF_KEY_DD:                         // DD
2805             case NF_KEY_DDD:                        // DDD
2806             case NF_KEY_DDDD:                       // DDDD
2807             case NF_KEY_YY:                         // YY
2808             case NF_KEY_YYYY:                       // YYYY
2809             case NF_KEY_NN:                         // NN
2810             case NF_KEY_NNN:                        // NNN
2811             case NF_KEY_NNNN:                       // NNNN
2812             case NF_KEY_WW :                        // WW
2813             case NF_KEY_AAA :                       // AAA
2814             case NF_KEY_AAAA :                      // AAAA
2815             case NF_KEY_EC :                        // E
2816             case NF_KEY_EEC :                       // EE
2817             case NF_KEY_G :                         // G
2818             case NF_KEY_GG :                        // GG
2819             case NF_KEY_GGG :                       // GGG
2820             case NF_KEY_R :                         // R
2821             case NF_KEY_RR :                        // RR
2822                 bTimePart = false;
2823                 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2824                 nPos = nPos + sStrArray[i].getLength();
2825                 if (bNewDateOrder)
2826                 {
2827                     // For simple numeric date formats record date order and
2828                     // later rearrange.
2829                     switch (nTypeArray[i])
2830                     {
2831                         case NF_KEY_M:
2832                         case NF_KEY_MM:
2833                             if (nMonthPos == SAL_MAX_UINT16)
2834                                 nMonthPos = i;
2835                             else
2836                                 bNewDateOrder = false;
2837                         break;
2838                         case NF_KEY_D:
2839                         case NF_KEY_DD:
2840                             if (nDayPos == SAL_MAX_UINT16)
2841                                 nDayPos = i;
2842                             else
2843                                 bNewDateOrder = false;
2844                         break;
2845                         case NF_KEY_YY:
2846                         case NF_KEY_YYYY:
2847                             if (nYearPos == SAL_MAX_UINT16)
2848                                 nYearPos = i;
2849                             else
2850                                 bNewDateOrder = false;
2851                         break;
2852                         default:
2853                             ;   // nothing
2854                     }
2855                 }
2856                 i++;
2857                 break;
2858             case NF_KEY_THAI_T :
2859                 bThaiT = true;
2860                 sStrArray[i] = sKeyword[nTypeArray[i]];
2861                 nPos = nPos + sStrArray[i].getLength();
2862                 i++;
2863                 break;
2864             default: // Other keywords
2865                 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2866                 nPos = nPos + sStrArray[i].getLength();
2867                 i++;
2868                 break;
2869             }
2870         } // of while
2871         nCntPost = nCounter; // decimals (100th seconds)
2872         if (bExp)
2873         {
2874             nCntExp = 1; // Remembers AM/PM
2875         }
2876         break; // of SvNumFormatType::DATETIME
2877     default:
2878         break;
2879     }
2880     if (eScannedType == SvNumFormatType::SCIENTIFIC &&
2881         (nCntPre + nCntPost == 0 || nCntExp == 0))
2882     {
2883         return nPos;
2884     }
2885     else if (eScannedType == SvNumFormatType::FRACTION && (nCntExp > 8 || nCntExp == 0))
2886     {
2887         return nPos;
2888     }
2889     if (bThaiT && !GetNatNumModifier())
2890     {
2891         SetNatNumModifier(1);
2892     }
2893     if ( bConvertMode )
2894     {
2895         if (bNewDateOrder && sOldDateSep == "-")
2896         {
2897             // Keep ISO formats Y-M-D, Y-M and M-D
2898             if (IsDateFragment( nYearPos, nMonthPos))
2899             {
2900                 nTypeArray[nYearPos+1] = NF_SYMBOLTYPE_STRING;
2901                 sStrArray[nYearPos+1] = sOldDateSep;
2902                 bNewDateOrder = false;
2903             }
2904             if (IsDateFragment( nMonthPos, nDayPos))
2905             {
2906                 nTypeArray[nMonthPos+1] = NF_SYMBOLTYPE_STRING;
2907                 sStrArray[nMonthPos+1] = sOldDateSep;
2908                 bNewDateOrder = false;
2909             }
2910         }
2911         if (bNewDateOrder)
2912         {
2913             // Rearrange date order to the target locale if the original order
2914             // includes date separators and is adjacent.
2915             /* TODO: for incomplete dates trailing separators need to be
2916              * handled according to the locale's usage, e.g. en-US M/D should
2917              * be converted to de-DE D.M. and vice versa. As is, it's
2918              * M/D -> D.M and D.M. -> M/D/ where specifically the latter looks
2919              * odd. Check accepted date patterns and append/remove? */
2920             switch (eOldDateOrder)
2921             {
2922                 case DateOrder::DMY:
2923                     switch (pLoc->getDateOrder())
2924                     {
2925                         case DateOrder::MDY:
2926                             // Convert only if the actual format is not of YDM
2927                             // order (which would be a completely unusual order
2928                             // anyway, but..), e.g. YYYY.DD.MM not to
2929                             // YYYY/MM/DD
2930                             if (IsDateFragment( nDayPos, nMonthPos) && !IsDateFragment( nYearPos, nDayPos))
2931                                 SwapArrayElements( nDayPos, nMonthPos);
2932                         break;
2933                         case DateOrder::YMD:
2934                             if (nYearPos != SAL_MAX_UINT16)
2935                             {
2936                                 if (IsDateFragment( nDayPos, nMonthPos) && IsDateFragment( nMonthPos, nYearPos))
2937                                     SwapArrayElements( nDayPos, nYearPos);
2938                             }
2939                             else
2940                             {
2941                                 if (IsDateFragment( nDayPos, nMonthPos))
2942                                     SwapArrayElements( nDayPos, nMonthPos);
2943                             }
2944                         break;
2945                         default:
2946                             ;   // nothing
2947                     }
2948                 break;
2949                 case DateOrder::MDY:
2950                     switch (pLoc->getDateOrder())
2951                     {
2952                         case DateOrder::DMY:
2953                             // Convert only if the actual format is not of YMD
2954                             // order, e.g. YYYY/MM/DD not to YYYY.DD.MM
2955                             /* TODO: convert such to DD.MM.YYYY instead? */
2956                             if (IsDateFragment( nMonthPos, nDayPos) && !IsDateFragment( nYearPos, nMonthPos))
2957                                 SwapArrayElements( nMonthPos, nDayPos);
2958                         break;
2959                         case DateOrder::YMD:
2960                             if (nYearPos != SAL_MAX_UINT16)
2961                             {
2962                                 if (IsDateFragment( nMonthPos, nDayPos) && IsDateFragment( nDayPos, nYearPos))
2963                                 {
2964                                     SwapArrayElements( nYearPos, nMonthPos);    // YDM
2965                                     SwapArrayElements( nYearPos, nDayPos);      // YMD
2966                                 }
2967                             }
2968                         break;
2969                         default:
2970                             ;   // nothing
2971                     }
2972                 break;
2973                 case DateOrder::YMD:
2974                     switch (pLoc->getDateOrder())
2975                     {
2976                         case DateOrder::DMY:
2977                             if (nYearPos != SAL_MAX_UINT16)
2978                             {
2979                                 if (IsDateFragment( nYearPos, nMonthPos) && IsDateFragment( nMonthPos, nDayPos))
2980                                     SwapArrayElements( nYearPos, nDayPos);
2981                             }
2982                             else
2983                             {
2984                                 if (IsDateFragment( nMonthPos, nDayPos))
2985                                     SwapArrayElements( nMonthPos, nDayPos);
2986                             }
2987                         break;
2988                         case DateOrder::MDY:
2989                             if (nYearPos != SAL_MAX_UINT16)
2990                             {
2991                                 if (IsDateFragment( nYearPos, nMonthPos) && IsDateFragment( nMonthPos, nDayPos))
2992                                 {
2993                                     SwapArrayElements( nYearPos, nDayPos);      // DMY
2994                                     SwapArrayElements( nYearPos, nMonthPos);    // MDY
2995                                 }
2996                             }
2997                         break;
2998                         default:
2999                             ;   // nothing
3000                     }
3001                 break;
3002                 default:
3003                     ;   // nothing
3004             }
3005         }
3006         // strings containing keywords of the target locale must be quoted, so
3007         // the user sees the difference and is able to edit the format string
3008         for ( i=0; i < nStringsCnt; i++ )
3009         {
3010             if ( nTypeArray[i] == NF_SYMBOLTYPE_STRING &&
3011                  sStrArray[i][0] != '\"' )
3012             {
3013                 if ( bConvertSystemToSystem && eScannedType == SvNumFormatType::CURRENCY )
3014                 {
3015                     // don't stringize automatic currency, will be converted
3016                     if ( sStrArray[i] == sOldCurSymbol )
3017                     {
3018                         continue; // for
3019                     }
3020                     // DM might be split into D and M
3021                     if ( sStrArray[i].getLength() < sOldCurSymbol.getLength() &&
3022                          pChrCls->uppercase( sStrArray[i], 0, 1 )[0] ==
3023                          sOldCurString[0] )
3024                     {
3025                         OUString aTmp( sStrArray[i] );
3026                         sal_uInt16 j = i + 1;
3027                         while ( aTmp.getLength() < sOldCurSymbol.getLength() &&
3028                                 j < nStringsCnt &&
3029                                 nTypeArray[j] == NF_SYMBOLTYPE_STRING )
3030                         {
3031                             aTmp += sStrArray[j++];
3032                         }
3033                         if ( pChrCls->uppercase( aTmp ) == sOldCurString )
3034                         {
3035                             sStrArray[i++] = aTmp;
3036                             for ( ; i<j; i++ )
3037                             {
3038                                 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
3039                                 nResultStringsCnt--;
3040                             }
3041                             i = j - 1;
3042                             continue; // for
3043                         }
3044                     }
3045                 }
3046                 OUString& rStr = sStrArray[i];
3047                 sal_Int32 nLen = rStr.getLength();
3048                 for ( sal_Int32 j = 0; j < nLen; j++ )
3049                 {
3050                     bool bFoundEnglish = false;
3051                     if ( (j == 0 || rStr[j - 1] != '\\') && GetKeyWord( rStr, j, bFoundEnglish) )
3052                     {
3053                         rStr = "\"" + rStr + "\"";
3054                         break; // for
3055                     }
3056                 }
3057             }
3058         }
3059     }
3060     // Concatenate strings, remove quotes for output, and rebuild the format string
3061     rString.clear();
3062     i = 0;
3063     while (i < nStringsCnt)
3064     {
3065         sal_Int32 nStringPos;
3066         sal_Int32 nArrPos = 0;
3067         sal_uInt16 iPos = i;
3068         switch ( nTypeArray[i] )
3069         {
3070         case NF_SYMBOLTYPE_STRING :
3071         case NF_SYMBOLTYPE_FRACBLANK :
3072             nStringPos = rString.getLength();
3073             do
3074             {
3075                 if (sStrArray[i].getLength() == 2 &&
3076                     sStrArray[i][0] == '\\')
3077                 {
3078                     // Unescape some simple forms of symbols even in the UI
3079                     // visible string to prevent duplicates that differ
3080                     // only in notation, originating from import.
3081                     // e.g. YYYY-MM-DD and YYYY\-MM\-DD are identical,
3082                     // but 0\ 000 0 and 0 000 0 in a French locale are not.
3083 
3084                     sal_Unicode c = sStrArray[i][1];
3085 
3086                     switch (c)
3087                     {
3088                     case '+':
3089                     case '-':
3090                         rString += OUStringChar(c);
3091                         break;
3092                     case ' ':
3093                     case '.':
3094                     case '/':
3095                         if (!(eScannedType & SvNumFormatType::DATE) &&
3096                             (StringEqualsChar( pFormatter->GetNumThousandSep(), c) ||
3097                              StringEqualsChar( pFormatter->GetNumDecimalSep(), c) ||
3098                              (c == ' ' &&
3099                               (StringEqualsChar( pFormatter->GetNumThousandSep(), cNoBreakSpace) ||
3100                                StringEqualsChar( pFormatter->GetNumThousandSep(), cNarrowNoBreakSpace)))))
3101                         {
3102                             rString += sStrArray[i];
3103                         }
3104                         else if ((eScannedType & SvNumFormatType::DATE) &&
3105                                  StringEqualsChar( pFormatter->GetDateSep(), c))
3106                         {
3107                             rString += sStrArray[i];
3108                         }
3109                         else if ((eScannedType & SvNumFormatType::TIME) &&
3110                                  (StringEqualsChar( pLoc->getTimeSep(), c) ||
3111                                   StringEqualsChar( pLoc->getTime100SecSep(), c)))
3112                         {
3113                             rString += sStrArray[i];
3114                         }
3115                         else if (eScannedType & SvNumFormatType::FRACTION)
3116                         {
3117                             rString += sStrArray[i];
3118                         }
3119                         else
3120                         {
3121                             rString += OUStringChar(c);
3122                         }
3123                         break;
3124                     default:
3125                         rString += sStrArray[i];
3126                     }
3127                 }
3128                 else
3129                 {
3130                     rString += sStrArray[i];
3131                 }
3132                 if ( RemoveQuotes( sStrArray[i] ) > 0 )
3133                 {
3134                     // update currency up to quoted string
3135                     if ( eScannedType == SvNumFormatType::CURRENCY )
3136                     {
3137                         // dM -> DM  or  DM -> $  in old automatic
3138                         // currency formats, oh my ..., why did we ever introduce them?
3139                         OUString aTmp( pChrCls->uppercase( sStrArray[iPos], nArrPos,
3140                                                            sStrArray[iPos].getLength()-nArrPos ) );
3141                         sal_Int32 nCPos = aTmp.indexOf( sOldCurString );
3142                         if ( nCPos >= 0 )
3143                         {
3144                             const OUString& rCur = bConvertMode && bConvertSystemToSystem ?
3145                                 GetCurSymbol() : sOldCurSymbol;
3146                             sStrArray[iPos] = sStrArray[iPos].replaceAt( nArrPos + nCPos,
3147                                                                          sOldCurString.getLength(),
3148                                                                          rCur );
3149                             rString = rString.replaceAt( nStringPos + nCPos,
3150                                                          sOldCurString.getLength(),
3151                                                          rCur );
3152                         }
3153                         nStringPos = rString.getLength();
3154                         if ( iPos == i )
3155                         {
3156                             nArrPos = sStrArray[iPos].getLength();
3157                         }
3158                         else
3159                         {
3160                             nArrPos = sStrArray[iPos].getLength() + sStrArray[i].getLength();
3161                         }
3162                     }
3163                 }
3164                 if ( iPos != i )
3165                 {
3166                     sStrArray[iPos] += sStrArray[i];
3167                     nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
3168                     nResultStringsCnt--;
3169                 }
3170                 i++;
3171             }
3172             while ( i < nStringsCnt && nTypeArray[i] == NF_SYMBOLTYPE_STRING );
3173 
3174             if ( i < nStringsCnt )
3175             {
3176                 i--; // enter switch on next symbol again
3177             }
3178             if ( eScannedType == SvNumFormatType::CURRENCY && nStringPos < rString.getLength() )
3179             {
3180                 // same as above, since last RemoveQuotes
3181                 OUString aTmp( pChrCls->uppercase( sStrArray[iPos], nArrPos,
3182                                                    sStrArray[iPos].getLength()-nArrPos ) );
3183                 sal_Int32 nCPos = aTmp.indexOf( sOldCurString );
3184                 if ( nCPos >= 0 )
3185                 {
3186                     const OUString& rCur = bConvertMode && bConvertSystemToSystem ?
3187                         GetCurSymbol() : sOldCurSymbol;
3188                     sStrArray[iPos] = sStrArray[iPos].replaceAt( nArrPos + nCPos,
3189                                                                  sOldCurString.getLength(),
3190                                                                  rCur );
3191                     rString = rString.replaceAt( nStringPos + nCPos,
3192                                                  sOldCurString.getLength(), rCur );
3193                 }
3194             }
3195             break;
3196         case NF_SYMBOLTYPE_CURRENCY :
3197             rString += sStrArray[i];
3198             RemoveQuotes( sStrArray[i] );
3199             break;
3200         case NF_KEY_THAI_T:
3201             if (bThaiT && GetNatNumModifier() == 1)
3202             {
3203                 // Remove T from format code, will be replaced with a [NatNum1] prefix.
3204                 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
3205                 nResultStringsCnt--;
3206             }
3207             else
3208             {
3209                 rString += sStrArray[i];
3210             }
3211             break;
3212         case NF_SYMBOLTYPE_EMPTY :
3213             // nothing
3214             break;
3215         default:
3216             rString += sStrArray[i];
3217         }
3218         i++;
3219     }
3220     return 0;
3221 }
3222 
RemoveQuotes(OUString & rStr)3223 sal_Int32 ImpSvNumberformatScan::RemoveQuotes( OUString& rStr )
3224 {
3225     if ( rStr.getLength() > 1 )
3226     {
3227         sal_Unicode c = rStr[0];
3228         sal_Int32 n = rStr.getLength() - 1;
3229         if ( c == '"' && rStr[n] == '"' )
3230         {
3231             rStr = rStr.copy( 1, n-1);
3232             return 2;
3233         }
3234         else if ( c == '\\' )
3235         {
3236             rStr = rStr.copy(1);
3237             return 1;
3238         }
3239     }
3240     return 0;
3241 }
3242 
ScanFormat(OUString & rString)3243 sal_Int32 ImpSvNumberformatScan::ScanFormat( OUString& rString )
3244 {
3245     sal_Int32 res = Symbol_Division(rString); // Lexical analysis
3246     if (!res)
3247     {
3248         res = ScanType(); // Recognizing the Format type
3249     }
3250     if (!res)
3251     {
3252         res = FinalScan( rString ); // Type dependent final analysis
3253     }
3254     return res; // res = control position; res = 0 => Format ok
3255 }
3256 
CopyInfo(ImpSvNumberformatInfo * pInfo,sal_uInt16 nCnt)3257 void ImpSvNumberformatScan::CopyInfo(ImpSvNumberformatInfo* pInfo, sal_uInt16 nCnt)
3258 {
3259     size_t i,j;
3260     j = 0;
3261     i = 0;
3262     while (i < nCnt && j < NF_MAX_FORMAT_SYMBOLS)
3263     {
3264         if (nTypeArray[j] != NF_SYMBOLTYPE_EMPTY)
3265         {
3266             pInfo->sStrArray[i]  = sStrArray[j];
3267             pInfo->nTypeArray[i] = nTypeArray[j];
3268             i++;
3269         }
3270         j++;
3271     }
3272     pInfo->eScannedType = eScannedType;
3273     pInfo->bThousand    = bThousand;
3274     pInfo->nThousand    = nThousand;
3275     pInfo->nCntPre      = nCntPre;
3276     pInfo->nCntPost     = nCntPost;
3277     pInfo->nCntExp      = nCntExp;
3278 }
3279 
ReplaceBooleanEquivalent(OUString & rString)3280 bool ImpSvNumberformatScan::ReplaceBooleanEquivalent( OUString& rString )
3281 {
3282     InitKeywords();
3283     /* TODO: compare case insensitive? Or rather leave as is and case not
3284      * matching indicates user supplied on purpose? Written to file / generated
3285      * was always uppercase. */
3286     if (rString == sBooleanEquivalent1 || rString == sBooleanEquivalent2)
3287     {
3288         rString = GetKeywords()[NF_KEY_BOOLEAN];
3289         return true;
3290     }
3291     return false;
3292 }
3293 
3294 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3295