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