nss: upgrade to release 3.73
[LibreOffice.git] / svl / source / numbers / zforscan.cxx
blob64875df1bfda1109a22c43eb4cda5fae4ca1ba13
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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 #include <com/sun/star/i18n/XNumberFormatCode.hpp>
32 #include <svl/zforlist.hxx>
33 #include <svl/zformat.hxx>
34 #include <unotools/digitgroupingiterator.hxx>
36 #include "zforscan.hxx"
38 #include <svl/nfsymbol.hxx>
39 using namespace svt;
41 const sal_Unicode cNoBreakSpace = 0xA0;
42 const sal_Unicode cNarrowNoBreakSpace = 0x202F;
44 const int MaxCntPost = 20; //max dec places allow by rtl_math_round
46 const NfKeywordTable ImpSvNumberformatScan::sEnglishKeyword =
47 { // Syntax keywords in English (USA)
48 //! All keywords MUST be UPPERCASE! In same order as NfKeywordIndex
49 "", // NF_KEY_NONE 0
50 "E", // NF_KEY_E Exponent
51 "AM/PM", // NF_KEY_AMPM AM/PM
52 "A/P", // NF_KEY_AP AM/PM short
53 "M", // NF_KEY_MI Minute
54 "MM", // NF_KEY_MMI Minute 02
55 "M", // NF_KEY_M month (!)
56 "MM", // NF_KEY_MM month 02 (!)
57 "MMM", // NF_KEY_MMM month short name
58 "MMMM", // NF_KEY_MMMM month long name
59 "MMMMM", // NF_KEY_MMMMM first letter of month name
60 "H", // NF_KEY_H hour
61 "HH", // NF_KEY_HH hour 02
62 "S", // NF_KEY_S Second
63 "SS", // NF_KEY_SS Second 02
64 "Q", // NF_KEY_Q Quarter short 'Q'
65 "QQ", // NF_KEY_QQ Quarter long
66 "D", // NF_KEY_D day of month
67 "DD", // NF_KEY_DD day of month 02
68 "DDD", // NF_KEY_DDD day of week short
69 "DDDD", // NF_KEY_DDDD day of week long
70 "YY", // NF_KEY_YY year two digits
71 "YYYY", // NF_KEY_YYYY year four digits
72 "NN", // NF_KEY_NN Day of week short
73 "NNN", // NF_KEY_NNN Day of week long
74 "NNNN", // NF_KEY_NNNN Day of week long incl. separator
75 "AAA", // NF_KEY_AAA
76 "AAAA", // NF_KEY_AAAA
77 "E", // NF_KEY_EC
78 "EE", // NF_KEY_EEC
79 "G", // NF_KEY_G
80 "GG", // NF_KEY_GG
81 "GGG", // NF_KEY_GGG
82 "R", // NF_KEY_R
83 "RR", // NF_KEY_RR
84 "WW", // NF_KEY_WW Week of year
85 "t", // NF_KEY_THAI_T Thai T modifier, speciality of Thai Excel, only
86 // used with Thai locale and converted to [NatNum1], only
87 // exception as lowercase
88 "CCC", // NF_KEY_CCC Currency abbreviation
89 "GENERAL", // NF_KEY_GENERAL General / Standard
91 // Reserved words translated and color names follow:
92 "TRUE", // NF_KEY_TRUE boolean true
93 "FALSE", // NF_KEY_FALSE boolean false
94 "BOOLEAN", // NF_KEY_BOOLEAN boolean
95 "COLOR", // NF_KEY_COLOR color
96 // colours
97 "BLACK", // NF_KEY_BLACK
98 "BLUE", // NF_KEY_BLUE
99 "GREEN", // NF_KEY_GREEN
100 "CYAN", // NF_KEY_CYAN
101 "RED", // NF_KEY_RED
102 "MAGENTA", // NF_KEY_MAGENTA
103 "BROWN", // NF_KEY_BROWN
104 "GREY", // NF_KEY_GREY
105 "YELLOW", // NF_KEY_YELLOW
106 "WHITE" // NF_KEY_WHITE
109 const ::std::vector<Color> ImpSvNumberformatScan::StandardColor{
110 COL_BLACK, COL_LIGHTBLUE, COL_LIGHTGREEN, COL_LIGHTCYAN, COL_LIGHTRED,
111 COL_LIGHTMAGENTA, COL_BROWN, COL_GRAY, COL_YELLOW, COL_WHITE
114 // This vector will hold *only* the color names in German language.
115 static const std::u16string_view& GermanColorName(size_t i)
117 static const std::u16string_view sGermanColorNames[]{ u"FARBE", u"SCHWARZ", u"BLAU",
118 u"GRÜN", u"CYAN", u"ROT",
119 u"MAGENTA", u"BRAUN", u"GRAU",
120 u"GELB", u"WEISS" };
121 assert(i < SAL_N_ELEMENTS(sGermanColorNames));
122 return sGermanColorNames[i];
125 ImpSvNumberformatScan::ImpSvNumberformatScan( SvNumberFormatter* pFormatterP )
126 : maNullDate( 30, 12, 1899)
127 , eNewLnge(LANGUAGE_DONTKNOW)
128 , eTmpLnge(LANGUAGE_DONTKNOW)
129 , nCurrPos(-1)
130 , meKeywordLocalization(KeywordLocalization::AllowEnglish)
132 pFormatter = pFormatterP;
133 xNFC = css::i18n::NumberFormatMapper::create( pFormatter->GetComponentContext() );
134 bConvertMode = false;
135 mbConvertDateOrder = false;
136 bConvertSystemToSystem = false;
137 bKeywordsNeedInit = true; // locale dependent and not locale dependent keywords
138 bCompatCurNeedInit = true; // locale dependent compatibility currency strings
140 static_assert( NF_KEY_BLACK - NF_KEY_COLOR == 1, "bad FARBE(COLOR), SCHWARZ(BLACK) sequence");
141 static_assert( NF_KEY_FIRSTCOLOR - NF_KEY_COLOR == 1, "bad color sequence");
142 static_assert( NF_MAX_DEFAULT_COLORS + 1 == 11, "bad color count");
143 static_assert( NF_KEY_WHITE - NF_KEY_COLOR + 1 == 11, "bad color sequence count");
145 nStandardPrec = 2;
147 Reset();
150 ImpSvNumberformatScan::~ImpSvNumberformatScan()
152 Reset();
155 void ImpSvNumberformatScan::ChangeIntl( KeywordLocalization eKeywordLocalization )
157 meKeywordLocalization = eKeywordLocalization;
158 bKeywordsNeedInit = true;
159 bCompatCurNeedInit = true;
160 // may be initialized by InitSpecialKeyword()
161 sKeyword[NF_KEY_TRUE].clear();
162 sKeyword[NF_KEY_FALSE].clear();
165 void ImpSvNumberformatScan::InitSpecialKeyword( NfKeywordIndex eIdx ) const
167 switch ( eIdx )
169 case NF_KEY_TRUE :
170 const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_TRUE] =
171 pFormatter->GetCharClass()->uppercase( pFormatter->GetLocaleData()->getTrueWord() );
172 if ( sKeyword[NF_KEY_TRUE].isEmpty() )
174 SAL_WARN( "svl.numbers", "InitSpecialKeyword: TRUE_WORD?" );
175 const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_TRUE] = sEnglishKeyword[NF_KEY_TRUE];
177 break;
178 case NF_KEY_FALSE :
179 const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_FALSE] =
180 pFormatter->GetCharClass()->uppercase( pFormatter->GetLocaleData()->getFalseWord() );
181 if ( sKeyword[NF_KEY_FALSE].isEmpty() )
183 SAL_WARN( "svl.numbers", "InitSpecialKeyword: FALSE_WORD?" );
184 const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_FALSE] = sEnglishKeyword[NF_KEY_FALSE];
186 break;
187 default:
188 SAL_WARN( "svl.numbers", "InitSpecialKeyword: unknown request" );
192 void ImpSvNumberformatScan::InitCompatCur() const
194 ImpSvNumberformatScan* pThis = const_cast<ImpSvNumberformatScan*>(this);
195 // currency symbol for old style ("automatic") compatibility format codes
196 pFormatter->GetCompatibilityCurrency( pThis->sCurSymbol, pThis->sCurAbbrev );
197 // currency symbol upper case
198 pThis->sCurString = pFormatter->GetCharClass()->uppercase( sCurSymbol );
199 bCompatCurNeedInit = false;
202 void ImpSvNumberformatScan::InitKeywords() const
204 if ( !bKeywordsNeedInit )
205 return ;
206 const_cast<ImpSvNumberformatScan*>(this)->SetDependentKeywords();
207 bKeywordsNeedInit = false;
210 /** Extract the name of General, Standard, Whatever, ignoring leading modifiers
211 such as [NatNum1]. */
212 static OUString lcl_extractStandardGeneralName( const OUString & rCode )
214 OUString aStr;
215 const sal_Unicode* p = rCode.getStr();
216 const sal_Unicode* const pStop = p + rCode.getLength();
217 const sal_Unicode* pBeg = p; // name begins here
218 bool bMod = false;
219 bool bDone = false;
220 while (p < pStop && !bDone)
222 switch (*p)
224 case '[':
225 bMod = true;
226 break;
227 case ']':
228 if (bMod)
230 bMod = false;
231 pBeg = p+1;
233 // else: would be a locale data error, easily to be spotted in
234 // UI dialog
235 break;
236 case ';':
237 if (!bMod)
239 bDone = true;
240 --p; // put back, increment by one follows
242 break;
244 ++p;
245 if (bMod)
247 pBeg = p;
250 if (pBeg < p)
252 aStr = rCode.copy( pBeg - rCode.getStr(), p - pBeg);
254 return aStr;
257 void ImpSvNumberformatScan::SetDependentKeywords()
259 using namespace ::com::sun::star;
260 using namespace ::com::sun::star::uno;
262 const CharClass* pCharClass = pFormatter->GetCharClass();
263 const LocaleDataWrapper* pLocaleData = pFormatter->GetLocaleData();
264 // #80023# be sure to generate keywords for the loaded Locale, not for the
265 // requested Locale, otherwise number format codes might not match
266 const LanguageTag& rLoadedLocale = pLocaleData->getLoadedLanguageTag();
267 LanguageType eLang = rLoadedLocale.getLanguageType( false);
269 bool bL10n = (meKeywordLocalization != KeywordLocalization::EnglishOnly);
270 if (bL10n)
272 // Check if this actually is a locale that uses any localized keywords,
273 // if not then disable localized keywords completely.
274 if ( !eLang.anyOf( LANGUAGE_GERMAN,
275 LANGUAGE_GERMAN_SWISS,
276 LANGUAGE_GERMAN_AUSTRIAN,
277 LANGUAGE_GERMAN_LUXEMBOURG,
278 LANGUAGE_GERMAN_LIECHTENSTEIN,
279 LANGUAGE_DUTCH,
280 LANGUAGE_DUTCH_BELGIAN,
281 LANGUAGE_FRENCH,
282 LANGUAGE_FRENCH_BELGIAN,
283 LANGUAGE_FRENCH_CANADIAN,
284 LANGUAGE_FRENCH_SWISS,
285 LANGUAGE_FRENCH_LUXEMBOURG,
286 LANGUAGE_FRENCH_MONACO,
287 LANGUAGE_FINNISH,
288 LANGUAGE_ITALIAN,
289 LANGUAGE_ITALIAN_SWISS,
290 LANGUAGE_DANISH,
291 LANGUAGE_NORWEGIAN,
292 LANGUAGE_NORWEGIAN_BOKMAL,
293 LANGUAGE_NORWEGIAN_NYNORSK,
294 LANGUAGE_SWEDISH,
295 LANGUAGE_SWEDISH_FINLAND,
296 LANGUAGE_PORTUGUESE,
297 LANGUAGE_PORTUGUESE_BRAZILIAN,
298 LANGUAGE_SPANISH_MODERN,
299 LANGUAGE_SPANISH_DATED,
300 LANGUAGE_SPANISH_MEXICAN,
301 LANGUAGE_SPANISH_GUATEMALA,
302 LANGUAGE_SPANISH_COSTARICA,
303 LANGUAGE_SPANISH_PANAMA,
304 LANGUAGE_SPANISH_DOMINICAN_REPUBLIC,
305 LANGUAGE_SPANISH_VENEZUELA,
306 LANGUAGE_SPANISH_COLOMBIA,
307 LANGUAGE_SPANISH_PERU,
308 LANGUAGE_SPANISH_ARGENTINA,
309 LANGUAGE_SPANISH_ECUADOR,
310 LANGUAGE_SPANISH_CHILE,
311 LANGUAGE_SPANISH_URUGUAY,
312 LANGUAGE_SPANISH_PARAGUAY,
313 LANGUAGE_SPANISH_BOLIVIA,
314 LANGUAGE_SPANISH_EL_SALVADOR,
315 LANGUAGE_SPANISH_HONDURAS,
316 LANGUAGE_SPANISH_NICARAGUA,
317 LANGUAGE_SPANISH_PUERTO_RICO ))
319 bL10n = false;
320 meKeywordLocalization = KeywordLocalization::EnglishOnly;
324 // Init the current NfKeywordTable with English keywords.
325 sKeyword = sEnglishKeyword;
327 // Set the uppercase localized General name, e.g. Standard -> STANDARD
328 i18n::NumberFormatCode aFormat = xNFC->getFormatCode( NF_NUMBER_STANDARD, rLoadedLocale.getLocale() );
329 sNameStandardFormat = lcl_extractStandardGeneralName( aFormat.Code );
330 sKeyword[NF_KEY_GENERAL] = pCharClass->uppercase( sNameStandardFormat );
332 // Thai T NatNum special. Other locale's small letter 't' results in upper
333 // case comparison not matching but length does in conversion mode. Ugly.
334 if (eLang == LANGUAGE_THAI)
336 sKeyword[NF_KEY_THAI_T] = "T";
338 else
340 sKeyword[NF_KEY_THAI_T] = sEnglishKeyword[NF_KEY_THAI_T];
343 // boolean keywords
344 InitSpecialKeyword( NF_KEY_TRUE );
345 InitSpecialKeyword( NF_KEY_FALSE );
347 // Boolean equivalent format codes that are written to Excel files, may
348 // have been written to ODF as well, specifically if such loaded Excel file
349 // was saved as ODF, and shall result in proper Boolean again.
350 // "TRUE";"TRUE";"FALSE"
351 sBooleanEquivalent1 = "\"" + sKeyword[NF_KEY_TRUE] + "\";\"" +
352 sKeyword[NF_KEY_TRUE] + "\";\"" + sKeyword[NF_KEY_FALSE] + "\"";
353 // [>0]"TRUE";[<0]"TRUE";"FALSE"
354 sBooleanEquivalent2 = "[>0]\"" + sKeyword[NF_KEY_TRUE] + "\";[<0]\"" +
355 sKeyword[NF_KEY_TRUE] + "\";\"" + sKeyword[NF_KEY_FALSE] + "\"";
357 // compatibility currency strings
358 InitCompatCur();
360 if (!bL10n)
361 return;
363 // All locale dependent keywords overrides follow.
365 if ( eLang.anyOf(
366 LANGUAGE_GERMAN,
367 LANGUAGE_GERMAN_SWISS,
368 LANGUAGE_GERMAN_AUSTRIAN,
369 LANGUAGE_GERMAN_LUXEMBOURG,
370 LANGUAGE_GERMAN_LIECHTENSTEIN))
372 //! all capital letters
373 sKeyword[NF_KEY_M] = "M"; // month 1
374 sKeyword[NF_KEY_MM] = "MM"; // month 01
375 sKeyword[NF_KEY_MMM] = "MMM"; // month Jan
376 sKeyword[NF_KEY_MMMM] = "MMMM"; // month Januar
377 sKeyword[NF_KEY_MMMMM] = "MMMMM"; // month J
378 sKeyword[NF_KEY_H] = "H"; // hour 2
379 sKeyword[NF_KEY_HH] = "HH"; // hour 02
380 sKeyword[NF_KEY_D] = "T";
381 sKeyword[NF_KEY_DD] = "TT";
382 sKeyword[NF_KEY_DDD] = "TTT";
383 sKeyword[NF_KEY_DDDD] = "TTTT";
384 sKeyword[NF_KEY_YY] = "JJ";
385 sKeyword[NF_KEY_YYYY] = "JJJJ";
386 sKeyword[NF_KEY_BOOLEAN] = "LOGISCH";
387 sKeyword[NF_KEY_COLOR] = GermanColorName(NF_KEY_COLOR - NF_KEY_COLOR);
388 sKeyword[NF_KEY_BLACK] = GermanColorName(NF_KEY_BLACK - NF_KEY_COLOR);
389 sKeyword[NF_KEY_BLUE] = GermanColorName(NF_KEY_BLUE - NF_KEY_COLOR);
390 sKeyword[NF_KEY_GREEN] = GermanColorName(NF_KEY_GREEN - NF_KEY_COLOR);
391 sKeyword[NF_KEY_CYAN] = GermanColorName(NF_KEY_CYAN - NF_KEY_COLOR);
392 sKeyword[NF_KEY_RED] = GermanColorName(NF_KEY_RED - NF_KEY_COLOR);
393 sKeyword[NF_KEY_MAGENTA] = GermanColorName(NF_KEY_MAGENTA - NF_KEY_COLOR);
394 sKeyword[NF_KEY_BROWN] = GermanColorName(NF_KEY_BROWN - NF_KEY_COLOR);
395 sKeyword[NF_KEY_GREY] = GermanColorName(NF_KEY_GREY - NF_KEY_COLOR);
396 sKeyword[NF_KEY_YELLOW] = GermanColorName(NF_KEY_YELLOW - NF_KEY_COLOR);
397 sKeyword[NF_KEY_WHITE] = GermanColorName(NF_KEY_WHITE - NF_KEY_COLOR);
399 else
401 // day
402 if ( eLang.anyOf(
403 LANGUAGE_ITALIAN,
404 LANGUAGE_ITALIAN_SWISS))
406 sKeyword[NF_KEY_D] = "G";
407 sKeyword[NF_KEY_DD] = "GG";
408 sKeyword[NF_KEY_DDD] = "GGG";
409 sKeyword[NF_KEY_DDDD] = "GGGG";
410 // must exchange the era code, same as Xcl
411 sKeyword[NF_KEY_G] = "X";
412 sKeyword[NF_KEY_GG] = "XX";
413 sKeyword[NF_KEY_GGG] = "XXX";
415 else if ( eLang.anyOf(
416 LANGUAGE_FRENCH,
417 LANGUAGE_FRENCH_BELGIAN,
418 LANGUAGE_FRENCH_CANADIAN,
419 LANGUAGE_FRENCH_SWISS,
420 LANGUAGE_FRENCH_LUXEMBOURG,
421 LANGUAGE_FRENCH_MONACO))
423 sKeyword[NF_KEY_D] = "J";
424 sKeyword[NF_KEY_DD] = "JJ";
425 sKeyword[NF_KEY_DDD] = "JJJ";
426 sKeyword[NF_KEY_DDDD] = "JJJJ";
428 else if ( eLang == LANGUAGE_FINNISH )
430 sKeyword[NF_KEY_D] = "P";
431 sKeyword[NF_KEY_DD] = "PP";
432 sKeyword[NF_KEY_DDD] = "PPP";
433 sKeyword[NF_KEY_DDDD] = "PPPP";
436 // month
437 if ( eLang == LANGUAGE_FINNISH )
439 sKeyword[NF_KEY_M] = "K";
440 sKeyword[NF_KEY_MM] = "KK";
441 sKeyword[NF_KEY_MMM] = "KKK";
442 sKeyword[NF_KEY_MMMM] = "KKKK";
443 sKeyword[NF_KEY_MMMMM] = "KKKKK";
446 // year
447 if ( eLang.anyOf(
448 LANGUAGE_ITALIAN,
449 LANGUAGE_ITALIAN_SWISS,
450 LANGUAGE_FRENCH,
451 LANGUAGE_FRENCH_BELGIAN,
452 LANGUAGE_FRENCH_CANADIAN,
453 LANGUAGE_FRENCH_SWISS,
454 LANGUAGE_FRENCH_LUXEMBOURG,
455 LANGUAGE_FRENCH_MONACO,
456 LANGUAGE_PORTUGUESE,
457 LANGUAGE_PORTUGUESE_BRAZILIAN,
458 LANGUAGE_SPANISH_MODERN,
459 LANGUAGE_SPANISH_DATED,
460 LANGUAGE_SPANISH_MEXICAN,
461 LANGUAGE_SPANISH_GUATEMALA,
462 LANGUAGE_SPANISH_COSTARICA,
463 LANGUAGE_SPANISH_PANAMA,
464 LANGUAGE_SPANISH_DOMINICAN_REPUBLIC,
465 LANGUAGE_SPANISH_VENEZUELA,
466 LANGUAGE_SPANISH_COLOMBIA,
467 LANGUAGE_SPANISH_PERU,
468 LANGUAGE_SPANISH_ARGENTINA,
469 LANGUAGE_SPANISH_ECUADOR,
470 LANGUAGE_SPANISH_CHILE,
471 LANGUAGE_SPANISH_URUGUAY,
472 LANGUAGE_SPANISH_PARAGUAY,
473 LANGUAGE_SPANISH_BOLIVIA,
474 LANGUAGE_SPANISH_EL_SALVADOR,
475 LANGUAGE_SPANISH_HONDURAS,
476 LANGUAGE_SPANISH_NICARAGUA,
477 LANGUAGE_SPANISH_PUERTO_RICO))
479 sKeyword[NF_KEY_YY] = "AA";
480 sKeyword[NF_KEY_YYYY] = "AAAA";
481 // must exchange the day of week name code, same as Xcl
482 sKeyword[NF_KEY_AAA] = "OOO";
483 sKeyword[NF_KEY_AAAA] = "OOOO";
485 else if ( eLang.anyOf(
486 LANGUAGE_DUTCH,
487 LANGUAGE_DUTCH_BELGIAN))
489 sKeyword[NF_KEY_YY] = "JJ";
490 sKeyword[NF_KEY_YYYY] = "JJJJ";
492 else if ( eLang == LANGUAGE_FINNISH )
494 sKeyword[NF_KEY_YY] = "VV";
495 sKeyword[NF_KEY_YYYY] = "VVVV";
498 // hour
499 if ( eLang.anyOf(
500 LANGUAGE_DUTCH,
501 LANGUAGE_DUTCH_BELGIAN))
503 sKeyword[NF_KEY_H] = "U";
504 sKeyword[NF_KEY_HH] = "UU";
506 else if ( eLang.anyOf(
507 LANGUAGE_FINNISH,
508 LANGUAGE_SWEDISH,
509 LANGUAGE_SWEDISH_FINLAND,
510 LANGUAGE_DANISH,
511 LANGUAGE_NORWEGIAN,
512 LANGUAGE_NORWEGIAN_BOKMAL,
513 LANGUAGE_NORWEGIAN_NYNORSK))
515 sKeyword[NF_KEY_H] = "T";
516 sKeyword[NF_KEY_HH] = "TT";
521 void ImpSvNumberformatScan::ChangeNullDate(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
523 maNullDate = Date(nDay, nMonth, nYear);
524 if (!maNullDate.IsValidDate())
526 maNullDate.Normalize();
527 SAL_WARN("svl.numbers","ImpSvNumberformatScan::ChangeNullDate - not valid"
528 " d: " << nDay << " m: " << nMonth << " y: " << nYear << " normalized to"
529 " d: " << maNullDate.GetDay() << " m: " << maNullDate.GetMonth() << " y: " << maNullDate.GetYear());
533 void ImpSvNumberformatScan::ChangeStandardPrec(sal_uInt16 nPrec)
535 nStandardPrec = nPrec;
538 const Color* ImpSvNumberformatScan::GetColor(OUString& sStr) const
540 OUString sString = pFormatter->GetCharClass()->uppercase(sStr);
541 const NfKeywordTable & rKeyword = GetKeywords();
542 size_t i = 0;
543 while (i < NF_MAX_DEFAULT_COLORS && sString != rKeyword[NF_KEY_FIRSTCOLOR+i] )
545 i++;
547 if (i >= NF_MAX_DEFAULT_COLORS && meKeywordLocalization == KeywordLocalization::AllowEnglish)
549 LanguageType eLang = pFormatter->GetLocaleData()->getLoadedLanguageTag().getLanguageType( false);
550 if ( eLang.anyOf(
551 LANGUAGE_GERMAN,
552 LANGUAGE_GERMAN_SWISS,
553 LANGUAGE_GERMAN_AUSTRIAN,
554 LANGUAGE_GERMAN_LUXEMBOURG,
555 LANGUAGE_GERMAN_LIECHTENSTEIN )) // only German uses localized color names
557 size_t j = 0;
558 while ( j < NF_MAX_DEFAULT_COLORS && sString != sEnglishKeyword[NF_KEY_FIRSTCOLOR + j] )
560 ++j;
562 if ( j < NF_MAX_DEFAULT_COLORS )
564 i = j;
569 enum ColorKeywordConversion
571 None,
572 GermanToEnglish,
573 EnglishToGerman
574 } eColorKeywordConversion(None);
576 if (bConvertMode)
578 const bool bFromGerman = eTmpLnge.anyOf(
579 LANGUAGE_GERMAN,
580 LANGUAGE_GERMAN_SWISS,
581 LANGUAGE_GERMAN_AUSTRIAN,
582 LANGUAGE_GERMAN_LUXEMBOURG,
583 LANGUAGE_GERMAN_LIECHTENSTEIN);
584 const bool bToGerman = eNewLnge.anyOf(
585 LANGUAGE_GERMAN,
586 LANGUAGE_GERMAN_SWISS,
587 LANGUAGE_GERMAN_AUSTRIAN,
588 LANGUAGE_GERMAN_LUXEMBOURG,
589 LANGUAGE_GERMAN_LIECHTENSTEIN);
590 if (bFromGerman && !bToGerman)
591 eColorKeywordConversion = ColorKeywordConversion::GermanToEnglish;
592 else if (!bFromGerman && bToGerman)
593 eColorKeywordConversion = ColorKeywordConversion::EnglishToGerman;
596 const Color* pResult = nullptr;
597 if (i >= NF_MAX_DEFAULT_COLORS)
599 const OUString& rColorWord = rKeyword[NF_KEY_COLOR];
600 bool bL10n = true;
601 if ((bL10n = sString.startsWith(rColorWord)) ||
602 ((meKeywordLocalization == KeywordLocalization::AllowEnglish) &&
603 sString.startsWith(sEnglishKeyword[NF_KEY_COLOR])))
605 sal_Int32 nPos = (bL10n ? rColorWord.getLength() : sEnglishKeyword[NF_KEY_COLOR].getLength());
606 sStr = sStr.copy(nPos);
607 sStr = comphelper::string::strip(sStr, ' ');
608 switch (eColorKeywordConversion)
610 case ColorKeywordConversion::None:
611 sStr = rColorWord + sStr;
612 break;
613 case ColorKeywordConversion::GermanToEnglish:
614 sStr = sEnglishKeyword[NF_KEY_COLOR] + sStr; // Farbe -> COLOR
615 break;
616 case ColorKeywordConversion::EnglishToGerman:
617 sStr = GermanColorName(NF_KEY_COLOR - NF_KEY_COLOR) + sStr; // Color -> FARBE
618 break;
620 sString = sString.copy(nPos);
621 sString = comphelper::string::strip(sString, ' ');
623 if ( CharClass::isAsciiNumeric( sString ) )
625 sal_Int32 nIndex = sString.toInt32();
626 if (nIndex > 0 && nIndex <= 64)
628 pResult = pFormatter->GetUserDefColor(static_cast<sal_uInt16>(nIndex)-1);
633 else
635 sStr.clear();
636 switch (eColorKeywordConversion)
638 case ColorKeywordConversion::None:
639 sStr = rKeyword[NF_KEY_FIRSTCOLOR+i];
640 break;
641 case ColorKeywordConversion::GermanToEnglish:
642 sStr = sEnglishKeyword[NF_KEY_FIRSTCOLOR + i]; // Rot -> RED
643 break;
644 case ColorKeywordConversion::EnglishToGerman:
645 sStr = GermanColorName(NF_KEY_FIRSTCOLOR - NF_KEY_COLOR + i); // Red -> ROT
646 break;
648 pResult = &(StandardColor[i]);
650 return pResult;
653 short ImpSvNumberformatScan::GetKeyWord( const OUString& sSymbol, sal_Int32 nPos, bool& rbFoundEnglish ) const
655 OUString sString = pFormatter->GetCharClass()->uppercase( sSymbol, nPos, sSymbol.getLength() - nPos );
656 const NfKeywordTable & rKeyword = GetKeywords();
657 // #77026# for the Xcl perverts: the GENERAL keyword is recognized anywhere
658 if (sString.startsWith( rKeyword[NF_KEY_GENERAL] ))
660 return NF_KEY_GENERAL;
662 if ((meKeywordLocalization == KeywordLocalization::AllowEnglish) &&
663 sString.startsWith( sEnglishKeyword[NF_KEY_GENERAL]))
665 rbFoundEnglish = true;
666 return NF_KEY_GENERAL;
669 // MUST be a reverse search to find longer strings first,
670 // new keywords take precedence over old keywords,
671 // skip colors et al after keywords.
672 short i = NF_KEY_LASTKEYWORD;
673 while (i > 0 && !sString.startsWith( rKeyword[i]))
675 i--;
677 if (i == 0 && meKeywordLocalization == KeywordLocalization::AllowEnglish)
679 // No localized (if so) keyword, try English keywords if keywords
680 // are localized. That was already checked in SetDependentKeywords().
681 i = NF_KEY_LASTKEYWORD;
682 while (i > 0 && !sString.startsWith( sEnglishKeyword[i]))
684 i--;
688 // The Thai T NatNum modifier during Xcl import.
689 if (i == 0 && bConvertMode &&
690 sString[0] == 'T' &&
691 eTmpLnge == LANGUAGE_ENGLISH_US &&
692 MsLangId::getRealLanguage( eNewLnge) == LANGUAGE_THAI)
694 i = NF_KEY_THAI_T;
696 return i; // 0 => not found
700 * Next_Symbol
702 * Splits up the input for further processing (by the Turing machine).
704 * Starting state = SsStar
706 * ---------------+-------------------+---------------------------+---------------
707 * Old state | Character read | Event | New state
708 * ---------------+-------------------+---------------------------+---------------
709 * SsStart | Character | Symbol = Character | SsGetWord
710 * | " | Type = String | SsGetString
711 * | \ | Type = String | SsGetChar
712 * | * | Type = Star | SsGetStar
713 * | _ | Type = Blank | SsGetBlank
714 * | @ # 0 ? / . , % [ | Symbol = Character; |
715 * | ] ' Blank | Type = Control character | SsStop
716 * | $ - + ( ) : | Type = String; |
717 * | Else | Symbol = Character | SsStop
718 * ---------------|-------------------+---------------------------+---------------
719 * SsGetChar | Else | Symbol = Character | SsStop
720 * ---------------+-------------------+---------------------------+---------------
721 * GetString | " | | SsStop
722 * | Else | Symbol += Character | GetString
723 * ---------------+-------------------+---------------------------+---------------
724 * SsGetWord | Character | Symbol += Character |
725 * | + - (E+ E-)| Symbol += Character | SsStop
726 * | / (AM/PM)| Symbol += Character |
727 * | Else | Pos--, if Key Type = Word | SsStop
728 * ---------------+-------------------+---------------------------+---------------
729 * SsGetStar | Else | Symbol += Character | SsStop
730 * | | Mark special case * |
731 * ---------------+-------------------+---------------------------+---------------
732 * SsGetBlank | Else | Symbol + =Character | SsStop
733 * | | Mark special case _ |
734 * ---------------------------------------------------------------+--------------
736 * If we recognize a keyword in the state SsGetWord (even as the symbol's start text)
737 * we write back the rest of the characters!
740 namespace {
742 enum ScanState
744 SsStop = 0,
745 SsStart = 1,
746 SsGetChar = 2,
747 SsGetString = 3,
748 SsGetWord = 4,
749 SsGetStar = 5,
750 SsGetBlank = 6
755 short ImpSvNumberformatScan::Next_Symbol( const OUString& rStr,
756 sal_Int32& nPos,
757 OUString& sSymbol ) const
759 InitKeywords();
760 const CharClass* pChrCls = pFormatter->GetCharClass();
761 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
762 short eType = 0;
763 ScanState eState = SsStart;
764 OUStringBuffer sSymbolBuffer;
765 while ( nPos < rStr.getLength() && eState != SsStop )
767 sal_Unicode cToken = rStr[nPos++];
768 switch (eState)
770 case SsStart:
771 // Fetch any currency longer than one character and don't get
772 // confused later on by "E/" or other combinations of letters
773 // and meaningful symbols. Necessary for old automatic currency.
774 // #96158# But don't do it if we're starting a "[...]" section,
775 // for example a "[$...]" new currency symbol to not parse away
776 // "$U" (symbol) of "[$UYU]" (abbreviation).
777 if ( nCurrPos >= 0 && sCurString.getLength() > 1 &&
778 nPos-1 + sCurString.getLength() <= rStr.getLength() &&
779 (nPos <= 1 || rStr[nPos-2] != '[') )
781 OUString aTest = pChrCls->uppercase( rStr.copy( nPos-1, sCurString.getLength() ) );
782 if ( aTest == sCurString )
784 sSymbol = rStr.copy( --nPos, sCurString.getLength() );
785 nPos = nPos + sSymbol.getLength();
786 eType = NF_SYMBOLTYPE_STRING;
787 return eType;
790 switch (cToken)
792 case '#':
793 case '0':
794 case '?':
795 case '%':
796 case '@':
797 case '[':
798 case ']':
799 case ',':
800 case '.':
801 case '/':
802 case '\'':
803 case ' ':
804 case ':':
805 case '-':
806 eType = NF_SYMBOLTYPE_DEL;
807 sSymbolBuffer.append(OUStringChar(cToken));
808 eState = SsStop;
809 break;
810 case '*':
811 eType = NF_SYMBOLTYPE_STAR;
812 sSymbolBuffer.append(OUStringChar(cToken));
813 eState = SsGetStar;
814 break;
815 case '_':
816 eType = NF_SYMBOLTYPE_BLANK;
817 sSymbolBuffer.append(OUStringChar(cToken));
818 eState = SsGetBlank;
819 break;
820 case '"':
821 eType = NF_SYMBOLTYPE_STRING;
822 eState = SsGetString;
823 sSymbolBuffer.append(OUStringChar(cToken));
824 break;
825 case '\\':
826 eType = NF_SYMBOLTYPE_STRING;
827 eState = SsGetChar;
828 sSymbolBuffer.append(OUStringChar(cToken));
829 break;
830 case '$':
831 case '+':
832 case '(':
833 case ')':
834 eType = NF_SYMBOLTYPE_STRING;
835 eState = SsStop;
836 sSymbolBuffer.append(OUStringChar(cToken));
837 break;
838 default :
839 if (StringEqualsChar( pFormatter->GetNumDecimalSep(), cToken) ||
840 StringEqualsChar( pFormatter->GetNumThousandSep(), cToken) ||
841 StringEqualsChar( pFormatter->GetDateSep(), cToken) ||
842 StringEqualsChar( pLoc->getTimeSep(), cToken) ||
843 StringEqualsChar( pLoc->getTime100SecSep(), cToken))
845 // Another separator than pre-known ASCII
846 eType = NF_SYMBOLTYPE_DEL;
847 sSymbolBuffer.append(OUStringChar(cToken));
848 eState = SsStop;
850 else if ( pChrCls->isLetter( rStr, nPos-1 ) )
852 bool bFoundEnglish = false;
853 short nTmpType = GetKeyWord( rStr, nPos-1, bFoundEnglish);
854 if ( nTmpType )
856 bool bCurrency = false;
857 // "Automatic" currency may start with keyword,
858 // like "R" (Rand) and 'R' (era)
859 if ( nCurrPos >= 0 &&
860 nPos-1 + sCurString.getLength() <= rStr.getLength() &&
861 sCurString.startsWith( bFoundEnglish ? sEnglishKeyword[nTmpType] : sKeyword[nTmpType]))
863 OUString aTest = pChrCls->uppercase( rStr.copy( nPos-1, sCurString.getLength() ) );
864 if ( aTest == sCurString )
866 bCurrency = true;
869 if ( bCurrency )
871 eState = SsGetWord;
872 sSymbolBuffer.append(OUStringChar(cToken));
874 else
876 eType = nTmpType;
877 // The code to be advanced is the detected keyword,
878 // not necessarily the locale's keyword, but the
879 // symbol is to be the locale's keyword.
880 sal_Int32 nLen;
881 if (bFoundEnglish)
883 nLen = sEnglishKeyword[eType].getLength();
884 // Use the locale's General keyword name, not uppercase.
885 sSymbolBuffer = (eType == NF_KEY_GENERAL ? sNameStandardFormat : sKeyword[eType]);
887 else
889 nLen = sKeyword[eType].getLength();
890 // Preserve a locale's keyword's case as entered.
891 sSymbolBuffer = rStr.copy( nPos-1, nLen);
893 if ((eType == NF_KEY_E || IsAmbiguousE(eType)) && nPos < rStr.getLength())
895 sal_Unicode cNext = rStr[nPos];
896 switch ( cNext )
898 case '+' :
899 case '-' : // E+ E- combine to one symbol
900 sSymbolBuffer.append(OUStringChar(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;
910 nPos--;
911 nPos = nPos + nLen;
912 eState = SsStop;
915 else
917 eState = SsGetWord;
918 sSymbolBuffer.append(OUStringChar(cToken));
921 else
923 eType = NF_SYMBOLTYPE_STRING;
924 eState = SsStop;
925 sSymbolBuffer.append(OUStringChar(cToken));
927 break;
929 break;
930 case SsGetChar:
931 sSymbolBuffer.append(OUStringChar(cToken));
932 eState = SsStop;
933 break;
934 case SsGetString:
935 if (cToken == '"')
937 eState = SsStop;
939 sSymbolBuffer.append(OUStringChar(cToken));
940 break;
941 case SsGetWord:
942 if ( pChrCls->isLetter( rStr, nPos-1 ) )
944 bool bFoundEnglish = false;
945 short nTmpType = GetKeyWord( rStr, nPos-1, bFoundEnglish);
946 if ( nTmpType )
948 // beginning of keyword, stop scan and put back
949 eType = NF_SYMBOLTYPE_STRING;
950 eState = SsStop;
951 nPos--;
953 else
955 sSymbolBuffer.append(OUStringChar(cToken));
958 else
960 bool bDontStop = false;
961 sal_Unicode cNext;
962 switch (cToken)
964 case '/': // AM/PM, A/P
965 if (nPos < rStr.getLength())
967 cNext = rStr[nPos];
968 if ( cNext == 'P' || cNext == 'p' )
970 sal_Int32 nLen = sSymbolBuffer.getLength();
971 if ( 1 <= nLen &&
972 (sSymbolBuffer[0] == 'A' || sSymbolBuffer[0] == 'a') &&
973 (nLen == 1 ||
974 (nLen == 2 && (sSymbolBuffer[1] == 'M' || sSymbolBuffer[1] == 'm')
975 && (rStr[nPos + 1] == 'M' || rStr[nPos + 1] == 'm'))))
977 sSymbolBuffer.append(OUStringChar(cToken));
978 bDontStop = true;
982 break;
984 // anything not recognized will stop the scan
985 if (!bDontStop)
987 eState = SsStop;
988 nPos--;
989 eType = NF_SYMBOLTYPE_STRING;
992 break;
993 case SsGetStar:
994 eState = SsStop;
995 sSymbolBuffer.append(OUStringChar(cToken));
996 break;
997 case SsGetBlank:
998 eState = SsStop;
999 sSymbolBuffer.append(OUStringChar(cToken));
1000 break;
1001 default:
1002 break;
1003 } // of switch
1004 } // of while
1005 if (eState == SsGetWord)
1007 eType = NF_SYMBOLTYPE_STRING;
1009 sSymbol = sSymbolBuffer.makeStringAndClear();
1010 return eType;
1013 sal_Int32 ImpSvNumberformatScan::Symbol_Division(const OUString& rString)
1015 nCurrPos = -1;
1016 // Do we have some sort of currency?
1017 OUString sString = pFormatter->GetCharClass()->uppercase(rString);
1018 sal_Int32 nCPos = 0;
1019 while (nCPos >= 0 && nCPos < sString.getLength())
1021 nCPos = sString.indexOf(GetCurString(),nCPos);
1022 if (nCPos >= 0)
1024 // In Quotes?
1025 sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sString, nCPos );
1026 if ( nQ < 0 )
1028 sal_Unicode c;
1029 if ( nCPos == 0 ||
1030 ((c = sString[nCPos-1]) != '"'
1031 && c != '\\') ) // dm can be protected by "dm \d
1033 nCurrPos = nCPos;
1034 nCPos = -1;
1036 else
1038 nCPos++; // Continue search
1041 else
1043 nCPos = nQ + 1; // Continue search
1047 nStringsCnt = 0;
1048 bool bStar = false; // Is set on detecting '*'
1049 Reset();
1051 sal_Int32 nPos = 0;
1052 const sal_Int32 nLen = rString.getLength();
1053 while (nPos < nLen && nStringsCnt < NF_MAX_FORMAT_SYMBOLS)
1055 nTypeArray[nStringsCnt] = Next_Symbol(rString, nPos, sStrArray[nStringsCnt]);
1056 if (nTypeArray[nStringsCnt] == NF_SYMBOLTYPE_STAR)
1057 { // Monitoring the '*'
1058 if (bStar)
1060 return nPos; // Error: double '*'
1062 else
1064 // Valid only if there is a character following, else we are
1065 // at the end of a code that does not have a fill character
1066 // (yet?).
1067 if (sStrArray[nStringsCnt].getLength() < 2)
1068 return nPos;
1069 bStar = true;
1072 nStringsCnt++;
1075 return 0; // 0 => ok
1078 void ImpSvNumberformatScan::SkipStrings(sal_uInt16& i, sal_Int32& nPos) const
1080 while (i < nStringsCnt && ( nTypeArray[i] == NF_SYMBOLTYPE_STRING
1081 || nTypeArray[i] == NF_SYMBOLTYPE_BLANK
1082 || nTypeArray[i] == NF_SYMBOLTYPE_STAR) )
1084 nPos = nPos + sStrArray[i].getLength();
1085 i++;
1089 sal_uInt16 ImpSvNumberformatScan::PreviousKeyword(sal_uInt16 i) const
1091 short res = 0;
1092 if (i > 0 && i < nStringsCnt)
1094 i--;
1095 while (i > 0 && nTypeArray[i] <= 0)
1097 i--;
1099 if (nTypeArray[i] > 0)
1101 res = nTypeArray[i];
1104 return res;
1107 sal_uInt16 ImpSvNumberformatScan::NextKeyword(sal_uInt16 i) const
1109 short res = 0;
1110 if (i < nStringsCnt-1)
1112 i++;
1113 while (i < nStringsCnt-1 && nTypeArray[i] <= 0)
1115 i++;
1117 if (nTypeArray[i] > 0)
1119 res = nTypeArray[i];
1122 return res;
1125 short ImpSvNumberformatScan::PreviousType( sal_uInt16 i ) const
1127 if ( i > 0 && i < nStringsCnt )
1131 i--;
1133 while ( i > 0 && nTypeArray[i] == NF_SYMBOLTYPE_EMPTY );
1134 return nTypeArray[i];
1136 return 0;
1139 sal_Unicode ImpSvNumberformatScan::PreviousChar(sal_uInt16 i) const
1141 sal_Unicode res = ' ';
1142 if (i > 0 && i < nStringsCnt)
1144 i--;
1145 while (i > 0 &&
1146 ( nTypeArray[i] == NF_SYMBOLTYPE_EMPTY ||
1147 nTypeArray[i] == NF_SYMBOLTYPE_STRING ||
1148 nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
1149 nTypeArray[i] == NF_SYMBOLTYPE_BLANK ))
1151 i--;
1153 if (sStrArray[i].getLength() > 0)
1155 res = sStrArray[i][sStrArray[i].getLength()-1];
1158 return res;
1161 sal_Unicode ImpSvNumberformatScan::NextChar(sal_uInt16 i) const
1163 sal_Unicode res = ' ';
1164 if (i < nStringsCnt-1)
1166 i++;
1167 while (i < nStringsCnt-1 &&
1168 ( nTypeArray[i] == NF_SYMBOLTYPE_EMPTY ||
1169 nTypeArray[i] == NF_SYMBOLTYPE_STRING ||
1170 nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
1171 nTypeArray[i] == NF_SYMBOLTYPE_BLANK))
1173 i++;
1175 if (sStrArray[i].getLength() > 0)
1177 res = sStrArray[i][0];
1180 return res;
1183 bool ImpSvNumberformatScan::IsLastBlankBeforeFrac(sal_uInt16 i) const
1185 bool res = true;
1186 if (i < nStringsCnt-1)
1188 bool bStop = false;
1189 i++;
1190 while (i < nStringsCnt-1 && !bStop)
1192 i++;
1193 if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL &&
1194 sStrArray[i][0] == '/')
1196 bStop = true;
1198 else if ( ( nTypeArray[i] == NF_SYMBOLTYPE_DEL &&
1199 sStrArray[i][0] == ' ') ||
1200 nTypeArray[i] == NF_SYMBOLTYPE_STRING ) // integer/fraction delimiter can also be a string
1202 res = false;
1205 if (!bStop) // no '/'{
1207 res = false;
1210 else
1212 res = false; // no '/' any more
1214 return res;
1217 void ImpSvNumberformatScan::Reset()
1219 nStringsCnt = 0;
1220 nResultStringsCnt = 0;
1221 eScannedType = SvNumFormatType::UNDEFINED;
1222 bExp = false;
1223 bThousand = false;
1224 nThousand = 0;
1225 bDecSep = false;
1226 nDecPos = sal_uInt16(-1);
1227 nExpPos = sal_uInt16(-1);
1228 nBlankPos = sal_uInt16(-1);
1229 nCntPre = 0;
1230 nCntPost = 0;
1231 nCntExp = 0;
1232 bFrac = false;
1233 bBlank = false;
1234 nNatNumModifier = 0;
1237 bool ImpSvNumberformatScan::Is100SecZero( sal_uInt16 i, bool bHadDecSep ) const
1239 sal_uInt16 nIndexPre = PreviousKeyword( i );
1240 return (nIndexPre == NF_KEY_S || nIndexPre == NF_KEY_SS) &&
1241 (bHadDecSep ||
1242 ( i > 0 && nTypeArray[i-1] == NF_SYMBOLTYPE_STRING));
1243 // SS"any"00 take "any" as a valid decimal separator
1246 sal_Int32 ImpSvNumberformatScan::ScanType()
1248 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
1250 sal_Int32 nPos = 0;
1251 sal_uInt16 i = 0;
1252 SvNumFormatType eNewType;
1253 bool bMatchBracket = false;
1254 bool bHaveGeneral = false; // if General/Standard encountered
1255 bool bIsTimeDetected =false; // hour or second found in format
1256 bool bHaveMinute = false;
1258 SkipStrings(i, nPos);
1259 while (i < nStringsCnt)
1261 if (nTypeArray[i] > 0)
1262 { // keyword
1263 sal_uInt16 nIndexPre;
1264 sal_uInt16 nIndexNex;
1266 switch (nTypeArray[i])
1268 case NF_KEY_E: // E
1269 eNewType = SvNumFormatType::SCIENTIFIC;
1270 break;
1271 case NF_KEY_H: // H
1272 case NF_KEY_HH: // HH
1273 bIsTimeDetected = true;
1274 [[fallthrough]];
1275 case NF_KEY_S: // S
1276 case NF_KEY_SS: // SS
1277 if ( !bHaveMinute )
1278 bIsTimeDetected = true;
1279 [[fallthrough]];
1280 case NF_KEY_AMPM: // AM,A,PM,P
1281 case NF_KEY_AP:
1282 eNewType = SvNumFormatType::TIME;
1283 break;
1284 case NF_KEY_M: // M
1285 case NF_KEY_MM: // MM
1286 case NF_KEY_MI: // M minute detected in Finnish
1287 case NF_KEY_MMI: // MM
1288 /* Minute or month.
1289 Minute if one of:
1290 * preceded by time keyword H (ignoring separators)
1291 * followed by time keyword S (ignoring separators)
1292 * H or S was detected and this is the first M following
1293 * preceded by '[' amount bracket
1294 Else month.
1295 That are the Excel rules. BUT, we break it because certainly
1296 in something like {HH YYYY-MM-DD} the MM is NOT meant to be
1297 minute, so not if MM is between YY and DD or DD and YY.
1298 Actually not if any date specific keyword followed a time
1299 setting keyword.
1301 nIndexPre = PreviousKeyword(i);
1302 nIndexNex = NextKeyword(i);
1303 if (nIndexPre == NF_KEY_H || // H
1304 nIndexPre == NF_KEY_HH || // HH
1305 nIndexNex == NF_KEY_S || // S
1306 nIndexNex == NF_KEY_SS || // SS
1307 bIsTimeDetected || // tdf#101147
1308 PreviousChar(i) == '[' ) // [M
1310 eNewType = SvNumFormatType::TIME;
1311 if ( nTypeArray[i] == NF_KEY_M || nTypeArray[i] == NF_KEY_MM )
1313 nTypeArray[i] -= 2; // 6 -> 4, 7 -> 5
1315 bIsTimeDetected = false; // next M should be month
1316 bHaveMinute = true;
1318 else
1320 eNewType = SvNumFormatType::DATE;
1321 if ( nTypeArray[i] == NF_KEY_MI || nTypeArray[i] == NF_KEY_MMI )
1322 { // follow resolution of tdf#33689 for Finnish
1323 nTypeArray[i] += 2; // 4 -> 6, 5 -> 7
1326 break;
1327 case NF_KEY_MMM: // MMM
1328 case NF_KEY_MMMM: // MMMM
1329 case NF_KEY_MMMMM: // MMMMM
1330 case NF_KEY_Q: // Q
1331 case NF_KEY_QQ: // QQ
1332 case NF_KEY_D: // D
1333 case NF_KEY_DD: // DD
1334 case NF_KEY_DDD: // DDD
1335 case NF_KEY_DDDD: // DDDD
1336 case NF_KEY_YY: // YY
1337 case NF_KEY_YYYY: // YYYY
1338 case NF_KEY_NN: // NN
1339 case NF_KEY_NNN: // NNN
1340 case NF_KEY_NNNN: // NNNN
1341 case NF_KEY_WW : // WW
1342 case NF_KEY_AAA : // AAA
1343 case NF_KEY_AAAA : // AAAA
1344 case NF_KEY_EC : // E
1345 case NF_KEY_EEC : // EE
1346 case NF_KEY_G : // G
1347 case NF_KEY_GG : // GG
1348 case NF_KEY_GGG : // GGG
1349 case NF_KEY_R : // R
1350 case NF_KEY_RR : // RR
1351 eNewType = SvNumFormatType::DATE;
1352 bIsTimeDetected = false;
1353 break;
1354 case NF_KEY_CCC: // CCC
1355 eNewType = SvNumFormatType::CURRENCY;
1356 break;
1357 case NF_KEY_GENERAL: // Standard
1358 eNewType = SvNumFormatType::NUMBER;
1359 bHaveGeneral = true;
1360 break;
1361 default:
1362 eNewType = SvNumFormatType::UNDEFINED;
1363 break;
1366 else
1367 { // control character
1368 switch ( sStrArray[i][0] )
1370 case '#':
1371 case '?':
1372 eNewType = SvNumFormatType::NUMBER;
1373 break;
1374 case '0':
1375 if ( eScannedType & SvNumFormatType::TIME )
1377 if ( Is100SecZero( i, bDecSep ) )
1379 bDecSep = true; // subsequent 0's
1380 eNewType = SvNumFormatType::TIME;
1382 else
1384 return nPos; // Error
1387 else
1389 eNewType = SvNumFormatType::NUMBER;
1391 break;
1392 case '%':
1393 eNewType = SvNumFormatType::PERCENT;
1394 break;
1395 case '/':
1396 eNewType = SvNumFormatType::FRACTION;
1397 break;
1398 case '[':
1399 if ( i < nStringsCnt-1 &&
1400 nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
1401 sStrArray[i+1][0] == '$' )
1403 eNewType = SvNumFormatType::CURRENCY;
1404 bMatchBracket = true;
1406 else if ( i < nStringsCnt-1 &&
1407 nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
1408 sStrArray[i+1][0] == '~' )
1410 eNewType = SvNumFormatType::DATE;
1411 bMatchBracket = true;
1413 else
1415 sal_uInt16 nIndexNex = NextKeyword(i);
1416 if (nIndexNex == NF_KEY_H || // H
1417 nIndexNex == NF_KEY_HH || // HH
1418 nIndexNex == NF_KEY_M || // M
1419 nIndexNex == NF_KEY_MM || // MM
1420 nIndexNex == NF_KEY_S || // S
1421 nIndexNex == NF_KEY_SS ) // SS
1422 eNewType = SvNumFormatType::TIME;
1423 else
1425 return nPos; // Error
1428 break;
1429 case '@':
1430 eNewType = SvNumFormatType::TEXT;
1431 break;
1432 default:
1433 if (pLoc->getTime100SecSep() == sStrArray[i])
1435 bDecSep = true; // for SS,0
1437 eNewType = SvNumFormatType::UNDEFINED;
1438 break;
1441 if (eScannedType == SvNumFormatType::UNDEFINED)
1443 eScannedType = eNewType;
1445 else if (eScannedType == SvNumFormatType::TEXT || eNewType == SvNumFormatType::TEXT)
1447 eScannedType = SvNumFormatType::TEXT; // Text always remains text
1449 else if (eNewType == SvNumFormatType::UNDEFINED)
1450 { // Remains as is
1452 else if (eScannedType != eNewType)
1454 switch (eScannedType)
1456 case SvNumFormatType::DATE:
1457 switch (eNewType)
1459 case SvNumFormatType::TIME:
1460 eScannedType = SvNumFormatType::DATETIME;
1461 break;
1462 case SvNumFormatType::FRACTION: // DD/MM
1463 break;
1464 default:
1465 if (nCurrPos >= 0)
1467 eScannedType = SvNumFormatType::UNDEFINED;
1469 else if ( sStrArray[i] != pFormatter->GetDateSep() )
1471 return nPos;
1474 break;
1475 case SvNumFormatType::TIME:
1476 switch (eNewType)
1478 case SvNumFormatType::DATE:
1479 eScannedType = SvNumFormatType::DATETIME;
1480 break;
1481 case SvNumFormatType::FRACTION: // MM/SS
1482 break;
1483 default:
1484 if (nCurrPos >= 0)
1486 eScannedType = SvNumFormatType::UNDEFINED;
1488 else if (pLoc->getTimeSep() != sStrArray[i])
1490 return nPos;
1492 break;
1494 break;
1495 case SvNumFormatType::DATETIME:
1496 switch (eNewType)
1498 case SvNumFormatType::TIME:
1499 case SvNumFormatType::DATE:
1500 break;
1501 case SvNumFormatType::FRACTION: // DD/MM
1502 break;
1503 default:
1504 if (nCurrPos >= 0)
1506 eScannedType = SvNumFormatType::UNDEFINED;
1508 else if ( pFormatter->GetDateSep() != sStrArray[i] &&
1509 pLoc->getTimeSep() != sStrArray[i] )
1511 return nPos;
1514 break;
1515 case SvNumFormatType::PERCENT:
1516 switch (eNewType)
1518 case SvNumFormatType::NUMBER: // Only number to percent
1519 break;
1520 default:
1521 return nPos;
1523 break;
1524 case SvNumFormatType::SCIENTIFIC:
1525 switch (eNewType)
1527 case SvNumFormatType::NUMBER: // Only number to E
1528 break;
1529 default:
1530 return nPos;
1532 break;
1533 case SvNumFormatType::NUMBER:
1534 switch (eNewType)
1536 case SvNumFormatType::SCIENTIFIC:
1537 case SvNumFormatType::PERCENT:
1538 case SvNumFormatType::FRACTION:
1539 case SvNumFormatType::CURRENCY:
1540 eScannedType = eNewType;
1541 break;
1542 default:
1543 if (nCurrPos >= 0)
1545 eScannedType = SvNumFormatType::UNDEFINED;
1547 else
1549 return nPos;
1552 break;
1553 case SvNumFormatType::FRACTION:
1554 switch (eNewType)
1556 case SvNumFormatType::NUMBER: // Only number to fraction
1557 break;
1558 default:
1559 return nPos;
1561 break;
1562 default:
1563 break;
1566 nPos = nPos + sStrArray[i].getLength(); // Position of correction
1567 i++;
1568 if ( bMatchBracket )
1569 { // no type detection inside of matching brackets if [$...], [~...]
1570 while ( bMatchBracket && i < nStringsCnt )
1572 if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL
1573 && sStrArray[i][0] == ']' )
1575 bMatchBracket = false;
1577 else
1579 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1581 nPos = nPos + sStrArray[i].getLength();
1582 i++;
1584 if ( bMatchBracket )
1586 return nPos; // missing closing bracket at end of code
1589 SkipStrings(i, nPos);
1592 if ((eScannedType == SvNumFormatType::NUMBER ||
1593 eScannedType == SvNumFormatType::UNDEFINED) &&
1594 nCurrPos >= 0 && !bHaveGeneral)
1596 eScannedType = SvNumFormatType::CURRENCY; // old "automatic" currency
1598 if (eScannedType == SvNumFormatType::UNDEFINED)
1600 eScannedType = SvNumFormatType::DEFINED;
1602 return 0; // All is fine
1605 bool ImpSvNumberformatScan::InsertSymbol( sal_uInt16 & nPos, svt::NfSymbolType eType, const OUString& rStr )
1607 if (nStringsCnt >= NF_MAX_FORMAT_SYMBOLS || nPos > nStringsCnt)
1609 return false;
1611 if (nPos > 0 && nTypeArray[nPos-1] == NF_SYMBOLTYPE_EMPTY)
1613 --nPos; // reuse position
1615 else
1617 if (nStringsCnt >= NF_MAX_FORMAT_SYMBOLS - 1)
1619 return false;
1621 ++nStringsCnt;
1622 for (size_t i = nStringsCnt; i > nPos; --i)
1624 nTypeArray[i] = nTypeArray[i-1];
1625 sStrArray[i] = sStrArray[i-1];
1628 ++nResultStringsCnt;
1629 nTypeArray[nPos] = static_cast<short>(eType);
1630 sStrArray[nPos] = rStr;
1631 return true;
1634 int ImpSvNumberformatScan::FinalScanGetCalendar( sal_Int32& nPos, sal_uInt16& i,
1635 sal_uInt16& rResultStringsCnt )
1637 if ( i < nStringsCnt-1 &&
1638 sStrArray[i][0] == '[' &&
1639 nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
1640 sStrArray[i+1][0] == '~' )
1642 // [~calendarID]
1643 nPos = nPos + sStrArray[i].getLength(); // [
1644 nTypeArray[i] = NF_SYMBOLTYPE_CALDEL;
1645 nPos = nPos + sStrArray[++i].getLength(); // ~
1646 sStrArray[i-1] += sStrArray[i]; // [~
1647 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
1648 rResultStringsCnt--;
1649 if ( ++i >= nStringsCnt )
1651 return -1; // error
1653 nPos = nPos + sStrArray[i].getLength(); // calendarID
1654 OUString& rStr = sStrArray[i];
1655 nTypeArray[i] = NF_SYMBOLTYPE_CALENDAR; // convert
1656 i++;
1657 while ( i < nStringsCnt && sStrArray[i][0] != ']' )
1659 nPos = nPos + sStrArray[i].getLength();
1660 rStr += sStrArray[i];
1661 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
1662 rResultStringsCnt--;
1663 i++;
1665 if ( rStr.getLength() && i < nStringsCnt &&
1666 sStrArray[i][0] == ']' )
1668 nTypeArray[i] = NF_SYMBOLTYPE_CALDEL;
1669 nPos = nPos + sStrArray[i].getLength();
1670 i++;
1672 else
1674 return -1; // error
1676 return 1;
1678 return 0;
1681 bool ImpSvNumberformatScan::IsDateFragment( size_t nPos1, size_t nPos2 ) const
1683 return nPos2 - nPos1 == 2 && nTypeArray[nPos1+1] == NF_SYMBOLTYPE_DATESEP;
1686 void ImpSvNumberformatScan::SwapArrayElements( size_t nPos1, size_t nPos2 )
1688 std::swap( nTypeArray[nPos1], nTypeArray[nPos2]);
1689 std::swap( sStrArray[nPos1], sStrArray[nPos2]);
1692 sal_Int32 ImpSvNumberformatScan::FinalScan( OUString& rString )
1694 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
1696 // save values for convert mode
1697 OUString sOldDecSep = pFormatter->GetNumDecimalSep();
1698 OUString sOldThousandSep = pFormatter->GetNumThousandSep();
1699 OUString sOldDateSep = pFormatter->GetDateSep();
1700 OUString sOldTimeSep = pLoc->getTimeSep();
1701 OUString sOldTime100SecSep= pLoc->getTime100SecSep();
1702 OUString sOldCurSymbol = GetCurSymbol();
1703 OUString sOldCurString = GetCurString();
1704 sal_Unicode cOldKeyH = sKeyword[NF_KEY_H][0];
1705 sal_Unicode cOldKeyMI = sKeyword[NF_KEY_MI][0];
1706 sal_Unicode cOldKeyS = sKeyword[NF_KEY_S][0];
1707 DateOrder eOldDateOrder = pLoc->getDateOrder();
1708 sal_uInt16 nDayPos, nMonthPos, nYearPos;
1709 nDayPos = nMonthPos = nYearPos = SAL_MAX_UINT16;
1711 // If the group separator is a No-Break Space (French) continue with a
1712 // normal space instead so queries on space work correctly.
1713 // The same for Narrow No-Break Space just in case some locale uses it.
1714 // The format string is adjusted to allow both.
1715 // For output of the format code string the LocaleData characters are used.
1716 if ( (sOldThousandSep[0] == cNoBreakSpace || sOldThousandSep[0] == cNarrowNoBreakSpace) &&
1717 sOldThousandSep.getLength() == 1 )
1719 sOldThousandSep = " ";
1721 bool bNewDateOrder = false;
1722 // change locale data et al
1723 if (bConvertMode)
1725 pFormatter->ChangeIntl(eNewLnge);
1726 //! pointer may have changed
1727 pLoc = pFormatter->GetLocaleData();
1728 //! init new keywords
1729 InitKeywords();
1730 // Adapt date order to target locale, but Excel does not handle date
1731 // particle re-ordering for the target locale when loading documents,
1732 // though it does exchange separators, tdf#113889
1733 bNewDateOrder = (mbConvertDateOrder && eOldDateOrder != pLoc->getDateOrder());
1735 const CharClass* pChrCls = pFormatter->GetCharClass();
1737 sal_Int32 nPos = 0; // error correction position
1738 sal_uInt16 i = 0; // symbol loop counter
1739 sal_uInt16 nCounter = 0; // counts digits
1740 nResultStringsCnt = nStringsCnt; // counts remaining symbols
1741 bDecSep = false; // reset in case already used in TypeCheck
1742 bool bThaiT = false; // Thai T NatNum modifier present
1743 bool bTimePart = false;
1744 bool bDenomin = false; // Set when reading end of denominator
1746 switch (eScannedType)
1748 case SvNumFormatType::TEXT:
1749 case SvNumFormatType::DEFINED:
1750 while (i < nStringsCnt)
1752 switch (nTypeArray[i])
1754 case NF_SYMBOLTYPE_BLANK:
1755 case NF_SYMBOLTYPE_STAR:
1756 break;
1757 case NF_KEY_GENERAL : // #77026# "General" is the same as "@"
1758 break;
1759 default:
1760 if ( nTypeArray[i] != NF_SYMBOLTYPE_DEL ||
1761 sStrArray[i][0] != '@' )
1763 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1765 break;
1767 nPos = nPos + sStrArray[i].getLength();
1768 i++;
1769 } // of while
1770 break;
1772 case SvNumFormatType::NUMBER:
1773 case SvNumFormatType::PERCENT:
1774 case SvNumFormatType::CURRENCY:
1775 case SvNumFormatType::SCIENTIFIC:
1776 case SvNumFormatType::FRACTION:
1777 while (i < nStringsCnt)
1779 // TODO: rechecking eScannedType is unnecessary.
1780 // This switch-case is for eScannedType == SvNumFormatType::FRACTION anyway
1781 if (eScannedType == SvNumFormatType::FRACTION && // special case
1782 nTypeArray[i] == NF_SYMBOLTYPE_DEL && // # ### #/#
1783 StringEqualsChar( sOldThousandSep, ' ' ) && // e.g. France or Sweden
1784 StringEqualsChar( sStrArray[i], ' ' ) &&
1785 !bFrac &&
1786 IsLastBlankBeforeFrac(i) )
1788 nTypeArray[i] = NF_SYMBOLTYPE_STRING; // del->string
1789 } // No thousands marker
1791 if (nTypeArray[i] == NF_SYMBOLTYPE_BLANK ||
1792 nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
1793 nTypeArray[i] == NF_KEY_CCC || // CCC
1794 nTypeArray[i] == NF_KEY_GENERAL ) // Standard
1796 if (nTypeArray[i] == NF_KEY_GENERAL)
1798 nThousand = FLAG_STANDARD_IN_FORMAT;
1799 if ( bConvertMode )
1801 sStrArray[i] = sNameStandardFormat;
1804 nPos = nPos + sStrArray[i].getLength();
1805 i++;
1807 else if (nTypeArray[i] == NF_SYMBOLTYPE_STRING || // No Strings or
1808 nTypeArray[i] > 0) // Keywords
1810 if (eScannedType == SvNumFormatType::SCIENTIFIC &&
1811 nTypeArray[i] == NF_KEY_E) // E+
1813 if (bExp) // Double
1815 return nPos;
1817 bExp = true;
1818 nExpPos = i;
1819 if (bDecSep)
1821 nCntPost = nCounter;
1823 else
1825 nCntPre = nCounter;
1827 nCounter = 0;
1828 nTypeArray[i] = NF_SYMBOLTYPE_EXP;
1830 else if (eScannedType == SvNumFormatType::FRACTION &&
1831 (sStrArray[i][0] == ' ' || ( nTypeArray[i] == NF_SYMBOLTYPE_STRING && (sStrArray[i][0] < '0' || sStrArray[i][0] > '9') ) ) )
1833 if (!bBlank && !bFrac) // Not double or after a /
1835 if (bDecSep && nCounter > 0) // Decimal places
1837 return nPos; // Error
1839 if (sStrArray[i][0] == ' ' || nCounter > 0 ) // treat string as integer/fraction delimiter only if there is integer
1841 bBlank = true;
1842 nBlankPos = i;
1843 nCntPre = nCounter;
1844 nCounter = 0;
1845 nTypeArray[i] = NF_SYMBOLTYPE_FRACBLANK;
1848 else if ( sStrArray[i][0] == ' ' )
1849 nTypeArray[i] = NF_SYMBOLTYPE_FRACBLANK;
1850 else if ( bFrac && ( nCounter > 0 ) )
1851 bDenomin = true; // following elements are no more part of denominator
1853 else if (nTypeArray[i] == NF_KEY_THAI_T)
1855 bThaiT = true;
1856 sStrArray[i] = sKeyword[nTypeArray[i]];
1858 else if (sStrArray[i][0] >= '0' &&
1859 sStrArray[i][0] <= '9' && !bDenomin) // denominator was not yet found
1861 OUString sDiv;
1862 sal_uInt16 j = i;
1863 while(j < nStringsCnt && sStrArray[j][0] >= '0' && sStrArray[j][0] <= '9')
1865 sDiv += sStrArray[j++];
1867 assert(j > 0 && "if i is 0, first iteration through loop is guaranteed by surrounding if condition");
1868 if (std::u16string_view(OUString::number(sDiv.toInt32())) == sDiv)
1870 // Found a Divisor
1871 while (i < j)
1873 nTypeArray[i++] = NF_SYMBOLTYPE_FRAC_FDIV;
1875 i = j - 1; // Stop the loop
1876 if (nCntPost)
1878 nCounter = nCntPost;
1880 else if (nCntPre)
1882 nCounter = nCntPre;
1884 // don't artificially increment nCntPre for forced denominator
1885 if ( ( eScannedType != SvNumFormatType::FRACTION ) && (!nCntPre) )
1887 nCntPre++;
1889 if ( bFrac )
1890 bDenomin = true; // next content should be treated as outside denominator
1893 else
1895 if ( bFrac && ( nCounter > 0 ) )
1896 bDenomin = true; // next content should be treated as outside denominator
1897 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1899 nPos = nPos + sStrArray[i].getLength();
1900 i++;
1902 else if (nTypeArray[i] == NF_SYMBOLTYPE_DEL)
1904 sal_Unicode cHere = sStrArray[i][0];
1905 sal_Unicode cSaved = cHere;
1906 // Handle not pre-known separators in switch.
1907 sal_Unicode cSimplified;
1908 if (StringEqualsChar( pFormatter->GetNumThousandSep(), cHere))
1910 cSimplified = ',';
1912 else if (StringEqualsChar( pFormatter->GetNumDecimalSep(), cHere))
1914 cSimplified = '.';
1916 else
1918 cSimplified = cHere;
1921 OUString& rStr = sStrArray[i];
1923 switch ( cSimplified )
1925 case '#':
1926 case '0':
1927 case '?':
1928 if (nThousand > 0) // #... #
1930 return nPos; // Error
1932 if ( !bDenomin )
1934 nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
1935 nPos = nPos + rStr.getLength();
1936 i++;
1937 nCounter++;
1938 while (i < nStringsCnt &&
1939 (sStrArray[i][0] == '#' ||
1940 sStrArray[i][0] == '0' ||
1941 sStrArray[i][0] == '?'))
1943 nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
1944 nPos = nPos + sStrArray[i].getLength();
1945 nCounter++;
1946 i++;
1949 else // after denominator, treat any character as text
1951 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1952 nPos = nPos + sStrArray[i].getLength();
1954 break;
1955 case '-':
1956 if ( bDecSep && nDecPos+1 == i &&
1957 nTypeArray[nDecPos] == NF_SYMBOLTYPE_DECSEP )
1959 // "0.--"
1960 nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
1961 nPos = nPos + rStr.getLength();
1962 i++;
1963 nCounter++;
1964 while (i < nStringsCnt &&
1965 (sStrArray[i][0] == '-') )
1967 // If more than two dashes are present in
1968 // currency formats the last dash will be
1969 // interpreted literally as a minus sign.
1970 // Has to be this ugly. Period.
1971 if ( eScannedType == SvNumFormatType::CURRENCY
1972 && rStr.getLength() >= 2 &&
1973 (i == nStringsCnt-1 ||
1974 sStrArray[i+1][0] != '-') )
1976 break;
1978 rStr += sStrArray[i];
1979 nPos = nPos + sStrArray[i].getLength();
1980 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
1981 nResultStringsCnt--;
1982 nCounter++;
1983 i++;
1986 else
1988 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1989 nPos = nPos + sStrArray[i].getLength();
1990 i++;
1992 break;
1993 case '.':
1994 case ',':
1995 case '\'':
1996 case ' ':
1997 if ( StringEqualsChar( sOldThousandSep, cSaved ) )
1999 // previous char with skip empty
2000 sal_Unicode cPre = PreviousChar(i);
2001 sal_Unicode cNext;
2002 if (bExp || bBlank || bFrac)
2004 // after E, / or ' '
2005 if ( !StringEqualsChar( sOldThousandSep, ' ' ) )
2007 nPos = nPos + sStrArray[i].getLength();
2008 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2009 nResultStringsCnt--;
2010 i++; // eat it
2012 else
2014 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2015 if ( bFrac && (nCounter > 0) )
2016 bDenomin = true; // end of denominator
2019 else if (i > 0 && i < nStringsCnt-1 &&
2020 (cPre == '#' || cPre == '0' || cPre == '?') &&
2021 ((cNext = NextChar(i)) == '#' || cNext == '0' || cNext == '?')) // #,#
2023 nPos = nPos + sStrArray[i].getLength();
2024 if (!bThousand) // only once
2026 bThousand = true;
2028 // Eat it, will be reinserted at proper grouping positions further down.
2029 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2030 nResultStringsCnt--;
2031 i++;
2033 else if (i > 0 && (cPre == '#' || cPre == '0' || cPre == '?')
2034 && PreviousType(i) == NF_SYMBOLTYPE_DIGIT
2035 && nThousand < FLAG_STANDARD_IN_FORMAT )
2036 { // #,,,,
2037 if ( StringEqualsChar( sOldThousandSep, ' ' ) )
2039 // strange, those French...
2040 bool bFirst = true;
2041 // set a hard No-Break Space or ConvertMode
2042 const OUString& rSepF = pFormatter->GetNumThousandSep();
2043 while ( i < nStringsCnt &&
2044 sStrArray[i] == sOldThousandSep &&
2045 StringEqualsChar( sOldThousandSep, NextChar(i) ) )
2046 { // last was a space or another space
2047 // is following => separator
2048 nPos = nPos + sStrArray[i].getLength();
2049 if ( bFirst )
2051 bFirst = false;
2052 rStr = rSepF;
2053 nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
2055 else
2057 rStr += rSepF;
2058 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2059 nResultStringsCnt--;
2061 nThousand++;
2062 i++;
2064 if ( i < nStringsCnt-1 &&
2065 sStrArray[i] == sOldThousandSep )
2067 // something following last space
2068 // => space if currency contained,
2069 // else separator
2070 nPos = nPos + sStrArray[i].getLength();
2071 if ( (nPos <= nCurrPos &&
2072 nCurrPos < nPos + sStrArray[i+1].getLength()) ||
2073 nTypeArray[i+1] == NF_KEY_CCC ||
2074 (i < nStringsCnt-2 &&
2075 sStrArray[i+1][0] == '[' &&
2076 sStrArray[i+2][0] == '$') )
2078 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2080 else
2082 if ( bFirst )
2084 rStr = rSepF;
2085 nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
2087 else
2089 rStr += rSepF;
2090 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2091 nResultStringsCnt--;
2093 nThousand++;
2095 i++;
2098 else
2102 nThousand++;
2103 nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
2104 nPos = nPos + sStrArray[i].getLength();
2105 sStrArray[i] = pFormatter->GetNumThousandSep();
2106 i++;
2108 while (i < nStringsCnt && sStrArray[i] == sOldThousandSep);
2111 else // any grsep
2113 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2114 nPos = nPos + rStr.getLength();
2115 i++;
2116 while ( i < nStringsCnt && sStrArray[i] == sOldThousandSep )
2118 rStr += sStrArray[i];
2119 nPos = nPos + sStrArray[i].getLength();
2120 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2121 nResultStringsCnt--;
2122 i++;
2126 else if ( StringEqualsChar( sOldDecSep, cSaved ) )
2128 if (bBlank || bFrac) // . behind / or ' '
2130 return nPos; // error
2132 else if (bExp) // behind E
2134 nPos = nPos + sStrArray[i].getLength();
2135 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2136 nResultStringsCnt--;
2137 i++; // eat it
2139 else if (bDecSep) // any .
2141 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2142 nPos = nPos + rStr.getLength();
2143 i++;
2144 while ( i < nStringsCnt && sStrArray[i] == sOldDecSep )
2146 rStr += sStrArray[i];
2147 nPos = nPos + sStrArray[i].getLength();
2148 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2149 nResultStringsCnt--;
2150 i++;
2153 else
2155 nPos = nPos + sStrArray[i].getLength();
2156 nTypeArray[i] = NF_SYMBOLTYPE_DECSEP;
2157 sStrArray[i] = pFormatter->GetNumDecimalSep();
2158 bDecSep = true;
2159 nDecPos = i;
2160 nCntPre = nCounter;
2161 nCounter = 0;
2163 i++;
2165 } // of else = DecSep
2166 else // . without meaning
2168 if (cSaved == ' ' &&
2169 eScannedType == SvNumFormatType::FRACTION &&
2170 StringEqualsChar( sStrArray[i], ' ' ) )
2172 if (!bBlank && !bFrac) // no dups
2173 { // or behind /
2174 if (bDecSep && nCounter > 0) // dec.
2176 return nPos; // error
2178 bBlank = true;
2179 nBlankPos = i;
2180 nCntPre = nCounter;
2181 nCounter = 0;
2183 if ( bFrac && (nCounter > 0) )
2184 bDenomin = true; // next content is not part of denominator
2185 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2186 nPos = nPos + sStrArray[i].getLength();
2188 else
2190 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2191 if ( bFrac && (nCounter > 0) )
2192 bDenomin = true; // next content is not part of denominator
2193 nPos = nPos + rStr.getLength();
2194 i++;
2195 while (i < nStringsCnt && StringEqualsChar( sStrArray[i], cSaved ) )
2197 rStr += sStrArray[i];
2198 nPos = nPos + sStrArray[i].getLength();
2199 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2200 nResultStringsCnt--;
2201 i++;
2205 break;
2206 case '/':
2207 if (eScannedType == SvNumFormatType::FRACTION)
2209 if ( i == 0 ||
2210 (nTypeArray[i-1] != NF_SYMBOLTYPE_DIGIT &&
2211 nTypeArray[i-1] != NF_SYMBOLTYPE_EMPTY) )
2213 return nPos ? nPos : 1; // /? not allowed
2215 else if (!bFrac || (bDecSep && nCounter > 0))
2217 bFrac = true;
2218 nCntPost = nCounter;
2219 nCounter = 0;
2220 nTypeArray[i] = NF_SYMBOLTYPE_FRAC;
2221 nPos = nPos + sStrArray[i].getLength();
2222 i++;
2224 else // / double or in , in the denominator
2226 return nPos; // Error
2229 else
2231 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2232 nPos = nPos + sStrArray[i].getLength();
2233 i++;
2235 break;
2236 case '[' :
2237 if ( eScannedType == SvNumFormatType::CURRENCY &&
2238 i < nStringsCnt-1 &&
2239 nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
2240 sStrArray[i+1][0] == '$' )
2242 // [$DM-xxx]
2243 nPos = nPos + sStrArray[i].getLength(); // [
2244 nTypeArray[i] = NF_SYMBOLTYPE_CURRDEL;
2245 nPos = nPos + sStrArray[++i].getLength(); // $
2246 sStrArray[i-1] += sStrArray[i]; // [$
2247 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2248 nResultStringsCnt--;
2249 if ( ++i >= nStringsCnt )
2251 return nPos; // Error
2253 nPos = nPos + sStrArray[i].getLength(); // DM
2254 OUString* pStr = &sStrArray[i];
2255 nTypeArray[i] = NF_SYMBOLTYPE_CURRENCY; // convert
2256 bool bHadDash = false;
2257 i++;
2258 while ( i < nStringsCnt && sStrArray[i][0] != ']' )
2260 nPos = nPos + sStrArray[i].getLength();
2261 if ( bHadDash )
2263 *pStr += sStrArray[i];
2264 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2265 nResultStringsCnt--;
2267 else
2269 if ( sStrArray[i][0] == '-' )
2271 bHadDash = true;
2272 pStr = &sStrArray[i];
2273 nTypeArray[i] = NF_SYMBOLTYPE_CURREXT;
2275 else
2277 *pStr += sStrArray[i];
2278 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2279 nResultStringsCnt--;
2282 i++;
2284 if ( rStr.getLength() && i < nStringsCnt && sStrArray[i][0] == ']' )
2286 nTypeArray[i] = NF_SYMBOLTYPE_CURRDEL;
2287 nPos = nPos + sStrArray[i].getLength();
2288 i++;
2290 else
2292 return nPos; // Error
2295 else
2297 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2298 nPos = nPos + sStrArray[i].getLength();
2299 i++;
2301 break;
2302 default: // Other Dels
2303 if (eScannedType == SvNumFormatType::PERCENT && cHere == '%')
2305 nTypeArray[i] = NF_SYMBOLTYPE_PERCENT;
2307 else
2309 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2311 nPos = nPos + sStrArray[i].getLength();
2312 i++;
2313 break;
2314 } // of switch (Del)
2315 } // of else Del
2316 else
2318 SAL_WARN( "svl.numbers", "unknown NF_SYMBOLTYPE_..." );
2319 nPos = nPos + sStrArray[i].getLength();
2320 i++;
2322 } // of while
2323 if (eScannedType == SvNumFormatType::FRACTION)
2325 if (bFrac)
2327 nCntExp = nCounter;
2329 else if (bBlank)
2331 nCntPost = nCounter;
2333 else
2335 nCntPre = nCounter;
2338 else
2340 if (bExp)
2342 nCntExp = nCounter;
2344 else if (bDecSep)
2346 nCntPost = nCounter;
2348 else
2350 nCntPre = nCounter;
2353 if (bThousand) // Expansion of grouping separators
2355 sal_uInt16 nMaxPos;
2356 if (bFrac)
2358 if (bBlank)
2360 nMaxPos = nBlankPos;
2362 else
2364 nMaxPos = 0; // no grouping
2367 else if (bDecSep) // decimal separator present
2369 nMaxPos = nDecPos;
2371 else if (bExp) // 'E' exponent present
2373 nMaxPos = nExpPos;
2375 else // up to end
2377 nMaxPos = i;
2379 // Insert separators at proper positions.
2380 sal_Int32 nCount = 0;
2381 utl::DigitGroupingIterator aGrouping( pLoc->getDigitGrouping());
2382 size_t nFirstDigitSymbol = nMaxPos;
2383 size_t nFirstGroupingSymbol = nMaxPos;
2384 i = nMaxPos;
2385 while (i-- > 0)
2387 if (nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
2389 nFirstDigitSymbol = i;
2390 nCount = nCount + sStrArray[i].getLength(); // MSC converts += to int and then warns, so ...
2391 // Insert separator only if not leftmost symbol.
2392 if (i > 0 && nCount >= aGrouping.getPos())
2394 DBG_ASSERT( sStrArray[i].getLength() == 1,
2395 "ImpSvNumberformatScan::FinalScan: combined digits in group separator insertion");
2396 if (!InsertSymbol( i, NF_SYMBOLTYPE_THSEP, pFormatter->GetNumThousandSep()))
2398 // nPos isn't correct here, but signals error
2399 return nPos;
2401 // i may have been decremented by 1
2402 nFirstDigitSymbol = i + 1;
2403 nFirstGroupingSymbol = i;
2404 aGrouping.advance();
2408 // Generated something like "string",000; remove separator again.
2409 if (nFirstGroupingSymbol < nFirstDigitSymbol)
2411 nTypeArray[nFirstGroupingSymbol] = NF_SYMBOLTYPE_EMPTY;
2412 nResultStringsCnt--;
2415 // Combine digits into groups to save memory (Info will be copied
2416 // later, taking only non-empty symbols).
2417 for (i = 0; i < nStringsCnt; ++i)
2419 if (nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
2421 OUString& rStr = sStrArray[i];
2422 while (++i < nStringsCnt && nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
2424 rStr += sStrArray[i];
2425 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2426 nResultStringsCnt--;
2430 break; // of SvNumFormatType::NUMBER
2431 case SvNumFormatType::DATE:
2432 while (i < nStringsCnt)
2434 switch (nTypeArray[i])
2436 case NF_SYMBOLTYPE_BLANK:
2437 case NF_SYMBOLTYPE_STAR:
2438 case NF_SYMBOLTYPE_STRING:
2439 nPos = nPos + sStrArray[i].getLength();
2440 i++;
2441 break;
2442 case NF_SYMBOLTYPE_DEL:
2443 int nCalRet;
2444 if (sStrArray[i] == sOldDateSep)
2446 nTypeArray[i] = NF_SYMBOLTYPE_DATESEP;
2447 nPos = nPos + sStrArray[i].getLength();
2448 if (bConvertMode)
2450 sStrArray[i] = pFormatter->GetDateSep();
2452 i++;
2454 else if ( (nCalRet = FinalScanGetCalendar( nPos, i, nResultStringsCnt )) != 0 )
2456 if ( nCalRet < 0 )
2458 return nPos; // error
2461 else
2463 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2464 nPos = nPos + sStrArray[i].getLength();
2465 i++;
2467 break;
2468 case NF_KEY_THAI_T :
2469 bThaiT = true;
2470 [[fallthrough]];
2471 case NF_KEY_M: // M
2472 case NF_KEY_MM: // MM
2473 case NF_KEY_MMM: // MMM
2474 case NF_KEY_MMMM: // MMMM
2475 case NF_KEY_MMMMM: // MMMMM
2476 case NF_KEY_Q: // Q
2477 case NF_KEY_QQ: // QQ
2478 case NF_KEY_D: // D
2479 case NF_KEY_DD: // DD
2480 case NF_KEY_DDD: // DDD
2481 case NF_KEY_DDDD: // DDDD
2482 case NF_KEY_YY: // YY
2483 case NF_KEY_YYYY: // YYYY
2484 case NF_KEY_NN: // NN
2485 case NF_KEY_NNN: // NNN
2486 case NF_KEY_NNNN: // NNNN
2487 case NF_KEY_WW : // WW
2488 case NF_KEY_AAA : // AAA
2489 case NF_KEY_AAAA : // AAAA
2490 case NF_KEY_EC : // E
2491 case NF_KEY_EEC : // EE
2492 case NF_KEY_G : // G
2493 case NF_KEY_GG : // GG
2494 case NF_KEY_GGG : // GGG
2495 case NF_KEY_R : // R
2496 case NF_KEY_RR : // RR
2497 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2498 nPos = nPos + sStrArray[i].getLength();
2499 if (bNewDateOrder)
2501 // For simple numeric date formats record date order and
2502 // later rearrange.
2503 switch (nTypeArray[i])
2505 case NF_KEY_M:
2506 case NF_KEY_MM:
2507 if (nMonthPos == SAL_MAX_UINT16)
2508 nMonthPos = i;
2509 else
2510 bNewDateOrder = false;
2511 break;
2512 case NF_KEY_D:
2513 case NF_KEY_DD:
2514 if (nDayPos == SAL_MAX_UINT16)
2515 nDayPos = i;
2516 else
2517 bNewDateOrder = false;
2518 break;
2519 case NF_KEY_YY:
2520 case NF_KEY_YYYY:
2521 if (nYearPos == SAL_MAX_UINT16)
2522 nYearPos = i;
2523 else
2524 bNewDateOrder = false;
2525 break;
2526 default:
2527 ; // nothing
2530 i++;
2531 break;
2532 default: // Other keywords
2533 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2534 nPos = nPos + sStrArray[i].getLength();
2535 i++;
2536 break;
2538 } // of while
2539 break; // of SvNumFormatType::DATE
2540 case SvNumFormatType::TIME:
2541 while (i < nStringsCnt)
2543 sal_Unicode cChar;
2545 switch (nTypeArray[i])
2547 case NF_SYMBOLTYPE_BLANK:
2548 case NF_SYMBOLTYPE_STAR:
2549 nPos = nPos + sStrArray[i].getLength();
2550 i++;
2551 break;
2552 case NF_SYMBOLTYPE_DEL:
2553 switch( sStrArray[i][0] )
2555 case '0':
2556 if ( Is100SecZero( i, bDecSep ) )
2558 bDecSep = true;
2559 nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
2560 OUString& rStr = sStrArray[i];
2561 nCounter++;
2562 i++;
2563 while (i < nStringsCnt &&
2564 sStrArray[i][0] == '0')
2566 rStr += sStrArray[i];
2567 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2568 nResultStringsCnt--;
2569 nCounter++;
2570 i++;
2572 nPos += rStr.getLength();
2574 else
2576 return nPos;
2578 break;
2579 case '#':
2580 case '?':
2581 return nPos;
2582 case '[':
2583 if (bThousand) // Double
2585 return nPos;
2587 bThousand = true; // Empty for Time
2588 cChar = pChrCls->uppercase(OUString(NextChar(i)))[0];
2589 if ( cChar == cOldKeyH )
2591 nThousand = 1; // H
2593 else if ( cChar == cOldKeyMI )
2595 nThousand = 2; // M
2597 else if ( cChar == cOldKeyS )
2599 nThousand = 3; // S
2601 else
2603 return nPos;
2605 nPos = nPos + sStrArray[i].getLength();
2606 i++;
2607 break;
2608 case ']':
2609 if (!bThousand) // No preceding [
2611 return nPos;
2613 nPos = nPos + sStrArray[i].getLength();
2614 i++;
2615 break;
2616 default:
2617 nPos = nPos + sStrArray[i].getLength();
2618 if ( sStrArray[i] == sOldTimeSep )
2620 nTypeArray[i] = NF_SYMBOLTYPE_TIMESEP;
2621 if ( bConvertMode )
2623 sStrArray[i] = pLoc->getTimeSep();
2626 else if ( sStrArray[i] == sOldTime100SecSep )
2628 bDecSep = true;
2629 nTypeArray[i] = NF_SYMBOLTYPE_TIME100SECSEP;
2630 if ( bConvertMode )
2632 sStrArray[i] = pLoc->getTime100SecSep();
2635 else
2637 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2639 i++;
2640 break;
2642 break;
2643 case NF_SYMBOLTYPE_STRING:
2644 nPos = nPos + sStrArray[i].getLength();
2645 i++;
2646 break;
2647 case NF_KEY_AMPM: // AM/PM
2648 case NF_KEY_AP: // A/P
2649 bExp = true; // Abuse for A/P
2650 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2651 nPos = nPos + sStrArray[i].getLength();
2652 i++;
2653 break;
2654 case NF_KEY_THAI_T :
2655 bThaiT = true;
2656 [[fallthrough]];
2657 case NF_KEY_MI: // M
2658 case NF_KEY_MMI: // MM
2659 case NF_KEY_H: // H
2660 case NF_KEY_HH: // HH
2661 case NF_KEY_S: // S
2662 case NF_KEY_SS: // SS
2663 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2664 nPos = nPos + sStrArray[i].getLength();
2665 i++;
2666 break;
2667 default: // Other keywords
2668 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2669 nPos = nPos + sStrArray[i].getLength();
2670 i++;
2671 break;
2673 } // of while
2674 nCntPost = nCounter; // Zero counter
2675 if (bExp)
2677 nCntExp = 1; // Remembers AM/PM
2679 break; // of SvNumFormatType::TIME
2680 case SvNumFormatType::DATETIME:
2681 while (i < nStringsCnt)
2683 int nCalRet;
2684 switch (nTypeArray[i])
2686 case NF_SYMBOLTYPE_BLANK:
2687 case NF_SYMBOLTYPE_STAR:
2688 case NF_SYMBOLTYPE_STRING:
2689 nPos = nPos + sStrArray[i].getLength();
2690 i++;
2691 break;
2692 case NF_SYMBOLTYPE_DEL:
2693 if ( (nCalRet = FinalScanGetCalendar( nPos, i, nResultStringsCnt )) != 0 )
2695 if ( nCalRet < 0 )
2697 return nPos; // Error
2700 else
2702 switch( sStrArray[i][0] )
2704 case '0':
2705 if (bTimePart && Is100SecZero(i, bDecSep) && nCounter < MaxCntPost)
2707 bDecSep = true;
2708 nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
2709 OUString& rStr = sStrArray[i];
2710 nCounter++;
2711 i++;
2712 while (i < nStringsCnt &&
2713 sStrArray[i][0] == '0' && nCounter < MaxCntPost)
2715 rStr += sStrArray[i];
2716 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2717 nResultStringsCnt--;
2718 nCounter++;
2719 i++;
2721 nPos += rStr.getLength();
2723 else
2725 return nPos;
2727 break;
2728 case '#':
2729 case '?':
2730 return nPos;
2731 default:
2732 nPos = nPos + sStrArray[i].getLength();
2733 if (bTimePart)
2735 if ( sStrArray[i] == sOldTimeSep )
2737 nTypeArray[i] = NF_SYMBOLTYPE_TIMESEP;
2738 if ( bConvertMode )
2740 sStrArray[i] = pLoc->getTimeSep();
2743 else if ( sStrArray[i] == sOldTime100SecSep )
2745 bDecSep = true;
2746 nTypeArray[i] = NF_SYMBOLTYPE_TIME100SECSEP;
2747 if ( bConvertMode )
2749 sStrArray[i] = pLoc->getTime100SecSep();
2752 else
2754 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2757 else
2759 if ( sStrArray[i] == sOldDateSep )
2761 nTypeArray[i] = NF_SYMBOLTYPE_DATESEP;
2762 if (bConvertMode)
2763 sStrArray[i] = pFormatter->GetDateSep();
2765 else
2767 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2770 i++;
2771 break;
2774 break;
2775 case NF_KEY_AMPM: // AM/PM
2776 case NF_KEY_AP: // A/P
2777 bTimePart = true;
2778 bExp = true; // Abuse for A/P
2779 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2780 nPos = nPos + sStrArray[i].getLength();
2781 i++;
2782 break;
2783 case NF_KEY_MI: // M
2784 case NF_KEY_MMI: // MM
2785 case NF_KEY_H: // H
2786 case NF_KEY_HH: // HH
2787 case NF_KEY_S: // S
2788 case NF_KEY_SS: // SS
2789 bTimePart = true;
2790 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2791 nPos = nPos + sStrArray[i].getLength();
2792 i++;
2793 break;
2794 case NF_KEY_M: // M
2795 case NF_KEY_MM: // MM
2796 case NF_KEY_MMM: // MMM
2797 case NF_KEY_MMMM: // MMMM
2798 case NF_KEY_MMMMM: // MMMMM
2799 case NF_KEY_Q: // Q
2800 case NF_KEY_QQ: // QQ
2801 case NF_KEY_D: // D
2802 case NF_KEY_DD: // DD
2803 case NF_KEY_DDD: // DDD
2804 case NF_KEY_DDDD: // DDDD
2805 case NF_KEY_YY: // YY
2806 case NF_KEY_YYYY: // YYYY
2807 case NF_KEY_NN: // NN
2808 case NF_KEY_NNN: // NNN
2809 case NF_KEY_NNNN: // NNNN
2810 case NF_KEY_WW : // WW
2811 case NF_KEY_AAA : // AAA
2812 case NF_KEY_AAAA : // AAAA
2813 case NF_KEY_EC : // E
2814 case NF_KEY_EEC : // EE
2815 case NF_KEY_G : // G
2816 case NF_KEY_GG : // GG
2817 case NF_KEY_GGG : // GGG
2818 case NF_KEY_R : // R
2819 case NF_KEY_RR : // RR
2820 bTimePart = false;
2821 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2822 nPos = nPos + sStrArray[i].getLength();
2823 if (bNewDateOrder)
2825 // For simple numeric date formats record date order and
2826 // later rearrange.
2827 switch (nTypeArray[i])
2829 case NF_KEY_M:
2830 case NF_KEY_MM:
2831 if (nMonthPos == SAL_MAX_UINT16)
2832 nMonthPos = i;
2833 else
2834 bNewDateOrder = false;
2835 break;
2836 case NF_KEY_D:
2837 case NF_KEY_DD:
2838 if (nDayPos == SAL_MAX_UINT16)
2839 nDayPos = i;
2840 else
2841 bNewDateOrder = false;
2842 break;
2843 case NF_KEY_YY:
2844 case NF_KEY_YYYY:
2845 if (nYearPos == SAL_MAX_UINT16)
2846 nYearPos = i;
2847 else
2848 bNewDateOrder = false;
2849 break;
2850 default:
2851 ; // nothing
2854 i++;
2855 break;
2856 case NF_KEY_THAI_T :
2857 bThaiT = true;
2858 sStrArray[i] = sKeyword[nTypeArray[i]];
2859 nPos = nPos + sStrArray[i].getLength();
2860 i++;
2861 break;
2862 default: // Other keywords
2863 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2864 nPos = nPos + sStrArray[i].getLength();
2865 i++;
2866 break;
2868 } // of while
2869 nCntPost = nCounter; // decimals (100th seconds)
2870 if (bExp)
2872 nCntExp = 1; // Remembers AM/PM
2874 break; // of SvNumFormatType::DATETIME
2875 default:
2876 break;
2878 if (eScannedType == SvNumFormatType::SCIENTIFIC &&
2879 (nCntPre + nCntPost == 0 || nCntExp == 0))
2881 return nPos;
2883 else if (eScannedType == SvNumFormatType::FRACTION && (nCntExp > 8 || nCntExp == 0))
2885 return nPos;
2887 if (bThaiT && !GetNatNumModifier())
2889 SetNatNumModifier(1);
2891 if ( bConvertMode )
2893 if (bNewDateOrder && sOldDateSep == "-")
2895 // Keep ISO formats Y-M-D, Y-M and M-D
2896 if (IsDateFragment( nYearPos, nMonthPos))
2898 nTypeArray[nYearPos+1] = NF_SYMBOLTYPE_STRING;
2899 sStrArray[nYearPos+1] = sOldDateSep;
2900 bNewDateOrder = false;
2902 if (IsDateFragment( nMonthPos, nDayPos))
2904 nTypeArray[nMonthPos+1] = NF_SYMBOLTYPE_STRING;
2905 sStrArray[nMonthPos+1] = sOldDateSep;
2906 bNewDateOrder = false;
2909 if (bNewDateOrder)
2911 // Rearrange date order to the target locale if the original order
2912 // includes date separators and is adjacent.
2913 /* TODO: for incomplete dates trailing separators need to be
2914 * handled according to the locale's usage, e.g. en-US M/D should
2915 * be converted to de-DE D.M. and vice versa. As is, it's
2916 * M/D -> D.M and D.M. -> M/D/ where specifically the latter looks
2917 * odd. Check accepted date patterns and append/remove? */
2918 switch (eOldDateOrder)
2920 case DateOrder::DMY:
2921 switch (pLoc->getDateOrder())
2923 case DateOrder::MDY:
2924 // Convert only if the actual format is not of YDM
2925 // order (which would be a completely unusual order
2926 // anyway, but..), e.g. YYYY.DD.MM not to
2927 // YYYY/MM/DD
2928 if (IsDateFragment( nDayPos, nMonthPos) && !IsDateFragment( nYearPos, nDayPos))
2929 SwapArrayElements( nDayPos, nMonthPos);
2930 break;
2931 case DateOrder::YMD:
2932 if (nYearPos != SAL_MAX_UINT16)
2934 if (IsDateFragment( nDayPos, nMonthPos) && IsDateFragment( nMonthPos, nYearPos))
2935 SwapArrayElements( nDayPos, nYearPos);
2937 else
2939 if (IsDateFragment( nDayPos, nMonthPos))
2940 SwapArrayElements( nDayPos, nMonthPos);
2942 break;
2943 default:
2944 ; // nothing
2946 break;
2947 case DateOrder::MDY:
2948 switch (pLoc->getDateOrder())
2950 case DateOrder::DMY:
2951 // Convert only if the actual format is not of YMD
2952 // order, e.g. YYYY/MM/DD not to YYYY.DD.MM
2953 /* TODO: convert such to DD.MM.YYYY instead? */
2954 if (IsDateFragment( nMonthPos, nDayPos) && !IsDateFragment( nYearPos, nMonthPos))
2955 SwapArrayElements( nMonthPos, nDayPos);
2956 break;
2957 case DateOrder::YMD:
2958 if (nYearPos != SAL_MAX_UINT16)
2960 if (IsDateFragment( nMonthPos, nDayPos) && IsDateFragment( nDayPos, nYearPos))
2962 SwapArrayElements( nYearPos, nMonthPos); // YDM
2963 SwapArrayElements( nYearPos, nDayPos); // YMD
2966 break;
2967 default:
2968 ; // nothing
2970 break;
2971 case DateOrder::YMD:
2972 switch (pLoc->getDateOrder())
2974 case DateOrder::DMY:
2975 if (nYearPos != SAL_MAX_UINT16)
2977 if (IsDateFragment( nYearPos, nMonthPos) && IsDateFragment( nMonthPos, nDayPos))
2978 SwapArrayElements( nYearPos, nDayPos);
2980 else
2982 if (IsDateFragment( nMonthPos, nDayPos))
2983 SwapArrayElements( nMonthPos, nDayPos);
2985 break;
2986 case DateOrder::MDY:
2987 if (nYearPos != SAL_MAX_UINT16)
2989 if (IsDateFragment( nYearPos, nMonthPos) && IsDateFragment( nMonthPos, nDayPos))
2991 SwapArrayElements( nYearPos, nDayPos); // DMY
2992 SwapArrayElements( nYearPos, nMonthPos); // MDY
2995 break;
2996 default:
2997 ; // nothing
2999 break;
3000 default:
3001 ; // nothing
3004 // strings containing keywords of the target locale must be quoted, so
3005 // the user sees the difference and is able to edit the format string
3006 for ( i=0; i < nStringsCnt; i++ )
3008 if ( nTypeArray[i] == NF_SYMBOLTYPE_STRING &&
3009 sStrArray[i][0] != '\"' )
3011 if ( bConvertSystemToSystem && eScannedType == SvNumFormatType::CURRENCY )
3013 // don't stringize automatic currency, will be converted
3014 if ( sStrArray[i] == sOldCurSymbol )
3016 continue; // for
3018 // DM might be split into D and M
3019 if ( sStrArray[i].getLength() < sOldCurSymbol.getLength() &&
3020 pChrCls->uppercase( sStrArray[i], 0, 1 )[0] ==
3021 sOldCurString[0] )
3023 OUString aTmp( sStrArray[i] );
3024 sal_uInt16 j = i + 1;
3025 while ( aTmp.getLength() < sOldCurSymbol.getLength() &&
3026 j < nStringsCnt &&
3027 nTypeArray[j] == NF_SYMBOLTYPE_STRING )
3029 aTmp += sStrArray[j++];
3031 if ( pChrCls->uppercase( aTmp ) == sOldCurString )
3033 sStrArray[i++] = aTmp;
3034 for ( ; i<j; i++ )
3036 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
3037 nResultStringsCnt--;
3039 i = j - 1;
3040 continue; // for
3044 OUString& rStr = sStrArray[i];
3045 sal_Int32 nLen = rStr.getLength();
3046 for ( sal_Int32 j = 0; j < nLen; j++ )
3048 bool bFoundEnglish = false;
3049 if ( (j == 0 || rStr[j - 1] != '\\') && GetKeyWord( rStr, j, bFoundEnglish) )
3051 rStr = "\"" + rStr + "\"";
3052 break; // for
3058 // Concatenate strings, remove quotes for output, and rebuild the format string
3059 rString.clear();
3060 i = 0;
3061 while (i < nStringsCnt)
3063 sal_Int32 nStringPos;
3064 sal_Int32 nArrPos = 0;
3065 sal_uInt16 iPos = i;
3066 switch ( nTypeArray[i] )
3068 case NF_SYMBOLTYPE_STRING :
3069 case NF_SYMBOLTYPE_FRACBLANK :
3070 nStringPos = rString.getLength();
3073 if (sStrArray[i].getLength() == 2 &&
3074 sStrArray[i][0] == '\\')
3076 // Unescape some simple forms of symbols even in the UI
3077 // visible string to prevent duplicates that differ
3078 // only in notation, originating from import.
3079 // e.g. YYYY-MM-DD and YYYY\-MM\-DD are identical,
3080 // but 0\ 000 0 and 0 000 0 in a French locale are not.
3082 sal_Unicode c = sStrArray[i][1];
3084 switch (c)
3086 case '+':
3087 case '-':
3088 rString += OUStringChar(c);
3089 break;
3090 case ' ':
3091 case '.':
3092 case '/':
3093 if (!(eScannedType & SvNumFormatType::DATE) &&
3094 (StringEqualsChar( pFormatter->GetNumThousandSep(), c) ||
3095 StringEqualsChar( pFormatter->GetNumDecimalSep(), c) ||
3096 (c == ' ' &&
3097 (StringEqualsChar( pFormatter->GetNumThousandSep(), cNoBreakSpace) ||
3098 StringEqualsChar( pFormatter->GetNumThousandSep(), cNarrowNoBreakSpace)))))
3100 rString += sStrArray[i];
3102 else if ((eScannedType & SvNumFormatType::DATE) &&
3103 StringEqualsChar( pFormatter->GetDateSep(), c))
3105 rString += sStrArray[i];
3107 else if ((eScannedType & SvNumFormatType::TIME) &&
3108 (StringEqualsChar( pLoc->getTimeSep(), c) ||
3109 StringEqualsChar( pLoc->getTime100SecSep(), c)))
3111 rString += sStrArray[i];
3113 else if (eScannedType & SvNumFormatType::FRACTION)
3115 rString += sStrArray[i];
3117 else
3119 rString += OUStringChar(c);
3121 break;
3122 default:
3123 rString += sStrArray[i];
3126 else
3128 rString += sStrArray[i];
3130 if ( RemoveQuotes( sStrArray[i] ) > 0 )
3132 // update currency up to quoted string
3133 if ( eScannedType == SvNumFormatType::CURRENCY )
3135 // dM -> DM or DM -> $ in old automatic
3136 // currency formats, oh my ..., why did we ever introduce them?
3137 OUString aTmp( pChrCls->uppercase( sStrArray[iPos], nArrPos,
3138 sStrArray[iPos].getLength()-nArrPos ) );
3139 sal_Int32 nCPos = aTmp.indexOf( sOldCurString );
3140 if ( nCPos >= 0 )
3142 const OUString& rCur = bConvertMode && bConvertSystemToSystem ?
3143 GetCurSymbol() : sOldCurSymbol;
3144 sStrArray[iPos] = sStrArray[iPos].replaceAt( nArrPos + nCPos,
3145 sOldCurString.getLength(),
3146 rCur );
3147 rString = rString.replaceAt( nStringPos + nCPos,
3148 sOldCurString.getLength(),
3149 rCur );
3151 nStringPos = rString.getLength();
3152 if ( iPos == i )
3154 nArrPos = sStrArray[iPos].getLength();
3156 else
3158 nArrPos = sStrArray[iPos].getLength() + sStrArray[i].getLength();
3162 if ( iPos != i )
3164 sStrArray[iPos] += sStrArray[i];
3165 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
3166 nResultStringsCnt--;
3168 i++;
3170 while ( i < nStringsCnt && nTypeArray[i] == NF_SYMBOLTYPE_STRING );
3172 if ( i < nStringsCnt )
3174 i--; // enter switch on next symbol again
3176 if ( eScannedType == SvNumFormatType::CURRENCY && nStringPos < rString.getLength() )
3178 // same as above, since last RemoveQuotes
3179 OUString aTmp( pChrCls->uppercase( sStrArray[iPos], nArrPos,
3180 sStrArray[iPos].getLength()-nArrPos ) );
3181 sal_Int32 nCPos = aTmp.indexOf( sOldCurString );
3182 if ( nCPos >= 0 )
3184 const OUString& rCur = bConvertMode && bConvertSystemToSystem ?
3185 GetCurSymbol() : sOldCurSymbol;
3186 sStrArray[iPos] = sStrArray[iPos].replaceAt( nArrPos + nCPos,
3187 sOldCurString.getLength(),
3188 rCur );
3189 rString = rString.replaceAt( nStringPos + nCPos,
3190 sOldCurString.getLength(), rCur );
3193 break;
3194 case NF_SYMBOLTYPE_CURRENCY :
3195 rString += sStrArray[i];
3196 RemoveQuotes( sStrArray[i] );
3197 break;
3198 case NF_KEY_THAI_T:
3199 if (bThaiT && GetNatNumModifier() == 1)
3201 // Remove T from format code, will be replaced with a [NatNum1] prefix.
3202 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
3203 nResultStringsCnt--;
3205 else
3207 rString += sStrArray[i];
3209 break;
3210 case NF_SYMBOLTYPE_EMPTY :
3211 // nothing
3212 break;
3213 default:
3214 rString += sStrArray[i];
3216 i++;
3218 return 0;
3221 sal_Int32 ImpSvNumberformatScan::RemoveQuotes( OUString& rStr )
3223 if ( rStr.getLength() > 1 )
3225 sal_Unicode c = rStr[0];
3226 sal_Int32 n = rStr.getLength() - 1;
3227 if ( c == '"' && rStr[n] == '"' )
3229 rStr = rStr.copy( 1, n-1);
3230 return 2;
3232 else if ( c == '\\' )
3234 rStr = rStr.copy(1);
3235 return 1;
3238 return 0;
3241 sal_Int32 ImpSvNumberformatScan::ScanFormat( OUString& rString )
3243 sal_Int32 res = Symbol_Division(rString); // Lexical analysis
3244 if (!res)
3246 res = ScanType(); // Recognizing the Format type
3248 if (!res)
3250 res = FinalScan( rString ); // Type dependent final analysis
3252 return res; // res = control position; res = 0 => Format ok
3255 void ImpSvNumberformatScan::CopyInfo(ImpSvNumberformatInfo* pInfo, sal_uInt16 nCnt)
3257 size_t i,j;
3258 j = 0;
3259 i = 0;
3260 while (i < nCnt && j < NF_MAX_FORMAT_SYMBOLS)
3262 if (nTypeArray[j] != NF_SYMBOLTYPE_EMPTY)
3264 pInfo->sStrArray[i] = sStrArray[j];
3265 pInfo->nTypeArray[i] = nTypeArray[j];
3266 i++;
3268 j++;
3270 pInfo->eScannedType = eScannedType;
3271 pInfo->bThousand = bThousand;
3272 pInfo->nThousand = nThousand;
3273 pInfo->nCntPre = nCntPre;
3274 pInfo->nCntPost = nCntPost;
3275 pInfo->nCntExp = nCntExp;
3278 void ImpSvNumberformatScan::ReplaceBooleanEquivalent( OUString& rString )
3280 InitKeywords();
3281 /* TODO: compare case insensitive? Or rather leave as is and case not
3282 * matching indicates user supplied on purpose? Written to file / generated
3283 * was always uppercase. */
3284 if (rString == sBooleanEquivalent1 || rString == sBooleanEquivalent2)
3285 rString = GetKeywords()[NF_KEY_BOOLEAN];
3288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */