Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svl/source/numbers/zformat.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
#include <string_view>
21
22
#include <o3tl/sprintf.hxx>
23
#include <o3tl/string_view.hxx>
24
#include <o3tl/numeric.hxx>
25
#include <comphelper/string.hxx>
26
#include <sal/log.hxx>
27
#include <tools/debug.hxx>
28
#include <tools/long.hxx>
29
#include <i18nlangtag/mslangid.hxx>
30
#include <rtl/math.hxx>
31
#include <unotools/charclass.hxx>
32
#include <unotools/calendarwrapper.hxx>
33
#include <unotools/nativenumberwrapper.hxx>
34
#include <com/sun/star/i18n/CalendarFieldIndex.hpp>
35
#include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
36
#include <com/sun/star/i18n/CalendarDisplayCode.hpp>
37
#include <com/sun/star/i18n/AmPmValue.hpp>
38
#include <com/sun/star/i18n/NativeNumberMode.hpp>
39
#include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>
40
41
#include <svl/zformat.hxx>
42
#include "zforscan.hxx"
43
44
#include "zforfind.hxx"
45
#include <svl/zforlist.hxx>
46
#include <unotools/digitgroupingiterator.hxx>
47
#include <svl/nfsymbol.hxx>
48
49
#include <cmath>
50
#include <array>
51
52
using namespace svt;
53
54
namespace {
55
56
constexpr OUString GREGORIAN = u"gregorian"_ustr;
57
58
const sal_uInt16 UPPER_PRECISION = 300; // entirely arbitrary...
59
const double EXP_LOWER_BOUND = 1.0E-4; // prefer scientific notation below this value.
60
const double EXP_ABS_UPPER_BOUND = 1.0E15;  // use exponential notation above that absolute value.
61
                                            // Back in time was E16 that lead
62
                                            // to display rounding errors, see
63
                                            // also sal/rtl/math.cxx
64
                                            // doubleToString()
65
66
constexpr sal_Int32 kTimeSignificantRound = 7;  // Round (date+)time at 7 decimals
67
                                                // (+5 of 86400 == 12 significant digits).
68
69
const sal_Unicode cBlankDigit = 0x2007;     // tdf#158890 use figure space for '?'
70
} // namespace
71
72
const double D_MAX_U_INT32 = double(0xffffffff);      // 4294967295.0
73
constexpr double D_MAX_INTEGER = (sal_uInt64(1) << 53) - 1;
74
75
const double D_MAX_D_BY_100  = 1.7E306;
76
const double D_MIN_M_BY_1000 = 2.3E-305;
77
78
const sal_uInt8 cCharWidths[ 128-32 ] = {
79
    1,1,1,2,2,3,2,1,1,1,1,2,1,1,1,1,
80
    2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,
81
    3,2,2,2,2,2,2,3,2,1,2,2,2,3,3,3,
82
    2,3,2,2,2,2,2,3,2,2,2,1,1,1,2,2,
83
    1,2,2,2,2,2,1,2,2,1,1,2,1,3,2,2,
84
    2,2,1,2,1,2,2,2,2,2,2,1,1,1,2,1
85
};
86
87
// static
88
sal_Int32 SvNumberformat::InsertBlanks( OUStringBuffer& r, sal_Int32 nPos, sal_Unicode c )
89
21.9k
{
90
21.9k
    if( c >= 32 )
91
21.9k
    {
92
21.9k
        int n = 2;   // Default for chars > 128 (HACK!)
93
21.9k
        if( c <= 127 )
94
20.1k
        {
95
20.1k
            n = static_cast<int>(cCharWidths[ c - 32 ]);
96
20.1k
        }
97
56.4k
        while( n-- )
98
34.5k
        {
99
34.5k
            r.insert( nPos++, ' ');
100
34.5k
        }
101
21.9k
    }
102
21.9k
    return nPos;
103
21.9k
}
104
105
static tools::Long GetPrecExp( double fAbsVal )
106
64.1k
{
107
64.1k
    DBG_ASSERT( fAbsVal > 0.0, "GetPrecExp: fAbsVal <= 0.0" );
108
64.1k
    if ( fAbsVal < 1e-7 || fAbsVal > 1e7 )
109
8.63k
    {
110
        // Shear: whether it's faster or not, falls in between 1e6 and 1e7
111
8.63k
        return static_cast<tools::Long>(floor( log10( fAbsVal ) )) + 1;
112
8.63k
    }
113
55.4k
    else
114
55.4k
    {
115
55.4k
        tools::Long nPrecExp = 1;
116
62.5k
        while( fAbsVal < 1 )
117
7.04k
        {
118
7.04k
            fAbsVal *= 10;
119
7.04k
            nPrecExp--;
120
7.04k
        }
121
140k
        while( fAbsVal >= 10 )
122
84.8k
        {
123
84.8k
            fAbsVal /= 10;
124
84.8k
            nPrecExp++;
125
84.8k
        }
126
55.4k
        return nPrecExp;
127
55.4k
    }
128
64.1k
}
129
130
/**
131
 * SvNumberformatInfo
132
 * */
133
134
void ImpSvNumberformatInfo::Copy( const ImpSvNumberformatInfo& rNumFor, sal_uInt16 nCnt )
135
4.60k
{
136
10.3k
    for (sal_uInt16 i = 0; i < nCnt; ++i)
137
5.76k
    {
138
5.76k
        sStrArray[i]  = rNumFor.sStrArray[i];
139
5.76k
        nTypeArray[i] = rNumFor.nTypeArray[i];
140
5.76k
    }
141
4.60k
    eScannedType = rNumFor.eScannedType;
142
4.60k
    bThousand    = rNumFor.bThousand;
143
4.60k
    nThousand    = rNumFor.nThousand;
144
4.60k
    nCntPre      = rNumFor.nCntPre;
145
4.60k
    nCntPost     = rNumFor.nCntPost;
146
4.60k
    nCntExp      = rNumFor.nCntExp;
147
4.60k
}
148
149
const std::map<LanguageType, std::array<sal_uInt8, 4>> tblDBNumToNatNum
150
    = { { primary(LANGUAGE_CHINESE),    { 4, 5, 3, 0 } },
151
        { primary(LANGUAGE_JAPANESE),   { 4, 5, 3, 0 } },
152
        { primary(LANGUAGE_KOREAN),     { 4, 5, 6, 10 } } };
153
154
// static
155
sal_uInt8 SvNumberNatNum::MapDBNumToNatNum( sal_uInt8 nDBNum, LanguageType eLang, bool bDate )
156
0
{
157
0
    sal_uInt8 nNatNum = 0;
158
0
    eLang = MsLangId::getRealLanguage( eLang );  // resolve SYSTEM etc.
159
0
    eLang = primary(eLang);    // 10 bit primary language
160
0
    if ( bDate )
161
0
    {
162
0
        if ( nDBNum == 4 && eLang == primary(LANGUAGE_KOREAN) )
163
0
        {
164
0
            nNatNum = 10;
165
0
        }
166
0
        else if ( nDBNum <= 3 )
167
0
        {
168
0
            nNatNum = nDBNum;   // known to be good for: zh,ja,ko / 1,2,3
169
0
        }
170
0
    }
171
0
    else
172
0
    {
173
0
        if (1 <= nDBNum && nDBNum <= 4)
174
0
        {
175
0
            auto const it = tblDBNumToNatNum.find(eLang);
176
0
            if (it != tblDBNumToNatNum.end())
177
0
                nNatNum = it->second[nDBNum - 1];
178
179
0
        }
180
0
    }
181
0
    return nNatNum;
182
0
}
183
184
const std::map<LanguageType, std::array<sal_uInt8, 9>> tblNatNumToDBNum
185
    = { { primary(LANGUAGE_CHINESE),    { 1, 0, 0, 1, 2, 3, 0, 0, 0 } },
186
        { primary(LANGUAGE_JAPANESE),   { 1, 2, 3, 1, 2, 3, 1, 2, 0 } },
187
        { primary(LANGUAGE_KOREAN),     { 1, 2, 3, 1, 2, 3, 1, 2, 4 } } };
188
189
// static
190
sal_uInt8 SvNumberNatNum::MapNatNumToDBNum( sal_uInt8 nNatNum, LanguageType eLang, bool bDate )
191
0
{
192
0
    sal_uInt8 nDBNum = 0;
193
0
    eLang = MsLangId::getRealLanguage( eLang );  // resolve SYSTEM etc.
194
0
    eLang = primary(eLang);    // 10 bit primary language
195
0
    if ( bDate )
196
0
    {
197
0
        if ( nNatNum == 10 && eLang == primary(LANGUAGE_KOREAN) )
198
0
        {
199
0
            nDBNum = 4;
200
0
        }
201
0
        else if ( nNatNum <= 3 )
202
0
        {
203
0
            nDBNum = nNatNum;   // known to be good for: zh,ja,ko / 1,2,3
204
0
        }
205
0
    }
206
0
    else
207
0
    {
208
0
        if (1 <= nNatNum && nNatNum <= 9)
209
0
        {
210
0
            auto const it = tblNatNumToDBNum.find(eLang);
211
0
            if (it != tblNatNumToDBNum.end())
212
0
                nDBNum = it->second[nNatNum - 1];
213
0
        }
214
0
    }
215
0
    return nDBNum;
216
0
}
217
218
/**
219
 * SvNumFor
220
 */
221
222
ImpSvNumFor::ImpSvNumFor()
223
100M
{
224
100M
    nStringsCnt = 0;
225
100M
    aI.eScannedType = SvNumFormatType::UNDEFINED;
226
100M
    aI.bThousand = false;
227
100M
    aI.nThousand = 0;
228
100M
    aI.nCntPre = 0;
229
100M
    aI.nCntPost = 0;
230
100M
    aI.nCntExp = 0;
231
100M
    pColor = nullptr;
232
100M
}
233
234
ImpSvNumFor::~ImpSvNumFor()
235
100M
{
236
100M
}
237
238
void ImpSvNumFor::Enlarge(sal_uInt16 nCnt)
239
29.0M
{
240
29.0M
    if ( nStringsCnt != nCnt )
241
29.0M
    {
242
29.0M
        nStringsCnt = nCnt;
243
29.0M
        aI.nTypeArray.resize(nCnt);
244
29.0M
        aI.sStrArray.resize(nCnt);
245
29.0M
    }
246
29.0M
}
247
248
void ImpSvNumFor::Copy( const ImpSvNumFor& rNumFor, const ImpSvNumberformatScan* pSc )
249
4.60k
{
250
4.60k
    Enlarge( rNumFor.nStringsCnt );
251
4.60k
    aI.Copy( rNumFor.aI, nStringsCnt );
252
4.60k
    sColorName = rNumFor.sColorName;
253
4.60k
    if ( pSc )
254
0
    {
255
0
        pColor = pSc->GetColor( sColorName );   // #121103# don't copy pointer between documents
256
0
    }
257
4.60k
    else
258
4.60k
    {
259
4.60k
        pColor = rNumFor.pColor;
260
4.60k
    }
261
4.60k
    aNatNum = rNumFor.aNatNum;
262
4.60k
}
263
264
bool ImpSvNumFor::HasNewCurrency() const
265
0
{
266
0
    for ( sal_uInt16 j=0; j<nStringsCnt; j++ )
267
0
    {
268
0
        if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY )
269
0
        {
270
0
            return true;
271
0
        }
272
0
    }
273
0
    return false;
274
0
}
275
276
bool ImpSvNumFor::GetNewCurrencySymbol( OUString& rSymbol,
277
                                        OUString& rExtension ) const
278
535k
{
279
1.05M
    for ( sal_uInt16 j=0; j<nStringsCnt; j++ )
280
540k
    {
281
540k
        if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY )
282
16.2k
        {
283
16.2k
            rSymbol = aI.sStrArray[j];
284
16.2k
            if ( j < nStringsCnt-1 && aI.nTypeArray[j+1] == NF_SYMBOLTYPE_CURREXT )
285
10.6k
            {
286
10.6k
                rExtension = aI.sStrArray[j+1];
287
10.6k
            }
288
5.59k
            else
289
5.59k
            {
290
5.59k
                rExtension.clear();
291
5.59k
            }
292
16.2k
            return true;
293
16.2k
        }
294
540k
    }
295
    //! No Erase at rSymbol, rExtension
296
519k
    return false;
297
535k
}
298
299
/**
300
 * SvNumberformat
301
 */
302
303
namespace {
304
305
enum BracketFormatSymbolType
306
{
307
    BRACKET_SYMBOLTYPE_FORMAT   = -1,   // subformat string
308
    BRACKET_SYMBOLTYPE_COLOR    = -2,   // color
309
    BRACKET_SYMBOLTYPE_ERROR    = -3,   // error
310
    BRACKET_SYMBOLTYPE_DBNUM1   = -4,   // DoubleByteNumber, represent numbers
311
    BRACKET_SYMBOLTYPE_DBNUM2   = -5,   // using CJK characters, Excel compatible
312
    BRACKET_SYMBOLTYPE_DBNUM3   = -6,
313
    BRACKET_SYMBOLTYPE_DBNUM4   = -7,
314
    BRACKET_SYMBOLTYPE_DBNUM5   = -8,
315
    BRACKET_SYMBOLTYPE_DBNUM6   = -9,
316
    BRACKET_SYMBOLTYPE_DBNUM7   = -10,
317
    BRACKET_SYMBOLTYPE_DBNUM8   = -11,
318
    BRACKET_SYMBOLTYPE_DBNUM9   = -12,
319
    BRACKET_SYMBOLTYPE_LOCALE   = -13,
320
    BRACKET_SYMBOLTYPE_NATNUM0  = -14,  // Our NativeNumber support, ASCII
321
    BRACKET_SYMBOLTYPE_NATNUM1  = -15,  // Our NativeNumber support, represent
322
    BRACKET_SYMBOLTYPE_NATNUM2  = -16,  // numbers using CJK, CTL, ...
323
    BRACKET_SYMBOLTYPE_NATNUM3  = -17,
324
    BRACKET_SYMBOLTYPE_NATNUM4  = -18,
325
    BRACKET_SYMBOLTYPE_NATNUM5  = -19,
326
    BRACKET_SYMBOLTYPE_NATNUM6  = -20,
327
    BRACKET_SYMBOLTYPE_NATNUM7  = -21,
328
    BRACKET_SYMBOLTYPE_NATNUM8  = -22,
329
    BRACKET_SYMBOLTYPE_NATNUM9  = -23,
330
    BRACKET_SYMBOLTYPE_NATNUM10 = -24,
331
    BRACKET_SYMBOLTYPE_NATNUM11 = -25,
332
    BRACKET_SYMBOLTYPE_NATNUM12 = -26,
333
    BRACKET_SYMBOLTYPE_NATNUM13 = -27,
334
    BRACKET_SYMBOLTYPE_NATNUM14 = -28,
335
    BRACKET_SYMBOLTYPE_NATNUM15 = -29,
336
    BRACKET_SYMBOLTYPE_NATNUM16 = -30,
337
    BRACKET_SYMBOLTYPE_NATNUM17 = -31,
338
    BRACKET_SYMBOLTYPE_NATNUM18 = -32,
339
    BRACKET_SYMBOLTYPE_NATNUM19 = -33
340
};
341
342
}
343
344
void SvNumberformat::ImpCopyNumberformat( const SvNumberformat& rFormat )
345
1.15k
{
346
1.15k
    sFormatstring = rFormat.sFormatstring;
347
1.15k
    eType         = rFormat.eType;
348
1.15k
    maLocale      = rFormat.maLocale;
349
1.15k
    fLimit1       = rFormat.fLimit1;
350
1.15k
    fLimit2       = rFormat.fLimit2;
351
1.15k
    eOp1          = rFormat.eOp1;
352
1.15k
    eOp2          = rFormat.eOp2;
353
1.15k
    bStandard     = rFormat.bStandard;
354
1.15k
    bIsUsed       = rFormat.bIsUsed;
355
1.15k
    sComment      = rFormat.sComment;
356
1.15k
    bAdditionalBuiltin = rFormat.bAdditionalBuiltin;
357
358
    // #121103# when copying between documents, get color pointers from own scanner
359
1.15k
    ImpSvNumberformatScan* pColorSc = ( &rScan != &rFormat.rScan ) ? &rScan : nullptr;
360
361
5.76k
    for (sal_uInt16 i = 0; i < 4; i++)
362
4.60k
    {
363
4.60k
        NumFor[i].Copy(rFormat.NumFor[i], pColorSc);
364
4.60k
    }
365
1.15k
}
366
367
SvNumberformat::SvNumberformat( SvNumberformat const & rFormat )
368
576
    : rScan(rFormat.rScan)
369
576
{
370
576
    ImpCopyNumberformat( rFormat );
371
576
}
372
373
SvNumberformat::SvNumberformat( SvNumberformat const & rFormat, ImpSvNumberformatScan& rSc )
374
0
    : rScan(rSc)
375
0
{
376
0
    ImpCopyNumberformat( rFormat );
377
0
}
378
379
static bool lcl_SvNumberformat_IsBracketedPrefix( short nSymbolType )
380
66.5M
{
381
66.5M
    if ( nSymbolType > 0  )
382
10.6k
    {
383
10.6k
        return true; // conditions
384
10.6k
    }
385
66.5M
    switch ( nSymbolType )
386
66.5M
    {
387
3.43M
    case BRACKET_SYMBOLTYPE_COLOR :
388
3.43M
    case BRACKET_SYMBOLTYPE_DBNUM1 :
389
3.43M
    case BRACKET_SYMBOLTYPE_DBNUM2 :
390
3.43M
    case BRACKET_SYMBOLTYPE_DBNUM3 :
391
3.43M
    case BRACKET_SYMBOLTYPE_DBNUM4 :
392
3.43M
    case BRACKET_SYMBOLTYPE_DBNUM5 :
393
3.43M
    case BRACKET_SYMBOLTYPE_DBNUM6 :
394
3.43M
    case BRACKET_SYMBOLTYPE_DBNUM7 :
395
3.43M
    case BRACKET_SYMBOLTYPE_DBNUM8 :
396
3.43M
    case BRACKET_SYMBOLTYPE_DBNUM9 :
397
3.77M
    case BRACKET_SYMBOLTYPE_LOCALE :
398
3.77M
    case BRACKET_SYMBOLTYPE_NATNUM0 :
399
3.84M
    case BRACKET_SYMBOLTYPE_NATNUM1 :
400
3.85M
    case BRACKET_SYMBOLTYPE_NATNUM2 :
401
3.86M
    case BRACKET_SYMBOLTYPE_NATNUM3 :
402
3.86M
    case BRACKET_SYMBOLTYPE_NATNUM4 :
403
3.86M
    case BRACKET_SYMBOLTYPE_NATNUM5 :
404
3.86M
    case BRACKET_SYMBOLTYPE_NATNUM6 :
405
3.86M
    case BRACKET_SYMBOLTYPE_NATNUM7 :
406
3.86M
    case BRACKET_SYMBOLTYPE_NATNUM8 :
407
3.87M
    case BRACKET_SYMBOLTYPE_NATNUM9 :
408
3.87M
    case BRACKET_SYMBOLTYPE_NATNUM10 :
409
3.87M
    case BRACKET_SYMBOLTYPE_NATNUM11 :
410
8.18M
    case BRACKET_SYMBOLTYPE_NATNUM12 :
411
8.18M
    case BRACKET_SYMBOLTYPE_NATNUM13 :
412
8.18M
    case BRACKET_SYMBOLTYPE_NATNUM14 :
413
8.18M
    case BRACKET_SYMBOLTYPE_NATNUM15 :
414
8.18M
    case BRACKET_SYMBOLTYPE_NATNUM16 :
415
8.18M
    case BRACKET_SYMBOLTYPE_NATNUM17 :
416
8.18M
    case BRACKET_SYMBOLTYPE_NATNUM18 :
417
8.18M
    case BRACKET_SYMBOLTYPE_NATNUM19 :
418
8.18M
        return true;
419
66.5M
    }
420
58.3M
    return false;
421
66.5M
}
422
423
/** Import extended LCID from Excel
424
 */
425
OUString SvNumberformat::ImpObtainCalendarAndNumerals( OUStringBuffer& rString, sal_Int32 nPos,
426
                                                       LanguageType& nLang, const LocaleType& aTmpLocale )
427
120k
{
428
120k
    OUString sCalendar;
429
120k
    sal_uInt16 nNatNum = 0;
430
120k
    LanguageType nLocaleLang = MsLangId::getRealLanguage( maLocale.meLanguage );
431
120k
    LanguageType nTmpLocaleLang = MsLangId::getRealLanguage( aTmpLocale.meLanguage );
432
    /* NOTE: enhancement to allow other possible locale dependent
433
     * calendars and numerals. BUT only if our locale data allows it! For LCID
434
     * numerals and calendars see
435
     * http://office.microsoft.com/en-us/excel/HA010346351033.aspx
436
     * Calendar is inserted after
437
     * all prefixes have been consumed as it is actually a format modifier
438
     * and not a prefix.
439
     * Currently calendars are tied to the locale of the entire number
440
     * format, e.g. [~buddhist] in en_US doesn't work.
441
     * => Having different locales in sub formats does not work!
442
     * */
443
    /* TODO: calendars could be tied to a sub format's NatNum info
444
     * instead, or even better be available for any locale. Needs a
445
     * different implementation of GetCal() and locale data calendars.
446
     * */
447
120k
    switch ( aTmpLocale.mnCalendarType & 0x7F )
448
120k
    {
449
4.15k
        case 0x03 : // Gengou calendar
450
            // Only Japanese language support Gengou calendar.
451
            // It is an implicit "other" calendar where E, EE, R and RR
452
            // automatically switch to and YY and YYYY switch to Gregorian. Do
453
            // not add the "[~gengou]" modifier.
454
4.15k
            if ( nLocaleLang != LANGUAGE_JAPANESE )
455
3.39k
            {
456
3.39k
                nLang = maLocale.meLanguage = LANGUAGE_JAPANESE;
457
3.39k
            }
458
4.15k
            break;
459
5.03k
        case 0x05 : // Korean Dangi calendar
460
5.03k
            sCalendar = "[~dangi]";
461
            // Only Korean language support dangi calendar
462
5.03k
            if ( nLocaleLang != LANGUAGE_KOREAN )
463
4.72k
            {
464
4.72k
                nLang = maLocale.meLanguage = LANGUAGE_KOREAN;
465
4.72k
            }
466
5.03k
            break;
467
3.80k
        case 0x06 : // Hijri calendar
468
6.21k
        case 0x17 : // same?
469
6.21k
            sCalendar = "[~hijri]";
470
            // Only Arabic or Farsi languages support Hijri calendar
471
6.21k
            if ( ( primary( nLocaleLang ) != LANGUAGE_ARABIC_PRIMARY_ONLY )
472
5.94k
                  && nLocaleLang != LANGUAGE_FARSI )
473
5.94k
            {
474
5.94k
                if ( ( primary( nTmpLocaleLang ) == LANGUAGE_ARABIC_PRIMARY_ONLY )
475
2.97k
                      || nTmpLocaleLang == LANGUAGE_FARSI )
476
2.96k
                {
477
2.96k
                    nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
478
2.96k
                }
479
2.97k
                else
480
2.97k
                {
481
2.97k
                    nLang = maLocale.meLanguage = LANGUAGE_ARABIC_SAUDI_ARABIA;
482
2.97k
                }
483
5.94k
            }
484
6.21k
            break;
485
4.28k
        case 0x07 : // Buddhist calendar
486
4.28k
            sCalendar="[~buddhist]";
487
            // Only Thai or Lao languages support Buddhist calendar
488
4.28k
            if ( nLocaleLang != LANGUAGE_THAI && nLocaleLang != LANGUAGE_LAO )
489
3.46k
            {
490
3.46k
                if ( nTmpLocaleLang == LANGUAGE_THAI || nTmpLocaleLang == LANGUAGE_LAO )
491
194
                {
492
194
                    nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
493
194
                }
494
3.27k
                else
495
3.27k
                {
496
3.27k
                    nLang = maLocale.meLanguage = LANGUAGE_THAI;
497
3.27k
                }
498
3.46k
            }
499
4.28k
            break;
500
3.00k
        case 0x08 : // Hebrew calendar
501
3.00k
            sCalendar = "[~jewish]";
502
            // Many languages (but not all) support Jewish calendar
503
            // Unable to find any logic => keep same language
504
3.00k
            break;
505
1.18k
        case 0x0E : // unknown calendar
506
2.03k
        case 0x0F : // unknown calendar
507
4.42k
        case 0x10 : // Indian calendar (unsupported)
508
6.03k
        case 0x11 : // unknown calendar
509
7.45k
        case 0x12 : // unknown calendar
510
8.81k
        case 0x13 : // unknown calendar
511
98.0k
        default : // other calendars (see tdf#36038) are not handle by LibO
512
98.0k
            break;
513
120k
    }
514
    /** Reference language for each numeral ID */
515
120k
    static const LanguageType aNumeralIDtoLanguage []=
516
120k
    {
517
120k
        LANGUAGE_DONTKNOW,              // 0x00
518
120k
        LANGUAGE_ENGLISH_US,            // 0x01
519
120k
        LANGUAGE_ARABIC_SAUDI_ARABIA,   // 0x02 + all Arabic
520
120k
        LANGUAGE_FARSI,                 // 0x03
521
120k
        LANGUAGE_HINDI,                 // 0x04 + Devanagari
522
120k
        LANGUAGE_BENGALI,               // 0x05
523
120k
        LANGUAGE_PUNJABI,               // 0x06
524
120k
        LANGUAGE_GUJARATI,              // 0x07
525
120k
        LANGUAGE_ODIA,                  // 0x08
526
120k
        LANGUAGE_TAMIL,                 // 0x09
527
120k
        LANGUAGE_TELUGU,                // 0x0A
528
120k
        LANGUAGE_KANNADA,               // 0x0B
529
120k
        LANGUAGE_MALAYALAM,             // 0x0C
530
120k
        LANGUAGE_THAI,                  // 0x0D
531
120k
        LANGUAGE_LAO,                   // 0x0E
532
120k
        LANGUAGE_TIBETAN,               // 0x0F
533
120k
        LANGUAGE_BURMESE,               // 0x10
534
120k
        LANGUAGE_TIGRIGNA_ETHIOPIA,     // 0x11
535
120k
        LANGUAGE_KHMER,                 // 0x12
536
120k
        LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA, // 0x13
537
120k
        LANGUAGE_DONTKNOW,              // 0x14
538
120k
        LANGUAGE_DONTKNOW,              // 0x15
539
120k
        LANGUAGE_DONTKNOW,              // 0x16
540
120k
        LANGUAGE_DONTKNOW,              // 0x17
541
120k
        LANGUAGE_DONTKNOW,              // 0x18
542
120k
        LANGUAGE_DONTKNOW,              // 0x19
543
120k
        LANGUAGE_DONTKNOW,              // 0x1A
544
120k
        LANGUAGE_JAPANESE,              // 0x1B
545
120k
        LANGUAGE_JAPANESE,              // 0x1C
546
120k
        LANGUAGE_JAPANESE,              // 0x1D
547
120k
        LANGUAGE_CHINESE_SIMPLIFIED,    // 0x1E
548
120k
        LANGUAGE_CHINESE_SIMPLIFIED,    // 0x1F
549
120k
        LANGUAGE_CHINESE_SIMPLIFIED,    // 0x20
550
120k
        LANGUAGE_CHINESE_TRADITIONAL,   // 0x21
551
120k
        LANGUAGE_CHINESE_TRADITIONAL,   // 0x22
552
120k
        LANGUAGE_CHINESE_TRADITIONAL,   // 0x23
553
120k
        LANGUAGE_KOREAN,                // 0x24
554
120k
        LANGUAGE_KOREAN,                // 0x25
555
120k
        LANGUAGE_KOREAN,                // 0x26
556
120k
        LANGUAGE_KOREAN                 // 0x27
557
120k
    };
558
559
120k
    sal_uInt16 nNumeralID = aTmpLocale.mnNumeralShape & 0x7F;
560
120k
    LanguageType nReferenceLanguage = nNumeralID <= 0x27 ? aNumeralIDtoLanguage[nNumeralID] : LANGUAGE_DONTKNOW;
561
562
120k
    switch ( nNumeralID )
563
120k
    {
564
        // Regular cases: all languages with same primary mask use same numerals
565
1.12k
        case 0x03 : // Perso-Arabic (Farsi) numerals
566
2.35k
        case 0x05 : // Bengali numerals
567
4.10k
        case 0x06 : // Punjabi numerals
568
5.20k
        case 0x07 : // Gujarati numerals
569
6.25k
        case 0x08 : // Odia (Orya) numerals
570
7.48k
        case 0x09 : // Tamil numerals
571
8.14k
        case 0x0A : // Telugu numerals
572
8.57k
        case 0x0B : // Kannada numerals
573
9.11k
        case 0x0C : // Malayalam numerals
574
10.5k
        case 0x0D : // Thai numerals
575
11.1k
        case 0x0E : // Lao numerals
576
12.2k
        case 0x0F : // Tibetan numerals
577
12.9k
        case 0x10 : // Burmese (Myanmar) numerals
578
13.6k
        case 0x11 : // Tigrigna (Ethiopia) numerals
579
14.3k
        case 0x12 : // Khmer numerals
580
14.3k
            if ( primary( nLocaleLang ) != primary( nReferenceLanguage ) )
581
13.2k
            {
582
13.2k
                if ( primary( nTmpLocaleLang ) == primary( nReferenceLanguage ) )
583
289
                {
584
289
                    nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
585
289
                }
586
12.9k
                else
587
12.9k
                {
588
12.9k
                    nLang = maLocale.meLanguage = nReferenceLanguage;
589
12.9k
                }
590
13.2k
            }
591
14.3k
            break;
592
        // Special cases
593
2.91k
        case 0x04 : // Devanagari (Hindi) numerals
594
            // same numerals (Devanagari) for languages with different primary masks
595
2.91k
            if ( nLocaleLang != LANGUAGE_HINDI    && nLocaleLang != LANGUAGE_MARATHI
596
2.44k
            && primary( nLocaleLang ) != primary( LANGUAGE_NEPALI ) )
597
2.44k
            {
598
2.44k
                if ( nTmpLocaleLang == LANGUAGE_HINDI || nTmpLocaleLang == LANGUAGE_MARATHI
599
2.24k
                || primary( nTmpLocaleLang ) == primary( LANGUAGE_NEPALI ) )
600
374
                {
601
374
                    nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
602
374
                }
603
2.06k
                else
604
2.06k
                {
605
2.06k
                    nLang = maLocale.meLanguage = LANGUAGE_HINDI;
606
2.06k
                }
607
2.44k
            }
608
2.91k
            break;
609
1.86k
        case 0x13 : // Mongolian numerals
610
            // not all Mongolian languages use Mongolian numerals
611
1.86k
            if ( nLocaleLang != LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
612
1.20k
              && nLocaleLang != LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
613
1.20k
              && nLocaleLang != LANGUAGE_MONGOLIAN_MONGOLIAN_LSO )
614
1.20k
            {
615
1.20k
                if ( nTmpLocaleLang == LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
616
1.20k
                  || nTmpLocaleLang == LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
617
1.20k
                  || nTmpLocaleLang == LANGUAGE_MONGOLIAN_MONGOLIAN_LSO )
618
0
                {
619
0
                    nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
620
0
                }
621
1.20k
                else
622
1.20k
                {
623
1.20k
                    nLang = maLocale.meLanguage = LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA;
624
1.20k
                }
625
1.20k
            }
626
1.86k
            break;
627
12.8k
        case 0x02 : // Eastern-Arabic numerals
628
            // all arabic primary mask + LANGUAGE_PUNJABI_ARABIC_LSO
629
12.8k
            if ( primary( nLocaleLang ) != LANGUAGE_ARABIC_PRIMARY_ONLY
630
12.5k
                && nLocaleLang != LANGUAGE_PUNJABI_ARABIC_LSO )
631
12.5k
            {
632
12.5k
                if ( primary( nTmpLocaleLang ) == LANGUAGE_ARABIC_PRIMARY_ONLY
633
12.2k
                    || nTmpLocaleLang != LANGUAGE_PUNJABI_ARABIC_LSO )
634
12.3k
                {
635
12.3k
                    nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
636
12.3k
                }
637
197
                else
638
197
                {
639
197
                    nLang = maLocale.meLanguage = nReferenceLanguage;
640
197
                }
641
12.5k
            }
642
12.8k
            break;
643
        // CJK numerals
644
407
        case 0x1B : // simple Asian numerals, Japanese
645
761
        case 0x1C : // financial Asian numerals, Japanese
646
1.10k
        case 0x1D : // Arabic fullwidth numerals, Japanese
647
1.78k
        case 0x24 : // simple Asian numerals, Korean
648
2.35k
        case 0x25 : // financial Asian numerals, Korean
649
2.59k
        case 0x26 : // Arabic fullwidth numerals, Korean
650
4.04k
        case 0x27 : // Korean Hangul numerals
651
            // Japanese and Korean are regular
652
4.04k
            if ( primary( nLocaleLang ) != primary( nReferenceLanguage ) )
653
3.60k
            {
654
3.60k
                if ( primary( nTmpLocaleLang ) == primary( nReferenceLanguage ) )
655
446
                {
656
446
                    nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
657
446
                }
658
3.15k
                else
659
3.15k
                {
660
3.15k
                    nLang = maLocale.meLanguage = nReferenceLanguage;
661
3.15k
                }
662
3.60k
            }
663
4.04k
            [[fallthrough]];
664
4.88k
        case 0x1E : // simple Asian numerals, Chinese-PRC
665
5.24k
        case 0x1F : // financial Asian numerals, Chinese-PRC
666
7.82k
        case 0x20 : // Arabic fullwidth numerals, Chinese-PRC
667
9.12k
        case 0x21 : // simple Asian numerals, Chinese-Taiwan
668
10.6k
        case 0x22 : // financial Asian numerals, Chinese-Taiwan
669
12.3k
        case 0x23 : // Arabic fullwidth numerals, Chinese-Taiwan
670
12.3k
            nNatNum = nNumeralID == 0x27 ? 9 : ( ( nNumeralID - 0x1B ) % 3 ) + 1;
671
            // [NatNum1] simple numerals
672
            // [natNum2] financial numerals
673
            // [NatNum3] Arabic fullwidth numerals
674
            // Chinese simplified and Chinese traditional have same primary mask
675
            // Chinese-PRC
676
12.3k
            if ( nReferenceLanguage == LANGUAGE_CHINESE_SIMPLIFIED
677
3.78k
              && nLocaleLang != LANGUAGE_CHINESE_SIMPLIFIED
678
3.38k
              && nLocaleLang != LANGUAGE_CHINESE_SINGAPORE
679
3.18k
              && nLocaleLang != LANGUAGE_CHINESE_LSO )
680
3.18k
            {
681
3.18k
                if ( nTmpLocaleLang == LANGUAGE_CHINESE_SIMPLIFIED
682
2.83k
                  || nTmpLocaleLang == LANGUAGE_CHINESE_SINGAPORE
683
2.38k
                  || nTmpLocaleLang == LANGUAGE_CHINESE_LSO )
684
808
                {
685
808
                    nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
686
808
                }
687
2.38k
                else
688
2.38k
                {
689
2.38k
                    nLang = maLocale.meLanguage = LANGUAGE_CHINESE_SIMPLIFIED;
690
2.38k
                }
691
3.18k
            }
692
            // Chinese-Taiwan
693
9.12k
            else if ( nReferenceLanguage == LANGUAGE_CHINESE_TRADITIONAL
694
4.48k
                   && nLocaleLang != LANGUAGE_CHINESE_TRADITIONAL
695
3.89k
                   && nLocaleLang != LANGUAGE_CHINESE_HONGKONG
696
3.64k
                   && nLocaleLang != LANGUAGE_CHINESE_MACAU )
697
3.64k
            {
698
3.64k
                if ( nTmpLocaleLang == LANGUAGE_CHINESE_TRADITIONAL
699
3.43k
                  || nTmpLocaleLang == LANGUAGE_CHINESE_HONGKONG
700
2.99k
                  || nTmpLocaleLang == LANGUAGE_CHINESE_MACAU )
701
650
                {
702
650
                    nLang = maLocale.meLanguage = aTmpLocale.meLanguage;
703
650
                }
704
2.99k
                else
705
2.99k
                {
706
2.99k
                    nLang = maLocale.meLanguage = LANGUAGE_CHINESE_TRADITIONAL;
707
2.99k
                }
708
3.64k
            }
709
12.3k
            break;
710
120k
    }
711
120k
    if ( nNumeralID >= 0x02 && nNumeralID <= 0x13 )
712
31.9k
        nNatNum = 1;
713
120k
    if ( nNatNum )
714
44.2k
        rString.insert(nPos, "[NatNum" + OUString::number(nNatNum) + "]");
715
120k
    return sCalendar;
716
120k
}
717
718
namespace
719
{
720
bool NatNumTakesParameters(sal_Int16 nNum)
721
2.50M
{
722
2.50M
    return (nNum == css::i18n::NativeNumberMode::NATNUM12);
723
2.50M
}
724
}
725
726
// is there a 3-letter bank code in NatNum12 param (but not
727
// followed by an equal mark, like in the date code "NNN=")?
728
static bool lcl_isNatNum12Currency( std::u16string_view sParam )
729
2.15M
{
730
2.15M
    sal_Int32 nUpper = 0;
731
2.15M
    sal_Int32 nLen = sParam.size();
732
26.6M
    for (sal_Int32 n = 0; n < nLen; ++n)
733
24.5M
    {
734
24.5M
        sal_Unicode c = sParam[n];
735
24.5M
        if ( 'A' <= c && c <= 'Z' )
736
3.23M
        {
737
3.23M
            ++nUpper;
738
3.23M
        }
739
21.2M
        else if ( c == ' ' && nUpper == 3 && (n == 3 || sParam[n - 4] == ' ') )
740
157
        {
741
157
            return true;
742
157
        }
743
21.2M
        else
744
21.2M
        {
745
21.2M
            nUpper = 0;
746
21.2M
        }
747
24.5M
    }
748
749
2.15M
    return nUpper == 3 && (nLen == 3 || sParam[nLen - 4] == ' ');
750
2.15M
}
751
752
SvNumberformat::SvNumberformat(OUString& rString,
753
                               ImpSvNumberformatScan* pSc,
754
                               ImpSvNumberInputScan* pISc,
755
                               const NativeNumberWrapper& rNatNum,
756
                               sal_Int32& nCheckPos,
757
                               LanguageType& eLan,
758
                               bool bReplaceBooleanEquivalent)
759
25.1M
        : rScan(*pSc)
760
25.1M
        , bAdditionalBuiltin( false )
761
25.1M
{
762
25.1M
    if (bReplaceBooleanEquivalent)
763
25.1M
        rScan.ReplaceBooleanEquivalent( rString);
764
765
25.1M
    OUStringBuffer sBuff(rString);
766
767
    // If the group (AKA thousand) separator is a No-Break Space (French)
768
    // replace all occurrences by a simple space.
769
    // The same for Narrow No-Break Space just in case some locale uses it.
770
    // The tokens will be changed to the LocaleData separator again later on.
771
25.1M
    const OUString& rThSep = GetCurrentLanguageData().GetNumThousandSep();
772
25.1M
    if ( rThSep.getLength() == 1)
773
25.1M
    {
774
25.1M
        const sal_Unicode cNBSp = 0xA0;
775
25.1M
        const sal_Unicode cNNBSp = 0x202F;
776
25.1M
        if (rThSep[0] == cNBSp )
777
0
            sBuff.replace( cNBSp, ' ');
778
25.1M
        else if (rThSep[0] == cNNBSp )
779
0
            sBuff.replace( cNNBSp, ' ');
780
25.1M
    }
781
782
25.1M
    OUString aConvertFromDecSep;
783
25.1M
    OUString aConvertToDecSep;
784
25.1M
    if (rScan.GetConvertMode())
785
641k
    {
786
641k
        aConvertFromDecSep = GetCurrentLanguageData().GetNumDecimalSep();
787
641k
        maLocale.meLanguage = rScan.GetNewLnge();
788
641k
        eLan = maLocale.meLanguage; // Make sure to return switch
789
641k
    }
790
24.4M
    else
791
24.4M
    {
792
24.4M
        maLocale.meLanguage = eLan;
793
24.4M
    }
794
25.1M
    bStandard = false;
795
25.1M
    bIsUsed = false;
796
25.1M
    fLimit1 = 0.0;
797
25.1M
    fLimit2 = 0.0;
798
25.1M
    eOp1 = NUMBERFORMAT_OP_NO;
799
25.1M
    eOp2 = NUMBERFORMAT_OP_NO;
800
25.1M
    eType = SvNumFormatType::DEFINED;
801
802
25.1M
    bool bCancel = false;
803
25.1M
    bool bCondition = false;
804
25.1M
    short eSymbolType;
805
25.1M
    sal_Int32 nPos = 0;
806
25.1M
    sal_Int32 nPosOld;
807
25.1M
    nCheckPos = 0;
808
809
    // Split into 4 sub formats
810
25.1M
    sal_uInt16 nIndex;
811
54.3M
    for ( nIndex = 0; nIndex < 4 && !bCancel; nIndex++ )
812
29.2M
    {
813
        // Original language/country may have to be reestablished
814
29.2M
        if (rScan.GetConvertMode())
815
845k
        {
816
845k
            rScan.GetCurrentLanguageData().ChangeIntl(rScan.GetTmpLnge());
817
845k
        }
818
29.2M
        OUString sInsertCalendar; // a calendar resulting from parsing LCID
819
29.2M
        OUString sStr;
820
29.2M
        nPosOld = nPos; // Start position of substring
821
        // first get bracketed prefixes; e.g. conditions, color
822
29.2M
        do
823
33.2M
        {
824
33.2M
            eSymbolType = ImpNextSymbol(sBuff, nPos, sStr);
825
33.2M
            if (eSymbolType > 0) // condition
826
21.3k
            {
827
21.3k
                if ( nIndex == 0 && !bCondition )
828
17.3k
                {
829
17.3k
                    bCondition = true;
830
17.3k
                    eOp1 = static_cast<SvNumberformatLimitOps>(eSymbolType);
831
17.3k
                }
832
4.01k
                else if ( nIndex == 1 && bCondition )
833
2.57k
                {
834
2.57k
                    eOp2 = static_cast<SvNumberformatLimitOps>(eSymbolType);
835
2.57k
                }
836
1.44k
                else                                // error
837
1.44k
                {
838
1.44k
                    bCancel = true;                 // break for
839
1.44k
                    nCheckPos = nPosOld;
840
1.44k
                }
841
21.3k
                if (!bCancel)
842
19.9k
                {
843
19.9k
                    double fNumber;
844
19.9k
                    sal_Int32 nCntChars = ImpGetNumber(sBuff, nPos, sStr);
845
19.9k
                    if (nCntChars > 0)
846
14.8k
                    {
847
14.8k
                        sal_Int32 nDecPos;
848
14.8k
                        SvNumFormatType F_Type = SvNumFormatType::UNDEFINED;
849
14.8k
                        if (!pISc->IsNumberFormat(sStr, F_Type, fNumber, nullptr, rNatNum, SvNumInputOptions::NONE) ||
850
6.45k
                            ( F_Type != SvNumFormatType::NUMBER &&
851
988
                              F_Type != SvNumFormatType::SCIENTIFIC) )
852
9.27k
                        {
853
9.27k
                            fNumber = 0.0;
854
9.27k
                            nPos = nPos - nCntChars;
855
9.27k
                            sBuff.remove(nPos, nCntChars);
856
9.27k
                            sBuff.insert(nPos, '0');
857
9.27k
                            nPos++;
858
9.27k
                        }
859
5.59k
                        else if (rScan.GetConvertMode() && ((nDecPos = sStr.indexOf( aConvertFromDecSep)) >= 0))
860
3.14k
                        {
861
3.14k
                            if (aConvertToDecSep.isEmpty())
862
2.84k
                                aConvertToDecSep = rScan.GetCurrentLanguageData().GetLangDecimalSep( rScan.GetNewLnge());
863
3.14k
                            if (aConvertToDecSep != aConvertFromDecSep)
864
211
                            {
865
211
                                const OUString aStr( sStr.replaceAt( nDecPos,
866
211
                                            aConvertFromDecSep.getLength(), aConvertToDecSep));
867
211
                                nPos = nPos - nCntChars;
868
211
                                sBuff.remove(nPos, nCntChars);
869
211
                                sBuff.insert(nPos, aStr);
870
211
                                nPos += aStr.getLength();
871
211
                            }
872
3.14k
                        }
873
14.8k
                    }
874
5.07k
                    else
875
5.07k
                    {
876
5.07k
                        fNumber = 0.0;
877
5.07k
                        sBuff.insert(nPos++, '0');
878
5.07k
                    }
879
19.9k
                    if (nIndex == 0)
880
17.3k
                    {
881
17.3k
                        fLimit1 = fNumber;
882
17.3k
                    }
883
2.57k
                    else
884
2.57k
                    {
885
2.57k
                        fLimit2 = fNumber;
886
2.57k
                    }
887
19.9k
                    if ( nPos < sBuff.getLength() && sBuff[nPos] == ']' )
888
10.6k
                    {
889
10.6k
                        nPos++;
890
10.6k
                    }
891
9.31k
                    else
892
9.31k
                    {
893
9.31k
                        bCancel = true;             // break for
894
9.31k
                        nCheckPos = nPos;
895
9.31k
                    }
896
19.9k
                }
897
21.3k
                nPosOld = nPos;                     // position before string
898
21.3k
            }
899
33.2M
            else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) )
900
4.10M
            {
901
4.10M
                OUString sSymbol( sStr);
902
4.10M
                switch ( eSymbolType )
903
4.10M
                {
904
1.72M
                case BRACKET_SYMBOLTYPE_COLOR :
905
1.72M
                    if ( NumFor[nIndex].GetColor() != nullptr )
906
185
                    {                           // error, more than one color
907
185
                        bCancel = true;         // break for
908
185
                        nCheckPos = nPosOld;
909
185
                    }
910
1.72M
                    else
911
1.72M
                    {
912
1.72M
                        const Color* pColor = pSc->GetColor( sStr);
913
1.72M
                        NumFor[nIndex].SetColor( pColor, sStr);
914
1.72M
                        if (pColor == nullptr)
915
8.68k
                        {                       // error
916
8.68k
                            bCancel = true;     // break for
917
8.68k
                            nCheckPos = nPosOld;
918
8.68k
                        }
919
1.72M
                    }
920
1.72M
                    break;
921
1.37k
                case BRACKET_SYMBOLTYPE_NATNUM0 :
922
36.6k
                case BRACKET_SYMBOLTYPE_NATNUM1 :
923
41.0k
                case BRACKET_SYMBOLTYPE_NATNUM2 :
924
45.9k
                case BRACKET_SYMBOLTYPE_NATNUM3 :
925
45.9k
                case BRACKET_SYMBOLTYPE_NATNUM4 :
926
46.0k
                case BRACKET_SYMBOLTYPE_NATNUM5 :
927
46.0k
                case BRACKET_SYMBOLTYPE_NATNUM6 :
928
46.1k
                case BRACKET_SYMBOLTYPE_NATNUM7 :
929
46.2k
                case BRACKET_SYMBOLTYPE_NATNUM8 :
930
47.6k
                case BRACKET_SYMBOLTYPE_NATNUM9 :
931
47.7k
                case BRACKET_SYMBOLTYPE_NATNUM10 :
932
47.8k
                case BRACKET_SYMBOLTYPE_NATNUM11 :
933
2.20M
                case BRACKET_SYMBOLTYPE_NATNUM12 :
934
2.20M
                case BRACKET_SYMBOLTYPE_NATNUM13 :
935
2.20M
                case BRACKET_SYMBOLTYPE_NATNUM14 :
936
2.20M
                case BRACKET_SYMBOLTYPE_NATNUM15 :
937
2.20M
                case BRACKET_SYMBOLTYPE_NATNUM16 :
938
2.20M
                case BRACKET_SYMBOLTYPE_NATNUM17 :
939
2.20M
                case BRACKET_SYMBOLTYPE_NATNUM18 :
940
2.20M
                case BRACKET_SYMBOLTYPE_NATNUM19 :
941
2.20M
                    if ( NumFor[nIndex].GetNatNum().IsSet() )
942
44
                    {
943
44
                        bCancel = true;         // break for
944
44
                        nCheckPos = nPosOld;
945
44
                    }
946
2.20M
                    else
947
2.20M
                    {
948
2.20M
                        OUString sParams;
949
2.20M
                        sal_Int32 nSpacePos = sStr.indexOf(' ');
950
2.20M
                        if (nSpacePos >= 0)
951
2.15M
                        {
952
2.15M
                            sParams = o3tl::trim(sStr.subView(nSpacePos+1));
953
2.15M
                        }
954
                        //! eSymbolType is negative
955
2.20M
                        sal_uInt8 nNum = static_cast<sal_uInt8>(0 - (eSymbolType - BRACKET_SYMBOLTYPE_NATNUM0));
956
2.20M
                        if (!sParams.isEmpty() && !NatNumTakesParameters(nNum))
957
854
                        {
958
854
                            bCancel = true; // break for
959
854
                            nCheckPos = nPosOld;
960
854
                            break;
961
854
                        }
962
2.20M
                        sStr = "NatNum" + OUString::number(nNum);
963
2.20M
                        NumFor[nIndex].SetNatNumNum( nNum, false );
964
                        // NatNum12 supports arguments
965
2.20M
                        if (nNum == 12)
966
2.15M
                        {
967
2.15M
                            if (sParams.isEmpty())
968
85
                                sParams = "cardinal"; // default NatNum12 format is "cardinal"
969
2.15M
                            else if (sParams.indexOf("CURRENCY") >= 0)
970
1.07M
                                sParams = sParams.replaceAll("CURRENCY",
971
1.07M
                                    rLoc().getCurrBankSymbol());
972
2.15M
                            NumFor[nIndex].SetNatNumParams(sParams);
973
2.15M
                            sStr += " " + sParams;
974
2.15M
                        }
975
2.20M
                    }
976
2.20M
                    break;
977
2.20M
                case BRACKET_SYMBOLTYPE_DBNUM1 :
978
0
                case BRACKET_SYMBOLTYPE_DBNUM2 :
979
0
                case BRACKET_SYMBOLTYPE_DBNUM3 :
980
0
                case BRACKET_SYMBOLTYPE_DBNUM4 :
981
0
                case BRACKET_SYMBOLTYPE_DBNUM5 :
982
0
                case BRACKET_SYMBOLTYPE_DBNUM6 :
983
0
                case BRACKET_SYMBOLTYPE_DBNUM7 :
984
0
                case BRACKET_SYMBOLTYPE_DBNUM8 :
985
0
                case BRACKET_SYMBOLTYPE_DBNUM9 :
986
0
                    if ( NumFor[nIndex].GetNatNum().IsSet() )
987
0
                    {
988
0
                        bCancel = true;         // break for
989
0
                        nCheckPos = nPosOld;
990
0
                    }
991
0
                    else
992
0
                    {
993
                        //! eSymbolType is negative
994
0
                        sal_uInt8 nNum = static_cast<sal_uInt8>(1 - (eSymbolType - BRACKET_SYMBOLTYPE_DBNUM1));
995
0
                        sStr = "DBNum" + OUStringChar(sal_Unicode('0' + nNum));
996
0
                        NumFor[nIndex].SetNatNumNum( nNum, true );
997
0
                    }
998
0
                    break;
999
175k
                case BRACKET_SYMBOLTYPE_LOCALE :
1000
175k
                    if ( NumFor[nIndex].GetNatNum().GetLang() != LANGUAGE_DONTKNOW ||
1001
175k
                         sBuff[nPos-1] != ']' )
1002
                        // Check also for ']' to avoid pulling in
1003
                        // locale data for the preview string for not
1004
                        // yet completed LCIDs in the dialog.
1005
4.59k
                    {
1006
4.59k
                        bCancel = true;         // break for
1007
4.59k
                        nCheckPos = nPosOld;
1008
4.59k
                    }
1009
171k
                    else
1010
171k
                    {
1011
171k
                        sal_Int32 nTmp = 2;
1012
171k
                        LocaleType aTmpLocale( ImpGetLocaleType( sStr, nTmp));
1013
171k
                        if (aTmpLocale.meLanguage == LANGUAGE_DONTKNOW)
1014
6.51k
                        {
1015
6.51k
                            bCancel = true;         // break for
1016
6.51k
                            nCheckPos = nPosOld;
1017
6.51k
                        }
1018
164k
                        else
1019
164k
                        {
1020
                            // Only the first sub format's locale will be
1021
                            // used as the format's overall locale.
1022
                            // Sorts this also under the corresponding
1023
                            // locale for the dialog.
1024
                            // If we don't support the locale this would
1025
                            // result in an unknown (empty) language
1026
                            // listbox entry and the user would never see
1027
                            // this format.
1028
164k
                            if (nIndex == 0 && (aTmpLocale.meLanguage == LANGUAGE_SYSTEM ||
1029
149k
                                                SvNumberFormatter::IsLocaleInstalled( aTmpLocale.meLanguage)))
1030
59.8k
                            {
1031
59.8k
                                maLocale = aTmpLocale;
1032
59.8k
                                eLan = aTmpLocale.meLanguage;   // return to caller
1033
1034
                                // Set new target locale also at scanner.
1035
                                // We have to do this because switching locale
1036
                                // may make replacing keywords and separators
1037
                                // necessary.
1038
                                // We can do this because it's the first
1039
                                // subformat and we're still at parsing the
1040
                                // modifiers, not keywords.
1041
59.8k
                                rScan.SetNewLnge( eLan);
1042
                                // We can not force conversion though because
1043
                                // the caller may have explicitly not set it.
1044
                                // In the usual case the target locale is the
1045
                                // originating locale the conversion is not
1046
                                // necessary, when reading alien documents
1047
                                // conversion is enabled anyway.
1048
1049
                                /* TODO: fiddle with scanner to make this
1050
                                 * known? A change in the locale may affect
1051
                                 * separators and keywords. On the other
1052
                                 * hand they may have been entered as used
1053
                                 * in the originating locale, there's no
1054
                                 * way to predict other than analyzing the
1055
                                 * format code, we assume here the current
1056
                                 * context is used, which is most likely
1057
                                 * the case.
1058
                                 * */
1059
1060
                                // Strip a plain locale identifier if locale
1061
                                // data is available to avoid duplicated
1062
                                // formats with and without LCID for the same
1063
                                // locale. Besides it looks ugly and confusing
1064
                                // and is unnecessary as the format will be
1065
                                // listed for the resulting locale.
1066
59.8k
                                if (aTmpLocale.isPlainLocale())
1067
41.7k
                                    sStr.clear();
1068
18.0k
                                else
1069
18.0k
                                    sStr = "$-" + aTmpLocale.generateCode();
1070
59.8k
                            }
1071
104k
                            else
1072
104k
                            {
1073
104k
                                if (nIndex == 0)
1074
                                    // Locale data not available, remember.
1075
96.1k
                                    maLocale.meLanguageWithoutLocaleData = aTmpLocale.meLanguage;
1076
1077
104k
                                sStr = "$-" + aTmpLocale.generateCode();
1078
104k
                            }
1079
164k
                            NumFor[nIndex].SetNatNumLang( MsLangId::getRealLanguage( aTmpLocale.meLanguage));
1080
1081
                            // "$-NNCCLLLL" Numerals and Calendar
1082
164k
                            if (sSymbol.getLength() > 6)
1083
120k
                            {
1084
120k
                                sInsertCalendar = ImpObtainCalendarAndNumerals( sBuff, nPos, eLan, aTmpLocale);
1085
120k
                            }
1086
                            /* NOTE: there can be only one calendar
1087
                             * inserted so the last one wins, though
1088
                             * our own calendar modifiers support
1089
                             * multiple calendars within one sub format
1090
                             * code if at different positions. */
1091
164k
                        }
1092
171k
                    }
1093
175k
                    break;
1094
4.10M
                }
1095
4.10M
                if ( !bCancel )
1096
4.08M
                {
1097
4.08M
                    if (sStr == sSymbol)
1098
2.82M
                    {
1099
2.82M
                        nPosOld = nPos;
1100
2.82M
                    }
1101
1.25M
                    else
1102
1.25M
                    {
1103
1.25M
                        sBuff.remove(nPosOld, nPos - nPosOld);
1104
1.25M
                        if (!sStr.isEmpty())
1105
1.21M
                        {
1106
1.21M
                            sBuff.insert(nPosOld, "[" + sStr + "]");
1107
1.21M
                            nPos = nPosOld + sStr.getLength() + 2;
1108
1.21M
                            nPosOld = nPos;     // position before string
1109
1.21M
                        }
1110
41.7k
                        else
1111
41.7k
                        {
1112
41.7k
                            nPos = nPosOld;     // prefix removed for whatever reason
1113
41.7k
                        }
1114
1.25M
                    }
1115
4.08M
                }
1116
4.10M
            }
1117
33.2M
        }
1118
33.2M
        while ( !bCancel && lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) );
1119
1120
        // The remaining format code string
1121
29.2M
        if ( !bCancel )
1122
29.1M
        {
1123
29.1M
            if (eSymbolType == BRACKET_SYMBOLTYPE_FORMAT)
1124
29.1M
            {
1125
29.1M
                if (nIndex == 1 && eOp1 == NUMBERFORMAT_OP_NO)
1126
3.99M
                {
1127
3.99M
                    eOp1 = NUMBERFORMAT_OP_GT;  // undefined condition, default: > 0
1128
3.99M
                }
1129
25.1M
                else if (nIndex == 2 && eOp2 == NUMBERFORMAT_OP_NO)
1130
52.8k
                {
1131
52.8k
                    eOp2 = NUMBERFORMAT_OP_LT;  // undefined condition, default: < 0
1132
52.8k
                }
1133
29.1M
                if (sStr.isEmpty())
1134
41.7k
                {
1135
                    // Empty sub format.
1136
41.7k
                    NumFor[nIndex].Info().eScannedType = SvNumFormatType::EMPTY;
1137
41.7k
                }
1138
29.1M
                else
1139
29.1M
                {
1140
29.1M
                    if (!sInsertCalendar.isEmpty())
1141
16.9k
                    {
1142
16.9k
                        sStr = sInsertCalendar + sStr;
1143
16.9k
                    }
1144
29.1M
                    sal_Int32 nStrPos = pSc->ScanFormat( sStr);
1145
29.1M
                    sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1146
29.1M
                    if (nCnt == 0 && nStrPos == 0)  // error
1147
0
                    {
1148
0
                        nStrPos = 1;
1149
0
                    }
1150
29.1M
                    if (nStrPos == 0)               // ok
1151
29.0M
                    {
1152
                        // e.g. Thai T speciality
1153
29.0M
                        if (pSc->GetNatNumModifier() && !NumFor[nIndex].GetNatNum().IsSet())
1154
0
                        {
1155
0
                            sStr = "[NatNum"  + OUString::number( pSc->GetNatNumModifier()) + "]" + sStr;
1156
0
                            NumFor[nIndex].SetNatNumNum( pSc->GetNatNumModifier(), false );
1157
0
                        }
1158
                        // #i53826# #i42727# For the Thai T speciality we need
1159
                        // to freeze the locale and immunize it against
1160
                        // conversions during exports, just in case we want to
1161
                        // save to Xcl. This disables the feature of being able
1162
                        // to convert a NatNum to another locale. You can't
1163
                        // have both.
1164
                        // FIXME: implement a specialized export conversion
1165
                        // that works on tokens (have to tokenize all first)
1166
                        // and doesn't use the format string and
1167
                        // PutandConvertEntry() to LANGUAGE_ENGLISH_US in
1168
                        // sc/source/filter/excel/xestyle.cxx
1169
                        // XclExpNumFmtBuffer::WriteFormatRecord().
1170
29.0M
                        LanguageType eLanguage;
1171
29.0M
                        if (NumFor[nIndex].GetNatNum().GetNatNum() == 1 &&
1172
20.4k
                            ((eLanguage = MsLangId::getRealLanguage( eLan)) == LANGUAGE_THAI) &&
1173
1.12k
                            NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW)
1174
0
                        {
1175
0
                            sStr = "[$-" + OUString::number( sal_uInt16(eLanguage), 16 ).toAsciiUpperCase() + "]" + sStr;
1176
0
                            NumFor[nIndex].SetNatNumLang( eLanguage);
1177
0
                        }
1178
29.0M
                        sBuff.remove(nPosOld, nPos - nPosOld);
1179
29.0M
                        sBuff.insert(nPosOld, sStr);
1180
29.0M
                        nPos = nPosOld + sStr.getLength();
1181
29.0M
                        if (nPos < sBuff.getLength())
1182
4.07M
                        {
1183
4.07M
                            sBuff.insert(nPos, ";");
1184
4.07M
                            nPos++;
1185
4.07M
                        }
1186
24.9M
                        else if (nIndex > 0)
1187
3.97M
                        {
1188
                            // The last subformat. If it is a trailing text
1189
                            // format the omitted subformats act like they were
1190
                            // not specified and "inherited" the first format,
1191
                            // e.g.  0;@  behaves like  0;-0;0;@
1192
3.97M
                            if (pSc->GetScannedType() == SvNumFormatType::TEXT)
1193
16.6k
                            {
1194
                                // Reset conditions, reverting any set above.
1195
16.6k
                                if (nIndex == 1)
1196
1.57k
                                    eOp1 = NUMBERFORMAT_OP_NO;
1197
15.0k
                                else if (nIndex == 2)
1198
617
                                    eOp2 = NUMBERFORMAT_OP_NO;
1199
16.6k
                                nIndex = 3;
1200
16.6k
                            }
1201
3.97M
                        }
1202
29.0M
                        NumFor[nIndex].Enlarge(nCnt);
1203
29.0M
                        pSc->CopyInfo(&(NumFor[nIndex].Info()), nCnt);
1204
                        // type check
1205
29.0M
                        if (nIndex == 0)
1206
25.0M
                        {
1207
25.0M
                            if ( NumFor[nIndex].GetNatNum().GetNatNum() == 12 &&
1208
2.15M
                                    lcl_isNatNum12Currency(NumFor[nIndex].GetNatNum().GetParams()) )
1209
1.07M
                                eType = SvNumFormatType::CURRENCY;
1210
23.9M
                            else
1211
23.9M
                                eType = NumFor[nIndex].Info().eScannedType;
1212
25.0M
                        }
1213
4.05M
                        else if (nIndex == 3)
1214
29.6k
                        {   // #77026# Everything recognized IS text
1215
29.6k
                            NumFor[nIndex].Info().eScannedType = SvNumFormatType::TEXT;
1216
29.6k
                        }
1217
4.02M
                        else if ( NumFor[nIndex].Info().eScannedType != eType)
1218
88.9k
                        {
1219
88.9k
                            eType = SvNumFormatType::DEFINED;
1220
88.9k
                        }
1221
29.0M
                    }
1222
68.1k
                    else
1223
68.1k
                    {
1224
68.1k
                        nCheckPos = nPosOld + nStrPos;  // error in string
1225
68.1k
                        bCancel = true;                 // break for
1226
68.1k
                    }
1227
29.1M
                }
1228
29.1M
            }
1229
2.41k
            else if (eSymbolType == BRACKET_SYMBOLTYPE_ERROR)   // error
1230
2.41k
            {
1231
2.41k
                nCheckPos = nPosOld;
1232
2.41k
                bCancel = true;
1233
2.41k
            }
1234
0
            else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) )
1235
0
            {
1236
0
                nCheckPos = nPosOld + 1;                // error, prefix in string
1237
0
                bCancel = true;                         // break for
1238
0
            }
1239
29.1M
        }
1240
29.2M
        if ( bCancel && !nCheckPos )
1241
19.9k
        {
1242
19.9k
            nCheckPos = 1;      // nCheckPos is used as an error condition
1243
19.9k
        }
1244
29.2M
        if ( !bCancel )
1245
29.1M
        {
1246
29.1M
            if ( NumFor[nIndex].GetNatNum().IsSet() &&
1247
2.20M
                 NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW )
1248
2.15M
            {
1249
2.15M
                 NumFor[nIndex].SetNatNumLang( eLan );
1250
2.15M
            }
1251
29.1M
        }
1252
29.2M
        if (sBuff.getLength() == nPos)
1253
25.1M
        {
1254
25.1M
            if (nIndex < 3 && rString[rString.getLength()-1] == ';')
1255
7.60k
            {
1256
                // A trailing ';' is significant and specifies the following
1257
                // subformat to be empty. We don't enter the scanning loop
1258
                // above again though.
1259
                // Note that the operators apply to the current last scanned
1260
                // subformat.
1261
7.60k
                if (nIndex == 0 && eOp1 == NUMBERFORMAT_OP_NO)
1262
4.06k
                {
1263
4.06k
                    eOp1 = NUMBERFORMAT_OP_GT;  // undefined condition, default: > 0
1264
4.06k
                }
1265
3.54k
                else if (nIndex == 1 && eOp2 == NUMBERFORMAT_OP_NO)
1266
2.04k
                {
1267
2.04k
                    eOp2 = NUMBERFORMAT_OP_LT;  // undefined condition, default: < 0
1268
2.04k
                }
1269
7.60k
                NumFor[nIndex+1].Info().eScannedType = SvNumFormatType::EMPTY;
1270
7.60k
                if (sBuff[nPos-1] != ';')
1271
5.19k
                    sBuff.insert( nPos++, ';');
1272
7.60k
            }
1273
25.1M
            if (nIndex == 2 && eSymbolType == BRACKET_SYMBOLTYPE_FORMAT && sBuff[nPos-1] == ';')
1274
591
            {
1275
                // #83510# A 4th subformat explicitly specified to be empty
1276
                // hides any text. Need the type here for HasTextFormat()
1277
591
                NumFor[3].Info().eScannedType = SvNumFormatType::TEXT;
1278
591
            }
1279
25.1M
            bCancel = true;
1280
25.1M
        }
1281
29.2M
        if ( NumFor[nIndex].GetNatNum().IsSet() )
1282
2.20M
        {
1283
2.20M
            NumFor[nIndex].SetNatNumDate( bool(NumFor[nIndex].Info().eScannedType & SvNumFormatType::DATE) );
1284
2.20M
        }
1285
29.2M
    }
1286
1287
25.1M
    if (!nCheckPos && IsSubstituted())
1288
1.38k
    {
1289
        // For to be substituted formats the scanned type must match the
1290
        // substitute type.
1291
1.38k
        if (IsSystemTimeFormat())
1292
511
        {
1293
511
            if ((eType & ~SvNumFormatType::DEFINED) != SvNumFormatType::TIME)
1294
147
                nCheckPos = std::max<sal_Int32>( sBuff.indexOf(']') + 1, 1);
1295
511
        }
1296
869
        else if (IsSystemLongDateFormat())
1297
415
        {
1298
415
            if ((eType & ~SvNumFormatType::DEFINED) != SvNumFormatType::DATE)
1299
198
                nCheckPos = std::max<sal_Int32>( sBuff.indexOf(']') + 1, 1);
1300
415
        }
1301
454
        else
1302
869
            assert(!"unhandled substitute");
1303
1.38k
    }
1304
1305
25.1M
    if ( bCondition && !nCheckPos )
1306
6.43k
    {
1307
6.43k
        if ( nIndex == 1 && NumFor[0].GetCount() == 0 &&
1308
1.81k
             sBuff[sBuff.getLength() - 1] != ';' )
1309
1.57k
        {
1310
            // No format code => GENERAL but not if specified empty
1311
1.57k
            OUString aAdd( pSc->GetStandardName() );
1312
1.57k
            if ( !pSc->ScanFormat( aAdd ) )
1313
1.57k
            {
1314
1.57k
                sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1315
1.57k
                if ( nCnt )
1316
1.57k
                {
1317
1.57k
                    NumFor[0].Enlarge(nCnt);
1318
1.57k
                    pSc->CopyInfo( &(NumFor[0].Info()), nCnt );
1319
1.57k
                    sBuff.append(aAdd);
1320
1.57k
                }
1321
1.57k
            }
1322
1.57k
        }
1323
4.86k
        else if ( nIndex == 1 && NumFor[nIndex].GetCount() == 0 &&
1324
2.56k
                  sBuff[sBuff.getLength() - 1] != ';' &&
1325
2.11k
                  (NumFor[0].GetCount() > 1 ||
1326
1.63k
                   (NumFor[0].GetCount() == 1 &&
1327
1.63k
                    NumFor[0].Info().nTypeArray[0] != NF_KEY_GENERAL)) )
1328
1.91k
        {
1329
            // No trailing second subformat => GENERAL but not if specified empty
1330
            // and not if first subformat is GENERAL
1331
1.91k
            OUString aAdd( pSc->GetStandardName() );
1332
1.91k
            if ( !pSc->ScanFormat( aAdd ) )
1333
1.91k
            {
1334
1.91k
                sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1335
1.91k
                if ( nCnt )
1336
1.91k
                {
1337
1.91k
                    NumFor[nIndex].Enlarge(nCnt);
1338
1.91k
                    pSc->CopyInfo( &(NumFor[nIndex].Info()), nCnt );
1339
1.91k
                    sBuff.append(";" + aAdd);
1340
1.91k
                }
1341
1.91k
            }
1342
1.91k
        }
1343
2.94k
        else if ( nIndex == 2 && NumFor[nIndex].GetCount() == 0 &&
1344
1.23k
                  sBuff[sBuff.getLength() - 1] != ';' &&
1345
972
                  eOp2 != NUMBERFORMAT_OP_NO )
1346
451
        {
1347
            // No trailing third subformat => GENERAL but not if specified empty
1348
451
            OUString aAdd( pSc->GetStandardName() );
1349
451
            if ( !pSc->ScanFormat( aAdd ) )
1350
451
            {
1351
451
                sal_uInt16 nCnt = pSc->GetResultStringsCnt();
1352
451
                if ( nCnt )
1353
451
                {
1354
451
                    NumFor[nIndex].Enlarge(nCnt);
1355
451
                    pSc->CopyInfo( &(NumFor[nIndex].Info()), nCnt );
1356
451
                    sBuff.append(";" + aAdd);
1357
451
                }
1358
451
            }
1359
451
        }
1360
6.43k
    }
1361
25.1M
    rString = sBuff.makeStringAndClear();
1362
25.1M
    sFormatstring = rString;
1363
1364
25.1M
    if (NumFor[2].GetCount() == 0 && // No third partial string
1365
25.0M
        eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_NO &&
1366
3.94M
        fLimit1 == 0.0 && fLimit2 == 0.0)
1367
3.94M
    {
1368
3.94M
        eOp1 = NUMBERFORMAT_OP_GE; // Add 0 to the first format
1369
3.94M
    }
1370
1371
25.1M
}
1372
1373
SvNumberformat::~SvNumberformat()
1374
25.1M
{
1375
25.1M
}
1376
1377
/**
1378
 * Next_Symbol
1379
 *
1380
 * Splits up the symbols for further processing (by the Turing machine)
1381
 *
1382
 * Start state = SsStart, * = Special state
1383
 * ---------------+-------------------+----------------------------+---------------
1384
 *  Old State     | Symbol read       | Event                      | New state
1385
 * ---------------+-------------------+----------------------------+---------------
1386
 *  SsStart       | "                 | Symbol += Character        | SsGetQuoted
1387
 *                | ;                 | Pos--                      | SsGetString
1388
 *                | [                 | Symbol += Character        | SsGetBracketed
1389
 *                | ]                 | Error                      | SsStop
1390
 *                | BLANK             |                            |
1391
 *                | Else              | Symbol += Character        | SsGetString
1392
 * ---------------+-------------------+----------------------------+---------------
1393
 *  SsGetString   | "                 | Symbol += Character        | SsGetQuoted
1394
 *                | ;                 |                            | SsStop
1395
 *                | Else              | Symbol += Character        |
1396
 * ---------------+-------------------+----------------------------+---------------
1397
 *  SsGetQuoted   | "                 | Symbol += Character        | SsGetString
1398
 *                | Else              | Symbol += Character        |
1399
 * ---------------+-------------------+----------------------------+---------------
1400
 * SsGetBracketed | <, > =            | del [                      |
1401
 *                |                   | Symbol += Character        | SsGetCon
1402
 *                | BLANK             |                            |
1403
 *                | h, H, m, M, s, S  | Symbol += Character        | SsGetTime
1404
 *                | Else              | del [                      |
1405
 *                |                   | Symbol += Character        | SsGetPrefix
1406
 * ---------------+-------------------+----------------------------+---------------
1407
 *  SsGetTime     | ]                 | Symbol += Character        | SsGetString
1408
 *                | h, H, m, M, s, S  | Symbol += Character, *     | SsGetString
1409
 *                | Else              | del [; Symbol += Character | SsGetPrefix
1410
 * ---------------+-------------------+----------------------------+---------------
1411
 *  SsGetPrefix   | ]                 |                            | SsStop
1412
 *                | Else              | Symbol += Character        |
1413
 * ---------------+-------------------+----------------------------+---------------
1414
 *  SsGetCon      | >, =              | Symbol += Character        |
1415
 *                | ]                 |                            | SsStop
1416
 *                | Else              | Error                      | SsStop
1417
 * ---------------+-------------------+----------------------------+---------------
1418
 */
1419
1420
namespace {
1421
1422
enum ScanState
1423
{
1424
    SsStop,
1425
    SsStart,
1426
    SsGetCon,           // condition
1427
    SsGetString,        // format string
1428
    SsGetPrefix,        // color or NatNumN
1429
    SsGetTime,          // [HH] for time
1430
    SsGetBracketed,     // any [...] not decided yet
1431
    SsGetQuoted         // quoted text
1432
};
1433
1434
}
1435
1436
// read a string until ']' and delete spaces in input
1437
// static
1438
sal_Int32 SvNumberformat::ImpGetNumber(OUStringBuffer& rString,
1439
                                       sal_Int32& nPos,
1440
                                       OUString& sSymbol)
1441
19.9k
{
1442
19.9k
    sal_Int32 nStartPos = nPos;
1443
19.9k
    sal_Unicode cToken;
1444
19.9k
    sal_Int32 nLen = rString.getLength();
1445
19.9k
    OUStringBuffer sBuffSymbol;
1446
189k
    while ( nPos < nLen )
1447
180k
    {
1448
180k
        cToken = rString[nPos];
1449
180k
        if (cToken == ']')
1450
10.6k
            break;
1451
169k
        if (cToken == ' ')
1452
3.93k
        {                                               // delete spaces
1453
3.93k
            rString.remove(nPos,1);
1454
3.93k
            nLen--;
1455
3.93k
        }
1456
166k
        else
1457
166k
        {
1458
166k
            nPos++;
1459
166k
            sBuffSymbol.append(cToken);
1460
166k
        }
1461
169k
    }
1462
19.9k
    sSymbol = sBuffSymbol.makeStringAndClear();
1463
19.9k
    return nPos - nStartPos;
1464
19.9k
}
1465
1466
namespace {
1467
1468
sal_Unicode toUniChar(sal_uInt8 n)
1469
417k
{
1470
417k
    if (n < 10)
1471
311k
        return static_cast<sal_Unicode>('0' + n);
1472
106k
    else
1473
106k
        return static_cast<sal_Unicode>('A' + n - 10);
1474
417k
}
1475
1476
bool IsCombiningSymbol( OUStringBuffer& rStringBuffer, sal_Int32 nPos )
1477
4.09M
{
1478
4.09M
    bool bRet = false;
1479
4.10M
    while (nPos >= 0)
1480
4.10M
    {
1481
4.10M
        switch (rStringBuffer[nPos])
1482
4.10M
        {
1483
1.46k
            case '*':
1484
4.17k
            case '\\':
1485
6.09k
            case '_':
1486
6.09k
                bRet = !bRet;
1487
6.09k
                --nPos;
1488
6.09k
                break;
1489
4.09M
            default:
1490
4.09M
                return bRet;
1491
4.10M
        }
1492
4.10M
    }
1493
514
    return bRet;
1494
4.09M
}
1495
1496
} // namespace
1497
1498
OUString SvNumberformat::LocaleType::generateCode() const
1499
122k
{
1500
122k
    OUStringBuffer aBuf;
1501
#if 0
1502
    // TODO: We may re-enable this later. Don't remove it! --Kohei
1503
    if (mnNumeralShape)
1504
    {
1505
        sal_uInt8 nVal = mnNumeralShape;
1506
        for (sal_uInt8 i = 0; i < 2; ++i)
1507
        {
1508
            sal_uInt8 n = (nVal & 0xF0) >> 4;
1509
            if (n || aBuf.getLength())
1510
            {
1511
                aBuf.append(toUniChar(n));
1512
            }
1513
            nVal = nVal << 4;
1514
        }
1515
    }
1516
1517
    if (mnNumeralShape || mnCalendarType)
1518
    {
1519
        sal_uInt8 nVal = mnCalendarType;
1520
        for (sal_uInt8 i = 0; i < 2; ++i)
1521
        {
1522
            sal_uInt8 n = (nVal & 0xF0) >> 4;
1523
            if (n || aBuf.getLength())
1524
            {
1525
                aBuf.append(toUniChar(n));
1526
            }
1527
            nVal = nVal << 4;
1528
        }
1529
    }
1530
#endif
1531
1532
122k
    sal_uInt16 n16 = static_cast<sal_uInt16>(
1533
122k
            (meLanguageWithoutLocaleData == LANGUAGE_DONTKNOW) ? meLanguage :
1534
122k
            meLanguageWithoutLocaleData);
1535
122k
    if (meLanguage == LANGUAGE_SYSTEM)
1536
3.93k
    {
1537
3.93k
        switch (meSubstitute)
1538
3.93k
        {
1539
2.51k
            case Substitute::NONE:
1540
2.51k
                ;   // nothing
1541
2.51k
                break;
1542
737
            case Substitute::TIME:
1543
737
                n16 = static_cast<sal_uInt16>(LANGUAGE_NF_SYSTEM_TIME);
1544
737
                break;
1545
683
            case Substitute::LONGDATE:
1546
683
                n16 = static_cast<sal_uInt16>(LANGUAGE_NF_SYSTEM_DATE);
1547
683
                break;
1548
3.93k
        }
1549
3.93k
    }
1550
614k
    for (sal_uInt8 i = 0; i < 4; ++i)
1551
491k
    {
1552
491k
        sal_uInt8 n = static_cast<sal_uInt8>((n16 & 0xF000) >> 12);
1553
        // Omit leading zeros for consistency.
1554
491k
        if (n || !aBuf.isEmpty() || i == 3)
1555
417k
        {
1556
417k
            aBuf.append(toUniChar(n));
1557
417k
        }
1558
491k
        n16 = (n16 << 4) & 0xFFFF;
1559
491k
    }
1560
1561
122k
    return aBuf.makeStringAndClear();
1562
122k
}
1563
1564
SvNumberformat::LocaleType::LocaleType()
1565
25.1M
    : meLanguage(LANGUAGE_DONTKNOW)
1566
25.1M
    , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW)
1567
25.1M
    , meSubstitute(Substitute::NONE)
1568
25.1M
    , mnNumeralShape(0)
1569
25.1M
    , mnCalendarType(0)
1570
25.1M
{
1571
25.1M
}
1572
1573
SvNumberformat::LocaleType::LocaleType(sal_uInt32 nRawNum)
1574
164k
    : meLanguage(LANGUAGE_DONTKNOW)
1575
164k
    , meLanguageWithoutLocaleData(LANGUAGE_DONTKNOW)
1576
164k
    , meSubstitute(Substitute::NONE)
1577
164k
    , mnNumeralShape(0)
1578
164k
    , mnCalendarType(0)
1579
164k
{
1580
164k
    meLanguage = static_cast<LanguageType>(nRawNum & 0x0000FFFF);
1581
164k
    if (meLanguage == LANGUAGE_NF_SYSTEM_TIME)
1582
737
    {
1583
737
        meSubstitute = Substitute::TIME;
1584
737
        meLanguage = LANGUAGE_SYSTEM;
1585
737
    }
1586
163k
    else if (meLanguage == LANGUAGE_NF_SYSTEM_DATE)
1587
683
    {
1588
683
        meSubstitute = Substitute::LONGDATE;
1589
683
        meLanguage = LANGUAGE_SYSTEM;
1590
683
    }
1591
164k
    nRawNum = (nRawNum >> 16);
1592
164k
    mnCalendarType = static_cast<sal_uInt8>(nRawNum & 0xFF);
1593
164k
    nRawNum = (nRawNum >> 8);
1594
164k
    mnNumeralShape = static_cast<sal_uInt8>(nRawNum & 0xFF);
1595
164k
}
1596
1597
bool SvNumberformat::LocaleType::isPlainLocale() const
1598
59.8k
{
1599
59.8k
    return meSubstitute == Substitute::NONE && !mnCalendarType && !mnNumeralShape;
1600
59.8k
}
1601
1602
// static
1603
SvNumberformat::LocaleType SvNumberformat::ImpGetLocaleType(std::u16string_view rString, sal_Int32& nPos )
1604
171k
{
1605
171k
    sal_uInt32 nNum = 0;
1606
171k
    sal_Unicode cToken = 0;
1607
171k
    sal_Int32 nStart = nPos;
1608
171k
    sal_Int32 nLen = rString.size();
1609
1.19M
    while ( nPos < nLen && (nPos - nStart < 8) )
1610
1.02M
    {
1611
1.02M
        cToken = rString[nPos];
1612
1.02M
        if (cToken == ']')
1613
0
            break;
1614
1615
1.02M
        int nValue = o3tl::convertToHex<int>(cToken);
1616
1617
1.02M
        if (nValue == -1)
1618
5.19k
            return LocaleType(); // LANGUAGE_DONTKNOW;
1619
1620
1.02M
        nNum *= 16;
1621
1.02M
        nNum += nValue;
1622
1623
1.02M
        ++nPos;
1624
1.02M
    }
1625
1626
166k
    return (cToken == ']' || nPos == nLen) ? LocaleType(nNum) : LocaleType();
1627
171k
}
1628
1629
static bool lcl_matchKeywordAndGetNumber( std::u16string_view rString, const sal_Int32 nPos,
1630
        std::u16string_view rKeyword, sal_Int32 & nNumber )
1631
6.76M
{
1632
6.76M
    if (0 <= nPos && nPos + static_cast<sal_Int32>(rKeyword.size()) < static_cast<sal_Int32>(rString.size()) && o3tl::matchIgnoreAsciiCase( rString, rKeyword, nPos))
1633
2.20M
    {
1634
2.20M
        nNumber = o3tl::toInt32(rString.substr( nPos + rKeyword.size()));
1635
2.20M
        return true;
1636
2.20M
    }
1637
4.55M
    else
1638
4.55M
    {
1639
4.55M
        nNumber = 0;
1640
4.55M
        return false;
1641
4.55M
    }
1642
6.76M
}
1643
1644
short SvNumberformat::ImpNextSymbol(OUStringBuffer& rString,
1645
                                    sal_Int32& nPos,
1646
                                    OUString& sSymbol) const
1647
33.2M
{
1648
33.2M
    short eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1649
33.2M
    sal_Unicode cToken;
1650
33.2M
    sal_Unicode cLetter = ' '; // Preliminary result
1651
33.2M
    sal_Int32 nLen = rString.getLength();
1652
33.2M
    ScanState eState = SsStart;
1653
33.2M
    OUStringBuffer sBuffSymbol(128);
1654
1655
33.2M
    const NfKeywordTable & rKeywords = rScan.GetKeywords();
1656
401M
    while (nPos < nLen && eState != SsStop)
1657
367M
    {
1658
367M
        cToken = rString[nPos];
1659
367M
        nPos++;
1660
367M
        switch (eState)
1661
367M
        {
1662
33.2M
        case SsStart:
1663
33.2M
            if (cToken == '\"')
1664
7.37k
            {
1665
7.37k
                eState = SsGetQuoted;
1666
7.37k
                sBuffSymbol.append(cToken);
1667
7.37k
            }
1668
33.2M
            else if (cToken == '[')
1669
9.34M
            {
1670
9.34M
                eState = SsGetBracketed;
1671
9.34M
                sBuffSymbol.append(cToken);
1672
9.34M
            }
1673
23.9M
            else if (cToken == ';')
1674
14.9k
            {
1675
14.9k
                eState = SsGetString;
1676
14.9k
                nPos--;
1677
14.9k
                eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1678
14.9k
            }
1679
23.9M
            else if (cToken == ']')
1680
1.39k
            {
1681
1.39k
                eState = SsStop;
1682
1.39k
                eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1683
1.39k
            }
1684
23.9M
            else if (cToken == ' ') // Skip Blanks
1685
6.48k
            {
1686
6.48k
                nPos--;
1687
6.48k
                rString.remove(nPos, 1);
1688
6.48k
                nLen--;
1689
6.48k
            }
1690
23.8M
            else
1691
23.8M
            {
1692
23.8M
                sBuffSymbol.append(cToken);
1693
23.8M
                eState = SsGetString;
1694
23.8M
                eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1695
23.8M
            }
1696
33.2M
            break;
1697
9.35M
        case SsGetBracketed:
1698
9.35M
            switch (cToken)
1699
9.35M
            {
1700
9.38k
            case '<':
1701
15.5k
            case '>':
1702
22.4k
            case '=':
1703
22.4k
                sBuffSymbol.stripStart('[');
1704
22.4k
                sBuffSymbol.append(cToken);
1705
22.4k
                cLetter = cToken;
1706
22.4k
                eState = SsGetCon;
1707
22.4k
                switch (cToken)
1708
22.4k
                {
1709
9.38k
                case '<':
1710
9.38k
                    eSymbolType = NUMBERFORMAT_OP_LT;
1711
9.38k
                    break;
1712
6.13k
                case '>':
1713
6.13k
                    eSymbolType = NUMBERFORMAT_OP_GT;
1714
6.13k
                    break;
1715
6.89k
                case '=':
1716
6.89k
                    eSymbolType = NUMBERFORMAT_OP_EQ;
1717
6.89k
                    break;
1718
22.4k
                }
1719
22.4k
                break;
1720
22.4k
            case ' ':
1721
6.28k
                nPos--;
1722
6.28k
                rString.remove(nPos, 1);
1723
6.28k
                nLen--;
1724
6.28k
                break;
1725
2.14M
            case '$' :
1726
2.14M
                if ( nPos < nLen && rString[nPos] == '-' )
1727
175k
                {
1728
                    // [$-xxx] locale
1729
175k
                    sBuffSymbol.stripStart('[');
1730
175k
                    eSymbolType = BRACKET_SYMBOLTYPE_LOCALE;
1731
175k
                    eState = SsGetPrefix;
1732
175k
                }
1733
1.96M
                else
1734
1.96M
                {   // currency
1735
1.96M
                    eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1736
1.96M
                    eState = SsGetString;
1737
1.96M
                }
1738
2.14M
                sBuffSymbol.append(cToken);
1739
2.14M
                break;
1740
2.69M
            case '~' :
1741
                // calendarID
1742
2.69M
                eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1743
2.69M
                sBuffSymbol.append(cToken);
1744
2.69M
                eState = SsGetString;
1745
2.69M
                break;
1746
4.48M
            default:
1747
4.48M
            {
1748
4.48M
                static constexpr OUString aNatNum(u"NATNUM"_ustr);
1749
4.48M
                static constexpr OUString aDBNum(u"DBNUM"_ustr);
1750
4.48M
                const OUString aBufStr( rString.toString());
1751
4.48M
                sal_Int32 nNatNumNum;
1752
4.48M
                sal_Int32 nDBNum;
1753
4.48M
                if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aNatNum, nNatNumNum) &&
1754
2.20M
                        0 <= nNatNumNum && nNatNumNum <= 19 )
1755
2.20M
                {
1756
2.20M
                    sBuffSymbol.stripStart('[');
1757
2.20M
                    sBuffSymbol.append( aBufStr.subView(--nPos, aNatNum.getLength()+1) );
1758
2.20M
                    nPos += aNatNum.getLength()+1;
1759
                    //! SymbolType is negative
1760
2.20M
                    eSymbolType = static_cast<short>(BRACKET_SYMBOLTYPE_NATNUM0 - nNatNumNum);
1761
2.20M
                    eState = SsGetPrefix;
1762
2.20M
                }
1763
2.27M
                else if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aDBNum, nDBNum) &&
1764
0
                        1 <= nDBNum && nDBNum <= 9 )
1765
0
                {
1766
0
                    sBuffSymbol.stripStart('[');
1767
0
                    sBuffSymbol.append( aBufStr.subView(--nPos, aDBNum.getLength()+1) );
1768
0
                    nPos += aDBNum.getLength()+1;
1769
                    //! SymbolType is negative
1770
0
                    eSymbolType = sal::static_int_cast< short >( BRACKET_SYMBOLTYPE_DBNUM1 - (nDBNum - 1) );
1771
0
                    eState = SsGetPrefix;
1772
0
                }
1773
2.27M
                else
1774
2.27M
                {
1775
2.27M
                    sal_Unicode cUpper = rChrCls().uppercase( aBufStr, nPos-1, 1)[0];
1776
2.27M
                    if (    cUpper == rKeywords[NF_KEY_H][0] ||     // H
1777
1.72M
                            cUpper == rKeywords[NF_KEY_MI][0] ||    // M
1778
1.72M
                            cUpper == rKeywords[NF_KEY_S][0] )      // S
1779
557k
                    {
1780
557k
                        sBuffSymbol.append(cToken);
1781
557k
                        eState = SsGetTime;
1782
557k
                        cLetter = cToken;
1783
557k
                    }
1784
1.72M
                    else
1785
1.72M
                    {
1786
1.72M
                        sBuffSymbol.stripStart('[');
1787
1.72M
                        sBuffSymbol.append(cToken);
1788
1.72M
                        eSymbolType = BRACKET_SYMBOLTYPE_COLOR;
1789
1.72M
                        eState = SsGetPrefix;
1790
1.72M
                    }
1791
2.27M
                }
1792
4.48M
            }
1793
9.35M
            }
1794
9.35M
            break;
1795
279M
        case SsGetString:
1796
279M
            if (cToken == '\"')
1797
598k
            {
1798
598k
                eState = SsGetQuoted;
1799
598k
                sBuffSymbol.append(cToken);
1800
598k
            }
1801
278M
            else if (cToken == ';' && (nPos < 2 || !IsCombiningSymbol( rString, nPos-2)))
1802
4.10M
            {
1803
4.10M
                eState = SsStop;
1804
4.10M
            }
1805
274M
            else
1806
274M
            {
1807
274M
                sBuffSymbol.append(cToken);
1808
274M
            }
1809
279M
            break;
1810
1.30M
        case SsGetQuoted:
1811
1.30M
            if (cToken == '\"')
1812
593k
            {
1813
593k
                eState = SsGetString;
1814
593k
                sBuffSymbol.append(cToken);
1815
593k
            }
1816
711k
            else
1817
711k
            {
1818
711k
                sBuffSymbol.append(cToken);
1819
711k
            }
1820
1.30M
            break;
1821
1.09M
        case SsGetTime:
1822
1.09M
            if (cToken == ']')
1823
550k
            {
1824
550k
                sBuffSymbol.append(cToken);
1825
550k
                eState = SsGetString;
1826
550k
                eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1827
550k
            }
1828
547k
            else
1829
547k
            {
1830
547k
                sal_Unicode cUpper = rChrCls().uppercase(rString.toString(), nPos-1, 1)[0];
1831
547k
                if (cUpper == rKeywords[NF_KEY_H][0] ||   // H
1832
8.14k
                    cUpper == rKeywords[NF_KEY_MI][0] ||  // M
1833
3.90k
                    cUpper == rKeywords[NF_KEY_S][0] )    // S
1834
546k
                {
1835
546k
                    if (cLetter == cToken)
1836
542k
                    {
1837
542k
                        sBuffSymbol.append(cToken);
1838
542k
                        cLetter = ' ';
1839
542k
                    }
1840
3.81k
                    else
1841
3.81k
                    {
1842
3.81k
                        sBuffSymbol.stripStart('[');
1843
3.81k
                        sBuffSymbol.append(cToken);
1844
3.81k
                        eState = SsGetPrefix;
1845
3.81k
                    }
1846
546k
                }
1847
1.02k
                else
1848
1.02k
                {
1849
1.02k
                    sBuffSymbol.stripStart('[');
1850
1.02k
                    sBuffSymbol.append(cToken);
1851
1.02k
                    eSymbolType = BRACKET_SYMBOLTYPE_COLOR;
1852
1.02k
                    eState = SsGetPrefix;
1853
1.02k
                }
1854
547k
            }
1855
1.09M
            break;
1856
23.6k
        case SsGetCon:
1857
23.6k
            switch (cToken)
1858
23.6k
            {
1859
316
            case '<':
1860
316
                eState = SsStop;
1861
316
                eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1862
316
                break;
1863
932
            case '>':
1864
932
                if (cLetter == '<')
1865
523
                {
1866
523
                    sBuffSymbol.append(cToken);
1867
523
                    cLetter = ' ';
1868
523
                    eState = SsStop;
1869
523
                    eSymbolType = NUMBERFORMAT_OP_NE;
1870
523
                }
1871
409
                else
1872
409
                {
1873
409
                    eState = SsStop;
1874
409
                    eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1875
409
                }
1876
932
                break;
1877
1.20k
            case '=':
1878
1.20k
                if (cLetter == '<')
1879
425
                {
1880
425
                    sBuffSymbol.append(cToken);
1881
425
                    cLetter = ' ';
1882
425
                    eSymbolType = NUMBERFORMAT_OP_LE;
1883
425
                }
1884
781
                else if (cLetter == '>')
1885
478
                {
1886
478
                    sBuffSymbol.append(cToken);
1887
478
                    cLetter = ' ';
1888
478
                    eSymbolType = NUMBERFORMAT_OP_GE;
1889
478
                }
1890
303
                else
1891
303
                {
1892
303
                    eState = SsStop;
1893
303
                    eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1894
303
                }
1895
1.20k
                break;
1896
1.55k
            case ' ':
1897
1.55k
                nPos--;
1898
1.55k
                rString.remove(nPos,1);
1899
1.55k
                nLen--;
1900
1.55k
                break;
1901
19.6k
            default:
1902
19.6k
                eState = SsStop;
1903
19.6k
                nPos--;
1904
19.6k
                break;
1905
23.6k
            }
1906
23.6k
            break;
1907
43.2M
        case SsGetPrefix:
1908
43.2M
            if (cToken == ']')
1909
4.09M
            {
1910
4.09M
                eState = SsStop;
1911
4.09M
            }
1912
39.1M
            else
1913
39.1M
            {
1914
39.1M
                sBuffSymbol.append(cToken);
1915
39.1M
            }
1916
43.2M
            break;
1917
0
        default:
1918
0
            break;
1919
367M
        } // of switch
1920
367M
    } // of while
1921
33.2M
    sSymbol = sBuffSymbol.makeStringAndClear();
1922
33.2M
    return eSymbolType;
1923
33.2M
}
1924
1925
void SvNumberformat::ConvertLanguage( SvNumberFormatter& rConverter,
1926
                                      LanguageType eConvertFrom,
1927
                                      LanguageType eConvertTo )
1928
576
{
1929
576
    sal_Int32 nCheckPos;
1930
576
    sal_uInt32 nKey;
1931
576
    SvNumFormatType nType = eType;
1932
576
    OUString aFormatString( sFormatstring );
1933
576
    rConverter.PutandConvertEntry( aFormatString, nCheckPos, nType,
1934
576
                                   nKey, eConvertFrom, eConvertTo, false);
1935
576
    const SvNumberformat* pFormat = rConverter.GetEntry( nKey );
1936
576
    DBG_ASSERT( pFormat, "SvNumberformat::ConvertLanguage: Conversion without format" );
1937
576
    if ( pFormat )
1938
576
    {
1939
576
        ImpCopyNumberformat( *pFormat );
1940
        // Reset values taken over from Formatter/Scanner
1941
        // pColor still points to table in temporary Formatter/Scanner
1942
576
        for (ImpSvNumFor & rFormatter : NumFor)
1943
2.30k
        {
1944
2.30k
            OUString aColorName = rFormatter.GetColorName();
1945
2.30k
            const Color* pColor = rScan.GetColor( aColorName );
1946
2.30k
            rFormatter.SetColor( pColor, aColorName );
1947
2.30k
        }
1948
576
    }
1949
576
}
1950
1951
bool SvNumberformat::HasNewCurrency() const
1952
0
{
1953
0
    for (const auto & j : NumFor)
1954
0
    {
1955
0
        if ( j.HasNewCurrency() )
1956
0
        {
1957
0
            return true;
1958
0
        }
1959
0
    }
1960
0
    return false;
1961
0
}
1962
1963
bool SvNumberformat::GetNewCurrencySymbol( OUString& rSymbol,
1964
                                           OUString& rExtension ) const
1965
143k
{
1966
143k
    for (const auto & j : NumFor)
1967
535k
    {
1968
535k
        if ( j.GetNewCurrencySymbol( rSymbol, rExtension ) )
1969
16.2k
        {
1970
16.2k
            return true;
1971
16.2k
        }
1972
535k
    }
1973
127k
    rSymbol.clear();
1974
127k
    rExtension.clear();
1975
127k
    return false;
1976
143k
}
1977
1978
// static
1979
OUString SvNumberformat::StripNewCurrencyDelimiters( const OUString& rStr )
1980
1.34M
{
1981
1.34M
    OUStringBuffer aTmp(rStr.getLength());
1982
1.34M
    sal_Int32 nStartPos, nPos, nLen;
1983
1.34M
    nLen = rStr.getLength();
1984
1.34M
    nStartPos = 0;
1985
4.04M
    while ( (nPos = rStr.indexOf( "[$", nStartPos )) >= 0 )
1986
2.69M
    {
1987
2.69M
        sal_Int32 nEnd;
1988
2.69M
        if ( (nEnd = GetQuoteEnd( rStr, nPos )) >= 0 )
1989
0
        {
1990
0
            aTmp.append(rStr.subView( nStartPos, ++nEnd - nStartPos ));
1991
0
            nStartPos = nEnd;
1992
0
        }
1993
2.69M
        else
1994
2.69M
        {
1995
2.69M
            aTmp.append(rStr.subView(nStartPos, nPos - nStartPos) );
1996
2.69M
            nStartPos = nPos + 2;
1997
2.69M
            sal_Int32 nDash;
1998
2.69M
            nEnd = nStartPos - 1;
1999
2.69M
            do
2000
2.69M
            {
2001
2.69M
                nDash = rStr.indexOf( '-', ++nEnd );
2002
2.69M
                nEnd = GetQuoteEnd( rStr, nDash );
2003
2.69M
            }
2004
2.69M
            while ( nEnd >= 0 );
2005
2.69M
            sal_Int32 nClose;
2006
2.69M
            nEnd = nStartPos - 1;
2007
2.69M
            do
2008
2.69M
            {
2009
2.69M
                nClose = rStr.indexOf( ']', ++nEnd );
2010
2.69M
                nEnd = GetQuoteEnd( rStr, nClose );
2011
2.69M
            }
2012
2.69M
            while ( nEnd >= 0 );
2013
2014
2.69M
            if(nClose < 0)
2015
0
            {
2016
                /* there should always be a closing ]
2017
                 * but the old String class would have hidden
2018
                 * that. so be conservative too
2019
                 */
2020
0
                nClose = nLen;
2021
0
            }
2022
2023
2.69M
            nPos = nClose;
2024
2.69M
            if(nDash >= 0 && nDash < nClose)
2025
2.69M
            {
2026
2.69M
                nPos = nDash;
2027
2.69M
            }
2028
2.69M
            aTmp.append(rStr.subView(nStartPos, nPos - nStartPos) );
2029
2.69M
            nStartPos = nClose + 1;
2030
2.69M
        }
2031
2.69M
    }
2032
1.34M
    if ( nLen > nStartPos )
2033
1.34M
    {
2034
1.34M
        aTmp.append(rStr.subView(nStartPos, nLen - nStartPos) );
2035
1.34M
    }
2036
1.34M
    return aTmp.makeStringAndClear();
2037
1.34M
}
2038
2039
void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUStringBuffer& rOutString,
2040
                                          const NativeNumberWrapper& rNatNum) const
2041
14.5k
{
2042
14.5k
    OUString sTemp;
2043
14.5k
    ImpGetOutputStandard(fNumber, sTemp, rNatNum);
2044
14.5k
    rOutString = sTemp;
2045
14.5k
}
2046
2047
void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUString& rOutString,
2048
                                          const NativeNumberWrapper& rNatNum) const
2049
14.5k
{
2050
14.5k
    sal_uInt16 nStandardPrec = rScan.GetStandardPrec();
2051
2052
14.5k
    if ( fabs(fNumber) > EXP_ABS_UPPER_BOUND )
2053
18
    {
2054
18
        nStandardPrec = ::std::min(nStandardPrec, static_cast<sal_uInt16>(14)); // limits to 14 decimals
2055
18
        rOutString = ::rtl::math::doubleToUString( fNumber,
2056
18
                                                  rtl_math_StringFormat_E2, nStandardPrec /*2*/,
2057
18
                                                  GetCurrentLanguageData().GetNumDecimalSep()[0]);
2058
18
    }
2059
14.4k
    else
2060
14.4k
    {
2061
14.4k
        ImpGetOutputStdToPrecision(fNumber, rOutString, nStandardPrec, rNatNum);
2062
14.4k
    }
2063
14.5k
}
2064
2065
namespace
2066
{
2067
2068
template<typename T>
2069
bool checkForAll0s(const T& rString, sal_Int32 nIdx=0)
2070
1.15k
{
2071
1.15k
    if (nIdx>=rString.getLength())
2072
0
        return false;
2073
2074
1.15k
    do
2075
1.23k
    {
2076
1.23k
        if (rString[nIdx]!='0')
2077
1.13k
            return false;
2078
1.23k
    }
2079
1.15k
    while (++nIdx<rString.getLength());
2080
2081
20
    return true;
2082
1.15k
}
zformat.cxx:bool (anonymous namespace)::checkForAll0s<rtl::OUString>(rtl::OUString const&, int)
Line
Count
Source
2070
634
{
2071
634
    if (nIdx>=rString.getLength())
2072
0
        return false;
2073
2074
634
    do
2075
692
    {
2076
692
        if (rString[nIdx]!='0')
2077
624
            return false;
2078
692
    }
2079
634
    while (++nIdx<rString.getLength());
2080
2081
10
    return true;
2082
634
}
zformat.cxx:bool (anonymous namespace)::checkForAll0s<rtl::OUStringBuffer>(rtl::OUStringBuffer const&, int)
Line
Count
Source
2070
525
{
2071
525
    if (nIdx>=rString.getLength())
2072
0
        return false;
2073
2074
525
    do
2075
541
    {
2076
541
        if (rString[nIdx]!='0')
2077
515
            return false;
2078
541
    }
2079
525
    while (++nIdx<rString.getLength());
2080
2081
10
    return true;
2082
525
}
2083
2084
OUString impTransliterateImpl(const OUString& rStr,
2085
                              const SvNumberNatNum& rNum,
2086
                              const NativeNumberWrapper& rNatNum)
2087
127
{
2088
127
    css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
2089
127
    return rNatNum.getNativeNumberStringParams(rStr, aLocale, rNum.GetNatNum(), rNum.GetParams());
2090
127
}
2091
2092
void impTransliterateImpl(OUStringBuffer& rStr,
2093
                          const SvNumberNatNum& rNum,
2094
                          const NativeNumberWrapper& rNatNum)
2095
690
{
2096
690
    css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
2097
2098
690
    rStr = rNatNum.getNativeNumberStringParams(
2099
690
        OUString::unacquired(rStr), aLocale, rNum.GetNatNum(), rNum.GetParams());
2100
690
}
2101
2102
OUString impTransliterate(const OUString& rStr, const SvNumberNatNum& rNum, const NativeNumberWrapper& rNatNum)
2103
34.6k
{
2104
34.6k
    return rNum.IsComplete() ? impTransliterateImpl(rStr, rNum, rNatNum) : rStr;
2105
34.6k
}
2106
2107
void impTransliterate(OUStringBuffer& rStr, const SvNumberNatNum& rNum, const NativeNumberWrapper& rNatNum)
2108
75.8k
{
2109
75.8k
    if(rNum.IsComplete())
2110
690
    {
2111
690
        impTransliterateImpl(rStr, rNum, rNatNum);
2112
690
    }
2113
75.8k
}
2114
2115
}
2116
2117
void SvNumberformat::ImpGetOutputStdToPrecision(double& rNumber, OUString& rOutString, sal_uInt16 nPrecision,
2118
                                                const NativeNumberWrapper& rNatNum) const
2119
14.4k
{
2120
    // Make sure the precision doesn't go over the maximum allowable precision.
2121
14.4k
    nPrecision = ::std::min(UPPER_PRECISION, nPrecision);
2122
2123
    // We decided to strip trailing zeros unconditionally, since binary
2124
    // double-precision rounding error makes it impossible to determine e.g.
2125
    // whether 844.10000000000002273737 is what the user has typed, or the
2126
    // user has typed 844.1 but IEEE 754 represents it that way internally.
2127
2128
14.4k
    rOutString = ::rtl::math::doubleToUString( rNumber,
2129
14.4k
                                               rtl_math_StringFormat_F, nPrecision /*2*/,
2130
14.4k
                                               GetCurrentLanguageData().GetNumDecimalSep()[0], true );
2131
14.4k
    if (rOutString[0] == '-' && checkForAll0s(rOutString, 1))
2132
10
    {
2133
10
        rOutString = comphelper::string::stripStart(rOutString, '-'); // not -0
2134
10
    }
2135
14.4k
    rOutString = ::impTransliterate(rOutString, NumFor[0].GetNatNum(), rNatNum);
2136
14.4k
}
2137
2138
void SvNumberformat::ImpGetOutputInputLine(double fNumber, OUString& OutString) const
2139
43.1k
{
2140
43.1k
    bool bModified = false;
2141
43.1k
    if ( (eType & SvNumFormatType::PERCENT) && (fabs(fNumber) < D_MAX_D_BY_100))
2142
0
    {
2143
0
        if (fNumber == 0.0)
2144
0
        {
2145
0
            OutString = "0%";
2146
0
            return;
2147
0
        }
2148
0
        fNumber *= 100;
2149
0
        bModified = true;
2150
0
    }
2151
2152
43.1k
    if (fNumber == 0.0)
2153
37.5k
    {
2154
37.5k
        OutString = "0";
2155
37.5k
        return;
2156
37.5k
    }
2157
2158
5.58k
    OutString = ::rtl::math::doubleToUString( fNumber,
2159
5.58k
                                              rtl_math_StringFormat_Automatic,
2160
5.58k
                                              rtl_math_DecimalPlaces_Max,
2161
5.58k
                                              GetCurrentLanguageData().GetNumDecimalSep()[0], true );
2162
2163
5.58k
    if ( eType & SvNumFormatType::PERCENT && bModified)
2164
0
    {
2165
0
        OutString += "%";
2166
0
    }
2167
5.58k
}
2168
2169
short SvNumberformat::ImpCheckCondition(double fNumber,
2170
                                        double fLimit,
2171
                                        SvNumberformatLimitOps eOp)
2172
431k
{
2173
431k
    switch(eOp)
2174
431k
    {
2175
410k
    case NUMBERFORMAT_OP_NO:
2176
410k
        return -1;
2177
0
    case NUMBERFORMAT_OP_EQ:
2178
0
        return static_cast<short>(fNumber == fLimit);
2179
0
    case NUMBERFORMAT_OP_NE:
2180
0
        return static_cast<short>(fNumber != fLimit);
2181
2.95k
    case NUMBERFORMAT_OP_LT:
2182
2.95k
        return static_cast<short>(fNumber <  fLimit);
2183
0
    case NUMBERFORMAT_OP_LE:
2184
0
        return static_cast<short>(fNumber <= fLimit);
2185
8.35k
    case NUMBERFORMAT_OP_GT:
2186
8.35k
        return static_cast<short>(fNumber >  fLimit);
2187
9.49k
    case NUMBERFORMAT_OP_GE:
2188
9.49k
        return static_cast<short>(fNumber >= fLimit);
2189
0
    default:
2190
0
        return -1;
2191
431k
    }
2192
431k
}
2193
2194
static bool lcl_appendStarFillChar( OUStringBuffer& rBuf, std::u16string_view rStr )
2195
0
{
2196
    // Right during user input the star symbol is the very
2197
    // last character before the user enters another one.
2198
0
    if (rStr.size() > 1)
2199
0
    {
2200
0
        rBuf.append(u'\x001B');
2201
0
        rBuf.append(rStr[1]);
2202
0
        return true;
2203
0
    }
2204
0
    return false;
2205
0
}
2206
2207
static bool lcl_insertStarFillChar( OUStringBuffer& rBuf, sal_Int32 nPos, std::u16string_view rStr )
2208
0
{
2209
0
    if (rStr.size() > 1)
2210
0
    {
2211
0
        rBuf.insert( nPos, rStr[1]);
2212
0
        rBuf.insert( nPos, u'\x001B');
2213
0
        return true;
2214
0
    }
2215
0
    return false;
2216
0
}
2217
2218
void SvNumberformat::GetOutputString(std::u16string_view sString,
2219
                                     OUString& OutString,
2220
                                     const Color** ppColor,
2221
                                     bool bStarFlag) const
2222
2.93k
{
2223
2.93k
    OUStringBuffer sOutBuff;
2224
2.93k
    sal_uInt16 nIx;
2225
2.93k
    if (eType & SvNumFormatType::TEXT)
2226
2.54k
    {
2227
2.54k
        nIx = 0;
2228
2.54k
    }
2229
391
    else if (NumFor[3].GetCount() > 0)
2230
391
    {
2231
391
        nIx = 3;
2232
391
    }
2233
0
    else
2234
0
    {
2235
0
        *ppColor = nullptr; // no change of color
2236
0
        return;
2237
0
    }
2238
2.93k
    *ppColor = NumFor[nIx].GetColor();
2239
2.93k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2240
2.93k
    if (rInfo.eScannedType == SvNumFormatType::TEXT)
2241
2.93k
    {
2242
2.93k
        const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2243
6.75k
        for (sal_uInt16 i = 0; i < nCnt; i++)
2244
3.82k
        {
2245
3.82k
            switch (rInfo.nTypeArray[i])
2246
3.82k
            {
2247
0
            case NF_SYMBOLTYPE_STAR:
2248
0
                if( bStarFlag )
2249
0
                {
2250
0
                    lcl_appendStarFillChar( sOutBuff, rInfo.sStrArray[i]);
2251
0
                }
2252
0
                break;
2253
552
            case NF_SYMBOLTYPE_BLANK:
2254
552
                if (rInfo.sStrArray[i].getLength() >= 2)
2255
552
                    InsertBlanks( sOutBuff, sOutBuff.getLength(), rInfo.sStrArray[i][1] );
2256
552
                break;
2257
0
            case NF_KEY_GENERAL :   // #77026# "General" is the same as "@"
2258
2.93k
            case NF_SYMBOLTYPE_DEL :
2259
2.93k
                sOutBuff.append(sString);
2260
2.93k
                break;
2261
336
            default:
2262
336
                sOutBuff.append(rInfo.sStrArray[i]);
2263
3.82k
            }
2264
3.82k
        }
2265
2.93k
    }
2266
2.93k
    OutString = sOutBuff.makeStringAndClear();
2267
2.93k
}
2268
2269
namespace {
2270
2271
void lcl_GetOutputStringScientific(double fNumber, sal_uInt16 nCharCount,
2272
                                   const SvNFLanguageData& rLanguageData, OUString& rOutString)
2273
0
{
2274
0
    bool bSign = std::signbit(fNumber);
2275
2276
    // 1.000E+015 (one digit and the decimal point, and the two chars +
2277
    // nExpDigit for the exponential part, totalling 6 or 7).
2278
0
    double fExp = log10( fabs(fNumber) );
2279
0
    if( fExp < 0.0 )
2280
0
      fExp = 1.0 - fExp;
2281
0
    sal_uInt16 nCharFormat = 6 + (fExp >= 100.0 ? 1 : 0);
2282
0
    sal_uInt16 nPrec = nCharCount > nCharFormat ? nCharCount - nCharFormat : 0;
2283
0
    if (nPrec && bSign)
2284
0
    {
2285
        // Make room for the negative sign.
2286
0
        --nPrec;
2287
0
    }
2288
0
    nPrec = ::std::min(nPrec, static_cast<sal_uInt16>(14)); // limit to 14 decimals.
2289
2290
0
    rOutString = ::rtl::math::doubleToUString(fNumber, rtl_math_StringFormat_E2,
2291
0
                                              nPrec, rLanguageData.GetNumDecimalSep()[0], true );
2292
0
}
2293
2294
OUString lcl_GetPercentString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2295
2.52k
{
2296
2.52k
    sal_Int32 i;
2297
2.52k
    OUStringBuffer aPercentString;
2298
12.6k
    for( i = 0; i < nCnt; i++ )
2299
10.0k
    {
2300
10.0k
        if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_PERCENT )
2301
2.52k
        {
2302
2.52k
            aPercentString.append( rInfo.sStrArray[i] );
2303
2.52k
            bool bStringFound = false;
2304
2.52k
            for( i--; i >= 0 && rInfo.nTypeArray[i] == NF_SYMBOLTYPE_STRING ; i-- )
2305
0
            {
2306
0
                if( !bStringFound )
2307
0
                {
2308
0
                    bStringFound = true;
2309
0
                    aPercentString.insert( 0, "\"" );
2310
0
                }
2311
0
                aPercentString.insert( 0, rInfo.sStrArray[i] );
2312
0
            }
2313
2.52k
            i = nCnt;
2314
2.52k
            if( bStringFound )
2315
0
                aPercentString.insert( 0, "\"" );
2316
2.52k
        }
2317
10.0k
    }
2318
2.52k
    return aPercentString.makeStringAndClear();
2319
2.52k
}
2320
2321
OUString lcl_GetDenominatorString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2322
3.38k
{
2323
3.38k
    sal_Int32 i;
2324
3.38k
    OUStringBuffer aDenominatorString;
2325
13.9k
    for( i = 0; i < nCnt; i++ )
2326
10.6k
    {
2327
10.6k
        if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC )
2328
3.38k
        {
2329
4.75k
            while ( ( ++i < nCnt ) && rInfo.nTypeArray[i] != NF_SYMBOLTYPE_FRAC_FDIV
2330
3.74k
                                   && rInfo.nTypeArray[i] != NF_SYMBOLTYPE_DIGIT );
2331
17.7k
            for( ; i < nCnt; i++ )
2332
14.3k
            {
2333
14.3k
                if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC_FDIV || rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT )
2334
11.4k
                    aDenominatorString.append( rInfo.sStrArray[i] );
2335
2.91k
                else
2336
2.91k
                    i = nCnt;
2337
14.3k
            }
2338
3.38k
        }
2339
10.6k
    }
2340
3.38k
    return aDenominatorString.makeStringAndClear();
2341
3.38k
}
2342
2343
OUString lcl_GetNumeratorString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2344
1.94k
{
2345
1.94k
    sal_Int32 i;
2346
1.94k
    OUStringBuffer aNumeratorString;
2347
8.00k
    for( i = 0; i < nCnt; i++ )
2348
6.05k
    {
2349
6.05k
        if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC )
2350
1.94k
        {
2351
5.21k
            for( i--; i >= 0 && rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT ; i-- )
2352
3.27k
            {
2353
3.27k
                aNumeratorString.insert( 0, rInfo.sStrArray[i] );
2354
3.27k
            }
2355
1.94k
            i = nCnt;
2356
1.94k
        }
2357
6.05k
    }
2358
1.94k
    return aNumeratorString.makeStringAndClear();
2359
1.94k
}
2360
2361
OUString lcl_GetFractionIntegerString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2362
1.94k
{
2363
1.94k
    sal_Int32 i;
2364
1.94k
    OUStringBuffer aIntegerString;
2365
23.8k
    for( i = 0; i < nCnt; i++ )
2366
21.8k
    {
2367
21.8k
        if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRACBLANK )
2368
740
        {
2369
1.13k
            for( i--; i >= 0 && ( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_DIGIT
2370
399
                               || rInfo.nTypeArray[i] == NF_SYMBOLTYPE_THSEP ); i-- )
2371
399
            {
2372
399
                aIntegerString.insert( 0, rInfo.sStrArray[i] );
2373
399
            }
2374
740
            i = nCnt;
2375
740
        }
2376
21.8k
    }
2377
1.94k
    return aIntegerString.makeStringAndClear();
2378
1.94k
}
2379
2380
OUString lcl_GetIntegerFractionDelimiterString(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nCnt)
2381
0
{
2382
0
    sal_uInt16 i;
2383
0
    for( i = 0; i < nCnt; i++ )
2384
0
    {
2385
0
        if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRACBLANK )
2386
0
        {
2387
0
            return rInfo.sStrArray[i];
2388
0
        }
2389
0
    }
2390
0
    return OUString();
2391
0
}
2392
2393
}
2394
2395
OUString SvNumberformat::GetPercentString( sal_uInt16 nNumFor ) const
2396
2.52k
{
2397
2.52k
    const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2398
2.52k
    sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2399
2.52k
    return lcl_GetPercentString( rInfo, nCnt );
2400
2.52k
}
2401
2402
OUString SvNumberformat::GetDenominatorString( sal_uInt16 nNumFor ) const
2403
0
{
2404
0
    const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2405
0
    sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2406
0
    return lcl_GetDenominatorString( rInfo, nCnt );
2407
0
}
2408
2409
OUString SvNumberformat::GetNumeratorString( sal_uInt16 nNumFor ) const
2410
0
{
2411
0
    const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2412
0
    sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2413
0
    return lcl_GetNumeratorString( rInfo, nCnt );
2414
0
}
2415
2416
OUString SvNumberformat::GetIntegerFractionDelimiterString( sal_uInt16 nNumFor ) const
2417
0
{
2418
0
    const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2419
0
    sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
2420
0
    return lcl_GetIntegerFractionDelimiterString( rInfo, nCnt );
2421
0
}
2422
2423
bool SvNumberformat::GetOutputString(double fNumber, sal_uInt16 nCharCount, OUString& rOutString, const NativeNumberWrapper& rNatNum) const
2424
0
{
2425
0
    if (eType != SvNumFormatType::NUMBER)
2426
0
    {
2427
0
        return false;
2428
0
    }
2429
0
    double fTestNum = fNumber;
2430
0
    bool bSign = std::signbit(fTestNum);
2431
0
    if (bSign)
2432
0
    {
2433
0
        fTestNum = -fTestNum;
2434
0
    }
2435
0
    if (fTestNum < EXP_LOWER_BOUND)
2436
0
    {
2437
0
        lcl_GetOutputStringScientific(fNumber, nCharCount, GetCurrentLanguageData(), rOutString);
2438
0
        return true;
2439
0
    }
2440
2441
0
    double fExp = log10(fTestNum);
2442
    // Values < 1.0 always have one digit before the decimal point.
2443
0
    sal_uInt16 nDigitPre = fExp >= 0.0 ? static_cast<sal_uInt16>(ceil(fExp)) : 1;
2444
2445
0
    if (nDigitPre > 15)
2446
0
    {
2447
0
        lcl_GetOutputStringScientific(fNumber, nCharCount, GetCurrentLanguageData(), rOutString);
2448
0
        return true;
2449
0
    }
2450
2451
0
    sal_uInt16 nPrec = nCharCount >= nDigitPre ? nCharCount - nDigitPre : 0;
2452
0
    if (nPrec && bSign)
2453
0
    {
2454
        // Subtract the negative sign.
2455
0
        --nPrec;
2456
0
    }
2457
0
    if (nPrec)
2458
0
    {
2459
        // Subtract the decimal point.
2460
0
        --nPrec;
2461
0
    }
2462
0
    ImpGetOutputStdToPrecision(fNumber, rOutString, nPrec, rNatNum);
2463
0
    if (rOutString.getLength() > nCharCount)
2464
0
    {
2465
        // String still wider than desired.  Switch to scientific notation.
2466
0
        lcl_GetOutputStringScientific(fNumber, nCharCount, GetCurrentLanguageData(), rOutString);
2467
0
    }
2468
0
    return true;
2469
0
}
2470
2471
sal_uInt16 SvNumberformat::GetSubformatIndex (double fNumber ) const
2472
426k
{
2473
426k
    sal_uInt16 nIx; // Index of the partial format
2474
426k
    double fLimit_1 = fLimit1;
2475
426k
    short nCheck = ImpCheckCondition(fNumber, fLimit_1, eOp1);
2476
426k
    if (nCheck == -1 || nCheck == 1) // Only 1 String or True
2477
421k
    {
2478
421k
        nIx = 0;
2479
421k
    }
2480
4.77k
    else
2481
4.77k
    {
2482
4.77k
        double fLimit_2 = fLimit2;
2483
4.77k
        nCheck = ImpCheckCondition(fNumber, fLimit_2, eOp2);
2484
4.77k
        if (nCheck == -1 || nCheck == 1)
2485
2.83k
        {
2486
2.83k
            nIx = 1;
2487
2.83k
        }
2488
1.94k
        else
2489
1.94k
        {
2490
1.94k
            nIx = 2;
2491
1.94k
        }
2492
4.77k
    }
2493
426k
    return nIx;
2494
426k
}
2495
2496
bool SvNumberformat::GetOutputString(double fNumber,
2497
                                     OUString& OutString,
2498
                                     const Color** ppColor,
2499
                                     const NativeNumberWrapper& rNatNum,
2500
                                     const SvNFLanguageData& rCurrentLang,
2501
                                     bool bStarFlag) const
2502
755k
{
2503
755k
    bool bRes = false;
2504
755k
    OutString.clear();
2505
755k
    *ppColor = nullptr; // No color change
2506
755k
    if (eType & SvNumFormatType::LOGICAL && sFormatstring == rScan.GetKeywords()[NF_KEY_BOOLEAN])
2507
6.64k
    {
2508
6.64k
        if (fNumber)
2509
4.89k
        {
2510
4.89k
            OutString = rScan.GetTrueString();
2511
4.89k
        }
2512
1.74k
        else
2513
1.74k
        {
2514
1.74k
            OutString = rScan.GetFalseString();
2515
1.74k
        }
2516
6.64k
        return false;
2517
6.64k
    }
2518
748k
    OUStringBuffer sBuff(64);
2519
748k
    if (eType & SvNumFormatType::TEXT)
2520
3
    {
2521
3
        ImpGetOutputStandard(fNumber, sBuff, rNatNum);
2522
3
        OutString = sBuff.makeStringAndClear();
2523
3
        return false;
2524
3
    }
2525
748k
    bool bHadStandard = false;
2526
748k
    if (bStandard) // Individual standard formats
2527
322k
    {
2528
322k
        if (rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION) // All number format InputLine
2529
43.1k
        {
2530
43.1k
            ImpGetOutputInputLine(fNumber, OutString);
2531
43.1k
            return false;
2532
43.1k
        }
2533
279k
        switch (eType)
2534
279k
        {
2535
263k
        case SvNumFormatType::NUMBER: // Standard number format
2536
263k
            if (rScan.GetStandardPrec() == SvNumberFormatter::UNLIMITED_PRECISION)
2537
248k
            {
2538
248k
                if (std::signbit(fNumber))
2539
6.35k
                {
2540
6.35k
                    if (!(fNumber < 0.0))
2541
158
                        fNumber = -fNumber;     // do not display -0.0
2542
6.35k
                }
2543
248k
                if (fNumber == 0.0)
2544
52.3k
                {
2545
52.3k
                    OutString = "0";
2546
52.3k
                }
2547
196k
                else if (fNumber < 1.0 && fNumber > -1.0)
2548
7.89k
                {
2549
                    // Decide whether to display as 0.000000123... or 1.23...e-07
2550
7.89k
                    bool bFix = (fNumber < -EXP_LOWER_BOUND || EXP_LOWER_BOUND < fNumber);
2551
7.89k
                    if (!bFix)
2552
450
                    {
2553
                        // Arbitrary, not too many 0s visually, start E2 at 1E-10.
2554
450
                        constexpr sal_Int32 kMaxExp = 9;
2555
450
                        const sal_Int32 nExp = static_cast<sal_Int32>(ceil( -log10( fabs( fNumber))));
2556
450
                        if (nExp <= kMaxExp && rtl::math::approxEqual(
2557
368
                                    rtl::math::round( fNumber, 16), rtl::math::round( fNumber, nExp + 16)))
2558
228
                        {
2559
                            // Not too many significant digits or accuracy
2560
                            // artefacts, otherwise leave everything to E2
2561
                            // format.
2562
228
                            bFix = true;
2563
228
                        }
2564
450
                    }
2565
7.89k
                    if (bFix)
2566
7.67k
                        OutString = ::rtl::math::doubleToUString( fNumber,
2567
7.67k
                                rtl_math_StringFormat_F,
2568
7.67k
                                rtl_math_DecimalPlaces_Max,
2569
7.67k
                                GetCurrentLanguageData().GetNumDecimalSep()[0], true);
2570
222
                    else
2571
222
                        OutString = ::rtl::math::doubleToUString( fNumber,
2572
222
                                rtl_math_StringFormat_E2,
2573
222
                                rtl_math_DecimalPlaces_Max,
2574
222
                                GetCurrentLanguageData().GetNumDecimalSep()[0], true);
2575
7.89k
                }
2576
188k
                else
2577
188k
                {
2578
188k
                    OutString = ::rtl::math::doubleToUString( fNumber,
2579
188k
                                rtl_math_StringFormat_Automatic,
2580
188k
                                rtl_math_DecimalPlaces_Max,
2581
188k
                                GetCurrentLanguageData().GetNumDecimalSep()[0], true);
2582
188k
                }
2583
248k
                return false;
2584
248k
            }
2585
14.4k
            ImpGetOutputStandard(fNumber, sBuff, rNatNum);
2586
14.4k
            bHadStandard = true;
2587
14.4k
            break;
2588
15.6k
        case SvNumFormatType::DATE:
2589
15.6k
            bRes |= ImpGetDateOutput(fNumber, 0, bStarFlag, rNatNum, rCurrentLang, sBuff);
2590
15.6k
            bHadStandard = true;
2591
15.6k
            break;
2592
25
        case SvNumFormatType::TIME:
2593
25
            bRes |= ImpGetTimeOutput(fNumber, 0, bStarFlag, rNatNum, rCurrentLang, sBuff);
2594
25
            bHadStandard = true;
2595
25
            break;
2596
19
        case SvNumFormatType::DATETIME:
2597
19
            bRes |= ImpGetDateTimeOutput(fNumber, 0, bStarFlag, rNatNum, rCurrentLang, sBuff);
2598
19
            bHadStandard = true;
2599
19
            break;
2600
481
        default: break;
2601
279k
        }
2602
279k
    }
2603
456k
    if ( !bHadStandard )
2604
426k
    {
2605
426k
        sal_uInt16 nIx = GetSubformatIndex ( fNumber ); // Index of the partial format
2606
426k
        if (fNumber < 0.0 &&
2607
3.62k
                ((nIx == 0 && IsFirstSubformatRealNegative()) || // 1st, usually positive subformat
2608
3.59k
                 (nIx == 1 && IsSecondSubformatRealNegative()))) // 2nd, usually negative subformat
2609
2.84k
        {
2610
2.84k
            fNumber = -fNumber; // eliminate sign
2611
2.84k
        }
2612
426k
        *ppColor = NumFor[nIx].GetColor();
2613
426k
        const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2614
426k
        const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2615
426k
        if (nCnt == 0 && rInfo.eScannedType == SvNumFormatType::EMPTY)
2616
337
        {
2617
337
            return false; // Empty => nothing
2618
337
        }
2619
426k
        else if (nCnt == 0) // Else Standard Format
2620
0
        {
2621
0
            ImpGetOutputStandard(fNumber, sBuff, rNatNum);
2622
0
            OutString = sBuff.makeStringAndClear();
2623
0
            return false;
2624
0
        }
2625
426k
        switch (rInfo.eScannedType)
2626
426k
        {
2627
149
        case SvNumFormatType::TEXT:
2628
485
        case SvNumFormatType::DEFINED:
2629
1.61k
            for (sal_uInt16 i = 0; i < nCnt; i++)
2630
1.13k
            {
2631
1.13k
                switch (rInfo.nTypeArray[i])
2632
1.13k
                {
2633
178
                case NF_SYMBOLTYPE_STAR:
2634
178
                    if( bStarFlag )
2635
0
                    {
2636
0
                        bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
2637
0
                    }
2638
178
                    break;
2639
18
                case NF_SYMBOLTYPE_BLANK:
2640
18
                    if (rInfo.sStrArray[i].getLength() >= 2)
2641
10
                        InsertBlanks(sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
2642
18
                    break;
2643
786
                case NF_SYMBOLTYPE_STRING:
2644
786
                case NF_SYMBOLTYPE_CURRENCY:
2645
786
                    sBuff.append(rInfo.sStrArray[i]);
2646
786
                    break;
2647
0
                case NF_SYMBOLTYPE_THSEP:
2648
0
                    if (rInfo.nThousand == 0)
2649
0
                    {
2650
0
                        sBuff.append(rInfo.sStrArray[i]);
2651
0
                    }
2652
0
                    break;
2653
149
                default:
2654
149
                    break;
2655
1.13k
                }
2656
1.13k
            }
2657
485
            break;
2658
330k
        case SvNumFormatType::DATE:
2659
330k
            bRes |= ImpGetDateOutput(fNumber, nIx, bStarFlag, rNatNum, rCurrentLang, sBuff);
2660
330k
            break;
2661
7.35k
        case SvNumFormatType::TIME:
2662
7.35k
            bRes |= ImpGetTimeOutput(fNumber, nIx, bStarFlag, rNatNum, rCurrentLang, sBuff);
2663
7.35k
                break;
2664
6.69k
        case SvNumFormatType::DATETIME:
2665
6.69k
            bRes |= ImpGetDateTimeOutput(fNumber, nIx, bStarFlag, rNatNum, rCurrentLang, sBuff);
2666
6.69k
            break;
2667
65.7k
        case SvNumFormatType::NUMBER:
2668
69.9k
        case SvNumFormatType::PERCENT:
2669
74.7k
        case SvNumFormatType::CURRENCY:
2670
74.7k
            bRes |= ImpGetNumberOutput(fNumber, nIx, bStarFlag, rNatNum, sBuff);
2671
74.7k
            break;
2672
31
        case SvNumFormatType::LOGICAL:
2673
31
            bRes |= ImpGetLogicalOutput(fNumber, nIx, rNatNum, sBuff);
2674
31
            break;
2675
1.94k
        case SvNumFormatType::FRACTION:
2676
1.94k
            bRes |= ImpGetFractionOutput(fNumber, nIx, bStarFlag, rNatNum, sBuff);
2677
1.94k
            break;
2678
5.00k
        case SvNumFormatType::SCIENTIFIC:
2679
5.00k
            bRes |= ImpGetScientificOutput(fNumber, nIx, bStarFlag, rNatNum, sBuff);
2680
5.00k
            break;
2681
1
        default: break;
2682
426k
        }
2683
426k
    }
2684
456k
    OutString = sBuff.makeStringAndClear();
2685
456k
    return bRes;
2686
456k
}
2687
2688
bool SvNumberformat::ImpGetScientificOutput(double fNumber,
2689
                                            sal_uInt16 nIx,
2690
                                            bool bStarFlag,
2691
                                            const NativeNumberWrapper& rNatNum,
2692
                                            OUStringBuffer& sStr) const
2693
5.00k
{
2694
5.00k
    bool bRes = false;
2695
5.00k
    bool bSign = false;
2696
2697
5.00k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2698
5.00k
    const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2699
2700
5.00k
    if (fNumber < 0)
2701
182
    {
2702
182
        if (nIx == 0) // Not in the ones at the end
2703
182
        {
2704
182
            bSign = true; // Formats
2705
182
        }
2706
182
        fNumber = -fNumber;
2707
182
    }
2708
2709
5.00k
    sStr = ::rtl::math::doubleToUString( fNumber,
2710
5.00k
                                         rtl_math_StringFormat_E,
2711
5.00k
                                         rInfo.nCntPre + rInfo.nCntPost - 1, '.' );
2712
5.00k
    OUStringBuffer ExpStr;
2713
5.00k
    short nExpSign = 1;
2714
5.00k
    sal_Int32 nExPos = sStr.indexOf('E');
2715
5.00k
    sal_Int32 nDecPos = -1;
2716
2717
5.00k
    if ( nExPos >= 0 )
2718
5.00k
    {
2719
        // split into mantissa and exponent and get rid of "E+" or "E-"
2720
5.00k
        sal_Int32 nExpStart = nExPos + 1;
2721
2722
5.00k
        switch ( sStr[ nExpStart ] )
2723
5.00k
        {
2724
398
        case '-' :
2725
398
            nExpSign = -1;
2726
398
            [[fallthrough]];
2727
5.00k
        case '+' :
2728
5.00k
            ++nExpStart;
2729
5.00k
            break;
2730
5.00k
        }
2731
5.00k
        ExpStr = sStr.subView( nExpStart );    // part following the "E+"
2732
5.00k
        sStr.truncate( nExPos );
2733
2734
5.00k
        if ( rInfo.nCntPre != 1 ) // rescale Exp
2735
219
        {
2736
219
            sal_Int32 nExp = OUString::unacquired(ExpStr).toInt32() * nExpSign;
2737
219
            sal_Int32 nRescale = (rInfo.nCntPre != 0) ? nExp % static_cast<sal_Int32>(rInfo.nCntPre) : -1;
2738
219
            if( nRescale < 0 && rInfo.nCntPre != 0 )
2739
76
                nRescale += static_cast<sal_Int32>(rInfo.nCntPre);
2740
219
            nExp -= nRescale;
2741
219
            if ( nExp < 0 )
2742
94
            {
2743
94
                nExpSign = -1;
2744
94
                nExp = -nExp;
2745
94
            }
2746
125
            else
2747
125
            {
2748
125
                nExpSign = 1;
2749
125
            }
2750
219
            ExpStr = OUString::number( nExp );
2751
219
            const sal_Unicode cFirstDigit = sStr[0];
2752
            // rescale mantissa
2753
219
            sStr = ::rtl::math::doubleToUString( fNumber,
2754
219
                                         rtl_math_StringFormat_E,
2755
219
                                         nRescale + rInfo.nCntPost, '.' );
2756
2757
            // sStr now may contain a rounded-up value shifted into the next
2758
            // magnitude, for example 1.000E+02 (4 digits) for fNumber 99.995
2759
            // (9.9995E+02 rounded to 3 decimals) but we want the final result
2760
            // to be 100.00E+00 (5 digits), so for the following fill routines
2761
            // below to work correctly append a zero decimal.
2762
            /* TODO: this is awkward, could an engineering notation mode be
2763
             * introduced to rtl_math_doubleToUString()? */
2764
219
            sStr.truncate( sStr.indexOf('E') );
2765
219
            if (sStr[0] == '1' && cFirstDigit != '1')
2766
0
                sStr.append('0');
2767
219
        }
2768
2769
        // cut any decimal delimiter
2770
5.00k
        sal_Int32 index = 0;
2771
2772
5.62k
        while((index = sStr.indexOf('.', index)) >= 0)
2773
619
        {
2774
619
            if (nDecPos < 0)
2775
619
                nDecPos = index;
2776
619
            sStr.remove(index, 1);
2777
619
        }
2778
5.00k
    }
2779
2780
5.00k
    sal_uInt16 j = nCnt-1;  // Last symbol
2781
5.00k
    sal_Int32 k = ExpStr.getLength() - 1;  // Position in ExpStr
2782
5.00k
    sal_Int32 nZeros = 0; // Erase leading zeros
2783
2784
    // erase all leading zeros except last one
2785
14.4k
    while (nZeros < k && ExpStr[nZeros] == '0')
2786
9.47k
    {
2787
9.47k
        ++nZeros;
2788
9.47k
    }
2789
5.00k
    if (nZeros)
2790
4.76k
    {
2791
4.76k
        ExpStr.remove( 0, nZeros);
2792
4.76k
    }
2793
2794
    // restore leading zeros or blanks according to format '0' or '?' tdf#156449
2795
5.00k
    bRes |= ImpNumberFill(rNatNum, ExpStr, fNumber, k, j, nIx, NF_SYMBOLTYPE_EXP, bStarFlag);
2796
2797
5.00k
    bool bCont = true;
2798
2799
5.00k
    if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_EXP)
2800
5.00k
    {
2801
5.00k
        const OUString& rStr = rInfo.sStrArray[j];
2802
5.00k
        if (nExpSign == -1)
2803
398
        {
2804
398
            ExpStr.insert(0, '-');
2805
398
        }
2806
4.60k
        else if (rStr.getLength() > 1 && rStr[1] == '+')
2807
482
        {
2808
482
            ExpStr.insert(0, '+');
2809
482
        }
2810
5.00k
        ExpStr.insert(0, rStr[0]);
2811
5.00k
        if ( j )
2812
776
        {
2813
776
            j--;
2814
776
        }
2815
4.22k
        else
2816
4.22k
        {
2817
4.22k
            bCont = false;
2818
4.22k
        }
2819
5.00k
    }
2820
    // Continue main number:
2821
5.00k
    if ( !bCont )
2822
4.22k
    {
2823
4.22k
        sStr.truncate();
2824
4.22k
    }
2825
776
    else
2826
776
    {
2827
776
        bRes |= ImpDecimalFill(rNatNum, sStr, fNumber, nDecPos, j, nIx, false, bStarFlag);
2828
776
    }
2829
2830
5.00k
    if (bSign)
2831
182
    {
2832
182
        sStr.insert(0, '-');
2833
182
    }
2834
5.00k
    sStr.append(ExpStr);
2835
2836
5.00k
    return bRes;
2837
5.00k
}
2838
2839
double SvNumberformat::GetRoundFractionValue ( double fNumber ) const
2840
0
{
2841
0
    sal_uInt16 nIx = GetSubformatIndex ( fNumber );
2842
0
    double fIntPart = 0.0;           // integer part of fraction
2843
0
    sal_Int64 nFrac = 0, nDiv = 1;  // numerator and denominator
2844
0
    double fSign = (fNumber < 0.0) ? -1.0 : 1.0;
2845
    // fNumber is modified in ImpGetFractionElements to absolute fractional part
2846
0
    ImpGetFractionElements ( fNumber, nIx, fIntPart, nFrac, nDiv );
2847
0
    if ( nDiv > 0 )
2848
0
        return fSign * ( fIntPart + static_cast<double>(nFrac) / static_cast<double>(nDiv) );
2849
0
    else
2850
0
        return fSign * fIntPart;
2851
0
}
2852
2853
void SvNumberformat::ImpGetFractionElements ( double& fNumber, sal_uInt16 nIx,
2854
                                              double& fIntPart, sal_Int64& nFrac, sal_Int64& nDiv ) const
2855
1.44k
{
2856
1.44k
    if ( fNumber < 0.0 )
2857
0
        fNumber = -fNumber;
2858
1.44k
    fIntPart = floor(fNumber); // Integral part
2859
1.44k
    fNumber -= fIntPart;         // Fractional part
2860
1.44k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2861
1.44k
    sal_Int64 nForcedDiv = lcl_GetDenominatorString( rInfo, NumFor[nIx].GetCount() ).toInt32();
2862
1.44k
    if( nForcedDiv > 0 )
2863
506
    {   // Forced Denominator
2864
506
        nDiv = nForcedDiv;
2865
506
        nFrac = static_cast<sal_Int64>(floor ( fNumber * nDiv ));
2866
506
        double fFracNew = static_cast<double>(nFrac) / static_cast<double>(nDiv);
2867
506
        double fFracNew1 = static_cast<double>(nFrac + 1) / static_cast<double>(nDiv);
2868
506
        double fDiff = fNumber - fFracNew;
2869
506
        if( fDiff > ( fFracNew1 - fNumber ) )
2870
18
        {
2871
18
            nFrac++;
2872
18
        }
2873
506
    }
2874
937
    else // Calculated Denominator
2875
937
    {
2876
937
        nDiv = 1;
2877
937
        sal_Int64 nBasis = static_cast<sal_Int64>(floor( pow(10.0,rInfo.nCntExp))) - 1; // 9, 99, 999 ,...
2878
937
        sal_Int64 nFracPrev = 1, nDivPrev = 0, nFracNext, nDivNext, nPartialDenom;
2879
937
        double fRemainder = fNumber;
2880
2881
        // Use continued fraction representation of fNumber
2882
        // See https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations
2883
1.11k
        while ( fRemainder > 0.0 )
2884
180
        {
2885
180
            double fTemp = 1.0 / fRemainder;             // 64bits precision required when fRemainder is very weak
2886
180
            nPartialDenom = static_cast<sal_Int64>(floor(fTemp));   // due to floating point notation with double precision
2887
180
            fRemainder = fTemp - static_cast<double>(nPartialDenom);
2888
180
            nDivNext = nPartialDenom * nDiv + nDivPrev;
2889
180
            if ( nDivNext <= nBasis )  // continue loop
2890
144
            {
2891
144
                nFracNext = nPartialDenom * nFrac + nFracPrev;
2892
144
                nFracPrev = nFrac;
2893
144
                nFrac = nFracNext;
2894
144
                nDivPrev = nDiv;
2895
144
                nDiv = nDivNext;
2896
144
            }
2897
36
            else // calculate collateral fraction and exit
2898
36
            {
2899
36
                sal_Int64 nCollat = (nBasis - nDivPrev) / nDiv;
2900
36
                if ( 2 * nCollat >= nPartialDenom )
2901
3
                {
2902
3
                    sal_Int64 nFracTest = nCollat * nFrac + nFracPrev;
2903
3
                    sal_Int64 nDivTest  = nCollat * nDiv  + nDivPrev;
2904
3
                    double fSign = (static_cast<double>(nFrac) > fNumber * static_cast<double>(nDiv))?1.0:-1.0;
2905
3
                    if ( fSign * ( double(nFrac * nDivTest + nDiv * nFracTest) - 2.0 * double(nDiv * nDivTest) * fNumber ) > 0.0 )
2906
3
                    {
2907
3
                        nFrac = nFracTest;
2908
3
                        nDiv  = nDivTest;
2909
3
                    }
2910
3
                }
2911
36
                fRemainder = 0.0; // exit while loop
2912
36
            }
2913
180
        }
2914
937
    }
2915
1.44k
    if (nFrac >= nDiv)
2916
0
    {
2917
0
        ++fIntPart;
2918
0
        nFrac = 0;
2919
0
        nDiv = ( nForcedDiv > 0 ) ? nForcedDiv : 1;
2920
0
    }
2921
1.44k
}
2922
2923
bool SvNumberformat::ImpGetFractionOutput(double fNumber,
2924
                                          sal_uInt16 nIx,
2925
                                          bool bStarFlag,
2926
                                          const NativeNumberWrapper& rNatNum,
2927
                                          OUStringBuffer& sBuff) const
2928
1.94k
{
2929
1.94k
    bool bRes = false;
2930
1.94k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2931
1.94k
    const sal_uInt16 nCnt = NumFor[nIx].GetCount();
2932
1.94k
    OUStringBuffer sStr, sFrac, sDiv; // Strings, value for Integral part Numerator and denominator
2933
1.94k
    bool bSign = ( (fNumber < 0) && (nIx == 0) ); // sign Not in the ones at the end
2934
1.94k
    const OUString sIntegerFormat = lcl_GetFractionIntegerString(rInfo, nCnt);
2935
1.94k
    const OUString sNumeratorFormat = lcl_GetNumeratorString(rInfo, nCnt);
2936
1.94k
    const OUString sDenominatorFormat = lcl_GetDenominatorString(rInfo, nCnt);
2937
2938
1.94k
    sal_Int64 nFrac = 0, nDiv = 1;
2939
1.94k
    double fNum = floor(fNumber); // Integral part
2940
2941
1.94k
    if (fNum > D_MAX_U_INT32 || rInfo.nCntExp > 9) // Too large
2942
502
    {
2943
502
        sBuff = ImpSvNumberformatScan::sErrStr;
2944
502
        return false;
2945
502
    }
2946
1.44k
    if (rInfo.nCntExp == 0)
2947
0
    {
2948
0
        SAL_WARN( "svl.numbers", "SvNumberformat:: Fraction, nCntExp == 0");
2949
0
        sBuff.truncate();
2950
0
        return false;
2951
0
    }
2952
2953
1.44k
    ImpGetFractionElements( fNumber, nIx, fNum, nFrac, nDiv);
2954
2955
1.44k
    if (rInfo.nCntPre == 0) // Improper fraction
2956
1.04k
    {
2957
1.04k
        double fNum1 = fNum * static_cast<double>(nDiv) + static_cast<double>(nFrac);
2958
2959
1.04k
        if (fNum1 > D_MAX_INTEGER)
2960
3
        {
2961
3
            sBuff = ImpSvNumberformatScan::sErrStr;
2962
3
            return false;
2963
3
        }
2964
1.04k
        nFrac = static_cast<sal_Int64>(floor(fNum1));
2965
1.04k
    }
2966
398
    else if (fNum == 0.0 && nFrac != 0)
2967
8
    {
2968
8
    }
2969
390
    else
2970
390
    {
2971
390
        char aBuf[100];
2972
390
        o3tl::sprintf( aBuf, "%.f", fNum ); // simple rounded integer
2973
390
        sStr.appendAscii( aBuf );
2974
390
        ::impTransliterate(sStr, NumFor[nIx].GetNatNum(), rNatNum);
2975
390
    }
2976
1.44k
    bool bHideFraction = (rInfo.nCntPre > 0 && nFrac == 0
2977
340
                        && (sNumeratorFormat.indexOf('0') < 0)
2978
328
                        && (sDenominatorFormat.indexOf('0') < 0
2979
274
                        || sDenominatorFormat.toInt32() > 0) );
2980
1.44k
    if ( bHideFraction )
2981
267
    {
2982
267
        sDiv.truncate();
2983
267
    }
2984
1.17k
    else  // if there are some '0' in format, force display of fraction
2985
1.17k
    {
2986
1.17k
        sFrac = ImpIntToString(rNatNum, nIx, nFrac);
2987
1.17k
        sDiv = ImpIntToString(rNatNum, nIx, nDiv);
2988
1.17k
    }
2989
2990
1.44k
    sal_uInt16 j = nCnt-1; // Last symbol -> backwards
2991
1.44k
    sal_Int32 k;           // Denominator
2992
2993
1.44k
    bRes |= ImpNumberFill(rNatNum, sDiv, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRAC, bStarFlag, true);
2994
2995
1.44k
    bool bCont = true;
2996
1.44k
    if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRAC)
2997
1.44k
    {
2998
1.44k
        if ( bHideFraction )
2999
267
        {   // do not insert blank for fraction if there is no '?'
3000
267
            if ( sNumeratorFormat.indexOf('?') >= 0
3001
248
              || sDenominatorFormat.indexOf('?') >= 0 )
3002
57
                sDiv.insert(0, ' ');
3003
267
        }
3004
1.17k
        else
3005
1.17k
        {
3006
1.17k
            sDiv.insert(0, rInfo.sStrArray[j][0]);
3007
1.17k
        }
3008
1.44k
        if ( j )
3009
1.44k
        {
3010
1.44k
            j--;
3011
1.44k
        }
3012
0
        else
3013
0
        {
3014
0
            bCont = false;
3015
0
        }
3016
1.44k
    }
3017
    // Further numerators:
3018
1.44k
    if ( !bCont )
3019
0
    {
3020
0
        sFrac.truncate();
3021
0
    }
3022
1.44k
    else
3023
1.44k
    {
3024
1.44k
        bRes |= ImpNumberFill(rNatNum, sFrac, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRACBLANK, bStarFlag);
3025
1.44k
        bCont = false;  // there is no integer part?
3026
1.44k
        if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRACBLANK)
3027
399
        {
3028
399
            if ( j )
3029
399
            {
3030
399
                if ( bHideFraction )
3031
267
                {   // '?' in any format force display of blank as delimiter
3032
267
                    if ( sIntegerFormat.indexOf('?') >= 0
3033
254
                      || sNumeratorFormat.indexOf('?') >= 0
3034
235
                      || sDenominatorFormat.indexOf('?') >= 0 )
3035
63
                    {
3036
362
                        for (sal_Int32 i = 0; i < rInfo.sStrArray[j].getLength(); i++)
3037
299
                            sFrac.insert(0, ' ');
3038
63
                    }
3039
267
                }
3040
132
                else
3041
132
                {
3042
132
                    if ( fNum != 0.0 || sIntegerFormat.indexOf('0') >= 0 )
3043
99
                        sFrac.insert(0, rInfo.sStrArray[j]); // insert Blank string only if there are both integer and fraction
3044
33
                    else
3045
33
                    {
3046
33
                        if ( sIntegerFormat.indexOf('?') >= 0
3047
32
                          || sNumeratorFormat.indexOf('?') >= 0 )
3048
29
                        {
3049
178
                            for (sal_Int32 i = 0; i < rInfo.sStrArray[j].getLength(); i++)
3050
149
                                sFrac.insert(0, ' ');
3051
29
                        }
3052
33
                    }
3053
132
                }
3054
399
                j--;
3055
399
                bCont = true;  // Yes, there is an integer
3056
399
            }
3057
0
            else
3058
0
                sFrac.insert(0, rInfo.sStrArray[j]);
3059
399
        }
3060
1.44k
    }
3061
    // Continue integer part
3062
1.44k
    if ( !bCont )
3063
1.04k
    {
3064
1.04k
        sStr.truncate();
3065
1.04k
    }
3066
399
    else
3067
399
    {
3068
399
        k = sStr.getLength(); // After last figure
3069
399
        bRes |= ImpNumberFillWithThousands(rNatNum, sStr, fNumber, k, j, nIx,
3070
399
                                           rInfo.nCntPre, bStarFlag);
3071
399
    }
3072
1.44k
    if (bSign && (nFrac != 0 || fNum != 0.0))
3073
0
    {
3074
0
        sBuff.insert(0, '-'); // Not -0
3075
0
    }
3076
1.44k
    sBuff.append(sStr);
3077
1.44k
    sBuff.append(sFrac);
3078
1.44k
    sBuff.append(sDiv);
3079
1.44k
    return bRes;
3080
1.44k
}
3081
3082
sal_uInt16 SvNumberformat::ImpGetFractionOfSecondString( OUStringBuffer& rBuf, double fFractionOfSecond,
3083
        int nFractionDecimals, bool bAddOneRoundingDecimal, sal_uInt16 nIx, sal_uInt16 nMinimumInputLineDecimals,
3084
        const NativeNumberWrapper& rNatNum) const
3085
14.0k
{
3086
14.0k
    if (!nFractionDecimals)
3087
13.4k
        return 0;
3088
3089
    // nFractionDecimals+1 to not round up what Time::GetClock() carefully
3090
    // truncated.
3091
658
    rBuf.append( rtl::math::doubleToUString( fFractionOfSecond, rtl_math_StringFormat_F,
3092
658
                (bAddOneRoundingDecimal ? nFractionDecimals + 1 : nFractionDecimals), '.'));
3093
658
    rBuf.stripStart('0');
3094
658
    rBuf.stripStart('.');
3095
658
    if (bAddOneRoundingDecimal && rBuf.getLength() > nFractionDecimals)
3096
658
        rBuf.truncate( nFractionDecimals); // the digit appended because of nFractionDecimals+1
3097
658
    if (nMinimumInputLineDecimals)
3098
0
    {
3099
0
        rBuf.stripEnd('0');
3100
0
        for (sal_Int32 index = rBuf.getLength(); index < nMinimumInputLineDecimals; ++index)
3101
0
        {
3102
0
            rBuf.append('0');
3103
0
        }
3104
0
        ::impTransliterate(rBuf, NumFor[nIx].GetNatNum(), rNatNum);
3105
0
        nFractionDecimals = rBuf.getLength();
3106
0
    }
3107
658
    else
3108
658
    {
3109
658
        ::impTransliterate(rBuf, NumFor[nIx].GetNatNum(), rNatNum);
3110
658
    }
3111
658
    return static_cast<sal_uInt16>(nFractionDecimals);
3112
14.0k
}
3113
3114
bool SvNumberformat::ImpGetTimeOutput(double fNumber,
3115
                                      sal_uInt16 nIx,
3116
                                      bool bStarFlag,
3117
                                      const NativeNumberWrapper& rNatNum,
3118
                                      const SvNFLanguageData& rCurrentLang,
3119
                                      OUStringBuffer& sBuff) const
3120
7.37k
{
3121
7.37k
    using namespace ::com::sun::star::i18n;
3122
7.37k
    bool bCalendarSet = false;
3123
7.37k
    const double fNumberOrig = fNumber;
3124
7.37k
    bool bRes = false;
3125
7.37k
    bool bSign = false;
3126
7.37k
    if (fNumber < 0.0)
3127
18
    {
3128
18
        fNumber = -fNumber;
3129
18
        if (nIx == 0)
3130
18
        {
3131
18
            bSign = true;
3132
18
        }
3133
18
    }
3134
7.37k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3135
7.37k
    bool bInputLine;
3136
7.37k
    sal_Int32 nCntPost;
3137
7.37k
    if ( rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION &&
3138
0
         0 < rInfo.nCntPost && rInfo.nCntPost < kTimeSignificantRound )
3139
0
    {
3140
0
        bInputLine = true;
3141
0
        nCntPost = kTimeSignificantRound;
3142
0
    }
3143
7.37k
    else
3144
7.37k
    {
3145
7.37k
        bInputLine = false;
3146
7.37k
        nCntPost = rInfo.nCntPost;
3147
7.37k
    }
3148
3149
7.37k
    OUStringBuffer sSecStr;
3150
7.37k
    sal_Int32 nSecPos = 0; // For figure by figure processing
3151
7.37k
    sal_uInt32 nHour, nMin, nSec;
3152
7.37k
    if (!rInfo.bThousand) // No [] format
3153
5.70k
    {
3154
5.70k
        sal_uInt16 nCHour, nCMinute, nCSecond;
3155
5.70k
        double fFractionOfSecond;
3156
5.70k
        tools::Time::GetClock( fNumberOrig, nCHour, nCMinute, nCSecond, fFractionOfSecond, nCntPost);
3157
5.70k
        nHour = nCHour;
3158
5.70k
        nMin = nCMinute;
3159
5.70k
        nSec = nCSecond;
3160
5.70k
        nCntPost = ImpGetFractionOfSecondString( sSecStr, fFractionOfSecond, nCntPost, true, nIx,
3161
5.70k
                (bInputLine ? rInfo.nCntPost : 0), rNatNum );
3162
5.70k
    }
3163
1.67k
    else
3164
1.67k
    {
3165
1.67k
        const double fTime = rtl::math::round( fNumber * 86400.0, int(nCntPost));
3166
1.67k
        if (bSign && fTime == 0.0)
3167
0
        {
3168
0
            bSign = false; // Not -00:00:00
3169
0
        }
3170
1.67k
        if (fTime > D_MAX_U_INT32)
3171
0
        {
3172
0
            sBuff = ImpSvNumberformatScan::sErrStr;
3173
0
            return false;
3174
0
        }
3175
1.67k
        sal_uInt32 nSeconds = static_cast<sal_uInt32>(fTime);
3176
3177
1.67k
        nCntPost = ImpGetFractionOfSecondString( sSecStr, fTime - nSeconds, nCntPost, false, nIx,
3178
1.67k
                (bInputLine ? rInfo.nCntPost : 0), rNatNum );
3179
3180
1.67k
        if (rInfo.nThousand == 3) // [ss]
3181
0
        {
3182
0
            nHour = 0;
3183
0
            nMin = 0;
3184
0
            nSec = nSeconds;
3185
0
        }
3186
1.67k
        else if (rInfo.nThousand == 2) // [mm]:ss
3187
3
        {
3188
3
            nHour = 0;
3189
3
            nMin = nSeconds / 60;
3190
3
            nSec = nSeconds % 60;
3191
3
        }
3192
1.67k
        else if (rInfo.nThousand == 1) // [hh]:mm:ss
3193
1.67k
        {
3194
1.67k
            nHour = nSeconds / 3600;
3195
1.67k
            nMin = (nSeconds%3600) / 60;
3196
1.67k
            nSec = nSeconds%60;
3197
1.67k
        }
3198
1
        else
3199
1
        {
3200
            // TODO  What should these be set to?
3201
1
            nHour = 0;
3202
1
            nMin  = 0;
3203
1
            nSec  = 0;
3204
1
        }
3205
1.67k
    }
3206
3207
7.37k
    sal_Unicode cAmPm = ' '; // a or p
3208
7.37k
    if (rInfo.nCntExp) // AM/PM
3209
63
    {
3210
63
        if (nHour == 0)
3211
3
        {
3212
3
            nHour = 12;
3213
3
            cAmPm = 'a';
3214
3
        }
3215
60
        else if (nHour < 12)
3216
50
        {
3217
50
            cAmPm = 'a';
3218
50
        }
3219
10
        else
3220
10
        {
3221
10
            cAmPm = 'p';
3222
10
            if (nHour > 12)
3223
6
            {
3224
6
                nHour -= 12;
3225
6
            }
3226
10
        }
3227
63
    }
3228
7.37k
    const sal_uInt16 nCnt = NumFor[nIx].GetCount();
3229
56.8k
    for (sal_uInt16 i = 0; i < nCnt; i++)
3230
49.5k
    {
3231
49.5k
        sal_Int32 nLen;
3232
49.5k
        switch (rInfo.nTypeArray[i])
3233
49.5k
        {
3234
136
        case NF_SYMBOLTYPE_STAR:
3235
136
            if( bStarFlag )
3236
0
            {
3237
0
                bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
3238
0
            }
3239
136
            break;
3240
66
        case NF_SYMBOLTYPE_BLANK:
3241
66
            if (rInfo.sStrArray[i].getLength() >= 2)
3242
65
                InsertBlanks(sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
3243
66
            break;
3244
2.79k
        case NF_SYMBOLTYPE_STRING:
3245
2.79k
        case NF_SYMBOLTYPE_CURRENCY:
3246
2.79k
        case NF_SYMBOLTYPE_DATESEP:
3247
26.6k
        case NF_SYMBOLTYPE_TIMESEP:
3248
26.6k
        case NF_SYMBOLTYPE_TIME100SECSEP:
3249
26.6k
            sBuff.append(rInfo.sStrArray[i]);
3250
26.6k
            break;
3251
608
        case NF_SYMBOLTYPE_DIGIT:
3252
608
            nLen = ( bInputLine && i > 0 &&
3253
0
                     (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING ||
3254
0
                      rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ?
3255
608
                     nCntPost : rInfo.sStrArray[i].getLength() );
3256
1.29k
            for (sal_Int32 j = 0; j < nLen && nSecPos < nCntPost && nSecPos < sSecStr.getLength(); ++j)
3257
683
            {
3258
683
                sBuff.append(sSecStr[nSecPos]);
3259
683
                nSecPos++;
3260
683
            }
3261
608
            break;
3262
63
        case NF_KEY_AMPM:               // AM/PM
3263
63
        {
3264
63
            CalendarWrapper& rCal = *rCurrentLang.GetCalendar();
3265
63
            if ( !bCalendarSet )
3266
63
            {
3267
63
                double fDiff = DateTime::Sub( DateTime(rScan.GetNullDate()), rCal.getEpochStart());
3268
63
                fDiff += fNumberOrig;
3269
63
                rCal.setLocalDateTime( fDiff );
3270
63
                bCalendarSet = true;
3271
63
            }
3272
63
            if (cAmPm == 'a')
3273
53
            {
3274
53
                sBuff.append(rCal.getDisplayName(
3275
53
                                 CalendarDisplayIndex::AM_PM, AmPmValue::AM, 0 ));
3276
53
            }
3277
10
            else
3278
10
            {
3279
10
                sBuff.append(rCal.getDisplayName(
3280
10
                                 CalendarDisplayIndex::AM_PM, AmPmValue::PM, 0 ));
3281
10
            }
3282
63
            break;
3283
26.6k
        }
3284
0
        case NF_KEY_AP:                 // A/P
3285
0
            if (cAmPm == 'a')
3286
0
            {
3287
0
                sBuff.append('a');
3288
0
            }
3289
0
            else
3290
0
            {
3291
0
                sBuff.append('p');
3292
0
            }
3293
0
            break;
3294
434
        case NF_KEY_MI:                 // M
3295
434
            sBuff.append(ImpIntToString(rNatNum, nIx, nMin ));
3296
434
            break;
3297
6.53k
        case NF_KEY_MMI:                // MM
3298
6.53k
            sBuff.append(ImpIntToString(rNatNum, nIx, nMin, 2 ));
3299
6.53k
            break;
3300
645
        case NF_KEY_H:                  // H
3301
645
            sBuff.append(ImpIntToString(rNatNum, nIx, nHour ));
3302
645
            break;
3303
6.01k
        case NF_KEY_HH:                 // HH
3304
6.01k
            sBuff.append(ImpIntToString(rNatNum, nIx, nHour, 2 ));
3305
6.01k
            break;
3306
662
        case NF_KEY_S:                  // S
3307
662
            sBuff.append(ImpIntToString(rNatNum, nIx, nSec ));
3308
662
            break;
3309
4.29k
        case NF_KEY_SS:                 // SS
3310
4.29k
            sBuff.append(ImpIntToString(rNatNum, nIx, nSec, 2 ));
3311
4.29k
            break;
3312
3.36k
        default:
3313
3.36k
            break;
3314
49.5k
        }
3315
49.5k
    }
3316
7.37k
    if (bSign && rInfo.bThousand)
3317
0
    {
3318
0
        sBuff.insert(0, '-');
3319
0
    }
3320
7.37k
    return bRes;
3321
7.37k
}
3322
3323
3324
/** If a day of month occurs within the format, the month name is in possessive
3325
    genitive case if the day follows the month, and partitive case if the day
3326
    precedes the month. If there is no day of month the nominative case (noun)
3327
    is returned. Also if the month is immediately preceded or followed by a
3328
    literal string other than space and not followed by a comma, the nominative
3329
    name is used, this prevents duplicated casing for MMMM\t\a and such in
3330
    documents imported from (e.g. Finnish) Excel or older LibO/OOo releases.
3331
 */
3332
3333
// IDEA: instead of eCodeType pass the index to nTypeArray and restrict
3334
// inspection of month name around that one, that would enable different month
3335
// cases in one format. Though probably the most rare use case ever...
3336
3337
sal_Int32 SvNumberformat::ImpUseMonthCase( int & io_nState, const ImpSvNumFor& rNumFor, NfKeywordIndex eCodeType )
3338
6.41k
{
3339
6.41k
    using namespace ::com::sun::star::i18n;
3340
6.41k
    if (!io_nState)
3341
3.46k
    {
3342
3.46k
        bool bMonthSeen = false;
3343
3.46k
        bool bDaySeen = false;
3344
3.46k
        const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3345
3.46k
        const sal_uInt16 nCount = rNumFor.GetCount();
3346
23.6k
        for (sal_uInt16 i = 0; i < nCount && io_nState == 0; ++i)
3347
20.2k
        {
3348
20.2k
            sal_Int32 nLen;
3349
20.2k
            switch (rInfo.nTypeArray[i])
3350
20.2k
            {
3351
1.21k
            case NF_KEY_D :
3352
2.78k
            case NF_KEY_DD :
3353
2.78k
                if (bMonthSeen)
3354
359
                {
3355
359
                    io_nState = 2;
3356
359
                }
3357
2.42k
                else
3358
2.42k
                {
3359
2.42k
                    bDaySeen = true;
3360
2.42k
                }
3361
2.78k
                break;
3362
1.21k
            case NF_KEY_MMM:
3363
2.87k
            case NF_KEY_MMMM:
3364
4.25k
            case NF_KEY_MMMMM:
3365
4.25k
                if ((i < nCount-1 &&
3366
4.23k
                     rInfo.nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
3367
                     // Literal following, not empty, space nor comma.
3368
2.76k
                     !rInfo.sStrArray[i+1].isEmpty() &&
3369
2.76k
                     rInfo.sStrArray[i+1][0] != ' ' && rInfo.sStrArray[i+1][0] != ',') ||
3370
3.90k
                    (i > 0 && rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING &&
3371
2.59k
                     ((nLen = rInfo.sStrArray[i-1].getLength()) > 0) &&
3372
                     // Literal preceding, not space.
3373
2.59k
                     rInfo.sStrArray[i-1][nLen-1] != ' '))
3374
807
                {
3375
807
                    io_nState = 1;
3376
807
                }
3377
3.45k
                else if (bDaySeen)
3378
2.21k
                {
3379
2.21k
                    io_nState = 3;
3380
2.21k
                }
3381
1.23k
                else
3382
1.23k
                {
3383
1.23k
                    bMonthSeen = true;
3384
1.23k
                }
3385
4.25k
                break;
3386
20.2k
            }
3387
20.2k
        }
3388
3.46k
        if (io_nState == 0)
3389
88
        {
3390
88
            io_nState = 1; // No day of month
3391
88
        }
3392
3.46k
    }
3393
6.41k
    switch (io_nState)
3394
6.41k
    {
3395
3.45k
    case 1:
3396
        // No day of month or forced nominative
3397
3.45k
        switch (eCodeType)
3398
3.45k
        {
3399
706
        case NF_KEY_MMM:
3400
706
            return CalendarDisplayCode::SHORT_MONTH_NAME;
3401
26
        case NF_KEY_MMMM:
3402
26
            return CalendarDisplayCode::LONG_MONTH_NAME;
3403
2.71k
        case NF_KEY_MMMMM:
3404
2.71k
            return CalendarDisplayCode::NARROW_MONTH_NAME;
3405
0
        default:
3406
0
            ;   // nothing
3407
3.45k
        }
3408
0
        break;
3409
688
    case 2:
3410
        // Day of month follows month (the month's 17th)
3411
688
        switch (eCodeType)
3412
688
        {
3413
259
        case NF_KEY_MMM:
3414
259
            return CalendarDisplayCode::SHORT_GENITIVE_MONTH_NAME;
3415
48
        case NF_KEY_MMMM:
3416
48
            return CalendarDisplayCode::LONG_GENITIVE_MONTH_NAME;
3417
381
        case NF_KEY_MMMMM:
3418
381
            return CalendarDisplayCode::NARROW_GENITIVE_MONTH_NAME;
3419
0
        default:
3420
0
            ;   // Nothing
3421
688
        }
3422
0
        break;
3423
2.27k
    case 3:
3424
        // Day of month precedes month (17 of month)
3425
2.27k
        switch (eCodeType)
3426
2.27k
        {
3427
614
        case NF_KEY_MMM:
3428
614
            return CalendarDisplayCode::SHORT_PARTITIVE_MONTH_NAME;
3429
1.59k
        case NF_KEY_MMMM:
3430
1.59k
            return CalendarDisplayCode::LONG_PARTITIVE_MONTH_NAME;
3431
69
        case NF_KEY_MMMMM:
3432
69
            return CalendarDisplayCode::NARROW_PARTITIVE_MONTH_NAME;
3433
0
        default:
3434
0
            ;   // nothing
3435
2.27k
        }
3436
0
        break;
3437
6.41k
    }
3438
0
    SAL_WARN( "svl.numbers", "ImpUseMonthCase: unhandled keyword index eCodeType");
3439
0
    return CalendarDisplayCode::LONG_MONTH_NAME;
3440
0
}
3441
3442
namespace {
3443
3444
bool ImpIsOtherCalendar( const ImpSvNumFor& rNumFor, const CalendarWrapper& rCal )
3445
352k
{
3446
352k
    if ( rCal.getUniqueID() != GREGORIAN )
3447
0
    {
3448
0
        return false;
3449
0
    }
3450
352k
    const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3451
352k
    const sal_uInt16 nCnt = rNumFor.GetCount();
3452
352k
    sal_uInt16 i;
3453
2.07M
    for ( i = 0; i < nCnt; i++ )
3454
1.75M
    {
3455
1.75M
        switch ( rInfo.nTypeArray[i] )
3456
1.75M
        {
3457
3.86k
        case NF_SYMBOLTYPE_CALENDAR :
3458
3.86k
            return false;
3459
905
        case NF_KEY_EC :
3460
6.98k
        case NF_KEY_EEC :
3461
10.1k
        case NF_KEY_R :
3462
10.1k
        case NF_KEY_RR :
3463
10.1k
        case NF_KEY_AAA :
3464
25.9k
        case NF_KEY_AAAA :
3465
27.2k
        case NF_KEY_G :
3466
27.2k
        case NF_KEY_GG :
3467
27.5k
        case NF_KEY_GGG :
3468
27.5k
            return true;
3469
1.75M
        }
3470
1.75M
    }
3471
321k
    return false;
3472
352k
}
3473
3474
}
3475
3476
void SvNumberformat::SwitchToOtherCalendar( OUString& rOrgCalendar,
3477
                                            double& fOrgDateTime,
3478
                                            CalendarWrapper& rCal ) const
3479
49.9k
{
3480
49.9k
    if ( rCal.getUniqueID() != GREGORIAN )
3481
0
        return;
3482
3483
49.9k
    using namespace ::com::sun::star::i18n;
3484
49.9k
    const css::uno::Sequence< OUString > xCals = rCal.getAllCalendars(
3485
49.9k
            rLoc().getLanguageTag().getLocale() );
3486
49.9k
    sal_Int32 nCnt = xCals.getLength();
3487
49.9k
    if ( nCnt <= 1 )
3488
0
        return;
3489
3490
49.9k
    auto pCal = std::find_if(xCals.begin(), xCals.end(),
3491
99.9k
        [](const OUString& rCalName) { return rCalName != GREGORIAN; });
3492
49.9k
    if (pCal == xCals.end())
3493
0
        return;
3494
3495
49.9k
    if ( !rOrgCalendar.getLength() )
3496
27.5k
    {
3497
27.5k
        rOrgCalendar = rCal.getUniqueID();
3498
27.5k
        fOrgDateTime = rCal.getDateTime();
3499
27.5k
    }
3500
49.9k
    rCal.loadCalendar( *pCal, rLoc().getLanguageTag().getLocale() );
3501
49.9k
    rCal.setDateTime( fOrgDateTime );
3502
49.9k
}
3503
3504
void SvNumberformat::SwitchToGregorianCalendar( std::u16string_view rOrgCalendar,
3505
                                                double fOrgDateTime,
3506
                                                CalendarWrapper& rCal ) const
3507
22.4k
{
3508
22.4k
    if ( rOrgCalendar.size() && rCal.getUniqueID() != GREGORIAN )
3509
22.4k
    {
3510
22.4k
        rCal.loadCalendar( GREGORIAN, rLoc().getLanguageTag().getLocale() );
3511
22.4k
        rCal.setDateTime( fOrgDateTime );
3512
22.4k
    }
3513
22.4k
}
3514
3515
bool SvNumberformat::ImpFallBackToGregorianCalendar( OUString& rOrgCalendar,
3516
                                                     double& fOrgDateTime,
3517
                                                     CalendarWrapper& rCal ) const
3518
356k
{
3519
356k
    using namespace ::com::sun::star::i18n;
3520
356k
    if ( rCal.getUniqueID() != GREGORIAN )
3521
29.5k
    {
3522
29.5k
        sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA );
3523
29.5k
        if ( nVal == 0 && rCal.getLoadedCalendar().Eras[0].ID == "Dummy" )
3524
0
        {
3525
0
            if ( !rOrgCalendar.getLength() )
3526
0
            {
3527
0
                rOrgCalendar = rCal.getUniqueID();
3528
0
                fOrgDateTime = rCal.getDateTime();
3529
0
            }
3530
0
            else if ( rOrgCalendar == GREGORIAN )
3531
0
            {
3532
0
                rOrgCalendar.clear();
3533
0
            }
3534
0
            rCal.loadCalendar( GREGORIAN, rLoc().getLanguageTag().getLocale() );
3535
0
            rCal.setDateTime( fOrgDateTime );
3536
0
            return true;
3537
0
        }
3538
29.5k
    }
3539
356k
    return false;
3540
356k
}
3541
3542
3543
#ifdef THE_FUTURE
3544
/* XXX NOTE: even if the ImpSwitchToSpecifiedCalendar method is currently
3545
 * unused please don't remove it, it would be needed by
3546
 * SwitchToSpecifiedCalendar(), see comment in
3547
 * ImpSvNumberInputScan::GetDateRef() */
3548
3549
bool SvNumberformat::ImpSwitchToSpecifiedCalendar( OUString& rOrgCalendar,
3550
                                                   double& fOrgDateTime,
3551
                                                   const ImpSvNumFor& rNumFor ) const
3552
{
3553
    const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3554
    const sal_uInt16 nCnt = rNumFor.GetCount();
3555
    for ( sal_uInt16 i = 0; i < nCnt; i++ )
3556
    {
3557
        if ( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_CALENDAR )
3558
        {
3559
            CalendarWrapper& rCal = GetCal();
3560
            if ( !rOrgCalendar.getLength() )
3561
            {
3562
                rOrgCalendar = rCal.getUniqueID();
3563
                fOrgDateTime = rCal.getDateTime();
3564
            }
3565
            rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLocale() );
3566
            rCal.setDateTime( fOrgDateTime );
3567
            return true;
3568
        }
3569
    }
3570
    return false;
3571
}
3572
#endif
3573
3574
// static
3575
void SvNumberformat::ImpAppendEraG( OUStringBuffer& OutString,
3576
                                    const CalendarWrapper& rCal,
3577
                                    sal_Int16 nNatNum )
3578
3.58k
{
3579
3.58k
    using namespace ::com::sun::star::i18n;
3580
3.58k
    if ( rCal.getUniqueID() == "gengou" )
3581
0
    {
3582
0
        sal_Unicode cEra;
3583
0
        sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA );
3584
0
        switch ( nVal )
3585
0
        {
3586
0
        case 1:
3587
0
            cEra = 'M';
3588
0
            break;
3589
0
        case 2:
3590
0
            cEra = 'T';
3591
0
            break;
3592
0
        case 3:
3593
0
            cEra = 'S';
3594
0
            break;
3595
0
        case 4:
3596
0
            cEra = 'H';
3597
0
            break;
3598
0
        case 5:
3599
0
            cEra = 'R';
3600
0
            break;
3601
0
        default:
3602
0
            cEra = '?';
3603
0
            break;
3604
0
        }
3605
0
        OutString.append(cEra);
3606
0
    }
3607
3.58k
    else
3608
3.58k
    {
3609
3.58k
        OutString.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
3610
3.58k
    }
3611
3.58k
}
3612
3613
bool SvNumberformat::ImpIsIso8601( const ImpSvNumFor& rNumFor ) const
3614
912
{
3615
912
    bool bIsIso = false;
3616
912
    if (eType & SvNumFormatType::DATE)
3617
912
    {
3618
912
        enum State
3619
912
        {
3620
912
            eNone,
3621
912
            eAtYear,
3622
912
            eAtSep1,
3623
912
            eAtMonth,
3624
912
            eAtSep2,
3625
912
            eNotIso
3626
912
        };
3627
912
        State eState = eNone;
3628
912
        auto & rTypeArray = rNumFor.Info().nTypeArray;
3629
912
        sal_uInt16 nCnt = rNumFor.GetCount();
3630
1.84k
        for (sal_uInt16 i=0; i < nCnt && !bIsIso && eState != eNotIso; ++i)
3631
936
        {
3632
936
            switch ( rTypeArray[i] )
3633
936
            {
3634
17
            case NF_KEY_YY:     // two digits not strictly ISO 8601
3635
27
            case NF_KEY_YYYY:
3636
27
                if (eState != eNone)
3637
0
                {
3638
0
                    eState = eNotIso;
3639
0
                }
3640
27
                else
3641
27
                {
3642
27
                    eState = eAtYear;
3643
27
                }
3644
27
                break;
3645
18
            case NF_KEY_M:      // single digit not strictly ISO 8601
3646
141
            case NF_KEY_MM:
3647
141
                if (eState != eAtSep1)
3648
138
                {
3649
138
                    eState = eNotIso;
3650
138
                }
3651
3
                else
3652
3
                {
3653
3
                    eState = eAtMonth;
3654
3
                }
3655
141
                break;
3656
149
            case NF_KEY_D:      // single digit not strictly ISO 8601
3657
378
            case NF_KEY_DD:
3658
378
                if (eState != eAtSep2)
3659
375
                {
3660
375
                    eState = eNotIso;
3661
375
                }
3662
3
                else
3663
3
                {
3664
3
                    bIsIso = true;
3665
3
                }
3666
378
                break;
3667
296
            case NF_SYMBOLTYPE_STRING:
3668
299
            case NF_SYMBOLTYPE_DATESEP:
3669
299
                if (rNumFor.Info().sStrArray[i] == "-")
3670
11
                {
3671
11
                    if (eState == eAtYear)
3672
5
                    {
3673
5
                        eState = eAtSep1;
3674
5
                    }
3675
6
                    else if (eState == eAtMonth)
3676
3
                    {
3677
3
                        eState = eAtSep2;
3678
3
                    }
3679
3
                    else
3680
3
                    {
3681
3
                        eState = eNotIso;
3682
3
                    }
3683
11
                }
3684
288
                else
3685
288
                {
3686
288
                    eState = eNotIso;
3687
288
                }
3688
299
                break;
3689
91
            default:
3690
91
                eState = eNotIso;
3691
936
            }
3692
936
        }
3693
912
    }
3694
0
    else
3695
0
    {
3696
0
       SAL_WARN( "svl.numbers", "SvNumberformat::ImpIsIso8601: no date" );
3697
0
    }
3698
912
    return bIsIso;
3699
912
}
3700
3701
static bool lcl_hasEra( const ImpSvNumFor& rNumFor )
3702
1
{
3703
1
    const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3704
1
    const sal_uInt16 nCnt = rNumFor.GetCount();
3705
6
    for ( sal_uInt16 i = 0; i < nCnt; i++ )
3706
5
    {
3707
5
        switch ( rInfo.nTypeArray[i] )
3708
5
        {
3709
0
            case NF_KEY_RR :
3710
0
            case NF_KEY_G :
3711
0
            case NF_KEY_GG :
3712
0
            case NF_KEY_GGG :
3713
0
                return true;
3714
5
        }
3715
5
    }
3716
1
    return false;
3717
1
}
3718
3719
static bool lcl_isSignedYear( const CalendarWrapper& rCal, const ImpSvNumFor& rNumFor )
3720
341k
{
3721
341k
    return rCal.getValue( css::i18n::CalendarFieldIndex::ERA ) == 0 &&
3722
1
        rCal.getUniqueID() == GREGORIAN && !lcl_hasEra( rNumFor );
3723
341k
}
3724
3725
/* XXX: if needed this could be stripped from rEpochStart and diff adding and
3726
 * moved to tools' DateTime to be reused elsewhere. */
3727
static bool lcl_getValidDate( const DateTime& rNullDate, const DateTime& rEpochStart, double& fNumber )
3728
352k
{
3729
352k
    static const DateTime aCE( Date(1,1,1));
3730
352k
    static const DateTime aMin( Date(1,1, SAL_MIN_INT16));
3731
352k
    static const DateTime aMax( Date(31,12, SAL_MAX_INT16), tools::Time(23,59,59, tools::Time::nanoSecPerSec - 1));
3732
352k
    static const double fMin = DateTime::Sub( aMin, aCE);
3733
352k
    static const double fMax = DateTime::Sub( aMax, aCE);
3734
    // Value must be representable in our tools::Date proleptic Gregorian
3735
    // calendar as well.
3736
352k
    const double fOff = DateTime::Sub( rNullDate, aCE) + fNumber;
3737
    // Add diff between epochs to serial date number.
3738
352k
    const double fDiff = DateTime::Sub( rNullDate, rEpochStart);
3739
352k
    fNumber += fDiff;
3740
352k
    return fMin <= fOff && fOff <= fMax;
3741
352k
}
3742
3743
bool SvNumberformat::ImpGetDateOutput(double fNumber,
3744
                                      sal_uInt16 nIx,
3745
                                      bool bStarFlag,
3746
                                      const NativeNumberWrapper& rNatNum,
3747
                                      const SvNFLanguageData& rCurrentLang,
3748
                                      OUStringBuffer& sBuff) const
3749
345k
{
3750
345k
    using namespace ::com::sun::star::i18n;
3751
345k
    bool bRes = false;
3752
3753
345k
    CalendarWrapper& rCal = *rCurrentLang.GetCalendar();
3754
345k
    if (!lcl_getValidDate( DateTime( rScan.GetNullDate() ), rCal.getEpochStart(), fNumber))
3755
4
    {
3756
4
        sBuff = ImpSvNumberformatScan::sErrStr;
3757
4
        return false;
3758
4
    }
3759
345k
    rCal.setLocalDateTime( fNumber );
3760
345k
    int nUseMonthCase = 0; // Not decided yet
3761
345k
    OUString aOrgCalendar; // empty => not changed yet
3762
3763
345k
    double fOrgDateTime(0.0);
3764
345k
    bool bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx], *rCurrentLang.GetCalendar() );
3765
345k
    if ( bOtherCalendar )
3766
22.9k
    {
3767
22.9k
        SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3768
22.9k
    }
3769
345k
    if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() ) )
3770
0
    {
3771
0
        bOtherCalendar = false;
3772
0
    }
3773
345k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3774
345k
    const sal_uInt16 nCnt = NumFor[nIx].GetCount();
3775
345k
    sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum();
3776
345k
    OUString aStr;
3777
3778
    // NatNum12: if the date format contains more than a date
3779
    // field, it needs to specify in NatNum12 argument
3780
    // which date element needs special formatting:
3781
    //
3782
    // '[NatNum12 ordinal-number]D'              -> "1st"
3783
    // '[NatNum12 D=ordinal-number]D" of "MMMM'  -> "1st of April"
3784
    // '[NatNum12 D=ordinal]D" of "MMMM'         -> "first of April"
3785
    // '[NatNum12 YYYY=year,D=ordinal]D" of "MMMM", "YYYY' -> "first of April, nineteen ninety"
3786
    //
3787
    // Note: set only for YYYY, MMMM, M, DDDD, D and NNN/AAAA in date formats.
3788
    // Additionally for MMMMM, MMM, DDD and NN/AA to support at least
3789
    // capitalize, upper, lower, title.
3790
    // XXX It's possible to extend this for other keywords and date + time
3791
    // combinations, as required.
3792
3793
345k
    bool bUseSpellout = NatNumTakesParameters(nNatNum) &&
3794
0
            (nCnt == 1 || NumFor[nIx].GetNatNum().GetParams().indexOf('=') > -1);
3795
3796
2.08M
    for (sal_uInt16 i = 0; i < nCnt; i++)
3797
1.73M
    {
3798
1.73M
        switch (rInfo.nTypeArray[i])
3799
1.73M
        {
3800
3.88k
        case NF_SYMBOLTYPE_CALENDAR :
3801
3.88k
            if ( !aOrgCalendar.getLength() )
3802
3.85k
            {
3803
3.85k
                aOrgCalendar = rCal.getUniqueID();
3804
3.85k
                fOrgDateTime = rCal.getDateTime();
3805
3.85k
            }
3806
3.88k
            rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLanguageTag().getLocale() );
3807
3.88k
            rCal.setDateTime( fOrgDateTime );
3808
3.88k
            ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3809
3.88k
            break;
3810
97
        case NF_SYMBOLTYPE_STAR:
3811
97
            if( bStarFlag )
3812
0
            {
3813
0
                bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
3814
0
            }
3815
97
            break;
3816
1.70k
        case NF_SYMBOLTYPE_BLANK:
3817
1.70k
            if (rInfo.sStrArray[i].getLength() >= 2)
3818
1.70k
                InsertBlanks( sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
3819
1.70k
            break;
3820
66.4k
        case NF_SYMBOLTYPE_STRING:
3821
66.4k
        case NF_SYMBOLTYPE_CURRENCY:
3822
698k
        case NF_SYMBOLTYPE_DATESEP:
3823
698k
        case NF_SYMBOLTYPE_TIMESEP:
3824
698k
        case NF_SYMBOLTYPE_TIME100SECSEP:
3825
698k
            sBuff.append(rInfo.sStrArray[i]);
3826
698k
            break;
3827
299k
        case NF_KEY_M:                  // M
3828
299k
            aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_MONTH, nNatNum );
3829
            // NatNum12: support variants of preposition, suffixation or article
3830
            // for example, Catalan "de març", but "d'abril" etc.
3831
299k
            if ( bUseSpellout )
3832
0
            {
3833
0
                aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3834
0
            }
3835
299k
            sBuff.append(aStr);
3836
299k
            break;
3837
39.5k
        case NF_KEY_MM:                 // MM
3838
39.5k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_MONTH, nNatNum ));
3839
39.5k
            break;
3840
1.40k
        case NF_KEY_MMM:                // MMM
3841
3.04k
        case NF_KEY_MMMM:               // MMMM
3842
4.33k
        case NF_KEY_MMMMM:              // MMMMM
3843
            // NatNum12: support variants of preposition, suffixation or
3844
            // article, or capitalize, upper, lower, title.
3845
            // Note: result of the "spell out" conversion can depend from the optional
3846
            // PartitiveMonths or GenitiveMonths defined in the locale data,
3847
            // see description of ImpUseMonthCase(), and locale data in
3848
            // i18npool/source/localedata/data/ and libnumbertext
3849
4.33k
            aStr = rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3850
4.33k
                                                           static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3851
4.33k
                                                           nNatNum);
3852
4.33k
            if ( bUseSpellout )
3853
0
            {
3854
0
                aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3855
0
            }
3856
4.33k
            sBuff.append(aStr);
3857
4.33k
            break;
3858
1.91k
        case NF_KEY_Q:                  // Q
3859
1.91k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_QUARTER, nNatNum ));
3860
1.91k
            break;
3861
515
        case NF_KEY_QQ:                 // QQ
3862
515
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_QUARTER, nNatNum ));
3863
515
            break;
3864
297k
        case NF_KEY_D:                  // D
3865
297k
            aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY, nNatNum );
3866
            // NatNum12: support variants of preposition, suffixation or article
3867
297k
            if ( bUseSpellout )
3868
0
            {
3869
0
                aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3870
0
            }
3871
297k
            sBuff.append(aStr);
3872
297k
            break;
3873
23.3k
        case NF_KEY_DD:                 // DD
3874
23.3k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY, nNatNum ));
3875
23.3k
            break;
3876
355
        case NF_KEY_DDD:                // DDD
3877
355
            if ( bOtherCalendar )
3878
243
            {
3879
243
                SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3880
243
            }
3881
355
            aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum );
3882
            // NatNum12: support at least capitalize, upper, lower, title
3883
355
            if ( bUseSpellout )
3884
0
            {
3885
0
                aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3886
0
            }
3887
355
            sBuff.append(aStr);
3888
355
            if ( bOtherCalendar )
3889
243
            {
3890
243
                SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3891
243
            }
3892
355
            break;
3893
1.10k
        case NF_KEY_DDDD:               // DDDD
3894
1.10k
            if ( bOtherCalendar )
3895
373
            {
3896
373
                SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3897
373
            }
3898
1.10k
            aStr = rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum );
3899
            // NatNum12: support variants of preposition, suffixation or article
3900
1.10k
            if ( bUseSpellout )
3901
0
            {
3902
0
                aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3903
0
            }
3904
1.10k
            sBuff.append(aStr);
3905
1.10k
            if ( bOtherCalendar )
3906
373
            {
3907
373
                SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3908
373
            }
3909
1.10k
            break;
3910
313k
        case NF_KEY_YY:                 // YY
3911
313k
            if ( bOtherCalendar )
3912
424
            {
3913
424
                SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3914
424
            }
3915
            // Prepend a minus sign if Gregorian BCE and era is not displayed.
3916
313k
            if (lcl_isSignedYear( rCal, NumFor[nIx] ))
3917
1
            {
3918
1
                sBuff.append('-');
3919
1
            }
3920
313k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3921
313k
            if ( bOtherCalendar )
3922
424
            {
3923
424
                SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3924
424
            }
3925
313k
            break;
3926
9.23k
        case NF_KEY_YYYY:               // YYYY
3927
9.23k
            if ( bOtherCalendar )
3928
2.29k
            {
3929
2.29k
                SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3930
2.29k
            }
3931
            // Prepend a minus sign if Gregorian BCE and era is not displayed.
3932
9.23k
            if (lcl_isSignedYear( rCal, NumFor[nIx] ))
3933
0
            {
3934
0
                sBuff.append('-');
3935
0
            }
3936
9.23k
            aStr = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
3937
9.23k
            if (aStr.getLength() < 4 && !lcl_hasEra(NumFor[nIx]))
3938
0
            {
3939
0
                using namespace comphelper::string;
3940
                // Ensure that year consists of at least 4 digits, so it
3941
                // can be distinguished from 2 digits display and edited
3942
                // without suddenly being hit by the 2-digit year magic.
3943
0
                OUStringBuffer aBuf;
3944
0
                padToLength(aBuf, 4 - aStr.getLength(), '0');
3945
0
                ::impTransliterate(aBuf, NumFor[nIx].GetNatNum(), rNatNum);
3946
0
                aBuf.append(aStr);
3947
0
                aStr = aBuf.makeStringAndClear();
3948
0
            }
3949
            // NatNum12: support variants of preposition, suffixation or article
3950
9.23k
            if ( bUseSpellout )
3951
0
            {
3952
0
                aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3953
0
            }
3954
9.23k
            sBuff.append(aStr);
3955
9.23k
            if ( bOtherCalendar )
3956
2.29k
            {
3957
2.29k
                SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
3958
2.29k
            }
3959
9.23k
            break;
3960
1.24k
        case NF_KEY_EC:                 // E
3961
1.24k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3962
1.24k
            break;
3963
7.93k
        case NF_KEY_EEC:                // EE
3964
10.3k
        case NF_KEY_R:                  // R
3965
10.3k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum ));
3966
10.3k
            break;
3967
1.60k
        case NF_KEY_NN:                 // NN
3968
1.62k
        case NF_KEY_AAA:                // AAA
3969
1.62k
            aStr = rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum );
3970
            // NatNum12: support at least capitalize, upper, lower, title
3971
1.62k
            if ( bUseSpellout )
3972
0
            {
3973
0
                aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3974
0
            }
3975
1.62k
            sBuff.append(aStr);
3976
1.62k
            break;
3977
136
        case NF_KEY_NNN:                // NNN
3978
18.3k
        case NF_KEY_AAAA:               // AAAA
3979
18.3k
            aStr = rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum );
3980
            // NatNum12: support variants of preposition, suffixation or article
3981
18.3k
            if ( bUseSpellout )
3982
0
            {
3983
0
                aStr = impTransliterate(aStr, NumFor[nIx].GetNatNum(), rInfo.nTypeArray[i], rNatNum);
3984
0
            }
3985
18.3k
            sBuff.append(aStr);
3986
18.3k
            break;
3987
98
        case NF_KEY_NNNN:               // NNNN
3988
98
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3989
98
            sBuff.append(rLoc().getLongDateDayOfWeekSep());
3990
98
            break;
3991
133
        case NF_KEY_WW :                // WW
3992
133
            sBuff.append(ImpIntToString(rNatNum, nIx,
3993
133
                                         rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR )));
3994
133
            break;
3995
256
        case NF_KEY_G:                  // G
3996
256
            ImpAppendEraG(sBuff, rCal, nNatNum );
3997
256
            break;
3998
269
        case NF_KEY_GG:                 // GG
3999
269
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
4000
269
            break;
4001
1.33k
        case NF_KEY_GGG:                // GGG
4002
1.33k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_ERA, nNatNum ));
4003
1.33k
            break;
4004
1.04k
        case NF_KEY_RR:                 // RR => GGGEE
4005
1.04k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ));
4006
1.04k
            break;
4007
1.73M
        }
4008
1.73M
    }
4009
345k
    if ( aOrgCalendar.getLength() )
4010
26.8k
    {
4011
26.8k
        rCal.loadCalendar( aOrgCalendar, rLoc().getLanguageTag().getLocale() );  // restore calendar
4012
26.8k
    }
4013
345k
    return bRes;
4014
345k
}
4015
4016
bool SvNumberformat::ImpGetDateTimeOutput(double fNumber,
4017
                                          sal_uInt16 nIx,
4018
                                          bool bStarFlag,
4019
                                          const NativeNumberWrapper& rNatNum,
4020
                                          const SvNFLanguageData& rCurrentLang,
4021
                                          OUStringBuffer& sBuff) const
4022
6.71k
{
4023
6.71k
    using namespace ::com::sun::star::i18n;
4024
6.71k
    bool bRes = false;
4025
4026
6.71k
    CalendarWrapper& rCal = *rCurrentLang.GetCalendar();
4027
6.71k
    if (!lcl_getValidDate( DateTime( rScan.GetNullDate() ), rCal.getEpochStart(), fNumber))
4028
7
    {
4029
7
        sBuff = ImpSvNumberformatScan::sErrStr;
4030
7
        return false;
4031
7
    }
4032
4033
6.70k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4034
6.70k
    bool bInputLine;
4035
6.70k
    sal_Int32 nCntPost, nFirstRounding;
4036
6.70k
    if ( rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION &&
4037
0
         0 < rInfo.nCntPost && rInfo.nCntPost < kTimeSignificantRound )
4038
0
    {
4039
0
        bInputLine = true;
4040
0
        nCntPost = nFirstRounding = kTimeSignificantRound;
4041
0
    }
4042
6.70k
    else
4043
6.70k
    {
4044
6.70k
        bInputLine = false;
4045
6.70k
        nCntPost = rInfo.nCntPost;
4046
        // For clock format (not []) do not round up to seconds and thus days.
4047
6.70k
        nFirstRounding = (rInfo.bThousand ? nCntPost : kTimeSignificantRound);
4048
6.70k
    }
4049
6.70k
    double fTime = (fNumber - floor( fNumber )) * 86400.0;
4050
6.70k
    fTime = ::rtl::math::round( fTime, int(nFirstRounding) );
4051
6.70k
    if (fTime >= 86400.0)
4052
0
    {
4053
        // result of fNumber==x.999999999... rounded up, use correct date/time
4054
0
        fTime -= 86400.0;
4055
0
        fNumber = floor( fNumber + 0.5) + fTime;
4056
0
    }
4057
6.70k
    rCal.setLocalDateTime( fNumber );
4058
4059
6.70k
    int nUseMonthCase = 0; // Not decided yet
4060
6.70k
    OUString aOrgCalendar; // empty => not changed yet
4061
6.70k
    double fOrgDateTime(0.0);
4062
6.70k
    bool bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx], *rCurrentLang.GetCalendar() );
4063
6.70k
    if ( bOtherCalendar )
4064
4.60k
    {
4065
4.60k
        SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4066
4.60k
    }
4067
6.70k
    if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() ) )
4068
0
    {
4069
0
        bOtherCalendar = false;
4070
0
    }
4071
6.70k
    sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum();
4072
4073
6.70k
    OUStringBuffer sSecStr;
4074
6.70k
    sal_Int32 nSecPos = 0; // For figure by figure processing
4075
6.70k
    sal_uInt32 nHour, nMin, nSec;
4076
6.70k
    if (!rInfo.bThousand) // No [] format
4077
6.70k
    {
4078
6.70k
        sal_uInt16 nCHour, nCMinute, nCSecond;
4079
6.70k
        double fFractionOfSecond;
4080
6.70k
        tools::Time::GetClock( fNumber, nCHour, nCMinute, nCSecond, fFractionOfSecond, nCntPost);
4081
6.70k
        nHour = nCHour;
4082
6.70k
        nMin = nCMinute;
4083
6.70k
        nSec = nCSecond;
4084
6.70k
        nCntPost = ImpGetFractionOfSecondString( sSecStr, fFractionOfSecond, nCntPost, true, nIx,
4085
6.70k
                (bInputLine ? rInfo.nCntPost : 0), rNatNum );
4086
6.70k
    }
4087
0
    else
4088
0
    {
4089
0
        sal_uInt32 nSeconds = static_cast<sal_uInt32>(floor( fTime ));
4090
4091
0
        nCntPost = ImpGetFractionOfSecondString( sSecStr, fTime - nSeconds, nCntPost, false, nIx,
4092
0
                (bInputLine ? rInfo.nCntPost : 0), rNatNum );
4093
4094
0
        if (rInfo.nThousand == 3) // [ss]
4095
0
        {
4096
0
            nHour = 0;
4097
0
            nMin = 0;
4098
0
            nSec = nSeconds;
4099
0
        }
4100
0
        else if (rInfo.nThousand == 2) // [mm]:ss
4101
0
        {
4102
0
            nHour = 0;
4103
0
            nMin = nSeconds / 60;
4104
0
            nSec = nSeconds % 60;
4105
0
        }
4106
0
        else if (rInfo.nThousand == 1) // [hh]:mm:ss
4107
0
        {
4108
0
            nHour = nSeconds / 3600;
4109
0
            nMin = (nSeconds%3600) / 60;
4110
0
            nSec = nSeconds%60;
4111
0
        }
4112
0
        else
4113
0
        {
4114
0
            nHour = 0;  // TODO What should these values be?
4115
0
            nMin  = 0;
4116
0
            nSec  = 0;
4117
0
        }
4118
0
    }
4119
6.70k
    sal_Unicode cAmPm = ' '; // a or p
4120
6.70k
    if (rInfo.nCntExp) // AM/PM
4121
917
    {
4122
917
        if (nHour == 0)
4123
4
        {
4124
4
            nHour = 12;
4125
4
            cAmPm = 'a';
4126
4
        }
4127
913
        else if (nHour < 12)
4128
236
        {
4129
236
            cAmPm = 'a';
4130
236
        }
4131
677
        else
4132
677
        {
4133
677
            cAmPm = 'p';
4134
677
            if (nHour > 12)
4135
677
            {
4136
677
                nHour -= 12;
4137
677
            }
4138
677
        }
4139
917
    }
4140
6.70k
    const sal_uInt16 nCnt = NumFor[nIx].GetCount();
4141
6.70k
    sal_Int32 nLen;
4142
6.70k
    OUString aYear;
4143
136k
    for (sal_uInt16 i = 0; i < nCnt; i++)
4144
129k
    {
4145
129k
        switch (rInfo.nTypeArray[i])
4146
129k
        {
4147
7
        case NF_SYMBOLTYPE_CALENDAR :
4148
7
            if ( !aOrgCalendar.getLength() )
4149
7
            {
4150
7
                aOrgCalendar = rCal.getUniqueID();
4151
7
                fOrgDateTime = rCal.getDateTime();
4152
7
            }
4153
7
            rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLanguageTag().getLocale() );
4154
7
            rCal.setDateTime( fOrgDateTime );
4155
7
            ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4156
7
            break;
4157
3.12k
        case NF_SYMBOLTYPE_STAR:
4158
3.12k
            if( bStarFlag )
4159
0
            {
4160
0
                bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
4161
0
            }
4162
3.12k
            break;
4163
9.95k
        case NF_SYMBOLTYPE_BLANK:
4164
9.95k
            if (rInfo.sStrArray[i].getLength() >= 2)
4165
9.95k
                InsertBlanks( sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
4166
9.95k
            break;
4167
40.6k
        case NF_SYMBOLTYPE_STRING:
4168
40.6k
        case NF_SYMBOLTYPE_CURRENCY:
4169
44.0k
        case NF_SYMBOLTYPE_DATESEP:
4170
46.1k
        case NF_SYMBOLTYPE_TIMESEP:
4171
46.1k
        case NF_SYMBOLTYPE_TIME100SECSEP:
4172
46.1k
            sBuff.append(rInfo.sStrArray[i]);
4173
46.1k
            break;
4174
230
        case NF_SYMBOLTYPE_DIGIT:
4175
230
            nLen = ( bInputLine && i > 0 &&
4176
0
                     (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING ||
4177
0
                      rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ?
4178
230
                     nCntPost : rInfo.sStrArray[i].getLength() );
4179
484
            for (sal_Int32 j = 0; j < nLen && nSecPos < nCntPost && nSecPos < sSecStr.getLength(); ++j)
4180
254
            {
4181
254
                sBuff.append(sSecStr[ nSecPos ]);
4182
254
                nSecPos++;
4183
254
            }
4184
230
            break;
4185
917
        case NF_KEY_AMPM:               // AM/PM
4186
917
            if (cAmPm == 'a')
4187
240
            {
4188
240
                sBuff.append(rCal.getDisplayName( CalendarDisplayIndex::AM_PM,
4189
240
                                                  AmPmValue::AM, 0 ));
4190
240
            }
4191
677
            else
4192
677
            {
4193
677
                sBuff.append(rCal.getDisplayName( CalendarDisplayIndex::AM_PM,
4194
677
                                                  AmPmValue::PM, 0 ));
4195
677
            }
4196
917
            break;
4197
0
        case NF_KEY_AP:                 // A/P
4198
0
            if (cAmPm == 'a')
4199
0
            {
4200
0
                sBuff.append('a');
4201
0
            }
4202
0
            else
4203
0
            {
4204
0
                sBuff.append('p');
4205
0
            }
4206
0
            break;
4207
1.77k
        case NF_KEY_MI:                 // M
4208
1.77k
            sBuff.append(ImpIntToString(rNatNum, nIx, nMin ));
4209
1.77k
            break;
4210
999
        case NF_KEY_MMI:                // MM
4211
999
            sBuff.append(ImpIntToString(rNatNum, nIx, nMin, 2 ));
4212
999
            break;
4213
1.24k
        case NF_KEY_H:                  // H
4214
1.24k
            sBuff.append(ImpIntToString(rNatNum, nIx, nHour ));
4215
1.24k
            break;
4216
1.09k
        case NF_KEY_HH:                 // HH
4217
1.09k
            sBuff.append(ImpIntToString(rNatNum, nIx, nHour, 2 ));
4218
1.09k
            break;
4219
6.50k
        case NF_KEY_S:                  // S
4220
6.50k
            sBuff.append(ImpIntToString(rNatNum, nIx, nSec ));
4221
6.50k
            break;
4222
1.12k
        case NF_KEY_SS:                 // SS
4223
1.12k
            sBuff.append(ImpIntToString(rNatNum, nIx, nSec, 2 ));
4224
1.12k
            break;
4225
5.33k
        case NF_KEY_M:                  // M
4226
5.33k
            sBuff.append(rCal.getDisplayString(
4227
5.33k
                             CalendarDisplayCode::SHORT_MONTH, nNatNum ));
4228
5.33k
            break;
4229
256
        case NF_KEY_MM:                 // MM
4230
256
            sBuff.append(rCal.getDisplayString(
4231
256
                             CalendarDisplayCode::LONG_MONTH, nNatNum ));
4232
256
            break;
4233
178
        case NF_KEY_MMM:                // MMM
4234
178
            sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4235
178
                                                                 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4236
178
                                                nNatNum));
4237
178
            break;
4238
25
        case NF_KEY_MMMM:               // MMMM
4239
25
            sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4240
25
                                                                 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4241
25
                                                nNatNum));
4242
25
            break;
4243
1.88k
        case NF_KEY_MMMMM:              // MMMMM
4244
1.88k
            sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4245
1.88k
                                                                 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4246
1.88k
                                                nNatNum));
4247
1.88k
            break;
4248
1.42k
        case NF_KEY_Q:                  // Q
4249
1.42k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_QUARTER, nNatNum ));
4250
1.42k
            break;
4251
182
        case NF_KEY_QQ:                 // QQ
4252
182
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_QUARTER, nNatNum ));
4253
182
            break;
4254
4.35k
        case NF_KEY_D:                  // D
4255
4.35k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY, nNatNum ));
4256
4.35k
            break;
4257
352
        case NF_KEY_DD:                 // DD
4258
352
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY, nNatNum ));
4259
352
            break;
4260
285
        case NF_KEY_DDD:                // DDD
4261
285
            if ( bOtherCalendar )
4262
223
            {
4263
223
                SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4264
223
            }
4265
285
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
4266
285
            if ( bOtherCalendar )
4267
223
            {
4268
223
                SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4269
223
            }
4270
285
            break;
4271
3.36k
        case NF_KEY_DDDD:               // DDDD
4272
3.36k
            if ( bOtherCalendar )
4273
2.80k
            {
4274
2.80k
                SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4275
2.80k
            }
4276
3.36k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4277
3.36k
            if ( bOtherCalendar )
4278
2.80k
            {
4279
2.80k
                SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4280
2.80k
            }
4281
3.36k
            break;
4282
2.10k
        case NF_KEY_YY:                 // YY
4283
2.10k
            if ( bOtherCalendar )
4284
1.90k
            {
4285
1.90k
                SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4286
1.90k
            }
4287
            // Prepend a minus sign if Gregorian BCE and era is not displayed.
4288
2.10k
            if (lcl_isSignedYear( rCal, NumFor[nIx] ))
4289
0
            {
4290
0
                sBuff.append('-');
4291
0
            }
4292
2.10k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
4293
2.10k
            if ( bOtherCalendar )
4294
1.90k
            {
4295
1.90k
                SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4296
1.90k
            }
4297
2.10k
            break;
4298
17.4k
        case NF_KEY_YYYY:               // YYYY
4299
17.4k
            if ( bOtherCalendar )
4300
14.1k
            {
4301
14.1k
                SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4302
14.1k
            }
4303
            // Prepend a minus sign if Gregorian BCE and era is not displayed.
4304
17.4k
            if (lcl_isSignedYear( rCal, NumFor[nIx] ))
4305
0
            {
4306
0
                sBuff.append('-');
4307
0
            }
4308
17.4k
            aYear = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
4309
17.4k
            if (aYear.getLength() < 4 && !lcl_hasEra(NumFor[nIx]))
4310
0
            {
4311
0
                using namespace comphelper::string;
4312
                // Ensure that year consists of at least 4 digits, so it
4313
                // can be distinguished from 2 digits display and edited
4314
                // without suddenly being hit by the 2-digit year magic.
4315
0
                OUStringBuffer aBuf;
4316
0
                padToLength(aBuf, 4 - aYear.getLength(), '0');
4317
0
                ::impTransliterate(aBuf, NumFor[nIx].GetNatNum(), rNatNum);
4318
0
                aBuf.append(aYear);
4319
0
                sBuff.append(aBuf);
4320
0
            }
4321
17.4k
            else
4322
17.4k
            {
4323
17.4k
                sBuff.append(aYear);
4324
17.4k
            }
4325
17.4k
            if ( bOtherCalendar )
4326
14.1k
            {
4327
14.1k
                SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime, *rCurrentLang.GetCalendar() );
4328
14.1k
            }
4329
17.4k
            break;
4330
2.35k
        case NF_KEY_EC:                 // E
4331
2.35k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
4332
2.35k
            break;
4333
1.07k
        case NF_KEY_EEC:                // EE
4334
7.73k
        case NF_KEY_R:                  // R
4335
7.73k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum ));
4336
7.73k
            break;
4337
14
        case NF_KEY_NN:                 // NN
4338
129
        case NF_KEY_AAA:                // AAA
4339
129
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
4340
129
            break;
4341
57
        case NF_KEY_NNN:                // NNN
4342
433
        case NF_KEY_AAAA:               // AAAA
4343
433
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4344
433
            break;
4345
57
        case NF_KEY_NNNN:               // NNNN
4346
57
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4347
57
            sBuff.append(rLoc().getLongDateDayOfWeekSep());
4348
57
            break;
4349
362
        case NF_KEY_WW :                // WW
4350
362
            sBuff.append(ImpIntToString(rNatNum, nIx, rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR )));
4351
362
            break;
4352
3.32k
        case NF_KEY_G:                  // G
4353
3.32k
            ImpAppendEraG( sBuff, rCal, nNatNum );
4354
3.32k
            break;
4355
253
        case NF_KEY_GG:                 // GG
4356
253
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
4357
253
            break;
4358
872
        case NF_KEY_GGG:                // GGG
4359
872
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_ERA, nNatNum ));
4360
872
            break;
4361
3.54k
        case NF_KEY_RR:                 // RR => GGGEE
4362
3.54k
            sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ));
4363
3.54k
            break;
4364
129k
        }
4365
129k
    }
4366
6.70k
    if ( aOrgCalendar.getLength() )
4367
4.61k
    {
4368
4.61k
        rCal.loadCalendar( aOrgCalendar, rLoc().getLanguageTag().getLocale() );  // restore calendar
4369
4.61k
    }
4370
6.70k
    return bRes;
4371
6.70k
}
4372
4373
bool SvNumberformat::ImpGetLogicalOutput(double fNumber,
4374
                                         sal_uInt16 nIx,
4375
                                         const NativeNumberWrapper& rNatNum,
4376
                                         OUStringBuffer& sStr) const
4377
31
{
4378
31
    bool bRes = false;
4379
31
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4380
31
    const sal_uInt16 nCnt = NumFor[nIx].GetCount();
4381
2.35k
    for (sal_uInt16 j = 0; j < nCnt; ++j)
4382
2.32k
    {
4383
2.32k
        switch (rInfo.nTypeArray[j])
4384
2.32k
        {
4385
31
            case NF_KEY_BOOLEAN:
4386
31
                sStr.append( fNumber ? rScan.GetTrueString() : rScan.GetFalseString());
4387
31
            break;
4388
894
            case NF_SYMBOLTYPE_STRING:
4389
894
                sStr.append( rInfo.sStrArray[j]);
4390
894
            break;
4391
2.32k
        }
4392
2.32k
    }
4393
31
    ::impTransliterate(sStr, NumFor[nIx].GetNatNum(), rNatNum);
4394
31
    return bRes;
4395
31
}
4396
4397
bool SvNumberformat::ImpGetNumberOutput(double fNumber,
4398
                                        sal_uInt16 nIx,
4399
                                        bool bStarFlag,
4400
                                        const NativeNumberWrapper& rNatNum,
4401
                                        OUStringBuffer& sStr) const
4402
74.7k
{
4403
74.7k
    bool bRes = false;
4404
74.7k
    bool bSign;
4405
74.7k
    if (fNumber < 0.0)
4406
558
    {
4407
558
        bSign = (nIx == 0); // Not in the ones at the back;
4408
558
        fNumber = -fNumber;
4409
558
    }
4410
74.1k
    else
4411
74.1k
    {
4412
74.1k
        bSign = false;
4413
74.1k
        if ( std::signbit( fNumber ) )
4414
7
        {
4415
7
            fNumber = -fNumber; // yes, -0.0 is possible, eliminate '-'
4416
7
        }
4417
74.1k
    }
4418
74.7k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4419
74.7k
    if (rInfo.eScannedType == SvNumFormatType::PERCENT)
4420
4.26k
    {
4421
4.26k
        if (fNumber < D_MAX_D_BY_100)
4422
4.26k
        {
4423
4.26k
            fNumber *= 100.0;
4424
4.26k
        }
4425
0
        else
4426
0
        {
4427
0
            sStr = ImpSvNumberformatScan::sErrStr;
4428
0
            return false;
4429
0
        }
4430
4.26k
    }
4431
74.7k
    sal_uInt16 i, j;
4432
74.7k
    sal_Int32 nDecPos = -1;
4433
74.7k
    bool bInteger = false;
4434
74.7k
    if ( rInfo.nThousand != FLAG_STANDARD_IN_FORMAT )
4435
74.7k
    {
4436
        // Special formatting only if no GENERAL keyword in format code
4437
74.7k
        const sal_uInt16 nThousand = rInfo.nThousand;
4438
74.7k
        tools::Long nPrecExp;
4439
76.0k
        for (i = 0; i < nThousand; i++)
4440
1.27k
        {
4441
1.27k
           if (fNumber > D_MIN_M_BY_1000)
4442
1.25k
           {
4443
1.25k
               fNumber /= 1000.0;
4444
1.25k
           }
4445
19
           else
4446
19
           {
4447
19
               fNumber = 0.0;
4448
19
           }
4449
1.27k
        }
4450
74.7k
        if (fNumber > 0.0)
4451
64.1k
        {
4452
64.1k
            nPrecExp = GetPrecExp( fNumber );
4453
64.1k
        }
4454
10.6k
        else
4455
10.6k
        {
4456
10.6k
            nPrecExp = 0;
4457
10.6k
        }
4458
        // Make sure that Calc's ROUND and formatted output agree
4459
74.7k
        fNumber = rtl_math_round(fNumber, rInfo.nCntPost, rtl_math_RoundingMode_Corrected);
4460
74.7k
        if (rInfo.nCntPost) // Decimal places
4461
36.6k
        {
4462
36.6k
            if ((rInfo.nCntPost + nPrecExp) > 15 && nPrecExp < 15)
4463
8.71k
            {
4464
8.71k
                sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, 15-nPrecExp, '.');
4465
68.6k
                for (tools::Long l = 15-nPrecExp; l < static_cast<tools::Long>(rInfo.nCntPost); l++)
4466
59.9k
                {
4467
59.9k
                    sStr.append('0');
4468
59.9k
                }
4469
8.71k
            }
4470
27.9k
            else
4471
27.9k
            {
4472
27.9k
                sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, rInfo.nCntPost, '.' );
4473
27.9k
            }
4474
36.6k
            sStr.stripStart('0'); // Strip leading zeros
4475
36.6k
        }
4476
38.0k
        else if (fNumber == 0.0) // Null
4477
8.59k
        {
4478
            // Nothing to be done here, keep empty string sStr,
4479
            // ImpNumberFillWithThousands does the rest
4480
8.59k
        }
4481
29.4k
        else // Integer
4482
29.4k
        {
4483
29.4k
            sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, 0, '.');
4484
29.4k
            sStr.stripStart('0'); // Strip leading zeros
4485
29.4k
        }
4486
74.7k
        nDecPos = sStr.indexOf('.' );
4487
74.7k
        if ( nDecPos >= 0)
4488
36.6k
        {
4489
36.6k
            const sal_Unicode* p = sStr.getStr() + nDecPos;
4490
230k
            while ( *++p == '0' )
4491
193k
                ;
4492
36.6k
            if ( !*p )
4493
23.9k
            {
4494
23.9k
                bInteger = true;
4495
23.9k
            }
4496
36.6k
            sStr.remove( nDecPos, 1 ); //  Remove .
4497
36.6k
        }
4498
74.7k
        if (bSign && (sStr.isEmpty() || checkForAll0s(sStr)))   // Only 00000
4499
40
        {
4500
40
            bSign = false;              // Not -0.00
4501
40
        }
4502
74.7k
    }                                   // End of != FLAG_STANDARD_IN_FORMAT
4503
4504
                                        // Edit backwards:
4505
74.7k
    j = NumFor[nIx].GetCount()-1;       // Last symbol
4506
                                        // Decimal places:
4507
74.7k
    bRes |= ImpDecimalFill(rNatNum, sStr, fNumber, nDecPos, j, nIx, bInteger, bStarFlag);
4508
74.7k
    if (bSign)
4509
515
    {
4510
515
        sStr.insert(0, '-');
4511
515
    }
4512
74.7k
    ::impTransliterate(sStr, NumFor[nIx].GetNatNum(), rNatNum);
4513
74.7k
    return bRes;
4514
74.7k
}
4515
4516
bool SvNumberformat::ImpDecimalFill(const NativeNumberWrapper& rNatNum,
4517
                                   OUStringBuffer& sStr,  // number string
4518
                                   double& rNumber,       // number
4519
                                   sal_Int32 nDecPos,     // decimals start
4520
                                   sal_uInt16 j,          // symbol index within format code
4521
                                   sal_uInt16 nIx,        // subformat index
4522
                                   bool bInteger,         // is integer
4523
                                   bool bStarFlag) const
4524
75.5k
{
4525
75.5k
    bool bRes = false;
4526
75.5k
    bool bFilled = false;               // Was filled?
4527
75.5k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4528
75.5k
    sal_Int32 k = sStr.getLength();     // After last figure
4529
                                        // Decimal places:
4530
75.5k
    if (rInfo.nCntPost > 0)
4531
37.2k
    {
4532
37.2k
        bool bTrailing = true;          // Trailing zeros?
4533
37.2k
        short nType;
4534
90.4k
        while (j > 0 &&                 // Backwards
4535
89.9k
               (nType = rInfo.nTypeArray[j]) != NF_SYMBOLTYPE_DECSEP)
4536
53.2k
        {
4537
53.2k
            switch ( nType )
4538
53.2k
            {
4539
120
            case NF_SYMBOLTYPE_STAR:
4540
120
                if( bStarFlag )
4541
0
                {
4542
0
                    bRes = lcl_insertStarFillChar( sStr, k, rInfo.sStrArray[j]);
4543
0
                }
4544
120
                break;
4545
1.33k
            case NF_SYMBOLTYPE_BLANK:
4546
1.33k
                if (rInfo.sStrArray[j].getLength() >= 2)
4547
1.33k
                    /*k = */ InsertBlanks(sStr, k, rInfo.sStrArray[j][1] );
4548
1.33k
                break;
4549
3.32k
            case NF_SYMBOLTYPE_STRING:
4550
4.31k
            case NF_SYMBOLTYPE_CURRENCY:
4551
6.57k
            case NF_SYMBOLTYPE_PERCENT:
4552
6.57k
                sStr.insert(k, rInfo.sStrArray[j]);
4553
6.57k
                break;
4554
770
            case NF_SYMBOLTYPE_THSEP:
4555
770
                if (rInfo.nThousand == 0)
4556
0
                {
4557
0
                    sStr.insert(k, rInfo.sStrArray[j]);
4558
0
                }
4559
770
                break;
4560
37.8k
            case NF_SYMBOLTYPE_DIGIT:
4561
37.8k
            {
4562
37.8k
                const OUString& rStr = rInfo.sStrArray[j];
4563
37.8k
                const sal_Unicode* p1 = rStr.getStr();
4564
37.8k
                const sal_Unicode* p = p1 + rStr.getLength();
4565
                // In case the number of decimals passed are less than the
4566
                // "digits" given, append trailing '0' characters, which here
4567
                // means insert them because literal strings may have been
4568
                // appended already. If they weren't to be '0' characters
4569
                // they'll be changed below, as if decimals with trailing zeros
4570
                // were passed.
4571
37.8k
                if (nDecPos >= 0 && nDecPos <= k)
4572
37.8k
                {
4573
37.8k
                    sal_Int32 nAppend = rStr.getLength() - (k - nDecPos);
4574
38.3k
                    while (nAppend-- > 0)
4575
429
                    {
4576
429
                        sStr.insert( k++, '0');
4577
429
                    }
4578
37.8k
                }
4579
268k
                while (k && p1 < p--)
4580
230k
                {
4581
230k
                    const sal_Unicode c = *p;
4582
230k
                    k--;
4583
230k
                    if ( sStr[k] != '0' )
4584
31.2k
                    {
4585
31.2k
                        bTrailing = false;
4586
31.2k
                        bFilled = true;
4587
31.2k
                    }
4588
230k
                    if (bTrailing)
4589
194k
                    {
4590
194k
                        if ( c == '0' )
4591
192k
                        {
4592
192k
                            bFilled = true;
4593
192k
                        }
4594
2.35k
                        else if ( c == '-' )
4595
338
                        {
4596
338
                            if ( bInteger )
4597
271
                            {
4598
271
                                sStr[ k ] = '-';
4599
271
                            }
4600
338
                            bFilled = true;
4601
338
                        }
4602
2.01k
                        else if ( c == '?' )
4603
221
                        {
4604
221
                            sStr[ k ] = cBlankDigit;
4605
221
                            bFilled = true;
4606
221
                        }
4607
1.79k
                        else if ( !bFilled ) // #
4608
1.66k
                        {
4609
1.66k
                            sStr.remove(k,1);
4610
1.66k
                        }
4611
194k
                    }
4612
230k
                } // of for
4613
37.8k
                break;
4614
4.31k
            } // of case digi
4615
204
            case NF_KEY_CCC: // CCC currency
4616
204
                sStr.insert(k, rScan.GetCurAbbrev());
4617
204
                break;
4618
0
            case NF_KEY_GENERAL: // Standard in the String
4619
0
            {
4620
0
                OUStringBuffer sNum;
4621
0
                ImpGetOutputStandard(rNumber, sNum, rNatNum);
4622
0
                sNum.stripStart('-');
4623
0
                sStr.insert(k, sNum);
4624
0
                break;
4625
4.31k
            }
4626
6.30k
            default:
4627
6.30k
                break;
4628
53.2k
            } // of switch
4629
53.2k
            j--;
4630
53.2k
        } // of while
4631
37.2k
    } // of decimal places
4632
4633
75.5k
    bRes |= ImpNumberFillWithThousands(rNatNum, sStr, rNumber, k, j, nIx, // Fill with . if needed
4634
75.5k
                                       rInfo.nCntPre, bStarFlag, bFilled );
4635
4636
75.5k
    return bRes;
4637
75.5k
}
4638
4639
bool SvNumberformat::ImpNumberFillWithThousands( const NativeNumberWrapper& rNatNum,
4640
                                                 OUStringBuffer& sBuff,  // number string
4641
                                                 double& rNumber,       // number
4642
                                                 sal_Int32 k,           // position within string
4643
                                                 sal_uInt16 j,          // symbol index within format code
4644
                                                 sal_uInt16 nIx,        // subformat index
4645
                                                 sal_Int32 nDigCnt,     // count of integer digits in format
4646
                                                 bool bStarFlag,
4647
                                                 bool bAddDecSep) const // add decimal separator if necessary
4648
75.9k
{
4649
75.9k
    bool bRes = false;
4650
75.9k
    sal_Int32 nLeadingStringChars = 0; // inserted StringChars before number
4651
75.9k
    sal_Int32 nDigitCount = 0;         // count of integer digits from the right
4652
75.9k
    bool bStop = false;
4653
75.9k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4654
    // no normal thousands separators if number divided by thousands
4655
75.9k
    bool bDoThousands = (rInfo.nThousand == 0);
4656
75.9k
    utl::DigitGroupingIterator aGrouping( GetCurrentLanguageData().GetLocaleData()->getDigitGrouping());
4657
4658
273k
    while (!bStop) // backwards
4659
197k
    {
4660
197k
        if (j == 0)
4661
75.9k
        {
4662
75.9k
            bStop = true;
4663
75.9k
        }
4664
197k
        switch (rInfo.nTypeArray[j])
4665
197k
        {
4666
37.2k
        case NF_SYMBOLTYPE_DECSEP:
4667
37.2k
            aGrouping.reset();
4668
37.2k
            [[fallthrough]];
4669
49.4k
        case NF_SYMBOLTYPE_STRING:
4670
49.4k
            if ( rInfo.nTypeArray[j] == NF_SYMBOLTYPE_STRING && nDigCnt == 0 )
4671
203
            {
4672
                // tdf#159930 no integer in format ".###"
4673
203
                k = 0; // insert string at the beginning
4674
203
            }
4675
49.4k
            [[fallthrough]];
4676
52.9k
        case NF_SYMBOLTYPE_CURRENCY:
4677
54.9k
        case NF_SYMBOLTYPE_PERCENT:
4678
54.9k
            if ( rInfo.nTypeArray[j] != NF_SYMBOLTYPE_DECSEP || bAddDecSep )
4679
54.3k
                sBuff.insert(k, rInfo.sStrArray[j]);
4680
54.9k
            if ( k == 0 )
4681
20.4k
            {
4682
20.4k
                nLeadingStringChars = nLeadingStringChars + rInfo.sStrArray[j].getLength();
4683
20.4k
            }
4684
54.9k
            break;
4685
4.08k
        case NF_SYMBOLTYPE_STAR:
4686
4.08k
            if( bStarFlag )
4687
0
            {
4688
0
                bRes = lcl_insertStarFillChar( sBuff, k, rInfo.sStrArray[j]);
4689
0
            }
4690
4.08k
            break;
4691
8.19k
        case NF_SYMBOLTYPE_BLANK:
4692
8.19k
            if (rInfo.sStrArray[j].getLength() >= 2)
4693
8.18k
                /*k = */ InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] );
4694
8.19k
            break;
4695
18.8k
        case NF_SYMBOLTYPE_THSEP:
4696
            // #i7284# #102685# Insert separator also if number is divided
4697
            // by thousands and the separator is specified somewhere in
4698
            // between and not only at the end.
4699
            // #i12596# But do not insert if it's a parenthesized negative
4700
            // format like (#,)
4701
            // In fact, do not insert if divided and regex [0#,],[^0#] and
4702
            // no other digit symbol follows (which was already detected
4703
            // during scan of format code, otherwise there would be no
4704
            // division), else do insert. Same in ImpNumberFill() below.
4705
18.8k
            if ( !bDoThousands && j < NumFor[nIx].GetCount()-1 )
4706
1.10k
            {
4707
1.10k
                bDoThousands = ((j == 0) ||
4708
1.10k
                                (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT &&
4709
0
                                 rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) ||
4710
1.10k
                                (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT));
4711
1.10k
            }
4712
18.8k
            if ( bDoThousands )
4713
18.3k
            {
4714
18.3k
                if (k > 0)
4715
7.02k
                {
4716
7.02k
                    sBuff.insert(k, rInfo.sStrArray[j]);
4717
7.02k
                }
4718
11.3k
                else if (nDigitCount < nDigCnt)
4719
11.3k
                {
4720
                    // Leading '#' displays nothing (e.g. no leading
4721
                    // separator for numbers <1000 with #,##0 format).
4722
                    // Leading '?' displays blank.
4723
                    // Everything else, including nothing, displays the
4724
                    // separator.
4725
11.3k
                    sal_Unicode cLeader = 0;
4726
11.3k
                    if (j > 0 && rInfo.nTypeArray[j-1] == NF_SYMBOLTYPE_DIGIT)
4727
11.3k
                    {
4728
11.3k
                        const OUString& rStr = rInfo.sStrArray[j-1];
4729
11.3k
                        sal_Int32 nLen = rStr.getLength();
4730
11.3k
                        if (nLen)
4731
11.3k
                        {
4732
11.3k
                            cLeader = rStr[ nLen - 1 ];
4733
11.3k
                        }
4734
11.3k
                    }
4735
11.3k
                    switch (cLeader)
4736
11.3k
                    {
4737
11.2k
                    case '#':
4738
11.2k
                        ;   // nothing
4739
11.2k
                        break;
4740
47
                    case '?':
4741
                        // replace thousand separator with blank
4742
47
                        sBuff.insert(k, ' ');
4743
47
                        break;
4744
17
                    default:
4745
17
                        sBuff.insert(k, rInfo.sStrArray[j]);
4746
11.3k
                    }
4747
11.3k
                }
4748
18.3k
                aGrouping.advance();
4749
18.3k
            }
4750
18.8k
            break;
4751
94.4k
        case NF_SYMBOLTYPE_DIGIT:
4752
94.4k
        {
4753
94.4k
            const OUString& rStr = rInfo.sStrArray[j];
4754
94.4k
            const sal_Unicode* p1 = rStr.getStr();
4755
94.4k
            const sal_Unicode* p = p1 + rStr.getLength();
4756
243k
            while ( p1 < p-- )
4757
148k
            {
4758
148k
                nDigitCount++;
4759
148k
                if (k > 0)
4760
96.8k
                {
4761
96.8k
                    k--;
4762
96.8k
                }
4763
52.1k
                else
4764
52.1k
                {
4765
52.1k
                    switch (*p)
4766
52.1k
                    {
4767
16.5k
                    case '0':
4768
16.5k
                        sBuff.insert(0, '0');
4769
16.5k
                        break;
4770
2.43k
                    case '?':
4771
2.43k
                        sBuff.insert(0, cBlankDigit);
4772
2.43k
                        break;
4773
52.1k
                    }
4774
52.1k
                }
4775
148k
                if (nDigitCount == nDigCnt && k > 0)
4776
29.4k
                {
4777
                    // more digits than specified
4778
29.4k
                    ImpDigitFill(sBuff, 0, k, nIx, nDigitCount, aGrouping);
4779
29.4k
                }
4780
148k
            }
4781
94.4k
            break;
4782
94.4k
        }
4783
94.4k
        case NF_KEY_CCC: // CCC currency
4784
193
            sBuff.insert(k, rScan.GetCurAbbrev());
4785
193
            break;
4786
20
        case NF_KEY_GENERAL: // "General" in string
4787
20
        {
4788
20
            OUStringBuffer sNum;
4789
20
            ImpGetOutputStandard(rNumber, sNum, rNatNum);
4790
20
            sNum.stripStart('-');
4791
20
            sBuff.insert(k, sNum);
4792
20
            break;
4793
94.4k
        }
4794
16.7k
        default:
4795
16.7k
            break;
4796
197k
        } // switch
4797
197k
        j--; // next format code string
4798
197k
    } // while
4799
4800
75.9k
    k = k + nLeadingStringChars;    // MSC converts += to int and then warns, so ...
4801
75.9k
    if (k > nLeadingStringChars)
4802
396
    {
4803
396
        ImpDigitFill(sBuff, nLeadingStringChars, k, nIx, nDigitCount, aGrouping);
4804
396
    }
4805
75.9k
    return bRes;
4806
75.9k
}
4807
4808
void SvNumberformat::ImpDigitFill(OUStringBuffer& sStr,     // number string
4809
                                  sal_Int32 nStart,         // start of digits
4810
                                  sal_Int32 & k,            // position within string
4811
                                  sal_uInt16 nIx,           // subformat index
4812
                                  sal_Int32 & nDigitCount,  // count of integer digits from the right so far
4813
                                  utl::DigitGroupingIterator & rGrouping ) const // current grouping
4814
29.8k
{
4815
29.8k
    if (NumFor[nIx].Info().bThousand) // Only if grouping fill in separators
4816
3.77k
    {
4817
3.77k
        const OUString& rThousandSep = GetCurrentLanguageData().GetNumThousandSep();
4818
15.0k
        while (k > nStart)
4819
11.2k
        {
4820
11.2k
            if (nDigitCount == rGrouping.getPos())
4821
2.36k
            {
4822
2.36k
                sStr.insert( k, rThousandSep );
4823
2.36k
                rGrouping.advance();
4824
2.36k
            }
4825
11.2k
            nDigitCount++;
4826
11.2k
            k--;
4827
11.2k
        }
4828
3.77k
    }
4829
26.0k
    else // simply skip
4830
26.0k
    {
4831
26.0k
        k = nStart;
4832
26.0k
    }
4833
29.8k
}
4834
4835
bool SvNumberformat::ImpNumberFill( const NativeNumberWrapper& rNatNum,
4836
                                    OUStringBuffer& sBuff, // number string
4837
                                    double& rNumber,       // number for "General" format
4838
                                    sal_Int32& k,          // position within string
4839
                                    sal_uInt16& j,         // symbol index within format code
4840
                                    sal_uInt16 nIx,        // subformat index
4841
                                    short eSymbolType,     // type of stop condition
4842
                                    bool bStarFlag,
4843
                                    bool bInsertRightBlank) const // insert blank on right for denominator (default = false)
4844
7.88k
{
4845
7.88k
    bool bRes = false;
4846
7.88k
    bool bStop = false;
4847
7.88k
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4848
    // no normal thousands separators if number divided by thousands
4849
7.88k
    bool bDoThousands = (rInfo.nThousand == 0);
4850
7.88k
    bool bFoundNumber = false;
4851
7.88k
    short nType;
4852
4853
7.88k
    k = sBuff.getLength(); // behind last digit
4854
4855
97.7k
    while (!bStop && (nType = rInfo.nTypeArray[j]) != eSymbolType ) // Backwards
4856
89.9k
    {
4857
89.9k
        switch ( nType )
4858
89.9k
        {
4859
1
        case NF_SYMBOLTYPE_STAR:
4860
1
            if( bStarFlag )
4861
0
            {
4862
0
                if ( bFoundNumber && eSymbolType != NF_SYMBOLTYPE_EXP )
4863
0
                    k = 0; // tdf#100842 jump to beginning of number before inserting something else
4864
0
                bRes = lcl_insertStarFillChar( sBuff, k, rInfo.sStrArray[j]);
4865
0
            }
4866
1
            break;
4867
124
        case NF_SYMBOLTYPE_BLANK:
4868
124
            if (rInfo.sStrArray[j].getLength() >= 2)
4869
124
            {
4870
124
                if ( bFoundNumber && eSymbolType != NF_SYMBOLTYPE_EXP )
4871
0
                    k = 0; // tdf#100842 jump to beginning of number before inserting something else
4872
124
                k = InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] );
4873
124
            }
4874
124
            break;
4875
0
        case NF_SYMBOLTYPE_THSEP:
4876
            // Same as in ImpNumberFillWithThousands() above, do not insert
4877
            // if divided and regex [0#,],[^0#] and no other digit symbol
4878
            // follows (which was already detected during scan of format
4879
            // code, otherwise there would be no division), else do insert.
4880
0
            if ( !bDoThousands && j < NumFor[nIx].GetCount()-1 )
4881
0
            {
4882
0
                bDoThousands = ((j == 0) ||
4883
0
                                (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT &&
4884
0
                                 rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) ||
4885
0
                                (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT));
4886
0
            }
4887
0
            if ( bDoThousands && k > 0 )
4888
0
            {
4889
0
                sBuff.insert(k, rInfo.sStrArray[j]);
4890
0
            }
4891
0
            break;
4892
10.2k
        case NF_SYMBOLTYPE_DIGIT:
4893
10.2k
        {
4894
10.2k
            bFoundNumber = true;
4895
10.2k
            sal_uInt16 nPosInsertBlank = bInsertRightBlank ? k : 0; // left alignment of denominator
4896
10.2k
            const OUString& rStr = rInfo.sStrArray[j];
4897
10.2k
            const sal_Unicode* p1 = rStr.getStr();
4898
10.2k
            const sal_Unicode* p = p1 + rStr.getLength();
4899
24.0k
            while ( p1 < p-- )
4900
13.7k
            {
4901
13.7k
                if (k > 0)
4902
3.98k
                {
4903
3.98k
                    k--;
4904
3.98k
                }
4905
9.75k
                else
4906
9.75k
                {
4907
9.75k
                    switch (*p)
4908
9.75k
                    {
4909
7.66k
                    case '0':
4910
7.66k
                        sBuff.insert(0, '0');
4911
7.66k
                        break;
4912
620
                    case '?':
4913
620
                        sBuff.insert(nPosInsertBlank, cBlankDigit);
4914
620
                        break;
4915
9.75k
                    }
4916
9.75k
                }
4917
13.7k
            }
4918
10.2k
        }
4919
10.2k
        break;
4920
10.2k
        case NF_KEY_CCC:                // CCC currency
4921
0
            sBuff.insert(k, rScan.GetCurAbbrev());
4922
0
            break;
4923
0
        case NF_KEY_GENERAL: // Standard in the String
4924
0
        {
4925
0
            OUStringBuffer sNum;
4926
0
            bFoundNumber = true;
4927
0
            ImpGetOutputStandard(rNumber, sNum, rNatNum);
4928
0
            sNum.stripStart('-');
4929
0
            sBuff.insert(k, sNum);
4930
0
        }
4931
0
        break;
4932
64.4k
        case NF_SYMBOLTYPE_FRAC_FDIV: // Do Nothing
4933
64.4k
            if (k > 0)
4934
7.08k
            {
4935
7.08k
                k--;
4936
7.08k
            }
4937
64.4k
            break;
4938
4939
15.0k
        default:
4940
15.0k
            if ( bFoundNumber && eSymbolType != NF_SYMBOLTYPE_EXP )
4941
1.56k
                k = 0; // tdf#100842 jump to beginning of number before inserting something else
4942
15.0k
            sBuff.insert(k, rInfo.sStrArray[j]);
4943
15.0k
            break;
4944
89.9k
        } // of switch
4945
89.9k
        if ( j )
4946
88.8k
            j--; // Next String
4947
1.04k
        else
4948
1.04k
            bStop = true;
4949
89.9k
    } // of while
4950
7.88k
    return bRes;
4951
7.88k
}
4952
4953
void SvNumberformat::GetFormatSpecialInfo(bool& bThousand,
4954
                                          bool& IsRed,
4955
                                          sal_uInt16& nPrecision,
4956
                                          sal_uInt16& nLeadingCnt) const
4957
22.4k
{
4958
    // as before: take info from nNumFor=0 for whole format (for dialog etc.)
4959
4960
22.4k
    SvNumFormatType nDummyType;
4961
22.4k
    GetNumForInfo( 0, nDummyType, bThousand, nPrecision, nLeadingCnt );
4962
4963
    // "negative in red" is only useful for the whole format
4964
4965
22.4k
    const Color* pColor = NumFor[1].GetColor();
4966
22.4k
    IsRed = fLimit1 == 0.0 && fLimit2 == 0.0 && pColor
4967
0
        && (*pColor == ImpSvNumberformatScan::GetRedColor());
4968
22.4k
}
4969
4970
void SvNumberformat::GetNumForInfo( sal_uInt16 nNumFor, SvNumFormatType& rScannedType,
4971
                    bool& bThousand, sal_uInt16& nPrecision, sal_uInt16& nLeadingCnt ) const
4972
22.4k
{
4973
    // take info from a specified sub-format (for XML export)
4974
4975
22.4k
    if ( nNumFor > 3 )
4976
0
    {
4977
0
        return; // invalid
4978
0
    }
4979
4980
22.4k
    const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
4981
22.4k
    rScannedType = rInfo.eScannedType;
4982
22.4k
    bThousand = rInfo.bThousand;
4983
22.4k
    nPrecision = (rInfo.eScannedType == SvNumFormatType::FRACTION)
4984
22.4k
                    ? rInfo.nCntExp  // number of denominator digits for fraction
4985
22.4k
                    : rInfo.nCntPost;
4986
22.4k
    sal_Int32 nPosHash = 1;
4987
22.4k
    if ( rInfo.eScannedType == SvNumFormatType::FRACTION &&
4988
0
            ( (nPosHash += GetDenominatorString(nNumFor).indexOf('#')) > 0 ) )
4989
0
        nPrecision -= nPosHash;
4990
22.4k
    if (bStandard && rInfo.eScannedType == SvNumFormatType::NUMBER)
4991
22.4k
    {
4992
        // StandardFormat
4993
22.4k
        nLeadingCnt = 1;
4994
22.4k
    }
4995
0
    else
4996
0
    {
4997
0
        nLeadingCnt = 0;
4998
0
        bool bStop = false;
4999
0
        sal_uInt16 i = 0;
5000
0
        const sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
5001
0
        while (!bStop && i < nCnt)
5002
0
        {
5003
0
            short nType = rInfo.nTypeArray[i];
5004
0
            if ( nType == NF_SYMBOLTYPE_DIGIT)
5005
0
            {
5006
0
                const sal_Unicode* p = rInfo.sStrArray[i].getStr();
5007
0
                while ( *p == '#' )
5008
0
                {
5009
0
                    p++;
5010
0
                }
5011
0
                while ( *p == '0' || *p == '?' )
5012
0
                {
5013
0
                    nLeadingCnt++;
5014
0
                    p++;
5015
0
                }
5016
0
            }
5017
0
            else if (nType == NF_SYMBOLTYPE_DECSEP
5018
0
                  || nType == NF_SYMBOLTYPE_EXP
5019
0
                  || nType == NF_SYMBOLTYPE_FRACBLANK)  // Fraction: stop after integer part,
5020
0
            {                                           // do not count '0' of fraction
5021
0
                bStop = true;
5022
0
            }
5023
0
            i++;
5024
0
        }
5025
0
    }
5026
22.4k
}
5027
5028
const OUString* SvNumberformat::GetNumForString( sal_uInt16 nNumFor, sal_uInt16 nPos,
5029
            bool bString /* = false */ ) const
5030
1.36M
{
5031
1.36M
    if ( nNumFor > 3 )
5032
0
    {
5033
0
        return nullptr;
5034
0
    }
5035
1.36M
    sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
5036
1.36M
    if ( !nCnt )
5037
685k
    {
5038
685k
        return nullptr;
5039
685k
    }
5040
677k
    if ( nPos == 0xFFFF )
5041
122k
    {
5042
122k
        nPos = nCnt - 1;
5043
122k
        if ( bString )
5044
122k
        {   // Backwards
5045
122k
            short const * pType = NumFor[nNumFor].Info().nTypeArray.data() + nPos;
5046
255k
            while ( nPos > 0 && (*pType != NF_SYMBOLTYPE_STRING) &&
5047
149k
                    (*pType != NF_SYMBOLTYPE_CURRENCY) )
5048
132k
            {
5049
132k
                pType--;
5050
132k
                nPos--;
5051
132k
            }
5052
122k
            if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) )
5053
13.1k
            {
5054
13.1k
                return nullptr;
5055
13.1k
            }
5056
122k
        }
5057
122k
    }
5058
554k
    else if ( nPos > nCnt - 1 )
5059
17.7k
    {
5060
17.7k
        return nullptr;
5061
17.7k
    }
5062
537k
    else if ( bString )
5063
531k
    {
5064
        // forward
5065
531k
        short const * pType = NumFor[nNumFor].Info().nTypeArray.data() + nPos;
5066
1.02M
        while ( nPos < nCnt && (*pType != NF_SYMBOLTYPE_STRING) &&
5067
527k
                (*pType != NF_SYMBOLTYPE_CURRENCY) )
5068
489k
        {
5069
489k
            pType++;
5070
489k
            nPos++;
5071
489k
        }
5072
531k
        if ( nPos >= nCnt || ((*pType != NF_SYMBOLTYPE_STRING) &&
5073
38.1k
                              (*pType != NF_SYMBOLTYPE_CURRENCY)) )
5074
110k
        {
5075
110k
            return nullptr;
5076
110k
        }
5077
531k
    }
5078
535k
    return &NumFor[nNumFor].Info().sStrArray[nPos];
5079
677k
}
5080
5081
short SvNumberformat::GetNumForType( sal_uInt16 nNumFor, sal_uInt16 nPos ) const
5082
0
{
5083
0
    if ( nNumFor > 3 )
5084
0
    {
5085
0
        return 0;
5086
0
    }
5087
0
    sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
5088
0
    if ( !nCnt )
5089
0
    {
5090
0
        return 0;
5091
0
    }
5092
0
    if ( nPos == 0xFFFF )
5093
0
    {
5094
0
        nPos = nCnt - 1;
5095
0
    }
5096
0
    else if ( nPos > nCnt - 1 )
5097
0
    {
5098
0
        return 0;
5099
0
    }
5100
0
    return NumFor[nNumFor].Info().nTypeArray[nPos];
5101
0
}
5102
5103
bool SvNumberformat::IsNegativeWithoutSign() const
5104
6.18k
{
5105
6.18k
    if ( IsSecondSubformatRealNegative() )
5106
6.18k
    {
5107
6.18k
        const OUString* pStr = GetNumForString( 1, 0, true );
5108
6.18k
        if ( pStr )
5109
6.18k
        {
5110
6.18k
            return !HasStringNegativeSign( *pStr );
5111
6.18k
        }
5112
6.18k
    }
5113
0
    return false;
5114
6.18k
}
5115
5116
bool SvNumberformat::IsNegativeInBracket() const
5117
51.5k
{
5118
51.5k
    sal_uInt16 nCnt = NumFor[1].GetCount();
5119
51.5k
    if (!nCnt)
5120
51.5k
    {
5121
51.5k
        return false;
5122
51.5k
    }
5123
0
    auto& tmp = NumFor[1].Info().sStrArray;
5124
0
    return tmp[0] == "(" && tmp[nCnt-1] == ")";
5125
51.5k
}
5126
5127
bool SvNumberformat::HasPositiveBracketPlaceholder() const
5128
486
{
5129
486
    sal_uInt16 nCnt = NumFor[0].GetCount();
5130
486
    return NumFor[0].Info().sStrArray[nCnt-1] == "_)";
5131
486
}
5132
5133
DateOrder SvNumberformat::GetDateOrder() const
5134
44.4k
{
5135
44.4k
    if ( eType & SvNumFormatType::DATE )
5136
44.4k
    {
5137
44.4k
        auto& rTypeArray = NumFor[0].Info().nTypeArray;
5138
44.4k
        sal_uInt16 nCnt = NumFor[0].GetCount();
5139
62.5k
        for ( sal_uInt16 j=0; j<nCnt; j++ )
5140
61.5k
        {
5141
61.5k
            switch ( rTypeArray[j] )
5142
61.5k
            {
5143
5.27k
            case NF_KEY_D :
5144
22.9k
            case NF_KEY_DD :
5145
22.9k
                return DateOrder::DMY;
5146
2.17k
            case NF_KEY_M :
5147
6.71k
            case NF_KEY_MM :
5148
7.56k
            case NF_KEY_MMM :
5149
8.22k
            case NF_KEY_MMMM :
5150
8.66k
            case NF_KEY_MMMMM :
5151
8.66k
                return DateOrder::MDY;
5152
1.94k
            case NF_KEY_YY :
5153
2.87k
            case NF_KEY_YYYY :
5154
3.26k
            case NF_KEY_EC :
5155
3.81k
            case NF_KEY_EEC :
5156
11.1k
            case NF_KEY_R :
5157
11.8k
            case NF_KEY_RR :
5158
11.8k
                return DateOrder::YMD;
5159
61.5k
            }
5160
61.5k
        }
5161
44.4k
    }
5162
0
    else
5163
0
    {
5164
0
       SAL_WARN( "svl.numbers", "SvNumberformat::GetDateOrder: no date" );
5165
0
    }
5166
1.00k
    return rLoc().getDateOrder();
5167
44.4k
}
5168
5169
sal_uInt32 SvNumberformat::GetExactDateOrder() const
5170
52.7k
{
5171
52.7k
    sal_uInt32 nRet = 0;
5172
52.7k
    if ( !(eType & SvNumFormatType::DATE) )
5173
0
    {
5174
0
        SAL_WARN( "svl.numbers", "SvNumberformat::GetExactDateOrder: no date" );
5175
0
        return nRet;
5176
0
    }
5177
52.7k
    auto& rTypeArray = NumFor[0].Info().nTypeArray;
5178
52.7k
    sal_uInt16 nCnt = NumFor[0].GetCount();
5179
52.7k
    int nShift = 0;
5180
257k
    for ( sal_uInt16 j=0; j<nCnt && nShift < 3; j++ )
5181
205k
    {
5182
205k
        switch ( rTypeArray[j] )
5183
205k
        {
5184
16.1k
        case NF_KEY_D :
5185
39.1k
        case NF_KEY_DD :
5186
39.1k
            nRet = (nRet << 8) | 'D';
5187
39.1k
            ++nShift;
5188
39.1k
            break;
5189
9.63k
        case NF_KEY_M :
5190
16.1k
        case NF_KEY_MM :
5191
16.6k
        case NF_KEY_MMM :
5192
31.6k
        case NF_KEY_MMMM :
5193
32.3k
        case NF_KEY_MMMMM :
5194
32.3k
            nRet = (nRet << 8) | 'M';
5195
32.3k
            ++nShift;
5196
32.3k
            break;
5197
6.60k
        case NF_KEY_YY :
5198
11.9k
        case NF_KEY_YYYY :
5199
16.7k
        case NF_KEY_EC :
5200
18.6k
        case NF_KEY_EEC :
5201
34.1k
        case NF_KEY_R :
5202
35.8k
        case NF_KEY_RR :
5203
35.8k
            nRet = (nRet << 8) | 'Y';
5204
35.8k
            ++nShift;
5205
35.8k
            break;
5206
205k
        }
5207
205k
    }
5208
52.7k
    return nRet;
5209
52.7k
}
5210
5211
void SvNumberformat::GetConditions( SvNumberformatLimitOps& rOper1, double& rVal1,
5212
                                    SvNumberformatLimitOps& rOper2, double& rVal2 ) const
5213
0
{
5214
0
    rOper1 = eOp1;
5215
0
    rOper2 = eOp2;
5216
0
    rVal1  = fLimit1;
5217
0
    rVal2  = fLimit2;
5218
0
}
5219
5220
const Color* SvNumberformat::GetColor( sal_uInt16 nNumFor ) const
5221
0
{
5222
0
    if ( nNumFor > 3 )
5223
0
    {
5224
0
        return nullptr;
5225
0
    }
5226
0
    return NumFor[nNumFor].GetColor();
5227
0
}
5228
5229
static void lcl_SvNumberformat_AddLimitStringImpl( OUString& rStr,
5230
                                                   SvNumberformatLimitOps eOp,
5231
                                                   double fLimit, std::u16string_view rDecSep )
5232
0
{
5233
0
    if ( eOp == NUMBERFORMAT_OP_NO )
5234
0
        return;
5235
5236
0
    switch ( eOp )
5237
0
    {
5238
0
    case NUMBERFORMAT_OP_EQ :
5239
0
        rStr = "[=";
5240
0
        break;
5241
0
    case NUMBERFORMAT_OP_NE :
5242
0
        rStr = "[<>";
5243
0
        break;
5244
0
    case NUMBERFORMAT_OP_LT :
5245
0
        rStr = "[<";
5246
0
        break;
5247
0
    case NUMBERFORMAT_OP_LE :
5248
0
        rStr = "[<=";
5249
0
        break;
5250
0
    case NUMBERFORMAT_OP_GT :
5251
0
        rStr = "[>";
5252
0
        break;
5253
0
    case NUMBERFORMAT_OP_GE :
5254
0
        rStr = "[>=";
5255
0
        break;
5256
0
    default:
5257
0
        SAL_WARN( "svl.numbers", "unsupported number format" );
5258
0
        break;
5259
0
    }
5260
0
    rStr +=  ::rtl::math::doubleToUString( fLimit,
5261
0
                                           rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
5262
0
                                           rDecSep[0], true);
5263
0
    rStr += "]";
5264
0
}
5265
5266
static void lcl_insertLCID( OUStringBuffer& rFormatStr, sal_uInt32 nLCID, sal_Int32 nPosInsertLCID, bool bDBNumInserted )
5267
0
{
5268
0
    if ( nLCID == 0 )
5269
0
        return;
5270
0
    if (nPosInsertLCID == rFormatStr.getLength() && !bDBNumInserted)
5271
        // No format code, no locale.
5272
0
        return;
5273
5274
0
    auto aLCIDString = OUString::number( nLCID , 16 ).toAsciiUpperCase();
5275
    // Search for only last DBNum which is the last element before insertion position
5276
0
    if ( bDBNumInserted && nPosInsertLCID >= 8
5277
0
        && aLCIDString.length > 4
5278
0
        && OUString::unacquired(rFormatStr).match( "[DBNum", nPosInsertLCID-8) )
5279
0
    {   // remove DBNumX code if long LCID
5280
0
        nPosInsertLCID -= 8;
5281
0
        rFormatStr.remove( nPosInsertLCID, 8 );
5282
0
    }
5283
0
    rFormatStr.insert( nPosInsertLCID, "[$-" + aLCIDString + "]" );
5284
0
}
5285
5286
/** Increment nAlphabetID for CJK numerals
5287
 * +1 for financial numerals [NatNum2]
5288
 * +2 for Arabic fullwidth numerals [NatNum3]
5289
 * */
5290
static void lcl_incrementAlphabetWithNatNum ( sal_uInt32& nAlphabetID, sal_uInt32 nNatNum )
5291
0
{
5292
0
    if ( nNatNum == 2) // financial
5293
0
        nAlphabetID += 1;
5294
0
    else if ( nNatNum == 3)
5295
0
        nAlphabetID += 2;
5296
0
    nAlphabetID = nAlphabetID << 24;
5297
0
}
5298
5299
OUString SvNumberformat::GetMappedFormatstring( const NfKeywordTable& rKeywords,
5300
                                                const LocaleDataWrapper& rLocWrp,
5301
                                                LanguageType nOriginalLang /* =LANGUAGE_DONTKNOW */,
5302
                                                bool bSystemLanguage /* =false */ ) const
5303
0
{
5304
0
    OUStringBuffer aStr;
5305
0
    if (maLocale.meSubstitute != LocaleType::Substitute::NONE)
5306
0
    {
5307
        // XXX: theoretically this could clash with the first subformat's
5308
        // lcl_insertLCID() below, in practice as long as it is used for system
5309
        // time and date modifiers it shouldn't (i.e. there is no calendar or
5310
        // numeral specified as well).
5311
0
        aStr.append("[$-" + maLocale.generateCode() + "]");
5312
0
    }
5313
0
    bool bDefault[4];
5314
    // 1 subformat matches all if no condition specified,
5315
0
    bDefault[0] = ( NumFor[1].GetCount() == 0 && eOp1 == NUMBERFORMAT_OP_NO );
5316
    // with 2 subformats [>=0];[<0] is implied if no condition specified
5317
0
    bDefault[1] = ( !bDefault[0] && NumFor[2].GetCount() == 0 &&
5318
0
                    eOp1 == NUMBERFORMAT_OP_GE && fLimit1 == 0.0 &&
5319
0
                    eOp2 == NUMBERFORMAT_OP_NO && fLimit2 == 0.0 );
5320
    // with 3 or more subformats [>0];[<0];[=0] is implied if no condition specified,
5321
    // note that subformats may be empty (;;;) and NumFor[2].GetCount()>0 is not checked.
5322
0
    bDefault[2] = ( !bDefault[0] && !bDefault[1] &&
5323
0
                    eOp1 == NUMBERFORMAT_OP_GT && fLimit1 == 0.0 &&
5324
0
                    eOp2 == NUMBERFORMAT_OP_LT && fLimit2 == 0.0 );
5325
0
    bool bDefaults = bDefault[0] || bDefault[1] || bDefault[2];
5326
    // from now on bDefault[] values are used to append empty subformats at the end
5327
0
    bDefault[3] = false;
5328
0
    if ( !bDefaults )
5329
0
    {
5330
        // conditions specified
5331
0
        if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 == NUMBERFORMAT_OP_NO )
5332
0
        {
5333
0
            bDefault[0] = bDefault[1] = true;                               // [];x
5334
0
        }
5335
0
        else if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 != NUMBERFORMAT_OP_NO &&
5336
0
                  NumFor[2].GetCount() == 0 )
5337
0
        {
5338
0
            bDefault[0] = bDefault[1] = bDefault[2] = bDefault[3] = true;   // [];[];;
5339
0
        }
5340
        // nothing to do if conditions specified for every subformat
5341
0
    }
5342
0
    else if ( bDefault[0] )
5343
0
    {
5344
0
        bDefault[0] = false; // a single unconditional subformat is never delimited
5345
0
    }
5346
0
    else
5347
0
    {
5348
0
        if ( bDefault[2] && NumFor[2].GetCount() == 0 && NumFor[1].GetCount() > 0 )
5349
0
        {
5350
0
            bDefault[3] = true; // special cases x;x;; and ;x;;
5351
0
        }
5352
0
        for ( int i=0; i<3 && !bDefault[i]; ++i )
5353
0
        {
5354
0
            bDefault[i] = true;
5355
0
        }
5356
0
    }
5357
0
    int nSem = 0; // needed ';' delimiters
5358
0
    int nSub = 0; // subformats delimited so far
5359
0
    for ( int n=0; n<4; n++ )
5360
0
    {
5361
0
        if ( n > 0 && NumFor[n].Info().eScannedType != SvNumFormatType::UNDEFINED )
5362
0
        {
5363
0
            nSem++;
5364
0
        }
5365
0
        OUString aPrefix;
5366
5367
0
        if ( !bDefaults )
5368
0
        {
5369
0
            switch ( n )
5370
0
            {
5371
0
            case 0 :
5372
0
                lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp1,
5373
0
                                                       fLimit1, rLocWrp.getNumDecimalSep() );
5374
0
                break;
5375
0
            case 1 :
5376
0
                lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp2,
5377
0
                                                       fLimit2, rLocWrp.getNumDecimalSep() );
5378
0
                break;
5379
0
            }
5380
0
        }
5381
5382
0
        const OUString& rColorName = NumFor[n].GetColorName();
5383
0
        if ( !rColorName.isEmpty() )
5384
0
        {
5385
0
            const NfKeywordTable & rKey = rScan.GetKeywords();
5386
0
            for ( int j = NF_KEY_FIRSTCOLOR; j <= NF_KEY_LASTCOLOR; j++ )
5387
0
            {
5388
0
                if ( rKey[j] == rColorName )
5389
0
                {
5390
0
                    aPrefix += "[" + rKeywords[j] + "]";
5391
0
                    break;  // for
5392
0
                }
5393
0
            }
5394
0
        }
5395
5396
0
        SvNumberNatNum aNatNum = NumFor[n].GetNatNum();
5397
0
        bool bDBNumInserted = false;
5398
0
        if (aNatNum.IsComplete() && (aNatNum.GetDBNum() > 0 || nOriginalLang != LANGUAGE_DONTKNOW))
5399
0
        {   // GetFormatStringForExcel() may have changed language to en_US
5400
0
            if (aNatNum.GetLang() == LANGUAGE_ENGLISH_US && nOriginalLang != LANGUAGE_DONTKNOW)
5401
0
                aNatNum.SetLang( nOriginalLang );
5402
0
            if ( aNatNum.GetDBNum() > 0 )
5403
0
            {
5404
0
                aPrefix += "[DBNum" + OUString::number( aNatNum.GetDBNum() ) + "]";
5405
0
                bDBNumInserted = true;
5406
0
            }
5407
0
        }
5408
5409
0
        sal_uInt16 nCnt = NumFor[n].GetCount();
5410
0
        if ( nSem && (nCnt || !aPrefix.isEmpty()) )
5411
0
        {
5412
0
            for ( ; nSem; --nSem )
5413
0
            {
5414
0
                aStr.append( ';' );
5415
0
            }
5416
0
            for ( ; nSub <= n; ++nSub )
5417
0
            {
5418
0
                bDefault[nSub] = false;
5419
0
            }
5420
0
        }
5421
5422
0
        if ( !aPrefix.isEmpty() )
5423
0
        {
5424
0
            aStr.append( aPrefix );
5425
0
        }
5426
0
        sal_Int32 nPosHaveLCID = -1;
5427
0
        sal_Int32 nPosInsertLCID = aStr.getLength();
5428
0
        sal_uInt32 nCalendarID = 0x0000000; // Excel ID of calendar used in sub-format see tdf#36038
5429
0
        constexpr sal_uInt32 kCalGengou = 0x0030000;
5430
0
        if ( nCnt )
5431
0
        {
5432
0
            auto& rTypeArray = NumFor[n].Info().nTypeArray;
5433
0
            auto& rStrArray = NumFor[n].Info().sStrArray;
5434
0
            for ( sal_uInt16 j=0; j<nCnt; j++ )
5435
0
            {
5436
0
                if ( 0 <= rTypeArray[j] && rTypeArray[j] < NF_KEYWORD_ENTRIES_COUNT )
5437
0
                {
5438
0
                    aStr.append( rKeywords[rTypeArray[j]] );
5439
0
                    if( NF_KEY_NNNN == rTypeArray[j] )
5440
0
                    {
5441
0
                        aStr.append( rLocWrp.getLongDateDayOfWeekSep() );
5442
0
                    }
5443
0
                    switch (rTypeArray[j])
5444
0
                    {
5445
0
                        case NF_KEY_EC:
5446
0
                        case NF_KEY_EEC:
5447
0
                        case NF_KEY_R:
5448
0
                        case NF_KEY_RR:
5449
                            // Implicit secondary (non-gregorian) calendar.
5450
                            // Currently only for ja-JP.
5451
                            /* TODO: same for all locales in
5452
                             * LocaleDataWrapper::doesSecondaryCalendarUseEC() ?
5453
                             * Should split the locales off that then. */
5454
0
                            if (!nCalendarID)
5455
0
                            {
5456
0
                                const LanguageType nLang = MsLangId::getRealLanguage( nOriginalLang);
5457
0
                                if (nLang == LANGUAGE_JAPANESE)
5458
0
                                    nCalendarID = kCalGengou;
5459
0
                            }
5460
0
                        break;
5461
0
                        default:
5462
0
                            ;   // nothing
5463
0
                    }
5464
0
                }
5465
0
                else
5466
0
                {
5467
0
                    switch ( rTypeArray[j] )
5468
0
                    {
5469
0
                    case NF_SYMBOLTYPE_DECSEP :
5470
0
                        aStr.append( rLocWrp.getNumDecimalSep() );
5471
0
                        break;
5472
0
                    case NF_SYMBOLTYPE_THSEP :
5473
0
                        aStr.append( rLocWrp.getNumThousandSep() );
5474
0
                        break;
5475
0
                    case NF_SYMBOLTYPE_EXP :
5476
0
                        aStr.append( rKeywords[NF_KEY_E] );
5477
0
                        if ( rStrArray[j].getLength() > 1 && rStrArray[j][1] == '+' )
5478
0
                            aStr.append( "+" );
5479
0
                        else
5480
                        // tdf#102370: Excel code for exponent without sign
5481
0
                            aStr.append( "-" );
5482
0
                        break;
5483
0
                    case NF_SYMBOLTYPE_DATESEP :
5484
0
                        aStr.append( rLocWrp.getDateSep() );
5485
0
                        break;
5486
0
                    case NF_SYMBOLTYPE_TIMESEP :
5487
0
                        aStr.append( rLocWrp.getTimeSep() );
5488
0
                        break;
5489
0
                    case NF_SYMBOLTYPE_TIME100SECSEP :
5490
0
                        aStr.append( rLocWrp.getTime100SecSep() );
5491
0
                        break;
5492
0
                    case NF_SYMBOLTYPE_FRACBLANK :
5493
0
                    case NF_SYMBOLTYPE_STRING :
5494
0
                        if ( rStrArray[j].getLength() == 1 )
5495
0
                        {
5496
0
                            if ( rTypeArray[j] == NF_SYMBOLTYPE_STRING )
5497
0
                                aStr.append( '\\' );
5498
0
                            aStr.append( rStrArray[j] );
5499
0
                        }
5500
0
                        else
5501
0
                        {
5502
0
                            aStr.append( "\"" + rStrArray[j] + "\"" );
5503
0
                        }
5504
0
                        break;
5505
0
                    case NF_SYMBOLTYPE_CALDEL :
5506
0
                        if (j + 1 >= nCnt)
5507
0
                            break;
5508
0
                        if ( rStrArray[j+1] == "gengou" )
5509
0
                        {
5510
0
                            nCalendarID = kCalGengou;
5511
0
                        }
5512
0
                        else if ( rStrArray[j+1] == "hijri" )
5513
0
                        {
5514
0
                            nCalendarID = 0x0060000;
5515
0
                        }
5516
0
                        else if ( rStrArray[j+1] == "buddhist" )
5517
0
                        {
5518
0
                            nCalendarID = 0x0070000;
5519
0
                        }
5520
0
                        else if ( rStrArray[j+1] == "jewish" )
5521
0
                        {
5522
0
                            nCalendarID = 0x0080000;
5523
0
                        }
5524
                        // Other calendars (see tdf#36038) not corresponding between LibO and XL.
5525
                        // However, skip any calendar modifier and don't write
5526
                        // as format code (if not as literal string).
5527
0
                        j += 2;
5528
0
                        break;
5529
0
                    case NF_SYMBOLTYPE_CURREXT :
5530
0
                        nPosHaveLCID = aStr.getLength();
5531
0
                        aStr.append( rStrArray[j] );
5532
0
                        break;
5533
0
                    default:
5534
0
                        aStr.append( rStrArray[j] );
5535
0
                    }
5536
0
                }
5537
0
            }
5538
0
        }
5539
0
        sal_uInt32 nAlphabetID = 0x0000000; // Excel ID of alphabet used for numerals see tdf#36038
5540
0
        LanguageType nLanguageID = LANGUAGE_SYSTEM;
5541
0
        if ( aNatNum.IsComplete() )
5542
0
        {
5543
0
            nLanguageID = MsLangId::getRealLanguage( aNatNum.GetLang());
5544
0
            if ( aNatNum.GetNatNum() == 0 )
5545
0
            {
5546
0
                nAlphabetID = 0x01000000;  // Arabic-european numerals
5547
0
            }
5548
0
            else if ( nCalendarID > 0 || aNatNum.GetDBNum() == 0 || aNatNum.GetDBNum() == aNatNum.GetNatNum() )
5549
0
            {   // if no DBNum code then use long LCID
5550
                // if DBNum value != NatNum value, use DBNum and not extended LCID
5551
                // if calendar, then DBNum will be removed
5552
0
                LanguageType pri = primary(nLanguageID);
5553
0
                if ( pri == LANGUAGE_ARABIC_PRIMARY_ONLY )
5554
0
                        nAlphabetID = 0x02000000;  // Arabic-indic numerals
5555
0
                else if ( pri == primary(LANGUAGE_FARSI) )
5556
0
                        nAlphabetID = 0x03000000;  // Farsi numerals
5557
0
                else if ( pri.anyOf(
5558
0
                    primary(LANGUAGE_HINDI),
5559
0
                    primary(LANGUAGE_MARATHI),
5560
0
                    primary(LANGUAGE_NEPALI) ))
5561
0
                        nAlphabetID = 0x04000000;  // Devanagari numerals
5562
0
                else if ( pri == primary(LANGUAGE_BENGALI) )
5563
0
                        nAlphabetID = 0x05000000;  // Bengali numerals
5564
0
                else if ( pri == primary(LANGUAGE_PUNJABI) )
5565
0
                {
5566
0
                    if ( nLanguageID == LANGUAGE_PUNJABI_ARABIC_LSO )
5567
0
                        nAlphabetID =  0x02000000;  // Arabic-indic numerals
5568
0
                    else
5569
0
                        nAlphabetID = 0x06000000;  // Punjabi numerals
5570
0
                }
5571
0
                else if ( pri == primary(LANGUAGE_GUJARATI) )
5572
0
                        nAlphabetID = 0x07000000;  // Gujarati numerals
5573
0
                else if ( pri == primary(LANGUAGE_ODIA))
5574
0
                        nAlphabetID = 0x08000000;  // Odia (Oriya) numerals
5575
0
                else if ( pri == primary(LANGUAGE_TAMIL))
5576
0
                        nAlphabetID = 0x09000000;  // Tamil numerals
5577
0
                else if ( pri == primary(LANGUAGE_TELUGU))
5578
0
                        nAlphabetID = 0x0A000000;  // Telugu numerals
5579
0
                else if ( pri == primary(LANGUAGE_KANNADA))
5580
0
                        nAlphabetID = 0x0B000000;  // Kannada numerals
5581
0
                else if ( pri == primary(LANGUAGE_MALAYALAM))
5582
0
                        nAlphabetID = 0x0C000000;  // Malayalam numerals
5583
0
                else if ( pri == primary(LANGUAGE_THAI))
5584
0
                {
5585
                    // The Thai T NatNum modifier during Xcl export.
5586
0
                    if ( rKeywords[NF_KEY_THAI_T] == "T" )
5587
0
                        nAlphabetID = 0x0D000000;  // Thai numerals
5588
0
                }
5589
0
                else if ( pri == primary(LANGUAGE_LAO))
5590
0
                        nAlphabetID = 0x0E000000;  // Lao numerals
5591
0
                else if ( pri == primary(LANGUAGE_TIBETAN))
5592
0
                        nAlphabetID = 0x0F000000;  // Tibetan numerals
5593
0
                else if ( pri == primary(LANGUAGE_BURMESE))
5594
0
                        nAlphabetID = 0x10000000;  // Burmese numerals
5595
0
                else if ( pri == primary(LANGUAGE_TIGRIGNA_ETHIOPIA))
5596
0
                        nAlphabetID = 0x11000000;  // Tigrigna numerals
5597
0
                else if ( pri == primary(LANGUAGE_KHMER))
5598
0
                        nAlphabetID = 0x12000000;  // Khmer numerals
5599
0
                else if ( pri == primary(LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA))
5600
0
                {
5601
0
                    if ( nLanguageID != LANGUAGE_MONGOLIAN_CYRILLIC_MONGOLIA
5602
0
                      && nLanguageID != LANGUAGE_MONGOLIAN_CYRILLIC_LSO )
5603
0
                        nAlphabetID = 0x13000000;  // Mongolian numerals
5604
0
                }
5605
                    // CJK numerals
5606
0
                else if ( pri == primary(LANGUAGE_JAPANESE))
5607
0
                {
5608
0
                    nAlphabetID = 0x1B;
5609
0
                    lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5610
0
                }
5611
0
                else if ( pri == primary(LANGUAGE_CHINESE))
5612
0
                {
5613
0
                    if ( nLanguageID == LANGUAGE_CHINESE_TRADITIONAL
5614
0
                      || nLanguageID == LANGUAGE_CHINESE_HONGKONG
5615
0
                      || nLanguageID == LANGUAGE_CHINESE_MACAU )
5616
0
                    {
5617
0
                        nAlphabetID = 0x21;
5618
0
                        lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5619
0
                    }
5620
0
                    else // LANGUAGE_CHINESE_SIMPLIFIED
5621
0
                    {
5622
0
                        nAlphabetID = 0x1E;
5623
0
                        lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5624
0
                    }
5625
0
                }
5626
0
                else if ( pri == primary(LANGUAGE_KOREAN))
5627
0
                {
5628
0
                    if ( aNatNum.GetNatNum() == 9 ) // Hangul
5629
0
                    {
5630
0
                        nAlphabetID = 0x27000000;
5631
0
                    }
5632
0
                    else
5633
0
                    {
5634
0
                        nAlphabetID = 0x24;
5635
0
                        lcl_incrementAlphabetWithNatNum ( nAlphabetID, aNatNum.GetNatNum() );
5636
0
                    }
5637
0
                }
5638
0
            }
5639
            // Add LCID to DBNum
5640
0
            if ( aNatNum.GetDBNum() > 0 && nLanguageID == LANGUAGE_SYSTEM )
5641
0
                nLanguageID = MsLangId::getRealLanguage( aNatNum.GetLang());
5642
0
        }
5643
0
        else if (nPosHaveLCID < 0)
5644
0
        {
5645
            // Do not insert a duplicated LCID that was already given with a
5646
            // currency format as [$R-1C09]
5647
0
            if (!bSystemLanguage && nOriginalLang != LANGUAGE_DONTKNOW)
5648
0
            {
5649
                // Explicit locale, write only to the first subformat.
5650
0
                if (n == 0)
5651
0
                    nLanguageID = MsLangId::getRealLanguage( nOriginalLang);
5652
0
            }
5653
0
            else if (bSystemLanguage && maLocale.meLanguageWithoutLocaleData != LANGUAGE_DONTKNOW)
5654
0
            {
5655
                // Explicit locale but no locale data thus assigned to system
5656
                // locale, preserve for roundtrip, write only to the first
5657
                // subformat.
5658
0
                if (n == 0)
5659
0
                    nLanguageID = maLocale.meLanguageWithoutLocaleData;
5660
0
            }
5661
0
        }
5662
0
        if ( nCalendarID > 0 )
5663
0
        {   // Add alphabet and language to calendar
5664
0
            if ( nAlphabetID == 0 )
5665
0
                nAlphabetID = 0x01000000;
5666
0
            if ( nLanguageID == LANGUAGE_SYSTEM && nOriginalLang != LANGUAGE_DONTKNOW )
5667
0
                nLanguageID = nOriginalLang;
5668
0
        }
5669
0
        lcl_insertLCID( aStr, nAlphabetID + nCalendarID + static_cast<sal_uInt16>(nLanguageID), nPosInsertLCID,
5670
0
                bDBNumInserted);
5671
0
    }
5672
0
    for ( ; nSub<4 && bDefault[nSub]; ++nSub )
5673
0
    {   // append empty subformats
5674
0
        aStr.append( ';' );
5675
0
    }
5676
0
    return aStr.makeStringAndClear();
5677
0
}
5678
5679
// static
5680
OUString SvNumberformat::ImpGetNatNumString(const SvNumberNatNum& rNum,
5681
                                            sal_Int64 nVal, sal_uInt16 nMinDigits,
5682
                                            const NativeNumberWrapper& rNatNum)
5683
20.1k
{
5684
20.1k
    OUString aStr;
5685
20.1k
    if ( nMinDigits )
5686
20.0k
    {
5687
20.0k
        if ( nMinDigits == 2 )
5688
20.0k
        {
5689
            // speed up the most common case
5690
20.0k
            if ( 0 <= nVal && nVal < 10 )
5691
10.1k
            {
5692
10.1k
                sal_Unicode aBuf[2];
5693
10.1k
                aBuf[0] = '0';
5694
10.1k
                aBuf[1] = '0' + nVal;
5695
10.1k
                aStr = OUString(aBuf, SAL_N_ELEMENTS(aBuf));
5696
10.1k
            }
5697
9.92k
            else
5698
9.92k
            {
5699
9.92k
                aStr = OUString::number( nVal );
5700
9.92k
            }
5701
20.0k
        }
5702
0
        else
5703
0
        {
5704
0
            OUString aValStr( OUString::number( nVal ) );
5705
0
            if ( aValStr.getLength() >= nMinDigits )
5706
0
            {
5707
0
                aStr = aValStr;
5708
0
            }
5709
0
            else
5710
0
            {
5711
0
                OUStringBuffer aBuf;
5712
0
                for(sal_Int32 index = 0; index < nMinDigits - aValStr.getLength(); ++index)
5713
0
                {
5714
0
                    aBuf.append('0');
5715
0
                }
5716
0
                aBuf.append(aValStr);
5717
0
                aStr = aBuf.makeStringAndClear();
5718
0
            }
5719
0
        }
5720
20.0k
    }
5721
111
    else
5722
111
    {
5723
111
        aStr = OUString::number( nVal );
5724
111
    }
5725
20.1k
    return ::impTransliterate(aStr, rNum, rNatNum);
5726
20.1k
}
5727
5728
OUString SvNumberformat::impTransliterateImpl(const OUString& rStr,
5729
                                              const SvNumberNatNum& rNum,
5730
                                              const sal_uInt16 nDateKey,
5731
                                              const NativeNumberWrapper& rNatNum) const
5732
0
{
5733
    // no KEYWORD=argument list in NatNum12
5734
0
    if (rNum.GetParams().indexOf('=') == -1)
5735
0
        return ::impTransliterateImpl( rStr, rNum, rNatNum);
5736
5737
0
    const NfKeywordTable & rKeywords = rScan.GetKeywords();
5738
5739
    // Format: KEYWORD=numbertext_prefix, ..., for example:
5740
    // [NatNum12 YYYY=title ordinal,MMMM=article, D=ordinal-number]
5741
0
    sal_Int32 nField = -1;
5742
0
    do
5743
0
    {
5744
0
        nField = rNum.GetParams().indexOf(Concat2View(rKeywords[nDateKey] + "="), ++nField);
5745
0
    }
5746
0
    while (nField != -1 && nField != 0 &&
5747
0
            (rNum.GetParams()[nField - 1] != ',' &&
5748
0
              rNum.GetParams()[nField - 1] != ' '));
5749
5750
    // no format specified for actual keyword
5751
0
    if (nField == -1)
5752
0
        return rStr;
5753
5754
0
    sal_Int32 nKeywordLen = rKeywords[nDateKey].getLength() + 1;
5755
0
    sal_Int32 nFieldEnd = rNum.GetParams().indexOf(',', nField);
5756
5757
0
    if (nFieldEnd == -1)
5758
0
        nFieldEnd = rNum.GetParams().getLength();
5759
5760
0
    css::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
5761
5762
0
    return rNatNum.getNativeNumberStringParams(
5763
0
        rStr, aLocale, rNum.GetNatNum(),
5764
0
        rNum.GetParams().copy(nField + nKeywordLen, nFieldEnd - nField - nKeywordLen));
5765
0
}
5766
5767
void SvNumberformat::GetNatNumXml( css::i18n::NativeNumberXmlAttributes2& rAttr,
5768
                                   sal_uInt16 nNumFor, const NativeNumberWrapper& rNatNum ) const
5769
0
{
5770
0
    if ( nNumFor <= 3 )
5771
0
    {
5772
0
        const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum();
5773
0
        if ( rNum.IsSet() )
5774
0
        {
5775
0
            css::lang::Locale aLocale(
5776
0
                    LanguageTag( rNum.GetLang() ).getLocale() );
5777
5778
            /* TODO: a new XNativeNumberSupplier2::convertToXmlAttributes()
5779
             * should rather return NativeNumberXmlAttributes2 and places
5780
             * adapted, and whether to fill Spellout or something different
5781
             * should be internal there. */
5782
0
            css::i18n::NativeNumberXmlAttributes aTmp(
5783
0
                    rNatNum.convertToXmlAttributes(
5784
0
                        aLocale, rNum.GetNatNum()));
5785
0
            rAttr.Locale = aTmp.Locale;
5786
0
            rAttr.Format = aTmp.Format;
5787
0
            rAttr.Style = aTmp.Style;
5788
0
            if ( NatNumTakesParameters(rNum.GetNatNum()) )
5789
0
            {
5790
                // NatNum12 spell out numbers, dates and money amounts
5791
0
                rAttr.Spellout = rNum.GetParams();
5792
                // Mutually exclusive.
5793
0
                rAttr.Format.clear();
5794
0
                rAttr.Style.clear();
5795
0
            }
5796
0
            else
5797
0
            {
5798
0
                rAttr.Spellout.clear();
5799
0
            }
5800
0
        }
5801
0
        else
5802
0
        {
5803
0
            rAttr = css::i18n::NativeNumberXmlAttributes2();
5804
0
        }
5805
0
    }
5806
0
    else
5807
0
    {
5808
0
        rAttr = css::i18n::NativeNumberXmlAttributes2();
5809
0
    }
5810
0
}
5811
5812
OUString SvNumberformat::GetNatNumModifierString( sal_uInt16 nNumFor ) const
5813
62.7k
{
5814
62.7k
    if ( nNumFor > 3 )
5815
0
        return u""_ustr;
5816
62.7k
    const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum();
5817
62.7k
    if ( !rNum.IsSet() )
5818
62.7k
        return u""_ustr;
5819
0
    const sal_Int32 nNum = rNum.GetNatNum();
5820
0
    OUStringBuffer sNatNumModifier = "[NatNum" + OUString::number( nNum );
5821
0
    if ( NatNumTakesParameters( nNum ) )
5822
0
    {
5823
0
        sNatNumModifier.append( " " + rNum.GetParams() );
5824
0
    }
5825
0
    sNatNumModifier.append( "]" );
5826
5827
0
    return sNatNumModifier.makeStringAndClear();
5828
62.7k
}
5829
5830
// static
5831
bool SvNumberformat::HasStringNegativeSign( const OUString& rStr )
5832
10.1k
{
5833
    // For Sign '-' needs to be at the start or at the end of the string (blanks ignored)
5834
10.1k
    sal_Int32 nLen = rStr.getLength();
5835
10.1k
    if ( !nLen )
5836
0
    {
5837
0
        return false;
5838
0
    }
5839
10.1k
    const sal_Unicode* const pBeg = rStr.getStr();
5840
10.1k
    const sal_Unicode* const pEnd = pBeg + nLen;
5841
10.1k
    const sal_Unicode* p = pBeg;
5842
10.1k
    do
5843
10.4k
    {   // Start
5844
10.4k
        if ( *p == '-' )
5845
2.85k
        {
5846
2.85k
            return true;
5847
2.85k
        }
5848
10.4k
    }
5849
10.1k
    while ( *p == ' ' && ++p < pEnd );
5850
5851
7.30k
    p = pEnd - 1;
5852
5853
7.30k
    do
5854
7.94k
    {   // End
5855
7.94k
        if ( *p == '-' )
5856
348
        {
5857
348
            return true;
5858
348
        }
5859
7.94k
    }
5860
7.59k
    while ( *p == ' ' && pBeg < --p );
5861
6.95k
    return false;
5862
7.30k
}
5863
5864
// static
5865
bool SvNumberformat::IsInQuote( const OUString& rStr, sal_Int32 nPos,
5866
                                sal_Unicode cQuote, sal_Unicode cEscIn, sal_Unicode cEscOut )
5867
14.7M
{
5868
14.7M
    sal_Int32 nLen = rStr.getLength();
5869
14.7M
    if ( nPos >= nLen )
5870
0
    {
5871
0
        return false;
5872
0
    }
5873
14.7M
    const sal_Unicode* p0 = rStr.getStr();
5874
14.7M
    const sal_Unicode* p = p0;
5875
14.7M
    const sal_Unicode* p1 = p0 + nPos;
5876
14.7M
    bool bQuoted = false;
5877
145M
    while ( p <= p1 )
5878
130M
    {
5879
130M
        if ( *p == cQuote )
5880
96.4k
        {
5881
96.4k
            if ( p == p0 )
5882
3.36k
            {
5883
3.36k
                bQuoted = true;
5884
3.36k
            }
5885
93.0k
            else if ( bQuoted )
5886
42.7k
            {
5887
42.7k
                if ( *(p-1) != cEscIn )
5888
42.7k
                {
5889
42.7k
                    bQuoted = false;
5890
42.7k
                }
5891
42.7k
            }
5892
50.2k
            else
5893
50.2k
            {
5894
50.2k
                if ( *(p-1) != cEscOut )
5895
48.4k
                {
5896
48.4k
                    bQuoted = true;
5897
48.4k
                }
5898
50.2k
            }
5899
96.4k
        }
5900
130M
        p++;
5901
130M
    }
5902
14.7M
    return bQuoted;
5903
14.7M
}
5904
5905
// static
5906
sal_Int32 SvNumberformat::GetQuoteEnd( const OUString& rStr, sal_Int32 nPos,
5907
                                       sal_Unicode cQuote, sal_Unicode cEscIn )
5908
14.7M
{
5909
14.7M
    if ( nPos < 0 )
5910
0
    {
5911
0
        return -1;
5912
0
    }
5913
14.7M
    sal_Int32 nLen = rStr.getLength();
5914
14.7M
    if ( nPos >= nLen )
5915
0
    {
5916
0
        return -1;
5917
0
    }
5918
14.7M
    if ( !IsInQuote( rStr, nPos, cQuote, cEscIn ) )
5919
14.7M
    {
5920
14.7M
        if ( rStr[ nPos ] == cQuote )
5921
0
        {
5922
0
            return nPos; // Closing cQuote
5923
0
        }
5924
14.7M
        return -1;
5925
14.7M
    }
5926
9.01k
    const sal_Unicode* p0 = rStr.getStr();
5927
9.01k
    const sal_Unicode* p = p0 + nPos;
5928
9.01k
    const sal_Unicode* p1 = p0 + nLen;
5929
30.9k
    while ( p < p1 )
5930
30.3k
    {
5931
30.3k
        if ( *p == cQuote && p > p0 && *(p-1) != cEscIn )
5932
8.40k
        {
5933
8.40k
            return sal::static_int_cast< sal_Int32 >(p - p0);
5934
8.40k
        }
5935
21.9k
        p++;
5936
21.9k
    }
5937
610
    return nLen; // End of String
5938
9.01k
}
5939
5940
sal_uInt16 SvNumberformat::GetNumForNumberElementCount( sal_uInt16 nNumFor ) const
5941
0
{
5942
0
    if ( nNumFor < 4 )
5943
0
    {
5944
0
        sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
5945
0
        return nCnt - ImpGetNumForStringElementCount( nNumFor );
5946
0
    }
5947
0
    return 0;
5948
0
}
5949
5950
sal_uInt16 SvNumberformat::ImpGetNumForStringElementCount( sal_uInt16 nNumFor ) const
5951
16.6k
{
5952
16.6k
    sal_uInt16 nCnt = 0;
5953
16.6k
    sal_uInt16 nNumForCnt = NumFor[nNumFor].GetCount();
5954
16.6k
    auto& rTypeArray = NumFor[nNumFor].Info().nTypeArray;
5955
112k
    for ( sal_uInt16 j=0; j<nNumForCnt; ++j )
5956
95.7k
    {
5957
95.7k
        switch ( rTypeArray[j] )
5958
95.7k
        {
5959
22.5k
        case NF_SYMBOLTYPE_STRING:
5960
23.1k
        case NF_SYMBOLTYPE_CURRENCY:
5961
23.9k
        case NF_SYMBOLTYPE_DATESEP:
5962
24.3k
        case NF_SYMBOLTYPE_TIMESEP:
5963
25.2k
        case NF_SYMBOLTYPE_TIME100SECSEP:
5964
25.6k
        case NF_SYMBOLTYPE_PERCENT:
5965
25.6k
            ++nCnt;
5966
25.6k
            break;
5967
95.7k
        }
5968
95.7k
    }
5969
16.6k
    return nCnt;
5970
16.6k
}
5971
5972
bool SvNumberformat::IsMinuteSecondFormat() const
5973
7.82k
{
5974
7.82k
    if (GetMaskedType() != SvNumFormatType::TIME)
5975
2.78k
        return false;
5976
5977
5.03k
    constexpr sal_uInt16 k00 = 0x00;    // Nada, Nilch
5978
5.03k
    constexpr sal_uInt16 kLB = 0x01;    // '[' Left Bracket
5979
5.03k
    constexpr sal_uInt16 kRB = 0x02;    // ']' Right Bracket
5980
5.03k
    constexpr sal_uInt16 kMM = 0x04;    // M or MM
5981
5.03k
    constexpr sal_uInt16 kTS = 0x08;    // Time Separator
5982
5.03k
    constexpr sal_uInt16 kSS = 0x10;    // S or SS
5983
5.03k
#define HAS_MINUTE_SECOND(state) ((state) == (kMM|kTS|kSS) || (state) == (kLB|kMM|kRB|kTS|kSS))
5984
    // Also (kMM|kTS|kLB|kSS|kRB) but those are the same bits.
5985
5986
5.03k
    sal_uInt16 nState = k00;
5987
5.03k
    bool bSep = false;
5988
5.03k
    sal_uInt16 nNumForCnt = NumFor[0].GetCount();
5989
5.03k
    auto const & rTypeArray = NumFor[0].Info().nTypeArray;
5990
22.1k
    for (sal_uInt16 j=0; j < nNumForCnt; ++j)
5991
20.0k
    {
5992
20.0k
        switch (rTypeArray[j])
5993
20.0k
        {
5994
2.66k
            case NF_SYMBOLTYPE_DEL:
5995
2.66k
                {
5996
                    // '[' or ']' before/after MM or SS
5997
2.66k
                    const OUString& rStr = NumFor[0].Info().sStrArray[j];
5998
2.66k
                    if (rStr == "[")
5999
1.57k
                    {
6000
1.57k
                        if (nState != k00 && nState != (kMM|kTS))
6001
204
                            return false;
6002
1.36k
                        nState |= kLB;
6003
1.36k
                    }
6004
1.09k
                    else if (rStr == "]")
6005
862
                    {
6006
862
                        if (nState != (kLB|kMM) && nState != (kMM|kTS|kLB|kSS))
6007
307
                            return false;
6008
555
                        nState |= kRB;
6009
555
                    }
6010
232
                    else
6011
232
                        return false;
6012
2.66k
                }
6013
1.92k
            break;
6014
1.96k
            case NF_KEY_MI:
6015
3.92k
            case NF_KEY_MMI:
6016
3.92k
                if (nState != k00 && nState != kLB)
6017
327
                    return false;
6018
3.59k
                nState |= kMM;
6019
3.59k
            break;
6020
2.70k
            case NF_SYMBOLTYPE_TIMESEP:
6021
2.70k
                if (nState != kMM && nState != (kLB|kMM|kRB))
6022
247
                    return false;
6023
2.45k
                nState |= kTS;
6024
2.45k
            break;
6025
1.86k
            case NF_KEY_S:
6026
2.67k
            case NF_KEY_SS:
6027
2.67k
                if (nState != (kMM|kTS) && nState != (kLB|kMM|kRB|kTS) && nState != (kMM|kTS|kLB))
6028
433
                    return false;
6029
2.23k
                nState |= kSS;
6030
2.23k
            break;
6031
1.03k
            case NF_SYMBOLTYPE_TIME100SECSEP:
6032
                // Trailing fraction of seconds allowed.
6033
1.03k
                if (!HAS_MINUTE_SECOND(nState))
6034
229
                    return false;
6035
805
                bSep = true;
6036
805
            break;
6037
838
            case NF_SYMBOLTYPE_DIGIT:
6038
838
                if (!bSep)
6039
210
                    return false;
6040
628
            break;
6041
5.45k
            case NF_SYMBOLTYPE_STRING:
6042
                // nothing, display literal
6043
5.45k
            break;
6044
757
            default:
6045
757
                return false;
6046
20.0k
        }
6047
20.0k
    }
6048
2.08k
    return HAS_MINUTE_SECOND(nState);
6049
5.03k
#undef HAS_MINUTE_SECOND
6050
5.03k
}
6051
6052
OUString SvNumberformat::GetFormatStringForTimePrecision( int nPrecision ) const
6053
0
{
6054
0
    OUStringBuffer sString;
6055
6056
0
    sal_uInt16 nNumForCnt = NumFor[0].GetCount();
6057
0
    auto const & rTypeArray = NumFor[0].Info().nTypeArray;
6058
0
    for (sal_uInt16 j=0; j < nNumForCnt; ++j)
6059
0
    {
6060
0
        switch (rTypeArray[j])
6061
0
        {
6062
0
            case NF_KEY_S :
6063
0
            case NF_KEY_SS:
6064
0
                sString.append( NumFor[0].Info().sStrArray[j] );
6065
0
                if ( j > 0 && rTypeArray[j-1] == NF_SYMBOLTYPE_DEL && j < nNumForCnt-1 )
6066
0
                {
6067
0
                    j++;
6068
0
                    sString.append( NumFor[0].Info().sStrArray[j] );
6069
0
                }
6070
0
                if (nPrecision > 0)
6071
0
                {
6072
0
                    sString.append(rLoc().getTime100SecSep() + RepeatedUChar('0', nPrecision));
6073
0
                }
6074
0
                break;
6075
0
            case NF_SYMBOLTYPE_TIME100SECSEP:
6076
0
            case NF_SYMBOLTYPE_DIGIT:
6077
0
                break;
6078
0
            case NF_SYMBOLTYPE_STRING:
6079
0
                sString.append( "\"" );
6080
0
                [[fallthrough]];
6081
0
            default:
6082
0
                sString.append( NumFor[0].Info().sStrArray[j] );
6083
0
                if (rTypeArray[j] == NF_SYMBOLTYPE_STRING)
6084
0
                {
6085
0
                    sString.append( "\"" );
6086
0
                }
6087
0
        }
6088
0
    }
6089
6090
0
    return sString.makeStringAndClear();
6091
0
}
6092
6093
sal_uInt16 SvNumberformat::GetThousandDivisorPrecision( sal_uInt16 nIx ) const
6094
0
{
6095
0
    if (nIx >= 4)
6096
0
        return 0;
6097
6098
0
    const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
6099
6100
0
    if (rInfo.eScannedType != SvNumFormatType::NUMBER && rInfo.eScannedType != SvNumFormatType::CURRENCY)
6101
0
        return 0;
6102
6103
0
    if (rInfo.nThousand == FLAG_STANDARD_IN_FORMAT)
6104
0
        return SvNumberFormatter::UNLIMITED_PRECISION;
6105
6106
0
    return rInfo.nThousand * 3;
6107
0
}
6108
6109
const CharClass& SvNumberformat::rChrCls() const
6110
2.82M
{
6111
2.82M
    return rScan.GetChrCls();
6112
2.82M
}
6113
6114
const LocaleDataWrapper& SvNumberformat::rLoc() const
6115
1.23M
{
6116
1.23M
    return rScan.GetLoc();
6117
1.23M
}
6118
6119
const SvNFLanguageData& SvNumberformat::GetCurrentLanguageData() const
6120
26.0M
{
6121
26.0M
    return rScan.GetCurrentLanguageData();
6122
26.0M
}
6123
6124
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */