LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / svl / source / numbers / zforscan.cxx
bloba5f25637678a91ed982ef1268fe16b2debd94261
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>
31 #include <svl/zforlist.hxx>
32 #include <svl/zformat.hxx>
33 #include <unotools/digitgroupingiterator.hxx>
35 #include "zforscan.hxx"
37 #include <svl/nfsymbol.hxx>
38 using namespace svt;
40 const sal_Unicode cNoBreakSpace = 0xA0;
41 const sal_Unicode cNarrowNoBreakSpace = 0x202F;
43 const int MaxCntPost = 20; //max dec places allow by rtl_math_round
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
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
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
113 // This vector will hold *only* the color names in German language.
114 static const std::u16string_view& GermanColorName(size_t i)
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];
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)
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
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");
144 nStandardPrec = 2;
146 Reset();
149 ImpSvNumberformatScan::~ImpSvNumberformatScan()
151 Reset();
154 void ImpSvNumberformatScan::ChangeIntl( KeywordLocalization eKeywordLocalization )
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();
164 void ImpSvNumberformatScan::InitSpecialKeyword( NfKeywordIndex eIdx ) const
166 switch ( eIdx )
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() )
173 SAL_WARN( "svl.numbers", "InitSpecialKeyword: TRUE_WORD?" );
174 const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_TRUE] = sEnglishKeyword[NF_KEY_TRUE];
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() )
182 SAL_WARN( "svl.numbers", "InitSpecialKeyword: FALSE_WORD?" );
183 const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_FALSE] = sEnglishKeyword[NF_KEY_FALSE];
185 break;
186 default:
187 SAL_WARN( "svl.numbers", "InitSpecialKeyword: unknown request" );
191 void ImpSvNumberformatScan::InitCompatCur() const
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;
201 void ImpSvNumberformatScan::InitKeywords() const
203 if ( !bKeywordsNeedInit )
204 return ;
205 const_cast<ImpSvNumberformatScan*>(this)->SetDependentKeywords();
206 bKeywordsNeedInit = false;
209 /** Extract the name of General, Standard, Whatever, ignoring leading modifiers
210 such as [NatNum1]. */
211 static OUString lcl_extractStandardGeneralName( const OUString & rCode )
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)
221 switch (*p)
223 case '[':
224 bMod = true;
225 break;
226 case ']':
227 if (bMod)
229 bMod = false;
230 pBeg = p+1;
232 // else: would be a locale data error, easily to be spotted in
233 // UI dialog
234 break;
235 case ';':
236 if (!bMod)
238 bDone = true;
239 --p; // put back, increment by one follows
241 break;
243 ++p;
244 if (bMod)
246 pBeg = p;
249 if (pBeg < p)
251 aStr = rCode.copy( pBeg - rCode.getStr(), p - pBeg);
253 return aStr;
256 void ImpSvNumberformatScan::SetDependentKeywords()
258 using namespace ::com::sun::star;
259 using namespace ::com::sun::star::uno;
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);
268 bool bL10n = (meKeywordLocalization != KeywordLocalization::EnglishOnly);
269 if (bL10n)
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 ))
318 bL10n = false;
319 meKeywordLocalization = KeywordLocalization::EnglishOnly;
323 // Init the current NfKeywordTable with English keywords.
324 sKeyword = sEnglishKeyword;
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 );
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)
335 sKeyword[NF_KEY_THAI_T] = "T";
337 else
339 sKeyword[NF_KEY_THAI_T] = sEnglishKeyword[NF_KEY_THAI_T];
342 // boolean keywords
343 InitSpecialKeyword( NF_KEY_TRUE );
344 InitSpecialKeyword( NF_KEY_FALSE );
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] + "\"";
356 // compatibility currency strings
357 InitCompatCur();
359 if (!bL10n)
360 return;
362 // All locale dependent keywords overrides follow.
364 if ( eLang.anyOf(
365 LANGUAGE_GERMAN,
366 LANGUAGE_GERMAN_SWISS,
367 LANGUAGE_GERMAN_AUSTRIAN,
368 LANGUAGE_GERMAN_LUXEMBOURG,
369 LANGUAGE_GERMAN_LIECHTENSTEIN))
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);
398 else
400 // day
401 if ( eLang.anyOf(
402 LANGUAGE_ITALIAN,
403 LANGUAGE_ITALIAN_SWISS))
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";
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))
422 sKeyword[NF_KEY_D] = "J";
423 sKeyword[NF_KEY_DD] = "JJ";
424 sKeyword[NF_KEY_DDD] = "JJJ";
425 sKeyword[NF_KEY_DDDD] = "JJJJ";
427 else if ( eLang == LANGUAGE_FINNISH )
429 sKeyword[NF_KEY_D] = "P";
430 sKeyword[NF_KEY_DD] = "PP";
431 sKeyword[NF_KEY_DDD] = "PPP";
432 sKeyword[NF_KEY_DDDD] = "PPPP";
435 // month
436 if ( eLang == LANGUAGE_FINNISH )
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";
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))
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";
484 else if ( eLang.anyOf(
485 LANGUAGE_DUTCH,
486 LANGUAGE_DUTCH_BELGIAN))
488 sKeyword[NF_KEY_YY] = "JJ";
489 sKeyword[NF_KEY_YYYY] = "JJJJ";
491 else if ( eLang == LANGUAGE_FINNISH )
493 sKeyword[NF_KEY_YY] = "VV";
494 sKeyword[NF_KEY_YYYY] = "VVVV";
497 // hour
498 if ( eLang.anyOf(
499 LANGUAGE_DUTCH,
500 LANGUAGE_DUTCH_BELGIAN))
502 sKeyword[NF_KEY_H] = "U";
503 sKeyword[NF_KEY_HH] = "UU";
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))
514 sKeyword[NF_KEY_H] = "T";
515 sKeyword[NF_KEY_HH] = "TT";
520 void ImpSvNumberformatScan::ChangeNullDate(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
522 maNullDate = Date(nDay, nMonth, nYear);
523 if (!maNullDate.IsValidDate())
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());
532 void ImpSvNumberformatScan::ChangeStandardPrec(sal_uInt16 nPrec)
534 nStandardPrec = nPrec;
537 const Color* ImpSvNumberformatScan::GetColor(OUString& sStr) const
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] )
544 i++;
546 if (i >= NF_MAX_DEFAULT_COLORS && meKeywordLocalization == KeywordLocalization::AllowEnglish)
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
556 size_t j = 0;
557 while ( j < NF_MAX_DEFAULT_COLORS && sString != sEnglishKeyword[NF_KEY_FIRSTCOLOR + j] )
559 ++j;
561 if ( j < NF_MAX_DEFAULT_COLORS )
563 i = j;
568 enum ColorKeywordConversion
570 None,
571 GermanToEnglish,
572 EnglishToGerman
573 } eColorKeywordConversion(None);
575 if (bConvertMode)
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;
595 const Color* pResult = nullptr;
596 if (i >= NF_MAX_DEFAULT_COLORS)
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])))
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)
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;
619 sString = sString.copy(nPos);
620 sString = comphelper::string::strip(sString, ' ');
622 if ( CharClass::isAsciiNumeric( sString ) )
624 sal_Int32 nIndex = sString.toInt32();
625 if (nIndex > 0 && nIndex <= 64)
627 pResult = pFormatter->GetUserDefColor(static_cast<sal_uInt16>(nIndex)-1);
632 else
634 sStr.clear();
635 switch (eColorKeywordConversion)
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;
647 pResult = &(StandardColor[i]);
649 return pResult;
652 short ImpSvNumberformatScan::GetKeyWord( const OUString& sSymbol, sal_Int32 nPos, bool& rbFoundEnglish ) const
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] ))
659 return NF_KEY_GENERAL;
661 if ((meKeywordLocalization == KeywordLocalization::AllowEnglish) &&
662 sString.startsWith( sEnglishKeyword[NF_KEY_GENERAL]))
664 rbFoundEnglish = true;
665 return NF_KEY_GENERAL;
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]))
674 i--;
676 if (i == 0 && meKeywordLocalization == KeywordLocalization::AllowEnglish)
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]))
683 i--;
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)
693 i = NF_KEY_THAI_T;
695 return i; // 0 => not found
699 * Next_Symbol
701 * Splits up the input for further processing (by the Turing machine).
703 * Starting state = SsStar
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 * ---------------------------------------------------------------+--------------
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!
739 namespace {
741 enum ScanState
743 SsStop = 0,
744 SsStart = 1,
745 SsGetChar = 2,
746 SsGetString = 3,
747 SsGetWord = 4,
748 SsGetStar = 5,
749 SsGetBlank = 6
754 short ImpSvNumberformatScan::Next_Symbol( const OUString& rStr,
755 sal_Int32& nPos,
756 OUString& sSymbol ) const
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 )
766 sal_Unicode cToken = rStr[nPos++];
767 switch (eState)
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] != '[') )
780 OUString aTest = pChrCls->uppercase( rStr.copy( nPos-1, sCurString.getLength() ) );
781 if ( aTest == sCurString )
783 sSymbol = rStr.copy( --nPos, sCurString.getLength() );
784 nPos = nPos + sSymbol.getLength();
785 eType = NF_SYMBOLTYPE_STRING;
786 return eType;
789 switch (cToken)
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))
844 // Another separator than pre-known ASCII
845 eType = NF_SYMBOLTYPE_DEL;
846 sSymbolBuffer.append(OUStringChar(cToken));
847 eState = SsStop;
849 else if ( pChrCls->isLetter( rStr, nPos-1 ) )
851 bool bFoundEnglish = false;
852 short nTmpType = GetKeyWord( rStr, nPos-1, bFoundEnglish);
853 if ( nTmpType )
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]))
862 OUString aTest = pChrCls->uppercase( rStr.copy( nPos-1, sCurString.getLength() ) );
863 if ( aTest == sCurString )
865 bCurrency = true;
868 if ( bCurrency )
870 eState = SsGetWord;
871 sSymbolBuffer.append(OUStringChar(cToken));
873 else
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)
882 nLen = sEnglishKeyword[eType].getLength();
883 // Use the locale's General keyword name, not uppercase.
884 sSymbolBuffer = (eType == NF_KEY_GENERAL ? sNameStandardFormat : sKeyword[eType]);
886 else
888 nLen = sKeyword[eType].getLength();
889 // Preserve a locale's keyword's case as entered.
890 sSymbolBuffer = rStr.subView( nPos-1, nLen);
892 if ((eType == NF_KEY_E || IsAmbiguousE(eType)) && nPos < rStr.getLength())
894 sal_Unicode cNext = rStr[nPos];
895 switch ( cNext )
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;
909 nPos--;
910 nPos = nPos + nLen;
911 eState = SsStop;
914 else
916 eState = SsGetWord;
917 sSymbolBuffer.append(OUStringChar(cToken));
920 else
922 eType = NF_SYMBOLTYPE_STRING;
923 eState = SsStop;
924 sSymbolBuffer.append(OUStringChar(cToken));
926 break;
928 break;
929 case SsGetChar:
930 sSymbolBuffer.append(OUStringChar(cToken));
931 eState = SsStop;
932 break;
933 case SsGetString:
934 if (cToken == '"')
936 eState = SsStop;
938 sSymbolBuffer.append(OUStringChar(cToken));
939 break;
940 case SsGetWord:
941 if ( pChrCls->isLetter( rStr, nPos-1 ) )
943 bool bFoundEnglish = false;
944 short nTmpType = GetKeyWord( rStr, nPos-1, bFoundEnglish);
945 if ( nTmpType )
947 // beginning of keyword, stop scan and put back
948 eType = NF_SYMBOLTYPE_STRING;
949 eState = SsStop;
950 nPos--;
952 else
954 sSymbolBuffer.append(OUStringChar(cToken));
957 else
959 bool bDontStop = false;
960 sal_Unicode cNext;
961 switch (cToken)
963 case '/': // AM/PM, A/P
964 if (nPos < rStr.getLength())
966 cNext = rStr[nPos];
967 if ( cNext == 'P' || cNext == 'p' )
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'))))
976 sSymbolBuffer.append(OUStringChar(cToken));
977 bDontStop = true;
981 break;
983 // anything not recognized will stop the scan
984 if (!bDontStop)
986 eState = SsStop;
987 nPos--;
988 eType = NF_SYMBOLTYPE_STRING;
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)
1006 eType = NF_SYMBOLTYPE_STRING;
1008 sSymbol = sSymbolBuffer.makeStringAndClear();
1009 return eType;
1012 sal_Int32 ImpSvNumberformatScan::Symbol_Division(const OUString& rString)
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())
1020 nCPos = sString.indexOf(GetCurString(),nCPos);
1021 if (nCPos >= 0)
1023 // In Quotes?
1024 sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sString, nCPos );
1025 if ( nQ < 0 )
1027 sal_Unicode c;
1028 if ( nCPos == 0 ||
1029 ((c = sString[nCPos-1]) != '"'
1030 && c != '\\') ) // dm can be protected by "dm \d
1032 nCurrPos = nCPos;
1033 nCPos = -1;
1035 else
1037 nCPos++; // Continue search
1040 else
1042 nCPos = nQ + 1; // Continue search
1046 nStringsCnt = 0;
1047 bool bStar = false; // Is set on detecting '*'
1048 Reset();
1050 sal_Int32 nPos = 0;
1051 const sal_Int32 nLen = rString.getLength();
1052 while (nPos < nLen && nStringsCnt < NF_MAX_FORMAT_SYMBOLS)
1054 nTypeArray[nStringsCnt] = Next_Symbol(rString, nPos, sStrArray[nStringsCnt]);
1055 if (nTypeArray[nStringsCnt] == NF_SYMBOLTYPE_STAR)
1056 { // Monitoring the '*'
1057 if (bStar)
1059 return nPos; // Error: double '*'
1061 else
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;
1071 nStringsCnt++;
1074 return 0; // 0 => ok
1077 void ImpSvNumberformatScan::SkipStrings(sal_uInt16& i, sal_Int32& nPos) const
1079 while (i < nStringsCnt && ( nTypeArray[i] == NF_SYMBOLTYPE_STRING
1080 || nTypeArray[i] == NF_SYMBOLTYPE_BLANK
1081 || nTypeArray[i] == NF_SYMBOLTYPE_STAR) )
1083 nPos = nPos + sStrArray[i].getLength();
1084 i++;
1088 sal_uInt16 ImpSvNumberformatScan::PreviousKeyword(sal_uInt16 i) const
1090 short res = 0;
1091 if (i > 0 && i < nStringsCnt)
1093 i--;
1094 while (i > 0 && nTypeArray[i] <= 0)
1096 i--;
1098 if (nTypeArray[i] > 0)
1100 res = nTypeArray[i];
1103 return res;
1106 sal_uInt16 ImpSvNumberformatScan::NextKeyword(sal_uInt16 i) const
1108 short res = 0;
1109 if (i < nStringsCnt-1)
1111 i++;
1112 while (i < nStringsCnt-1 && nTypeArray[i] <= 0)
1114 i++;
1116 if (nTypeArray[i] > 0)
1118 res = nTypeArray[i];
1121 return res;
1124 short ImpSvNumberformatScan::PreviousType( sal_uInt16 i ) const
1126 if ( i > 0 && i < nStringsCnt )
1130 i--;
1132 while ( i > 0 && nTypeArray[i] == NF_SYMBOLTYPE_EMPTY );
1133 return nTypeArray[i];
1135 return 0;
1138 sal_Unicode ImpSvNumberformatScan::PreviousChar(sal_uInt16 i) const
1140 sal_Unicode res = ' ';
1141 if (i > 0 && i < nStringsCnt)
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 ))
1150 i--;
1152 if (sStrArray[i].getLength() > 0)
1154 res = sStrArray[i][sStrArray[i].getLength()-1];
1157 return res;
1160 sal_Unicode ImpSvNumberformatScan::NextChar(sal_uInt16 i) const
1162 sal_Unicode res = ' ';
1163 if (i < nStringsCnt-1)
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))
1172 i++;
1174 if (sStrArray[i].getLength() > 0)
1176 res = sStrArray[i][0];
1179 return res;
1182 bool ImpSvNumberformatScan::IsLastBlankBeforeFrac(sal_uInt16 i) const
1184 bool res = true;
1185 if (i < nStringsCnt-1)
1187 bool bStop = false;
1188 i++;
1189 while (i < nStringsCnt-1 && !bStop)
1191 i++;
1192 if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL &&
1193 sStrArray[i][0] == '/')
1195 bStop = true;
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
1201 res = false;
1204 if (!bStop) // no '/'{
1206 res = false;
1209 else
1211 res = false; // no '/' any more
1213 return res;
1216 void ImpSvNumberformatScan::Reset()
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;
1236 bool ImpSvNumberformatScan::Is100SecZero( sal_uInt16 i, bool bHadDecSep ) const
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
1245 sal_Int32 ImpSvNumberformatScan::ScanType()
1247 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
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;
1257 SkipStrings(i, nPos);
1258 while (i < nStringsCnt)
1260 if (nTypeArray[i] > 0)
1261 { // keyword
1262 sal_uInt16 nIndexPre;
1263 sal_uInt16 nIndexNex;
1265 switch (nTypeArray[i])
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.
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
1309 eNewType = SvNumFormatType::TIME;
1310 if ( nTypeArray[i] == NF_KEY_M || nTypeArray[i] == NF_KEY_MM )
1312 nTypeArray[i] -= 2; // 6 -> 4, 7 -> 5
1314 bIsTimeDetected = false; // next M should be month
1315 bHaveMinute = true;
1317 else
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
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;
1368 else
1369 { // control character
1370 switch ( sStrArray[i][0] )
1372 case '#':
1373 case '?':
1374 eNewType = SvNumFormatType::NUMBER;
1375 break;
1376 case '0':
1377 if ( eScannedType & SvNumFormatType::TIME )
1379 if ( Is100SecZero( i, bDecSep ) )
1381 bDecSep = true; // subsequent 0's
1382 eNewType = SvNumFormatType::TIME;
1384 else
1386 return nPos; // Error
1389 else
1391 eNewType = SvNumFormatType::NUMBER;
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] == '$' )
1405 eNewType = SvNumFormatType::CURRENCY;
1406 bMatchBracket = true;
1408 else if ( i < nStringsCnt-1 &&
1409 nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
1410 sStrArray[i+1][0] == '~' )
1412 eNewType = SvNumFormatType::DATE;
1413 bMatchBracket = true;
1415 else
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
1427 return nPos; // Error
1430 break;
1431 case '@':
1432 eNewType = SvNumFormatType::TEXT;
1433 break;
1434 default:
1435 // Separator for SS,0
1436 if ((eScannedType & SvNumFormatType::TIME)
1437 && 0 < i && (nTypeArray[i-1] == NF_KEY_S || nTypeArray[i-1] == NF_KEY_SS))
1439 // For ISO 8601 only YYYY-MM-DD"T"HH:MM:SS,0 accept both
1440 // ',' and '.' regardless of locale's separator, and only
1441 // those.
1442 // XXX NOTE: this catches only known separators of
1443 // NF_SYMBOLTYPE_DEL as all NF_SYMBOLTYPE_STRING are
1444 // skipped during the loop. Meant to error out if the
1445 // Time100SecSep or decimal separator differ and were used.
1446 if ((eScannedType & SvNumFormatType::DATE)
1447 && 11 <= i && i < nStringsCnt-1
1448 && (nTypeArray[i-6] == NF_SYMBOLTYPE_STRING
1449 && (sStrArray[i-6] == "\"T\"" || sStrArray[i-6] == "\\T"
1450 || sStrArray[i-6] == "T"))
1451 && (nTypeArray[i-11] == NF_KEY_YYYY)
1452 && (nTypeArray[i-9] == NF_KEY_M || nTypeArray[i-9] == NF_KEY_MM)
1453 && (nTypeArray[i-7] == NF_KEY_D || nTypeArray[i-7] == NF_KEY_DD)
1454 && (nTypeArray[i-5] == NF_KEY_H || nTypeArray[i-5] == NF_KEY_HH)
1455 && (nTypeArray[i-3] == NF_KEY_MI || nTypeArray[i-3] == NF_KEY_MMI)
1456 && (nTypeArray[i+1] == NF_SYMBOLTYPE_DEL && sStrArray[i+1][0] == '0'))
1459 if (sStrArray[i].getLength() == 1 && (sStrArray[i][0] == ',' || sStrArray[i][0] == '.'))
1460 bDecSep = true;
1461 else
1462 return nPos; // Error
1464 else if (pLoc->getTime100SecSep() == sStrArray[i])
1465 bDecSep = true;
1467 eNewType = SvNumFormatType::UNDEFINED;
1468 break;
1471 if (eScannedType == SvNumFormatType::UNDEFINED)
1473 eScannedType = eNewType;
1475 else if (eScannedType == SvNumFormatType::TEXT || eNewType == SvNumFormatType::TEXT)
1477 eScannedType = SvNumFormatType::TEXT; // Text always remains text
1479 else if (eNewType == SvNumFormatType::UNDEFINED)
1480 { // Remains as is
1482 else if (eScannedType != eNewType)
1484 switch (eScannedType)
1486 case SvNumFormatType::DATE:
1487 switch (eNewType)
1489 case SvNumFormatType::TIME:
1490 eScannedType = SvNumFormatType::DATETIME;
1491 break;
1492 case SvNumFormatType::FRACTION: // DD/MM
1493 break;
1494 default:
1495 if (nCurrPos >= 0)
1497 eScannedType = SvNumFormatType::UNDEFINED;
1499 else if ( sStrArray[i] != pFormatter->GetDateSep() )
1501 return nPos;
1504 break;
1505 case SvNumFormatType::TIME:
1506 switch (eNewType)
1508 case SvNumFormatType::DATE:
1509 eScannedType = SvNumFormatType::DATETIME;
1510 break;
1511 case SvNumFormatType::FRACTION: // MM/SS
1512 break;
1513 default:
1514 if (nCurrPos >= 0)
1516 eScannedType = SvNumFormatType::UNDEFINED;
1518 else if (pLoc->getTimeSep() != sStrArray[i])
1520 return nPos;
1522 break;
1524 break;
1525 case SvNumFormatType::DATETIME:
1526 switch (eNewType)
1528 case SvNumFormatType::TIME:
1529 case SvNumFormatType::DATE:
1530 break;
1531 case SvNumFormatType::FRACTION: // DD/MM
1532 break;
1533 default:
1534 if (nCurrPos >= 0)
1536 eScannedType = SvNumFormatType::UNDEFINED;
1538 else if ( pFormatter->GetDateSep() != sStrArray[i] &&
1539 pLoc->getTimeSep() != sStrArray[i] )
1541 return nPos;
1544 break;
1545 case SvNumFormatType::PERCENT:
1546 switch (eNewType)
1548 case SvNumFormatType::NUMBER: // Only number to percent
1549 break;
1550 default:
1551 return nPos;
1553 break;
1554 case SvNumFormatType::SCIENTIFIC:
1555 switch (eNewType)
1557 case SvNumFormatType::NUMBER: // Only number to E
1558 break;
1559 default:
1560 return nPos;
1562 break;
1563 case SvNumFormatType::NUMBER:
1564 switch (eNewType)
1566 case SvNumFormatType::SCIENTIFIC:
1567 case SvNumFormatType::PERCENT:
1568 case SvNumFormatType::FRACTION:
1569 case SvNumFormatType::CURRENCY:
1570 eScannedType = eNewType;
1571 break;
1572 default:
1573 if (nCurrPos >= 0)
1575 eScannedType = SvNumFormatType::UNDEFINED;
1577 else
1579 return nPos;
1582 break;
1583 case SvNumFormatType::FRACTION:
1584 switch (eNewType)
1586 case SvNumFormatType::NUMBER: // Only number to fraction
1587 break;
1588 default:
1589 return nPos;
1591 break;
1592 default:
1593 break;
1596 nPos = nPos + sStrArray[i].getLength(); // Position of correction
1597 i++;
1598 if ( bMatchBracket )
1599 { // no type detection inside of matching brackets if [$...], [~...]
1600 while ( bMatchBracket && i < nStringsCnt )
1602 if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL
1603 && sStrArray[i][0] == ']' )
1605 bMatchBracket = false;
1607 else
1609 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1611 nPos = nPos + sStrArray[i].getLength();
1612 i++;
1614 if ( bMatchBracket )
1616 return nPos; // missing closing bracket at end of code
1619 SkipStrings(i, nPos);
1622 if ((eScannedType == SvNumFormatType::NUMBER ||
1623 eScannedType == SvNumFormatType::UNDEFINED) &&
1624 nCurrPos >= 0 && !bHaveGeneral)
1626 eScannedType = SvNumFormatType::CURRENCY; // old "automatic" currency
1628 if (eScannedType == SvNumFormatType::UNDEFINED)
1630 eScannedType = SvNumFormatType::DEFINED;
1632 return 0; // All is fine
1635 bool ImpSvNumberformatScan::InsertSymbol( sal_uInt16 & nPos, svt::NfSymbolType eType, const OUString& rStr )
1637 if (nStringsCnt >= NF_MAX_FORMAT_SYMBOLS || nPos > nStringsCnt)
1639 return false;
1641 if (nPos > 0 && nTypeArray[nPos-1] == NF_SYMBOLTYPE_EMPTY)
1643 --nPos; // reuse position
1645 else
1647 if (nStringsCnt >= NF_MAX_FORMAT_SYMBOLS - 1)
1649 return false;
1651 ++nStringsCnt;
1652 for (size_t i = nStringsCnt; i > nPos; --i)
1654 nTypeArray[i] = nTypeArray[i-1];
1655 sStrArray[i] = sStrArray[i-1];
1658 ++nResultStringsCnt;
1659 nTypeArray[nPos] = static_cast<short>(eType);
1660 sStrArray[nPos] = rStr;
1661 return true;
1664 int ImpSvNumberformatScan::FinalScanGetCalendar( sal_Int32& nPos, sal_uInt16& i,
1665 sal_uInt16& rResultStringsCnt )
1667 if ( i < nStringsCnt-1 &&
1668 sStrArray[i][0] == '[' &&
1669 nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
1670 sStrArray[i+1][0] == '~' )
1672 // [~calendarID]
1673 nPos = nPos + sStrArray[i].getLength(); // [
1674 nTypeArray[i] = NF_SYMBOLTYPE_CALDEL;
1675 nPos = nPos + sStrArray[++i].getLength(); // ~
1676 sStrArray[i-1] += sStrArray[i]; // [~
1677 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
1678 rResultStringsCnt--;
1679 if ( ++i >= nStringsCnt )
1681 return -1; // error
1683 nPos = nPos + sStrArray[i].getLength(); // calendarID
1684 OUString& rStr = sStrArray[i];
1685 nTypeArray[i] = NF_SYMBOLTYPE_CALENDAR; // convert
1686 i++;
1687 while ( i < nStringsCnt && sStrArray[i][0] != ']' )
1689 nPos = nPos + sStrArray[i].getLength();
1690 rStr += sStrArray[i];
1691 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
1692 rResultStringsCnt--;
1693 i++;
1695 if ( rStr.getLength() && i < nStringsCnt &&
1696 sStrArray[i][0] == ']' )
1698 nTypeArray[i] = NF_SYMBOLTYPE_CALDEL;
1699 nPos = nPos + sStrArray[i].getLength();
1700 i++;
1702 else
1704 return -1; // error
1706 return 1;
1708 return 0;
1711 bool ImpSvNumberformatScan::IsDateFragment( size_t nPos1, size_t nPos2 ) const
1713 return nPos2 - nPos1 == 2 && nTypeArray[nPos1+1] == NF_SYMBOLTYPE_DATESEP;
1716 void ImpSvNumberformatScan::SwapArrayElements( size_t nPos1, size_t nPos2 )
1718 std::swap( nTypeArray[nPos1], nTypeArray[nPos2]);
1719 std::swap( sStrArray[nPos1], sStrArray[nPos2]);
1722 sal_Int32 ImpSvNumberformatScan::FinalScan( OUString& rString )
1724 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
1726 // save values for convert mode
1727 OUString sOldDecSep = pFormatter->GetNumDecimalSep();
1728 OUString sOldThousandSep = pFormatter->GetNumThousandSep();
1729 OUString sOldDateSep = pFormatter->GetDateSep();
1730 OUString sOldTimeSep = pLoc->getTimeSep();
1731 OUString sOldTime100SecSep= pLoc->getTime100SecSep();
1732 OUString sOldCurSymbol = GetCurSymbol();
1733 OUString sOldCurString = GetCurString();
1734 sal_Unicode cOldKeyH = sKeyword[NF_KEY_H][0];
1735 sal_Unicode cOldKeyMI = sKeyword[NF_KEY_MI][0];
1736 sal_Unicode cOldKeyS = sKeyword[NF_KEY_S][0];
1737 DateOrder eOldDateOrder = pLoc->getDateOrder();
1738 sal_uInt16 nDayPos, nMonthPos, nYearPos;
1739 nDayPos = nMonthPos = nYearPos = SAL_MAX_UINT16;
1741 // If the group separator is a No-Break Space (French) continue with a
1742 // normal space instead so queries on space work correctly.
1743 // The same for Narrow No-Break Space just in case some locale uses it.
1744 // The format string is adjusted to allow both.
1745 // For output of the format code string the LocaleData characters are used.
1746 if ( (sOldThousandSep[0] == cNoBreakSpace || sOldThousandSep[0] == cNarrowNoBreakSpace) &&
1747 sOldThousandSep.getLength() == 1 )
1749 sOldThousandSep = " ";
1751 bool bNewDateOrder = false;
1752 // change locale data et al
1753 if (bConvertMode)
1755 pFormatter->ChangeIntl(eNewLnge);
1756 //! pointer may have changed
1757 pLoc = pFormatter->GetLocaleData();
1758 //! init new keywords
1759 InitKeywords();
1760 // Adapt date order to target locale, but Excel does not handle date
1761 // particle re-ordering for the target locale when loading documents,
1762 // though it does exchange separators, tdf#113889
1763 bNewDateOrder = (mbConvertDateOrder && eOldDateOrder != pLoc->getDateOrder());
1765 const CharClass* pChrCls = pFormatter->GetCharClass();
1767 sal_Int32 nPos = 0; // error correction position
1768 sal_uInt16 i = 0; // symbol loop counter
1769 sal_uInt16 nCounter = 0; // counts digits
1770 nResultStringsCnt = nStringsCnt; // counts remaining symbols
1771 bDecSep = false; // reset in case already used in TypeCheck
1772 bool bThaiT = false; // Thai T NatNum modifier present
1773 bool bTimePart = false;
1774 bool bDenomin = false; // Set when reading end of denominator
1776 switch (eScannedType)
1778 case SvNumFormatType::TEXT:
1779 case SvNumFormatType::DEFINED:
1780 while (i < nStringsCnt)
1782 switch (nTypeArray[i])
1784 case NF_SYMBOLTYPE_BLANK:
1785 case NF_SYMBOLTYPE_STAR:
1786 break;
1787 case NF_KEY_GENERAL : // #77026# "General" is the same as "@"
1788 break;
1789 default:
1790 if ( nTypeArray[i] != NF_SYMBOLTYPE_DEL ||
1791 sStrArray[i][0] != '@' )
1793 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1795 break;
1797 nPos = nPos + sStrArray[i].getLength();
1798 i++;
1799 } // of while
1800 break;
1802 case SvNumFormatType::NUMBER:
1803 case SvNumFormatType::PERCENT:
1804 case SvNumFormatType::CURRENCY:
1805 case SvNumFormatType::SCIENTIFIC:
1806 case SvNumFormatType::FRACTION:
1807 while (i < nStringsCnt)
1809 // TODO: rechecking eScannedType is unnecessary.
1810 // This switch-case is for eScannedType == SvNumFormatType::FRACTION anyway
1811 if (eScannedType == SvNumFormatType::FRACTION && // special case
1812 nTypeArray[i] == NF_SYMBOLTYPE_DEL && // # ### #/#
1813 StringEqualsChar( sOldThousandSep, ' ' ) && // e.g. France or Sweden
1814 StringEqualsChar( sStrArray[i], ' ' ) &&
1815 !bFrac &&
1816 IsLastBlankBeforeFrac(i) )
1818 nTypeArray[i] = NF_SYMBOLTYPE_STRING; // del->string
1819 } // No thousands marker
1821 if (nTypeArray[i] == NF_SYMBOLTYPE_BLANK ||
1822 nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
1823 nTypeArray[i] == NF_KEY_CCC || // CCC
1824 nTypeArray[i] == NF_KEY_GENERAL ) // Standard
1826 if (nTypeArray[i] == NF_KEY_GENERAL)
1828 nThousand = FLAG_STANDARD_IN_FORMAT;
1829 if ( bConvertMode )
1831 sStrArray[i] = sNameStandardFormat;
1834 nPos = nPos + sStrArray[i].getLength();
1835 i++;
1837 else if (nTypeArray[i] == NF_SYMBOLTYPE_STRING || // No Strings or
1838 nTypeArray[i] > 0) // Keywords
1840 if (eScannedType == SvNumFormatType::SCIENTIFIC &&
1841 nTypeArray[i] == NF_KEY_E) // E+
1843 if (bExp) // Double
1845 return nPos;
1847 bExp = true;
1848 nExpPos = i;
1849 if (bDecSep)
1851 nCntPost = nCounter;
1853 else
1855 nCntPre = nCounter;
1857 nCounter = 0;
1858 nTypeArray[i] = NF_SYMBOLTYPE_EXP;
1860 else if (eScannedType == SvNumFormatType::FRACTION &&
1861 (sStrArray[i][0] == ' ' || ( nTypeArray[i] == NF_SYMBOLTYPE_STRING && (sStrArray[i][0] < '0' || sStrArray[i][0] > '9') ) ) )
1863 if (!bBlank && !bFrac) // Not double or after a /
1865 if (bDecSep && nCounter > 0) // Decimal places
1867 return nPos; // Error
1869 if (sStrArray[i][0] == ' ' || nCounter > 0 ) // treat string as integer/fraction delimiter only if there is integer
1871 bBlank = true;
1872 nBlankPos = i;
1873 nCntPre = nCounter;
1874 nCounter = 0;
1875 nTypeArray[i] = NF_SYMBOLTYPE_FRACBLANK;
1878 else if ( sStrArray[i][0] == ' ' )
1879 nTypeArray[i] = NF_SYMBOLTYPE_FRACBLANK;
1880 else if ( bFrac && ( nCounter > 0 ) )
1881 bDenomin = true; // following elements are no more part of denominator
1883 else if (nTypeArray[i] == NF_KEY_THAI_T)
1885 bThaiT = true;
1886 sStrArray[i] = sKeyword[nTypeArray[i]];
1888 else if (sStrArray[i][0] >= '0' &&
1889 sStrArray[i][0] <= '9' && !bDenomin) // denominator was not yet found
1891 OUString sDiv;
1892 sal_uInt16 j = i;
1893 while(j < nStringsCnt && sStrArray[j][0] >= '0' && sStrArray[j][0] <= '9')
1895 sDiv += sStrArray[j++];
1897 assert(j > 0 && "if i is 0, first iteration through loop is guaranteed by surrounding if condition");
1898 if (std::u16string_view(OUString::number(sDiv.toInt32())) == sDiv)
1900 // Found a Divisor
1901 while (i < j)
1903 nTypeArray[i++] = NF_SYMBOLTYPE_FRAC_FDIV;
1905 i = j - 1; // Stop the loop
1906 if (nCntPost)
1908 nCounter = nCntPost;
1910 else if (nCntPre)
1912 nCounter = nCntPre;
1914 // don't artificially increment nCntPre for forced denominator
1915 if ( ( eScannedType != SvNumFormatType::FRACTION ) && (!nCntPre) )
1917 nCntPre++;
1919 if ( bFrac )
1920 bDenomin = true; // next content should be treated as outside denominator
1923 else
1925 if ( bFrac && ( nCounter > 0 ) )
1926 bDenomin = true; // next content should be treated as outside denominator
1927 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1929 nPos = nPos + sStrArray[i].getLength();
1930 i++;
1932 else if (nTypeArray[i] == NF_SYMBOLTYPE_DEL)
1934 sal_Unicode cHere = sStrArray[i][0];
1935 sal_Unicode cSaved = cHere;
1936 // Handle not pre-known separators in switch.
1937 sal_Unicode cSimplified;
1938 if (StringEqualsChar( pFormatter->GetNumThousandSep(), cHere))
1940 cSimplified = ',';
1942 else if (StringEqualsChar( pFormatter->GetNumDecimalSep(), cHere))
1944 cSimplified = '.';
1946 else
1948 cSimplified = cHere;
1951 OUString& rStr = sStrArray[i];
1953 switch ( cSimplified )
1955 case '#':
1956 case '0':
1957 case '?':
1958 if (nThousand > 0) // #... #
1960 return nPos; // Error
1962 if ( !bDenomin )
1964 nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
1965 nPos = nPos + rStr.getLength();
1966 i++;
1967 nCounter++;
1968 while (i < nStringsCnt &&
1969 (sStrArray[i][0] == '#' ||
1970 sStrArray[i][0] == '0' ||
1971 sStrArray[i][0] == '?'))
1973 nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
1974 nPos = nPos + sStrArray[i].getLength();
1975 nCounter++;
1976 i++;
1979 else // after denominator, treat any character as text
1981 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
1982 nPos = nPos + sStrArray[i].getLength();
1984 break;
1985 case '-':
1986 if ( bDecSep && nDecPos+1 == i &&
1987 nTypeArray[nDecPos] == NF_SYMBOLTYPE_DECSEP )
1989 // "0.--"
1990 nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
1991 nPos = nPos + rStr.getLength();
1992 i++;
1993 nCounter++;
1994 while (i < nStringsCnt &&
1995 (sStrArray[i][0] == '-') )
1997 // If more than two dashes are present in
1998 // currency formats the last dash will be
1999 // interpreted literally as a minus sign.
2000 // Has to be this ugly. Period.
2001 if ( eScannedType == SvNumFormatType::CURRENCY
2002 && rStr.getLength() >= 2 &&
2003 (i == nStringsCnt-1 ||
2004 sStrArray[i+1][0] != '-') )
2006 break;
2008 rStr += sStrArray[i];
2009 nPos = nPos + sStrArray[i].getLength();
2010 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2011 nResultStringsCnt--;
2012 nCounter++;
2013 i++;
2016 else
2018 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2019 nPos = nPos + sStrArray[i].getLength();
2020 i++;
2022 break;
2023 case '.':
2024 case ',':
2025 case '\'':
2026 case ' ':
2027 if ( StringEqualsChar( sOldThousandSep, cSaved ) )
2029 // previous char with skip empty
2030 sal_Unicode cPre = PreviousChar(i);
2031 sal_Unicode cNext;
2032 if (bExp || bBlank || bFrac)
2034 // after E, / or ' '
2035 if ( !StringEqualsChar( sOldThousandSep, ' ' ) )
2037 nPos = nPos + sStrArray[i].getLength();
2038 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2039 nResultStringsCnt--;
2040 i++; // eat it
2042 else
2044 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2045 if ( bFrac && (nCounter > 0) )
2046 bDenomin = true; // end of denominator
2049 else if (i > 0 && i < nStringsCnt-1 &&
2050 (cPre == '#' || cPre == '0' || cPre == '?') &&
2051 ((cNext = NextChar(i)) == '#' || cNext == '0' || cNext == '?')) // #,#
2053 nPos = nPos + sStrArray[i].getLength();
2054 if (!bThousand) // only once
2056 bThousand = true;
2058 // Eat it, will be reinserted at proper grouping positions further down.
2059 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2060 nResultStringsCnt--;
2061 i++;
2063 else if (i > 0 && (cPre == '#' || cPre == '0' || cPre == '?')
2064 && PreviousType(i) == NF_SYMBOLTYPE_DIGIT
2065 && nThousand < FLAG_STANDARD_IN_FORMAT )
2066 { // #,,,,
2067 if ( StringEqualsChar( sOldThousandSep, ' ' ) )
2069 // strange, those French...
2070 bool bFirst = true;
2071 // set a hard No-Break Space or ConvertMode
2072 const OUString& rSepF = pFormatter->GetNumThousandSep();
2073 while ( i < nStringsCnt &&
2074 sStrArray[i] == sOldThousandSep &&
2075 StringEqualsChar( sOldThousandSep, NextChar(i) ) )
2076 { // last was a space or another space
2077 // is following => separator
2078 nPos = nPos + sStrArray[i].getLength();
2079 if ( bFirst )
2081 bFirst = false;
2082 rStr = rSepF;
2083 nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
2085 else
2087 rStr += rSepF;
2088 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2089 nResultStringsCnt--;
2091 nThousand++;
2092 i++;
2094 if ( i < nStringsCnt-1 &&
2095 sStrArray[i] == sOldThousandSep )
2097 // something following last space
2098 // => space if currency contained,
2099 // else separator
2100 nPos = nPos + sStrArray[i].getLength();
2101 if ( (nPos <= nCurrPos &&
2102 nCurrPos < nPos + sStrArray[i+1].getLength()) ||
2103 nTypeArray[i+1] == NF_KEY_CCC ||
2104 (i < nStringsCnt-2 &&
2105 sStrArray[i+1][0] == '[' &&
2106 sStrArray[i+2][0] == '$') )
2108 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2110 else
2112 if ( bFirst )
2114 rStr = rSepF;
2115 nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
2117 else
2119 rStr += rSepF;
2120 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2121 nResultStringsCnt--;
2123 nThousand++;
2125 i++;
2128 else
2132 nThousand++;
2133 nTypeArray[i] = NF_SYMBOLTYPE_THSEP;
2134 nPos = nPos + sStrArray[i].getLength();
2135 sStrArray[i] = pFormatter->GetNumThousandSep();
2136 i++;
2138 while (i < nStringsCnt && sStrArray[i] == sOldThousandSep);
2141 else // any grsep
2143 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2144 nPos = nPos + rStr.getLength();
2145 i++;
2146 while ( i < nStringsCnt && sStrArray[i] == sOldThousandSep )
2148 rStr += sStrArray[i];
2149 nPos = nPos + sStrArray[i].getLength();
2150 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2151 nResultStringsCnt--;
2152 i++;
2156 else if ( StringEqualsChar( sOldDecSep, cSaved ) )
2158 if (bBlank || bFrac) // . behind / or ' '
2160 return nPos; // error
2162 else if (bExp) // behind E
2164 nPos = nPos + sStrArray[i].getLength();
2165 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2166 nResultStringsCnt--;
2167 i++; // eat it
2169 else if (bDecSep) // any .
2171 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2172 nPos = nPos + rStr.getLength();
2173 i++;
2174 while ( i < nStringsCnt && sStrArray[i] == sOldDecSep )
2176 rStr += sStrArray[i];
2177 nPos = nPos + sStrArray[i].getLength();
2178 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2179 nResultStringsCnt--;
2180 i++;
2183 else
2185 nPos = nPos + sStrArray[i].getLength();
2186 nTypeArray[i] = NF_SYMBOLTYPE_DECSEP;
2187 sStrArray[i] = pFormatter->GetNumDecimalSep();
2188 bDecSep = true;
2189 nDecPos = i;
2190 nCntPre = nCounter;
2191 nCounter = 0;
2193 i++;
2195 } // of else = DecSep
2196 else // . without meaning
2198 if (cSaved == ' ' &&
2199 eScannedType == SvNumFormatType::FRACTION &&
2200 StringEqualsChar( sStrArray[i], ' ' ) )
2202 if (!bBlank && !bFrac) // no dups
2203 { // or behind /
2204 if (bDecSep && nCounter > 0) // dec.
2206 return nPos; // error
2208 bBlank = true;
2209 nBlankPos = i;
2210 nCntPre = nCounter;
2211 nCounter = 0;
2213 if ( bFrac && (nCounter > 0) )
2214 bDenomin = true; // next content is not part of denominator
2215 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2216 nPos = nPos + sStrArray[i].getLength();
2218 else
2220 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2221 if ( bFrac && (nCounter > 0) )
2222 bDenomin = true; // next content is not part of denominator
2223 nPos = nPos + rStr.getLength();
2224 i++;
2225 while (i < nStringsCnt && StringEqualsChar( sStrArray[i], cSaved ) )
2227 rStr += sStrArray[i];
2228 nPos = nPos + sStrArray[i].getLength();
2229 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2230 nResultStringsCnt--;
2231 i++;
2235 break;
2236 case '/':
2237 if (eScannedType == SvNumFormatType::FRACTION)
2239 if ( i == 0 ||
2240 (nTypeArray[i-1] != NF_SYMBOLTYPE_DIGIT &&
2241 nTypeArray[i-1] != NF_SYMBOLTYPE_EMPTY) )
2243 return nPos ? nPos : 1; // /? not allowed
2245 else if (!bFrac || (bDecSep && nCounter > 0))
2247 bFrac = true;
2248 nCntPost = nCounter;
2249 nCounter = 0;
2250 nTypeArray[i] = NF_SYMBOLTYPE_FRAC;
2251 nPos = nPos + sStrArray[i].getLength();
2252 i++;
2254 else // / double or in , in the denominator
2256 return nPos; // Error
2259 else
2261 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2262 nPos = nPos + sStrArray[i].getLength();
2263 i++;
2265 break;
2266 case '[' :
2267 if ( eScannedType == SvNumFormatType::CURRENCY &&
2268 i < nStringsCnt-1 &&
2269 nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
2270 sStrArray[i+1][0] == '$' )
2272 // [$DM-xxx]
2273 nPos = nPos + sStrArray[i].getLength(); // [
2274 nTypeArray[i] = NF_SYMBOLTYPE_CURRDEL;
2275 nPos = nPos + sStrArray[++i].getLength(); // $
2276 sStrArray[i-1] += sStrArray[i]; // [$
2277 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2278 nResultStringsCnt--;
2279 if ( ++i >= nStringsCnt )
2281 return nPos; // Error
2283 nPos = nPos + sStrArray[i].getLength(); // DM
2284 OUString* pStr = &sStrArray[i];
2285 nTypeArray[i] = NF_SYMBOLTYPE_CURRENCY; // convert
2286 bool bHadDash = false;
2287 i++;
2288 while ( i < nStringsCnt && sStrArray[i][0] != ']' )
2290 nPos = nPos + sStrArray[i].getLength();
2291 if ( bHadDash )
2293 *pStr += sStrArray[i];
2294 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2295 nResultStringsCnt--;
2297 else
2299 if ( sStrArray[i][0] == '-' )
2301 bHadDash = true;
2302 pStr = &sStrArray[i];
2303 nTypeArray[i] = NF_SYMBOLTYPE_CURREXT;
2305 else
2307 *pStr += sStrArray[i];
2308 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2309 nResultStringsCnt--;
2312 i++;
2314 if ( rStr.getLength() && i < nStringsCnt && sStrArray[i][0] == ']' )
2316 nTypeArray[i] = NF_SYMBOLTYPE_CURRDEL;
2317 nPos = nPos + sStrArray[i].getLength();
2318 i++;
2320 else
2322 return nPos; // Error
2325 else
2327 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2328 nPos = nPos + sStrArray[i].getLength();
2329 i++;
2331 break;
2332 default: // Other Dels
2333 if (eScannedType == SvNumFormatType::PERCENT && cHere == '%')
2335 nTypeArray[i] = NF_SYMBOLTYPE_PERCENT;
2337 else
2339 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2341 nPos = nPos + sStrArray[i].getLength();
2342 i++;
2343 break;
2344 } // of switch (Del)
2345 } // of else Del
2346 else
2348 SAL_WARN( "svl.numbers", "unknown NF_SYMBOLTYPE_..." );
2349 nPos = nPos + sStrArray[i].getLength();
2350 i++;
2352 } // of while
2353 if (eScannedType == SvNumFormatType::FRACTION)
2355 if (bFrac)
2357 nCntExp = nCounter;
2359 else if (bBlank)
2361 nCntPost = nCounter;
2363 else
2365 nCntPre = nCounter;
2368 else
2370 if (bExp)
2372 nCntExp = nCounter;
2374 else if (bDecSep)
2376 nCntPost = nCounter;
2378 else
2380 nCntPre = nCounter;
2383 if (bThousand) // Expansion of grouping separators
2385 sal_uInt16 nMaxPos;
2386 if (bFrac)
2388 if (bBlank)
2390 nMaxPos = nBlankPos;
2392 else
2394 nMaxPos = 0; // no grouping
2397 else if (bDecSep) // decimal separator present
2399 nMaxPos = nDecPos;
2401 else if (bExp) // 'E' exponent present
2403 nMaxPos = nExpPos;
2405 else // up to end
2407 nMaxPos = i;
2409 // Insert separators at proper positions.
2410 sal_Int32 nCount = 0;
2411 utl::DigitGroupingIterator aGrouping( pLoc->getDigitGrouping());
2412 size_t nFirstDigitSymbol = nMaxPos;
2413 size_t nFirstGroupingSymbol = nMaxPos;
2414 i = nMaxPos;
2415 while (i-- > 0)
2417 if (nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
2419 nFirstDigitSymbol = i;
2420 nCount = nCount + sStrArray[i].getLength(); // MSC converts += to int and then warns, so ...
2421 // Insert separator only if not leftmost symbol.
2422 if (i > 0 && nCount >= aGrouping.getPos())
2424 DBG_ASSERT( sStrArray[i].getLength() == 1,
2425 "ImpSvNumberformatScan::FinalScan: combined digits in group separator insertion");
2426 if (!InsertSymbol( i, NF_SYMBOLTYPE_THSEP, pFormatter->GetNumThousandSep()))
2428 // nPos isn't correct here, but signals error
2429 return nPos;
2431 // i may have been decremented by 1
2432 nFirstDigitSymbol = i + 1;
2433 nFirstGroupingSymbol = i;
2434 aGrouping.advance();
2438 // Generated something like "string",000; remove separator again.
2439 if (nFirstGroupingSymbol < nFirstDigitSymbol)
2441 nTypeArray[nFirstGroupingSymbol] = NF_SYMBOLTYPE_EMPTY;
2442 nResultStringsCnt--;
2445 // Combine digits into groups to save memory (Info will be copied
2446 // later, taking only non-empty symbols).
2447 for (i = 0; i < nStringsCnt; ++i)
2449 if (nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
2451 OUString& rStr = sStrArray[i];
2452 while (++i < nStringsCnt && nTypeArray[i] == NF_SYMBOLTYPE_DIGIT)
2454 rStr += sStrArray[i];
2455 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2456 nResultStringsCnt--;
2460 break; // of SvNumFormatType::NUMBER
2461 case SvNumFormatType::DATE:
2462 while (i < nStringsCnt)
2464 switch (nTypeArray[i])
2466 case NF_SYMBOLTYPE_BLANK:
2467 case NF_SYMBOLTYPE_STAR:
2468 case NF_SYMBOLTYPE_STRING:
2469 nPos = nPos + sStrArray[i].getLength();
2470 i++;
2471 break;
2472 case NF_SYMBOLTYPE_DEL:
2473 int nCalRet;
2474 if (sStrArray[i] == sOldDateSep)
2476 nTypeArray[i] = NF_SYMBOLTYPE_DATESEP;
2477 nPos = nPos + sStrArray[i].getLength();
2478 if (bConvertMode)
2480 sStrArray[i] = pFormatter->GetDateSep();
2482 i++;
2484 else if ( (nCalRet = FinalScanGetCalendar( nPos, i, nResultStringsCnt )) != 0 )
2486 if ( nCalRet < 0 )
2488 return nPos; // error
2491 else
2493 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2494 nPos = nPos + sStrArray[i].getLength();
2495 i++;
2497 break;
2498 case NF_KEY_THAI_T :
2499 bThaiT = true;
2500 [[fallthrough]];
2501 case NF_KEY_M: // M
2502 case NF_KEY_MM: // MM
2503 case NF_KEY_MMM: // MMM
2504 case NF_KEY_MMMM: // MMMM
2505 case NF_KEY_MMMMM: // MMMMM
2506 case NF_KEY_Q: // Q
2507 case NF_KEY_QQ: // QQ
2508 case NF_KEY_D: // D
2509 case NF_KEY_DD: // DD
2510 case NF_KEY_DDD: // DDD
2511 case NF_KEY_DDDD: // DDDD
2512 case NF_KEY_YY: // YY
2513 case NF_KEY_YYYY: // YYYY
2514 case NF_KEY_NN: // NN
2515 case NF_KEY_NNN: // NNN
2516 case NF_KEY_NNNN: // NNNN
2517 case NF_KEY_WW : // WW
2518 case NF_KEY_AAA : // AAA
2519 case NF_KEY_AAAA : // AAAA
2520 case NF_KEY_EC : // E
2521 case NF_KEY_EEC : // EE
2522 case NF_KEY_G : // G
2523 case NF_KEY_GG : // GG
2524 case NF_KEY_GGG : // GGG
2525 case NF_KEY_R : // R
2526 case NF_KEY_RR : // RR
2527 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2528 nPos = nPos + sStrArray[i].getLength();
2529 if (bNewDateOrder)
2531 // For simple numeric date formats record date order and
2532 // later rearrange.
2533 switch (nTypeArray[i])
2535 case NF_KEY_M:
2536 case NF_KEY_MM:
2537 if (nMonthPos == SAL_MAX_UINT16)
2538 nMonthPos = i;
2539 else
2540 bNewDateOrder = false;
2541 break;
2542 case NF_KEY_D:
2543 case NF_KEY_DD:
2544 if (nDayPos == SAL_MAX_UINT16)
2545 nDayPos = i;
2546 else
2547 bNewDateOrder = false;
2548 break;
2549 case NF_KEY_YY:
2550 case NF_KEY_YYYY:
2551 if (nYearPos == SAL_MAX_UINT16)
2552 nYearPos = i;
2553 else
2554 bNewDateOrder = false;
2555 break;
2556 default:
2557 ; // nothing
2560 i++;
2561 break;
2562 default: // Other keywords
2563 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2564 nPos = nPos + sStrArray[i].getLength();
2565 i++;
2566 break;
2568 } // of while
2569 break; // of SvNumFormatType::DATE
2570 case SvNumFormatType::TIME:
2571 while (i < nStringsCnt)
2573 sal_Unicode cChar;
2575 switch (nTypeArray[i])
2577 case NF_SYMBOLTYPE_BLANK:
2578 case NF_SYMBOLTYPE_STAR:
2579 nPos = nPos + sStrArray[i].getLength();
2580 i++;
2581 break;
2582 case NF_SYMBOLTYPE_DEL:
2583 switch( sStrArray[i][0] )
2585 case '0':
2586 if ( Is100SecZero( i, bDecSep ) )
2588 bDecSep = true;
2589 nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
2590 OUString& rStr = sStrArray[i];
2591 nCounter++;
2592 i++;
2593 while (i < nStringsCnt &&
2594 sStrArray[i][0] == '0')
2596 rStr += sStrArray[i];
2597 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2598 nResultStringsCnt--;
2599 nCounter++;
2600 i++;
2602 nPos += rStr.getLength();
2604 else
2606 return nPos;
2608 break;
2609 case '#':
2610 case '?':
2611 return nPos;
2612 case '[':
2613 if (bThousand) // Double
2615 return nPos;
2617 bThousand = true; // Empty for Time
2618 cChar = pChrCls->uppercase(OUString(NextChar(i)))[0];
2619 if ( cChar == cOldKeyH )
2621 nThousand = 1; // H
2623 else if ( cChar == cOldKeyMI )
2625 nThousand = 2; // M
2627 else if ( cChar == cOldKeyS )
2629 nThousand = 3; // S
2631 else
2633 return nPos;
2635 nPos = nPos + sStrArray[i].getLength();
2636 i++;
2637 break;
2638 case ']':
2639 if (!bThousand) // No preceding [
2641 return nPos;
2643 nPos = nPos + sStrArray[i].getLength();
2644 i++;
2645 break;
2646 default:
2647 nPos = nPos + sStrArray[i].getLength();
2648 if ( sStrArray[i] == sOldTimeSep )
2650 nTypeArray[i] = NF_SYMBOLTYPE_TIMESEP;
2651 if ( bConvertMode )
2653 sStrArray[i] = pLoc->getTimeSep();
2656 else if ( sStrArray[i] == sOldTime100SecSep )
2658 bDecSep = true;
2659 nTypeArray[i] = NF_SYMBOLTYPE_TIME100SECSEP;
2660 if ( bConvertMode )
2662 sStrArray[i] = pLoc->getTime100SecSep();
2665 else
2667 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2669 i++;
2670 break;
2672 break;
2673 case NF_SYMBOLTYPE_STRING:
2674 nPos = nPos + sStrArray[i].getLength();
2675 i++;
2676 break;
2677 case NF_KEY_AMPM: // AM/PM
2678 case NF_KEY_AP: // A/P
2679 bExp = true; // Abuse for A/P
2680 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2681 nPos = nPos + sStrArray[i].getLength();
2682 i++;
2683 break;
2684 case NF_KEY_THAI_T :
2685 bThaiT = true;
2686 [[fallthrough]];
2687 case NF_KEY_MI: // M
2688 case NF_KEY_MMI: // MM
2689 case NF_KEY_H: // H
2690 case NF_KEY_HH: // HH
2691 case NF_KEY_S: // S
2692 case NF_KEY_SS: // SS
2693 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2694 nPos = nPos + sStrArray[i].getLength();
2695 i++;
2696 break;
2697 default: // Other keywords
2698 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2699 nPos = nPos + sStrArray[i].getLength();
2700 i++;
2701 break;
2703 } // of while
2704 nCntPost = nCounter; // Zero counter
2705 if (bExp)
2707 nCntExp = 1; // Remembers AM/PM
2709 break; // of SvNumFormatType::TIME
2710 case SvNumFormatType::DATETIME:
2711 while (i < nStringsCnt)
2713 int nCalRet;
2714 switch (nTypeArray[i])
2716 case NF_SYMBOLTYPE_BLANK:
2717 case NF_SYMBOLTYPE_STAR:
2718 case NF_SYMBOLTYPE_STRING:
2719 nPos = nPos + sStrArray[i].getLength();
2720 i++;
2721 break;
2722 case NF_SYMBOLTYPE_DEL:
2723 if ( (nCalRet = FinalScanGetCalendar( nPos, i, nResultStringsCnt )) != 0 )
2725 if ( nCalRet < 0 )
2727 return nPos; // Error
2730 else
2732 switch( sStrArray[i][0] )
2734 case '0':
2735 if (bTimePart && Is100SecZero(i, bDecSep) && nCounter < MaxCntPost)
2737 bDecSep = true;
2738 nTypeArray[i] = NF_SYMBOLTYPE_DIGIT;
2739 OUString& rStr = sStrArray[i];
2740 nCounter++;
2741 i++;
2742 while (i < nStringsCnt &&
2743 sStrArray[i][0] == '0' && nCounter < MaxCntPost)
2745 rStr += sStrArray[i];
2746 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
2747 nResultStringsCnt--;
2748 nCounter++;
2749 i++;
2751 nPos += rStr.getLength();
2753 else
2755 return nPos;
2757 break;
2758 case '#':
2759 case '?':
2760 return nPos;
2761 default:
2762 nPos = nPos + sStrArray[i].getLength();
2763 if (bTimePart)
2765 if ( sStrArray[i] == sOldTimeSep )
2767 nTypeArray[i] = NF_SYMBOLTYPE_TIMESEP;
2768 if ( bConvertMode )
2770 sStrArray[i] = pLoc->getTimeSep();
2773 else if ( sStrArray[i] == sOldTime100SecSep )
2775 bDecSep = true;
2776 nTypeArray[i] = NF_SYMBOLTYPE_TIME100SECSEP;
2777 if ( bConvertMode )
2779 sStrArray[i] = pLoc->getTime100SecSep();
2782 else
2784 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2787 else
2789 if ( sStrArray[i] == sOldDateSep )
2791 nTypeArray[i] = NF_SYMBOLTYPE_DATESEP;
2792 if (bConvertMode)
2793 sStrArray[i] = pFormatter->GetDateSep();
2795 else
2797 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2800 i++;
2801 break;
2804 break;
2805 case NF_KEY_AMPM: // AM/PM
2806 case NF_KEY_AP: // A/P
2807 bTimePart = true;
2808 bExp = true; // Abuse for A/P
2809 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2810 nPos = nPos + sStrArray[i].getLength();
2811 i++;
2812 break;
2813 case NF_KEY_MI: // M
2814 case NF_KEY_MMI: // MM
2815 case NF_KEY_H: // H
2816 case NF_KEY_HH: // HH
2817 case NF_KEY_S: // S
2818 case NF_KEY_SS: // SS
2819 bTimePart = true;
2820 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2821 nPos = nPos + sStrArray[i].getLength();
2822 i++;
2823 break;
2824 case NF_KEY_M: // M
2825 case NF_KEY_MM: // MM
2826 case NF_KEY_MMM: // MMM
2827 case NF_KEY_MMMM: // MMMM
2828 case NF_KEY_MMMMM: // MMMMM
2829 case NF_KEY_Q: // Q
2830 case NF_KEY_QQ: // QQ
2831 case NF_KEY_D: // D
2832 case NF_KEY_DD: // DD
2833 case NF_KEY_DDD: // DDD
2834 case NF_KEY_DDDD: // DDDD
2835 case NF_KEY_YY: // YY
2836 case NF_KEY_YYYY: // YYYY
2837 case NF_KEY_NN: // NN
2838 case NF_KEY_NNN: // NNN
2839 case NF_KEY_NNNN: // NNNN
2840 case NF_KEY_WW : // WW
2841 case NF_KEY_AAA : // AAA
2842 case NF_KEY_AAAA : // AAAA
2843 case NF_KEY_EC : // E
2844 case NF_KEY_EEC : // EE
2845 case NF_KEY_G : // G
2846 case NF_KEY_GG : // GG
2847 case NF_KEY_GGG : // GGG
2848 case NF_KEY_R : // R
2849 case NF_KEY_RR : // RR
2850 bTimePart = false;
2851 sStrArray[i] = sKeyword[nTypeArray[i]]; // tTtT -> TTTT
2852 nPos = nPos + sStrArray[i].getLength();
2853 if (bNewDateOrder)
2855 // For simple numeric date formats record date order and
2856 // later rearrange.
2857 switch (nTypeArray[i])
2859 case NF_KEY_M:
2860 case NF_KEY_MM:
2861 if (nMonthPos == SAL_MAX_UINT16)
2862 nMonthPos = i;
2863 else
2864 bNewDateOrder = false;
2865 break;
2866 case NF_KEY_D:
2867 case NF_KEY_DD:
2868 if (nDayPos == SAL_MAX_UINT16)
2869 nDayPos = i;
2870 else
2871 bNewDateOrder = false;
2872 break;
2873 case NF_KEY_YY:
2874 case NF_KEY_YYYY:
2875 if (nYearPos == SAL_MAX_UINT16)
2876 nYearPos = i;
2877 else
2878 bNewDateOrder = false;
2879 break;
2880 default:
2881 ; // nothing
2884 i++;
2885 break;
2886 case NF_KEY_THAI_T :
2887 bThaiT = true;
2888 sStrArray[i] = sKeyword[nTypeArray[i]];
2889 nPos = nPos + sStrArray[i].getLength();
2890 i++;
2891 break;
2892 default: // Other keywords
2893 nTypeArray[i] = NF_SYMBOLTYPE_STRING;
2894 nPos = nPos + sStrArray[i].getLength();
2895 i++;
2896 break;
2898 } // of while
2899 nCntPost = nCounter; // decimals (100th seconds)
2900 if (bExp)
2902 nCntExp = 1; // Remembers AM/PM
2904 break; // of SvNumFormatType::DATETIME
2905 default:
2906 break;
2908 if (eScannedType == SvNumFormatType::SCIENTIFIC &&
2909 (nCntPre + nCntPost == 0 || nCntExp == 0))
2911 return nPos;
2913 else if (eScannedType == SvNumFormatType::FRACTION && (nCntExp > 8 || nCntExp == 0))
2915 return nPos;
2917 if (bThaiT && !GetNatNumModifier())
2919 SetNatNumModifier(1);
2921 if ( bConvertMode )
2923 if (bNewDateOrder && sOldDateSep == "-")
2925 // Keep ISO formats Y-M-D, Y-M and M-D
2926 if (IsDateFragment( nYearPos, nMonthPos))
2928 nTypeArray[nYearPos+1] = NF_SYMBOLTYPE_STRING;
2929 sStrArray[nYearPos+1] = sOldDateSep;
2930 bNewDateOrder = false;
2932 if (IsDateFragment( nMonthPos, nDayPos))
2934 nTypeArray[nMonthPos+1] = NF_SYMBOLTYPE_STRING;
2935 sStrArray[nMonthPos+1] = sOldDateSep;
2936 bNewDateOrder = false;
2939 if (bNewDateOrder)
2941 // Rearrange date order to the target locale if the original order
2942 // includes date separators and is adjacent.
2943 /* TODO: for incomplete dates trailing separators need to be
2944 * handled according to the locale's usage, e.g. en-US M/D should
2945 * be converted to de-DE D.M. and vice versa. As is, it's
2946 * M/D -> D.M and D.M. -> M/D/ where specifically the latter looks
2947 * odd. Check accepted date patterns and append/remove? */
2948 switch (eOldDateOrder)
2950 case DateOrder::DMY:
2951 switch (pLoc->getDateOrder())
2953 case DateOrder::MDY:
2954 // Convert only if the actual format is not of YDM
2955 // order (which would be a completely unusual order
2956 // anyway, but..), e.g. YYYY.DD.MM not to
2957 // YYYY/MM/DD
2958 if (IsDateFragment( nDayPos, nMonthPos) && !IsDateFragment( nYearPos, nDayPos))
2959 SwapArrayElements( nDayPos, nMonthPos);
2960 break;
2961 case DateOrder::YMD:
2962 if (nYearPos != SAL_MAX_UINT16)
2964 if (IsDateFragment( nDayPos, nMonthPos) && IsDateFragment( nMonthPos, nYearPos))
2965 SwapArrayElements( nDayPos, nYearPos);
2967 else
2969 if (IsDateFragment( nDayPos, nMonthPos))
2970 SwapArrayElements( nDayPos, nMonthPos);
2972 break;
2973 default:
2974 ; // nothing
2976 break;
2977 case DateOrder::MDY:
2978 switch (pLoc->getDateOrder())
2980 case DateOrder::DMY:
2981 // Convert only if the actual format is not of YMD
2982 // order, e.g. YYYY/MM/DD not to YYYY.DD.MM
2983 /* TODO: convert such to DD.MM.YYYY instead? */
2984 if (IsDateFragment( nMonthPos, nDayPos) && !IsDateFragment( nYearPos, nMonthPos))
2985 SwapArrayElements( nMonthPos, nDayPos);
2986 break;
2987 case DateOrder::YMD:
2988 if (nYearPos != SAL_MAX_UINT16)
2990 if (IsDateFragment( nMonthPos, nDayPos) && IsDateFragment( nDayPos, nYearPos))
2992 SwapArrayElements( nYearPos, nMonthPos); // YDM
2993 SwapArrayElements( nYearPos, nDayPos); // YMD
2996 break;
2997 default:
2998 ; // nothing
3000 break;
3001 case DateOrder::YMD:
3002 switch (pLoc->getDateOrder())
3004 case DateOrder::DMY:
3005 if (nYearPos != SAL_MAX_UINT16)
3007 if (IsDateFragment( nYearPos, nMonthPos) && IsDateFragment( nMonthPos, nDayPos))
3008 SwapArrayElements( nYearPos, nDayPos);
3010 else
3012 if (IsDateFragment( nMonthPos, nDayPos))
3013 SwapArrayElements( nMonthPos, nDayPos);
3015 break;
3016 case DateOrder::MDY:
3017 if (nYearPos != SAL_MAX_UINT16)
3019 if (IsDateFragment( nYearPos, nMonthPos) && IsDateFragment( nMonthPos, nDayPos))
3021 SwapArrayElements( nYearPos, nDayPos); // DMY
3022 SwapArrayElements( nYearPos, nMonthPos); // MDY
3025 break;
3026 default:
3027 ; // nothing
3029 break;
3030 default:
3031 ; // nothing
3034 // strings containing keywords of the target locale must be quoted, so
3035 // the user sees the difference and is able to edit the format string
3036 for ( i=0; i < nStringsCnt; i++ )
3038 if ( nTypeArray[i] == NF_SYMBOLTYPE_STRING &&
3039 sStrArray[i][0] != '\"' )
3041 if ( bConvertSystemToSystem && eScannedType == SvNumFormatType::CURRENCY )
3043 // don't stringize automatic currency, will be converted
3044 if ( sStrArray[i] == sOldCurSymbol )
3046 continue; // for
3048 // DM might be split into D and M
3049 if ( sStrArray[i].getLength() < sOldCurSymbol.getLength() &&
3050 pChrCls->uppercase( sStrArray[i], 0, 1 )[0] ==
3051 sOldCurString[0] )
3053 OUString aTmp( sStrArray[i] );
3054 sal_uInt16 j = i + 1;
3055 while ( aTmp.getLength() < sOldCurSymbol.getLength() &&
3056 j < nStringsCnt &&
3057 nTypeArray[j] == NF_SYMBOLTYPE_STRING )
3059 aTmp += sStrArray[j++];
3061 if ( pChrCls->uppercase( aTmp ) == sOldCurString )
3063 sStrArray[i++] = aTmp;
3064 for ( ; i<j; i++ )
3066 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
3067 nResultStringsCnt--;
3069 i = j - 1;
3070 continue; // for
3074 OUString& rStr = sStrArray[i];
3075 sal_Int32 nLen = rStr.getLength();
3076 for ( sal_Int32 j = 0; j < nLen; j++ )
3078 bool bFoundEnglish = false;
3079 if ( (j == 0 || rStr[j - 1] != '\\') && GetKeyWord( rStr, j, bFoundEnglish) )
3081 rStr = "\"" + rStr + "\"";
3082 break; // for
3088 // Concatenate strings, remove quotes for output, and rebuild the format string
3089 rString.clear();
3090 i = 0;
3091 while (i < nStringsCnt)
3093 sal_Int32 nStringPos;
3094 sal_Int32 nArrPos = 0;
3095 sal_uInt16 iPos = i;
3096 switch ( nTypeArray[i] )
3098 case NF_SYMBOLTYPE_STRING :
3099 case NF_SYMBOLTYPE_FRACBLANK :
3100 nStringPos = rString.getLength();
3103 if (sStrArray[i].getLength() == 2 &&
3104 sStrArray[i][0] == '\\')
3106 // Unescape some simple forms of symbols even in the UI
3107 // visible string to prevent duplicates that differ
3108 // only in notation, originating from import.
3109 // e.g. YYYY-MM-DD and YYYY\-MM\-DD are identical,
3110 // but 0\ 000 0 and 0 000 0 in a French locale are not.
3112 sal_Unicode c = sStrArray[i][1];
3114 switch (c)
3116 case '+':
3117 case '-':
3118 rString += OUStringChar(c);
3119 break;
3120 case ' ':
3121 case '.':
3122 case '/':
3123 if (!(eScannedType & SvNumFormatType::DATE) &&
3124 (StringEqualsChar( pFormatter->GetNumThousandSep(), c) ||
3125 StringEqualsChar( pFormatter->GetNumDecimalSep(), c) ||
3126 (c == ' ' &&
3127 (StringEqualsChar( pFormatter->GetNumThousandSep(), cNoBreakSpace) ||
3128 StringEqualsChar( pFormatter->GetNumThousandSep(), cNarrowNoBreakSpace)))))
3130 rString += sStrArray[i];
3132 else if ((eScannedType & SvNumFormatType::DATE) &&
3133 StringEqualsChar( pFormatter->GetDateSep(), c))
3135 rString += sStrArray[i];
3137 else if ((eScannedType & SvNumFormatType::TIME) &&
3138 (StringEqualsChar( pLoc->getTimeSep(), c) ||
3139 StringEqualsChar( pLoc->getTime100SecSep(), c)))
3141 rString += sStrArray[i];
3143 else if (eScannedType & SvNumFormatType::FRACTION)
3145 rString += sStrArray[i];
3147 else
3149 rString += OUStringChar(c);
3151 break;
3152 default:
3153 rString += sStrArray[i];
3156 else
3158 rString += sStrArray[i];
3160 if ( RemoveQuotes( sStrArray[i] ) > 0 )
3162 // update currency up to quoted string
3163 if ( eScannedType == SvNumFormatType::CURRENCY )
3165 // dM -> DM or DM -> $ in old automatic
3166 // currency formats, oh my ..., why did we ever introduce them?
3167 OUString aTmp( pChrCls->uppercase( sStrArray[iPos], nArrPos,
3168 sStrArray[iPos].getLength()-nArrPos ) );
3169 sal_Int32 nCPos = aTmp.indexOf( sOldCurString );
3170 if ( nCPos >= 0 )
3172 const OUString& rCur = bConvertMode && bConvertSystemToSystem ?
3173 GetCurSymbol() : sOldCurSymbol;
3174 sStrArray[iPos] = sStrArray[iPos].replaceAt( nArrPos + nCPos,
3175 sOldCurString.getLength(),
3176 rCur );
3177 rString = rString.replaceAt( nStringPos + nCPos,
3178 sOldCurString.getLength(),
3179 rCur );
3181 nStringPos = rString.getLength();
3182 if ( iPos == i )
3184 nArrPos = sStrArray[iPos].getLength();
3186 else
3188 nArrPos = sStrArray[iPos].getLength() + sStrArray[i].getLength();
3192 if ( iPos != i )
3194 sStrArray[iPos] += sStrArray[i];
3195 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
3196 nResultStringsCnt--;
3198 i++;
3200 while ( i < nStringsCnt && nTypeArray[i] == NF_SYMBOLTYPE_STRING );
3202 if ( i < nStringsCnt )
3204 i--; // enter switch on next symbol again
3206 if ( eScannedType == SvNumFormatType::CURRENCY && nStringPos < rString.getLength() )
3208 // same as above, since last RemoveQuotes
3209 OUString aTmp( pChrCls->uppercase( sStrArray[iPos], nArrPos,
3210 sStrArray[iPos].getLength()-nArrPos ) );
3211 sal_Int32 nCPos = aTmp.indexOf( sOldCurString );
3212 if ( nCPos >= 0 )
3214 const OUString& rCur = bConvertMode && bConvertSystemToSystem ?
3215 GetCurSymbol() : sOldCurSymbol;
3216 sStrArray[iPos] = sStrArray[iPos].replaceAt( nArrPos + nCPos,
3217 sOldCurString.getLength(),
3218 rCur );
3219 rString = rString.replaceAt( nStringPos + nCPos,
3220 sOldCurString.getLength(), rCur );
3223 break;
3224 case NF_SYMBOLTYPE_CURRENCY :
3225 rString += sStrArray[i];
3226 RemoveQuotes( sStrArray[i] );
3227 break;
3228 case NF_KEY_THAI_T:
3229 if (bThaiT && GetNatNumModifier() == 1)
3231 // Remove T from format code, will be replaced with a [NatNum1] prefix.
3232 nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
3233 nResultStringsCnt--;
3235 else
3237 rString += sStrArray[i];
3239 break;
3240 case NF_SYMBOLTYPE_EMPTY :
3241 // nothing
3242 break;
3243 default:
3244 rString += sStrArray[i];
3246 i++;
3248 return 0;
3251 sal_Int32 ImpSvNumberformatScan::RemoveQuotes( OUString& rStr )
3253 if ( rStr.getLength() > 1 )
3255 sal_Unicode c = rStr[0];
3256 sal_Int32 n = rStr.getLength() - 1;
3257 if ( c == '"' && rStr[n] == '"' )
3259 rStr = rStr.copy( 1, n-1);
3260 return 2;
3262 else if ( c == '\\' )
3264 rStr = rStr.copy(1);
3265 return 1;
3268 return 0;
3271 sal_Int32 ImpSvNumberformatScan::ScanFormat( OUString& rString )
3273 sal_Int32 res = Symbol_Division(rString); // Lexical analysis
3274 if (!res)
3276 res = ScanType(); // Recognizing the Format type
3278 if (!res)
3280 res = FinalScan( rString ); // Type dependent final analysis
3282 return res; // res = control position; res = 0 => Format ok
3285 void ImpSvNumberformatScan::CopyInfo(ImpSvNumberformatInfo* pInfo, sal_uInt16 nCnt)
3287 size_t i,j;
3288 j = 0;
3289 i = 0;
3290 while (i < nCnt && j < NF_MAX_FORMAT_SYMBOLS)
3292 if (nTypeArray[j] != NF_SYMBOLTYPE_EMPTY)
3294 pInfo->sStrArray[i] = sStrArray[j];
3295 pInfo->nTypeArray[i] = nTypeArray[j];
3296 i++;
3298 j++;
3300 pInfo->eScannedType = eScannedType;
3301 pInfo->bThousand = bThousand;
3302 pInfo->nThousand = nThousand;
3303 pInfo->nCntPre = nCntPre;
3304 pInfo->nCntPost = nCntPost;
3305 pInfo->nCntExp = nCntExp;
3308 bool ImpSvNumberformatScan::ReplaceBooleanEquivalent( OUString& rString )
3310 InitKeywords();
3311 /* TODO: compare case insensitive? Or rather leave as is and case not
3312 * matching indicates user supplied on purpose? Written to file / generated
3313 * was always uppercase. */
3314 if (rString == sBooleanEquivalent1 || rString == sBooleanEquivalent2)
3316 rString = GetKeywords()[NF_KEY_BOOLEAN];
3317 return true;
3319 return false;
3322 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */