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>
30 #include <com/sun/star/i18n/XNumberFormatCode.hpp>
32 #include <svl/zforlist.hxx>
33 #include <svl/zformat.hxx>
34 #include <unotools/digitgroupingiterator.hxx>
36 #include "zforscan.hxx"
38 #include <svl/nfsymbol.hxx>
41 const sal_Unicode cNoBreakSpace
= 0xA0;
42 const sal_Unicode cNarrowNoBreakSpace
= 0x202F;
44 const int MaxCntPost
= 20; //max dec places allow by rtl_math_round
46 const NfKeywordTable
ImpSvNumberformatScan::sEnglishKeyword
=
47 { // Syntax keywords in English (USA)
48 //! All keywords MUST be UPPERCASE! In same order as NfKeywordIndex
50 "E", // NF_KEY_E Exponent
51 "AM/PM", // NF_KEY_AMPM AM/PM
52 "A/P", // NF_KEY_AP AM/PM short
53 "M", // NF_KEY_MI Minute
54 "MM", // NF_KEY_MMI Minute 02
55 "M", // NF_KEY_M month (!)
56 "MM", // NF_KEY_MM month 02 (!)
57 "MMM", // NF_KEY_MMM month short name
58 "MMMM", // NF_KEY_MMMM month long name
59 "MMMMM", // NF_KEY_MMMMM first letter of month name
61 "HH", // NF_KEY_HH hour 02
62 "S", // NF_KEY_S Second
63 "SS", // NF_KEY_SS Second 02
64 "Q", // NF_KEY_Q Quarter short 'Q'
65 "QQ", // NF_KEY_QQ Quarter long
66 "D", // NF_KEY_D day of month
67 "DD", // NF_KEY_DD day of month 02
68 "DDD", // NF_KEY_DDD day of week short
69 "DDDD", // NF_KEY_DDDD day of week long
70 "YY", // NF_KEY_YY year two digits
71 "YYYY", // NF_KEY_YYYY year four digits
72 "NN", // NF_KEY_NN Day of week short
73 "NNN", // NF_KEY_NNN Day of week long
74 "NNNN", // NF_KEY_NNNN Day of week long incl. separator
76 "AAAA", // NF_KEY_AAAA
84 "WW", // NF_KEY_WW Week of year
85 "t", // NF_KEY_THAI_T Thai T modifier, speciality of Thai Excel, only
86 // used with Thai locale and converted to [NatNum1], only
87 // exception as lowercase
88 "CCC", // NF_KEY_CCC Currency abbreviation
89 "GENERAL", // NF_KEY_GENERAL General / Standard
91 // Reserved words translated and color names follow:
92 "TRUE", // NF_KEY_TRUE boolean true
93 "FALSE", // NF_KEY_FALSE boolean false
94 "BOOLEAN", // NF_KEY_BOOLEAN boolean
95 "COLOR", // NF_KEY_COLOR color
97 "BLACK", // NF_KEY_BLACK
98 "BLUE", // NF_KEY_BLUE
99 "GREEN", // NF_KEY_GREEN
100 "CYAN", // NF_KEY_CYAN
102 "MAGENTA", // NF_KEY_MAGENTA
103 "BROWN", // NF_KEY_BROWN
104 "GREY", // NF_KEY_GREY
105 "YELLOW", // NF_KEY_YELLOW
106 "WHITE" // NF_KEY_WHITE
109 const ::std::vector
<Color
> ImpSvNumberformatScan::StandardColor
{
110 COL_BLACK
, COL_LIGHTBLUE
, COL_LIGHTGREEN
, COL_LIGHTCYAN
, COL_LIGHTRED
,
111 COL_LIGHTMAGENTA
, COL_BROWN
, COL_GRAY
, COL_YELLOW
, COL_WHITE
114 // This vector will hold *only* the color names in German language.
115 static const std::u16string_view
& GermanColorName(size_t i
)
117 static const std::u16string_view sGermanColorNames
[]{ u
"FARBE", u
"SCHWARZ", u
"BLAU",
118 u
"GRÜN", u
"CYAN", u
"ROT",
119 u
"MAGENTA", u
"BRAUN", u
"GRAU",
121 assert(i
< SAL_N_ELEMENTS(sGermanColorNames
));
122 return sGermanColorNames
[i
];
125 ImpSvNumberformatScan::ImpSvNumberformatScan( SvNumberFormatter
* pFormatterP
)
126 : maNullDate( 30, 12, 1899)
127 , eNewLnge(LANGUAGE_DONTKNOW
)
128 , eTmpLnge(LANGUAGE_DONTKNOW
)
130 , meKeywordLocalization(KeywordLocalization::AllowEnglish
)
132 pFormatter
= pFormatterP
;
133 xNFC
= css::i18n::NumberFormatMapper::create( pFormatter
->GetComponentContext() );
134 bConvertMode
= false;
135 mbConvertDateOrder
= false;
136 bConvertSystemToSystem
= false;
137 bKeywordsNeedInit
= true; // locale dependent and not locale dependent keywords
138 bCompatCurNeedInit
= true; // locale dependent compatibility currency strings
140 static_assert( NF_KEY_BLACK
- NF_KEY_COLOR
== 1, "bad FARBE(COLOR), SCHWARZ(BLACK) sequence");
141 static_assert( NF_KEY_FIRSTCOLOR
- NF_KEY_COLOR
== 1, "bad color sequence");
142 static_assert( NF_MAX_DEFAULT_COLORS
+ 1 == 11, "bad color count");
143 static_assert( NF_KEY_WHITE
- NF_KEY_COLOR
+ 1 == 11, "bad color sequence count");
150 ImpSvNumberformatScan::~ImpSvNumberformatScan()
155 void ImpSvNumberformatScan::ChangeIntl( KeywordLocalization eKeywordLocalization
)
157 meKeywordLocalization
= eKeywordLocalization
;
158 bKeywordsNeedInit
= true;
159 bCompatCurNeedInit
= true;
160 // may be initialized by InitSpecialKeyword()
161 sKeyword
[NF_KEY_TRUE
].clear();
162 sKeyword
[NF_KEY_FALSE
].clear();
165 void ImpSvNumberformatScan::InitSpecialKeyword( NfKeywordIndex eIdx
) const
170 const_cast<ImpSvNumberformatScan
*>(this)->sKeyword
[NF_KEY_TRUE
] =
171 pFormatter
->GetCharClass()->uppercase( pFormatter
->GetLocaleData()->getTrueWord() );
172 if ( sKeyword
[NF_KEY_TRUE
].isEmpty() )
174 SAL_WARN( "svl.numbers", "InitSpecialKeyword: TRUE_WORD?" );
175 const_cast<ImpSvNumberformatScan
*>(this)->sKeyword
[NF_KEY_TRUE
] = sEnglishKeyword
[NF_KEY_TRUE
];
179 const_cast<ImpSvNumberformatScan
*>(this)->sKeyword
[NF_KEY_FALSE
] =
180 pFormatter
->GetCharClass()->uppercase( pFormatter
->GetLocaleData()->getFalseWord() );
181 if ( sKeyword
[NF_KEY_FALSE
].isEmpty() )
183 SAL_WARN( "svl.numbers", "InitSpecialKeyword: FALSE_WORD?" );
184 const_cast<ImpSvNumberformatScan
*>(this)->sKeyword
[NF_KEY_FALSE
] = sEnglishKeyword
[NF_KEY_FALSE
];
188 SAL_WARN( "svl.numbers", "InitSpecialKeyword: unknown request" );
192 void ImpSvNumberformatScan::InitCompatCur() const
194 ImpSvNumberformatScan
* pThis
= const_cast<ImpSvNumberformatScan
*>(this);
195 // currency symbol for old style ("automatic") compatibility format codes
196 pFormatter
->GetCompatibilityCurrency( pThis
->sCurSymbol
, pThis
->sCurAbbrev
);
197 // currency symbol upper case
198 pThis
->sCurString
= pFormatter
->GetCharClass()->uppercase( sCurSymbol
);
199 bCompatCurNeedInit
= false;
202 void ImpSvNumberformatScan::InitKeywords() const
204 if ( !bKeywordsNeedInit
)
206 const_cast<ImpSvNumberformatScan
*>(this)->SetDependentKeywords();
207 bKeywordsNeedInit
= false;
210 /** Extract the name of General, Standard, Whatever, ignoring leading modifiers
211 such as [NatNum1]. */
212 static OUString
lcl_extractStandardGeneralName( const OUString
& rCode
)
215 const sal_Unicode
* p
= rCode
.getStr();
216 const sal_Unicode
* const pStop
= p
+ rCode
.getLength();
217 const sal_Unicode
* pBeg
= p
; // name begins here
220 while (p
< pStop
&& !bDone
)
233 // else: would be a locale data error, easily to be spotted in
240 --p
; // put back, increment by one follows
252 aStr
= rCode
.copy( pBeg
- rCode
.getStr(), p
- pBeg
);
257 void ImpSvNumberformatScan::SetDependentKeywords()
259 using namespace ::com::sun::star
;
260 using namespace ::com::sun::star::uno
;
262 const CharClass
* pCharClass
= pFormatter
->GetCharClass();
263 const LocaleDataWrapper
* pLocaleData
= pFormatter
->GetLocaleData();
264 // #80023# be sure to generate keywords for the loaded Locale, not for the
265 // requested Locale, otherwise number format codes might not match
266 const LanguageTag
& rLoadedLocale
= pLocaleData
->getLoadedLanguageTag();
267 LanguageType eLang
= rLoadedLocale
.getLanguageType( false);
269 bool bL10n
= (meKeywordLocalization
!= KeywordLocalization::EnglishOnly
);
272 // Check if this actually is a locale that uses any localized keywords,
273 // if not then disable localized keywords completely.
274 if ( !eLang
.anyOf( LANGUAGE_GERMAN
,
275 LANGUAGE_GERMAN_SWISS
,
276 LANGUAGE_GERMAN_AUSTRIAN
,
277 LANGUAGE_GERMAN_LUXEMBOURG
,
278 LANGUAGE_GERMAN_LIECHTENSTEIN
,
280 LANGUAGE_DUTCH_BELGIAN
,
282 LANGUAGE_FRENCH_BELGIAN
,
283 LANGUAGE_FRENCH_CANADIAN
,
284 LANGUAGE_FRENCH_SWISS
,
285 LANGUAGE_FRENCH_LUXEMBOURG
,
286 LANGUAGE_FRENCH_MONACO
,
289 LANGUAGE_ITALIAN_SWISS
,
292 LANGUAGE_NORWEGIAN_BOKMAL
,
293 LANGUAGE_NORWEGIAN_NYNORSK
,
295 LANGUAGE_SWEDISH_FINLAND
,
297 LANGUAGE_PORTUGUESE_BRAZILIAN
,
298 LANGUAGE_SPANISH_MODERN
,
299 LANGUAGE_SPANISH_DATED
,
300 LANGUAGE_SPANISH_MEXICAN
,
301 LANGUAGE_SPANISH_GUATEMALA
,
302 LANGUAGE_SPANISH_COSTARICA
,
303 LANGUAGE_SPANISH_PANAMA
,
304 LANGUAGE_SPANISH_DOMINICAN_REPUBLIC
,
305 LANGUAGE_SPANISH_VENEZUELA
,
306 LANGUAGE_SPANISH_COLOMBIA
,
307 LANGUAGE_SPANISH_PERU
,
308 LANGUAGE_SPANISH_ARGENTINA
,
309 LANGUAGE_SPANISH_ECUADOR
,
310 LANGUAGE_SPANISH_CHILE
,
311 LANGUAGE_SPANISH_URUGUAY
,
312 LANGUAGE_SPANISH_PARAGUAY
,
313 LANGUAGE_SPANISH_BOLIVIA
,
314 LANGUAGE_SPANISH_EL_SALVADOR
,
315 LANGUAGE_SPANISH_HONDURAS
,
316 LANGUAGE_SPANISH_NICARAGUA
,
317 LANGUAGE_SPANISH_PUERTO_RICO
))
320 meKeywordLocalization
= KeywordLocalization::EnglishOnly
;
324 // Init the current NfKeywordTable with English keywords.
325 sKeyword
= sEnglishKeyword
;
327 // Set the uppercase localized General name, e.g. Standard -> STANDARD
328 i18n::NumberFormatCode aFormat
= xNFC
->getFormatCode( NF_NUMBER_STANDARD
, rLoadedLocale
.getLocale() );
329 sNameStandardFormat
= lcl_extractStandardGeneralName( aFormat
.Code
);
330 sKeyword
[NF_KEY_GENERAL
] = pCharClass
->uppercase( sNameStandardFormat
);
332 // Thai T NatNum special. Other locale's small letter 't' results in upper
333 // case comparison not matching but length does in conversion mode. Ugly.
334 if (eLang
== LANGUAGE_THAI
)
336 sKeyword
[NF_KEY_THAI_T
] = "T";
340 sKeyword
[NF_KEY_THAI_T
] = sEnglishKeyword
[NF_KEY_THAI_T
];
344 InitSpecialKeyword( NF_KEY_TRUE
);
345 InitSpecialKeyword( NF_KEY_FALSE
);
347 // Boolean equivalent format codes that are written to Excel files, may
348 // have been written to ODF as well, specifically if such loaded Excel file
349 // was saved as ODF, and shall result in proper Boolean again.
350 // "TRUE";"TRUE";"FALSE"
351 sBooleanEquivalent1
= "\"" + sKeyword
[NF_KEY_TRUE
] + "\";\"" +
352 sKeyword
[NF_KEY_TRUE
] + "\";\"" + sKeyword
[NF_KEY_FALSE
] + "\"";
353 // [>0]"TRUE";[<0]"TRUE";"FALSE"
354 sBooleanEquivalent2
= "[>0]\"" + sKeyword
[NF_KEY_TRUE
] + "\";[<0]\"" +
355 sKeyword
[NF_KEY_TRUE
] + "\";\"" + sKeyword
[NF_KEY_FALSE
] + "\"";
357 // compatibility currency strings
363 // All locale dependent keywords overrides follow.
367 LANGUAGE_GERMAN_SWISS
,
368 LANGUAGE_GERMAN_AUSTRIAN
,
369 LANGUAGE_GERMAN_LUXEMBOURG
,
370 LANGUAGE_GERMAN_LIECHTENSTEIN
))
372 //! all capital letters
373 sKeyword
[NF_KEY_M
] = "M"; // month 1
374 sKeyword
[NF_KEY_MM
] = "MM"; // month 01
375 sKeyword
[NF_KEY_MMM
] = "MMM"; // month Jan
376 sKeyword
[NF_KEY_MMMM
] = "MMMM"; // month Januar
377 sKeyword
[NF_KEY_MMMMM
] = "MMMMM"; // month J
378 sKeyword
[NF_KEY_H
] = "H"; // hour 2
379 sKeyword
[NF_KEY_HH
] = "HH"; // hour 02
380 sKeyword
[NF_KEY_D
] = "T";
381 sKeyword
[NF_KEY_DD
] = "TT";
382 sKeyword
[NF_KEY_DDD
] = "TTT";
383 sKeyword
[NF_KEY_DDDD
] = "TTTT";
384 sKeyword
[NF_KEY_YY
] = "JJ";
385 sKeyword
[NF_KEY_YYYY
] = "JJJJ";
386 sKeyword
[NF_KEY_BOOLEAN
] = "LOGISCH";
387 sKeyword
[NF_KEY_COLOR
] = GermanColorName(NF_KEY_COLOR
- NF_KEY_COLOR
);
388 sKeyword
[NF_KEY_BLACK
] = GermanColorName(NF_KEY_BLACK
- NF_KEY_COLOR
);
389 sKeyword
[NF_KEY_BLUE
] = GermanColorName(NF_KEY_BLUE
- NF_KEY_COLOR
);
390 sKeyword
[NF_KEY_GREEN
] = GermanColorName(NF_KEY_GREEN
- NF_KEY_COLOR
);
391 sKeyword
[NF_KEY_CYAN
] = GermanColorName(NF_KEY_CYAN
- NF_KEY_COLOR
);
392 sKeyword
[NF_KEY_RED
] = GermanColorName(NF_KEY_RED
- NF_KEY_COLOR
);
393 sKeyword
[NF_KEY_MAGENTA
] = GermanColorName(NF_KEY_MAGENTA
- NF_KEY_COLOR
);
394 sKeyword
[NF_KEY_BROWN
] = GermanColorName(NF_KEY_BROWN
- NF_KEY_COLOR
);
395 sKeyword
[NF_KEY_GREY
] = GermanColorName(NF_KEY_GREY
- NF_KEY_COLOR
);
396 sKeyword
[NF_KEY_YELLOW
] = GermanColorName(NF_KEY_YELLOW
- NF_KEY_COLOR
);
397 sKeyword
[NF_KEY_WHITE
] = GermanColorName(NF_KEY_WHITE
- NF_KEY_COLOR
);
404 LANGUAGE_ITALIAN_SWISS
))
406 sKeyword
[NF_KEY_D
] = "G";
407 sKeyword
[NF_KEY_DD
] = "GG";
408 sKeyword
[NF_KEY_DDD
] = "GGG";
409 sKeyword
[NF_KEY_DDDD
] = "GGGG";
410 // must exchange the era code, same as Xcl
411 sKeyword
[NF_KEY_G
] = "X";
412 sKeyword
[NF_KEY_GG
] = "XX";
413 sKeyword
[NF_KEY_GGG
] = "XXX";
415 else if ( eLang
.anyOf(
417 LANGUAGE_FRENCH_BELGIAN
,
418 LANGUAGE_FRENCH_CANADIAN
,
419 LANGUAGE_FRENCH_SWISS
,
420 LANGUAGE_FRENCH_LUXEMBOURG
,
421 LANGUAGE_FRENCH_MONACO
))
423 sKeyword
[NF_KEY_D
] = "J";
424 sKeyword
[NF_KEY_DD
] = "JJ";
425 sKeyword
[NF_KEY_DDD
] = "JJJ";
426 sKeyword
[NF_KEY_DDDD
] = "JJJJ";
428 else if ( eLang
== LANGUAGE_FINNISH
)
430 sKeyword
[NF_KEY_D
] = "P";
431 sKeyword
[NF_KEY_DD
] = "PP";
432 sKeyword
[NF_KEY_DDD
] = "PPP";
433 sKeyword
[NF_KEY_DDDD
] = "PPPP";
437 if ( eLang
== LANGUAGE_FINNISH
)
439 sKeyword
[NF_KEY_M
] = "K";
440 sKeyword
[NF_KEY_MM
] = "KK";
441 sKeyword
[NF_KEY_MMM
] = "KKK";
442 sKeyword
[NF_KEY_MMMM
] = "KKKK";
443 sKeyword
[NF_KEY_MMMMM
] = "KKKKK";
449 LANGUAGE_ITALIAN_SWISS
,
451 LANGUAGE_FRENCH_BELGIAN
,
452 LANGUAGE_FRENCH_CANADIAN
,
453 LANGUAGE_FRENCH_SWISS
,
454 LANGUAGE_FRENCH_LUXEMBOURG
,
455 LANGUAGE_FRENCH_MONACO
,
457 LANGUAGE_PORTUGUESE_BRAZILIAN
,
458 LANGUAGE_SPANISH_MODERN
,
459 LANGUAGE_SPANISH_DATED
,
460 LANGUAGE_SPANISH_MEXICAN
,
461 LANGUAGE_SPANISH_GUATEMALA
,
462 LANGUAGE_SPANISH_COSTARICA
,
463 LANGUAGE_SPANISH_PANAMA
,
464 LANGUAGE_SPANISH_DOMINICAN_REPUBLIC
,
465 LANGUAGE_SPANISH_VENEZUELA
,
466 LANGUAGE_SPANISH_COLOMBIA
,
467 LANGUAGE_SPANISH_PERU
,
468 LANGUAGE_SPANISH_ARGENTINA
,
469 LANGUAGE_SPANISH_ECUADOR
,
470 LANGUAGE_SPANISH_CHILE
,
471 LANGUAGE_SPANISH_URUGUAY
,
472 LANGUAGE_SPANISH_PARAGUAY
,
473 LANGUAGE_SPANISH_BOLIVIA
,
474 LANGUAGE_SPANISH_EL_SALVADOR
,
475 LANGUAGE_SPANISH_HONDURAS
,
476 LANGUAGE_SPANISH_NICARAGUA
,
477 LANGUAGE_SPANISH_PUERTO_RICO
))
479 sKeyword
[NF_KEY_YY
] = "AA";
480 sKeyword
[NF_KEY_YYYY
] = "AAAA";
481 // must exchange the day of week name code, same as Xcl
482 sKeyword
[NF_KEY_AAA
] = "OOO";
483 sKeyword
[NF_KEY_AAAA
] = "OOOO";
485 else if ( eLang
.anyOf(
487 LANGUAGE_DUTCH_BELGIAN
))
489 sKeyword
[NF_KEY_YY
] = "JJ";
490 sKeyword
[NF_KEY_YYYY
] = "JJJJ";
492 else if ( eLang
== LANGUAGE_FINNISH
)
494 sKeyword
[NF_KEY_YY
] = "VV";
495 sKeyword
[NF_KEY_YYYY
] = "VVVV";
501 LANGUAGE_DUTCH_BELGIAN
))
503 sKeyword
[NF_KEY_H
] = "U";
504 sKeyword
[NF_KEY_HH
] = "UU";
506 else if ( eLang
.anyOf(
509 LANGUAGE_SWEDISH_FINLAND
,
512 LANGUAGE_NORWEGIAN_BOKMAL
,
513 LANGUAGE_NORWEGIAN_NYNORSK
))
515 sKeyword
[NF_KEY_H
] = "T";
516 sKeyword
[NF_KEY_HH
] = "TT";
521 void ImpSvNumberformatScan::ChangeNullDate(sal_uInt16 nDay
, sal_uInt16 nMonth
, sal_Int16 nYear
)
523 maNullDate
= Date(nDay
, nMonth
, nYear
);
524 if (!maNullDate
.IsValidDate())
526 maNullDate
.Normalize();
527 SAL_WARN("svl.numbers","ImpSvNumberformatScan::ChangeNullDate - not valid"
528 " d: " << nDay
<< " m: " << nMonth
<< " y: " << nYear
<< " normalized to"
529 " d: " << maNullDate
.GetDay() << " m: " << maNullDate
.GetMonth() << " y: " << maNullDate
.GetYear());
533 void ImpSvNumberformatScan::ChangeStandardPrec(sal_uInt16 nPrec
)
535 nStandardPrec
= nPrec
;
538 const Color
* ImpSvNumberformatScan::GetColor(OUString
& sStr
) const
540 OUString sString
= pFormatter
->GetCharClass()->uppercase(sStr
);
541 const NfKeywordTable
& rKeyword
= GetKeywords();
543 while (i
< NF_MAX_DEFAULT_COLORS
&& sString
!= rKeyword
[NF_KEY_FIRSTCOLOR
+i
] )
547 if (i
>= NF_MAX_DEFAULT_COLORS
&& meKeywordLocalization
== KeywordLocalization::AllowEnglish
)
549 LanguageType eLang
= pFormatter
->GetLocaleData()->getLoadedLanguageTag().getLanguageType( false);
552 LANGUAGE_GERMAN_SWISS
,
553 LANGUAGE_GERMAN_AUSTRIAN
,
554 LANGUAGE_GERMAN_LUXEMBOURG
,
555 LANGUAGE_GERMAN_LIECHTENSTEIN
)) // only German uses localized color names
558 while ( j
< NF_MAX_DEFAULT_COLORS
&& sString
!= sEnglishKeyword
[NF_KEY_FIRSTCOLOR
+ j
] )
562 if ( j
< NF_MAX_DEFAULT_COLORS
)
569 enum ColorKeywordConversion
574 } eColorKeywordConversion(None
);
578 const bool bFromGerman
= eTmpLnge
.anyOf(
580 LANGUAGE_GERMAN_SWISS
,
581 LANGUAGE_GERMAN_AUSTRIAN
,
582 LANGUAGE_GERMAN_LUXEMBOURG
,
583 LANGUAGE_GERMAN_LIECHTENSTEIN
);
584 const bool bToGerman
= eNewLnge
.anyOf(
586 LANGUAGE_GERMAN_SWISS
,
587 LANGUAGE_GERMAN_AUSTRIAN
,
588 LANGUAGE_GERMAN_LUXEMBOURG
,
589 LANGUAGE_GERMAN_LIECHTENSTEIN
);
590 if (bFromGerman
&& !bToGerman
)
591 eColorKeywordConversion
= ColorKeywordConversion::GermanToEnglish
;
592 else if (!bFromGerman
&& bToGerman
)
593 eColorKeywordConversion
= ColorKeywordConversion::EnglishToGerman
;
596 const Color
* pResult
= nullptr;
597 if (i
>= NF_MAX_DEFAULT_COLORS
)
599 const OUString
& rColorWord
= rKeyword
[NF_KEY_COLOR
];
601 if ((bL10n
= sString
.startsWith(rColorWord
)) ||
602 ((meKeywordLocalization
== KeywordLocalization::AllowEnglish
) &&
603 sString
.startsWith(sEnglishKeyword
[NF_KEY_COLOR
])))
605 sal_Int32 nPos
= (bL10n
? rColorWord
.getLength() : sEnglishKeyword
[NF_KEY_COLOR
].getLength());
606 sStr
= sStr
.copy(nPos
);
607 sStr
= comphelper::string::strip(sStr
, ' ');
608 switch (eColorKeywordConversion
)
610 case ColorKeywordConversion::None
:
611 sStr
= rColorWord
+ sStr
;
613 case ColorKeywordConversion::GermanToEnglish
:
614 sStr
= sEnglishKeyword
[NF_KEY_COLOR
] + sStr
; // Farbe -> COLOR
616 case ColorKeywordConversion::EnglishToGerman
:
617 sStr
= GermanColorName(NF_KEY_COLOR
- NF_KEY_COLOR
) + sStr
; // Color -> FARBE
620 sString
= sString
.copy(nPos
);
621 sString
= comphelper::string::strip(sString
, ' ');
623 if ( CharClass::isAsciiNumeric( sString
) )
625 sal_Int32 nIndex
= sString
.toInt32();
626 if (nIndex
> 0 && nIndex
<= 64)
628 pResult
= pFormatter
->GetUserDefColor(static_cast<sal_uInt16
>(nIndex
)-1);
636 switch (eColorKeywordConversion
)
638 case ColorKeywordConversion::None
:
639 sStr
= rKeyword
[NF_KEY_FIRSTCOLOR
+i
];
641 case ColorKeywordConversion::GermanToEnglish
:
642 sStr
= sEnglishKeyword
[NF_KEY_FIRSTCOLOR
+ i
]; // Rot -> RED
644 case ColorKeywordConversion::EnglishToGerman
:
645 sStr
= GermanColorName(NF_KEY_FIRSTCOLOR
- NF_KEY_COLOR
+ i
); // Red -> ROT
648 pResult
= &(StandardColor
[i
]);
653 short ImpSvNumberformatScan::GetKeyWord( const OUString
& sSymbol
, sal_Int32 nPos
, bool& rbFoundEnglish
) const
655 OUString sString
= pFormatter
->GetCharClass()->uppercase( sSymbol
, nPos
, sSymbol
.getLength() - nPos
);
656 const NfKeywordTable
& rKeyword
= GetKeywords();
657 // #77026# for the Xcl perverts: the GENERAL keyword is recognized anywhere
658 if (sString
.startsWith( rKeyword
[NF_KEY_GENERAL
] ))
660 return NF_KEY_GENERAL
;
662 if ((meKeywordLocalization
== KeywordLocalization::AllowEnglish
) &&
663 sString
.startsWith( sEnglishKeyword
[NF_KEY_GENERAL
]))
665 rbFoundEnglish
= true;
666 return NF_KEY_GENERAL
;
669 // MUST be a reverse search to find longer strings first,
670 // new keywords take precedence over old keywords,
671 // skip colors et al after keywords.
672 short i
= NF_KEY_LASTKEYWORD
;
673 while (i
> 0 && !sString
.startsWith( rKeyword
[i
]))
677 if (i
== 0 && meKeywordLocalization
== KeywordLocalization::AllowEnglish
)
679 // No localized (if so) keyword, try English keywords if keywords
680 // are localized. That was already checked in SetDependentKeywords().
681 i
= NF_KEY_LASTKEYWORD
;
682 while (i
> 0 && !sString
.startsWith( sEnglishKeyword
[i
]))
688 // The Thai T NatNum modifier during Xcl import.
689 if (i
== 0 && bConvertMode
&&
691 eTmpLnge
== LANGUAGE_ENGLISH_US
&&
692 MsLangId::getRealLanguage( eNewLnge
) == LANGUAGE_THAI
)
696 return i
; // 0 => not found
702 * Splits up the input for further processing (by the Turing machine).
704 * Starting state = SsStar
706 * ---------------+-------------------+---------------------------+---------------
707 * Old state | Character read | Event | New state
708 * ---------------+-------------------+---------------------------+---------------
709 * SsStart | Character | Symbol = Character | SsGetWord
710 * | " | Type = String | SsGetString
711 * | \ | Type = String | SsGetChar
712 * | * | Type = Star | SsGetStar
713 * | _ | Type = Blank | SsGetBlank
714 * | @ # 0 ? / . , % [ | Symbol = Character; |
715 * | ] ' Blank | Type = Control character | SsStop
716 * | $ - + ( ) : | Type = String; |
717 * | Else | Symbol = Character | SsStop
718 * ---------------|-------------------+---------------------------+---------------
719 * SsGetChar | Else | Symbol = Character | SsStop
720 * ---------------+-------------------+---------------------------+---------------
721 * GetString | " | | SsStop
722 * | Else | Symbol += Character | GetString
723 * ---------------+-------------------+---------------------------+---------------
724 * SsGetWord | Character | Symbol += Character |
725 * | + - (E+ E-)| Symbol += Character | SsStop
726 * | / (AM/PM)| Symbol += Character |
727 * | Else | Pos--, if Key Type = Word | SsStop
728 * ---------------+-------------------+---------------------------+---------------
729 * SsGetStar | Else | Symbol += Character | SsStop
730 * | | Mark special case * |
731 * ---------------+-------------------+---------------------------+---------------
732 * SsGetBlank | Else | Symbol + =Character | SsStop
733 * | | Mark special case _ |
734 * ---------------------------------------------------------------+--------------
736 * If we recognize a keyword in the state SsGetWord (even as the symbol's start text)
737 * we write back the rest of the characters!
755 short ImpSvNumberformatScan::Next_Symbol( const OUString
& rStr
,
757 OUString
& sSymbol
) const
760 const CharClass
* pChrCls
= pFormatter
->GetCharClass();
761 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
763 ScanState eState
= SsStart
;
764 OUStringBuffer sSymbolBuffer
;
765 while ( nPos
< rStr
.getLength() && eState
!= SsStop
)
767 sal_Unicode cToken
= rStr
[nPos
++];
771 // Fetch any currency longer than one character and don't get
772 // confused later on by "E/" or other combinations of letters
773 // and meaningful symbols. Necessary for old automatic currency.
774 // #96158# But don't do it if we're starting a "[...]" section,
775 // for example a "[$...]" new currency symbol to not parse away
776 // "$U" (symbol) of "[$UYU]" (abbreviation).
777 if ( nCurrPos
>= 0 && sCurString
.getLength() > 1 &&
778 nPos
-1 + sCurString
.getLength() <= rStr
.getLength() &&
779 (nPos
<= 1 || rStr
[nPos
-2] != '[') )
781 OUString aTest
= pChrCls
->uppercase( rStr
.copy( nPos
-1, sCurString
.getLength() ) );
782 if ( aTest
== sCurString
)
784 sSymbol
= rStr
.copy( --nPos
, sCurString
.getLength() );
785 nPos
= nPos
+ sSymbol
.getLength();
786 eType
= NF_SYMBOLTYPE_STRING
;
806 eType
= NF_SYMBOLTYPE_DEL
;
807 sSymbolBuffer
.append(OUStringChar(cToken
));
811 eType
= NF_SYMBOLTYPE_STAR
;
812 sSymbolBuffer
.append(OUStringChar(cToken
));
816 eType
= NF_SYMBOLTYPE_BLANK
;
817 sSymbolBuffer
.append(OUStringChar(cToken
));
821 eType
= NF_SYMBOLTYPE_STRING
;
822 eState
= SsGetString
;
823 sSymbolBuffer
.append(OUStringChar(cToken
));
826 eType
= NF_SYMBOLTYPE_STRING
;
828 sSymbolBuffer
.append(OUStringChar(cToken
));
834 eType
= NF_SYMBOLTYPE_STRING
;
836 sSymbolBuffer
.append(OUStringChar(cToken
));
839 if (StringEqualsChar( pFormatter
->GetNumDecimalSep(), cToken
) ||
840 StringEqualsChar( pFormatter
->GetNumThousandSep(), cToken
) ||
841 StringEqualsChar( pFormatter
->GetDateSep(), cToken
) ||
842 StringEqualsChar( pLoc
->getTimeSep(), cToken
) ||
843 StringEqualsChar( pLoc
->getTime100SecSep(), cToken
))
845 // Another separator than pre-known ASCII
846 eType
= NF_SYMBOLTYPE_DEL
;
847 sSymbolBuffer
.append(OUStringChar(cToken
));
850 else if ( pChrCls
->isLetter( rStr
, nPos
-1 ) )
852 bool bFoundEnglish
= false;
853 short nTmpType
= GetKeyWord( rStr
, nPos
-1, bFoundEnglish
);
856 bool bCurrency
= false;
857 // "Automatic" currency may start with keyword,
858 // like "R" (Rand) and 'R' (era)
859 if ( nCurrPos
>= 0 &&
860 nPos
-1 + sCurString
.getLength() <= rStr
.getLength() &&
861 sCurString
.startsWith( bFoundEnglish
? sEnglishKeyword
[nTmpType
] : sKeyword
[nTmpType
]))
863 OUString aTest
= pChrCls
->uppercase( rStr
.copy( nPos
-1, sCurString
.getLength() ) );
864 if ( aTest
== sCurString
)
872 sSymbolBuffer
.append(OUStringChar(cToken
));
877 // The code to be advanced is the detected keyword,
878 // not necessarily the locale's keyword, but the
879 // symbol is to be the locale's keyword.
883 nLen
= sEnglishKeyword
[eType
].getLength();
884 // Use the locale's General keyword name, not uppercase.
885 sSymbolBuffer
= (eType
== NF_KEY_GENERAL
? sNameStandardFormat
: sKeyword
[eType
]);
889 nLen
= sKeyword
[eType
].getLength();
890 // Preserve a locale's keyword's case as entered.
891 sSymbolBuffer
= rStr
.copy( nPos
-1, nLen
);
893 if ((eType
== NF_KEY_E
|| IsAmbiguousE(eType
)) && nPos
< rStr
.getLength())
895 sal_Unicode cNext
= rStr
[nPos
];
899 case '-' : // E+ E- combine to one symbol
900 sSymbolBuffer
.append(OUStringChar(cNext
));
905 case '#' : // scientific E without sign
918 sSymbolBuffer
.append(OUStringChar(cToken
));
923 eType
= NF_SYMBOLTYPE_STRING
;
925 sSymbolBuffer
.append(OUStringChar(cToken
));
931 sSymbolBuffer
.append(OUStringChar(cToken
));
939 sSymbolBuffer
.append(OUStringChar(cToken
));
942 if ( pChrCls
->isLetter( rStr
, nPos
-1 ) )
944 bool bFoundEnglish
= false;
945 short nTmpType
= GetKeyWord( rStr
, nPos
-1, bFoundEnglish
);
948 // beginning of keyword, stop scan and put back
949 eType
= NF_SYMBOLTYPE_STRING
;
955 sSymbolBuffer
.append(OUStringChar(cToken
));
960 bool bDontStop
= false;
964 case '/': // AM/PM, A/P
965 if (nPos
< rStr
.getLength())
968 if ( cNext
== 'P' || cNext
== 'p' )
970 sal_Int32 nLen
= sSymbolBuffer
.getLength();
972 (sSymbolBuffer
[0] == 'A' || sSymbolBuffer
[0] == 'a') &&
974 (nLen
== 2 && (sSymbolBuffer
[1] == 'M' || sSymbolBuffer
[1] == 'm')
975 && (rStr
[nPos
+ 1] == 'M' || rStr
[nPos
+ 1] == 'm'))))
977 sSymbolBuffer
.append(OUStringChar(cToken
));
984 // anything not recognized will stop the scan
989 eType
= NF_SYMBOLTYPE_STRING
;
995 sSymbolBuffer
.append(OUStringChar(cToken
));
999 sSymbolBuffer
.append(OUStringChar(cToken
));
1005 if (eState
== SsGetWord
)
1007 eType
= NF_SYMBOLTYPE_STRING
;
1009 sSymbol
= sSymbolBuffer
.makeStringAndClear();
1013 sal_Int32
ImpSvNumberformatScan::Symbol_Division(const OUString
& rString
)
1016 // Do we have some sort of currency?
1017 OUString sString
= pFormatter
->GetCharClass()->uppercase(rString
);
1018 sal_Int32 nCPos
= 0;
1019 while (nCPos
>= 0 && nCPos
< sString
.getLength())
1021 nCPos
= sString
.indexOf(GetCurString(),nCPos
);
1025 sal_Int32 nQ
= SvNumberformat::GetQuoteEnd( sString
, nCPos
);
1030 ((c
= sString
[nCPos
-1]) != '"'
1031 && c
!= '\\') ) // dm can be protected by "dm \d
1038 nCPos
++; // Continue search
1043 nCPos
= nQ
+ 1; // Continue search
1048 bool bStar
= false; // Is set on detecting '*'
1052 const sal_Int32 nLen
= rString
.getLength();
1053 while (nPos
< nLen
&& nStringsCnt
< NF_MAX_FORMAT_SYMBOLS
)
1055 nTypeArray
[nStringsCnt
] = Next_Symbol(rString
, nPos
, sStrArray
[nStringsCnt
]);
1056 if (nTypeArray
[nStringsCnt
] == NF_SYMBOLTYPE_STAR
)
1057 { // Monitoring the '*'
1060 return nPos
; // Error: double '*'
1064 // Valid only if there is a character following, else we are
1065 // at the end of a code that does not have a fill character
1067 if (sStrArray
[nStringsCnt
].getLength() < 2)
1075 return 0; // 0 => ok
1078 void ImpSvNumberformatScan::SkipStrings(sal_uInt16
& i
, sal_Int32
& nPos
) const
1080 while (i
< nStringsCnt
&& ( nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
1081 || nTypeArray
[i
] == NF_SYMBOLTYPE_BLANK
1082 || nTypeArray
[i
] == NF_SYMBOLTYPE_STAR
) )
1084 nPos
= nPos
+ sStrArray
[i
].getLength();
1089 sal_uInt16
ImpSvNumberformatScan::PreviousKeyword(sal_uInt16 i
) const
1092 if (i
> 0 && i
< nStringsCnt
)
1095 while (i
> 0 && nTypeArray
[i
] <= 0)
1099 if (nTypeArray
[i
] > 0)
1101 res
= nTypeArray
[i
];
1107 sal_uInt16
ImpSvNumberformatScan::NextKeyword(sal_uInt16 i
) const
1110 if (i
< nStringsCnt
-1)
1113 while (i
< nStringsCnt
-1 && nTypeArray
[i
] <= 0)
1117 if (nTypeArray
[i
] > 0)
1119 res
= nTypeArray
[i
];
1125 short ImpSvNumberformatScan::PreviousType( sal_uInt16 i
) const
1127 if ( i
> 0 && i
< nStringsCnt
)
1133 while ( i
> 0 && nTypeArray
[i
] == NF_SYMBOLTYPE_EMPTY
);
1134 return nTypeArray
[i
];
1139 sal_Unicode
ImpSvNumberformatScan::PreviousChar(sal_uInt16 i
) const
1141 sal_Unicode res
= ' ';
1142 if (i
> 0 && i
< nStringsCnt
)
1146 ( nTypeArray
[i
] == NF_SYMBOLTYPE_EMPTY
||
1147 nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
||
1148 nTypeArray
[i
] == NF_SYMBOLTYPE_STAR
||
1149 nTypeArray
[i
] == NF_SYMBOLTYPE_BLANK
))
1153 if (sStrArray
[i
].getLength() > 0)
1155 res
= sStrArray
[i
][sStrArray
[i
].getLength()-1];
1161 sal_Unicode
ImpSvNumberformatScan::NextChar(sal_uInt16 i
) const
1163 sal_Unicode res
= ' ';
1164 if (i
< nStringsCnt
-1)
1167 while (i
< nStringsCnt
-1 &&
1168 ( nTypeArray
[i
] == NF_SYMBOLTYPE_EMPTY
||
1169 nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
||
1170 nTypeArray
[i
] == NF_SYMBOLTYPE_STAR
||
1171 nTypeArray
[i
] == NF_SYMBOLTYPE_BLANK
))
1175 if (sStrArray
[i
].getLength() > 0)
1177 res
= sStrArray
[i
][0];
1183 bool ImpSvNumberformatScan::IsLastBlankBeforeFrac(sal_uInt16 i
) const
1186 if (i
< nStringsCnt
-1)
1190 while (i
< nStringsCnt
-1 && !bStop
)
1193 if ( nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
&&
1194 sStrArray
[i
][0] == '/')
1198 else if ( ( nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
&&
1199 sStrArray
[i
][0] == ' ') ||
1200 nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
) // integer/fraction delimiter can also be a string
1205 if (!bStop
) // no '/'{
1212 res
= false; // no '/' any more
1217 void ImpSvNumberformatScan::Reset()
1220 nResultStringsCnt
= 0;
1221 eScannedType
= SvNumFormatType::UNDEFINED
;
1226 nDecPos
= sal_uInt16(-1);
1227 nExpPos
= sal_uInt16(-1);
1228 nBlankPos
= sal_uInt16(-1);
1234 nNatNumModifier
= 0;
1237 bool ImpSvNumberformatScan::Is100SecZero( sal_uInt16 i
, bool bHadDecSep
) const
1239 sal_uInt16 nIndexPre
= PreviousKeyword( i
);
1240 return (nIndexPre
== NF_KEY_S
|| nIndexPre
== NF_KEY_SS
) &&
1242 ( i
> 0 && nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
));
1243 // SS"any"00 take "any" as a valid decimal separator
1246 sal_Int32
ImpSvNumberformatScan::ScanType()
1248 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
1252 SvNumFormatType eNewType
;
1253 bool bMatchBracket
= false;
1254 bool bHaveGeneral
= false; // if General/Standard encountered
1255 bool bIsTimeDetected
=false; // hour or second found in format
1256 bool bHaveMinute
= false;
1258 SkipStrings(i
, nPos
);
1259 while (i
< nStringsCnt
)
1261 if (nTypeArray
[i
] > 0)
1263 sal_uInt16 nIndexPre
;
1264 sal_uInt16 nIndexNex
;
1266 switch (nTypeArray
[i
])
1269 eNewType
= SvNumFormatType::SCIENTIFIC
;
1272 case NF_KEY_HH
: // HH
1273 bIsTimeDetected
= true;
1276 case NF_KEY_SS
: // SS
1278 bIsTimeDetected
= true;
1280 case NF_KEY_AMPM
: // AM,A,PM,P
1282 eNewType
= SvNumFormatType::TIME
;
1285 case NF_KEY_MM
: // MM
1286 case NF_KEY_MI
: // M minute detected in Finnish
1287 case NF_KEY_MMI
: // MM
1290 * preceded by time keyword H (ignoring separators)
1291 * followed by time keyword S (ignoring separators)
1292 * H or S was detected and this is the first M following
1293 * preceded by '[' amount bracket
1295 That are the Excel rules. BUT, we break it because certainly
1296 in something like {HH YYYY-MM-DD} the MM is NOT meant to be
1297 minute, so not if MM is between YY and DD or DD and YY.
1298 Actually not if any date specific keyword followed a time
1301 nIndexPre
= PreviousKeyword(i
);
1302 nIndexNex
= NextKeyword(i
);
1303 if (nIndexPre
== NF_KEY_H
|| // H
1304 nIndexPre
== NF_KEY_HH
|| // HH
1305 nIndexNex
== NF_KEY_S
|| // S
1306 nIndexNex
== NF_KEY_SS
|| // SS
1307 bIsTimeDetected
|| // tdf#101147
1308 PreviousChar(i
) == '[' ) // [M
1310 eNewType
= SvNumFormatType::TIME
;
1311 if ( nTypeArray
[i
] == NF_KEY_M
|| nTypeArray
[i
] == NF_KEY_MM
)
1313 nTypeArray
[i
] -= 2; // 6 -> 4, 7 -> 5
1315 bIsTimeDetected
= false; // next M should be month
1320 eNewType
= SvNumFormatType::DATE
;
1321 if ( nTypeArray
[i
] == NF_KEY_MI
|| nTypeArray
[i
] == NF_KEY_MMI
)
1322 { // follow resolution of tdf#33689 for Finnish
1323 nTypeArray
[i
] += 2; // 4 -> 6, 5 -> 7
1327 case NF_KEY_MMM
: // MMM
1328 case NF_KEY_MMMM
: // MMMM
1329 case NF_KEY_MMMMM
: // MMMMM
1331 case NF_KEY_QQ
: // QQ
1333 case NF_KEY_DD
: // DD
1334 case NF_KEY_DDD
: // DDD
1335 case NF_KEY_DDDD
: // DDDD
1336 case NF_KEY_YY
: // YY
1337 case NF_KEY_YYYY
: // YYYY
1338 case NF_KEY_NN
: // NN
1339 case NF_KEY_NNN
: // NNN
1340 case NF_KEY_NNNN
: // NNNN
1341 case NF_KEY_WW
: // WW
1342 case NF_KEY_AAA
: // AAA
1343 case NF_KEY_AAAA
: // AAAA
1344 case NF_KEY_EC
: // E
1345 case NF_KEY_EEC
: // EE
1346 case NF_KEY_G
: // G
1347 case NF_KEY_GG
: // GG
1348 case NF_KEY_GGG
: // GGG
1349 case NF_KEY_R
: // R
1350 case NF_KEY_RR
: // RR
1351 eNewType
= SvNumFormatType::DATE
;
1352 bIsTimeDetected
= false;
1354 case NF_KEY_CCC
: // CCC
1355 eNewType
= SvNumFormatType::CURRENCY
;
1357 case NF_KEY_GENERAL
: // Standard
1358 eNewType
= SvNumFormatType::NUMBER
;
1359 bHaveGeneral
= true;
1362 eNewType
= SvNumFormatType::UNDEFINED
;
1367 { // control character
1368 switch ( sStrArray
[i
][0] )
1372 eNewType
= SvNumFormatType::NUMBER
;
1375 if ( eScannedType
& SvNumFormatType::TIME
)
1377 if ( Is100SecZero( i
, bDecSep
) )
1379 bDecSep
= true; // subsequent 0's
1380 eNewType
= SvNumFormatType::TIME
;
1384 return nPos
; // Error
1389 eNewType
= SvNumFormatType::NUMBER
;
1393 eNewType
= SvNumFormatType::PERCENT
;
1396 eNewType
= SvNumFormatType::FRACTION
;
1399 if ( i
< nStringsCnt
-1 &&
1400 nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
1401 sStrArray
[i
+1][0] == '$' )
1403 eNewType
= SvNumFormatType::CURRENCY
;
1404 bMatchBracket
= true;
1406 else if ( i
< nStringsCnt
-1 &&
1407 nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
1408 sStrArray
[i
+1][0] == '~' )
1410 eNewType
= SvNumFormatType::DATE
;
1411 bMatchBracket
= true;
1415 sal_uInt16 nIndexNex
= NextKeyword(i
);
1416 if (nIndexNex
== NF_KEY_H
|| // H
1417 nIndexNex
== NF_KEY_HH
|| // HH
1418 nIndexNex
== NF_KEY_M
|| // M
1419 nIndexNex
== NF_KEY_MM
|| // MM
1420 nIndexNex
== NF_KEY_S
|| // S
1421 nIndexNex
== NF_KEY_SS
) // SS
1422 eNewType
= SvNumFormatType::TIME
;
1425 return nPos
; // Error
1430 eNewType
= SvNumFormatType::TEXT
;
1433 if (pLoc
->getTime100SecSep() == sStrArray
[i
])
1435 bDecSep
= true; // for SS,0
1437 eNewType
= SvNumFormatType::UNDEFINED
;
1441 if (eScannedType
== SvNumFormatType::UNDEFINED
)
1443 eScannedType
= eNewType
;
1445 else if (eScannedType
== SvNumFormatType::TEXT
|| eNewType
== SvNumFormatType::TEXT
)
1447 eScannedType
= SvNumFormatType::TEXT
; // Text always remains text
1449 else if (eNewType
== SvNumFormatType::UNDEFINED
)
1452 else if (eScannedType
!= eNewType
)
1454 switch (eScannedType
)
1456 case SvNumFormatType::DATE
:
1459 case SvNumFormatType::TIME
:
1460 eScannedType
= SvNumFormatType::DATETIME
;
1462 case SvNumFormatType::FRACTION
: // DD/MM
1467 eScannedType
= SvNumFormatType::UNDEFINED
;
1469 else if ( sStrArray
[i
] != pFormatter
->GetDateSep() )
1475 case SvNumFormatType::TIME
:
1478 case SvNumFormatType::DATE
:
1479 eScannedType
= SvNumFormatType::DATETIME
;
1481 case SvNumFormatType::FRACTION
: // MM/SS
1486 eScannedType
= SvNumFormatType::UNDEFINED
;
1488 else if (pLoc
->getTimeSep() != sStrArray
[i
])
1495 case SvNumFormatType::DATETIME
:
1498 case SvNumFormatType::TIME
:
1499 case SvNumFormatType::DATE
:
1501 case SvNumFormatType::FRACTION
: // DD/MM
1506 eScannedType
= SvNumFormatType::UNDEFINED
;
1508 else if ( pFormatter
->GetDateSep() != sStrArray
[i
] &&
1509 pLoc
->getTimeSep() != sStrArray
[i
] )
1515 case SvNumFormatType::PERCENT
:
1518 case SvNumFormatType::NUMBER
: // Only number to percent
1524 case SvNumFormatType::SCIENTIFIC
:
1527 case SvNumFormatType::NUMBER
: // Only number to E
1533 case SvNumFormatType::NUMBER
:
1536 case SvNumFormatType::SCIENTIFIC
:
1537 case SvNumFormatType::PERCENT
:
1538 case SvNumFormatType::FRACTION
:
1539 case SvNumFormatType::CURRENCY
:
1540 eScannedType
= eNewType
;
1545 eScannedType
= SvNumFormatType::UNDEFINED
;
1553 case SvNumFormatType::FRACTION
:
1556 case SvNumFormatType::NUMBER
: // Only number to fraction
1566 nPos
= nPos
+ sStrArray
[i
].getLength(); // Position of correction
1568 if ( bMatchBracket
)
1569 { // no type detection inside of matching brackets if [$...], [~...]
1570 while ( bMatchBracket
&& i
< nStringsCnt
)
1572 if ( nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
1573 && sStrArray
[i
][0] == ']' )
1575 bMatchBracket
= false;
1579 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1581 nPos
= nPos
+ sStrArray
[i
].getLength();
1584 if ( bMatchBracket
)
1586 return nPos
; // missing closing bracket at end of code
1589 SkipStrings(i
, nPos
);
1592 if ((eScannedType
== SvNumFormatType::NUMBER
||
1593 eScannedType
== SvNumFormatType::UNDEFINED
) &&
1594 nCurrPos
>= 0 && !bHaveGeneral
)
1596 eScannedType
= SvNumFormatType::CURRENCY
; // old "automatic" currency
1598 if (eScannedType
== SvNumFormatType::UNDEFINED
)
1600 eScannedType
= SvNumFormatType::DEFINED
;
1602 return 0; // All is fine
1605 bool ImpSvNumberformatScan::InsertSymbol( sal_uInt16
& nPos
, svt::NfSymbolType eType
, const OUString
& rStr
)
1607 if (nStringsCnt
>= NF_MAX_FORMAT_SYMBOLS
|| nPos
> nStringsCnt
)
1611 if (nPos
> 0 && nTypeArray
[nPos
-1] == NF_SYMBOLTYPE_EMPTY
)
1613 --nPos
; // reuse position
1617 if (nStringsCnt
>= NF_MAX_FORMAT_SYMBOLS
- 1)
1622 for (size_t i
= nStringsCnt
; i
> nPos
; --i
)
1624 nTypeArray
[i
] = nTypeArray
[i
-1];
1625 sStrArray
[i
] = sStrArray
[i
-1];
1628 ++nResultStringsCnt
;
1629 nTypeArray
[nPos
] = static_cast<short>(eType
);
1630 sStrArray
[nPos
] = rStr
;
1634 int ImpSvNumberformatScan::FinalScanGetCalendar( sal_Int32
& nPos
, sal_uInt16
& i
,
1635 sal_uInt16
& rResultStringsCnt
)
1637 if ( i
< nStringsCnt
-1 &&
1638 sStrArray
[i
][0] == '[' &&
1639 nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
1640 sStrArray
[i
+1][0] == '~' )
1643 nPos
= nPos
+ sStrArray
[i
].getLength(); // [
1644 nTypeArray
[i
] = NF_SYMBOLTYPE_CALDEL
;
1645 nPos
= nPos
+ sStrArray
[++i
].getLength(); // ~
1646 sStrArray
[i
-1] += sStrArray
[i
]; // [~
1647 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
1648 rResultStringsCnt
--;
1649 if ( ++i
>= nStringsCnt
)
1653 nPos
= nPos
+ sStrArray
[i
].getLength(); // calendarID
1654 OUString
& rStr
= sStrArray
[i
];
1655 nTypeArray
[i
] = NF_SYMBOLTYPE_CALENDAR
; // convert
1657 while ( i
< nStringsCnt
&& sStrArray
[i
][0] != ']' )
1659 nPos
= nPos
+ sStrArray
[i
].getLength();
1660 rStr
+= sStrArray
[i
];
1661 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
1662 rResultStringsCnt
--;
1665 if ( rStr
.getLength() && i
< nStringsCnt
&&
1666 sStrArray
[i
][0] == ']' )
1668 nTypeArray
[i
] = NF_SYMBOLTYPE_CALDEL
;
1669 nPos
= nPos
+ sStrArray
[i
].getLength();
1681 bool ImpSvNumberformatScan::IsDateFragment( size_t nPos1
, size_t nPos2
) const
1683 return nPos2
- nPos1
== 2 && nTypeArray
[nPos1
+1] == NF_SYMBOLTYPE_DATESEP
;
1686 void ImpSvNumberformatScan::SwapArrayElements( size_t nPos1
, size_t nPos2
)
1688 std::swap( nTypeArray
[nPos1
], nTypeArray
[nPos2
]);
1689 std::swap( sStrArray
[nPos1
], sStrArray
[nPos2
]);
1692 sal_Int32
ImpSvNumberformatScan::FinalScan( OUString
& rString
)
1694 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
1696 // save values for convert mode
1697 OUString sOldDecSep
= pFormatter
->GetNumDecimalSep();
1698 OUString sOldThousandSep
= pFormatter
->GetNumThousandSep();
1699 OUString sOldDateSep
= pFormatter
->GetDateSep();
1700 OUString sOldTimeSep
= pLoc
->getTimeSep();
1701 OUString sOldTime100SecSep
= pLoc
->getTime100SecSep();
1702 OUString sOldCurSymbol
= GetCurSymbol();
1703 OUString sOldCurString
= GetCurString();
1704 sal_Unicode cOldKeyH
= sKeyword
[NF_KEY_H
][0];
1705 sal_Unicode cOldKeyMI
= sKeyword
[NF_KEY_MI
][0];
1706 sal_Unicode cOldKeyS
= sKeyword
[NF_KEY_S
][0];
1707 DateOrder eOldDateOrder
= pLoc
->getDateOrder();
1708 sal_uInt16 nDayPos
, nMonthPos
, nYearPos
;
1709 nDayPos
= nMonthPos
= nYearPos
= SAL_MAX_UINT16
;
1711 // If the group separator is a No-Break Space (French) continue with a
1712 // normal space instead so queries on space work correctly.
1713 // The same for Narrow No-Break Space just in case some locale uses it.
1714 // The format string is adjusted to allow both.
1715 // For output of the format code string the LocaleData characters are used.
1716 if ( (sOldThousandSep
[0] == cNoBreakSpace
|| sOldThousandSep
[0] == cNarrowNoBreakSpace
) &&
1717 sOldThousandSep
.getLength() == 1 )
1719 sOldThousandSep
= " ";
1721 bool bNewDateOrder
= false;
1722 // change locale data et al
1725 pFormatter
->ChangeIntl(eNewLnge
);
1726 //! pointer may have changed
1727 pLoc
= pFormatter
->GetLocaleData();
1728 //! init new keywords
1730 // Adapt date order to target locale, but Excel does not handle date
1731 // particle re-ordering for the target locale when loading documents,
1732 // though it does exchange separators, tdf#113889
1733 bNewDateOrder
= (mbConvertDateOrder
&& eOldDateOrder
!= pLoc
->getDateOrder());
1735 const CharClass
* pChrCls
= pFormatter
->GetCharClass();
1737 sal_Int32 nPos
= 0; // error correction position
1738 sal_uInt16 i
= 0; // symbol loop counter
1739 sal_uInt16 nCounter
= 0; // counts digits
1740 nResultStringsCnt
= nStringsCnt
; // counts remaining symbols
1741 bDecSep
= false; // reset in case already used in TypeCheck
1742 bool bThaiT
= false; // Thai T NatNum modifier present
1743 bool bTimePart
= false;
1744 bool bDenomin
= false; // Set when reading end of denominator
1746 switch (eScannedType
)
1748 case SvNumFormatType::TEXT
:
1749 case SvNumFormatType::DEFINED
:
1750 while (i
< nStringsCnt
)
1752 switch (nTypeArray
[i
])
1754 case NF_SYMBOLTYPE_BLANK
:
1755 case NF_SYMBOLTYPE_STAR
:
1757 case NF_KEY_GENERAL
: // #77026# "General" is the same as "@"
1760 if ( nTypeArray
[i
] != NF_SYMBOLTYPE_DEL
||
1761 sStrArray
[i
][0] != '@' )
1763 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1767 nPos
= nPos
+ sStrArray
[i
].getLength();
1772 case SvNumFormatType::NUMBER
:
1773 case SvNumFormatType::PERCENT
:
1774 case SvNumFormatType::CURRENCY
:
1775 case SvNumFormatType::SCIENTIFIC
:
1776 case SvNumFormatType::FRACTION
:
1777 while (i
< nStringsCnt
)
1779 // TODO: rechecking eScannedType is unnecessary.
1780 // This switch-case is for eScannedType == SvNumFormatType::FRACTION anyway
1781 if (eScannedType
== SvNumFormatType::FRACTION
&& // special case
1782 nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
&& // # ### #/#
1783 StringEqualsChar( sOldThousandSep
, ' ' ) && // e.g. France or Sweden
1784 StringEqualsChar( sStrArray
[i
], ' ' ) &&
1786 IsLastBlankBeforeFrac(i
) )
1788 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
; // del->string
1789 } // No thousands marker
1791 if (nTypeArray
[i
] == NF_SYMBOLTYPE_BLANK
||
1792 nTypeArray
[i
] == NF_SYMBOLTYPE_STAR
||
1793 nTypeArray
[i
] == NF_KEY_CCC
|| // CCC
1794 nTypeArray
[i
] == NF_KEY_GENERAL
) // Standard
1796 if (nTypeArray
[i
] == NF_KEY_GENERAL
)
1798 nThousand
= FLAG_STANDARD_IN_FORMAT
;
1801 sStrArray
[i
] = sNameStandardFormat
;
1804 nPos
= nPos
+ sStrArray
[i
].getLength();
1807 else if (nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
|| // No Strings or
1808 nTypeArray
[i
] > 0) // Keywords
1810 if (eScannedType
== SvNumFormatType::SCIENTIFIC
&&
1811 nTypeArray
[i
] == NF_KEY_E
) // E+
1821 nCntPost
= nCounter
;
1828 nTypeArray
[i
] = NF_SYMBOLTYPE_EXP
;
1830 else if (eScannedType
== SvNumFormatType::FRACTION
&&
1831 (sStrArray
[i
][0] == ' ' || ( nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
&& (sStrArray
[i
][0] < '0' || sStrArray
[i
][0] > '9') ) ) )
1833 if (!bBlank
&& !bFrac
) // Not double or after a /
1835 if (bDecSep
&& nCounter
> 0) // Decimal places
1837 return nPos
; // Error
1839 if (sStrArray
[i
][0] == ' ' || nCounter
> 0 ) // treat string as integer/fraction delimiter only if there is integer
1845 nTypeArray
[i
] = NF_SYMBOLTYPE_FRACBLANK
;
1848 else if ( sStrArray
[i
][0] == ' ' )
1849 nTypeArray
[i
] = NF_SYMBOLTYPE_FRACBLANK
;
1850 else if ( bFrac
&& ( nCounter
> 0 ) )
1851 bDenomin
= true; // following elements are no more part of denominator
1853 else if (nTypeArray
[i
] == NF_KEY_THAI_T
)
1856 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]];
1858 else if (sStrArray
[i
][0] >= '0' &&
1859 sStrArray
[i
][0] <= '9' && !bDenomin
) // denominator was not yet found
1863 while(j
< nStringsCnt
&& sStrArray
[j
][0] >= '0' && sStrArray
[j
][0] <= '9')
1865 sDiv
+= sStrArray
[j
++];
1867 assert(j
> 0 && "if i is 0, first iteration through loop is guaranteed by surrounding if condition");
1868 if (std::u16string_view(OUString::number(sDiv
.toInt32())) == sDiv
)
1873 nTypeArray
[i
++] = NF_SYMBOLTYPE_FRAC_FDIV
;
1875 i
= j
- 1; // Stop the loop
1878 nCounter
= nCntPost
;
1884 // don't artificially increment nCntPre for forced denominator
1885 if ( ( eScannedType
!= SvNumFormatType::FRACTION
) && (!nCntPre
) )
1890 bDenomin
= true; // next content should be treated as outside denominator
1895 if ( bFrac
&& ( nCounter
> 0 ) )
1896 bDenomin
= true; // next content should be treated as outside denominator
1897 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1899 nPos
= nPos
+ sStrArray
[i
].getLength();
1902 else if (nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
)
1904 sal_Unicode cHere
= sStrArray
[i
][0];
1905 sal_Unicode cSaved
= cHere
;
1906 // Handle not pre-known separators in switch.
1907 sal_Unicode cSimplified
;
1908 if (StringEqualsChar( pFormatter
->GetNumThousandSep(), cHere
))
1912 else if (StringEqualsChar( pFormatter
->GetNumDecimalSep(), cHere
))
1918 cSimplified
= cHere
;
1921 OUString
& rStr
= sStrArray
[i
];
1923 switch ( cSimplified
)
1928 if (nThousand
> 0) // #... #
1930 return nPos
; // Error
1934 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
1935 nPos
= nPos
+ rStr
.getLength();
1938 while (i
< nStringsCnt
&&
1939 (sStrArray
[i
][0] == '#' ||
1940 sStrArray
[i
][0] == '0' ||
1941 sStrArray
[i
][0] == '?'))
1943 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
1944 nPos
= nPos
+ sStrArray
[i
].getLength();
1949 else // after denominator, treat any character as text
1951 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1952 nPos
= nPos
+ sStrArray
[i
].getLength();
1956 if ( bDecSep
&& nDecPos
+1 == i
&&
1957 nTypeArray
[nDecPos
] == NF_SYMBOLTYPE_DECSEP
)
1960 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
1961 nPos
= nPos
+ rStr
.getLength();
1964 while (i
< nStringsCnt
&&
1965 (sStrArray
[i
][0] == '-') )
1967 // If more than two dashes are present in
1968 // currency formats the last dash will be
1969 // interpreted literally as a minus sign.
1970 // Has to be this ugly. Period.
1971 if ( eScannedType
== SvNumFormatType::CURRENCY
1972 && rStr
.getLength() >= 2 &&
1973 (i
== nStringsCnt
-1 ||
1974 sStrArray
[i
+1][0] != '-') )
1978 rStr
+= sStrArray
[i
];
1979 nPos
= nPos
+ sStrArray
[i
].getLength();
1980 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
1981 nResultStringsCnt
--;
1988 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1989 nPos
= nPos
+ sStrArray
[i
].getLength();
1997 if ( StringEqualsChar( sOldThousandSep
, cSaved
) )
1999 // previous char with skip empty
2000 sal_Unicode cPre
= PreviousChar(i
);
2002 if (bExp
|| bBlank
|| bFrac
)
2004 // after E, / or ' '
2005 if ( !StringEqualsChar( sOldThousandSep
, ' ' ) )
2007 nPos
= nPos
+ sStrArray
[i
].getLength();
2008 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2009 nResultStringsCnt
--;
2014 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2015 if ( bFrac
&& (nCounter
> 0) )
2016 bDenomin
= true; // end of denominator
2019 else if (i
> 0 && i
< nStringsCnt
-1 &&
2020 (cPre
== '#' || cPre
== '0' || cPre
== '?') &&
2021 ((cNext
= NextChar(i
)) == '#' || cNext
== '0' || cNext
== '?')) // #,#
2023 nPos
= nPos
+ sStrArray
[i
].getLength();
2024 if (!bThousand
) // only once
2028 // Eat it, will be reinserted at proper grouping positions further down.
2029 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2030 nResultStringsCnt
--;
2033 else if (i
> 0 && (cPre
== '#' || cPre
== '0' || cPre
== '?')
2034 && PreviousType(i
) == NF_SYMBOLTYPE_DIGIT
2035 && nThousand
< FLAG_STANDARD_IN_FORMAT
)
2037 if ( StringEqualsChar( sOldThousandSep
, ' ' ) )
2039 // strange, those French...
2041 // set a hard No-Break Space or ConvertMode
2042 const OUString
& rSepF
= pFormatter
->GetNumThousandSep();
2043 while ( i
< nStringsCnt
&&
2044 sStrArray
[i
] == sOldThousandSep
&&
2045 StringEqualsChar( sOldThousandSep
, NextChar(i
) ) )
2046 { // last was a space or another space
2047 // is following => separator
2048 nPos
= nPos
+ sStrArray
[i
].getLength();
2053 nTypeArray
[i
] = NF_SYMBOLTYPE_THSEP
;
2058 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2059 nResultStringsCnt
--;
2064 if ( i
< nStringsCnt
-1 &&
2065 sStrArray
[i
] == sOldThousandSep
)
2067 // something following last space
2068 // => space if currency contained,
2070 nPos
= nPos
+ sStrArray
[i
].getLength();
2071 if ( (nPos
<= nCurrPos
&&
2072 nCurrPos
< nPos
+ sStrArray
[i
+1].getLength()) ||
2073 nTypeArray
[i
+1] == NF_KEY_CCC
||
2074 (i
< nStringsCnt
-2 &&
2075 sStrArray
[i
+1][0] == '[' &&
2076 sStrArray
[i
+2][0] == '$') )
2078 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2085 nTypeArray
[i
] = NF_SYMBOLTYPE_THSEP
;
2090 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2091 nResultStringsCnt
--;
2103 nTypeArray
[i
] = NF_SYMBOLTYPE_THSEP
;
2104 nPos
= nPos
+ sStrArray
[i
].getLength();
2105 sStrArray
[i
] = pFormatter
->GetNumThousandSep();
2108 while (i
< nStringsCnt
&& sStrArray
[i
] == sOldThousandSep
);
2113 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2114 nPos
= nPos
+ rStr
.getLength();
2116 while ( i
< nStringsCnt
&& sStrArray
[i
] == sOldThousandSep
)
2118 rStr
+= sStrArray
[i
];
2119 nPos
= nPos
+ sStrArray
[i
].getLength();
2120 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2121 nResultStringsCnt
--;
2126 else if ( StringEqualsChar( sOldDecSep
, cSaved
) )
2128 if (bBlank
|| bFrac
) // . behind / or ' '
2130 return nPos
; // error
2132 else if (bExp
) // behind E
2134 nPos
= nPos
+ sStrArray
[i
].getLength();
2135 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2136 nResultStringsCnt
--;
2139 else if (bDecSep
) // any .
2141 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2142 nPos
= nPos
+ rStr
.getLength();
2144 while ( i
< nStringsCnt
&& sStrArray
[i
] == sOldDecSep
)
2146 rStr
+= sStrArray
[i
];
2147 nPos
= nPos
+ sStrArray
[i
].getLength();
2148 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2149 nResultStringsCnt
--;
2155 nPos
= nPos
+ sStrArray
[i
].getLength();
2156 nTypeArray
[i
] = NF_SYMBOLTYPE_DECSEP
;
2157 sStrArray
[i
] = pFormatter
->GetNumDecimalSep();
2165 } // of else = DecSep
2166 else // . without meaning
2168 if (cSaved
== ' ' &&
2169 eScannedType
== SvNumFormatType::FRACTION
&&
2170 StringEqualsChar( sStrArray
[i
], ' ' ) )
2172 if (!bBlank
&& !bFrac
) // no dups
2174 if (bDecSep
&& nCounter
> 0) // dec.
2176 return nPos
; // error
2183 if ( bFrac
&& (nCounter
> 0) )
2184 bDenomin
= true; // next content is not part of denominator
2185 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2186 nPos
= nPos
+ sStrArray
[i
].getLength();
2190 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2191 if ( bFrac
&& (nCounter
> 0) )
2192 bDenomin
= true; // next content is not part of denominator
2193 nPos
= nPos
+ rStr
.getLength();
2195 while (i
< nStringsCnt
&& StringEqualsChar( sStrArray
[i
], cSaved
) )
2197 rStr
+= sStrArray
[i
];
2198 nPos
= nPos
+ sStrArray
[i
].getLength();
2199 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2200 nResultStringsCnt
--;
2207 if (eScannedType
== SvNumFormatType::FRACTION
)
2210 (nTypeArray
[i
-1] != NF_SYMBOLTYPE_DIGIT
&&
2211 nTypeArray
[i
-1] != NF_SYMBOLTYPE_EMPTY
) )
2213 return nPos
? nPos
: 1; // /? not allowed
2215 else if (!bFrac
|| (bDecSep
&& nCounter
> 0))
2218 nCntPost
= nCounter
;
2220 nTypeArray
[i
] = NF_SYMBOLTYPE_FRAC
;
2221 nPos
= nPos
+ sStrArray
[i
].getLength();
2224 else // / double or in , in the denominator
2226 return nPos
; // Error
2231 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2232 nPos
= nPos
+ sStrArray
[i
].getLength();
2237 if ( eScannedType
== SvNumFormatType::CURRENCY
&&
2238 i
< nStringsCnt
-1 &&
2239 nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
2240 sStrArray
[i
+1][0] == '$' )
2243 nPos
= nPos
+ sStrArray
[i
].getLength(); // [
2244 nTypeArray
[i
] = NF_SYMBOLTYPE_CURRDEL
;
2245 nPos
= nPos
+ sStrArray
[++i
].getLength(); // $
2246 sStrArray
[i
-1] += sStrArray
[i
]; // [$
2247 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2248 nResultStringsCnt
--;
2249 if ( ++i
>= nStringsCnt
)
2251 return nPos
; // Error
2253 nPos
= nPos
+ sStrArray
[i
].getLength(); // DM
2254 OUString
* pStr
= &sStrArray
[i
];
2255 nTypeArray
[i
] = NF_SYMBOLTYPE_CURRENCY
; // convert
2256 bool bHadDash
= false;
2258 while ( i
< nStringsCnt
&& sStrArray
[i
][0] != ']' )
2260 nPos
= nPos
+ sStrArray
[i
].getLength();
2263 *pStr
+= sStrArray
[i
];
2264 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2265 nResultStringsCnt
--;
2269 if ( sStrArray
[i
][0] == '-' )
2272 pStr
= &sStrArray
[i
];
2273 nTypeArray
[i
] = NF_SYMBOLTYPE_CURREXT
;
2277 *pStr
+= sStrArray
[i
];
2278 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2279 nResultStringsCnt
--;
2284 if ( rStr
.getLength() && i
< nStringsCnt
&& sStrArray
[i
][0] == ']' )
2286 nTypeArray
[i
] = NF_SYMBOLTYPE_CURRDEL
;
2287 nPos
= nPos
+ sStrArray
[i
].getLength();
2292 return nPos
; // Error
2297 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2298 nPos
= nPos
+ sStrArray
[i
].getLength();
2302 default: // Other Dels
2303 if (eScannedType
== SvNumFormatType::PERCENT
&& cHere
== '%')
2305 nTypeArray
[i
] = NF_SYMBOLTYPE_PERCENT
;
2309 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2311 nPos
= nPos
+ sStrArray
[i
].getLength();
2314 } // of switch (Del)
2318 SAL_WARN( "svl.numbers", "unknown NF_SYMBOLTYPE_..." );
2319 nPos
= nPos
+ sStrArray
[i
].getLength();
2323 if (eScannedType
== SvNumFormatType::FRACTION
)
2331 nCntPost
= nCounter
;
2346 nCntPost
= nCounter
;
2353 if (bThousand
) // Expansion of grouping separators
2360 nMaxPos
= nBlankPos
;
2364 nMaxPos
= 0; // no grouping
2367 else if (bDecSep
) // decimal separator present
2371 else if (bExp
) // 'E' exponent present
2379 // Insert separators at proper positions.
2380 sal_Int32 nCount
= 0;
2381 utl::DigitGroupingIterator
aGrouping( pLoc
->getDigitGrouping());
2382 size_t nFirstDigitSymbol
= nMaxPos
;
2383 size_t nFirstGroupingSymbol
= nMaxPos
;
2387 if (nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
)
2389 nFirstDigitSymbol
= i
;
2390 nCount
= nCount
+ sStrArray
[i
].getLength(); // MSC converts += to int and then warns, so ...
2391 // Insert separator only if not leftmost symbol.
2392 if (i
> 0 && nCount
>= aGrouping
.getPos())
2394 DBG_ASSERT( sStrArray
[i
].getLength() == 1,
2395 "ImpSvNumberformatScan::FinalScan: combined digits in group separator insertion");
2396 if (!InsertSymbol( i
, NF_SYMBOLTYPE_THSEP
, pFormatter
->GetNumThousandSep()))
2398 // nPos isn't correct here, but signals error
2401 // i may have been decremented by 1
2402 nFirstDigitSymbol
= i
+ 1;
2403 nFirstGroupingSymbol
= i
;
2404 aGrouping
.advance();
2408 // Generated something like "string",000; remove separator again.
2409 if (nFirstGroupingSymbol
< nFirstDigitSymbol
)
2411 nTypeArray
[nFirstGroupingSymbol
] = NF_SYMBOLTYPE_EMPTY
;
2412 nResultStringsCnt
--;
2415 // Combine digits into groups to save memory (Info will be copied
2416 // later, taking only non-empty symbols).
2417 for (i
= 0; i
< nStringsCnt
; ++i
)
2419 if (nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
)
2421 OUString
& rStr
= sStrArray
[i
];
2422 while (++i
< nStringsCnt
&& nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
)
2424 rStr
+= sStrArray
[i
];
2425 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2426 nResultStringsCnt
--;
2430 break; // of SvNumFormatType::NUMBER
2431 case SvNumFormatType::DATE
:
2432 while (i
< nStringsCnt
)
2434 switch (nTypeArray
[i
])
2436 case NF_SYMBOLTYPE_BLANK
:
2437 case NF_SYMBOLTYPE_STAR
:
2438 case NF_SYMBOLTYPE_STRING
:
2439 nPos
= nPos
+ sStrArray
[i
].getLength();
2442 case NF_SYMBOLTYPE_DEL
:
2444 if (sStrArray
[i
] == sOldDateSep
)
2446 nTypeArray
[i
] = NF_SYMBOLTYPE_DATESEP
;
2447 nPos
= nPos
+ sStrArray
[i
].getLength();
2450 sStrArray
[i
] = pFormatter
->GetDateSep();
2454 else if ( (nCalRet
= FinalScanGetCalendar( nPos
, i
, nResultStringsCnt
)) != 0 )
2458 return nPos
; // error
2463 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2464 nPos
= nPos
+ sStrArray
[i
].getLength();
2468 case NF_KEY_THAI_T
:
2472 case NF_KEY_MM
: // MM
2473 case NF_KEY_MMM
: // MMM
2474 case NF_KEY_MMMM
: // MMMM
2475 case NF_KEY_MMMMM
: // MMMMM
2477 case NF_KEY_QQ
: // QQ
2479 case NF_KEY_DD
: // DD
2480 case NF_KEY_DDD
: // DDD
2481 case NF_KEY_DDDD
: // DDDD
2482 case NF_KEY_YY
: // YY
2483 case NF_KEY_YYYY
: // YYYY
2484 case NF_KEY_NN
: // NN
2485 case NF_KEY_NNN
: // NNN
2486 case NF_KEY_NNNN
: // NNNN
2487 case NF_KEY_WW
: // WW
2488 case NF_KEY_AAA
: // AAA
2489 case NF_KEY_AAAA
: // AAAA
2490 case NF_KEY_EC
: // E
2491 case NF_KEY_EEC
: // EE
2492 case NF_KEY_G
: // G
2493 case NF_KEY_GG
: // GG
2494 case NF_KEY_GGG
: // GGG
2495 case NF_KEY_R
: // R
2496 case NF_KEY_RR
: // RR
2497 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2498 nPos
= nPos
+ sStrArray
[i
].getLength();
2501 // For simple numeric date formats record date order and
2503 switch (nTypeArray
[i
])
2507 if (nMonthPos
== SAL_MAX_UINT16
)
2510 bNewDateOrder
= false;
2514 if (nDayPos
== SAL_MAX_UINT16
)
2517 bNewDateOrder
= false;
2521 if (nYearPos
== SAL_MAX_UINT16
)
2524 bNewDateOrder
= false;
2532 default: // Other keywords
2533 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2534 nPos
= nPos
+ sStrArray
[i
].getLength();
2539 break; // of SvNumFormatType::DATE
2540 case SvNumFormatType::TIME
:
2541 while (i
< nStringsCnt
)
2545 switch (nTypeArray
[i
])
2547 case NF_SYMBOLTYPE_BLANK
:
2548 case NF_SYMBOLTYPE_STAR
:
2549 nPos
= nPos
+ sStrArray
[i
].getLength();
2552 case NF_SYMBOLTYPE_DEL
:
2553 switch( sStrArray
[i
][0] )
2556 if ( Is100SecZero( i
, bDecSep
) )
2559 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
2560 OUString
& rStr
= sStrArray
[i
];
2563 while (i
< nStringsCnt
&&
2564 sStrArray
[i
][0] == '0')
2566 rStr
+= sStrArray
[i
];
2567 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2568 nResultStringsCnt
--;
2572 nPos
+= rStr
.getLength();
2583 if (bThousand
) // Double
2587 bThousand
= true; // Empty for Time
2588 cChar
= pChrCls
->uppercase(OUString(NextChar(i
)))[0];
2589 if ( cChar
== cOldKeyH
)
2593 else if ( cChar
== cOldKeyMI
)
2597 else if ( cChar
== cOldKeyS
)
2605 nPos
= nPos
+ sStrArray
[i
].getLength();
2609 if (!bThousand
) // No preceding [
2613 nPos
= nPos
+ sStrArray
[i
].getLength();
2617 nPos
= nPos
+ sStrArray
[i
].getLength();
2618 if ( sStrArray
[i
] == sOldTimeSep
)
2620 nTypeArray
[i
] = NF_SYMBOLTYPE_TIMESEP
;
2623 sStrArray
[i
] = pLoc
->getTimeSep();
2626 else if ( sStrArray
[i
] == sOldTime100SecSep
)
2629 nTypeArray
[i
] = NF_SYMBOLTYPE_TIME100SECSEP
;
2632 sStrArray
[i
] = pLoc
->getTime100SecSep();
2637 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2643 case NF_SYMBOLTYPE_STRING
:
2644 nPos
= nPos
+ sStrArray
[i
].getLength();
2647 case NF_KEY_AMPM
: // AM/PM
2648 case NF_KEY_AP
: // A/P
2649 bExp
= true; // Abuse for A/P
2650 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2651 nPos
= nPos
+ sStrArray
[i
].getLength();
2654 case NF_KEY_THAI_T
:
2657 case NF_KEY_MI
: // M
2658 case NF_KEY_MMI
: // MM
2660 case NF_KEY_HH
: // HH
2662 case NF_KEY_SS
: // SS
2663 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2664 nPos
= nPos
+ sStrArray
[i
].getLength();
2667 default: // Other keywords
2668 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2669 nPos
= nPos
+ sStrArray
[i
].getLength();
2674 nCntPost
= nCounter
; // Zero counter
2677 nCntExp
= 1; // Remembers AM/PM
2679 break; // of SvNumFormatType::TIME
2680 case SvNumFormatType::DATETIME
:
2681 while (i
< nStringsCnt
)
2684 switch (nTypeArray
[i
])
2686 case NF_SYMBOLTYPE_BLANK
:
2687 case NF_SYMBOLTYPE_STAR
:
2688 case NF_SYMBOLTYPE_STRING
:
2689 nPos
= nPos
+ sStrArray
[i
].getLength();
2692 case NF_SYMBOLTYPE_DEL
:
2693 if ( (nCalRet
= FinalScanGetCalendar( nPos
, i
, nResultStringsCnt
)) != 0 )
2697 return nPos
; // Error
2702 switch( sStrArray
[i
][0] )
2705 if (bTimePart
&& Is100SecZero(i
, bDecSep
) && nCounter
< MaxCntPost
)
2708 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
2709 OUString
& rStr
= sStrArray
[i
];
2712 while (i
< nStringsCnt
&&
2713 sStrArray
[i
][0] == '0' && nCounter
< MaxCntPost
)
2715 rStr
+= sStrArray
[i
];
2716 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2717 nResultStringsCnt
--;
2721 nPos
+= rStr
.getLength();
2732 nPos
= nPos
+ sStrArray
[i
].getLength();
2735 if ( sStrArray
[i
] == sOldTimeSep
)
2737 nTypeArray
[i
] = NF_SYMBOLTYPE_TIMESEP
;
2740 sStrArray
[i
] = pLoc
->getTimeSep();
2743 else if ( sStrArray
[i
] == sOldTime100SecSep
)
2746 nTypeArray
[i
] = NF_SYMBOLTYPE_TIME100SECSEP
;
2749 sStrArray
[i
] = pLoc
->getTime100SecSep();
2754 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2759 if ( sStrArray
[i
] == sOldDateSep
)
2761 nTypeArray
[i
] = NF_SYMBOLTYPE_DATESEP
;
2763 sStrArray
[i
] = pFormatter
->GetDateSep();
2767 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2775 case NF_KEY_AMPM
: // AM/PM
2776 case NF_KEY_AP
: // A/P
2778 bExp
= true; // Abuse for A/P
2779 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2780 nPos
= nPos
+ sStrArray
[i
].getLength();
2783 case NF_KEY_MI
: // M
2784 case NF_KEY_MMI
: // MM
2786 case NF_KEY_HH
: // HH
2788 case NF_KEY_SS
: // SS
2790 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2791 nPos
= nPos
+ sStrArray
[i
].getLength();
2795 case NF_KEY_MM
: // MM
2796 case NF_KEY_MMM
: // MMM
2797 case NF_KEY_MMMM
: // MMMM
2798 case NF_KEY_MMMMM
: // MMMMM
2800 case NF_KEY_QQ
: // QQ
2802 case NF_KEY_DD
: // DD
2803 case NF_KEY_DDD
: // DDD
2804 case NF_KEY_DDDD
: // DDDD
2805 case NF_KEY_YY
: // YY
2806 case NF_KEY_YYYY
: // YYYY
2807 case NF_KEY_NN
: // NN
2808 case NF_KEY_NNN
: // NNN
2809 case NF_KEY_NNNN
: // NNNN
2810 case NF_KEY_WW
: // WW
2811 case NF_KEY_AAA
: // AAA
2812 case NF_KEY_AAAA
: // AAAA
2813 case NF_KEY_EC
: // E
2814 case NF_KEY_EEC
: // EE
2815 case NF_KEY_G
: // G
2816 case NF_KEY_GG
: // GG
2817 case NF_KEY_GGG
: // GGG
2818 case NF_KEY_R
: // R
2819 case NF_KEY_RR
: // RR
2821 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2822 nPos
= nPos
+ sStrArray
[i
].getLength();
2825 // For simple numeric date formats record date order and
2827 switch (nTypeArray
[i
])
2831 if (nMonthPos
== SAL_MAX_UINT16
)
2834 bNewDateOrder
= false;
2838 if (nDayPos
== SAL_MAX_UINT16
)
2841 bNewDateOrder
= false;
2845 if (nYearPos
== SAL_MAX_UINT16
)
2848 bNewDateOrder
= false;
2856 case NF_KEY_THAI_T
:
2858 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]];
2859 nPos
= nPos
+ sStrArray
[i
].getLength();
2862 default: // Other keywords
2863 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2864 nPos
= nPos
+ sStrArray
[i
].getLength();
2869 nCntPost
= nCounter
; // decimals (100th seconds)
2872 nCntExp
= 1; // Remembers AM/PM
2874 break; // of SvNumFormatType::DATETIME
2878 if (eScannedType
== SvNumFormatType::SCIENTIFIC
&&
2879 (nCntPre
+ nCntPost
== 0 || nCntExp
== 0))
2883 else if (eScannedType
== SvNumFormatType::FRACTION
&& (nCntExp
> 8 || nCntExp
== 0))
2887 if (bThaiT
&& !GetNatNumModifier())
2889 SetNatNumModifier(1);
2893 if (bNewDateOrder
&& sOldDateSep
== "-")
2895 // Keep ISO formats Y-M-D, Y-M and M-D
2896 if (IsDateFragment( nYearPos
, nMonthPos
))
2898 nTypeArray
[nYearPos
+1] = NF_SYMBOLTYPE_STRING
;
2899 sStrArray
[nYearPos
+1] = sOldDateSep
;
2900 bNewDateOrder
= false;
2902 if (IsDateFragment( nMonthPos
, nDayPos
))
2904 nTypeArray
[nMonthPos
+1] = NF_SYMBOLTYPE_STRING
;
2905 sStrArray
[nMonthPos
+1] = sOldDateSep
;
2906 bNewDateOrder
= false;
2911 // Rearrange date order to the target locale if the original order
2912 // includes date separators and is adjacent.
2913 /* TODO: for incomplete dates trailing separators need to be
2914 * handled according to the locale's usage, e.g. en-US M/D should
2915 * be converted to de-DE D.M. and vice versa. As is, it's
2916 * M/D -> D.M and D.M. -> M/D/ where specifically the latter looks
2917 * odd. Check accepted date patterns and append/remove? */
2918 switch (eOldDateOrder
)
2920 case DateOrder::DMY
:
2921 switch (pLoc
->getDateOrder())
2923 case DateOrder::MDY
:
2924 // Convert only if the actual format is not of YDM
2925 // order (which would be a completely unusual order
2926 // anyway, but..), e.g. YYYY.DD.MM not to
2928 if (IsDateFragment( nDayPos
, nMonthPos
) && !IsDateFragment( nYearPos
, nDayPos
))
2929 SwapArrayElements( nDayPos
, nMonthPos
);
2931 case DateOrder::YMD
:
2932 if (nYearPos
!= SAL_MAX_UINT16
)
2934 if (IsDateFragment( nDayPos
, nMonthPos
) && IsDateFragment( nMonthPos
, nYearPos
))
2935 SwapArrayElements( nDayPos
, nYearPos
);
2939 if (IsDateFragment( nDayPos
, nMonthPos
))
2940 SwapArrayElements( nDayPos
, nMonthPos
);
2947 case DateOrder::MDY
:
2948 switch (pLoc
->getDateOrder())
2950 case DateOrder::DMY
:
2951 // Convert only if the actual format is not of YMD
2952 // order, e.g. YYYY/MM/DD not to YYYY.DD.MM
2953 /* TODO: convert such to DD.MM.YYYY instead? */
2954 if (IsDateFragment( nMonthPos
, nDayPos
) && !IsDateFragment( nYearPos
, nMonthPos
))
2955 SwapArrayElements( nMonthPos
, nDayPos
);
2957 case DateOrder::YMD
:
2958 if (nYearPos
!= SAL_MAX_UINT16
)
2960 if (IsDateFragment( nMonthPos
, nDayPos
) && IsDateFragment( nDayPos
, nYearPos
))
2962 SwapArrayElements( nYearPos
, nMonthPos
); // YDM
2963 SwapArrayElements( nYearPos
, nDayPos
); // YMD
2971 case DateOrder::YMD
:
2972 switch (pLoc
->getDateOrder())
2974 case DateOrder::DMY
:
2975 if (nYearPos
!= SAL_MAX_UINT16
)
2977 if (IsDateFragment( nYearPos
, nMonthPos
) && IsDateFragment( nMonthPos
, nDayPos
))
2978 SwapArrayElements( nYearPos
, nDayPos
);
2982 if (IsDateFragment( nMonthPos
, nDayPos
))
2983 SwapArrayElements( nMonthPos
, nDayPos
);
2986 case DateOrder::MDY
:
2987 if (nYearPos
!= SAL_MAX_UINT16
)
2989 if (IsDateFragment( nYearPos
, nMonthPos
) && IsDateFragment( nMonthPos
, nDayPos
))
2991 SwapArrayElements( nYearPos
, nDayPos
); // DMY
2992 SwapArrayElements( nYearPos
, nMonthPos
); // MDY
3004 // strings containing keywords of the target locale must be quoted, so
3005 // the user sees the difference and is able to edit the format string
3006 for ( i
=0; i
< nStringsCnt
; i
++ )
3008 if ( nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
&&
3009 sStrArray
[i
][0] != '\"' )
3011 if ( bConvertSystemToSystem
&& eScannedType
== SvNumFormatType::CURRENCY
)
3013 // don't stringize automatic currency, will be converted
3014 if ( sStrArray
[i
] == sOldCurSymbol
)
3018 // DM might be split into D and M
3019 if ( sStrArray
[i
].getLength() < sOldCurSymbol
.getLength() &&
3020 pChrCls
->uppercase( sStrArray
[i
], 0, 1 )[0] ==
3023 OUString
aTmp( sStrArray
[i
] );
3024 sal_uInt16 j
= i
+ 1;
3025 while ( aTmp
.getLength() < sOldCurSymbol
.getLength() &&
3027 nTypeArray
[j
] == NF_SYMBOLTYPE_STRING
)
3029 aTmp
+= sStrArray
[j
++];
3031 if ( pChrCls
->uppercase( aTmp
) == sOldCurString
)
3033 sStrArray
[i
++] = aTmp
;
3036 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
3037 nResultStringsCnt
--;
3044 OUString
& rStr
= sStrArray
[i
];
3045 sal_Int32 nLen
= rStr
.getLength();
3046 for ( sal_Int32 j
= 0; j
< nLen
; j
++ )
3048 bool bFoundEnglish
= false;
3049 if ( (j
== 0 || rStr
[j
- 1] != '\\') && GetKeyWord( rStr
, j
, bFoundEnglish
) )
3051 rStr
= "\"" + rStr
+ "\"";
3058 // Concatenate strings, remove quotes for output, and rebuild the format string
3061 while (i
< nStringsCnt
)
3063 sal_Int32 nStringPos
;
3064 sal_Int32 nArrPos
= 0;
3065 sal_uInt16 iPos
= i
;
3066 switch ( nTypeArray
[i
] )
3068 case NF_SYMBOLTYPE_STRING
:
3069 case NF_SYMBOLTYPE_FRACBLANK
:
3070 nStringPos
= rString
.getLength();
3073 if (sStrArray
[i
].getLength() == 2 &&
3074 sStrArray
[i
][0] == '\\')
3076 // Unescape some simple forms of symbols even in the UI
3077 // visible string to prevent duplicates that differ
3078 // only in notation, originating from import.
3079 // e.g. YYYY-MM-DD and YYYY\-MM\-DD are identical,
3080 // but 0\ 000 0 and 0 000 0 in a French locale are not.
3082 sal_Unicode c
= sStrArray
[i
][1];
3088 rString
+= OUStringChar(c
);
3093 if (!(eScannedType
& SvNumFormatType::DATE
) &&
3094 (StringEqualsChar( pFormatter
->GetNumThousandSep(), c
) ||
3095 StringEqualsChar( pFormatter
->GetNumDecimalSep(), c
) ||
3097 (StringEqualsChar( pFormatter
->GetNumThousandSep(), cNoBreakSpace
) ||
3098 StringEqualsChar( pFormatter
->GetNumThousandSep(), cNarrowNoBreakSpace
)))))
3100 rString
+= sStrArray
[i
];
3102 else if ((eScannedType
& SvNumFormatType::DATE
) &&
3103 StringEqualsChar( pFormatter
->GetDateSep(), c
))
3105 rString
+= sStrArray
[i
];
3107 else if ((eScannedType
& SvNumFormatType::TIME
) &&
3108 (StringEqualsChar( pLoc
->getTimeSep(), c
) ||
3109 StringEqualsChar( pLoc
->getTime100SecSep(), c
)))
3111 rString
+= sStrArray
[i
];
3113 else if (eScannedType
& SvNumFormatType::FRACTION
)
3115 rString
+= sStrArray
[i
];
3119 rString
+= OUStringChar(c
);
3123 rString
+= sStrArray
[i
];
3128 rString
+= sStrArray
[i
];
3130 if ( RemoveQuotes( sStrArray
[i
] ) > 0 )
3132 // update currency up to quoted string
3133 if ( eScannedType
== SvNumFormatType::CURRENCY
)
3135 // dM -> DM or DM -> $ in old automatic
3136 // currency formats, oh my ..., why did we ever introduce them?
3137 OUString
aTmp( pChrCls
->uppercase( sStrArray
[iPos
], nArrPos
,
3138 sStrArray
[iPos
].getLength()-nArrPos
) );
3139 sal_Int32 nCPos
= aTmp
.indexOf( sOldCurString
);
3142 const OUString
& rCur
= bConvertMode
&& bConvertSystemToSystem
?
3143 GetCurSymbol() : sOldCurSymbol
;
3144 sStrArray
[iPos
] = sStrArray
[iPos
].replaceAt( nArrPos
+ nCPos
,
3145 sOldCurString
.getLength(),
3147 rString
= rString
.replaceAt( nStringPos
+ nCPos
,
3148 sOldCurString
.getLength(),
3151 nStringPos
= rString
.getLength();
3154 nArrPos
= sStrArray
[iPos
].getLength();
3158 nArrPos
= sStrArray
[iPos
].getLength() + sStrArray
[i
].getLength();
3164 sStrArray
[iPos
] += sStrArray
[i
];
3165 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
3166 nResultStringsCnt
--;
3170 while ( i
< nStringsCnt
&& nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
);
3172 if ( i
< nStringsCnt
)
3174 i
--; // enter switch on next symbol again
3176 if ( eScannedType
== SvNumFormatType::CURRENCY
&& nStringPos
< rString
.getLength() )
3178 // same as above, since last RemoveQuotes
3179 OUString
aTmp( pChrCls
->uppercase( sStrArray
[iPos
], nArrPos
,
3180 sStrArray
[iPos
].getLength()-nArrPos
) );
3181 sal_Int32 nCPos
= aTmp
.indexOf( sOldCurString
);
3184 const OUString
& rCur
= bConvertMode
&& bConvertSystemToSystem
?
3185 GetCurSymbol() : sOldCurSymbol
;
3186 sStrArray
[iPos
] = sStrArray
[iPos
].replaceAt( nArrPos
+ nCPos
,
3187 sOldCurString
.getLength(),
3189 rString
= rString
.replaceAt( nStringPos
+ nCPos
,
3190 sOldCurString
.getLength(), rCur
);
3194 case NF_SYMBOLTYPE_CURRENCY
:
3195 rString
+= sStrArray
[i
];
3196 RemoveQuotes( sStrArray
[i
] );
3199 if (bThaiT
&& GetNatNumModifier() == 1)
3201 // Remove T from format code, will be replaced with a [NatNum1] prefix.
3202 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
3203 nResultStringsCnt
--;
3207 rString
+= sStrArray
[i
];
3210 case NF_SYMBOLTYPE_EMPTY
:
3214 rString
+= sStrArray
[i
];
3221 sal_Int32
ImpSvNumberformatScan::RemoveQuotes( OUString
& rStr
)
3223 if ( rStr
.getLength() > 1 )
3225 sal_Unicode c
= rStr
[0];
3226 sal_Int32 n
= rStr
.getLength() - 1;
3227 if ( c
== '"' && rStr
[n
] == '"' )
3229 rStr
= rStr
.copy( 1, n
-1);
3232 else if ( c
== '\\' )
3234 rStr
= rStr
.copy(1);
3241 sal_Int32
ImpSvNumberformatScan::ScanFormat( OUString
& rString
)
3243 sal_Int32 res
= Symbol_Division(rString
); // Lexical analysis
3246 res
= ScanType(); // Recognizing the Format type
3250 res
= FinalScan( rString
); // Type dependent final analysis
3252 return res
; // res = control position; res = 0 => Format ok
3255 void ImpSvNumberformatScan::CopyInfo(ImpSvNumberformatInfo
* pInfo
, sal_uInt16 nCnt
)
3260 while (i
< nCnt
&& j
< NF_MAX_FORMAT_SYMBOLS
)
3262 if (nTypeArray
[j
] != NF_SYMBOLTYPE_EMPTY
)
3264 pInfo
->sStrArray
[i
] = sStrArray
[j
];
3265 pInfo
->nTypeArray
[i
] = nTypeArray
[j
];
3270 pInfo
->eScannedType
= eScannedType
;
3271 pInfo
->bThousand
= bThousand
;
3272 pInfo
->nThousand
= nThousand
;
3273 pInfo
->nCntPre
= nCntPre
;
3274 pInfo
->nCntPost
= nCntPost
;
3275 pInfo
->nCntExp
= nCntExp
;
3278 void ImpSvNumberformatScan::ReplaceBooleanEquivalent( OUString
& rString
)
3281 /* TODO: compare case insensitive? Or rather leave as is and case not
3282 * matching indicates user supplied on purpose? Written to file / generated
3283 * was always uppercase. */
3284 if (rString
== sBooleanEquivalent1
|| rString
== sBooleanEquivalent2
)
3285 rString
= GetKeywords()[NF_KEY_BOOLEAN
];
3288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */