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 ::std::vector
<Color
> ImpSvNumberformatScan::StandardColor
;
110 bool ImpSvNumberformatScan::bStandardColorNeedInitialization
= true;
112 // This vector will hold *only* the color names in German language.
113 ::std::vector
<OUString
> ImpSvNumberformatScan::sGermanColorNames
;
115 const OUString
ImpSvNumberformatScan::sErrStr
= "###";
117 ImpSvNumberformatScan::ImpSvNumberformatScan( SvNumberFormatter
* pFormatterP
)
118 : maNullDate( 30, 12, 1899)
119 , eNewLnge(LANGUAGE_DONTKNOW
)
120 , eTmpLnge(LANGUAGE_DONTKNOW
)
122 , meKeywordLocalization(KeywordLocalization::AllowEnglish
)
124 pFormatter
= pFormatterP
;
125 xNFC
= css::i18n::NumberFormatMapper::create( pFormatter
->GetComponentContext() );
126 bConvertMode
= false;
127 mbConvertDateOrder
= false;
128 bConvertSystemToSystem
= false;
129 bKeywordsNeedInit
= true; // locale dependent and not locale dependent keywords
130 bCompatCurNeedInit
= true; // locale dependent compatibility currency strings
132 if ( bStandardColorNeedInitialization
)
134 bStandardColorNeedInitialization
= false;
135 StandardColor
.push_back( COL_BLACK
);
136 StandardColor
.push_back( COL_LIGHTBLUE
);
137 StandardColor
.push_back( COL_LIGHTGREEN
);
138 StandardColor
.push_back( COL_LIGHTCYAN
);
139 StandardColor
.push_back( COL_LIGHTRED
);
140 StandardColor
.push_back( COL_LIGHTMAGENTA
);
141 StandardColor
.push_back( COL_BROWN
);
142 StandardColor
.push_back( COL_GRAY
);
143 StandardColor
.push_back( COL_YELLOW
);
144 StandardColor
.push_back( COL_WHITE
);
146 static_assert( NF_KEY_BLACK
- NF_KEY_COLOR
== 1, "bad FARBE(COLOR), SCHWARZ(BLACK) sequence");
147 static_assert( NF_KEY_FIRSTCOLOR
- NF_KEY_COLOR
== 1, "bad color sequence");
148 static_assert( NF_MAX_DEFAULT_COLORS
+ 1 == 11, "bad color count");
149 static_assert( NF_KEY_WHITE
- NF_KEY_COLOR
+ 1 == 11, "bad color sequence count");
151 sGermanColorNames
.resize( NF_KEY_WHITE
- NF_KEY_COLOR
+ 1 );
152 sGermanColorNames
[NF_KEY_COLOR
- NF_KEY_COLOR
] = "FARBE";
153 sGermanColorNames
[NF_KEY_BLACK
- NF_KEY_COLOR
] = "SCHWARZ";
154 sGermanColorNames
[NF_KEY_BLUE
- NF_KEY_COLOR
] = "BLAU";
155 sGermanColorNames
[NF_KEY_GREEN
- NF_KEY_COLOR
] = OUString( "GR" "\xDC" "N", 4, RTL_TEXTENCODING_ISO_8859_1
);
156 sGermanColorNames
[NF_KEY_CYAN
- NF_KEY_COLOR
] = "CYAN";
157 sGermanColorNames
[NF_KEY_RED
- NF_KEY_COLOR
] = "ROT";
158 sGermanColorNames
[NF_KEY_MAGENTA
- NF_KEY_COLOR
] = "MAGENTA";
159 sGermanColorNames
[NF_KEY_BROWN
- NF_KEY_COLOR
] = "BRAUN";
160 sGermanColorNames
[NF_KEY_GREY
- NF_KEY_COLOR
] = "GRAU";
161 sGermanColorNames
[NF_KEY_YELLOW
- NF_KEY_COLOR
] = "GELB";
162 sGermanColorNames
[NF_KEY_WHITE
- NF_KEY_COLOR
] = "WEISS";
170 ImpSvNumberformatScan::~ImpSvNumberformatScan()
175 void ImpSvNumberformatScan::ChangeIntl( KeywordLocalization eKeywordLocalization
)
177 meKeywordLocalization
= eKeywordLocalization
;
178 bKeywordsNeedInit
= true;
179 bCompatCurNeedInit
= true;
180 // may be initialized by InitSpecialKeyword()
181 sKeyword
[NF_KEY_TRUE
].clear();
182 sKeyword
[NF_KEY_FALSE
].clear();
185 void ImpSvNumberformatScan::InitSpecialKeyword( NfKeywordIndex eIdx
) const
190 const_cast<ImpSvNumberformatScan
*>(this)->sKeyword
[NF_KEY_TRUE
] =
191 pFormatter
->GetCharClass()->uppercase( pFormatter
->GetLocaleData()->getTrueWord() );
192 if ( sKeyword
[NF_KEY_TRUE
].isEmpty() )
194 SAL_WARN( "svl.numbers", "InitSpecialKeyword: TRUE_WORD?" );
195 const_cast<ImpSvNumberformatScan
*>(this)->sKeyword
[NF_KEY_TRUE
] = sEnglishKeyword
[NF_KEY_TRUE
];
199 const_cast<ImpSvNumberformatScan
*>(this)->sKeyword
[NF_KEY_FALSE
] =
200 pFormatter
->GetCharClass()->uppercase( pFormatter
->GetLocaleData()->getFalseWord() );
201 if ( sKeyword
[NF_KEY_FALSE
].isEmpty() )
203 SAL_WARN( "svl.numbers", "InitSpecialKeyword: FALSE_WORD?" );
204 const_cast<ImpSvNumberformatScan
*>(this)->sKeyword
[NF_KEY_FALSE
] = sEnglishKeyword
[NF_KEY_FALSE
];
208 SAL_WARN( "svl.numbers", "InitSpecialKeyword: unknown request" );
212 void ImpSvNumberformatScan::InitCompatCur() const
214 ImpSvNumberformatScan
* pThis
= const_cast<ImpSvNumberformatScan
*>(this);
215 // currency symbol for old style ("automatic") compatibility format codes
216 pFormatter
->GetCompatibilityCurrency( pThis
->sCurSymbol
, pThis
->sCurAbbrev
);
217 // currency symbol upper case
218 pThis
->sCurString
= pFormatter
->GetCharClass()->uppercase( sCurSymbol
);
219 bCompatCurNeedInit
= false;
222 void ImpSvNumberformatScan::InitKeywords() const
224 if ( !bKeywordsNeedInit
)
226 const_cast<ImpSvNumberformatScan
*>(this)->SetDependentKeywords();
227 bKeywordsNeedInit
= false;
230 /** Extract the name of General, Standard, Whatever, ignoring leading modifiers
231 such as [NatNum1]. */
232 static OUString
lcl_extractStandardGeneralName( const OUString
& rCode
)
235 const sal_Unicode
* p
= rCode
.getStr();
236 const sal_Unicode
* const pStop
= p
+ rCode
.getLength();
237 const sal_Unicode
* pBeg
= p
; // name begins here
240 while (p
< pStop
&& !bDone
)
253 // else: would be a locale data error, easily to be spotted in
260 --p
; // put back, increment by one follows
272 aStr
= rCode
.copy( pBeg
- rCode
.getStr(), p
- pBeg
);
277 void ImpSvNumberformatScan::SetDependentKeywords()
279 using namespace ::com::sun::star
;
280 using namespace ::com::sun::star::uno
;
282 const CharClass
* pCharClass
= pFormatter
->GetCharClass();
283 const LocaleDataWrapper
* pLocaleData
= pFormatter
->GetLocaleData();
284 // #80023# be sure to generate keywords for the loaded Locale, not for the
285 // requested Locale, otherwise number format codes might not match
286 const LanguageTag
& rLoadedLocale
= pLocaleData
->getLoadedLanguageTag();
287 LanguageType eLang
= rLoadedLocale
.getLanguageType( false);
289 bool bL10n
= (meKeywordLocalization
!= KeywordLocalization::EnglishOnly
);
292 // Check if this actually is a locale that uses any localized keywords,
293 // if not then disable localized keywords completely.
294 if ( !eLang
.anyOf( LANGUAGE_GERMAN
,
295 LANGUAGE_GERMAN_SWISS
,
296 LANGUAGE_GERMAN_AUSTRIAN
,
297 LANGUAGE_GERMAN_LUXEMBOURG
,
298 LANGUAGE_GERMAN_LIECHTENSTEIN
,
300 LANGUAGE_DUTCH_BELGIAN
,
302 LANGUAGE_FRENCH_BELGIAN
,
303 LANGUAGE_FRENCH_CANADIAN
,
304 LANGUAGE_FRENCH_SWISS
,
305 LANGUAGE_FRENCH_LUXEMBOURG
,
306 LANGUAGE_FRENCH_MONACO
,
309 LANGUAGE_ITALIAN_SWISS
,
312 LANGUAGE_NORWEGIAN_BOKMAL
,
313 LANGUAGE_NORWEGIAN_NYNORSK
,
315 LANGUAGE_SWEDISH_FINLAND
,
317 LANGUAGE_PORTUGUESE_BRAZILIAN
,
318 LANGUAGE_SPANISH_MODERN
,
319 LANGUAGE_SPANISH_DATED
,
320 LANGUAGE_SPANISH_MEXICAN
,
321 LANGUAGE_SPANISH_GUATEMALA
,
322 LANGUAGE_SPANISH_COSTARICA
,
323 LANGUAGE_SPANISH_PANAMA
,
324 LANGUAGE_SPANISH_DOMINICAN_REPUBLIC
,
325 LANGUAGE_SPANISH_VENEZUELA
,
326 LANGUAGE_SPANISH_COLOMBIA
,
327 LANGUAGE_SPANISH_PERU
,
328 LANGUAGE_SPANISH_ARGENTINA
,
329 LANGUAGE_SPANISH_ECUADOR
,
330 LANGUAGE_SPANISH_CHILE
,
331 LANGUAGE_SPANISH_URUGUAY
,
332 LANGUAGE_SPANISH_PARAGUAY
,
333 LANGUAGE_SPANISH_BOLIVIA
,
334 LANGUAGE_SPANISH_EL_SALVADOR
,
335 LANGUAGE_SPANISH_HONDURAS
,
336 LANGUAGE_SPANISH_NICARAGUA
,
337 LANGUAGE_SPANISH_PUERTO_RICO
))
340 meKeywordLocalization
= KeywordLocalization::EnglishOnly
;
344 // Init the current NfKeywordTable with English keywords.
345 sKeyword
= sEnglishKeyword
;
347 // Set the uppercase localized General name, e.g. Standard -> STANDARD
348 i18n::NumberFormatCode aFormat
= xNFC
->getFormatCode( NF_NUMBER_STANDARD
, rLoadedLocale
.getLocale() );
349 sNameStandardFormat
= lcl_extractStandardGeneralName( aFormat
.Code
);
350 sKeyword
[NF_KEY_GENERAL
] = pCharClass
->uppercase( sNameStandardFormat
);
352 // Thai T NatNum special. Other locale's small letter 't' results in upper
353 // case comparison not matching but length does in conversion mode. Ugly.
354 if (eLang
== LANGUAGE_THAI
)
356 sKeyword
[NF_KEY_THAI_T
] = "T";
360 sKeyword
[NF_KEY_THAI_T
] = sEnglishKeyword
[NF_KEY_THAI_T
];
364 InitSpecialKeyword( NF_KEY_TRUE
);
365 InitSpecialKeyword( NF_KEY_FALSE
);
367 // Boolean equivalent format codes that are written to Excel files, may
368 // have been written to ODF as well, specifically if such loaded Excel file
369 // was saved as ODF, and shall result in proper Boolean again.
370 // "TRUE";"TRUE";"FALSE"
371 sBooleanEquivalent1
= "\"" + sKeyword
[NF_KEY_TRUE
] + "\";\"" +
372 sKeyword
[NF_KEY_TRUE
] + "\";\"" + sKeyword
[NF_KEY_FALSE
] + "\"";
373 // [>0]"TRUE";[<0]"TRUE";"FALSE"
374 sBooleanEquivalent2
= "[>0]\"" + sKeyword
[NF_KEY_TRUE
] + "\";[<0]\"" +
375 sKeyword
[NF_KEY_TRUE
] + "\";\"" + sKeyword
[NF_KEY_FALSE
] + "\"";
377 // compatibility currency strings
383 // All locale dependent keywords overrides follow.
387 LANGUAGE_GERMAN_SWISS
,
388 LANGUAGE_GERMAN_AUSTRIAN
,
389 LANGUAGE_GERMAN_LUXEMBOURG
,
390 LANGUAGE_GERMAN_LIECHTENSTEIN
))
392 //! all capital letters
393 sKeyword
[NF_KEY_M
] = "M"; // month 1
394 sKeyword
[NF_KEY_MM
] = "MM"; // month 01
395 sKeyword
[NF_KEY_MMM
] = "MMM"; // month Jan
396 sKeyword
[NF_KEY_MMMM
] = "MMMM"; // month Januar
397 sKeyword
[NF_KEY_MMMMM
] = "MMMMM"; // month J
398 sKeyword
[NF_KEY_H
] = "H"; // hour 2
399 sKeyword
[NF_KEY_HH
] = "HH"; // hour 02
400 sKeyword
[NF_KEY_D
] = "T";
401 sKeyword
[NF_KEY_DD
] = "TT";
402 sKeyword
[NF_KEY_DDD
] = "TTT";
403 sKeyword
[NF_KEY_DDDD
] = "TTTT";
404 sKeyword
[NF_KEY_YY
] = "JJ";
405 sKeyword
[NF_KEY_YYYY
] = "JJJJ";
406 sKeyword
[NF_KEY_BOOLEAN
] = "LOGISCH";
407 sKeyword
[NF_KEY_COLOR
] = sGermanColorNames
[NF_KEY_COLOR
- NF_KEY_COLOR
];
408 sKeyword
[NF_KEY_BLACK
] = sGermanColorNames
[NF_KEY_BLACK
- NF_KEY_COLOR
];
409 sKeyword
[NF_KEY_BLUE
] = sGermanColorNames
[NF_KEY_BLUE
- NF_KEY_COLOR
];
410 sKeyword
[NF_KEY_GREEN
] = sGermanColorNames
[NF_KEY_GREEN
- NF_KEY_COLOR
];
411 sKeyword
[NF_KEY_CYAN
] = sGermanColorNames
[NF_KEY_CYAN
- NF_KEY_COLOR
];
412 sKeyword
[NF_KEY_RED
] = sGermanColorNames
[NF_KEY_RED
- NF_KEY_COLOR
];
413 sKeyword
[NF_KEY_MAGENTA
] = sGermanColorNames
[NF_KEY_MAGENTA
- NF_KEY_COLOR
];
414 sKeyword
[NF_KEY_BROWN
] = sGermanColorNames
[NF_KEY_BROWN
- NF_KEY_COLOR
];
415 sKeyword
[NF_KEY_GREY
] = sGermanColorNames
[NF_KEY_GREY
- NF_KEY_COLOR
];
416 sKeyword
[NF_KEY_YELLOW
] = sGermanColorNames
[NF_KEY_YELLOW
- NF_KEY_COLOR
];
417 sKeyword
[NF_KEY_WHITE
] = sGermanColorNames
[NF_KEY_WHITE
- NF_KEY_COLOR
];
424 LANGUAGE_ITALIAN_SWISS
))
426 sKeyword
[NF_KEY_D
] = "G";
427 sKeyword
[NF_KEY_DD
] = "GG";
428 sKeyword
[NF_KEY_DDD
] = "GGG";
429 sKeyword
[NF_KEY_DDDD
] = "GGGG";
430 // must exchange the era code, same as Xcl
431 sKeyword
[NF_KEY_G
] = "X";
432 sKeyword
[NF_KEY_GG
] = "XX";
433 sKeyword
[NF_KEY_GGG
] = "XXX";
435 else if ( eLang
.anyOf(
437 LANGUAGE_FRENCH_BELGIAN
,
438 LANGUAGE_FRENCH_CANADIAN
,
439 LANGUAGE_FRENCH_SWISS
,
440 LANGUAGE_FRENCH_LUXEMBOURG
,
441 LANGUAGE_FRENCH_MONACO
))
443 sKeyword
[NF_KEY_D
] = "J";
444 sKeyword
[NF_KEY_DD
] = "JJ";
445 sKeyword
[NF_KEY_DDD
] = "JJJ";
446 sKeyword
[NF_KEY_DDDD
] = "JJJJ";
448 else if ( eLang
== LANGUAGE_FINNISH
)
450 sKeyword
[NF_KEY_D
] = "P";
451 sKeyword
[NF_KEY_DD
] = "PP";
452 sKeyword
[NF_KEY_DDD
] = "PPP";
453 sKeyword
[NF_KEY_DDDD
] = "PPPP";
457 if ( eLang
== LANGUAGE_FINNISH
)
459 sKeyword
[NF_KEY_M
] = "K";
460 sKeyword
[NF_KEY_MM
] = "KK";
461 sKeyword
[NF_KEY_MMM
] = "KKK";
462 sKeyword
[NF_KEY_MMMM
] = "KKKK";
463 sKeyword
[NF_KEY_MMMMM
] = "KKKKK";
469 LANGUAGE_ITALIAN_SWISS
,
471 LANGUAGE_FRENCH_BELGIAN
,
472 LANGUAGE_FRENCH_CANADIAN
,
473 LANGUAGE_FRENCH_SWISS
,
474 LANGUAGE_FRENCH_LUXEMBOURG
,
475 LANGUAGE_FRENCH_MONACO
,
477 LANGUAGE_PORTUGUESE_BRAZILIAN
,
478 LANGUAGE_SPANISH_MODERN
,
479 LANGUAGE_SPANISH_DATED
,
480 LANGUAGE_SPANISH_MEXICAN
,
481 LANGUAGE_SPANISH_GUATEMALA
,
482 LANGUAGE_SPANISH_COSTARICA
,
483 LANGUAGE_SPANISH_PANAMA
,
484 LANGUAGE_SPANISH_DOMINICAN_REPUBLIC
,
485 LANGUAGE_SPANISH_VENEZUELA
,
486 LANGUAGE_SPANISH_COLOMBIA
,
487 LANGUAGE_SPANISH_PERU
,
488 LANGUAGE_SPANISH_ARGENTINA
,
489 LANGUAGE_SPANISH_ECUADOR
,
490 LANGUAGE_SPANISH_CHILE
,
491 LANGUAGE_SPANISH_URUGUAY
,
492 LANGUAGE_SPANISH_PARAGUAY
,
493 LANGUAGE_SPANISH_BOLIVIA
,
494 LANGUAGE_SPANISH_EL_SALVADOR
,
495 LANGUAGE_SPANISH_HONDURAS
,
496 LANGUAGE_SPANISH_NICARAGUA
,
497 LANGUAGE_SPANISH_PUERTO_RICO
))
499 sKeyword
[NF_KEY_YY
] = "AA";
500 sKeyword
[NF_KEY_YYYY
] = "AAAA";
501 // must exchange the day of week name code, same as Xcl
502 sKeyword
[NF_KEY_AAA
] = "OOO";
503 sKeyword
[NF_KEY_AAAA
] = "OOOO";
505 else if ( eLang
.anyOf(
507 LANGUAGE_DUTCH_BELGIAN
))
509 sKeyword
[NF_KEY_YY
] = "JJ";
510 sKeyword
[NF_KEY_YYYY
] = "JJJJ";
512 else if ( eLang
== LANGUAGE_FINNISH
)
514 sKeyword
[NF_KEY_YY
] = "VV";
515 sKeyword
[NF_KEY_YYYY
] = "VVVV";
521 LANGUAGE_DUTCH_BELGIAN
))
523 sKeyword
[NF_KEY_H
] = "U";
524 sKeyword
[NF_KEY_HH
] = "UU";
526 else if ( eLang
.anyOf(
529 LANGUAGE_SWEDISH_FINLAND
,
532 LANGUAGE_NORWEGIAN_BOKMAL
,
533 LANGUAGE_NORWEGIAN_NYNORSK
))
535 sKeyword
[NF_KEY_H
] = "T";
536 sKeyword
[NF_KEY_HH
] = "TT";
541 void ImpSvNumberformatScan::ChangeNullDate(sal_uInt16 nDay
, sal_uInt16 nMonth
, sal_Int16 nYear
)
543 maNullDate
= Date(nDay
, nMonth
, nYear
);
544 if (!maNullDate
.IsValidDate())
546 maNullDate
.Normalize();
547 SAL_WARN("svl.numbers","ImpSvNumberformatScan::ChangeNullDate - not valid"
548 " d: " << nDay
<< " m: " << nMonth
<< " y: " << nYear
<< " normalized to"
549 " d: " << maNullDate
.GetDay() << " m: " << maNullDate
.GetMonth() << " y: " << maNullDate
.GetYear());
553 void ImpSvNumberformatScan::ChangeStandardPrec(sal_uInt16 nPrec
)
555 nStandardPrec
= nPrec
;
558 Color
* ImpSvNumberformatScan::GetColor(OUString
& sStr
)
560 OUString sString
= pFormatter
->GetCharClass()->uppercase(sStr
);
561 const NfKeywordTable
& rKeyword
= GetKeywords();
563 while (i
< NF_MAX_DEFAULT_COLORS
&& sString
!= rKeyword
[NF_KEY_FIRSTCOLOR
+i
] )
567 if (i
>= NF_MAX_DEFAULT_COLORS
&& meKeywordLocalization
== KeywordLocalization::AllowEnglish
)
569 LanguageType eLang
= pFormatter
->GetLocaleData()->getLoadedLanguageTag().getLanguageType( false);
572 LANGUAGE_GERMAN_SWISS
,
573 LANGUAGE_GERMAN_AUSTRIAN
,
574 LANGUAGE_GERMAN_LUXEMBOURG
,
575 LANGUAGE_GERMAN_LIECHTENSTEIN
)) // only German uses localized color names
578 while ( j
< NF_MAX_DEFAULT_COLORS
&& sString
!= sEnglishKeyword
[NF_KEY_FIRSTCOLOR
+ j
] )
582 if ( j
< NF_MAX_DEFAULT_COLORS
)
589 enum ColorKeywordConversion
594 } eColorKeywordConversion(None
);
598 const bool bFromGerman
= eTmpLnge
.anyOf(
600 LANGUAGE_GERMAN_SWISS
,
601 LANGUAGE_GERMAN_AUSTRIAN
,
602 LANGUAGE_GERMAN_LUXEMBOURG
,
603 LANGUAGE_GERMAN_LIECHTENSTEIN
);
604 const bool bToGerman
= eNewLnge
.anyOf(
606 LANGUAGE_GERMAN_SWISS
,
607 LANGUAGE_GERMAN_AUSTRIAN
,
608 LANGUAGE_GERMAN_LUXEMBOURG
,
609 LANGUAGE_GERMAN_LIECHTENSTEIN
);
610 if (bFromGerman
&& !bToGerman
)
611 eColorKeywordConversion
= ColorKeywordConversion::GermanToEnglish
;
612 else if (!bFromGerman
&& bToGerman
)
613 eColorKeywordConversion
= ColorKeywordConversion::EnglishToGerman
;
616 Color
* pResult
= nullptr;
617 if (i
>= NF_MAX_DEFAULT_COLORS
)
619 const OUString
& rColorWord
= rKeyword
[NF_KEY_COLOR
];
621 if ((bL10n
= sString
.startsWith(rColorWord
)) ||
622 ((meKeywordLocalization
== KeywordLocalization::AllowEnglish
) &&
623 sString
.startsWith(sEnglishKeyword
[NF_KEY_COLOR
])))
625 sal_Int32 nPos
= (bL10n
? rColorWord
.getLength() : sEnglishKeyword
[NF_KEY_COLOR
].getLength());
626 sStr
= sStr
.copy(nPos
);
627 sStr
= comphelper::string::strip(sStr
, ' ');
628 switch (eColorKeywordConversion
)
630 case ColorKeywordConversion::None
:
631 sStr
= rColorWord
+ sStr
;
633 case ColorKeywordConversion::GermanToEnglish
:
634 sStr
= sEnglishKeyword
[NF_KEY_COLOR
] + sStr
; // Farbe -> COLOR
636 case ColorKeywordConversion::EnglishToGerman
:
637 sStr
= sGermanColorNames
[NF_KEY_COLOR
- NF_KEY_COLOR
] + sStr
; // Color -> FARBE
640 sString
= sString
.copy(nPos
);
641 sString
= comphelper::string::strip(sString
, ' ');
643 if ( CharClass::isAsciiNumeric( sString
) )
645 sal_Int32 nIndex
= sString
.toInt32();
646 if (nIndex
> 0 && nIndex
<= 64)
648 pResult
= pFormatter
->GetUserDefColor(static_cast<sal_uInt16
>(nIndex
)-1);
656 switch (eColorKeywordConversion
)
658 case ColorKeywordConversion::None
:
659 sStr
= rKeyword
[NF_KEY_FIRSTCOLOR
+i
];
661 case ColorKeywordConversion::GermanToEnglish
:
662 sStr
= sEnglishKeyword
[NF_KEY_FIRSTCOLOR
+ i
]; // Rot -> RED
664 case ColorKeywordConversion::EnglishToGerman
:
665 sStr
= sGermanColorNames
[NF_KEY_FIRSTCOLOR
- NF_KEY_COLOR
+ i
]; // Red -> ROT
668 pResult
= &(StandardColor
[i
]);
673 short ImpSvNumberformatScan::GetKeyWord( const OUString
& sSymbol
, sal_Int32 nPos
, bool& rbFoundEnglish
) const
675 OUString sString
= pFormatter
->GetCharClass()->uppercase( sSymbol
, nPos
, sSymbol
.getLength() - nPos
);
676 const NfKeywordTable
& rKeyword
= GetKeywords();
677 // #77026# for the Xcl perverts: the GENERAL keyword is recognized anywhere
678 if (sString
.startsWith( rKeyword
[NF_KEY_GENERAL
] ))
680 return NF_KEY_GENERAL
;
682 if ((meKeywordLocalization
== KeywordLocalization::AllowEnglish
) &&
683 sString
.startsWith( sEnglishKeyword
[NF_KEY_GENERAL
]))
685 rbFoundEnglish
= true;
686 return NF_KEY_GENERAL
;
689 // MUST be a reverse search to find longer strings first,
690 // new keywords take precedence over old keywords,
691 // skip colors et al after keywords.
692 short i
= NF_KEY_LASTKEYWORD
;
693 while (i
> 0 && !sString
.startsWith( rKeyword
[i
]))
697 if (i
== 0 && meKeywordLocalization
== KeywordLocalization::AllowEnglish
)
699 // No localized (if so) keyword, try English keywords if keywords
700 // are localized. That was already checked in SetDependentKeywords().
701 i
= NF_KEY_LASTKEYWORD
;
702 while (i
> 0 && !sString
.startsWith( sEnglishKeyword
[i
]))
708 // The Thai T NatNum modifier during Xcl import.
709 if (i
== 0 && bConvertMode
&&
711 eTmpLnge
== LANGUAGE_ENGLISH_US
&&
712 MsLangId::getRealLanguage( eNewLnge
) == LANGUAGE_THAI
)
716 return i
; // 0 => not found
722 * Splits up the input for further processing (by the Turing machine).
724 * Starting state = SsStar
726 * ---------------+-------------------+---------------------------+---------------
727 * Old state | Character read | Event | New state
728 * ---------------+-------------------+---------------------------+---------------
729 * SsStart | Character | Symbol = Character | SsGetWord
730 * | " | Type = String | SsGetString
731 * | \ | Type = String | SsGetChar
732 * | * | Type = Star | SsGetStar
733 * | _ | Type = Blank | SsGetBlank
734 * | @ # 0 ? / . , % [ | Symbol = Character; |
735 * | ] ' Blank | Type = Control character | SsStop
736 * | $ - + ( ) : | Type = String; |
737 * | Else | Symbol = Character | SsStop
738 * ---------------|-------------------+---------------------------+---------------
739 * SsGetChar | Else | Symbol = Character | SsStop
740 * ---------------+-------------------+---------------------------+---------------
741 * GetString | " | | SsStop
742 * | Else | Symbol += Character | GetString
743 * ---------------+-------------------+---------------------------+---------------
744 * SsGetWord | Character | Symbol += Character |
745 * | + - (E+ E-)| Symbol += Character | SsStop
746 * | / (AM/PM)| Symbol += Character |
747 * | Else | Pos--, if Key Type = Word | SsStop
748 * ---------------+-------------------+---------------------------+---------------
749 * SsGetStar | Else | Symbol += Character | SsStop
750 * | | Mark special case * |
751 * ---------------+-------------------+---------------------------+---------------
752 * SsGetBlank | Else | Symbol + =Character | SsStop
753 * | | Mark special case _ |
754 * ---------------------------------------------------------------+--------------
756 * If we recognize a keyword in the state SsGetWord (even as the symbol's start text)
757 * we write back the rest of the characters!
771 short ImpSvNumberformatScan::Next_Symbol( const OUString
& rStr
,
773 OUString
& sSymbol
) const
776 const CharClass
* pChrCls
= pFormatter
->GetCharClass();
777 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
779 ScanState eState
= SsStart
;
780 OUStringBuffer sSymbolBuffer
;
781 while ( nPos
< rStr
.getLength() && eState
!= SsStop
)
783 sal_Unicode cToken
= rStr
[nPos
++];
787 // Fetch any currency longer than one character and don't get
788 // confused later on by "E/" or other combinations of letters
789 // and meaningful symbols. Necessary for old automatic currency.
790 // #96158# But don't do it if we're starting a "[...]" section,
791 // for example a "[$...]" new currency symbol to not parse away
792 // "$U" (symbol) of "[$UYU]" (abbreviation).
793 if ( nCurrPos
>= 0 && sCurString
.getLength() > 1 &&
794 nPos
-1 + sCurString
.getLength() <= rStr
.getLength() &&
795 !(nPos
> 1 && rStr
[nPos
-2] == '[') )
797 OUString aTest
= pChrCls
->uppercase( rStr
.copy( nPos
-1, sCurString
.getLength() ) );
798 if ( aTest
== sCurString
)
800 sSymbol
= rStr
.copy( --nPos
, sCurString
.getLength() );
801 nPos
= nPos
+ sSymbol
.getLength();
802 eType
= NF_SYMBOLTYPE_STRING
;
822 eType
= NF_SYMBOLTYPE_DEL
;
823 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
827 eType
= NF_SYMBOLTYPE_STAR
;
828 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
832 eType
= NF_SYMBOLTYPE_BLANK
;
833 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
837 eType
= NF_SYMBOLTYPE_STRING
;
838 eState
= SsGetString
;
839 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
842 eType
= NF_SYMBOLTYPE_STRING
;
844 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
850 eType
= NF_SYMBOLTYPE_STRING
;
852 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
855 if (StringEqualsChar( pFormatter
->GetNumDecimalSep(), cToken
) ||
856 StringEqualsChar( pFormatter
->GetNumThousandSep(), cToken
) ||
857 StringEqualsChar( pFormatter
->GetDateSep(), cToken
) ||
858 StringEqualsChar( pLoc
->getTimeSep(), cToken
) ||
859 StringEqualsChar( pLoc
->getTime100SecSep(), cToken
))
861 // Another separator than pre-known ASCII
862 eType
= NF_SYMBOLTYPE_DEL
;
863 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
866 else if ( pChrCls
->isLetter( rStr
, nPos
-1 ) )
868 bool bFoundEnglish
= false;
869 short nTmpType
= GetKeyWord( rStr
, nPos
-1, bFoundEnglish
);
872 bool bCurrency
= false;
873 // "Automatic" currency may start with keyword,
874 // like "R" (Rand) and 'R' (era)
875 if ( nCurrPos
>= 0 &&
876 nPos
-1 + sCurString
.getLength() <= rStr
.getLength() &&
877 sCurString
.startsWith( bFoundEnglish
? sEnglishKeyword
[nTmpType
] : sKeyword
[nTmpType
]))
879 OUString aTest
= pChrCls
->uppercase( rStr
.copy( nPos
-1, sCurString
.getLength() ) );
880 if ( aTest
== sCurString
)
888 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
893 // The code to be advanced is the detected keyword,
894 // not necessarily the locale's keyword, but the
895 // symbol is to be the locale's keyword.
899 nLen
= sEnglishKeyword
[eType
].getLength();
900 // Use the locale's General keyword name, not uppercase.
901 sSymbolBuffer
= (eType
== NF_KEY_GENERAL
? sNameStandardFormat
: sKeyword
[eType
]);
905 nLen
= sKeyword
[eType
].getLength();
906 // Preserve a locale's keyword's case as entered.
907 sSymbolBuffer
= rStr
.copy( nPos
-1, nLen
);
909 if ((eType
== NF_KEY_E
|| IsAmbiguousE(eType
)) && nPos
< rStr
.getLength())
911 sal_Unicode cNext
= rStr
[nPos
];
915 case '-' : // E+ E- combine to one symbol
916 sSymbolBuffer
.append(OUStringLiteral1(cNext
));
921 case '#' : // scientific E without sign
934 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
939 eType
= NF_SYMBOLTYPE_STRING
;
941 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
947 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
955 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
958 if ( pChrCls
->isLetter( rStr
, nPos
-1 ) )
960 bool bFoundEnglish
= false;
961 short nTmpType
= GetKeyWord( rStr
, nPos
-1, bFoundEnglish
);
964 // beginning of keyword, stop scan and put back
965 eType
= NF_SYMBOLTYPE_STRING
;
971 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
976 bool bDontStop
= false;
980 case '/': // AM/PM, A/P
981 if (nPos
< rStr
.getLength())
984 if ( cNext
== 'P' || cNext
== 'p' )
986 sal_Int32 nLen
= sSymbolBuffer
.getLength();
988 (sSymbolBuffer
[0] == 'A' || sSymbolBuffer
[0] == 'a') &&
990 (nLen
== 2 && (sSymbolBuffer
[1] == 'M' || sSymbolBuffer
[1] == 'm')
991 && (rStr
[nPos
+ 1] == 'M' || rStr
[nPos
+ 1] == 'm'))))
993 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
1000 // anything not recognized will stop the scan
1001 if ( eState
!= SsStop
&& !bDontStop
)
1005 eType
= NF_SYMBOLTYPE_STRING
;
1011 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
1015 sSymbolBuffer
.append(OUStringLiteral1(cToken
));
1021 if (eState
== SsGetWord
)
1023 eType
= NF_SYMBOLTYPE_STRING
;
1025 sSymbol
= sSymbolBuffer
.makeStringAndClear();
1029 sal_Int32
ImpSvNumberformatScan::Symbol_Division(const OUString
& rString
)
1032 // Do we have some sort of currency?
1033 OUString sString
= pFormatter
->GetCharClass()->uppercase(rString
);
1034 sal_Int32 nCPos
= 0;
1035 while (nCPos
>= 0 && nCPos
< sString
.getLength())
1037 nCPos
= sString
.indexOf(GetCurString(),nCPos
);
1041 sal_Int32 nQ
= SvNumberformat::GetQuoteEnd( sString
, nCPos
);
1046 ((c
= sString
[nCPos
-1]) != '"'
1047 && c
!= '\\') ) // dm can be protected by "dm \d
1054 nCPos
++; // Continue search
1059 nCPos
= nQ
+ 1; // Continue search
1064 bool bStar
= false; // Is set on detecting '*'
1068 const sal_Int32 nLen
= rString
.getLength();
1069 while (nPos
< nLen
&& nStringsCnt
< NF_MAX_FORMAT_SYMBOLS
)
1071 nTypeArray
[nStringsCnt
] = Next_Symbol(rString
, nPos
, sStrArray
[nStringsCnt
]);
1072 if (nTypeArray
[nStringsCnt
] == NF_SYMBOLTYPE_STAR
)
1073 { // Monitoring the '*'
1076 return nPos
; // Error: double '*'
1080 // Valid only if there is a character following, else we are
1081 // at the end of a code that does not have a fill character
1083 if (sStrArray
[nStringsCnt
].getLength() < 2)
1091 return 0; // 0 => ok
1094 void ImpSvNumberformatScan::SkipStrings(sal_uInt16
& i
, sal_Int32
& nPos
) const
1096 while (i
< nStringsCnt
&& ( nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
1097 || nTypeArray
[i
] == NF_SYMBOLTYPE_BLANK
1098 || nTypeArray
[i
] == NF_SYMBOLTYPE_STAR
) )
1100 nPos
= nPos
+ sStrArray
[i
].getLength();
1105 sal_uInt16
ImpSvNumberformatScan::PreviousKeyword(sal_uInt16 i
) const
1108 if (i
> 0 && i
< nStringsCnt
)
1111 while (i
> 0 && nTypeArray
[i
] <= 0)
1115 if (nTypeArray
[i
] > 0)
1117 res
= nTypeArray
[i
];
1123 sal_uInt16
ImpSvNumberformatScan::NextKeyword(sal_uInt16 i
) const
1126 if (i
< nStringsCnt
-1)
1129 while (i
< nStringsCnt
-1 && nTypeArray
[i
] <= 0)
1133 if (nTypeArray
[i
] > 0)
1135 res
= nTypeArray
[i
];
1141 short ImpSvNumberformatScan::PreviousType( sal_uInt16 i
) const
1143 if ( i
> 0 && i
< nStringsCnt
)
1149 while ( i
> 0 && nTypeArray
[i
] == NF_SYMBOLTYPE_EMPTY
);
1150 return nTypeArray
[i
];
1155 sal_Unicode
ImpSvNumberformatScan::PreviousChar(sal_uInt16 i
) const
1157 sal_Unicode res
= ' ';
1158 if (i
> 0 && i
< nStringsCnt
)
1162 ( nTypeArray
[i
] == NF_SYMBOLTYPE_EMPTY
||
1163 nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
||
1164 nTypeArray
[i
] == NF_SYMBOLTYPE_STAR
||
1165 nTypeArray
[i
] == NF_SYMBOLTYPE_BLANK
))
1169 if (sStrArray
[i
].getLength() > 0)
1171 res
= sStrArray
[i
][sStrArray
[i
].getLength()-1];
1177 sal_Unicode
ImpSvNumberformatScan::NextChar(sal_uInt16 i
) const
1179 sal_Unicode res
= ' ';
1180 if (i
< nStringsCnt
-1)
1183 while (i
< nStringsCnt
-1 &&
1184 ( nTypeArray
[i
] == NF_SYMBOLTYPE_EMPTY
||
1185 nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
||
1186 nTypeArray
[i
] == NF_SYMBOLTYPE_STAR
||
1187 nTypeArray
[i
] == NF_SYMBOLTYPE_BLANK
))
1191 if (sStrArray
[i
].getLength() > 0)
1193 res
= sStrArray
[i
][0];
1199 bool ImpSvNumberformatScan::IsLastBlankBeforeFrac(sal_uInt16 i
) const
1202 if (i
< nStringsCnt
-1)
1206 while (i
< nStringsCnt
-1 && !bStop
)
1209 if ( nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
&&
1210 sStrArray
[i
][0] == '/')
1214 else if ( ( nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
&&
1215 sStrArray
[i
][0] == ' ') ||
1216 nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
) // integer/fraction delimiter can also be a string
1221 if (!bStop
) // no '/'{
1228 res
= false; // no '/' any more
1233 void ImpSvNumberformatScan::Reset()
1236 nResultStringsCnt
= 0;
1237 eScannedType
= SvNumFormatType::UNDEFINED
;
1242 nDecPos
= sal_uInt16(-1);
1243 nExpPos
= sal_uInt16(-1);
1244 nBlankPos
= sal_uInt16(-1);
1250 nNatNumModifier
= 0;
1253 bool ImpSvNumberformatScan::Is100SecZero( sal_uInt16 i
, bool bHadDecSep
) const
1255 sal_uInt16 nIndexPre
= PreviousKeyword( i
);
1256 return (nIndexPre
== NF_KEY_S
|| nIndexPre
== NF_KEY_SS
) &&
1258 ( i
> 0 && nTypeArray
[i
-1] == NF_SYMBOLTYPE_STRING
));
1259 // SS"any"00 take "any" as a valid decimal separator
1262 sal_Int32
ImpSvNumberformatScan::ScanType()
1264 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
1268 SvNumFormatType eNewType
;
1269 bool bMatchBracket
= false;
1270 bool bHaveGeneral
= false; // if General/Standard encountered
1271 bool bIsTimeDetected
=false; // hour or second found in format
1272 bool bHaveMinute
= false;
1274 SkipStrings(i
, nPos
);
1275 while (i
< nStringsCnt
)
1277 if (nTypeArray
[i
] > 0)
1279 sal_uInt16 nIndexPre
;
1280 sal_uInt16 nIndexNex
;
1282 switch (nTypeArray
[i
])
1285 eNewType
= SvNumFormatType::SCIENTIFIC
;
1288 case NF_KEY_HH
: // HH
1289 bIsTimeDetected
= true;
1292 case NF_KEY_SS
: // SS
1294 bIsTimeDetected
= true;
1296 case NF_KEY_AMPM
: // AM,A,PM,P
1298 eNewType
= SvNumFormatType::TIME
;
1301 case NF_KEY_MM
: // MM
1302 case NF_KEY_MI
: // M minute detected in Finnish
1303 case NF_KEY_MMI
: // MM
1306 * preceded by time keyword H (ignoring separators)
1307 * followed by time keyword S (ignoring separators)
1308 * H or S was detected and this is the first M following
1309 * preceded by '[' amount bracket
1311 That are the Excel rules. BUT, we break it because certainly
1312 in something like {HH YYYY-MM-DD} the MM is NOT meant to be
1313 minute, so not if MM is between YY and DD or DD and YY.
1314 Actually not if any date specific keyword followed a time
1317 nIndexPre
= PreviousKeyword(i
);
1318 nIndexNex
= NextKeyword(i
);
1319 if (nIndexPre
== NF_KEY_H
|| // H
1320 nIndexPre
== NF_KEY_HH
|| // HH
1321 nIndexNex
== NF_KEY_S
|| // S
1322 nIndexNex
== NF_KEY_SS
|| // SS
1323 bIsTimeDetected
|| // tdf#101147
1324 PreviousChar(i
) == '[' ) // [M
1326 eNewType
= SvNumFormatType::TIME
;
1327 if ( nTypeArray
[i
] == NF_KEY_M
|| nTypeArray
[i
] == NF_KEY_MM
)
1329 nTypeArray
[i
] -= 2; // 6 -> 4, 7 -> 5
1331 bIsTimeDetected
= false; // next M should be month
1336 eNewType
= SvNumFormatType::DATE
;
1337 if ( nTypeArray
[i
] == NF_KEY_MI
|| nTypeArray
[i
] == NF_KEY_MMI
)
1338 { // follow resolution of tdf#33689 for Finnish
1339 nTypeArray
[i
] += 2; // 4 -> 6, 5 -> 7
1343 case NF_KEY_MMM
: // MMM
1344 case NF_KEY_MMMM
: // MMMM
1345 case NF_KEY_MMMMM
: // MMMMM
1347 case NF_KEY_QQ
: // QQ
1349 case NF_KEY_DD
: // DD
1350 case NF_KEY_DDD
: // DDD
1351 case NF_KEY_DDDD
: // DDDD
1352 case NF_KEY_YY
: // YY
1353 case NF_KEY_YYYY
: // YYYY
1354 case NF_KEY_NN
: // NN
1355 case NF_KEY_NNN
: // NNN
1356 case NF_KEY_NNNN
: // NNNN
1357 case NF_KEY_WW
: // WW
1358 case NF_KEY_AAA
: // AAA
1359 case NF_KEY_AAAA
: // AAAA
1360 case NF_KEY_EC
: // E
1361 case NF_KEY_EEC
: // EE
1362 case NF_KEY_G
: // G
1363 case NF_KEY_GG
: // GG
1364 case NF_KEY_GGG
: // GGG
1365 case NF_KEY_R
: // R
1366 case NF_KEY_RR
: // RR
1367 eNewType
= SvNumFormatType::DATE
;
1368 bIsTimeDetected
= false;
1370 case NF_KEY_CCC
: // CCC
1371 eNewType
= SvNumFormatType::CURRENCY
;
1373 case NF_KEY_GENERAL
: // Standard
1374 eNewType
= SvNumFormatType::NUMBER
;
1375 bHaveGeneral
= true;
1378 eNewType
= SvNumFormatType::UNDEFINED
;
1383 { // control character
1384 switch ( sStrArray
[i
][0] )
1388 eNewType
= SvNumFormatType::NUMBER
;
1391 if ( eScannedType
& SvNumFormatType::TIME
)
1393 if ( Is100SecZero( i
, bDecSep
) )
1395 bDecSep
= true; // subsequent 0's
1396 eNewType
= SvNumFormatType::TIME
;
1400 return nPos
; // Error
1405 eNewType
= SvNumFormatType::NUMBER
;
1409 eNewType
= SvNumFormatType::PERCENT
;
1412 eNewType
= SvNumFormatType::FRACTION
;
1415 if ( i
< nStringsCnt
-1 &&
1416 nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
1417 sStrArray
[i
+1][0] == '$' )
1419 eNewType
= SvNumFormatType::CURRENCY
;
1420 bMatchBracket
= true;
1422 else if ( i
< nStringsCnt
-1 &&
1423 nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
1424 sStrArray
[i
+1][0] == '~' )
1426 eNewType
= SvNumFormatType::DATE
;
1427 bMatchBracket
= true;
1431 sal_uInt16 nIndexNex
= NextKeyword(i
);
1432 if (nIndexNex
== NF_KEY_H
|| // H
1433 nIndexNex
== NF_KEY_HH
|| // HH
1434 nIndexNex
== NF_KEY_M
|| // M
1435 nIndexNex
== NF_KEY_MM
|| // MM
1436 nIndexNex
== NF_KEY_S
|| // S
1437 nIndexNex
== NF_KEY_SS
) // SS
1438 eNewType
= SvNumFormatType::TIME
;
1441 return nPos
; // Error
1446 eNewType
= SvNumFormatType::TEXT
;
1449 if (pLoc
->getTime100SecSep() == sStrArray
[i
])
1451 bDecSep
= true; // for SS,0
1453 eNewType
= SvNumFormatType::UNDEFINED
;
1457 if (eScannedType
== SvNumFormatType::UNDEFINED
)
1459 eScannedType
= eNewType
;
1461 else if (eScannedType
== SvNumFormatType::TEXT
|| eNewType
== SvNumFormatType::TEXT
)
1463 eScannedType
= SvNumFormatType::TEXT
; // Text always remains text
1465 else if (eNewType
== SvNumFormatType::UNDEFINED
)
1468 else if (eScannedType
!= eNewType
)
1470 switch (eScannedType
)
1472 case SvNumFormatType::DATE
:
1475 case SvNumFormatType::TIME
:
1476 eScannedType
= SvNumFormatType::DATETIME
;
1478 case SvNumFormatType::FRACTION
: // DD/MM
1483 eScannedType
= SvNumFormatType::UNDEFINED
;
1485 else if ( sStrArray
[i
] != pFormatter
->GetDateSep() )
1491 case SvNumFormatType::TIME
:
1494 case SvNumFormatType::DATE
:
1495 eScannedType
= SvNumFormatType::DATETIME
;
1497 case SvNumFormatType::FRACTION
: // MM/SS
1502 eScannedType
= SvNumFormatType::UNDEFINED
;
1504 else if (pLoc
->getTimeSep() != sStrArray
[i
])
1511 case SvNumFormatType::DATETIME
:
1514 case SvNumFormatType::TIME
:
1515 case SvNumFormatType::DATE
:
1517 case SvNumFormatType::FRACTION
: // DD/MM
1522 eScannedType
= SvNumFormatType::UNDEFINED
;
1524 else if ( pFormatter
->GetDateSep() != sStrArray
[i
] &&
1525 pLoc
->getTimeSep() != sStrArray
[i
] )
1531 case SvNumFormatType::PERCENT
:
1534 case SvNumFormatType::NUMBER
: // Only number to percent
1540 case SvNumFormatType::SCIENTIFIC
:
1543 case SvNumFormatType::NUMBER
: // Only number to E
1549 case SvNumFormatType::NUMBER
:
1552 case SvNumFormatType::SCIENTIFIC
:
1553 case SvNumFormatType::PERCENT
:
1554 case SvNumFormatType::FRACTION
:
1555 case SvNumFormatType::CURRENCY
:
1556 eScannedType
= eNewType
;
1561 eScannedType
= SvNumFormatType::UNDEFINED
;
1569 case SvNumFormatType::FRACTION
:
1572 case SvNumFormatType::NUMBER
: // Only number to fraction
1582 nPos
= nPos
+ sStrArray
[i
].getLength(); // Position of correction
1584 if ( bMatchBracket
)
1585 { // no type detection inside of matching brackets if [$...], [~...]
1586 while ( bMatchBracket
&& i
< nStringsCnt
)
1588 if ( nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
1589 && sStrArray
[i
][0] == ']' )
1591 bMatchBracket
= false;
1595 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1597 nPos
= nPos
+ sStrArray
[i
].getLength();
1600 if ( bMatchBracket
)
1602 return nPos
; // missing closing bracket at end of code
1605 SkipStrings(i
, nPos
);
1608 if ((eScannedType
== SvNumFormatType::NUMBER
||
1609 eScannedType
== SvNumFormatType::UNDEFINED
) &&
1610 nCurrPos
>= 0 && !bHaveGeneral
)
1612 eScannedType
= SvNumFormatType::CURRENCY
; // old "automatic" currency
1614 if (eScannedType
== SvNumFormatType::UNDEFINED
)
1616 eScannedType
= SvNumFormatType::DEFINED
;
1618 return 0; // All is fine
1621 bool ImpSvNumberformatScan::InsertSymbol( sal_uInt16
& nPos
, svt::NfSymbolType eType
, const OUString
& rStr
)
1623 if (nStringsCnt
>= NF_MAX_FORMAT_SYMBOLS
|| nPos
> nStringsCnt
)
1627 if (nPos
> 0 && nTypeArray
[nPos
-1] == NF_SYMBOLTYPE_EMPTY
)
1629 --nPos
; // reuse position
1633 if (static_cast<size_t>(nStringsCnt
+ 1) >= NF_MAX_FORMAT_SYMBOLS
)
1638 for (size_t i
= nStringsCnt
; i
> nPos
; --i
)
1640 nTypeArray
[i
] = nTypeArray
[i
-1];
1641 sStrArray
[i
] = sStrArray
[i
-1];
1644 ++nResultStringsCnt
;
1645 nTypeArray
[nPos
] = static_cast<short>(eType
);
1646 sStrArray
[nPos
] = rStr
;
1650 int ImpSvNumberformatScan::FinalScanGetCalendar( sal_Int32
& nPos
, sal_uInt16
& i
,
1651 sal_uInt16
& rResultStringsCnt
)
1653 if ( i
< nStringsCnt
-1 &&
1654 sStrArray
[i
][0] == '[' &&
1655 nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
1656 sStrArray
[i
+1][0] == '~' )
1659 nPos
= nPos
+ sStrArray
[i
].getLength(); // [
1660 nTypeArray
[i
] = NF_SYMBOLTYPE_CALDEL
;
1661 nPos
= nPos
+ sStrArray
[++i
].getLength(); // ~
1662 sStrArray
[i
-1] += sStrArray
[i
]; // [~
1663 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
1664 rResultStringsCnt
--;
1665 if ( ++i
>= nStringsCnt
)
1669 nPos
= nPos
+ sStrArray
[i
].getLength(); // calendarID
1670 OUString
& rStr
= sStrArray
[i
];
1671 nTypeArray
[i
] = NF_SYMBOLTYPE_CALENDAR
; // convert
1673 while ( i
< nStringsCnt
&& sStrArray
[i
][0] != ']' )
1675 nPos
= nPos
+ sStrArray
[i
].getLength();
1676 rStr
+= sStrArray
[i
];
1677 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
1678 rResultStringsCnt
--;
1681 if ( rStr
.getLength() && i
< nStringsCnt
&&
1682 sStrArray
[i
][0] == ']' )
1684 nTypeArray
[i
] = NF_SYMBOLTYPE_CALDEL
;
1685 nPos
= nPos
+ sStrArray
[i
].getLength();
1697 bool ImpSvNumberformatScan::IsDateFragment( size_t nPos1
, size_t nPos2
) const
1699 return nPos2
- nPos1
== 2 && nTypeArray
[nPos1
+1] == NF_SYMBOLTYPE_DATESEP
;
1702 void ImpSvNumberformatScan::SwapArrayElements( size_t nPos1
, size_t nPos2
)
1704 std::swap( nTypeArray
[nPos1
], nTypeArray
[nPos2
]);
1705 std::swap( sStrArray
[nPos1
], sStrArray
[nPos2
]);
1708 sal_Int32
ImpSvNumberformatScan::FinalScan( OUString
& rString
)
1710 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
1712 // save values for convert mode
1713 OUString sOldDecSep
= pFormatter
->GetNumDecimalSep();
1714 OUString sOldThousandSep
= pFormatter
->GetNumThousandSep();
1715 OUString sOldDateSep
= pFormatter
->GetDateSep();
1716 OUString sOldTimeSep
= pLoc
->getTimeSep();
1717 OUString sOldTime100SecSep
= pLoc
->getTime100SecSep();
1718 OUString sOldCurSymbol
= GetCurSymbol();
1719 OUString sOldCurString
= GetCurString();
1720 sal_Unicode cOldKeyH
= sKeyword
[NF_KEY_H
][0];
1721 sal_Unicode cOldKeyMI
= sKeyword
[NF_KEY_MI
][0];
1722 sal_Unicode cOldKeyS
= sKeyword
[NF_KEY_S
][0];
1723 DateOrder eOldDateOrder
= pLoc
->getDateOrder();
1724 sal_uInt16 nDayPos
, nMonthPos
, nYearPos
;
1725 nDayPos
= nMonthPos
= nYearPos
= SAL_MAX_UINT16
;
1727 // If the group separator is a No-Break Space (French) continue with a
1728 // normal space instead so queries on space work correctly.
1729 // The same for Narrow No-Break Space just in case some locale uses it.
1730 // The format string is adjusted to allow both.
1731 // For output of the format code string the LocaleData characters are used.
1732 if ( (sOldThousandSep
[0] == cNoBreakSpace
|| sOldThousandSep
[0] == cNarrowNoBreakSpace
) &&
1733 sOldThousandSep
.getLength() == 1 )
1735 sOldThousandSep
= " ";
1737 bool bNewDateOrder
= false;
1738 // change locale data et al
1741 pFormatter
->ChangeIntl(eNewLnge
);
1742 //! pointer may have changed
1743 pLoc
= pFormatter
->GetLocaleData();
1744 //! init new keywords
1746 // Adapt date order to target locale, but Excel does not handle date
1747 // particle re-ordering for the target locale when loading documents,
1748 // though it does exchange separators, tdf#113889
1749 bNewDateOrder
= (mbConvertDateOrder
&& eOldDateOrder
!= pLoc
->getDateOrder());
1751 const CharClass
* pChrCls
= pFormatter
->GetCharClass();
1753 sal_Int32 nPos
= 0; // error correction position
1754 sal_uInt16 i
= 0; // symbol loop counter
1755 sal_uInt16 nCounter
= 0; // counts digits
1756 nResultStringsCnt
= nStringsCnt
; // counts remaining symbols
1757 bDecSep
= false; // reset in case already used in TypeCheck
1758 bool bThaiT
= false; // Thai T NatNum modifier present
1759 bool bTimePart
= false;
1760 bool bDenomin
= false; // Set when reading end of denominator
1762 switch (eScannedType
)
1764 case SvNumFormatType::TEXT
:
1765 case SvNumFormatType::DEFINED
:
1766 while (i
< nStringsCnt
)
1768 switch (nTypeArray
[i
])
1770 case NF_SYMBOLTYPE_BLANK
:
1771 case NF_SYMBOLTYPE_STAR
:
1773 case NF_KEY_GENERAL
: // #77026# "General" is the same as "@"
1776 if ( nTypeArray
[i
] != NF_SYMBOLTYPE_DEL
||
1777 sStrArray
[i
][0] != '@' )
1779 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1783 nPos
= nPos
+ sStrArray
[i
].getLength();
1788 case SvNumFormatType::NUMBER
:
1789 case SvNumFormatType::PERCENT
:
1790 case SvNumFormatType::CURRENCY
:
1791 case SvNumFormatType::SCIENTIFIC
:
1792 case SvNumFormatType::FRACTION
:
1793 while (i
< nStringsCnt
)
1795 // TODO: rechecking eScannedType is unnecessary.
1796 // This switch-case is for eScannedType == SvNumFormatType::FRACTION anyway
1797 if (eScannedType
== SvNumFormatType::FRACTION
&& // special case
1798 nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
&& // # ### #/#
1799 StringEqualsChar( sOldThousandSep
, ' ' ) && // e.g. France or Sweden
1800 StringEqualsChar( sStrArray
[i
], ' ' ) &&
1802 IsLastBlankBeforeFrac(i
) )
1804 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
; // del->string
1805 } // No thousands marker
1807 if (nTypeArray
[i
] == NF_SYMBOLTYPE_BLANK
||
1808 nTypeArray
[i
] == NF_SYMBOLTYPE_STAR
||
1809 nTypeArray
[i
] == NF_KEY_CCC
|| // CCC
1810 nTypeArray
[i
] == NF_KEY_GENERAL
) // Standard
1812 if (nTypeArray
[i
] == NF_KEY_GENERAL
)
1814 nThousand
= FLAG_STANDARD_IN_FORMAT
;
1817 sStrArray
[i
] = sNameStandardFormat
;
1820 nPos
= nPos
+ sStrArray
[i
].getLength();
1823 else if (nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
|| // No Strings or
1824 nTypeArray
[i
] > 0) // Keywords
1826 if (eScannedType
== SvNumFormatType::SCIENTIFIC
&&
1827 nTypeArray
[i
] == NF_KEY_E
) // E+
1837 nCntPost
= nCounter
;
1844 nTypeArray
[i
] = NF_SYMBOLTYPE_EXP
;
1846 else if (eScannedType
== SvNumFormatType::FRACTION
&&
1847 (sStrArray
[i
][0] == ' ' || ( nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
&& (sStrArray
[i
][0] < '0' || sStrArray
[i
][0] > '9') ) ) )
1849 if (!bBlank
&& !bFrac
) // Not double or after a /
1851 if (bDecSep
&& nCounter
> 0) // Decimal places
1853 return nPos
; // Error
1855 if (sStrArray
[i
][0] == ' ' || nCounter
> 0 ) // treat string as integer/fraction delimiter only if there is integer
1861 nTypeArray
[i
] = NF_SYMBOLTYPE_FRACBLANK
;
1864 else if ( sStrArray
[i
][0] == ' ' )
1865 nTypeArray
[i
] = NF_SYMBOLTYPE_FRACBLANK
;
1866 else if ( bFrac
&& ( nCounter
> 0 ) )
1867 bDenomin
= true; // following elements are no more part of denominator
1869 else if (nTypeArray
[i
] == NF_KEY_THAI_T
)
1872 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]];
1874 else if (sStrArray
[i
][0] >= '0' &&
1875 sStrArray
[i
][0] <= '9' && !bDenomin
) // denominator was not yet found
1879 while(j
< nStringsCnt
&& sStrArray
[j
][0] >= '0' && sStrArray
[j
][0] <= '9')
1881 sDiv
+= sStrArray
[j
++];
1883 assert(j
> 0 && "if i is 0, first iteration through loop is guaranteed by surrounding if condition");
1884 if (OUString::number(sDiv
.toInt32()) == sDiv
)
1889 nTypeArray
[i
++] = NF_SYMBOLTYPE_FRAC_FDIV
;
1891 i
= j
- 1; // Stop the loop
1894 nCounter
= nCntPost
;
1900 // don't artificially increment nCntPre for forced denominator
1901 if ( ( eScannedType
!= SvNumFormatType::FRACTION
) && (!nCntPre
) )
1906 bDenomin
= true; // next content should be treated as outside denominator
1911 if ( bFrac
&& ( nCounter
> 0 ) )
1912 bDenomin
= true; // next content should be treated as outside denominator
1913 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1915 nPos
= nPos
+ sStrArray
[i
].getLength();
1918 else if (nTypeArray
[i
] == NF_SYMBOLTYPE_DEL
)
1920 sal_Unicode cHere
= sStrArray
[i
][0];
1921 sal_Unicode cSaved
= cHere
;
1922 // Handle not pre-known separators in switch.
1923 sal_Unicode cSimplified
;
1924 if (StringEqualsChar( pFormatter
->GetNumThousandSep(), cHere
))
1928 else if (StringEqualsChar( pFormatter
->GetNumDecimalSep(), cHere
))
1934 cSimplified
= cHere
;
1937 OUString
& rStr
= sStrArray
[i
];
1939 switch ( cSimplified
)
1944 if (nThousand
> 0) // #... #
1946 return nPos
; // Error
1950 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
1951 nPos
= nPos
+ rStr
.getLength();
1954 while (i
< nStringsCnt
&&
1955 (sStrArray
[i
][0] == '#' ||
1956 sStrArray
[i
][0] == '0' ||
1957 sStrArray
[i
][0] == '?'))
1959 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
1960 nPos
= nPos
+ sStrArray
[i
].getLength();
1965 else // after denominator, treat any character as text
1967 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
1968 nPos
= nPos
+ sStrArray
[i
].getLength();
1972 if ( bDecSep
&& nDecPos
+1 == i
&&
1973 nTypeArray
[nDecPos
] == NF_SYMBOLTYPE_DECSEP
)
1976 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
1977 nPos
= nPos
+ rStr
.getLength();
1980 while (i
< nStringsCnt
&&
1981 (sStrArray
[i
][0] == '-') )
1983 // If more than two dashes are present in
1984 // currency formats the last dash will be
1985 // interpreted literally as a minus sign.
1986 // Has to be this ugly. Period.
1987 if ( eScannedType
== SvNumFormatType::CURRENCY
1988 && rStr
.getLength() >= 2 &&
1989 (i
== nStringsCnt
-1 ||
1990 sStrArray
[i
+1][0] != '-') )
1994 rStr
+= sStrArray
[i
];
1995 nPos
= nPos
+ sStrArray
[i
].getLength();
1996 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
1997 nResultStringsCnt
--;
2004 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2005 nPos
= nPos
+ sStrArray
[i
].getLength();
2013 if ( StringEqualsChar( sOldThousandSep
, cSaved
) )
2015 // previous char with skip empty
2016 sal_Unicode cPre
= PreviousChar(i
);
2018 if (bExp
|| bBlank
|| bFrac
)
2020 // after E, / or ' '
2021 if ( !StringEqualsChar( sOldThousandSep
, ' ' ) )
2023 nPos
= nPos
+ sStrArray
[i
].getLength();
2024 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2025 nResultStringsCnt
--;
2030 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2031 if ( bFrac
&& (nCounter
> 0) )
2032 bDenomin
= true; // end of denominator
2035 else if (i
> 0 && i
< nStringsCnt
-1 &&
2036 (cPre
== '#' || cPre
== '0' || cPre
== '?') &&
2037 ((cNext
= NextChar(i
)) == '#' || cNext
== '0' || cNext
== '?')) // #,#
2039 nPos
= nPos
+ sStrArray
[i
].getLength();
2040 if (!bThousand
) // only once
2044 // Eat it, will be reinserted at proper grouping positions further down.
2045 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2046 nResultStringsCnt
--;
2049 else if (i
> 0 && (cPre
== '#' || cPre
== '0' || cPre
== '?')
2050 && PreviousType(i
) == NF_SYMBOLTYPE_DIGIT
2051 && nThousand
< FLAG_STANDARD_IN_FORMAT
)
2053 if ( StringEqualsChar( sOldThousandSep
, ' ' ) )
2055 // strange, those French..
2057 // set a hard No-Break Space or ConvertMode
2058 const OUString
& rSepF
= pFormatter
->GetNumThousandSep();
2059 while ( i
< nStringsCnt
&&
2060 sStrArray
[i
] == sOldThousandSep
&&
2061 StringEqualsChar( sOldThousandSep
, NextChar(i
) ) )
2062 { // last was a space or another space
2063 // is following => separator
2064 nPos
= nPos
+ sStrArray
[i
].getLength();
2069 nTypeArray
[i
] = NF_SYMBOLTYPE_THSEP
;
2074 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2075 nResultStringsCnt
--;
2080 if ( i
< nStringsCnt
-1 &&
2081 sStrArray
[i
] == sOldThousandSep
)
2083 // something following last space
2084 // => space if currency contained,
2086 nPos
= nPos
+ sStrArray
[i
].getLength();
2087 if ( (nPos
<= nCurrPos
&&
2088 nCurrPos
< nPos
+ sStrArray
[i
+1].getLength()) ||
2089 nTypeArray
[i
+1] == NF_KEY_CCC
||
2090 (i
< nStringsCnt
-2 &&
2091 sStrArray
[i
+1][0] == '[' &&
2092 sStrArray
[i
+2][0] == '$') )
2094 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2101 nTypeArray
[i
] = NF_SYMBOLTYPE_THSEP
;
2106 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2107 nResultStringsCnt
--;
2119 nTypeArray
[i
] = NF_SYMBOLTYPE_THSEP
;
2120 nPos
= nPos
+ sStrArray
[i
].getLength();
2121 sStrArray
[i
] = pFormatter
->GetNumThousandSep();
2124 while (i
< nStringsCnt
&& sStrArray
[i
] == sOldThousandSep
);
2129 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2130 nPos
= nPos
+ rStr
.getLength();
2132 while ( i
< nStringsCnt
&& sStrArray
[i
] == sOldThousandSep
)
2134 rStr
+= sStrArray
[i
];
2135 nPos
= nPos
+ sStrArray
[i
].getLength();
2136 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2137 nResultStringsCnt
--;
2142 else if ( StringEqualsChar( sOldDecSep
, cSaved
) )
2144 if (bBlank
|| bFrac
) // . behind / or ' '
2146 return nPos
; // error
2148 else if (bExp
) // behind E
2150 nPos
= nPos
+ sStrArray
[i
].getLength();
2151 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2152 nResultStringsCnt
--;
2155 else if (bDecSep
) // any .
2157 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2158 nPos
= nPos
+ rStr
.getLength();
2160 while ( i
< nStringsCnt
&& sStrArray
[i
] == sOldDecSep
)
2162 rStr
+= sStrArray
[i
];
2163 nPos
= nPos
+ sStrArray
[i
].getLength();
2164 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2165 nResultStringsCnt
--;
2171 nPos
= nPos
+ sStrArray
[i
].getLength();
2172 nTypeArray
[i
] = NF_SYMBOLTYPE_DECSEP
;
2173 sStrArray
[i
] = pFormatter
->GetNumDecimalSep();
2181 } // of else = DecSep
2182 else // . without meaning
2184 if (cSaved
== ' ' &&
2185 eScannedType
== SvNumFormatType::FRACTION
&&
2186 StringEqualsChar( sStrArray
[i
], ' ' ) )
2188 if (!bBlank
&& !bFrac
) // no dups
2190 if (bDecSep
&& nCounter
> 0) // dec.
2192 return nPos
; // error
2199 if ( bFrac
&& (nCounter
> 0) )
2200 bDenomin
= true; // next content is not part of denominator
2201 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2202 nPos
= nPos
+ sStrArray
[i
].getLength();
2206 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2207 if ( bFrac
&& (nCounter
> 0) )
2208 bDenomin
= true; // next content is not part of denominator
2209 nPos
= nPos
+ rStr
.getLength();
2211 while (i
< nStringsCnt
&& StringEqualsChar( sStrArray
[i
], cSaved
) )
2213 rStr
+= sStrArray
[i
];
2214 nPos
= nPos
+ sStrArray
[i
].getLength();
2215 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2216 nResultStringsCnt
--;
2223 if (eScannedType
== SvNumFormatType::FRACTION
)
2226 (nTypeArray
[i
-1] != NF_SYMBOLTYPE_DIGIT
&&
2227 nTypeArray
[i
-1] != NF_SYMBOLTYPE_EMPTY
) )
2229 return nPos
? nPos
: 1; // /? not allowed
2231 else if (!bFrac
|| (bDecSep
&& nCounter
> 0))
2234 nCntPost
= nCounter
;
2236 nTypeArray
[i
] = NF_SYMBOLTYPE_FRAC
;
2237 nPos
= nPos
+ sStrArray
[i
].getLength();
2240 else // / double or in , in the denominator
2242 return nPos
; // Error
2247 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2248 nPos
= nPos
+ sStrArray
[i
].getLength();
2253 if ( eScannedType
== SvNumFormatType::CURRENCY
&&
2254 i
< nStringsCnt
-1 &&
2255 nTypeArray
[i
+1] == NF_SYMBOLTYPE_STRING
&&
2256 sStrArray
[i
+1][0] == '$' )
2259 nPos
= nPos
+ sStrArray
[i
].getLength(); // [
2260 nTypeArray
[i
] = NF_SYMBOLTYPE_CURRDEL
;
2261 nPos
= nPos
+ sStrArray
[++i
].getLength(); // $
2262 sStrArray
[i
-1] += sStrArray
[i
]; // [$
2263 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2264 nResultStringsCnt
--;
2265 if ( ++i
>= nStringsCnt
)
2267 return nPos
; // Error
2269 nPos
= nPos
+ sStrArray
[i
].getLength(); // DM
2270 OUString
* pStr
= &sStrArray
[i
];
2271 nTypeArray
[i
] = NF_SYMBOLTYPE_CURRENCY
; // convert
2272 bool bHadDash
= false;
2274 while ( i
< nStringsCnt
&& sStrArray
[i
][0] != ']' )
2276 nPos
= nPos
+ sStrArray
[i
].getLength();
2279 *pStr
+= sStrArray
[i
];
2280 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2281 nResultStringsCnt
--;
2285 if ( sStrArray
[i
][0] == '-' )
2288 pStr
= &sStrArray
[i
];
2289 nTypeArray
[i
] = NF_SYMBOLTYPE_CURREXT
;
2293 *pStr
+= sStrArray
[i
];
2294 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2295 nResultStringsCnt
--;
2300 if ( rStr
.getLength() && i
< nStringsCnt
&& sStrArray
[i
][0] == ']' )
2302 nTypeArray
[i
] = NF_SYMBOLTYPE_CURRDEL
;
2303 nPos
= nPos
+ sStrArray
[i
].getLength();
2308 return nPos
; // Error
2313 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2314 nPos
= nPos
+ sStrArray
[i
].getLength();
2318 default: // Other Dels
2319 if (eScannedType
== SvNumFormatType::PERCENT
&& cHere
== '%')
2321 nTypeArray
[i
] = NF_SYMBOLTYPE_PERCENT
;
2325 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2327 nPos
= nPos
+ sStrArray
[i
].getLength();
2330 } // of switch (Del)
2334 SAL_WARN( "svl.numbers", "unknown NF_SYMBOLTYPE_..." );
2335 nPos
= nPos
+ sStrArray
[i
].getLength();
2339 if (eScannedType
== SvNumFormatType::FRACTION
)
2347 nCntPost
= nCounter
;
2362 nCntPost
= nCounter
;
2369 if (bThousand
) // Expansion of grouping separators
2376 nMaxPos
= nBlankPos
;
2380 nMaxPos
= 0; // no grouping
2383 else if (bDecSep
) // decimal separator present
2387 else if (bExp
) // 'E' exponent present
2395 // Insert separators at proper positions.
2396 sal_Int32 nCount
= 0;
2397 utl::DigitGroupingIterator
aGrouping( pLoc
->getDigitGrouping());
2398 size_t nFirstDigitSymbol
= nMaxPos
;
2399 size_t nFirstGroupingSymbol
= nMaxPos
;
2403 if (nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
)
2405 nFirstDigitSymbol
= i
;
2406 nCount
= nCount
+ sStrArray
[i
].getLength(); // MSC converts += to int and then warns, so ...
2407 // Insert separator only if not leftmost symbol.
2408 if (i
> 0 && nCount
>= aGrouping
.getPos())
2410 DBG_ASSERT( sStrArray
[i
].getLength() == 1,
2411 "ImpSvNumberformatScan::FinalScan: combined digits in group separator insertion");
2412 if (!InsertSymbol( i
, NF_SYMBOLTYPE_THSEP
, pFormatter
->GetNumThousandSep()))
2414 // nPos isn't correct here, but signals error
2417 // i may have been decremented by 1
2418 nFirstDigitSymbol
= i
+ 1;
2419 nFirstGroupingSymbol
= i
;
2420 aGrouping
.advance();
2424 // Generated something like "string",000; remove separator again.
2425 if (nFirstGroupingSymbol
< nFirstDigitSymbol
)
2427 nTypeArray
[nFirstGroupingSymbol
] = NF_SYMBOLTYPE_EMPTY
;
2428 nResultStringsCnt
--;
2431 // Combine digits into groups to save memory (Info will be copied
2432 // later, taking only non-empty symbols).
2433 for (i
= 0; i
< nStringsCnt
; ++i
)
2435 if (nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
)
2437 OUString
& rStr
= sStrArray
[i
];
2438 while (++i
< nStringsCnt
&& nTypeArray
[i
] == NF_SYMBOLTYPE_DIGIT
)
2440 rStr
+= sStrArray
[i
];
2441 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2442 nResultStringsCnt
--;
2446 break; // of SvNumFormatType::NUMBER
2447 case SvNumFormatType::DATE
:
2448 while (i
< nStringsCnt
)
2450 switch (nTypeArray
[i
])
2452 case NF_SYMBOLTYPE_BLANK
:
2453 case NF_SYMBOLTYPE_STAR
:
2454 case NF_SYMBOLTYPE_STRING
:
2455 nPos
= nPos
+ sStrArray
[i
].getLength();
2458 case NF_SYMBOLTYPE_DEL
:
2460 if (sStrArray
[i
] == sOldDateSep
)
2462 nTypeArray
[i
] = NF_SYMBOLTYPE_DATESEP
;
2463 nPos
= nPos
+ sStrArray
[i
].getLength();
2466 sStrArray
[i
] = pFormatter
->GetDateSep();
2470 else if ( (nCalRet
= FinalScanGetCalendar( nPos
, i
, nResultStringsCnt
)) != 0 )
2474 return nPos
; // error
2479 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2480 nPos
= nPos
+ sStrArray
[i
].getLength();
2484 case NF_KEY_THAI_T
:
2488 case NF_KEY_MM
: // MM
2489 case NF_KEY_MMM
: // MMM
2490 case NF_KEY_MMMM
: // MMMM
2491 case NF_KEY_MMMMM
: // MMMMM
2493 case NF_KEY_QQ
: // QQ
2495 case NF_KEY_DD
: // DD
2496 case NF_KEY_DDD
: // DDD
2497 case NF_KEY_DDDD
: // DDDD
2498 case NF_KEY_YY
: // YY
2499 case NF_KEY_YYYY
: // YYYY
2500 case NF_KEY_NN
: // NN
2501 case NF_KEY_NNN
: // NNN
2502 case NF_KEY_NNNN
: // NNNN
2503 case NF_KEY_WW
: // WW
2504 case NF_KEY_AAA
: // AAA
2505 case NF_KEY_AAAA
: // AAAA
2506 case NF_KEY_EC
: // E
2507 case NF_KEY_EEC
: // EE
2508 case NF_KEY_G
: // G
2509 case NF_KEY_GG
: // GG
2510 case NF_KEY_GGG
: // GGG
2511 case NF_KEY_R
: // R
2512 case NF_KEY_RR
: // RR
2513 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2514 nPos
= nPos
+ sStrArray
[i
].getLength();
2517 // For simple numeric date formats record date order and
2519 switch (nTypeArray
[i
])
2523 if (nMonthPos
== SAL_MAX_UINT16
)
2526 bNewDateOrder
= false;
2530 if (nDayPos
== SAL_MAX_UINT16
)
2533 bNewDateOrder
= false;
2537 if (nYearPos
== SAL_MAX_UINT16
)
2540 bNewDateOrder
= false;
2548 default: // Other keywords
2549 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2550 nPos
= nPos
+ sStrArray
[i
].getLength();
2555 break; // of SvNumFormatType::DATE
2556 case SvNumFormatType::TIME
:
2557 while (i
< nStringsCnt
)
2561 switch (nTypeArray
[i
])
2563 case NF_SYMBOLTYPE_BLANK
:
2564 case NF_SYMBOLTYPE_STAR
:
2565 nPos
= nPos
+ sStrArray
[i
].getLength();
2568 case NF_SYMBOLTYPE_DEL
:
2569 switch( sStrArray
[i
][0] )
2572 if ( Is100SecZero( i
, bDecSep
) )
2575 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
2576 OUString
& rStr
= sStrArray
[i
];
2579 while (i
< nStringsCnt
&&
2580 sStrArray
[i
][0] == '0')
2582 rStr
+= sStrArray
[i
];
2583 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2584 nResultStringsCnt
--;
2588 nPos
+= rStr
.getLength();
2599 if (bThousand
) // Double
2603 bThousand
= true; // Empty for Time
2604 cChar
= pChrCls
->uppercase(OUString(NextChar(i
)))[0];
2605 if ( cChar
== cOldKeyH
)
2609 else if ( cChar
== cOldKeyMI
)
2613 else if ( cChar
== cOldKeyS
)
2621 nPos
= nPos
+ sStrArray
[i
].getLength();
2625 if (!bThousand
) // No preceding [
2629 nPos
= nPos
+ sStrArray
[i
].getLength();
2633 nPos
= nPos
+ sStrArray
[i
].getLength();
2634 if ( sStrArray
[i
] == sOldTimeSep
)
2636 nTypeArray
[i
] = NF_SYMBOLTYPE_TIMESEP
;
2639 sStrArray
[i
] = pLoc
->getTimeSep();
2642 else if ( sStrArray
[i
] == sOldTime100SecSep
)
2645 nTypeArray
[i
] = NF_SYMBOLTYPE_TIME100SECSEP
;
2648 sStrArray
[i
] = pLoc
->getTime100SecSep();
2653 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2659 case NF_SYMBOLTYPE_STRING
:
2660 nPos
= nPos
+ sStrArray
[i
].getLength();
2663 case NF_KEY_AMPM
: // AM/PM
2664 case NF_KEY_AP
: // A/P
2665 bExp
= true; // Abuse for A/P
2666 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2667 nPos
= nPos
+ sStrArray
[i
].getLength();
2670 case NF_KEY_THAI_T
:
2673 case NF_KEY_MI
: // M
2674 case NF_KEY_MMI
: // MM
2676 case NF_KEY_HH
: // HH
2678 case NF_KEY_SS
: // SS
2679 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2680 nPos
= nPos
+ sStrArray
[i
].getLength();
2683 default: // Other keywords
2684 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2685 nPos
= nPos
+ sStrArray
[i
].getLength();
2690 nCntPost
= nCounter
; // Zero counter
2693 nCntExp
= 1; // Remembers AM/PM
2695 break; // of SvNumFormatType::TIME
2696 case SvNumFormatType::DATETIME
:
2697 while (i
< nStringsCnt
)
2700 switch (nTypeArray
[i
])
2702 case NF_SYMBOLTYPE_BLANK
:
2703 case NF_SYMBOLTYPE_STAR
:
2704 case NF_SYMBOLTYPE_STRING
:
2705 nPos
= nPos
+ sStrArray
[i
].getLength();
2708 case NF_SYMBOLTYPE_DEL
:
2709 if ( (nCalRet
= FinalScanGetCalendar( nPos
, i
, nResultStringsCnt
)) != 0 )
2713 return nPos
; // Error
2718 switch( sStrArray
[i
][0] )
2721 if (bTimePart
&& Is100SecZero(i
, bDecSep
) && nCounter
< MaxCntPost
)
2724 nTypeArray
[i
] = NF_SYMBOLTYPE_DIGIT
;
2725 OUString
& rStr
= sStrArray
[i
];
2728 while (i
< nStringsCnt
&&
2729 sStrArray
[i
][0] == '0' && nCounter
< MaxCntPost
)
2731 rStr
+= sStrArray
[i
];
2732 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
2733 nResultStringsCnt
--;
2737 nPos
+= rStr
.getLength();
2748 nPos
= nPos
+ sStrArray
[i
].getLength();
2751 if ( sStrArray
[i
] == sOldTimeSep
)
2753 nTypeArray
[i
] = NF_SYMBOLTYPE_TIMESEP
;
2756 sStrArray
[i
] = pLoc
->getTimeSep();
2759 else if ( sStrArray
[i
] == sOldTime100SecSep
)
2762 nTypeArray
[i
] = NF_SYMBOLTYPE_TIME100SECSEP
;
2765 sStrArray
[i
] = pLoc
->getTime100SecSep();
2770 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2775 if ( sStrArray
[i
] == sOldDateSep
)
2777 nTypeArray
[i
] = NF_SYMBOLTYPE_DATESEP
;
2779 sStrArray
[i
] = pFormatter
->GetDateSep();
2783 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2791 case NF_KEY_AMPM
: // AM/PM
2792 case NF_KEY_AP
: // A/P
2794 bExp
= true; // Abuse for A/P
2795 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2796 nPos
= nPos
+ sStrArray
[i
].getLength();
2799 case NF_KEY_MI
: // M
2800 case NF_KEY_MMI
: // MM
2802 case NF_KEY_HH
: // HH
2804 case NF_KEY_SS
: // SS
2806 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2807 nPos
= nPos
+ sStrArray
[i
].getLength();
2811 case NF_KEY_MM
: // MM
2812 case NF_KEY_MMM
: // MMM
2813 case NF_KEY_MMMM
: // MMMM
2814 case NF_KEY_MMMMM
: // MMMMM
2816 case NF_KEY_QQ
: // QQ
2818 case NF_KEY_DD
: // DD
2819 case NF_KEY_DDD
: // DDD
2820 case NF_KEY_DDDD
: // DDDD
2821 case NF_KEY_YY
: // YY
2822 case NF_KEY_YYYY
: // YYYY
2823 case NF_KEY_NN
: // NN
2824 case NF_KEY_NNN
: // NNN
2825 case NF_KEY_NNNN
: // NNNN
2826 case NF_KEY_WW
: // WW
2827 case NF_KEY_AAA
: // AAA
2828 case NF_KEY_AAAA
: // AAAA
2829 case NF_KEY_EC
: // E
2830 case NF_KEY_EEC
: // EE
2831 case NF_KEY_G
: // G
2832 case NF_KEY_GG
: // GG
2833 case NF_KEY_GGG
: // GGG
2834 case NF_KEY_R
: // R
2835 case NF_KEY_RR
: // RR
2837 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]]; // tTtT -> TTTT
2838 nPos
= nPos
+ sStrArray
[i
].getLength();
2841 // For simple numeric date formats record date order and
2843 switch (nTypeArray
[i
])
2847 if (nMonthPos
== SAL_MAX_UINT16
)
2850 bNewDateOrder
= false;
2854 if (nDayPos
== SAL_MAX_UINT16
)
2857 bNewDateOrder
= false;
2861 if (nYearPos
== SAL_MAX_UINT16
)
2864 bNewDateOrder
= false;
2872 case NF_KEY_THAI_T
:
2874 sStrArray
[i
] = sKeyword
[nTypeArray
[i
]];
2875 nPos
= nPos
+ sStrArray
[i
].getLength();
2878 default: // Other keywords
2879 nTypeArray
[i
] = NF_SYMBOLTYPE_STRING
;
2880 nPos
= nPos
+ sStrArray
[i
].getLength();
2885 nCntPost
= nCounter
; // decimals (100th seconds)
2888 nCntExp
= 1; // Remembers AM/PM
2890 break; // of SvNumFormatType::DATETIME
2894 if (eScannedType
== SvNumFormatType::SCIENTIFIC
&&
2895 (nCntPre
+ nCntPost
== 0 || nCntExp
== 0))
2899 else if (eScannedType
== SvNumFormatType::FRACTION
&& (nCntExp
> 8 || nCntExp
== 0))
2903 if (bThaiT
&& !GetNatNumModifier())
2905 SetNatNumModifier(1);
2909 if (bNewDateOrder
&& sOldDateSep
== "-")
2911 // Keep ISO formats Y-M-D, Y-M and M-D
2912 if (IsDateFragment( nYearPos
, nMonthPos
))
2914 nTypeArray
[nYearPos
+1] = NF_SYMBOLTYPE_STRING
;
2915 sStrArray
[nYearPos
+1] = sOldDateSep
;
2916 bNewDateOrder
= false;
2918 if (IsDateFragment( nMonthPos
, nDayPos
))
2920 nTypeArray
[nMonthPos
+1] = NF_SYMBOLTYPE_STRING
;
2921 sStrArray
[nMonthPos
+1] = sOldDateSep
;
2922 bNewDateOrder
= false;
2927 // Rearrange date order to the target locale if the original order
2928 // includes date separators and is adjacent.
2929 /* TODO: for incomplete dates trailing separators need to be
2930 * handled according to the locale's usage, e.g. en-US M/D should
2931 * be converted to de-DE D.M. and vice versa. As is, it's
2932 * M/D -> D.M and D.M. -> M/D/ where specifically the latter looks
2933 * odd. Check accepted date patterns and append/remove? */
2934 switch (eOldDateOrder
)
2936 case DateOrder::DMY
:
2937 switch (pLoc
->getDateOrder())
2939 case DateOrder::MDY
:
2940 // Convert only if the actual format is not of YDM
2941 // order (which would be a completely unusual order
2942 // anyway, but..), e.g. YYYY.DD.MM not to
2944 if (IsDateFragment( nDayPos
, nMonthPos
) && !IsDateFragment( nYearPos
, nDayPos
))
2945 SwapArrayElements( nDayPos
, nMonthPos
);
2947 case DateOrder::YMD
:
2948 if (nYearPos
!= SAL_MAX_UINT16
)
2950 if (IsDateFragment( nDayPos
, nMonthPos
) && IsDateFragment( nMonthPos
, nYearPos
))
2951 SwapArrayElements( nDayPos
, nYearPos
);
2955 if (IsDateFragment( nDayPos
, nMonthPos
))
2956 SwapArrayElements( nDayPos
, nMonthPos
);
2963 case DateOrder::MDY
:
2964 switch (pLoc
->getDateOrder())
2966 case DateOrder::DMY
:
2967 // Convert only if the actual format is not of YMD
2968 // order, e.g. YYYY/MM/DD not to YYYY.DD.MM
2969 /* TODO: convert such to DD.MM.YYYY instead? */
2970 if (IsDateFragment( nMonthPos
, nDayPos
) && !IsDateFragment( nYearPos
, nMonthPos
))
2971 SwapArrayElements( nMonthPos
, nDayPos
);
2973 case DateOrder::YMD
:
2974 if (nYearPos
!= SAL_MAX_UINT16
)
2976 if (IsDateFragment( nMonthPos
, nDayPos
) && IsDateFragment( nDayPos
, nYearPos
))
2978 SwapArrayElements( nYearPos
, nMonthPos
); // YDM
2979 SwapArrayElements( nYearPos
, nDayPos
); // YMD
2987 case DateOrder::YMD
:
2988 switch (pLoc
->getDateOrder())
2990 case DateOrder::DMY
:
2991 if (nYearPos
!= SAL_MAX_UINT16
)
2993 if (IsDateFragment( nYearPos
, nMonthPos
) && IsDateFragment( nMonthPos
, nDayPos
))
2994 SwapArrayElements( nYearPos
, nDayPos
);
2998 if (IsDateFragment( nMonthPos
, nDayPos
))
2999 SwapArrayElements( nMonthPos
, nDayPos
);
3002 case DateOrder::MDY
:
3003 if (nYearPos
!= SAL_MAX_UINT16
)
3005 if (IsDateFragment( nYearPos
, nMonthPos
) && IsDateFragment( nMonthPos
, nDayPos
))
3007 SwapArrayElements( nYearPos
, nDayPos
); // DMY
3008 SwapArrayElements( nYearPos
, nMonthPos
); // MDY
3020 // strings containing keywords of the target locale must be quoted, so
3021 // the user sees the difference and is able to edit the format string
3022 for ( i
=0; i
< nStringsCnt
; i
++ )
3024 if ( nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
&&
3025 sStrArray
[i
][0] != '\"' )
3027 if ( bConvertSystemToSystem
&& eScannedType
== SvNumFormatType::CURRENCY
)
3029 // don't stringize automatic currency, will be converted
3030 if ( sStrArray
[i
] == sOldCurSymbol
)
3034 // DM might be split into D and M
3035 if ( sStrArray
[i
].getLength() < sOldCurSymbol
.getLength() &&
3036 pChrCls
->uppercase( sStrArray
[i
], 0, 1 )[0] ==
3039 OUString
aTmp( sStrArray
[i
] );
3040 sal_uInt16 j
= i
+ 1;
3041 while ( aTmp
.getLength() < sOldCurSymbol
.getLength() &&
3043 nTypeArray
[j
] == NF_SYMBOLTYPE_STRING
)
3045 aTmp
+= sStrArray
[j
++];
3047 if ( pChrCls
->uppercase( aTmp
) == sOldCurString
)
3049 sStrArray
[i
++] = aTmp
;
3052 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
3053 nResultStringsCnt
--;
3060 OUString
& rStr
= sStrArray
[i
];
3061 sal_Int32 nLen
= rStr
.getLength();
3062 for ( sal_Int32 j
= 0; j
< nLen
; j
++ )
3064 bool bFoundEnglish
= false;
3065 if ( (j
== 0 || rStr
[j
- 1] != '\\') && GetKeyWord( rStr
, j
, bFoundEnglish
) )
3067 rStr
= "\"" + rStr
+ "\"";
3074 // Concatenate strings, remove quotes for output, and rebuild the format string
3077 while (i
< nStringsCnt
)
3079 sal_Int32 nStringPos
;
3080 sal_Int32 nArrPos
= 0;
3081 sal_uInt16 iPos
= i
;
3082 switch ( nTypeArray
[i
] )
3084 case NF_SYMBOLTYPE_STRING
:
3085 case NF_SYMBOLTYPE_FRACBLANK
:
3086 nStringPos
= rString
.getLength();
3089 if (sStrArray
[i
].getLength() == 2 &&
3090 sStrArray
[i
][0] == '\\')
3092 // Unescape some simple forms of symbols even in the UI
3093 // visible string to prevent duplicates that differ
3094 // only in notation, originating from import.
3095 // e.g. YYYY-MM-DD and YYYY\-MM\-DD are identical,
3096 // but 0\ 000 0 and 0 000 0 in a French locale are not.
3098 sal_Unicode c
= sStrArray
[i
][1];
3104 rString
+= OUStringLiteral1(c
);
3109 if (!(eScannedType
& SvNumFormatType::DATE
) &&
3110 (StringEqualsChar( pFormatter
->GetNumThousandSep(), c
) ||
3111 StringEqualsChar( pFormatter
->GetNumDecimalSep(), c
) ||
3113 (StringEqualsChar( pFormatter
->GetNumThousandSep(), cNoBreakSpace
) ||
3114 StringEqualsChar( pFormatter
->GetNumThousandSep(), cNarrowNoBreakSpace
)))))
3116 rString
+= sStrArray
[i
];
3118 else if ((eScannedType
& SvNumFormatType::DATE
) &&
3119 StringEqualsChar( pFormatter
->GetDateSep(), c
))
3121 rString
+= sStrArray
[i
];
3123 else if ((eScannedType
& SvNumFormatType::TIME
) &&
3124 (StringEqualsChar( pLoc
->getTimeSep(), c
) ||
3125 StringEqualsChar( pLoc
->getTime100SecSep(), c
)))
3127 rString
+= sStrArray
[i
];
3129 else if (eScannedType
& SvNumFormatType::FRACTION
)
3131 rString
+= sStrArray
[i
];
3135 rString
+= OUStringLiteral1(c
);
3139 rString
+= sStrArray
[i
];
3144 rString
+= sStrArray
[i
];
3146 if ( RemoveQuotes( sStrArray
[i
] ) > 0 )
3148 // update currency up to quoted string
3149 if ( eScannedType
== SvNumFormatType::CURRENCY
)
3151 // dM -> DM or DM -> $ in old automatic
3152 // currency formats, oh my ..., why did we ever introduce them?
3153 OUString
aTmp( pChrCls
->uppercase( sStrArray
[iPos
], nArrPos
,
3154 sStrArray
[iPos
].getLength()-nArrPos
) );
3155 sal_Int32 nCPos
= aTmp
.indexOf( sOldCurString
);
3158 const OUString
& rCur
= bConvertMode
&& bConvertSystemToSystem
?
3159 GetCurSymbol() : sOldCurSymbol
;
3160 sStrArray
[iPos
] = sStrArray
[iPos
].replaceAt( nArrPos
+ nCPos
,
3161 sOldCurString
.getLength(),
3163 rString
= rString
.replaceAt( nStringPos
+ nCPos
,
3164 sOldCurString
.getLength(),
3167 nStringPos
= rString
.getLength();
3170 nArrPos
= sStrArray
[iPos
].getLength();
3174 nArrPos
= sStrArray
[iPos
].getLength() + sStrArray
[i
].getLength();
3180 sStrArray
[iPos
] += sStrArray
[i
];
3181 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
3182 nResultStringsCnt
--;
3186 while ( i
< nStringsCnt
&& nTypeArray
[i
] == NF_SYMBOLTYPE_STRING
);
3188 if ( i
< nStringsCnt
)
3190 i
--; // enter switch on next symbol again
3192 if ( eScannedType
== SvNumFormatType::CURRENCY
&& nStringPos
< rString
.getLength() )
3194 // same as above, since last RemoveQuotes
3195 OUString
aTmp( pChrCls
->uppercase( sStrArray
[iPos
], nArrPos
,
3196 sStrArray
[iPos
].getLength()-nArrPos
) );
3197 sal_Int32 nCPos
= aTmp
.indexOf( sOldCurString
);
3200 const OUString
& rCur
= bConvertMode
&& bConvertSystemToSystem
?
3201 GetCurSymbol() : sOldCurSymbol
;
3202 sStrArray
[iPos
] = sStrArray
[iPos
].replaceAt( nArrPos
+ nCPos
,
3203 sOldCurString
.getLength(),
3205 rString
= rString
.replaceAt( nStringPos
+ nCPos
,
3206 sOldCurString
.getLength(), rCur
);
3210 case NF_SYMBOLTYPE_CURRENCY
:
3211 rString
+= sStrArray
[i
];
3212 RemoveQuotes( sStrArray
[i
] );
3215 if (bThaiT
&& GetNatNumModifier() == 1)
3217 // Remove T from format code, will be replaced with a [NatNum1] prefix.
3218 nTypeArray
[i
] = NF_SYMBOLTYPE_EMPTY
;
3219 nResultStringsCnt
--;
3223 rString
+= sStrArray
[i
];
3226 case NF_SYMBOLTYPE_EMPTY
:
3230 rString
+= sStrArray
[i
];
3237 sal_Int32
ImpSvNumberformatScan::RemoveQuotes( OUString
& rStr
)
3239 if ( rStr
.getLength() > 1 )
3241 sal_Unicode c
= rStr
[0];
3242 sal_Int32 n
= rStr
.getLength() - 1;
3243 if ( c
== '"' && rStr
[n
] == '"' )
3245 rStr
= rStr
.copy( 1, n
-1);
3248 else if ( c
== '\\' )
3250 rStr
= rStr
.copy(1);
3257 sal_Int32
ImpSvNumberformatScan::ScanFormat( OUString
& rString
)
3259 sal_Int32 res
= Symbol_Division(rString
); // Lexical analysis
3262 res
= ScanType(); // Recognizing the Format type
3266 res
= FinalScan( rString
); // Type dependent final analysis
3268 return res
; // res = control position; res = 0 => Format ok
3271 void ImpSvNumberformatScan::CopyInfo(ImpSvNumberformatInfo
* pInfo
, sal_uInt16 nCnt
)
3276 while (i
< nCnt
&& j
< NF_MAX_FORMAT_SYMBOLS
)
3278 if (nTypeArray
[j
] != NF_SYMBOLTYPE_EMPTY
)
3280 pInfo
->sStrArray
[i
] = sStrArray
[j
];
3281 pInfo
->nTypeArray
[i
] = nTypeArray
[j
];
3286 pInfo
->eScannedType
= eScannedType
;
3287 pInfo
->bThousand
= bThousand
;
3288 pInfo
->nThousand
= nThousand
;
3289 pInfo
->nCntPre
= nCntPre
;
3290 pInfo
->nCntPost
= nCntPost
;
3291 pInfo
->nCntExp
= nCntExp
;
3294 void ImpSvNumberformatScan::ReplaceBooleanEquivalent( OUString
& rString
)
3297 /* TODO: compare case insensitive? Or rather leave as is and case not
3298 * matching indicates user supplied on purpose? Written to file / generated
3299 * was always uppercase. */
3300 if (rString
== sBooleanEquivalent1
|| rString
== sBooleanEquivalent2
)
3301 rString
= GetKeywords()[NF_KEY_BOOLEAN
];
3304 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */