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