Coverage Report

Created: 2026-05-16 09:25

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