1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
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
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
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
75 "AAAA", // NF_KEY_AAAA
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
96 "BLACK", // NF_KEY_BLACK
97 "BLUE", // NF_KEY_BLUE
98 "GREEN", // NF_KEY_GREEN
99 "CYAN", // NF_KEY_CYAN
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",
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
)
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");
149 ImpSvNumberformatScan::~ImpSvNumberformatScan()
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
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
];
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
];
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
)
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
)
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
219 while (p
< pStop
&& !bDone
)
232 // else: would be a locale data error, easily to be spotted in
239 --p
; // put back, increment by one follows
251 aStr
= rCode
.copy( pBeg
- rCode
.getStr(), p
- pBeg
);
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
);
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
,
279 LANGUAGE_DUTCH_BELGIAN
,
281 LANGUAGE_FRENCH_BELGIAN
,
282 LANGUAGE_FRENCH_CANADIAN
,
283 LANGUAGE_FRENCH_SWISS
,
284 LANGUAGE_FRENCH_LUXEMBOURG
,
285 LANGUAGE_FRENCH_MONACO
,
288 LANGUAGE_ITALIAN_SWISS
,
291 LANGUAGE_NORWEGIAN_BOKMAL
,
292 LANGUAGE_NORWEGIAN_NYNORSK
,
294 LANGUAGE_SWEDISH_FINLAND
,
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
))
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";
339 sKeyword
[NF_KEY_THAI_T
] = sEnglishKeyword
[NF_KEY_THAI_T
];
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
362 // All locale dependent keywords overrides follow.
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
);
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(
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";
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";
448 LANGUAGE_ITALIAN_SWISS
,
450 LANGUAGE_FRENCH_BELGIAN
,
451 LANGUAGE_FRENCH_CANADIAN
,
452 LANGUAGE_FRENCH_SWISS
,
453 LANGUAGE_FRENCH_LUXEMBOURG
,
454 LANGUAGE_FRENCH_MONACO
,
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(
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";
500 LANGUAGE_DUTCH_BELGIAN
))
502 sKeyword
[NF_KEY_H
] = "U";
503 sKeyword
[NF_KEY_HH
] = "UU";
505 else if ( eLang
.anyOf(
508 LANGUAGE_SWEDISH_FINLAND
,
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();
542 while (i
< NF_MAX_DEFAULT_COLORS
&& sString
!= rKeyword
[NF_KEY_FIRSTCOLOR
+i
] )
546 if (i
>= NF_MAX_DEFAULT_COLORS
&& meKeywordLocalization
== KeywordLocalization::AllowEnglish
)
548 LanguageType eLang
= pFormatter
->GetLocaleData()->getLoadedLanguageTag().getLanguageType( false);
551 LANGUAGE_GERMAN_SWISS
,
552 LANGUAGE_GERMAN_AUSTRIAN
,
553 LANGUAGE_GERMAN_LUXEMBOURG
,
554 LANGUAGE_GERMAN_LIECHTENSTEIN
)) // only German uses localized color names
557 while ( j
< NF_MAX_DEFAULT_COLORS
&& sString
!= sEnglishKeyword
[NF_KEY_FIRSTCOLOR
+ j
] )
561 if ( j
< NF_MAX_DEFAULT_COLORS
)
568 enum ColorKeywordConversion
573 } eColorKeywordConversion(None
);
577 const bool bFromGerman
= eTmpLnge
.anyOf(
579 LANGUAGE_GERMAN_SWISS
,
580 LANGUAGE_GERMAN_AUSTRIAN
,
581 LANGUAGE_GERMAN_LUXEMBOURG
,
582 LANGUAGE_GERMAN_LIECHTENSTEIN
);
583 const bool bToGerman
= eNewLnge
.anyOf(
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
];
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
;
612 case ColorKeywordConversion::GermanToEnglish
:
613 sStr
= sEnglishKeyword
[NF_KEY_COLOR
] + sStr
; // Farbe -> COLOR
615 case ColorKeywordConversion::EnglishToGerman
:
616 sStr
= GermanColorName(NF_KEY_COLOR
- NF_KEY_COLOR
) + sStr
; // Color -> FARBE
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);
635 switch (eColorKeywordConversion
)
637 case ColorKeywordConversion::None
:
638 sStr
= rKeyword
[NF_KEY_FIRSTCOLOR
+i
];
640 case ColorKeywordConversion::GermanToEnglish
:
641 sStr
= sEnglishKeyword
[NF_KEY_FIRSTCOLOR
+ i
]; // Rot -> RED
643 case ColorKeywordConversion::EnglishToGerman
:
644 sStr
= GermanColorName(NF_KEY_FIRSTCOLOR
- NF_KEY_COLOR
+ i
); // Red -> ROT
647 pResult
= &(StandardColor
[i
]);
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
]))
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
]))
687 // The Thai T NatNum modifier during Xcl import.
688 if (i
== 0 && bConvertMode
&&
690 eTmpLnge
== LANGUAGE_ENGLISH_US
&&
691 MsLangId::getRealLanguage( eNewLnge
) == LANGUAGE_THAI
)
695 return i
; // 0 => not found
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!
754 short ImpSvNumberformatScan::Next_Symbol( const OUString
& rStr
,
756 OUString
& sSymbol
) const
759 const CharClass
* pChrCls
= pFormatter
->GetCharClass();
760 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
762 ScanState eState
= SsStart
;
763 OUStringBuffer sSymbolBuffer
;
764 while ( nPos
< rStr
.getLength() && eState
!= SsStop
)
766 sal_Unicode cToken
= rStr
[nPos
++];
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
;
805 eType
= NF_SYMBOLTYPE_DEL
;
806 sSymbolBuffer
.append(OUStringChar(cToken
));
810 eType
= NF_SYMBOLTYPE_STAR
;
811 sSymbolBuffer
.append(OUStringChar(cToken
));
815 eType
= NF_SYMBOLTYPE_BLANK
;
816 sSymbolBuffer
.append(OUStringChar(cToken
));
820 eType
= NF_SYMBOLTYPE_STRING
;
821 eState
= SsGetString
;
822 sSymbolBuffer
.append(OUStringChar(cToken
));
825 eType
= NF_SYMBOLTYPE_STRING
;
827 sSymbolBuffer
.append(OUStringChar(cToken
));
833 eType
= NF_SYMBOLTYPE_STRING
;
835 sSymbolBuffer
.append(OUStringChar(cToken
));
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
));
849 else if ( pChrCls
->isLetter( rStr
, nPos
-1 ) )
851 bool bFoundEnglish
= false;
852 short nTmpType
= GetKeyWord( rStr
, nPos
-1, bFoundEnglish
);
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
)
871 sSymbolBuffer
.append(OUStringChar(cToken
));
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.
882 nLen
= sEnglishKeyword
[eType
].getLength();
883 // Use the locale's General keyword name, not uppercase.
884 sSymbolBuffer
= (eType
== NF_KEY_GENERAL
? sNameStandardFormat
: sKeyword
[eType
]);
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
];
898 case '-' : // E+ E- combine to one symbol
899 sSymbolBuffer
.append(OUStringChar(cNext
));
904 case '#' : // scientific E without sign
917 sSymbolBuffer
.append(OUStringChar(cToken
));
922 eType
= NF_SYMBOLTYPE_STRING
;
924 sSymbolBuffer
.append(OUStringChar(cToken
));
930 sSymbolBuffer
.append(OUStringChar(cToken
));
938 sSymbolBuffer
.append(OUStringChar(cToken
));
941 if ( pChrCls
->isLetter( rStr
, nPos
-1 ) )
943 bool bFoundEnglish
= false;
944 short nTmpType
= GetKeyWord( rStr
, nPos
-1, bFoundEnglish
);
947 // beginning of keyword, stop scan and put back
948 eType
= NF_SYMBOLTYPE_STRING
;
954 sSymbolBuffer
.append(OUStringChar(cToken
));
959 bool bDontStop
= false;
963 case '/': // AM/PM, A/P
964 if (nPos
< rStr
.getLength())
967 if ( cNext
== 'P' || cNext
== 'p' )
969 sal_Int32 nLen
= sSymbolBuffer
.getLength();
971 (sSymbolBuffer
[0] == 'A' || sSymbolBuffer
[0] == 'a') &&
973 (nLen
== 2 && (sSymbolBuffer
[1] == 'M' || sSymbolBuffer
[1] == 'm')
974 && (rStr
[nPos
+ 1] == 'M' || rStr
[nPos
+ 1] == 'm'))))
976 sSymbolBuffer
.append(OUStringChar(cToken
));
983 // anything not recognized will stop the scan
988 eType
= NF_SYMBOLTYPE_STRING
;
994 sSymbolBuffer
.append(OUStringChar(cToken
));
998 sSymbolBuffer
.append(OUStringChar(cToken
));
1004 if (eState
== SsGetWord
)
1006 eType
= NF_SYMBOLTYPE_STRING
;
1008 sSymbol
= sSymbolBuffer
.makeStringAndClear();
1012 sal_Int32
ImpSvNumberformatScan::Symbol_Division(const OUString
& rString
)
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
);
1024 sal_Int32 nQ
= SvNumberformat::GetQuoteEnd( sString
, nCPos
);
1029 ((c
= sString
[nCPos
-1]) != '"'
1030 && c
!= '\\') ) // dm can be protected by "dm \d
1037 nCPos
++; // Continue search
1042 nCPos
= nQ
+ 1; // Continue search
1047 bool bStar
= false; // Is set on detecting '*'
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 '*'
1059 return nPos
; // Error: double '*'
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
1066 if (sStrArray
[nStringsCnt
].getLength() < 2)
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();
1088 sal_uInt16
ImpSvNumberformatScan::PreviousKeyword(sal_uInt16 i
) const
1091 if (i
> 0 && i
< nStringsCnt
)
1094 while (i
> 0 && nTypeArray
[i
] <= 0)
1098 if (nTypeArray
[i
] > 0)
1100 res
= nTypeArray
[i
];
1106 sal_uInt16
ImpSvNumberformatScan::NextKeyword(sal_uInt16 i
) const
1109 if (i
< nStringsCnt
-1)
1112 while (i
< nStringsCnt
-1 && nTypeArray
[i
] <= 0)
1116 if (nTypeArray
[i
] > 0)
1118 res
= nTypeArray
[i
];
1124 short ImpSvNumberformatScan::PreviousType( sal_uInt16 i
) const
1126 if ( i
> 0 && i
< nStringsCnt
)
1132 while ( i
> 0 && nTypeArray
[i
] == NF_SYMBOLTYPE_EMPTY
);
1133 return nTypeArray
[i
];
1138 sal_Unicode
ImpSvNumberformatScan::PreviousChar(sal_uInt16 i
) const
1140 sal_Unicode res
= ' ';
1141 if (i
> 0 && i
< nStringsCnt
)
1145 ( nTypeArray
[i
] == NF_SYMBOLTYPE_EMPTY
||
1146 nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
||
1147 nTypeArray
[i
] == NF_SYMBOLTYPE_STAR
||
1148 nTypeArray
[i
] == NF_SYMBOLTYPE_BLANK
))
1152 if (sStrArray
[i
].getLength() > 0)
1154 res
= sStrArray
[i
][sStrArray
[i
].getLength()-1];
1160 sal_Unicode
ImpSvNumberformatScan::NextChar(sal_uInt16 i
) const
1162 sal_Unicode res
= ' ';
1163 if (i
< nStringsCnt
-1)
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
))
1174 if (sStrArray
[i
].getLength() > 0)
1176 res
= sStrArray
[i
][0];
1182 bool ImpSvNumberformatScan::IsLastBlankBeforeFrac(sal_uInt16 i
) const
1185 if (i
< nStringsCnt
-1)
1189 while (i
< nStringsCnt
-1 && !bStop
)
1192 if ( nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
&&
1193 sStrArray
[i
][0] == '/')
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
1204 if (!bStop
) // no '/'{
1211 res
= false; // no '/' any more
1216 void ImpSvNumberformatScan::Reset()
1219 nResultStringsCnt
= 0;
1220 eScannedType
= SvNumFormatType::UNDEFINED
;
1225 nDecPos
= sal_uInt16(-1);
1226 nExpPos
= sal_uInt16(-1);
1227 nBlankPos
= sal_uInt16(-1);
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
) &&
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();
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)
1262 sal_uInt16 nIndexPre
;
1263 sal_uInt16 nIndexNex
;
1265 switch (nTypeArray
[i
])
1268 eNewType
= SvNumFormatType::SCIENTIFIC
;
1271 case NF_KEY_HH
: // HH
1272 bIsTimeDetected
= true;
1275 case NF_KEY_SS
: // SS
1277 bIsTimeDetected
= true;
1279 case NF_KEY_AMPM
: // AM,A,PM,P
1281 eNewType
= SvNumFormatType::TIME
;
1284 case NF_KEY_MM
: // MM
1285 case NF_KEY_MI
: // M minute detected in Finnish
1286 case NF_KEY_MMI
: // MM
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
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
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
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
1326 case NF_KEY_MMM
: // MMM
1327 case NF_KEY_MMMM
: // MMMM
1328 case NF_KEY_MMMMM
: // MMMMM
1330 case NF_KEY_QQ
: // QQ
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;
1353 case NF_KEY_CCC
: // CCC
1354 eNewType
= SvNumFormatType::CURRENCY
;
1356 case NF_KEY_BOOLEAN
: // BOOLEAN
1357 eNewType
= SvNumFormatType::LOGICAL
;
1359 case NF_KEY_GENERAL
: // General
1360 eNewType
= SvNumFormatType::NUMBER
;
1361 bHaveGeneral
= true;
1364 eNewType
= SvNumFormatType::UNDEFINED
;
1369 { // control character
1370 switch ( sStrArray
[i
][0] )
1374 eNewType
= SvNumFormatType::NUMBER
;
1377 if ( eScannedType
& SvNumFormatType::TIME
)
1379 if ( Is100SecZero( i
, bDecSep
) )
1381 bDecSep
= true; // subsequent 0's
1382 eNewType
= SvNumFormatType::TIME
;
1386 return nPos
; // Error
1391 eNewType
= SvNumFormatType::NUMBER
;
1395 eNewType
= SvNumFormatType::PERCENT
;
1398 eNewType
= SvNumFormatType::FRACTION
;
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;
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
;
1427 return nPos
; // Error
1432 eNewType
= SvNumFormatType::TEXT
;
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
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] == '.'))
1462 return nPos
; // Error
1464 else if (pLoc
->getTime100SecSep() == sStrArray
[i
])
1467 eNewType
= SvNumFormatType::UNDEFINED
;
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
)
1482 else if (eScannedType
!= eNewType
)
1484 switch (eScannedType
)
1486 case SvNumFormatType::DATE
:
1489 case SvNumFormatType::TIME
:
1490 eScannedType
= SvNumFormatType::DATETIME
;
1492 case SvNumFormatType::FRACTION
: // DD/MM
1497 eScannedType
= SvNumFormatType::UNDEFINED
;
1499 else if ( sStrArray
[i
] != pFormatter
->GetDateSep() )
1505 case SvNumFormatType::TIME
:
1508 case SvNumFormatType::DATE
:
1509 eScannedType
= SvNumFormatType::DATETIME
;
1511 case SvNumFormatType::FRACTION
: // MM/SS
1516 eScannedType
= SvNumFormatType::UNDEFINED
;
1518 else if (pLoc
->getTimeSep() != sStrArray
[i
])
1525 case SvNumFormatType::DATETIME
:
1528 case SvNumFormatType::TIME
:
1529 case SvNumFormatType::DATE
:
1531 case SvNumFormatType::FRACTION
: // DD/MM
1536 eScannedType
= SvNumFormatType::UNDEFINED
;
1538 else if ( pFormatter
->GetDateSep() != sStrArray
[i
] &&
1539 pLoc
->getTimeSep() != sStrArray
[i
] )
1545 case SvNumFormatType::PERCENT
:
1548 case SvNumFormatType::NUMBER
: // Only number to percent
1554 case SvNumFormatType::SCIENTIFIC
:
1557 case SvNumFormatType::NUMBER
: // Only number to E
1563 case SvNumFormatType::NUMBER
:
1566 case SvNumFormatType::SCIENTIFIC
:
1567 case SvNumFormatType::PERCENT
:
1568 case SvNumFormatType::FRACTION
:
1569 case SvNumFormatType::CURRENCY
:
1570 eScannedType
= eNewType
;
1575 eScannedType
= SvNumFormatType::UNDEFINED
;
1583 case SvNumFormatType::FRACTION
:
1586 case SvNumFormatType::NUMBER
: // Only number to fraction
1596 nPos
= nPos
+ sStrArray
[i
].getLength(); // Position of correction
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;
1609 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1611 nPos
= nPos
+ sStrArray
[i
].getLength();
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
)
1641 if (nPos
> 0 && nTypeArray
[nPos
-1] == NF_SYMBOLTYPE_EMPTY
)
1643 --nPos
; // reuse position
1647 if (nStringsCnt
>= NF_MAX_FORMAT_SYMBOLS
- 1)
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
;
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] == '~' )
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
)
1683 nPos
= nPos
+ sStrArray
[i
].getLength(); // calendarID
1684 OUString
& rStr
= sStrArray
[i
];
1685 nTypeArray
[i
] = NF_SYMBOLTYPE_CALENDAR
; // convert
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
--;
1695 if ( rStr
.getLength() && i
< nStringsCnt
&&
1696 sStrArray
[i
][0] == ']' )
1698 nTypeArray
[i
] = NF_SYMBOLTYPE_CALDEL
;
1699 nPos
= nPos
+ sStrArray
[i
].getLength();
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
1755 pFormatter
->ChangeIntl(eNewLnge
);
1756 //! pointer may have changed
1757 pLoc
= pFormatter
->GetLocaleData();
1758 //! init new keywords
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
:
1787 case NF_KEY_GENERAL
: // #77026# "General" is the same as "@"
1790 if ( nTypeArray
[i
] != NF_SYMBOLTYPE_DEL
||
1791 sStrArray
[i
][0] != '@' )
1793 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1797 nPos
= nPos
+ sStrArray
[i
].getLength();
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
], ' ' ) &&
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
;
1831 sStrArray
[i
] = sNameStandardFormat
;
1834 nPos
= nPos
+ sStrArray
[i
].getLength();
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+
1851 nCntPost
= nCounter
;
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
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
)
1886 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]];
1888 else if (sStrArray
[i
][0] >= '0' &&
1889 sStrArray
[i
][0] <= '9' && !bDenomin
) // denominator was not yet found
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
)
1903 nTypeArray
[i
++] = NF_SYMBOLTYPE_FRAC_FDIV
;
1905 i
= j
- 1; // Stop the loop
1908 nCounter
= nCntPost
;
1914 // don't artificially increment nCntPre for forced denominator
1915 if ( ( eScannedType
!= SvNumFormatType::FRACTION
) && (!nCntPre
) )
1920 bDenomin
= true; // next content should be treated as outside denominator
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();
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
))
1942 else if (StringEqualsChar( pFormatter
->GetNumDecimalSep(), cHere
))
1948 cSimplified
= cHere
;
1951 OUString
& rStr
= sStrArray
[i
];
1953 switch ( cSimplified
)
1958 if (nThousand
> 0) // #... #
1960 return nPos
; // Error
1964 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
1965 nPos
= nPos
+ rStr
.getLength();
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();
1979 else // after denominator, treat any character as text
1981 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1982 nPos
= nPos
+ sStrArray
[i
].getLength();
1986 if ( bDecSep
&& nDecPos
+1 == i
&&
1987 nTypeArray
[nDecPos
] == NF_SYMBOLTYPE_DECSEP
)
1990 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
1991 nPos
= nPos
+ rStr
.getLength();
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] != '-') )
2008 rStr
+= sStrArray
[i
];
2009 nPos
= nPos
+ sStrArray
[i
].getLength();
2010 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2011 nResultStringsCnt
--;
2018 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2019 nPos
= nPos
+ sStrArray
[i
].getLength();
2027 if ( StringEqualsChar( sOldThousandSep
, cSaved
) )
2029 // previous char with skip empty
2030 sal_Unicode cPre
= PreviousChar(i
);
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
--;
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
2058 // Eat it, will be reinserted at proper grouping positions further down.
2059 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2060 nResultStringsCnt
--;
2063 else if (i
> 0 && (cPre
== '#' || cPre
== '0' || cPre
== '?')
2064 && PreviousType(i
) == NF_SYMBOLTYPE_DIGIT
2065 && nThousand
< FLAG_STANDARD_IN_FORMAT
)
2067 if ( StringEqualsChar( sOldThousandSep
, ' ' ) )
2069 // strange, those French...
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();
2083 nTypeArray
[i
] = NF_SYMBOLTYPE_THSEP
;
2088 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2089 nResultStringsCnt
--;
2094 if ( i
< nStringsCnt
-1 &&
2095 sStrArray
[i
] == sOldThousandSep
)
2097 // something following last space
2098 // => space if currency contained,
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
;
2115 nTypeArray
[i
] = NF_SYMBOLTYPE_THSEP
;
2120 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2121 nResultStringsCnt
--;
2133 nTypeArray
[i
] = NF_SYMBOLTYPE_THSEP
;
2134 nPos
= nPos
+ sStrArray
[i
].getLength();
2135 sStrArray
[i
] = pFormatter
->GetNumThousandSep();
2138 while (i
< nStringsCnt
&& sStrArray
[i
] == sOldThousandSep
);
2143 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2144 nPos
= nPos
+ rStr
.getLength();
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
--;
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
--;
2169 else if (bDecSep
) // any .
2171 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2172 nPos
= nPos
+ rStr
.getLength();
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
--;
2185 nPos
= nPos
+ sStrArray
[i
].getLength();
2186 nTypeArray
[i
] = NF_SYMBOLTYPE_DECSEP
;
2187 sStrArray
[i
] = pFormatter
->GetNumDecimalSep();
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
2204 if (bDecSep
&& nCounter
> 0) // dec.
2206 return nPos
; // error
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();
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();
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
--;
2237 if (eScannedType
== SvNumFormatType::FRACTION
)
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))
2248 nCntPost
= nCounter
;
2250 nTypeArray
[i
] = NF_SYMBOLTYPE_FRAC
;
2251 nPos
= nPos
+ sStrArray
[i
].getLength();
2254 else // / double or in , in the denominator
2256 return nPos
; // Error
2261 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2262 nPos
= nPos
+ sStrArray
[i
].getLength();
2267 if ( eScannedType
== SvNumFormatType::CURRENCY
&&
2268 i
< nStringsCnt
-1 &&
2269 nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
2270 sStrArray
[i
+1][0] == '$' )
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;
2288 while ( i
< nStringsCnt
&& sStrArray
[i
][0] != ']' )
2290 nPos
= nPos
+ sStrArray
[i
].getLength();
2293 *pStr
+= sStrArray
[i
];
2294 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2295 nResultStringsCnt
--;
2299 if ( sStrArray
[i
][0] == '-' )
2302 pStr
= &sStrArray
[i
];
2303 nTypeArray
[i
] = NF_SYMBOLTYPE_CURREXT
;
2307 *pStr
+= sStrArray
[i
];
2308 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2309 nResultStringsCnt
--;
2314 if ( rStr
.getLength() && i
< nStringsCnt
&& sStrArray
[i
][0] == ']' )
2316 nTypeArray
[i
] = NF_SYMBOLTYPE_CURRDEL
;
2317 nPos
= nPos
+ sStrArray
[i
].getLength();
2322 return nPos
; // Error
2327 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2328 nPos
= nPos
+ sStrArray
[i
].getLength();
2332 default: // Other Dels
2333 if (eScannedType
== SvNumFormatType::PERCENT
&& cHere
== '%')
2335 nTypeArray
[i
] = NF_SYMBOLTYPE_PERCENT
;
2339 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2341 nPos
= nPos
+ sStrArray
[i
].getLength();
2344 } // of switch (Del)
2348 SAL_WARN( "svl.numbers", "unknown NF_SYMBOLTYPE_..." );
2349 nPos
= nPos
+ sStrArray
[i
].getLength();
2353 if (eScannedType
== SvNumFormatType::FRACTION
)
2361 nCntPost
= nCounter
;
2376 nCntPost
= nCounter
;
2383 if (bThousand
) // Expansion of grouping separators
2390 nMaxPos
= nBlankPos
;
2394 nMaxPos
= 0; // no grouping
2397 else if (bDecSep
) // decimal separator present
2401 else if (bExp
) // 'E' exponent present
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
;
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
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();
2472 case NF_SYMBOLTYPE_DEL
:
2474 if (sStrArray
[i
] == sOldDateSep
)
2476 nTypeArray
[i
] = NF_SYMBOLTYPE_DATESEP
;
2477 nPos
= nPos
+ sStrArray
[i
].getLength();
2480 sStrArray
[i
] = pFormatter
->GetDateSep();
2484 else if ( (nCalRet
= FinalScanGetCalendar( nPos
, i
, nResultStringsCnt
)) != 0 )
2488 return nPos
; // error
2493 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2494 nPos
= nPos
+ sStrArray
[i
].getLength();
2498 case NF_KEY_THAI_T
:
2502 case NF_KEY_MM
: // MM
2503 case NF_KEY_MMM
: // MMM
2504 case NF_KEY_MMMM
: // MMMM
2505 case NF_KEY_MMMMM
: // MMMMM
2507 case NF_KEY_QQ
: // QQ
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();
2531 // For simple numeric date formats record date order and
2533 switch (nTypeArray
[i
])
2537 if (nMonthPos
== SAL_MAX_UINT16
)
2540 bNewDateOrder
= false;
2544 if (nDayPos
== SAL_MAX_UINT16
)
2547 bNewDateOrder
= false;
2551 if (nYearPos
== SAL_MAX_UINT16
)
2554 bNewDateOrder
= false;
2562 default: // Other keywords
2563 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2564 nPos
= nPos
+ sStrArray
[i
].getLength();
2569 break; // of SvNumFormatType::DATE
2570 case SvNumFormatType::TIME
:
2571 while (i
< nStringsCnt
)
2575 switch (nTypeArray
[i
])
2577 case NF_SYMBOLTYPE_BLANK
:
2578 case NF_SYMBOLTYPE_STAR
:
2579 nPos
= nPos
+ sStrArray
[i
].getLength();
2582 case NF_SYMBOLTYPE_DEL
:
2583 switch( sStrArray
[i
][0] )
2586 if ( Is100SecZero( i
, bDecSep
) )
2589 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
2590 OUString
& rStr
= sStrArray
[i
];
2593 while (i
< nStringsCnt
&&
2594 sStrArray
[i
][0] == '0')
2596 rStr
+= sStrArray
[i
];
2597 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2598 nResultStringsCnt
--;
2602 nPos
+= rStr
.getLength();
2613 if (bThousand
) // Double
2617 bThousand
= true; // Empty for Time
2618 cChar
= pChrCls
->uppercase(OUString(NextChar(i
)))[0];
2619 if ( cChar
== cOldKeyH
)
2623 else if ( cChar
== cOldKeyMI
)
2627 else if ( cChar
== cOldKeyS
)
2635 nPos
= nPos
+ sStrArray
[i
].getLength();
2639 if (!bThousand
) // No preceding [
2643 nPos
= nPos
+ sStrArray
[i
].getLength();
2647 nPos
= nPos
+ sStrArray
[i
].getLength();
2648 if ( sStrArray
[i
] == sOldTimeSep
)
2650 nTypeArray
[i
] = NF_SYMBOLTYPE_TIMESEP
;
2653 sStrArray
[i
] = pLoc
->getTimeSep();
2656 else if ( sStrArray
[i
] == sOldTime100SecSep
)
2659 nTypeArray
[i
] = NF_SYMBOLTYPE_TIME100SECSEP
;
2662 sStrArray
[i
] = pLoc
->getTime100SecSep();
2667 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2673 case NF_SYMBOLTYPE_STRING
:
2674 nPos
= nPos
+ sStrArray
[i
].getLength();
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();
2684 case NF_KEY_THAI_T
:
2687 case NF_KEY_MI
: // M
2688 case NF_KEY_MMI
: // MM
2690 case NF_KEY_HH
: // HH
2692 case NF_KEY_SS
: // SS
2693 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2694 nPos
= nPos
+ sStrArray
[i
].getLength();
2697 default: // Other keywords
2698 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2699 nPos
= nPos
+ sStrArray
[i
].getLength();
2704 nCntPost
= nCounter
; // Zero counter
2707 nCntExp
= 1; // Remembers AM/PM
2709 break; // of SvNumFormatType::TIME
2710 case SvNumFormatType::DATETIME
:
2711 while (i
< nStringsCnt
)
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();
2722 case NF_SYMBOLTYPE_DEL
:
2723 if ( (nCalRet
= FinalScanGetCalendar( nPos
, i
, nResultStringsCnt
)) != 0 )
2727 return nPos
; // Error
2732 switch( sStrArray
[i
][0] )
2735 if (bTimePart
&& Is100SecZero(i
, bDecSep
) && nCounter
< MaxCntPost
)
2738 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
2739 OUString
& rStr
= sStrArray
[i
];
2742 while (i
< nStringsCnt
&&
2743 sStrArray
[i
][0] == '0' && nCounter
< MaxCntPost
)
2745 rStr
+= sStrArray
[i
];
2746 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2747 nResultStringsCnt
--;
2751 nPos
+= rStr
.getLength();
2762 nPos
= nPos
+ sStrArray
[i
].getLength();
2765 if ( sStrArray
[i
] == sOldTimeSep
)
2767 nTypeArray
[i
] = NF_SYMBOLTYPE_TIMESEP
;
2770 sStrArray
[i
] = pLoc
->getTimeSep();
2773 else if ( sStrArray
[i
] == sOldTime100SecSep
)
2776 nTypeArray
[i
] = NF_SYMBOLTYPE_TIME100SECSEP
;
2779 sStrArray
[i
] = pLoc
->getTime100SecSep();
2784 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2789 if ( sStrArray
[i
] == sOldDateSep
)
2791 nTypeArray
[i
] = NF_SYMBOLTYPE_DATESEP
;
2793 sStrArray
[i
] = pFormatter
->GetDateSep();
2797 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2805 case NF_KEY_AMPM
: // AM/PM
2806 case NF_KEY_AP
: // A/P
2808 bExp
= true; // Abuse for A/P
2809 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2810 nPos
= nPos
+ sStrArray
[i
].getLength();
2813 case NF_KEY_MI
: // M
2814 case NF_KEY_MMI
: // MM
2816 case NF_KEY_HH
: // HH
2818 case NF_KEY_SS
: // SS
2820 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2821 nPos
= nPos
+ sStrArray
[i
].getLength();
2825 case NF_KEY_MM
: // MM
2826 case NF_KEY_MMM
: // MMM
2827 case NF_KEY_MMMM
: // MMMM
2828 case NF_KEY_MMMMM
: // MMMMM
2830 case NF_KEY_QQ
: // QQ
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
2851 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2852 nPos
= nPos
+ sStrArray
[i
].getLength();
2855 // For simple numeric date formats record date order and
2857 switch (nTypeArray
[i
])
2861 if (nMonthPos
== SAL_MAX_UINT16
)
2864 bNewDateOrder
= false;
2868 if (nDayPos
== SAL_MAX_UINT16
)
2871 bNewDateOrder
= false;
2875 if (nYearPos
== SAL_MAX_UINT16
)
2878 bNewDateOrder
= false;
2886 case NF_KEY_THAI_T
:
2888 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]];
2889 nPos
= nPos
+ sStrArray
[i
].getLength();
2892 default: // Other keywords
2893 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2894 nPos
= nPos
+ sStrArray
[i
].getLength();
2899 nCntPost
= nCounter
; // decimals (100th seconds)
2902 nCntExp
= 1; // Remembers AM/PM
2904 break; // of SvNumFormatType::DATETIME
2908 if (eScannedType
== SvNumFormatType::SCIENTIFIC
&&
2909 (nCntPre
+ nCntPost
== 0 || nCntExp
== 0))
2913 else if (eScannedType
== SvNumFormatType::FRACTION
&& (nCntExp
> 8 || nCntExp
== 0))
2917 if (bThaiT
&& !GetNatNumModifier())
2919 SetNatNumModifier(1);
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;
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
2958 if (IsDateFragment( nDayPos
, nMonthPos
) && !IsDateFragment( nYearPos
, nDayPos
))
2959 SwapArrayElements( nDayPos
, nMonthPos
);
2961 case DateOrder::YMD
:
2962 if (nYearPos
!= SAL_MAX_UINT16
)
2964 if (IsDateFragment( nDayPos
, nMonthPos
) && IsDateFragment( nMonthPos
, nYearPos
))
2965 SwapArrayElements( nDayPos
, nYearPos
);
2969 if (IsDateFragment( nDayPos
, nMonthPos
))
2970 SwapArrayElements( nDayPos
, nMonthPos
);
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
);
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
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
);
3012 if (IsDateFragment( nMonthPos
, nDayPos
))
3013 SwapArrayElements( nMonthPos
, nDayPos
);
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
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
)
3048 // DM might be split into D and M
3049 if ( sStrArray
[i
].getLength() < sOldCurSymbol
.getLength() &&
3050 pChrCls
->uppercase( sStrArray
[i
], 0, 1 )[0] ==
3053 OUString
aTmp( sStrArray
[i
] );
3054 sal_uInt16 j
= i
+ 1;
3055 while ( aTmp
.getLength() < sOldCurSymbol
.getLength() &&
3057 nTypeArray
[j
] == NF_SYMBOLTYPE_STRING
)
3059 aTmp
+= sStrArray
[j
++];
3061 if ( pChrCls
->uppercase( aTmp
) == sOldCurString
)
3063 sStrArray
[i
++] = aTmp
;
3066 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
3067 nResultStringsCnt
--;
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
+ "\"";
3088 // Concatenate strings, remove quotes for output, and rebuild the format string
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];
3118 rString
+= OUStringChar(c
);
3123 if (!(eScannedType
& SvNumFormatType::DATE
) &&
3124 (StringEqualsChar( pFormatter
->GetNumThousandSep(), c
) ||
3125 StringEqualsChar( pFormatter
->GetNumDecimalSep(), 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
];
3149 rString
+= OUStringChar(c
);
3153 rString
+= sStrArray
[i
];
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
);
3172 const OUString
& rCur
= bConvertMode
&& bConvertSystemToSystem
?
3173 GetCurSymbol() : sOldCurSymbol
;
3174 sStrArray
[iPos
] = sStrArray
[iPos
].replaceAt( nArrPos
+ nCPos
,
3175 sOldCurString
.getLength(),
3177 rString
= rString
.replaceAt( nStringPos
+ nCPos
,
3178 sOldCurString
.getLength(),
3181 nStringPos
= rString
.getLength();
3184 nArrPos
= sStrArray
[iPos
].getLength();
3188 nArrPos
= sStrArray
[iPos
].getLength() + sStrArray
[i
].getLength();
3194 sStrArray
[iPos
] += sStrArray
[i
];
3195 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
3196 nResultStringsCnt
--;
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
);
3214 const OUString
& rCur
= bConvertMode
&& bConvertSystemToSystem
?
3215 GetCurSymbol() : sOldCurSymbol
;
3216 sStrArray
[iPos
] = sStrArray
[iPos
].replaceAt( nArrPos
+ nCPos
,
3217 sOldCurString
.getLength(),
3219 rString
= rString
.replaceAt( nStringPos
+ nCPos
,
3220 sOldCurString
.getLength(), rCur
);
3224 case NF_SYMBOLTYPE_CURRENCY
:
3225 rString
+= sStrArray
[i
];
3226 RemoveQuotes( sStrArray
[i
] );
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
--;
3237 rString
+= sStrArray
[i
];
3240 case NF_SYMBOLTYPE_EMPTY
:
3244 rString
+= sStrArray
[i
];
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);
3262 else if ( c
== '\\' )
3264 rStr
= rStr
.copy(1);
3271 sal_Int32
ImpSvNumberformatScan::ScanFormat( OUString
& rString
)
3273 sal_Int32 res
= Symbol_Division(rString
); // Lexical analysis
3276 res
= ScanType(); // Recognizing the Format type
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
)
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
];
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
)
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
];
3322 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */