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