Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svl/source/numbers/zforfind.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 <cstdlib>
21
#include <fast_float/fast_float.h>
22
#include <float.h>
23
#include <comphelper/string.hxx>
24
#include <o3tl/string_view.hxx>
25
#include <sal/log.hxx>
26
#include <tools/date.hxx>
27
#include <rtl/math.hxx>
28
#include <rtl/character.hxx>
29
#include <unotools/charclass.hxx>
30
#include <unotools/calendarwrapper.hxx>
31
#include <unotools/localedatawrapper.hxx>
32
#include <com/sun/star/i18n/CalendarFieldIndex.hpp>
33
#include <com/sun/star/i18n/LocaleCalendar2.hpp>
34
#include <unotools/digitgroupingiterator.hxx>
35
#include <comphelper/sequence.hxx>
36
37
#include <svl/zforlist.hxx>
38
#include "zforscan.hxx"
39
#include <svl/zformat.hxx>
40
41
#include <memory>
42
43
#include "zforfind.hxx"
44
45
#ifndef DBG_UTIL
46
#define NF_TEST_CALENDAR 0
47
#else
48
#define NF_TEST_CALENDAR 0
49
#endif
50
#if NF_TEST_CALENDAR
51
#include <comphelper/processfactory.hxx>
52
#include <com/sun/star/i18n/XCalendar4.hpp>
53
#endif
54
55
56
const sal_uInt8 ImpSvNumberInputScan::nMatchedEndString    = 0x01;
57
const sal_uInt8 ImpSvNumberInputScan::nMatchedMidString    = 0x02;
58
const sal_uInt8 ImpSvNumberInputScan::nMatchedStartString  = 0x04;
59
const sal_uInt8 ImpSvNumberInputScan::nMatchedVirgin       = 0x08;
60
const sal_uInt8 ImpSvNumberInputScan::nMatchedUsedAsReturn = 0x10;
61
62
/* It is not clear how we want timezones to be handled. Convert them to local
63
 * time isn't wanted, as it isn't done in any other place and timezone
64
 * information isn't stored anywhere. Ignoring them and pretending local time
65
 * may be wrong too and might not be what the user expects. Keep the input as
66
 * string so that no information is lost.
67
 * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it
68
 * would work, together with the nTimezonePos handling in GetTimeRef(). */
69
#define NF_RECOGNIZE_ISO8601_TIMEZONES 0
70
71
const sal_Unicode cNoBreakSpace = 0xA0;
72
const sal_Unicode cNarrowNoBreakSpace = 0x202F;
73
const bool kDefaultEra = true;     // Gregorian CE, positive year
74
75
ImpSvNumberInputScan::ImpSvNumberInputScan(SvNFLanguageData& rCurrentLanguage)
76
        :
77
189k
        mrCurrentLanguageData(rCurrentLanguage),
78
189k
        maNullDate( 30,12,1899 ),
79
189k
        bTextInitialized( false ),
80
189k
        bScanGenitiveMonths( false ),
81
189k
        bScanPartitiveMonths( false ),
82
189k
        eScannedType( SvNumFormatType::UNDEFINED ),
83
189k
        eSetType( SvNumFormatType::UNDEFINED )
84
189k
{
85
189k
    nYear2000 = SvNumberFormatter::GetYear2000Default();
86
189k
    Reset();
87
189k
    ChangeIntl();
88
189k
}
89
90
ImpSvNumberInputScan::~ImpSvNumberInputScan()
91
189k
{
92
189k
}
93
94
95
void ImpSvNumberInputScan::Reset()
96
5.04M
{
97
5.04M
    mpFormat     = nullptr;
98
5.04M
    nMonth       = 0;
99
5.04M
    nMonthPos    = 0;
100
5.04M
    nDayOfWeek   = 0;
101
5.04M
    nTimePos     = 0;
102
5.04M
    nSign        = 0;
103
5.04M
    nESign       = 0;
104
5.04M
    nDecPos      = 0;
105
5.04M
    bNegCheck    = false;
106
5.04M
    nStringsCnt  = 0;
107
5.04M
    nNumericsCnt = 0;
108
5.04M
    nThousand    = 0;
109
5.04M
    eScannedType = SvNumFormatType::UNDEFINED;
110
5.04M
    nAmPm        = 0;
111
5.04M
    nPosThousandString = 0;
112
5.04M
    nLogical     = 0;
113
5.04M
    mbEraCE        = kDefaultEra;
114
5.04M
    nStringScanNumFor = 0;
115
5.04M
    nStringScanSign = 0;
116
5.04M
    nMatchedAllStrings = nMatchedVirgin;
117
5.04M
    nMayBeIso8601 = 0;
118
5.04M
    bIso8601Tsep = false;
119
5.04M
    nMayBeMonthDate = 0;
120
5.04M
    nAcceptedDatePattern = -2;
121
5.04M
    nDatePatternStart = 0;
122
5.04M
    nDatePatternNumbers = 0;
123
124
106M
    for (sal_uInt32 i = 0; i < SV_MAX_COUNT_INPUT_STRINGS; i++)
125
100M
    {
126
100M
        IsNum[i] = false;
127
100M
        nNums[i] = 0;
128
100M
    }
129
5.04M
}
130
131
// native number transliteration if necessary
132
static void TransformInput(const NativeNumberWrapper& rNatNum, const SvNFLanguageData& rCurrentLanguage, OUString& rStr)
133
4.85M
{
134
4.85M
    sal_Int32 nPos, nLen;
135
42.5M
    for ( nPos = 0, nLen = rStr.getLength(); nPos < nLen; ++nPos )
136
37.6M
    {
137
37.6M
        if ( 256 <= rStr[ nPos ] &&
138
1.28M
             rCurrentLanguage.GetCharClass()->isDigit( rStr, nPos ) )
139
1.85k
        {
140
1.85k
            break;
141
1.85k
        }
142
37.6M
    }
143
4.85M
    if ( nPos < nLen )
144
1.85k
    {
145
1.85k
        rStr = rNatNum.getNativeNumberString(rStr, rCurrentLanguage.GetLanguageTag().getLocale(), 0);
146
1.85k
    }
147
4.85M
}
148
149
150
/**
151
 * Only simple unsigned floating point values without any error detection,
152
 * decimal separator has to be '.'
153
 */
154
static double StringToDouble( std::u16string_view aStr )
155
943k
{
156
943k
    const auto fmt = fast_float::chars_format::fixed | fast_float::chars_format::no_infnan;
157
943k
    double result = 0;
158
943k
    (void)fast_float::from_chars(aStr.data(), aStr.data() + aStr.size(), result, fmt);
159
943k
    return result;
160
943k
}
161
162
namespace {
163
164
/**
165
 * Splits up the input into numbers and strings for further processing
166
 * (by the Turing machine).
167
 *
168
 * Starting state = GetChar
169
 * ---------------+-------------------+-----------------------------+---------------
170
 *  Old State     | Character read    | Event                       | New state
171
 * ---------------+-------------------+-----------------------------+---------------
172
 *  GetChar       | Number            | Symbol = Character          | GetValue
173
 *                | Else              | Symbol = Character          | GetString
174
 * ---------------|-------------------+-----------------------------+---------------
175
 *  GetValue      | Number            | Symbol = Symbol + Character | GetValue
176
 *                | Else              | Dec(CharPos)                | Stop
177
 * ---------------+-------------------+-----------------------------+---------------
178
 *  GetString     | Number            | Dec(CharPos)                | Stop
179
 *                | Else              | Symbol = Symbol + Character | GetString
180
 * ---------------+-------------------+-----------------------------+---------------
181
 */
182
enum ScanState  // States of the Turing machine
183
{
184
    SsStop      = 0,
185
    SsStart     = 1,
186
    SsGetValue  = 2,
187
    SsGetString = 3
188
};
189
190
}
191
192
bool ImpSvNumberInputScan::NextNumberStringSymbol( const sal_Unicode*& pStr,
193
                                                   OUString& rSymbol )
194
13.3M
{
195
13.3M
    bool isNumber = false;
196
13.3M
    sal_Unicode cToken;
197
13.3M
    ScanState eState = SsStart;
198
13.3M
    const sal_Unicode* pHere = pStr;
199
13.3M
    sal_Int32 nChars = 0;
200
201
13.3M
    for (;;)
202
56.5M
    {
203
56.5M
        cToken = *pHere;
204
56.5M
        if (cToken == 0 || eState == SsStop)
205
13.3M
            break;
206
43.2M
        pHere++;
207
43.2M
        switch (eState)
208
43.2M
        {
209
11.4M
        case SsStart:
210
11.4M
            if ( rtl::isAsciiDigit( cToken ) )
211
4.97M
            {
212
4.97M
                eState = SsGetValue;
213
4.97M
                isNumber = true;
214
4.97M
            }
215
6.44M
            else
216
6.44M
            {
217
6.44M
                eState = SsGetString;
218
6.44M
            }
219
11.4M
            nChars++;
220
11.4M
            break;
221
12.0M
        case SsGetValue:
222
12.0M
            if ( rtl::isAsciiDigit( cToken ) )
223
8.70M
            {
224
8.70M
                nChars++;
225
8.70M
            }
226
3.38M
            else
227
3.38M
            {
228
3.38M
                eState = SsStop;
229
3.38M
                pHere--;
230
3.38M
            }
231
12.0M
            break;
232
19.7M
        case SsGetString:
233
19.7M
            if ( !rtl::isAsciiDigit( cToken ) )
234
16.4M
            {
235
16.4M
                nChars++;
236
16.4M
            }
237
3.23M
            else
238
3.23M
            {
239
3.23M
                eState = SsStop;
240
3.23M
                pHere--;
241
3.23M
            }
242
19.7M
            break;
243
0
        default:
244
0
            break;
245
43.2M
        } // switch
246
43.2M
    } // while
247
248
13.3M
    if ( nChars )
249
11.4M
    {
250
11.4M
        rSymbol = OUString( pStr, nChars );
251
11.4M
    }
252
1.89M
    else
253
1.89M
    {
254
1.89M
        rSymbol.clear();
255
1.89M
    }
256
257
13.3M
    pStr = pHere;
258
259
13.3M
    return isNumber;
260
13.3M
}
261
262
263
// FIXME: should be grouping; it is only used though in case nStringsCnt is
264
// near SV_MAX_COUNT_INPUT_STRINGS, in NumberStringDivision().
265
266
bool ImpSvNumberInputScan::SkipThousands( const sal_Unicode*& pStr,
267
                                          OUString& rSymbol ) const
268
86.8k
{
269
86.8k
    bool res = false;
270
86.8k
    OUStringBuffer sBuff(rSymbol);
271
86.8k
    sal_Unicode cToken;
272
86.8k
    const OUString& rThSep = mrCurrentLanguageData.GetNumThousandSep();
273
86.8k
    const sal_Unicode* pHere = pStr;
274
86.8k
    ScanState eState = SsStart;
275
86.8k
    sal_Int32 nCounter = 0; // counts 3 digits
276
277
86.8k
    for (;;)
278
232k
    {
279
232k
        cToken = *pHere;
280
232k
        if (cToken == 0 || eState == SsStop)
281
86.8k
            break;
282
145k
        pHere++;
283
145k
        switch (eState)
284
145k
        {
285
81.2k
        case SsStart:
286
81.2k
            if ( StringPtrContains( rThSep, pHere-1, 0 ) )
287
30.1k
            {
288
30.1k
                nCounter = 0;
289
30.1k
                eState = SsGetValue;
290
30.1k
                pHere += rThSep.getLength() - 1;
291
30.1k
            }
292
51.1k
            else
293
51.1k
            {
294
51.1k
                eState = SsStop;
295
51.1k
                pHere--;
296
51.1k
            }
297
81.2k
            break;
298
64.4k
        case SsGetValue:
299
64.4k
            if ( rtl::isAsciiDigit( cToken ) )
300
57.2k
            {
301
57.2k
                sBuff.append(cToken);
302
57.2k
                nCounter++;
303
57.2k
                if (nCounter == 3)
304
14.5k
                {
305
14.5k
                    eState = SsStart;
306
14.5k
                    res = true; // .000 combination found
307
14.5k
                }
308
57.2k
            }
309
7.27k
            else
310
7.27k
            {
311
7.27k
                eState = SsStop;
312
7.27k
                pHere--;
313
7.27k
            }
314
64.4k
            break;
315
0
        default:
316
0
            break;
317
145k
        } // switch
318
145k
    } // while
319
320
86.8k
    if (eState == SsGetValue) // break with less than 3 digits
321
8.30k
    {
322
8.30k
        if ( nCounter )
323
5.63k
        {
324
5.63k
            sBuff.remove( sBuff.getLength() - nCounter, nCounter );
325
5.63k
        }
326
8.30k
        pHere -= nCounter + rThSep.getLength(); // put back ThSep also
327
8.30k
    }
328
86.8k
    rSymbol = sBuff.makeStringAndClear();
329
86.8k
    pStr = pHere;
330
331
86.8k
    return res;
332
86.8k
}
333
334
335
void ImpSvNumberInputScan::NumberStringDivision( const OUString& rString )
336
4.85M
{
337
4.85M
    const sal_Unicode* pStr = rString.getStr();
338
4.85M
    const sal_Unicode* const pEnd = pStr + rString.getLength();
339
18.1M
    while ( pStr < pEnd && nStringsCnt < SV_MAX_COUNT_INPUT_STRINGS )
340
13.3M
    {
341
13.3M
        if ( NextNumberStringSymbol( pStr, sStrArray[nStringsCnt] ) )
342
4.97M
        {   // Number
343
4.97M
            IsNum[nStringsCnt] = true;
344
4.97M
            nNums[nNumericsCnt] = nStringsCnt;
345
4.97M
            nNumericsCnt++;
346
4.97M
            if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS - 7 &&
347
91.7k
                nPosThousandString == 0) // Only once
348
86.8k
            {
349
86.8k
                if ( SkipThousands( pStr, sStrArray[nStringsCnt] ) )
350
6.46k
                {
351
6.46k
                    nPosThousandString = nStringsCnt;
352
6.46k
                }
353
86.8k
            }
354
4.97M
        }
355
8.34M
        else
356
8.34M
        {
357
8.34M
            IsNum[nStringsCnt] = false;
358
8.34M
        }
359
13.3M
        nStringsCnt++;
360
13.3M
    }
361
4.85M
}
362
363
364
/**
365
 * Whether rString contains rWhat at nPos
366
 */
367
bool ImpSvNumberInputScan::StringContainsImpl( const OUString& rWhat,
368
                                               const OUString& rString, sal_Int32 nPos )
369
729k
{
370
729k
    if ( nPos + rWhat.getLength() <= rString.getLength() )
371
716k
    {
372
716k
        return StringPtrContainsImpl( rWhat, rString.getStr(), nPos );
373
716k
    }
374
12.8k
    return false;
375
729k
}
376
377
378
/**
379
 * Whether pString contains rWhat at nPos
380
 */
381
bool ImpSvNumberInputScan::StringPtrContainsImpl( const OUString& rWhat,
382
                                                  const sal_Unicode* pString, sal_Int32 nPos )
383
6.92M
{
384
6.92M
    if ( rWhat.isEmpty() )
385
0
    {
386
0
        return false;
387
0
    }
388
6.92M
    const sal_Unicode* pWhat = rWhat.getStr();
389
6.92M
    const sal_Unicode* const pEnd = pWhat + rWhat.getLength();
390
6.92M
    const sal_Unicode* pStr = pString + nPos;
391
8.16M
    while ( pWhat < pEnd )
392
7.34M
    {
393
7.34M
        if ( *pWhat != *pStr )
394
6.11M
        {
395
6.11M
            return false;
396
6.11M
        }
397
1.23M
        pWhat++;
398
1.23M
        pStr++;
399
1.23M
    }
400
812k
    return true;
401
6.92M
}
402
403
404
/**
405
 * Whether rString contains word rWhat at nPos
406
 */
407
bool ImpSvNumberInputScan::StringContainsWord( const OUString& rWhat,
408
                                               const OUString& rString, sal_Int32 nPos ) const
409
17.9M
{
410
17.9M
    if (rWhat.isEmpty() || rString.getLength() < nPos + rWhat.getLength())
411
11.8M
        return false;
412
413
6.18M
    if (StringPtrContainsImpl( rWhat, rString.getStr(), nPos))
414
80.5k
    {
415
80.5k
        nPos += rWhat.getLength();
416
80.5k
        if (nPos == rString.getLength())
417
17.2k
            return true;    // word at end of string
418
419
        /* TODO: we COULD invoke bells and whistles word break iterator to find
420
         * the next boundary, but really ... this is called for date input, so
421
         * how many languages do not separate the day and month names in some
422
         * form? */
423
424
        // Check simple ASCII first before invoking i18n or anything else.
425
63.3k
        const sal_Unicode c = rString[nPos];
426
427
        // Common separating ASCII characters in date context.
428
63.3k
        switch (c)
429
63.3k
        {
430
13.8k
            case ' ':
431
31.5k
            case '-':
432
48.2k
            case '.':
433
48.2k
            case '/':
434
48.2k
                return true;
435
15.0k
            default:
436
15.0k
                ;   // nothing
437
63.3k
        }
438
439
15.0k
        if (rtl::isAsciiAlphanumeric( c ))
440
4.35k
            return false;   // Alpha or numeric is not word gap.
441
442
10.7k
        sal_Int32 nIndex = nPos;
443
10.7k
        rString.iterateCodePoints( &nIndex);
444
10.7k
        if (nPos+1 < nIndex)
445
0
            return true;    // Surrogate, assume these to be new words.
446
447
10.7k
        const sal_Int32 nType = mrCurrentLanguageData.GetCharClass()->getCharacterType( rString, nPos);
448
10.7k
        using namespace ::com::sun::star::i18n;
449
450
10.7k
        if ((nType & (KCharacterType::UPPER | KCharacterType::LOWER | KCharacterType::DIGIT)) != 0)
451
848
            return false;   // Alpha or numeric is not word gap.
452
453
9.87k
        if (nType & KCharacterType::LETTER)
454
0
            return true;    // Letter other than alpha is new word. (Is it?)
455
456
9.87k
        return true;        // Catch all remaining as gap until we know better.
457
9.87k
    }
458
459
6.09M
    return false;
460
6.18M
}
461
462
463
/**
464
 * Skips the supplied char
465
 */
466
inline bool ImpSvNumberInputScan::SkipChar( sal_Unicode c, std::u16string_view rString,
467
                                            sal_Int32& nPos )
468
5.16M
{
469
5.16M
    if ((nPos < static_cast<sal_Int32>(rString.size())) && (rString[nPos] == c))
470
625k
    {
471
625k
        nPos++;
472
625k
        return true;
473
625k
    }
474
4.53M
    return false;
475
5.16M
}
476
477
478
/**
479
 * Skips blanks
480
 */
481
inline bool ImpSvNumberInputScan::SkipBlanks( const OUString& rString,
482
                                              sal_Int32& nPos )
483
6.80M
{
484
6.80M
    sal_Int32 nHere = nPos;
485
6.80M
    if ( nPos < rString.getLength() )
486
5.06M
    {
487
5.06M
        const sal_Unicode* p = rString.getStr() + nPos;
488
6.56M
        while ( *p == ' ' || *p == cNoBreakSpace || *p == cNarrowNoBreakSpace )
489
1.49M
        {
490
1.49M
            nPos++;
491
1.49M
            p++;
492
1.49M
        }
493
5.06M
    }
494
6.80M
    return nHere < nPos;
495
6.80M
}
496
497
498
/**
499
 * jump over rWhat in rString at nPos
500
 */
501
inline bool ImpSvNumberInputScan::SkipString( const OUString& rWhat,
502
                                              const OUString& rString, sal_Int32& nPos )
503
3.99M
{
504
3.99M
    if ( StringContains( rWhat, rString, nPos ) )
505
572k
    {
506
572k
        nPos = nPos + rWhat.getLength();
507
572k
        return true;
508
572k
    }
509
3.42M
    return false;
510
3.99M
}
511
512
513
/**
514
 * Recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping
515
 */
516
inline bool ImpSvNumberInputScan::GetThousandSep( std::u16string_view rString,
517
                                                  sal_Int32& nPos,
518
                                                  sal_uInt16 nStringPos ) const
519
1.35M
{
520
1.35M
    const OUString& rSep = mrCurrentLanguageData.GetNumThousandSep();
521
    // Is it an ordinary space instead of a no-break space?
522
1.35M
    bool bSpaceBreak = (rSep[0] == cNoBreakSpace || rSep[0] == cNarrowNoBreakSpace) &&
523
0
        rString[0] == u' ' &&
524
0
        rSep.getLength() == 1 && rString.size() == 1;
525
1.35M
    if (!((rString == rSep || bSpaceBreak) &&      // nothing else
526
60.1k
           nStringPos < nStringsCnt - 1 &&         // safety first!
527
60.1k
           IsNum[ nStringPos + 1 ] ))              // number follows
528
1.29M
    {
529
1.29M
        return false; // no? => out
530
1.29M
    }
531
532
60.1k
    utl::DigitGroupingIterator aGrouping( mrCurrentLanguageData.GetLocaleData()->getDigitGrouping());
533
    // Match ,### in {3} or ,## in {3,2}
534
    /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or
535
     * ,##,### and to match ,### in {3,2} only if it's the last. However,
536
     * currently there is no track kept where group separators occur. In {3,2}
537
     * #,###,### and #,##,## would be valid input, which maybe isn't even bad
538
     * for #,###,###. Other combinations such as #,###,## maybe not. */
539
60.1k
    sal_Int32 nLen = sStrArray[ nStringPos + 1 ].getLength();
540
60.1k
    if (nLen == aGrouping.get() ||                  // with 3 (or so) digits
541
20.9k
        nLen == aGrouping.advance().get() ||        // or with 2 (or 3 or so) digits
542
20.6k
        nPosThousandString == nStringPos + 1 )      // or concatenated
543
41.8k
    {
544
41.8k
        nPos = nPos + rSep.getLength();
545
41.8k
        return true;
546
41.8k
    }
547
18.2k
    return false;
548
60.1k
}
549
550
551
/**
552
 * Conversion of text to logical value
553
 *  "true" =>  1:
554
 *  "false"=> -1:
555
 *  else   =>  0:
556
 */
557
short ImpSvNumberInputScan::GetLogical( std::u16string_view rString ) const
558
2.06M
{
559
2.06M
    short res;
560
561
2.06M
    const ImpSvNumberformatScan* pFS = mrCurrentLanguageData.GetFormatScanner();
562
2.06M
    if ( rString == pFS->GetTrueString() )
563
756
    {
564
756
        res = 1;
565
756
    }
566
2.06M
    else if ( rString == pFS->GetFalseString() )
567
601
    {
568
601
        res = -1;
569
601
    }
570
2.06M
    else
571
2.06M
    {
572
2.06M
        res = 0;
573
2.06M
    }
574
2.06M
    return res;
575
2.06M
}
576
577
578
/**
579
 * Converts a string containing a month name (JAN, January) at nPos into the
580
 * month number (negative if abbreviated), returns 0 if nothing found
581
 */
582
short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
583
3.15M
{
584
3.15M
    short res = 0; // no month found
585
586
3.15M
    if (rString.getLength() > nPos) // only if needed
587
1.68M
    {
588
1.68M
        if ( !bTextInitialized )
589
129k
        {
590
129k
            InitText();
591
129k
        }
592
1.68M
        sal_Int16 nMonths = mrCurrentLanguageData.GetCalendar()->getNumberOfMonthsInYear();
593
8.77M
        for ( sal_Int16 i = 0; i < nMonths; i++ )
594
7.16M
        {
595
7.16M
            if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveMonthText[i], rString, nPos ) )
596
0
            {   // genitive full names first
597
0
                nPos = nPos + pUpperGenitiveMonthText[i].getLength();
598
0
                res = i + 1;
599
0
                break;  // for
600
0
            }
601
7.16M
            else if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveAbbrevMonthText[i], rString, nPos ) )
602
0
            {   // genitive abbreviated
603
0
                nPos = nPos + pUpperGenitiveAbbrevMonthText[i].getLength();
604
0
                res = sal::static_int_cast< short >(-(i+1)); // negative
605
0
                break;  // for
606
0
            }
607
7.16M
            else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveMonthText[i], rString, nPos ) )
608
0
            {   // partitive full names
609
0
                nPos = nPos + pUpperPartitiveMonthText[i].getLength();
610
0
                res = i+1;
611
0
                break;  // for
612
0
            }
613
7.16M
            else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveAbbrevMonthText[i], rString, nPos ) )
614
0
            {   // partitive abbreviated
615
0
                nPos = nPos + pUpperPartitiveAbbrevMonthText[i].getLength();
616
0
                res = sal::static_int_cast< short >(-(i+1)); // negative
617
0
                break;  // for
618
0
            }
619
7.16M
            else if ( StringContainsWord( pUpperMonthText[i], rString, nPos ) )
620
12.7k
            {   // noun full names
621
12.7k
                nPos = nPos + pUpperMonthText[i].getLength();
622
12.7k
                res = i+1;
623
12.7k
                break;  // for
624
12.7k
            }
625
7.15M
            else if ( StringContainsWord( pUpperAbbrevMonthText[i], rString, nPos ) )
626
51.5k
            {   // noun abbreviated
627
51.5k
                nPos = nPos + pUpperAbbrevMonthText[i].getLength();
628
51.5k
                res = sal::static_int_cast< short >(-(i+1)); // negative
629
51.5k
                break;  // for
630
51.5k
            }
631
7.09M
            else if (i == 2 && mrCurrentLanguageData.GetLanguageTag().getLanguage() == "de")
632
255
            {
633
255
                if (pUpperAbbrevMonthText[i] == u"M\u00C4R" && StringContainsWord( u"MRZ"_ustr, rString, nPos))
634
0
                {   // Accept MRZ for MÄR
635
0
                    nPos = nPos + 3;
636
0
                    res = sal::static_int_cast< short >(-(i+1)); // negative
637
0
                    break;  // for
638
0
                }
639
255
                else if (pUpperAbbrevMonthText[i] == "MRZ" && StringContainsWord( u"M\u00C4R"_ustr, rString, nPos))
640
0
                {   // And vice versa, accept MÄR for MRZ
641
0
                    nPos = nPos + 3;
642
0
                    res = sal::static_int_cast< short >(-(i+1)); // negative
643
0
                    break;  // for
644
0
                }
645
255
            }
646
7.09M
            else if (i == 8)
647
581k
            {
648
                // This assumes the weirdness is applicable to all locales.
649
                // It is the case for at least en-* and de-* locales.
650
581k
                if (pUpperAbbrevMonthText[i] == "SEPT" && StringContainsWord( u"SEP"_ustr, rString, nPos))
651
0
                {   // #102136# The correct English form of month September abbreviated is
652
                    // SEPT, but almost every data contains SEP instead.
653
0
                    nPos = nPos + 3;
654
0
                    res = sal::static_int_cast< short >(-(i+1)); // negative
655
0
                    break;  // for
656
0
                }
657
581k
                else if (pUpperAbbrevMonthText[i] == "SEP" && StringContainsWord( u"SEPT"_ustr, rString, nPos))
658
4.00k
                {   // And vice versa, accept SEPT for SEP
659
4.00k
                    nPos = nPos + 4;
660
4.00k
                    res = sal::static_int_cast< short >(-(i+1)); // negative
661
4.00k
                    break;  // for
662
4.00k
                }
663
581k
            }
664
7.16M
        }
665
1.68M
        if (!res)
666
1.61M
        {
667
            // Brutal hack for German locales that know "Januar" or "Jänner".
668
            /* TODO: add alternative month names to locale data? if there are
669
             * more languages... */
670
1.61M
            const LanguageTag& rLanguageTag = mrCurrentLanguageData.GetLanguageTag();
671
1.61M
            if (rLanguageTag.getLanguage() == "de")
672
1.25k
            {
673
1.25k
                if (rLanguageTag.getCountry() == "AT")
674
424
                {
675
                    // Locale data has Jänner/Jän
676
424
                    assert(pUpperMonthText[0] == u"J\u00C4NNER");
677
424
                    if (StringContainsWord( u"JANUAR"_ustr, rString, nPos))
678
0
                    {
679
0
                        nPos += 6;
680
0
                        res = 1;
681
0
                    }
682
424
                    else if (StringContainsWord( u"JAN"_ustr, rString, nPos))
683
0
                    {
684
0
                        nPos += 3;
685
0
                        res = -1;
686
0
                    }
687
424
                }
688
834
                else
689
834
                {
690
                    // Locale data has Januar/Jan
691
834
                    assert(pUpperMonthText[0] == "JANUAR");
692
834
                    if (StringContainsWord( u"J\u00C4NNER"_ustr, rString, nPos))
693
0
                    {
694
0
                        nPos += 6;
695
0
                        res = 1;
696
0
                    }
697
834
                    else if (StringContainsWord( u"J\u00C4N"_ustr, rString, nPos))
698
0
                    {
699
0
                        nPos += 3;
700
0
                        res = -1;
701
0
                    }
702
834
                }
703
1.25k
            }
704
1.61M
        }
705
1.68M
    }
706
707
3.15M
    return res;
708
3.15M
}
709
710
711
/**
712
 * Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the
713
 * DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found
714
 */
715
int ImpSvNumberInputScan::GetDayOfWeek( const OUString& rString, sal_Int32& nPos )
716
752k
{
717
752k
    int res = 0; // no day found
718
719
752k
    if (rString.getLength() > nPos) // only if needed
720
660k
    {
721
660k
        if ( !bTextInitialized )
722
0
        {
723
0
            InitText();
724
0
        }
725
660k
        sal_Int16 nDays = mrCurrentLanguageData.GetCalendar()->getNumberOfDaysInWeek();
726
2.20M
        for ( sal_Int16 i = 0; i < nDays; i++ )
727
1.54M
        {
728
1.54M
            if ( StringContainsWord( pUpperDayText[i], rString, nPos ) )
729
483
            {   // full names first
730
483
                nPos = nPos + pUpperDayText[i].getLength();
731
483
                res = i + 1;
732
483
                break;  // for
733
483
            }
734
1.54M
            if ( StringContainsWord( pUpperAbbrevDayText[i], rString, nPos ) )
735
6.60k
            {   // abbreviated
736
6.60k
                nPos = nPos + pUpperAbbrevDayText[i].getLength();
737
6.60k
                res = -(i + 1); // negative
738
6.60k
                break;  // for
739
6.60k
            }
740
1.54M
        }
741
660k
    }
742
743
752k
    return res;
744
752k
}
745
746
747
/**
748
 * Reading a currency symbol
749
 * '$'   => true
750
 * else => false
751
 */
752
bool ImpSvNumberInputScan::GetCurrency( const OUString& rString, sal_Int32& nPos )
753
1.40M
{
754
1.40M
    if ( rString.getLength() > nPos )
755
1.26M
    {
756
1.26M
        if ( !aUpperCurrSymbol.getLength() )
757
121k
        {   // If no format specified the currency of the currently active locale.
758
121k
            LanguageType eLang = (mpFormat ? mpFormat->GetLanguage() :
759
121k
                    mrCurrentLanguageData.GetLocaleData()->getLanguageTag().getLanguageType());
760
121k
            aUpperCurrSymbol = mrCurrentLanguageData.GetCharClass()->uppercase(
761
121k
                SvNumberFormatter::GetCurrencyEntry( eLang ).GetSymbol() );
762
121k
        }
763
1.26M
        if ( StringContains( aUpperCurrSymbol, rString, nPos ) )
764
98.9k
        {
765
98.9k
            nPos = nPos + aUpperCurrSymbol.getLength();
766
98.9k
            return true;
767
98.9k
        }
768
1.17M
        if ( mpFormat )
769
99.9k
        {
770
99.9k
            OUString aSymbol, aExtension;
771
99.9k
            if ( mpFormat->GetNewCurrencySymbol( aSymbol, aExtension ) )
772
6.78k
            {
773
6.78k
                if ( aSymbol.getLength() <= rString.getLength() - nPos )
774
4.59k
                {
775
4.59k
                    aSymbol = mrCurrentLanguageData.GetCharClass()->uppercase(aSymbol);
776
4.59k
                    if ( StringContains( aSymbol, rString, nPos ) )
777
81
                    {
778
81
                        nPos = nPos + aSymbol.getLength();
779
81
                        return true;
780
81
                    }
781
4.59k
                }
782
6.78k
            }
783
99.9k
        }
784
1.17M
    }
785
786
1.31M
    return false;
787
1.40M
}
788
789
790
/**
791
 * Reading the time period specifier (AM/PM) for the 12 hour clock
792
 *
793
 *  Returns:
794
 *   "AM" or "PM" => true
795
 *   else         => false
796
 *
797
 *  nAmPos:
798
 *   "AM"  =>  1
799
 *   "PM"  => -1
800
 *   else =>  0
801
*/
802
bool ImpSvNumberInputScan::GetTimeAmPm( const OUString& rString, sal_Int32& nPos )
803
642k
{
804
805
642k
    if ( rString.getLength() > nPos )
806
338k
    {
807
338k
        const CharClass* pChr = mrCurrentLanguageData.GetCharClass();
808
338k
        const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();
809
338k
        if ( StringContains( pChr->uppercase( pLoc->getTimeAM() ), rString, nPos ) )
810
10.0k
        {
811
10.0k
            nAmPm = 1;
812
10.0k
            nPos = nPos + pLoc->getTimeAM().getLength();
813
10.0k
            return true;
814
10.0k
        }
815
328k
        else if ( StringContains( pChr->uppercase( pLoc->getTimePM() ), rString, nPos ) )
816
15.7k
        {
817
15.7k
            nAmPm = -1;
818
15.7k
            nPos = nPos + pLoc->getTimePM().getLength();
819
15.7k
            return true;
820
15.7k
        }
821
338k
    }
822
823
616k
    return false;
824
642k
}
825
826
827
/**
828
 * Read a decimal separator (',')
829
 * ','   => true
830
 * else => false
831
 */
832
inline bool ImpSvNumberInputScan::GetDecSep( std::u16string_view rString, sal_Int32& nPos ) const
833
2.90M
{
834
2.90M
    if ( static_cast<sal_Int32>(rString.size()) > nPos )
835
2.63M
    {
836
2.63M
        const OUString& rSep = mrCurrentLanguageData.GetNumDecimalSep();
837
2.63M
        if ( o3tl::starts_with(rString.substr(nPos), rSep) )
838
284k
        {
839
284k
            nPos = nPos + rSep.getLength();
840
284k
            return true;
841
284k
        }
842
2.34M
        const OUString& rSepAlt = mrCurrentLanguageData.GetNumDecimalSepAlt();
843
2.34M
        if ( !rSepAlt.isEmpty() && o3tl::starts_with(rString.substr(nPos), rSepAlt) )
844
0
        {
845
0
            nPos = nPos + rSepAlt.getLength();
846
0
            return true;
847
0
        }
848
2.34M
    }
849
2.61M
    return false;
850
2.90M
}
851
852
853
/**
854
 * Reading a hundredth seconds separator
855
 */
856
inline bool ImpSvNumberInputScan::GetTime100SecSep( std::u16string_view rString, sal_Int32& nPos ) const
857
91.4k
{
858
91.4k
    if ( static_cast<sal_Int32>(rString.size()) > nPos )
859
79.7k
    {
860
79.7k
        if (bIso8601Tsep)
861
22.1k
        {
862
            // ISO 8601 specifies both '.' dot and ',' comma as fractional
863
            // separator.
864
22.1k
            if (rString[nPos] == '.' || rString[nPos] == ',')
865
9.60k
            {
866
9.60k
                ++nPos;
867
9.60k
                return true;
868
9.60k
            }
869
22.1k
        }
870
        // Even in an otherwise ISO 8601 string be lenient and accept the
871
        // locale defined separator.
872
70.1k
        const OUString& rSep = mrCurrentLanguageData.GetLocaleData()->getTime100SecSep();
873
70.1k
        if ( o3tl::starts_with(rString.substr(nPos), rSep))
874
0
        {
875
0
            nPos = nPos + rSep.getLength();
876
0
            return true;
877
0
        }
878
70.1k
    }
879
81.8k
    return false;
880
91.4k
}
881
882
883
/**
884
 * Read a sign including brackets
885
 * '+'   =>  1
886
 * '-'   => -1
887
 * u'−'   => -1
888
 *  '('   => -1, bNegCheck = 1
889
 * else =>  0
890
 */
891
int ImpSvNumberInputScan::GetSign( std::u16string_view rString, sal_Int32& nPos )
892
1.57M
{
893
1.57M
    if (static_cast<sal_Int32>(rString.size()) > nPos)
894
1.42M
        switch (rString[ nPos ])
895
1.42M
        {
896
22.5k
        case '+':
897
22.5k
            nPos++;
898
22.5k
            return 1;
899
61.8k
        case '(': // '(' similar to '-' ?!?
900
61.8k
            bNegCheck = true;
901
61.8k
            [[fallthrough]];
902
206k
        case '-':
903
        // tdf#117037 - unicode minus (0x2212)
904
206k
        case u'−':
905
206k
            nPos++;
906
206k
            return -1;
907
1.19M
        default:
908
1.19M
            break;
909
1.42M
        }
910
911
1.34M
    return 0;
912
1.57M
}
913
914
915
/**
916
 * Read a sign with an exponent
917
 * '+'   =>  1
918
 * '-'   => -1
919
 * else =>  0
920
 */
921
short ImpSvNumberInputScan::GetESign( std::u16string_view rString, sal_Int32& nPos )
922
106k
{
923
106k
    if (static_cast<sal_Int32>(rString.size()) > nPos)
924
56.1k
    {
925
56.1k
        switch (rString[nPos])
926
56.1k
        {
927
11.2k
        case '+':
928
11.2k
            nPos++;
929
11.2k
            return 1;
930
39.6k
        case '-':
931
39.6k
            nPos++;
932
39.6k
            return -1;
933
5.22k
        default:
934
5.22k
            break;
935
56.1k
        }
936
56.1k
    }
937
56.0k
    return 0;
938
106k
}
939
940
941
/**
942
 * i counts string portions, j counts numbers thereof.
943
 * It should had been called SkipNumber instead.
944
 */
945
inline bool ImpSvNumberInputScan::GetNextNumber( sal_uInt16& i, sal_uInt16& j ) const
946
5.79M
{
947
5.79M
    if ( i < nStringsCnt && IsNum[i] )
948
3.11M
    {
949
3.11M
        j++;
950
3.11M
        i++;
951
3.11M
        return true;
952
3.11M
    }
953
2.68M
    return false;
954
5.79M
}
955
956
957
bool ImpSvNumberInputScan::GetTimeRef( double& fOutNumber,
958
                                       sal_uInt16 nIndex, // j-value of the first numeric time part of input, default 0
959
                                       sal_uInt16 nCnt,   // count of numeric time parts
960
                                       SvNumInputOptions eInputOptions
961
                                     ) const
962
234k
{
963
234k
    bool bRet = true;
964
234k
    sal_Int32 nHour;
965
234k
    sal_Int32 nMinute = 0;
966
234k
    sal_Int32 nSecond = 0;
967
234k
    double fSecond100 = 0.0;
968
234k
    sal_uInt16 nStartIndex = nIndex;
969
970
234k
    if (nDecPos == 2 && (nCnt == 3 || nCnt == 2)) // 20:45.5 or 45.5
971
12.9k
    {
972
12.9k
        nHour = 0;
973
12.9k
    }
974
222k
    else if (mpFormat && nDecPos == 0 && nCnt == 2 && mpFormat->IsMinuteSecondFormat())
975
920
    {
976
        // Input on MM:SS format, instead of doing HH:MM:00
977
920
        nHour = 0;
978
920
    }
979
221k
    else if (nIndex - nStartIndex < nCnt)
980
221k
    {
981
221k
        const OUString& rValStr = sStrArray[nNums[nIndex++]];
982
221k
        nHour = rValStr.toInt32();
983
221k
        if (nHour == 0 && rValStr != "0" && rValStr != "00")
984
5.03k
            bRet = false;   // overflow -> Text
985
221k
    }
986
0
    else
987
0
    {
988
0
        nHour = 0;
989
0
        bRet = false;
990
0
        SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetTimeRef: bad number index");
991
0
    }
992
993
    // 0:123 or 0:0:123 or 0:123:59 is valid
994
234k
    bool bAllowDuration = (nHour == 0 && !nAmPm);
995
996
234k
    if (nAmPm && nHour > 12) // not a valid AM/PM clock time
997
4.88k
    {
998
4.88k
        bRet = false;
999
4.88k
    }
1000
230k
    else if (nAmPm == -1 && nHour != 12) // PM
1001
6.35k
    {
1002
6.35k
        nHour += 12;
1003
6.35k
    }
1004
223k
    else if (nAmPm == 1 && nHour == 12) // 12 AM
1005
573
    {
1006
573
        nHour = 0;
1007
573
    }
1008
1009
234k
    if (nDecPos == 2 && nCnt == 2) // 45.5
1010
25
    {
1011
25
        nMinute = 0;
1012
25
    }
1013
234k
    else if (nIndex - nStartIndex < nCnt)
1014
65.8k
    {
1015
65.8k
        const OUString& rValStr = sStrArray[nNums[nIndex++]];
1016
65.8k
        nMinute = rValStr.toInt32();
1017
65.8k
        if (nMinute == 0 && rValStr != "0" && rValStr != "00")
1018
4.06k
            bRet = false;   // overflow -> Text
1019
65.8k
        if (!(eInputOptions & SvNumInputOptions::LAX_TIME) && !bAllowDuration
1020
40.0k
                && nIndex > 1 && nMinute > 59)
1021
8.36k
            bRet = false;   // 1:60 or 1:123 is invalid, 123:1 or 0:123 is valid
1022
65.8k
        if (bAllowDuration)
1023
25.8k
            bAllowDuration = (nMinute == 0);
1024
65.8k
    }
1025
234k
    if (nIndex - nStartIndex < nCnt)
1026
41.2k
    {
1027
41.2k
        const OUString& rValStr = sStrArray[nNums[nIndex++]];
1028
41.2k
        nSecond = rValStr.toInt32();
1029
41.2k
        if (nSecond == 0 && rValStr != "0" && rValStr != "00")
1030
2.86k
            bRet = false;   // overflow -> Text
1031
41.2k
        if (!(eInputOptions & SvNumInputOptions::LAX_TIME) && !bAllowDuration
1032
34.1k
                && nIndex > 1 && nSecond > 59 && !(nHour == 23 && nMinute == 59 && nSecond == 60))
1033
8.61k
            bRet = false;   // 1:60 or 1:123 or 1:1:123 is invalid, 123:1 or 123:1:1 or 0:0:123 is valid, or leap second
1034
41.2k
    }
1035
234k
    if (nIndex - nStartIndex < nCnt)
1036
17.8k
    {
1037
17.8k
        fSecond100 = StringToDouble(Concat2View("." + sStrArray[nNums[nIndex]]));
1038
17.8k
    }
1039
234k
    fOutNumber = (static_cast<double>(nHour)*3600 +
1040
234k
                  static_cast<double>(nMinute)*60 +
1041
234k
                  static_cast<double>(nSecond) +
1042
234k
                  fSecond100)/86400.0;
1043
234k
    return bRet;
1044
234k
}
1045
1046
1047
sal_uInt16 ImpSvNumberInputScan::ImplGetDay( sal_uInt16 nIndex ) const
1048
118k
{
1049
118k
    sal_uInt16 nRes = 0;
1050
1051
118k
    if (sStrArray[nNums[nIndex]].getLength() <= 2)
1052
97.1k
    {
1053
97.1k
        sal_uInt16 nNum = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1054
97.1k
        if (nNum <= 31)
1055
91.1k
        {
1056
91.1k
            nRes = nNum;
1057
91.1k
        }
1058
97.1k
    }
1059
1060
118k
    return nRes;
1061
118k
}
1062
1063
1064
sal_uInt16 ImpSvNumberInputScan::ImplGetMonth( sal_uInt16 nIndex ) const
1065
106k
{
1066
    // Preset invalid month number
1067
106k
    sal_uInt16 nRes = mrCurrentLanguageData.GetCalendar()->getNumberOfMonthsInYear();
1068
1069
106k
    if (sStrArray[nNums[nIndex]].getLength() <= 2)
1070
90.7k
    {
1071
90.7k
        sal_uInt16 nNum = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1072
90.7k
        if ( 0 < nNum && nNum <= nRes )
1073
46.5k
        {
1074
46.5k
            nRes = nNum - 1; // zero based for CalendarFieldIndex::MONTH
1075
46.5k
        }
1076
90.7k
    }
1077
1078
106k
    return nRes;
1079
106k
}
1080
1081
1082
/**
1083
 * 30 -> 1930, 29 -> 2029, or 56 -> 1756, 55 -> 1855, ...
1084
 */
1085
sal_uInt16 ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex )
1086
116k
{
1087
116k
    sal_uInt16 nYear = 0;
1088
1089
116k
    sal_Int32 nLen = sStrArray[nNums[nIndex]].getLength();
1090
    // 16-bit integer year width can have 5 digits, allow for one additional
1091
    // leading zero as convention.
1092
116k
    if (nLen <= 6)
1093
108k
    {
1094
108k
        nYear = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1095
        // A year in another, not Gregorian CE era is never expanded.
1096
        // A year < 100 entered with at least 3 digits with leading 0 is taken
1097
        // as is without expansion.
1098
108k
        if (mbEraCE == kDefaultEra && nYear < 100 && nLen < 3)
1099
55.3k
        {
1100
55.3k
            nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 );
1101
55.3k
        }
1102
108k
    }
1103
1104
116k
    return nYear;
1105
116k
}
1106
1107
1108
bool ImpSvNumberInputScan::MayBeIso8601()
1109
2.08M
{
1110
2.08M
    if (nMayBeIso8601 == 0)
1111
1.25M
    {
1112
1.25M
        nMayBeIso8601 = 1;
1113
1.25M
        sal_Int32 nLen = ((nNumericsCnt >= 1 && nNums[0] < nStringsCnt) ? sStrArray[nNums[0]].getLength() : 0);
1114
1.25M
        if (nLen)
1115
1.25M
        {
1116
1.25M
            sal_Int32 n;
1117
1.25M
            if (nNumericsCnt >= 3 && nNums[2] < nStringsCnt &&
1118
444k
                sStrArray[nNums[0]+1] == "-" && // separator year-month
1119
137k
                (n = sStrArray[nNums[1]].toInt32()) >= 1 && n <= 12 &&  // month
1120
123k
                sStrArray[nNums[1]+1] == "-" && // separator month-day
1121
113k
                (n = sStrArray[nNums[2]].toInt32()) >= 1 && n <= 31)    // day
1122
106k
            {
1123
                // Year (nNums[0]) value not checked, may be anything, but
1124
                // length (number of digits) is checked.
1125
106k
                nMayBeIso8601 = (nLen >= 4 ? 4 : (nLen == 3 ? 3 : (nLen > 0 ? 2 : 1)));
1126
106k
            }
1127
1.25M
        }
1128
1.25M
    }
1129
2.08M
    return nMayBeIso8601 > 1;
1130
2.08M
}
1131
1132
1133
bool ImpSvNumberInputScan::CanForceToIso8601( DateOrder eDateOrder )
1134
149k
{
1135
149k
    int nCanForceToIso8601 = 0;
1136
149k
    if (!MayBeIso8601())
1137
55.3k
    {
1138
55.3k
        return false;
1139
55.3k
    }
1140
94.2k
    else if (nMayBeIso8601 >= 3)
1141
56.5k
    {
1142
56.5k
        return true;    // at least 3 digits in year
1143
56.5k
    }
1144
37.7k
    else
1145
37.7k
    {
1146
37.7k
        if (eDateOrder == DateOrder::Invalid)
1147
1.83k
        {
1148
            // As if any of the cases below can be applied, but only if a
1149
            // locale dependent date pattern was not matched.
1150
1.83k
            if ((GetDatePatternNumbers() == nNumericsCnt) && IsDatePatternNumberOfType(0,'Y'))
1151
0
                return false;
1152
1.83k
            eDateOrder = GetDateOrder();
1153
1.83k
        }
1154
1155
        // No date pattern matched at all can be forced to ISO 8601 here as is.
1156
37.7k
        if (GetDatePatternNumbers() == 0)
1157
37.7k
            return true;
1158
1159
0
        nCanForceToIso8601 = 1;
1160
0
    }
1161
1162
0
    sal_Int32 n;
1163
0
    switch (eDateOrder)
1164
0
    {
1165
0
        case DateOrder::DMY:               // "day" value out of range => ISO 8601 year
1166
0
            n = sStrArray[nNums[0]].toInt32();
1167
0
            if (n < 1 || n > 31)
1168
0
            {
1169
0
                nCanForceToIso8601 = 2;
1170
0
            }
1171
0
        break;
1172
0
        case DateOrder::MDY:               // "month" value out of range => ISO 8601 year
1173
0
            n = sStrArray[nNums[0]].toInt32();
1174
0
            if (n < 1 || n > 12)
1175
0
            {
1176
0
                nCanForceToIso8601 = 2;
1177
0
            }
1178
0
        break;
1179
0
        case DateOrder::YMD:               // always possible
1180
0
            nCanForceToIso8601 = 2;
1181
0
        break;
1182
0
        default: break;
1183
0
    }
1184
0
    return nCanForceToIso8601 > 1;
1185
0
}
1186
1187
1188
bool ImpSvNumberInputScan::IsAcceptableIso8601()
1189
44.2k
{
1190
44.2k
    if (mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE))
1191
11.5k
    {
1192
11.5k
        switch (mrCurrentLanguageData.GetEvalDateFormat())
1193
11.5k
        {
1194
0
            case NfEvalDateFormat::International:
1195
0
                return CanForceToIso8601( GetDateOrder());
1196
0
            case NfEvalDateFormat::Format:
1197
0
                return CanForceToIso8601( mpFormat->GetDateOrder());
1198
11.5k
            default:
1199
11.5k
                return CanForceToIso8601( GetDateOrder()) || CanForceToIso8601( mpFormat->GetDateOrder());
1200
11.5k
        }
1201
11.5k
    }
1202
32.7k
    return CanForceToIso8601( GetDateOrder());
1203
44.2k
}
1204
1205
1206
bool ImpSvNumberInputScan::MayBeMonthDate()
1207
1.42M
{
1208
1.42M
    if (nMayBeMonthDate == 0)
1209
1.11M
    {
1210
1.11M
        nMayBeMonthDate = 1;
1211
1.11M
        if (nNumericsCnt >= 2 && nNums[1] < nStringsCnt)
1212
627k
        {
1213
            // "-Jan-"
1214
627k
            const OUString& rM = sStrArray[ nNums[ 0 ] + 1 ];
1215
627k
            if (rM.getLength() >= 3 && rM[0] == '-' && rM[ rM.getLength() - 1] == '-')
1216
25.9k
            {
1217
                // Check year length assuming at least 3 digits (including
1218
                // leading zero). Two digit years 1..31 are out of luck here
1219
                // and may be taken as day of month.
1220
25.9k
                bool bYear1 = (sStrArray[nNums[0]].getLength() >= 3);
1221
25.9k
                bool bYear2 = (sStrArray[nNums[1]].getLength() >= 3);
1222
25.9k
                sal_Int32 n;
1223
25.9k
                bool bDay1 = !bYear1;
1224
25.9k
                if (bDay1)
1225
15.5k
                {
1226
15.5k
                    n = sStrArray[nNums[0]].toInt32();
1227
15.5k
                    bDay1 = n >= 1 && n <= 31;
1228
15.5k
                }
1229
25.9k
                bool bDay2 = !bYear2;
1230
25.9k
                if (bDay2)
1231
21.5k
                {
1232
21.5k
                    n = sStrArray[nNums[1]].toInt32();
1233
21.5k
                    bDay2 = n >= 1 && n <= 31;
1234
21.5k
                }
1235
1236
25.9k
                if (bDay1 && !bDay2)
1237
3.73k
                {
1238
3.73k
                    nMayBeMonthDate = 2;        // dd-month-yy
1239
3.73k
                }
1240
22.2k
                else if (!bDay1 && bDay2)
1241
9.32k
                {
1242
9.32k
                    nMayBeMonthDate = 3;        // yy-month-dd
1243
9.32k
                }
1244
12.9k
                else if (bDay1 && bDay2)
1245
7.74k
                {
1246
                    // Ambiguous ##-MMM-## date, but some big vendor's database
1247
                    // reports write this crap, assume this always to be
1248
7.74k
                    nMayBeMonthDate = 2;        // dd-month-yy
1249
7.74k
                }
1250
25.9k
            }
1251
627k
        }
1252
1.11M
    }
1253
1.42M
    return nMayBeMonthDate > 1;
1254
1.42M
}
1255
1256
1257
/** If a string is a separator plus '-' minus sign preceding a 'Y' year in
1258
    a date pattern at position nPat.
1259
 */
1260
static bool lcl_IsSignedYearSep( std::u16string_view rStr, std::u16string_view rPat, sal_Int32 nPat )
1261
320k
{
1262
320k
    bool bOk = false;
1263
320k
    sal_Int32 nLen = rStr.size();
1264
320k
    if (nLen > 1 && rStr[nLen-1] == '-')
1265
27.2k
    {
1266
27.2k
        --nLen;
1267
27.2k
        if (nPat + nLen < static_cast<sal_Int32>(rPat.size()) && rPat[nPat+nLen] == 'Y')
1268
5.92k
        {
1269
            // Signed year is possible.
1270
5.92k
            bOk = (rPat.find( rStr.substr( 0, nLen), nPat) == static_cast<size_t>(nPat));
1271
5.92k
        }
1272
27.2k
    }
1273
320k
    return bOk;
1274
320k
}
1275
1276
1277
/** Length of separator usually is 1 but theoretically could be anything. */
1278
static sal_Int32 lcl_getPatternSeparatorLength( std::u16string_view rPat, sal_Int32 nPat )
1279
424k
{
1280
424k
    sal_Int32 nSep = nPat;
1281
424k
    sal_Unicode c;
1282
848k
    while (nSep < static_cast<sal_Int32>(rPat.size()) && (c = rPat[nSep]) != 'D' && c != 'M' && c != 'Y')
1283
424k
        ++nSep;
1284
424k
    return nSep - nPat;
1285
424k
}
1286
1287
1288
bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
1289
2.38M
{
1290
2.38M
    if (nAcceptedDatePattern >= -1)
1291
1.07M
    {
1292
1.07M
        return (nAcceptedDatePattern >= 0);
1293
1.07M
    }
1294
1.30M
    if (!nNumericsCnt)
1295
0
    {
1296
0
        nAcceptedDatePattern = -1;
1297
0
    }
1298
1.30M
    else if (!sDateAcceptancePatterns.hasElements())
1299
118k
    {
1300
        // The current locale is the format's locale, if a format is present.
1301
118k
        const NfEvalDateFormat eEDF = mrCurrentLanguageData.GetEvalDateFormat();
1302
118k
        if (!mpFormat || eEDF == NfEvalDateFormat::Format || mpFormat->GetLanguage() == mrCurrentLanguageData.GetIniLanguage())
1303
82.8k
        {
1304
82.8k
            sDateAcceptancePatterns = mrCurrentLanguageData.GetLocaleData()->getDateAcceptancePatterns();
1305
82.8k
        }
1306
36.0k
        else
1307
36.0k
        {
1308
36.0k
            OnDemandLocaleDataWrapper& xLocaleData = mrCurrentLanguageData.GetOnDemandLocaleDataWrapper(
1309
36.0k
                    SvNFLanguageData::InputScannerPrivateAccess());
1310
36.0k
            const LanguageTag aSaveLocale( xLocaleData->getLanguageTag() );
1311
36.0k
            assert(mpFormat->GetLanguage() == aSaveLocale.getLanguageType());   // prerequisite
1312
            // Obtain formatter's locale's (e.g. system) patterns.
1313
36.0k
            xLocaleData.changeLocale( LanguageTag( mrCurrentLanguageData.GetIniLanguage()));
1314
36.0k
            css::uno::Sequence<OUString> aLocalePatterns( xLocaleData->getDateAcceptancePatterns());
1315
            // Reset to format's locale.
1316
36.0k
            xLocaleData.changeLocale( aSaveLocale);
1317
            // When concatenating don't care about duplicates, combining
1318
            // weeding those out reallocs yet another time and probably doesn't
1319
            // take less time than looping over two additional patterns below...
1320
36.0k
            switch (eEDF)
1321
36.0k
            {
1322
0
                case NfEvalDateFormat::Format:
1323
0
                    assert(!"shouldn't reach here");
1324
0
                break;
1325
0
                case NfEvalDateFormat::International:
1326
0
                    sDateAcceptancePatterns = std::move(aLocalePatterns);
1327
0
                break;
1328
36.0k
                case NfEvalDateFormat::InternationalThenFormat:
1329
36.0k
                    sDateAcceptancePatterns = comphelper::concatSequences(
1330
36.0k
                            aLocalePatterns,
1331
36.0k
                            xLocaleData->getDateAcceptancePatterns());
1332
36.0k
                break;
1333
19
                case NfEvalDateFormat::FormatThenInternational:
1334
19
                    sDateAcceptancePatterns = comphelper::concatSequences(
1335
19
                            xLocaleData->getDateAcceptancePatterns(),
1336
19
                            aLocalePatterns);
1337
19
                break;
1338
36.0k
            }
1339
36.0k
        }
1340
118k
        SAL_WARN_IF( !sDateAcceptancePatterns.hasElements(), "svl.numbers", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
1341
118k
        nAcceptedDatePattern = (sDateAcceptancePatterns.hasElements() ? -2 : -1);
1342
118k
    }
1343
1344
1.30M
    if (nAcceptedDatePattern == -1)
1345
0
    {
1346
0
        return false;
1347
0
    }
1348
1.30M
    nDatePatternStart = nStartPatternAt; // remember start particle
1349
1350
1.30M
    const sal_Int32 nMonthsInYear = mrCurrentLanguageData.GetCalendar()->getNumberOfMonthsInYear();
1351
1352
4.01M
    for (sal_Int32 nPattern=0; nPattern < sDateAcceptancePatterns.getLength(); ++nPattern)
1353
2.72M
    {
1354
2.72M
        const OUString& rPat = sDateAcceptancePatterns[nPattern];
1355
2.72M
        if (rPat.getLength() == 3)
1356
1.37M
        {
1357
            // Ignore a pattern that would match numeric input with decimal
1358
            // separator. It may had been read from configuration or resulted
1359
            // from the locales' patterns concatenation above.
1360
1.37M
            if (    rPat[1] == mrCurrentLanguageData.GetLocaleData()->getNumDecimalSep().toChar()
1361
1.37M
                 || rPat[1] == mrCurrentLanguageData.GetLocaleData()->getNumDecimalSepAlt().toChar())
1362
0
            {
1363
0
                SAL_WARN("svl.numbers", "ignoring date acceptance pattern with decimal separator ambiguity: " << rPat);
1364
0
                continue;   // for, next pattern
1365
0
            }
1366
1.37M
        }
1367
2.72M
        sal_uInt16 nNext = nDatePatternStart;
1368
2.72M
        nDatePatternNumbers = 0;
1369
2.72M
        bool bOk = true;
1370
2.72M
        sal_Int32 nPat = 0;
1371
5.92M
        for ( ; nPat < rPat.getLength() && bOk && nNext < nStringsCnt; ++nPat, ++nNext)
1372
3.19M
        {
1373
3.19M
            const sal_Unicode c = rPat[nPat];
1374
3.19M
            switch (c)
1375
3.19M
            {
1376
21.5k
            case 'Y':
1377
2.69M
            case 'M':
1378
2.79M
            case 'D':
1379
2.79M
                bOk = IsNum[nNext];
1380
2.79M
                if (bOk && (c == 'M' || c == 'D'))
1381
2.77M
                {
1382
                    // Check the D and M cases for plausibility. This also
1383
                    // prevents recognition of date instead of number with a
1384
                    // numeric group input if date separator is identical to
1385
                    // group separator, for example with D.M as a pattern and
1386
                    // #.### as a group.
1387
2.77M
                    sal_Int32 nMaxLen, nMaxVal;
1388
2.77M
                    switch (c)
1389
2.77M
                    {
1390
2.67M
                        case 'M':
1391
2.67M
                            nMaxLen = 2;
1392
2.67M
                            nMaxVal = nMonthsInYear;
1393
2.67M
                            break;
1394
103k
                        case 'D':
1395
103k
                            nMaxLen = 2;
1396
103k
                            nMaxVal = 31;
1397
103k
                            break;
1398
0
                        default:
1399
                            // This merely exists against
1400
                            // -Werror=maybe-uninitialized, which is nonsense
1401
                            // after the (c == 'M' || c == 'D') check above,
1402
                            // but ...
1403
0
                            nMaxLen = 2;
1404
0
                            nMaxVal = 31;
1405
2.77M
                    }
1406
2.77M
                    bOk = (sStrArray[nNext].getLength() <= nMaxLen);
1407
2.77M
                    if (bOk)
1408
2.11M
                    {
1409
2.11M
                        sal_Int32 nNum = sStrArray[nNext].toInt32();
1410
2.11M
                        bOk = (1 <= nNum && nNum <= nMaxVal);
1411
2.11M
                    }
1412
2.77M
                }
1413
2.79M
                if (bOk)
1414
437k
                    ++nDatePatternNumbers;
1415
2.79M
                break;
1416
398k
            default:
1417
398k
                bOk = !IsNum[nNext];
1418
398k
                if (bOk)
1419
398k
                {
1420
398k
                    const sal_Int32 nSepLen = lcl_getPatternSeparatorLength( rPat, nPat);
1421
                    // Non-numeric input must match separator exactly to be
1422
                    // accepted as such.
1423
398k
                    const sal_Int32 nLen = sStrArray[nNext].getLength();
1424
398k
                    bOk = (nLen == nSepLen && rPat.indexOf( sStrArray[nNext], nPat) == nPat);
1425
398k
                    if (bOk)
1426
79.2k
                    {
1427
79.2k
                        nPat += nLen - 1;
1428
79.2k
                    }
1429
318k
                    else if ((bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat)))
1430
1.22k
                    {
1431
1.22k
                        nPat += nLen - 2;
1432
1.22k
                    }
1433
317k
                    else if (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' ')
1434
42.5k
                    {
1435
42.5k
                        using namespace comphelper::string;
1436
                        // Trailing blanks in input.
1437
42.5k
                        OUStringBuffer aBuf(sStrArray[nNext]);
1438
42.5k
                        aBuf.stripEnd();
1439
                        // Expand again in case of pattern "M. D. " and
1440
                        // input "M. D.  ", maybe fetched far, but...
1441
42.5k
                        padToLength(aBuf, rPat.getLength() - nPat, ' ');
1442
42.5k
                        bOk = (rPat.indexOf( aBuf, nPat) == nPat);
1443
42.5k
                        if (bOk)
1444
1.81k
                        {
1445
1.81k
                            nPat += aBuf.getLength() - 1;
1446
1.81k
                        }
1447
42.5k
                    }
1448
398k
                }
1449
398k
                break;
1450
3.19M
            }
1451
3.19M
        }
1452
2.72M
        if (bOk)
1453
47.4k
        {
1454
            // Check for trailing characters mismatch.
1455
47.4k
            if (nNext < nStringsCnt)
1456
25.8k
            {
1457
                // Pattern end but not input end.
1458
                // A trailing blank may be part of the current pattern input,
1459
                // if pattern is "D.M." and input is "D.M. hh:mm" last was
1460
                // ". ", or may be following the current pattern input, if
1461
                // pattern is "D.M" and input is "D.M hh:mm" last was "M".
1462
25.8k
                sal_Int32 nPos = 0;
1463
25.8k
                sal_uInt16 nCheck;
1464
25.8k
                if (nPat > 0 && nNext > 0)
1465
25.8k
                {
1466
                    // nPat is one behind after the for loop.
1467
25.8k
                    sal_Int32 nPatCheck = nPat - 1;
1468
25.8k
                    switch (rPat[nPatCheck])
1469
25.8k
                    {
1470
7.03k
                        case 'Y':
1471
7.12k
                        case 'M':
1472
25.8k
                        case 'D':
1473
25.8k
                            nCheck = nNext;
1474
25.8k
                            break;
1475
0
                        default:
1476
0
                            {
1477
0
                                nCheck = nNext - 1;
1478
                                // Advance position in input to match length of
1479
                                // non-YMD (separator) characters in pattern.
1480
0
                                sal_Unicode c;
1481
0
                                do
1482
0
                                {
1483
0
                                    ++nPos;
1484
0
                                    c = rPat[--nPatCheck];
1485
0
                                } while (c != 'Y' && c != 'M' && c != 'D' && nPatCheck > 0);
1486
0
                            }
1487
25.8k
                    }
1488
25.8k
                }
1489
0
                else
1490
0
                {
1491
0
                    nCheck = nNext;
1492
0
                }
1493
25.8k
                if (!IsNum[nCheck])
1494
24.7k
                {
1495
                    // Trailing (or separating if time follows) blanks are ok.
1496
                    // No blank and a following number is not.
1497
24.7k
                    const bool bBlanks = SkipBlanks( sStrArray[nCheck], nPos);
1498
24.7k
                    if (nPos == sStrArray[nCheck].getLength() && (bBlanks || !IsNum[nNext]))
1499
5.04k
                    {
1500
5.04k
                        nAcceptedDatePattern = nPattern;
1501
5.04k
                        return true;
1502
5.04k
                    }
1503
24.7k
                }
1504
25.8k
            }
1505
21.6k
            else if (nPat == rPat.getLength())
1506
12.5k
            {
1507
                // Input end and pattern end => match.
1508
12.5k
                nAcceptedDatePattern = nPattern;
1509
12.5k
                return true;
1510
12.5k
            }
1511
            // else Input end but not pattern end, no match.
1512
47.4k
        }
1513
2.72M
    }
1514
1.29M
    nAcceptedDatePattern = -1;
1515
1.29M
    return false;
1516
1.30M
}
1517
1518
1519
bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, sal_Int32 & rPos, bool & rSignedYear )
1520
2.01M
{
1521
    // If not initialized yet start with first number, if any.
1522
2.01M
    if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1523
1.97M
    {
1524
1.97M
        return false;
1525
1.97M
    }
1526
36.3k
    if (nParticle < nDatePatternStart || nParticle >= nStringsCnt || IsNum[nParticle])
1527
0
    {
1528
0
        return false;
1529
0
    }
1530
36.3k
    sal_uInt16 nNext = nDatePatternStart;
1531
36.3k
    const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1532
125k
    for (sal_Int32 nPat = 0; nPat < rPat.getLength() && nNext < nStringsCnt; ++nPat, ++nNext)
1533
115k
    {
1534
115k
        switch (rPat[nPat])
1535
115k
        {
1536
7.42k
        case 'Y':
1537
43.0k
        case 'M':
1538
62.4k
        case 'D':
1539
62.4k
            break;
1540
52.7k
        default:
1541
52.7k
            if (nNext == nParticle)
1542
25.8k
            {
1543
25.8k
                const sal_Int32 nSepLen = lcl_getPatternSeparatorLength( rPat, nPat);
1544
25.8k
                const sal_Int32 nLen = sStrArray[nNext].getLength();
1545
25.8k
                bool bOk = (nLen == nSepLen && rPat.indexOf( sStrArray[nNext], nPat) == nPat);
1546
25.8k
                if (!bOk)
1547
1.83k
                {
1548
1.83k
                    bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat);
1549
1.83k
                    if (bOk)
1550
1.10k
                        rSignedYear = true;
1551
1.83k
                }
1552
25.8k
                if (!bOk && (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' '))
1553
728
                {
1554
                    // The same ugly trailing blanks check as in
1555
                    // IsAcceptedDatePattern().
1556
728
                    using namespace comphelper::string;
1557
728
                    OUStringBuffer aBuf(sStrArray[nNext]);
1558
728
                    aBuf.stripEnd();
1559
728
                    padToLength(aBuf, rPat.getLength() - nPat, ' ');
1560
728
                    bOk = (rPat.indexOf(aBuf, nPat) == nPat);
1561
728
                }
1562
25.8k
                if (bOk)
1563
25.8k
                {
1564
25.8k
                    rPos = nLen; // yes, set, not add!
1565
25.8k
                    return true;
1566
25.8k
                }
1567
0
                else
1568
0
                    return false;
1569
25.8k
            }
1570
26.8k
            nPat += sStrArray[nNext].getLength() - 1;
1571
26.8k
            break;
1572
115k
        }
1573
115k
    }
1574
10.4k
    return false;
1575
36.3k
}
1576
1577
1578
sal_uInt16 ImpSvNumberInputScan::GetDatePatternNumbers()
1579
127k
{
1580
    // If not initialized yet start with first number, if any.
1581
127k
    if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1582
115k
    {
1583
115k
        return 0;
1584
115k
    }
1585
12.2k
    return nDatePatternNumbers;
1586
127k
}
1587
1588
1589
bool ImpSvNumberInputScan::IsDatePatternNumberOfType( sal_uInt16 nNumber, sal_Unicode cType )
1590
34.3k
{
1591
34.3k
    if (GetDatePatternNumbers() <= nNumber)
1592
31.6k
        return false;
1593
1594
2.69k
    sal_uInt16 nNum = 0;
1595
2.69k
    const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1596
2.69k
    for (sal_Int32 nPat = 0; nPat < rPat.getLength(); ++nPat)
1597
2.69k
    {
1598
2.69k
        switch (rPat[nPat])
1599
2.69k
        {
1600
0
            case 'Y':
1601
2.52k
            case 'M':
1602
2.69k
            case 'D':
1603
2.69k
                if (nNum == nNumber)
1604
2.69k
                    return rPat[nPat] == cType;
1605
0
                ++nNum;
1606
0
            break;
1607
2.69k
        }
1608
2.69k
    }
1609
0
    return false;
1610
2.69k
}
1611
1612
1613
sal_uInt32 ImpSvNumberInputScan::GetDatePatternOrder()
1614
188k
{
1615
    // If not initialized yet start with first number, if any.
1616
188k
    if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1617
169k
    {
1618
169k
        return 0;
1619
169k
    }
1620
18.1k
    sal_uInt32 nOrder = 0;
1621
18.1k
    const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1622
77.7k
    for (sal_Int32 nPat = 0; nPat < rPat.getLength() && !(nOrder & 0xff0000); ++nPat)
1623
59.5k
    {
1624
59.5k
        switch (rPat[nPat])
1625
59.5k
        {
1626
2.46k
        case 'Y':
1627
20.6k
        case 'M':
1628
38.8k
        case 'D':
1629
38.8k
            nOrder = (nOrder << 8) | rPat[nPat];
1630
38.8k
            break;
1631
59.5k
        }
1632
59.5k
    }
1633
18.1k
    return nOrder;
1634
18.1k
}
1635
1636
1637
DateOrder ImpSvNumberInputScan::GetDateOrder( bool bFromFormatIfNoPattern )
1638
167k
{
1639
167k
    sal_uInt32 nOrder = GetDatePatternOrder();
1640
167k
    if (!nOrder)
1641
157k
    {
1642
157k
        if (bFromFormatIfNoPattern && mpFormat)
1643
14.3k
            return mpFormat->GetDateOrder();
1644
143k
        else
1645
143k
            return mrCurrentLanguageData.GetLocaleData()->getDateOrder();
1646
157k
    }
1647
10.2k
    switch ((nOrder & 0xff0000) >> 16)
1648
10.2k
    {
1649
0
    case 'Y':
1650
0
        if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'D'))
1651
0
        {
1652
0
            return DateOrder::YMD;
1653
0
        }
1654
0
        break;
1655
2.43k
    case 'M':
1656
2.43k
        if ((((nOrder & 0xff00) >> 8) == 'D') && ((nOrder & 0xff) == 'Y'))
1657
2.43k
        {
1658
2.43k
            return DateOrder::MDY;
1659
2.43k
        }
1660
0
        break;
1661
4
    case 'D':
1662
4
        if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'Y'))
1663
4
        {
1664
4
            return DateOrder::DMY;
1665
4
        }
1666
0
        break;
1667
0
    default:
1668
7.83k
    case 0:
1669
7.83k
        switch ((nOrder & 0xff00) >> 8)
1670
7.83k
        {
1671
0
        case 'Y':
1672
0
            switch (nOrder & 0xff)
1673
0
            {
1674
0
            case 'M':
1675
0
                return DateOrder::YMD;
1676
0
            }
1677
0
            break;
1678
7.30k
        case 'M':
1679
7.30k
            switch (nOrder & 0xff)
1680
7.30k
            {
1681
0
            case 'Y':
1682
0
                return DateOrder::DMY;
1683
7.30k
            case 'D':
1684
7.30k
                return DateOrder::MDY;
1685
7.30k
            }
1686
0
            break;
1687
539
        case 'D':
1688
539
            switch (nOrder & 0xff)
1689
539
            {
1690
0
            case 'Y':
1691
0
                return DateOrder::MDY;
1692
539
            case 'M':
1693
539
                return DateOrder::DMY;
1694
539
            }
1695
0
            break;
1696
0
        default:
1697
0
        case 0:
1698
0
            switch (nOrder & 0xff)
1699
0
            {
1700
0
            case 'Y':
1701
0
                return DateOrder::YMD;
1702
0
            case 'M':
1703
0
                return DateOrder::MDY;
1704
0
            case 'D':
1705
0
                return DateOrder::DMY;
1706
0
            }
1707
0
            break;
1708
7.83k
        }
1709
10.2k
    }
1710
0
    SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateOrder: undefined, falling back to locale's default");
1711
0
    return mrCurrentLanguageData.GetLocaleData()->getDateOrder();
1712
0
}
1713
1714
LongDateOrder ImpSvNumberInputScan::GetMiddleMonthLongDateOrder( bool bFormatTurn,
1715
                                                                 const LocaleDataWrapper* pLoc,
1716
                                                                 DateOrder eDateOrder )
1717
17.4k
{
1718
17.4k
    if (MayBeMonthDate())
1719
8.24k
        return (nMayBeMonthDate == 2) ? LongDateOrder::DMY : LongDateOrder::YMD;
1720
1721
9.20k
    LongDateOrder eLDO;
1722
9.20k
    const sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->GetExactDateOrder() : 0);
1723
9.20k
    if (!nExactDateOrder)
1724
6.05k
        eLDO = pLoc->getLongDateOrder();
1725
3.15k
    else if ((((nExactDateOrder >> 16) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D'))
1726
0
        eLDO = LongDateOrder::YMD;
1727
3.15k
    else if ((((nExactDateOrder >> 16) & 0xff) == 'D') && ((nExactDateOrder & 0xff) == 'Y'))
1728
0
        eLDO = LongDateOrder::DMY;
1729
3.15k
    else
1730
3.15k
        eLDO = pLoc->getLongDateOrder();
1731
9.20k
    if (eLDO != LongDateOrder::YMD && eLDO != LongDateOrder::DMY)
1732
9.20k
    {
1733
9.20k
        switch (eDateOrder)
1734
9.20k
        {
1735
0
            case DateOrder::YMD:
1736
0
                eLDO = LongDateOrder::YMD;
1737
0
            break;
1738
2.84k
            case DateOrder::DMY:
1739
2.84k
                eLDO = LongDateOrder::DMY;
1740
2.84k
            break;
1741
6.35k
            default:
1742
6.35k
                ;   // nothing, not a date
1743
9.20k
        }
1744
9.20k
    }
1745
0
    else if (eLDO == LongDateOrder::DMY && eDateOrder == DateOrder::YMD)
1746
0
    {
1747
        // Check possible order and maybe switch.
1748
0
        if (!ImplGetDay(0) && ImplGetDay(1))
1749
0
            eLDO = LongDateOrder::YMD;
1750
0
    }
1751
0
    else if (eLDO == LongDateOrder::YMD && eDateOrder == DateOrder::DMY)
1752
0
    {
1753
        // Check possible order and maybe switch.
1754
0
        if (!ImplGetDay(1) && ImplGetDay(0))
1755
0
            eLDO = LongDateOrder::DMY;
1756
0
    }
1757
9.20k
    return eLDO;
1758
9.20k
}
1759
1760
bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter )
1761
107k
{
1762
107k
    using namespace ::com::sun::star::i18n;
1763
107k
    NfEvalDateFormat eEDF;
1764
107k
    int nFormatOrder;
1765
107k
    if ( mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE) )
1766
33.5k
    {
1767
33.5k
        eEDF = mrCurrentLanguageData.GetEvalDateFormat();
1768
33.5k
        switch ( eEDF )
1769
33.5k
        {
1770
0
        case NfEvalDateFormat::International :
1771
0
        case NfEvalDateFormat::Format :
1772
0
            nFormatOrder = 1; // only one loop
1773
0
            break;
1774
33.5k
        default:
1775
33.5k
            nFormatOrder = 2;
1776
33.5k
            if ( nMatchedAllStrings )
1777
14.3k
            {
1778
14.3k
                eEDF = NfEvalDateFormat::FormatThenInternational;
1779
                // we have a complete match, use it
1780
14.3k
            }
1781
33.5k
        }
1782
33.5k
    }
1783
73.6k
    else
1784
73.6k
    {
1785
73.6k
        eEDF = NfEvalDateFormat::International;
1786
73.6k
        nFormatOrder = 1;
1787
73.6k
    }
1788
107k
    bool res = true;
1789
1790
107k
    const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();
1791
107k
    CalendarWrapper* pCal = mrCurrentLanguageData.GetCalendar();
1792
107k
    const DateTime aToday{ DateTime( Date( Date::SYSTEM))};
1793
244k
    for ( int nTryOrder = 1; nTryOrder <= nFormatOrder; nTryOrder++ )
1794
137k
    {
1795
137k
        pCal->setGregorianDateTime( aToday); // today
1796
137k
        OUString aOrgCalendar; // empty => not changed yet
1797
137k
        DateOrder DateFmt;
1798
137k
        bool bFormatTurn;
1799
137k
        switch ( eEDF )
1800
137k
        {
1801
73.6k
        case NfEvalDateFormat::International :
1802
73.6k
            bFormatTurn = false;
1803
73.6k
            DateFmt = GetDateOrder();
1804
73.6k
            break;
1805
0
        case NfEvalDateFormat::Format :
1806
0
            bFormatTurn = true;
1807
0
            DateFmt = mpFormat->GetDateOrder();
1808
0
            break;
1809
35.3k
        case NfEvalDateFormat::InternationalThenFormat :
1810
35.3k
            if ( nTryOrder == 1 )
1811
19.1k
            {
1812
19.1k
                bFormatTurn = false;
1813
19.1k
                DateFmt = GetDateOrder();
1814
19.1k
            }
1815
16.1k
            else
1816
16.1k
            {
1817
16.1k
                bFormatTurn = true;
1818
16.1k
                DateFmt = mpFormat->GetDateOrder();
1819
16.1k
            }
1820
35.3k
            break;
1821
28.7k
        case NfEvalDateFormat::FormatThenInternational :
1822
28.7k
            if ( nTryOrder == 2 )
1823
14.3k
            {
1824
14.3k
                bFormatTurn = false;
1825
14.3k
                DateFmt = GetDateOrder();
1826
14.3k
            }
1827
14.3k
            else
1828
14.3k
            {
1829
14.3k
                bFormatTurn = true;
1830
                // Even if the format pattern is to be preferred, the input may
1831
                // have matched a pattern of the current locale, which then
1832
                // again is to be preferred. Both date orders can be different
1833
                // so we need to obtain the actual match. For example ISO
1834
                // YYYY-MM-DD format vs locale's DD.MM.YY input.
1835
                // If no pattern was matched, obtain from format.
1836
                // Note that patterns may have been constructed from the
1837
                // format's locale and prepended to the current locale's
1838
                // patterns, it doesn't necessarily mean a current locale's
1839
                // pattern was matched, but may if the format's locale's
1840
                // patterns didn't match, which were tried first.
1841
14.3k
                DateFmt = GetDateOrder(true);
1842
14.3k
            }
1843
28.7k
            break;
1844
0
        default:
1845
0
            SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
1846
0
            DateFmt = DateOrder::YMD;
1847
0
            bFormatTurn = false;
1848
137k
        }
1849
137k
        if ( bFormatTurn )
1850
30.5k
        {
1851
/* TODO:
1852
We are currently not able to fully support a switch to another calendar during
1853
input for the following reasons:
1854
1. We do have a problem if both (locale's default and format's) calendars
1855
   define the same YMD order and use the same date separator, there is no way
1856
   to distinguish between them if the input results in valid calendar input for
1857
   both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should
1858
   it always be set to NfEvalDateFormat::FormatThenInternational and thus the format's
1859
   calendar be preferred? This could be confusing if a Calc cell was formatted
1860
   different to the locale's default and has no content yet, then the user has
1861
   no clue about the format or calendar being set.
1862
2. In Calc cell edit mode a date is always displayed and edited using the
1863
   default edit format of the default calendar (normally being Gregorian). If
1864
   input was ambiguous due to issue #1 we'd need a mechanism to tell that a
1865
   date was edited and not newly entered. Not feasible. Otherwise we'd need a
1866
   mechanism to use a specific edit format with a specific calendar according
1867
   to the format set.
1868
3. For some calendars like Japanese Gengou we'd need era input, which isn't
1869
   implemented at all. Though this is a rare and special case, forcing a
1870
   calendar dependent edit format as suggested in item #2 might require era
1871
   input, if it shouldn't result in a fallback to Gregorian calendar.
1872
4. Last and least: the GetMonth() method currently only matches month names of
1873
   the default calendar. Alternating month names of the actual format's
1874
   calendar would have to be implemented. No problem.
1875
1876
*/
1877
#ifdef THE_FUTURE
1878
            if ( mpFormat->IsOtherCalendar( nStringScanNumFor ) )
1879
            {
1880
                mpFormat->SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
1881
            }
1882
            else
1883
            {
1884
                mpFormat->SwitchToSpecifiedCalendar( aOrgCalendar, fOrgDateTime,
1885
                                                    nStringScanNumFor );
1886
            }
1887
#endif
1888
30.5k
        }
1889
1890
137k
        res = true;
1891
137k
        nCounter = 0;
1892
        // For incomplete dates, always assume first day of month if not specified.
1893
137k
        pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1894
1895
137k
        switch (nNumericsCnt) // count of numbers in string
1896
137k
        {
1897
0
        case 0:                 // none
1898
0
            if (nMonthPos)      // only month (Jan)
1899
0
            {
1900
0
                pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1901
0
            }
1902
0
            else
1903
0
            {
1904
0
                res = false;
1905
0
            }
1906
0
            break;
1907
1908
19.2k
        case 1:                 // only one number
1909
19.2k
            nCounter = 1;
1910
19.2k
            switch (nMonthPos)  // where is the month
1911
19.2k
            {
1912
7.14k
            case 0:             // not found
1913
7.14k
            {
1914
                // If input matched a date pattern, use the pattern
1915
                // to determine if it is a day, month or year. The
1916
                // pattern should have only one single value then,
1917
                // 'D-', 'M-' or 'Y-'. If input did not match a
1918
                // pattern assume the usual day of current month.
1919
7.14k
                sal_uInt32 nDateOrder = (bFormatTurn ?
1920
2.95k
                                         mpFormat->GetExactDateOrder() :
1921
7.14k
                                         GetDatePatternOrder());
1922
7.14k
                switch (nDateOrder)
1923
7.14k
                {
1924
1.05k
                case 'Y':
1925
1.05k
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1926
1.05k
                    break;
1927
1.14k
                case 'M':
1928
1.14k
                    pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1929
1.14k
                    break;
1930
14
                case 'D':
1931
4.94k
                default:
1932
4.94k
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1933
4.94k
                    break;
1934
7.14k
                }
1935
7.14k
                break;
1936
7.14k
            }
1937
7.14k
            case 1:             // month at the beginning (Jan 01)
1938
5.56k
                pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1939
5.56k
                switch (DateFmt)
1940
5.56k
                {
1941
5.13k
                case DateOrder::MDY:
1942
5.13k
                case DateOrder::YMD:
1943
5.13k
                    if (sal_uInt16 day = ImplGetDay(0); day > 0 && day <= 32) // Why 32?
1944
1.91k
                    {
1945
1.91k
                        pCal->setValue(CalendarFieldIndex::DAY_OF_MONTH, day);
1946
1.91k
                        break;
1947
1.91k
                    }
1948
                    // Parse 'june-2007' as June 1 2007
1949
3.22k
                    [[fallthrough]];
1950
3.65k
                case DateOrder::DMY:
1951
3.65k
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1952
3.65k
                    break;
1953
0
                default:
1954
0
                    res = false;
1955
0
                    break;
1956
5.56k
                }
1957
5.56k
                break;
1958
6.49k
            case 3:             // month at the end (10 Jan)
1959
6.49k
                pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1960
6.49k
                switch (DateFmt)
1961
6.49k
                {
1962
767
                case DateOrder::DMY:
1963
767
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1964
767
                    break;
1965
0
                case DateOrder::YMD:
1966
0
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1967
0
                    break;
1968
5.73k
                default:
1969
5.73k
                    res = false;
1970
5.73k
                    break;
1971
6.49k
                }
1972
6.49k
                break;
1973
6.49k
            default:
1974
0
                res = false;
1975
0
                break;
1976
19.2k
            }   // switch (nMonthPos)
1977
19.2k
            break;
1978
1979
37.5k
        case 2:                 // 2 numbers
1980
37.5k
            nCounter = 2;
1981
37.5k
            switch (nMonthPos)  // where is the month
1982
37.5k
            {
1983
25.5k
            case 0:             // not found
1984
25.5k
            {
1985
25.5k
                sal_uInt32 nExactDateOrder = (bFormatTurn ?
1986
10.2k
                                              mpFormat->GetExactDateOrder() :
1987
25.5k
                                              GetDatePatternOrder());
1988
25.5k
                bool bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
1989
25.5k
                if (!bIsExact && bFormatTurn && IsAcceptedDatePattern( nNums[0]))
1990
898
                {
1991
                    // If input does not match format but pattern, use pattern
1992
                    // instead, even if eEDF==NfEvalDateFormat::FormatThenInternational.
1993
                    // For example, format has "Y-M-D" and pattern is "D.M.",
1994
                    // input with 2 numbers can't match format and 31.12. would
1995
                    // lead to 1931-12-01 (fdo#54344)
1996
898
                    nExactDateOrder = GetDatePatternOrder();
1997
898
                    bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
1998
898
                }
1999
25.5k
                bool bHadExact;
2000
25.5k
                if (bIsExact)
2001
15.5k
                {
2002
                    // formatted as date and exactly 2 parts
2003
15.5k
                    bHadExact = true;
2004
15.5k
                    switch ( (nExactDateOrder >> 8) & 0xff )
2005
15.5k
                    {
2006
2.76k
                    case 'Y':
2007
2.76k
                        pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2008
2.76k
                        break;
2009
7.40k
                    case 'M':
2010
7.40k
                        pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2011
7.40k
                        break;
2012
5.42k
                    case 'D':
2013
5.42k
                        pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2014
5.42k
                        break;
2015
0
                    default:
2016
0
                        bHadExact = false;
2017
15.5k
                    }
2018
15.5k
                    switch ( nExactDateOrder & 0xff )
2019
15.5k
                    {
2020
5.09k
                    case 'Y':
2021
5.09k
                        pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2022
5.09k
                        break;
2023
1.40k
                    case 'M':
2024
1.40k
                        pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2025
1.40k
                        break;
2026
9.09k
                    case 'D':
2027
9.09k
                        pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2028
9.09k
                        break;
2029
0
                    default:
2030
0
                        bHadExact = false;
2031
15.5k
                    }
2032
15.5k
                    SAL_WARN_IF( !bHadExact, "svl.numbers", "ImpSvNumberInputScan::GetDateRef: error in exact date order");
2033
15.5k
                }
2034
9.99k
                else
2035
9.99k
                {
2036
9.99k
                    bHadExact = false;
2037
9.99k
                }
2038
                // If input matched against a date acceptance pattern
2039
                // do not attempt to mess around with guessing the
2040
                // order, either it matches or it doesn't.
2041
25.5k
                if ((bFormatTurn || !bIsExact) && (!bHadExact || !pCal->isValid()))
2042
19.1k
                {
2043
19.1k
                    if ( !bHadExact && nExactDateOrder )
2044
1.11k
                    {
2045
1.11k
                        pCal->setGregorianDateTime( aToday); // reset today
2046
1.11k
                    }
2047
19.1k
                    switch (DateFmt)
2048
19.1k
                    {
2049
9.77k
                    case DateOrder::MDY:
2050
9.77k
                    {
2051
                        // M D
2052
9.77k
                        const auto month = ImplGetMonth(0);
2053
9.77k
                        pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2054
9.77k
                        pCal->setValue( CalendarFieldIndex::MONTH, month );
2055
9.77k
                        if ( !pCal->isValid() )             // 2nd try
2056
9.76k
                        {                                   // M Y
2057
9.76k
                            pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
2058
9.76k
                            pCal->setValue( CalendarFieldIndex::MONTH, month );
2059
9.76k
                            pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2060
9.76k
                        }
2061
9.77k
                        break;
2062
0
                    }
2063
6.00k
                    case DateOrder::DMY:
2064
                        // D M
2065
6.00k
                        pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2066
6.00k
                        pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2067
6.00k
                        if ( !pCal->isValid() )             // 2nd try
2068
6.00k
                        {                                   // M Y
2069
6.00k
                            pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
2070
6.00k
                            pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2071
6.00k
                            pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2072
6.00k
                        }
2073
6.00k
                        break;
2074
3.33k
                    case DateOrder::YMD:
2075
                        // M D
2076
3.33k
                        pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2077
3.33k
                        pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2078
3.33k
                        if ( !pCal->isValid() )             // 2nd try
2079
3.33k
                        {                                   // Y M
2080
3.33k
                            pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
2081
3.33k
                            pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2082
3.33k
                            pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2083
3.33k
                        }
2084
3.33k
                        break;
2085
0
                    default:
2086
0
                        res = false;
2087
0
                        break;
2088
19.1k
                    }
2089
19.1k
                }
2090
25.5k
            }
2091
25.5k
            break;
2092
25.5k
            case 1:             // month at the beginning (Jan 01 01)
2093
3.07k
            {
2094
                // The input is valid as MDY in almost any
2095
                // constellation, there is no date order (M)YD except if
2096
                // set in a format applied.
2097
3.07k
                pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2098
3.07k
                sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->GetExactDateOrder() : 0);
2099
3.07k
                if ((((nExactDateOrder >> 8) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D'))
2100
0
                {
2101
0
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2102
0
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2103
0
                }
2104
3.07k
                else
2105
3.07k
                {
2106
3.07k
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2107
3.07k
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2108
3.07k
                }
2109
3.07k
                break;
2110
25.5k
            }
2111
7.33k
            case 2:             // month in the middle (10 Jan 94)
2112
7.33k
            {
2113
7.33k
                pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2114
7.33k
                const LongDateOrder eLDO = GetMiddleMonthLongDateOrder( bFormatTurn, pLoc, DateFmt);
2115
7.33k
                switch (eLDO)
2116
7.33k
                {
2117
2.56k
                case LongDateOrder::DMY:
2118
2.56k
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2119
2.56k
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2120
2.56k
                    break;
2121
1.70k
                case LongDateOrder::YMD:
2122
1.70k
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2123
1.70k
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2124
1.70k
                    break;
2125
3.06k
                default:
2126
3.06k
                    res = false;
2127
3.06k
                    break;
2128
7.33k
                }
2129
7.33k
                break;
2130
7.33k
            }
2131
7.33k
            case 3:             // month at the end (94 10 Jan)
2132
1.56k
                if (pLoc->getLongDateOrder() != LongDateOrder::YDM)
2133
1.56k
                    res = false;
2134
0
                else
2135
0
                {
2136
0
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2137
0
                    pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2138
0
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2139
0
                }
2140
1.56k
                break;
2141
0
            default:
2142
0
                res = false;
2143
0
                break;
2144
37.5k
            }   // switch (nMonthPos)
2145
37.5k
            break;
2146
2147
80.9k
        default:                // more than two numbers (31.12.94 8:23) (31.12. 8:23)
2148
80.9k
            switch (nMonthPos)  // where is the month
2149
80.9k
            {
2150
68.1k
            case 0:             // not found
2151
68.1k
            {
2152
68.1k
                nCounter = 3;
2153
68.1k
                if ( nTimePos > 1 )
2154
33.3k
                {   // find first time number index (should only be 3 or 2 anyway)
2155
123k
                    for ( sal_uInt16 j = 0; j < nNumericsCnt; j++ )
2156
123k
                    {
2157
123k
                        if ( nNums[j] == nTimePos - 2 )
2158
33.3k
                        {
2159
33.3k
                            nCounter = j;
2160
33.3k
                            break; // for
2161
33.3k
                        }
2162
123k
                    }
2163
33.3k
                }
2164
                // ISO 8601 yyyy-mm-dd forced recognition
2165
68.1k
                DateOrder eDF = (CanForceToIso8601( DateFmt) ? DateOrder::YMD : DateFmt);
2166
68.1k
                switch (eDF)
2167
68.1k
                {
2168
6.29k
                case DateOrder::MDY:
2169
6.29k
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2170
6.29k
                    pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2171
6.29k
                    if ( nCounter > 2 )
2172
6.24k
                        pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) );
2173
6.29k
                    break;
2174
2.39k
                case DateOrder::DMY:
2175
2.39k
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2176
2.39k
                    pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2177
2.39k
                    if ( nCounter > 2 )
2178
2.39k
                        pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) );
2179
2.39k
                    break;
2180
59.4k
                case DateOrder::YMD:
2181
59.4k
                    if ( nCounter > 2 )
2182
48.8k
                        pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(2) );
2183
59.4k
                    pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2184
59.4k
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2185
59.4k
                    break;
2186
0
                default:
2187
0
                    res = false;
2188
0
                    break;
2189
68.1k
                }
2190
68.1k
                break;
2191
68.1k
            }
2192
68.1k
            case 1:             // month at the beginning (Jan 01 01 8:23)
2193
2.60k
            {
2194
2.60k
                nCounter = 2;
2195
                // The input is valid as MDY in almost any
2196
                // constellation, there is no date order (M)YD except if
2197
                // set in a format applied.
2198
2.60k
                pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2199
2.60k
                sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->GetExactDateOrder() : 0);
2200
2.60k
                if ((((nExactDateOrder >> 8) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D'))
2201
0
                {
2202
0
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2203
0
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2204
0
                }
2205
2.60k
                else
2206
2.60k
                {
2207
2.60k
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2208
2.60k
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2209
2.60k
                }
2210
2.60k
                break;
2211
68.1k
            }
2212
10.1k
            case 2:             // month in the middle (10 Jan 94 8:23)
2213
10.1k
            {
2214
10.1k
                nCounter = 2;
2215
10.1k
                pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2216
10.1k
                const LongDateOrder eLDO = GetMiddleMonthLongDateOrder( bFormatTurn, pLoc, DateFmt);
2217
10.1k
                switch (eLDO)
2218
10.1k
                {
2219
3.41k
                case LongDateOrder::DMY:
2220
3.41k
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2221
3.41k
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2222
3.41k
                    break;
2223
3.40k
                case LongDateOrder::YMD:
2224
3.40k
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2225
3.40k
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2226
3.40k
                    break;
2227
3.29k
                default:
2228
3.29k
                    res = false;
2229
3.29k
                    break;
2230
10.1k
                }
2231
10.1k
                break;
2232
10.1k
            }
2233
10.1k
            case 3:            // month at the end (94 10 Jan 8:23)
2234
88
                nCounter = 2;
2235
88
                if (pLoc->getLongDateOrder() != LongDateOrder::YDM)
2236
88
                    res = false;
2237
0
                else
2238
0
                {
2239
0
                    pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2240
0
                    pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2241
0
                    pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2242
0
                }
2243
88
                break;
2244
0
            default:
2245
0
                nCounter = 2;
2246
0
                res = false;
2247
0
                break;
2248
80.9k
            }   // switch (nMonthPos)
2249
80.9k
            break;
2250
137k
        }   // switch (nNumericsCnt)
2251
2252
137k
        if (mbEraCE != kDefaultEra)
2253
12.6k
            pCal->setValue( CalendarFieldIndex::ERA, mbEraCE ? 1 : 0);
2254
2255
137k
        if ( res && pCal->isValid() )
2256
44.0k
        {
2257
44.0k
            double fDiff = DateTime::Sub( DateTime(maNullDate), pCal->getEpochStart());
2258
44.0k
            fDays = ::rtl::math::approxFloor( pCal->getLocalDateTime() );
2259
44.0k
            fDays -= fDiff;
2260
44.0k
            nTryOrder = nFormatOrder; // break for
2261
44.0k
        }
2262
93.6k
        else
2263
93.6k
        {
2264
93.6k
            res = false;
2265
93.6k
        }
2266
137k
        if ( aOrgCalendar.getLength() )
2267
0
        {
2268
0
            pCal->loadCalendar( aOrgCalendar, pLoc->getLanguageTag().getLocale() ); // restore calendar
2269
0
        }
2270
#if NF_TEST_CALENDAR
2271
        {
2272
            using namespace ::com::sun::star;
2273
            struct entry { const char* lan; const char* cou; const char* cal; };
2274
            const entry cals[] = {
2275
                { "en", "US",  "gregorian" },
2276
                { "ar", "TN",      "hijri" },
2277
                { "he", "IL",     "jewish" },
2278
                { "ja", "JP",     "gengou" },
2279
                { "ko", "KR", "hanja_yoil" },
2280
                { "th", "TH",   "buddhist" },
2281
                { "zh", "TW",        "ROC" },
2282
                {0,0,0}
2283
            };
2284
            lang::Locale aLocale;
2285
            bool bValid;
2286
            sal_Int16 nDay, nMyMonth, nYear, nHour, nMinute, nSecond;
2287
            sal_Int16 nDaySet, nMonthSet, nYearSet, nHourSet, nMinuteSet, nSecondSet;
2288
            sal_Int16 nZO, nDST1, nDST2, nDST, nZOmillis, nDST1millis, nDST2millis, nDSTmillis;
2289
            sal_Int32 nZoneInMillis, nDST1InMillis, nDST2InMillis;
2290
            uno::Reference< uno::XComponentContext > xContext =
2291
                ::comphelper::getProcessComponentContext();
2292
            uno::Reference< i18n::XCalendar4 > xCal = i18n::LocaleCalendar2::create(xContext);
2293
            for ( const entry* p = cals; p->lan; ++p )
2294
            {
2295
                aLocale.Language = OUString::createFromAscii( p->lan );
2296
                aLocale.Country  = OUString::createFromAscii( p->cou );
2297
                xCal->loadCalendar( OUString::createFromAscii( p->cal ),
2298
                                    aLocale );
2299
                double nDateTime = 0.0; // 1-Jan-1970 00:00:00
2300
                nZO           = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET );
2301
                nZOmillis     = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS );
2302
                nZoneInMillis = static_cast<sal_Int32>(nZO) * 60000 +
2303
                    (nZO < 0 ? -1 : 1) * static_cast<sal_uInt16>(nZOmillis);
2304
                nDST1         = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2305
                nDST1millis   = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2306
                nDST1InMillis = static_cast<sal_Int32>(nDST1) * 60000 +
2307
                    (nDST1 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST1millis);
2308
                nDateTime    -= (double)(nZoneInMillis + nDST1InMillis) / 1000.0 / 60.0 / 60.0 / 24.0;
2309
                xCal->setDateTime( nDateTime );
2310
                nDST2         = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2311
                nDST2millis   = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2312
                nDST2InMillis = static_cast<sal_Int32>(nDST2) * 60000 +
2313
                    (nDST2 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST2millis);
2314
                if ( nDST1InMillis != nDST2InMillis )
2315
                {
2316
                    nDateTime = 0.0 - (double)(nZoneInMillis + nDST2InMillis) / 1000.0 / 60.0 / 60.0 / 24.0;
2317
                    xCal->setDateTime( nDateTime );
2318
                }
2319
                nDaySet    = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH );
2320
                nMonthSet  = xCal->getValue( i18n::CalendarFieldIndex::MONTH );
2321
                nYearSet   = xCal->getValue( i18n::CalendarFieldIndex::YEAR );
2322
                nHourSet   = xCal->getValue( i18n::CalendarFieldIndex::HOUR );
2323
                nMinuteSet = xCal->getValue( i18n::CalendarFieldIndex::MINUTE );
2324
                nSecondSet = xCal->getValue( i18n::CalendarFieldIndex::SECOND );
2325
                nZO        = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET );
2326
                nZOmillis  = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS );
2327
                nDST       = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2328
                nDSTmillis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2329
                xCal->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDaySet );
2330
                xCal->setValue( i18n::CalendarFieldIndex::MONTH, nMonthSet );
2331
                xCal->setValue( i18n::CalendarFieldIndex::YEAR, nYearSet );
2332
                xCal->setValue( i18n::CalendarFieldIndex::HOUR, nHourSet );
2333
                xCal->setValue( i18n::CalendarFieldIndex::MINUTE, nMinuteSet );
2334
                xCal->setValue( i18n::CalendarFieldIndex::SECOND, nSecondSet );
2335
                bValid  = xCal->isValid();
2336
                nDay    = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH );
2337
                nMyMonth= xCal->getValue( i18n::CalendarFieldIndex::MONTH );
2338
                nYear   = xCal->getValue( i18n::CalendarFieldIndex::YEAR );
2339
                nHour   = xCal->getValue( i18n::CalendarFieldIndex::HOUR );
2340
                nMinute = xCal->getValue( i18n::CalendarFieldIndex::MINUTE );
2341
                nSecond = xCal->getValue( i18n::CalendarFieldIndex::SECOND );
2342
                bValid = bValid && nDay == nDaySet && nMyMonth == nMonthSet && nYear ==
2343
                    nYearSet && nHour == nHourSet && nMinute == nMinuteSet && nSecond
2344
                    == nSecondSet;
2345
            }
2346
        }
2347
#endif  // NF_TEST_CALENDAR
2348
2349
137k
    }
2350
2351
107k
    return res;
2352
107k
}
2353
2354
2355
/**
2356
 * Analyze first string
2357
 * All gone => true
2358
 * else     => false
2359
 */
2360
bool ImpSvNumberInputScan::ScanStartString( const OUString& rString )
2361
958k
{
2362
958k
    sal_Int32 nPos = 0;
2363
2364
    // First of all, eat leading blanks
2365
958k
    SkipBlanks(rString, nPos);
2366
2367
    // Yes, nMatchedAllStrings should know about the sign position
2368
958k
    nSign = GetSign(rString, nPos);
2369
958k
    if ( nSign )           // sign?
2370
187k
    {
2371
187k
        SkipBlanks(rString, nPos);
2372
187k
    }
2373
    // #102371# match against format string only if start string is not a sign character
2374
958k
    if ( nMatchedAllStrings && !(nSign && rString.getLength() == 1) )
2375
834k
    {
2376
        // Match against format in any case, so later on for a "x1-2-3" input
2377
        // we may distinguish between a xy-m-d (or similar) date and a x0-0-0
2378
        // format. No sign detection here!
2379
834k
        if ( ScanStringNumFor( rString, nPos, 0, true ) )
2380
10.2k
        {
2381
10.2k
            nMatchedAllStrings |= nMatchedStartString;
2382
10.2k
        }
2383
824k
        else
2384
824k
        {
2385
824k
            nMatchedAllStrings = 0;
2386
824k
        }
2387
834k
    }
2388
2389
    // Bail out early for just a sign.
2390
958k
    if (nSign && nPos == rString.getLength())
2391
147k
        return true;
2392
2393
811k
    const sal_Int32 nStartBlanks = nPos;
2394
811k
    if ( GetDecSep(rString, nPos) )                 // decimal separator in start string
2395
64.8k
    {
2396
64.8k
        if (SkipBlanks(rString, nPos))
2397
3.02k
            nPos = nStartBlanks;                    // `. 2` not a decimal separator
2398
61.7k
        else
2399
61.7k
            nDecPos = 1;                            // leading decimal separator
2400
64.8k
    }
2401
746k
    else if ( GetCurrency(rString, nPos) )          // currency (DM 1)?
2402
41.0k
    {
2403
41.0k
        eScannedType = SvNumFormatType::CURRENCY;       // !!! it IS currency !!!
2404
41.0k
        SkipBlanks(rString, nPos);
2405
41.0k
        if (nSign == 0)                             // no sign yet
2406
35.7k
        {
2407
35.7k
            nSign = GetSign(rString, nPos);
2408
35.7k
            if ( nSign )   // DM -1
2409
12.8k
            {
2410
12.8k
                SkipBlanks(rString, nPos);
2411
12.8k
            }
2412
35.7k
        }
2413
41.0k
        if ( GetDecSep(rString, nPos) )             // decimal separator follows currency
2414
6.59k
        {
2415
6.59k
            if (SkipBlanks(rString, nPos))
2416
2.07k
            {
2417
2.07k
                nPos = nStartBlanks;                // `DM . 2` not a decimal separator
2418
2.07k
                eScannedType = SvNumFormatType::UNDEFINED;  // !!! it is NOT currency !!!
2419
2.07k
            }
2420
4.51k
            else
2421
4.51k
                nDecPos = 1;                        // leading decimal separator
2422
6.59k
        }
2423
41.0k
    }
2424
705k
    else
2425
705k
    {
2426
705k
        const sal_Int32 nMonthStart = nPos;
2427
705k
        short nTempMonth = GetMonth(rString, nPos);
2428
705k
        if (nTempMonth < 0)
2429
15.4k
        {
2430
            // Short month and day names may be identical in some locales, e.g.
2431
            // "mar" for "martes" or "marzo" in Spanish.
2432
            // Do not let a month name immediately take precedence if a day
2433
            // name was meant instead. Assume that both could be valid, until
2434
            // encountered differently or the final evaluation in
2435
            // IsNumberFormat() checks, but continue with weighing the month
2436
            // name higher unless we have both day of week and month name here.
2437
15.4k
            sal_Int32 nTempPos = nMonthStart;
2438
15.4k
            nDayOfWeek = GetDayOfWeek( rString, nTempPos);
2439
15.4k
            if (nDayOfWeek < 0)
2440
0
            {
2441
0
                SkipChar( '.', rString, nTempPos ); // abbreviated
2442
0
                SkipString( mrCurrentLanguageData.GetLocaleData()->getLongDateDayOfWeekSep(), rString, nTempPos );
2443
0
                SkipBlanks( rString, nTempPos);
2444
0
                short nTempTempMonth = GetMonth( rString, nTempPos);
2445
0
                if (nTempTempMonth)
2446
0
                {
2447
                    // Fall into the else branch below that handles both.
2448
0
                    nTempMonth = 0;
2449
0
                    nPos = nMonthStart;
2450
0
                    nDayOfWeek = 0;
2451
                    // Do not set nDayOfWeek hereafter, anywhere.
2452
0
                }
2453
0
            }
2454
15.4k
        }
2455
705k
        if ( nTempMonth )    // month (Jan 1)?
2456
18.5k
        {
2457
            // Jan1 without separator is not a date, unless it is followed by a
2458
            // separator and a (year) number.
2459
18.5k
            if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
2460
17.9k
            {
2461
17.9k
                eScannedType = SvNumFormatType::DATE;   // !!! it IS a date !!!
2462
17.9k
                nMonth = nTempMonth;
2463
17.9k
                nMonthPos = 1;                      // month at the beginning
2464
17.9k
                if ( nMonth < 0 )
2465
14.9k
                {
2466
14.9k
                    SkipChar( '.', rString, nPos ); // abbreviated
2467
14.9k
                }
2468
17.9k
                SkipBlanks(rString, nPos);
2469
17.9k
            }
2470
568
            else
2471
568
            {
2472
568
                nPos = nMonthStart;                 // rewind month
2473
568
            }
2474
18.5k
        }
2475
686k
        else
2476
686k
        {
2477
686k
            int nTempDayOfWeek = GetDayOfWeek( rString, nPos );
2478
686k
            if ( nTempDayOfWeek )
2479
5.74k
            {
2480
                // day of week is just parsed away
2481
5.74k
                eScannedType = SvNumFormatType::DATE;       // !!! it IS a date !!!
2482
5.74k
                if ( nPos < rString.getLength() )
2483
5.42k
                {
2484
5.42k
                    if ( nTempDayOfWeek < 0 )
2485
4.95k
                    {
2486
                        // abbreviated
2487
4.95k
                        if ( rString[ nPos ] == '.' )
2488
2.11k
                        {
2489
2.11k
                            ++nPos;
2490
2.11k
                        }
2491
4.95k
                    }
2492
465
                    else
2493
465
                    {
2494
                        // full long name
2495
465
                        SkipBlanks(rString, nPos);
2496
465
                        SkipString( mrCurrentLanguageData.GetLocaleData()->getLongDateDayOfWeekSep(), rString, nPos );
2497
465
                    }
2498
5.42k
                    SkipBlanks(rString, nPos);
2499
5.42k
                    nTempMonth = GetMonth(rString, nPos);
2500
5.42k
                    if ( nTempMonth ) // month (Jan 1)?
2501
2.24k
                    {
2502
                        // Jan1 without separator is not a date, unless it is followed by a
2503
                        // separator and a (year) number.
2504
2.24k
                        if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
2505
1.50k
                        {
2506
1.50k
                            nMonth = nTempMonth;
2507
1.50k
                            nMonthPos = 1; // month at the beginning
2508
1.50k
                            if ( nMonth < 0 )
2509
1.45k
                            {
2510
1.45k
                                SkipChar( '.', rString, nPos ); // abbreviated
2511
1.45k
                            }
2512
1.50k
                            SkipBlanks(rString, nPos);
2513
1.50k
                        }
2514
746
                        else
2515
746
                        {
2516
746
                            nPos = nMonthStart;                 // rewind month
2517
746
                        }
2518
2.24k
                    }
2519
5.42k
                }
2520
5.74k
                if (!nMonth)
2521
4.24k
                {
2522
                    // Determine and remember following date pattern, if any.
2523
4.24k
                    IsAcceptedDatePattern( 1);
2524
4.24k
                }
2525
5.74k
            }
2526
686k
        }
2527
        // Skip one trailing '-' or '/' character to recognize June-2007
2528
705k
        if (nMonth && nPos + 1 == rString.getLength())
2529
7.72k
        {
2530
7.72k
            SkipChar('-', rString, nPos) || SkipChar('/', rString, nPos);
2531
7.72k
        }
2532
705k
    }
2533
2534
811k
    if (nPos < rString.getLength()) // not everything consumed
2535
613k
    {
2536
        // Does input StartString equal StartString of format?
2537
        // This time with sign detection!
2538
613k
        if ( !ScanStringNumFor( rString, nPos, 0 ) )
2539
591k
        {
2540
591k
            return MatchedReturn();
2541
591k
        }
2542
613k
    }
2543
2544
219k
    return true;
2545
811k
}
2546
2547
2548
static bool lcl_isBlanks( const OUString& rStr )
2549
20.3k
{
2550
20.3k
    if (rStr.isEmpty())
2551
0
        return false;
2552
2553
41.0k
    for (sal_Int32 i = rStr.getLength(); i-- > 0; )
2554
22.6k
    {
2555
22.6k
        if (rStr[i] != ' ')
2556
1.96k
            return false;
2557
22.6k
    }
2558
18.4k
    return true;
2559
20.3k
}
2560
2561
2562
/**
2563
 * Analyze string in the middle
2564
 * All gone => true
2565
 * else     => false
2566
 */
2567
bool ImpSvNumberInputScan::ScanMidString( const OUString& rString, sal_uInt16 nStringPos, sal_uInt16 nCurNumCount )
2568
1.37M
{
2569
1.37M
    sal_Int32 nPos = 0;
2570
1.37M
    SvNumFormatType eOldScannedType = eScannedType;
2571
2572
1.37M
    if ( nMatchedAllStrings )
2573
688k
    {   // Match against format in any case, so later on for a "1-2-3-4" input
2574
        // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2575
        // format.
2576
688k
        if ( ScanStringNumFor( rString, 0, nStringPos ) )
2577
25.8k
        {
2578
25.8k
            nMatchedAllStrings |= nMatchedMidString;
2579
25.8k
        }
2580
663k
        else
2581
663k
        {
2582
663k
            nMatchedAllStrings = 0;
2583
663k
        }
2584
688k
    }
2585
2586
1.37M
    const sal_Int32 nStartBlanks = nPos;
2587
1.37M
    const bool bBlanks = SkipBlanks(rString, nPos);
2588
1.37M
    if (GetDecSep(rString, nPos))                   // decimal separator?
2589
170k
    {
2590
170k
        if (nDecPos == 1 || nDecPos == 3)           // .12.4 or 1.E2.1
2591
2.38k
        {
2592
2.38k
            return MatchedReturn();
2593
2.38k
        }
2594
168k
        else if (nDecPos == 2)                      // . dup: 12.4.
2595
7.90k
        {
2596
7.90k
            bool bSignedYear = false;
2597
7.90k
            if (bDecSepInDateSeps ||                // . also date separator
2598
7.90k
                SkipDatePatternSeparator( nStringPos, nPos, bSignedYear))
2599
0
            {
2600
0
                if ( eScannedType != SvNumFormatType::UNDEFINED &&
2601
0
                     eScannedType != SvNumFormatType::DATE &&
2602
0
                     eScannedType != SvNumFormatType::DATETIME)  // already another type
2603
0
                {
2604
0
                    return MatchedReturn();
2605
0
                }
2606
0
                if (eScannedType == SvNumFormatType::UNDEFINED)
2607
0
                {
2608
0
                    eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2609
0
                }
2610
0
                SkipBlanks(rString, nPos);
2611
0
            }
2612
7.90k
            else
2613
7.90k
            {
2614
7.90k
                return MatchedReturn();
2615
7.90k
            }
2616
7.90k
        }
2617
160k
        else if (bBlanks)
2618
3.21k
        {
2619
            // `1 .2` or `1 . 2` not a decimal separator, reset
2620
3.21k
            nPos = nStartBlanks;
2621
3.21k
        }
2622
157k
        else if (SkipBlanks(rString, nPos))
2623
5.12k
        {
2624
            // `1. 2` not a decimal separator, reset
2625
5.12k
            nPos = nStartBlanks;
2626
5.12k
        }
2627
151k
        else
2628
151k
        {
2629
151k
            nDecPos = 2;                            // . in mid string
2630
151k
        }
2631
170k
    }
2632
1.20M
    else if ( (eScannedType & SvNumFormatType::TIME) &&
2633
91.4k
              GetTime100SecSep( rString, nPos ) )
2634
9.60k
    {                                               // hundredth seconds separator
2635
9.60k
        if ( nDecPos )
2636
962
        {
2637
962
            return MatchedReturn();
2638
962
        }
2639
8.64k
        nDecPos = 2;                                // . in mid string
2640
2641
        // If this is exactly an ISO 8601 fractional seconds separator, bail
2642
        // out early to not get confused by later checks for group separator or
2643
        // other.
2644
8.64k
        if (bIso8601Tsep && nPos == rString.getLength() &&
2645
6.63k
                eScannedType == SvNumFormatType::DATETIME && (rString == "." || rString == ","))
2646
5.80k
            return true;
2647
2648
2.84k
        SkipBlanks(rString, nPos);
2649
2.84k
    }
2650
2651
1.35M
    if (SkipChar('/', rString, nPos))               // fraction?
2652
176k
    {
2653
176k
        if ( eScannedType != SvNumFormatType::UNDEFINED &&  // already another type
2654
49.9k
             eScannedType != SvNumFormatType::DATE)         // except date
2655
1.91k
        {
2656
1.91k
            return MatchedReturn();                         // => jan/31/1994
2657
1.91k
        }
2658
174k
        else if (eScannedType != SvNumFormatType::DATE &&   // analyzed no date until now
2659
126k
                 (eSetType == SvNumFormatType::FRACTION ||  // and preset was fraction
2660
125k
                  (nNumericsCnt == 3 &&                     // or 3 numbers
2661
51.5k
                   (nStringPos == 3 ||                      // and 4th string particle
2662
40.9k
                    (nStringPos == 4 && nSign)) &&          // or 5th if signed
2663
20.3k
                   lcl_isBlanks(sStrArray[nStringPos-2])))) // and not 23/11/1999
2664
                                                            // that was not accepted as date yet,
2665
                                                            // nor anything else than blanks after integer.
2666
19.5k
        {
2667
19.5k
            SkipBlanks(rString, nPos);
2668
19.5k
            if (nPos == rString.getLength())
2669
17.5k
            {
2670
17.5k
                eScannedType = SvNumFormatType::FRACTION;   // !!! it IS a fraction (so far)
2671
17.5k
                if (eSetType == SvNumFormatType::FRACTION &&
2672
1.08k
                    nNumericsCnt == 2 &&
2673
521
                    (nStringPos == 1 ||                     // for 4/5
2674
268
                     (nStringPos == 2 && nSign)))           // or signed -4/5
2675
446
                {
2676
446
                    return true;                            // don't fall into date trap
2677
446
                }
2678
17.5k
            }
2679
19.5k
        }
2680
155k
        else
2681
155k
        {
2682
155k
            nPos--;                                 // put '/' back
2683
155k
        }
2684
176k
    }
2685
2686
1.35M
    if (GetThousandSep(rString, nPos, nStringPos))  // 1,000
2687
41.8k
    {
2688
41.8k
        if ( eScannedType != SvNumFormatType::UNDEFINED &&   // already another type
2689
2.61k
             eScannedType != SvNumFormatType::CURRENCY)      // except currency
2690
1.58k
        {
2691
1.58k
            return MatchedReturn();
2692
1.58k
        }
2693
40.3k
        nThousand++;
2694
40.3k
    }
2695
2696
1.35M
    const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();
2697
1.35M
    bool bSignedYear = false;
2698
1.35M
    bool bDate = SkipDatePatternSeparator( nStringPos, nPos, bSignedYear);   // 12/31  31.12.  12/31/1999  31.12.1999
2699
1.35M
    if (!bDate)
2700
1.32M
    {
2701
1.32M
        const OUString& rDate = mrCurrentLanguageData.GetDateSep();
2702
1.32M
        SkipBlanks(rString, nPos);
2703
1.32M
        bDate = SkipString( rDate, rString, nPos);      // 10.  10-  10/
2704
1.32M
    }
2705
1.35M
    if (!bDate && nStringPos == 1 && mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE))
2706
24.8k
    {
2707
        // If a DMY format was given and a mid string starts with a literal
2708
        // ". " dot+space and could contain a following month name and ends
2709
        // with a space or LongDateMonthSeparator, like it's scanned in
2710
        // `14". AUG "18`, then it may be a date as well. Regardless whether
2711
        // defined such by the locale or not.
2712
        // This *could* check for presence of ". "MMM or ". "MMMM in the actual
2713
        // format code for further restriction to match only if present, but..
2714
2715
24.8k
        const sal_uInt32 nExactDateOrder = mpFormat->GetExactDateOrder();
2716
        // Exactly DMY.
2717
24.8k
        if (((nExactDateOrder & 0xff) == 'Y') && (((nExactDateOrder >> 8) & 0xff) == 'M')
2718
3.48k
                && (((nExactDateOrder >> 16) & 0xff) == 'D'))
2719
1.81k
        {
2720
1.81k
            const sal_Int32 nTmpPos = nPos;
2721
1.81k
            if (SkipChar('.', rString, nPos) && SkipBlanks(rString, nPos) && nPos + 2 < rString.getLength()
2722
770
                    && (rString.endsWith(" ") || rString.endsWith( pLoc->getLongDateMonthSep())))
2723
101
                bDate = true;
2724
1.71k
            else
2725
1.71k
                nPos = nTmpPos;
2726
1.81k
        }
2727
24.8k
    }
2728
1.35M
    if (bDate || ((MayBeIso8601() || MayBeMonthDate()) &&    // 1999-12-31  31-Dec-1999
2729
389k
                  SkipChar( '-', rString, nPos)))
2730
407k
    {
2731
407k
        if ( eScannedType != SvNumFormatType::UNDEFINED &&  // already another type
2732
174k
             eScannedType != SvNumFormatType::DATE)       // except date
2733
1.90k
        {
2734
1.90k
            return MatchedReturn();
2735
1.90k
        }
2736
405k
        SkipBlanks(rString, nPos);
2737
405k
        eScannedType = SvNumFormatType::DATE;           // !!! it IS a date
2738
405k
        short nTmpMonth = GetMonth(rString, nPos);  // 10. Jan 94
2739
405k
        if (nMonth && nTmpMonth)                    // month dup
2740
247
        {
2741
247
            return MatchedReturn();
2742
247
        }
2743
405k
        if (nTmpMonth)
2744
15.3k
        {
2745
15.3k
            nMonth = nTmpMonth;
2746
15.3k
            nMonthPos = 2;                          // month in the middle
2747
15.3k
            if ( nMonth < 0 && SkipChar( '.', rString, nPos ) )
2748
913
                ;   // short month may be abbreviated Jan.
2749
14.4k
            else if ( SkipChar( '-', rString, nPos ) )
2750
11.3k
                ;   // #79632# recognize 17-Jan-2001 to be a date
2751
                    // #99065# short and long month name
2752
3.16k
            else
2753
3.16k
            {
2754
3.16k
                SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
2755
3.16k
            }
2756
15.3k
            SkipBlanks(rString, nPos);
2757
15.3k
        }
2758
405k
        if (bSignedYear)
2759
1.10k
        {
2760
1.10k
            if (mbEraCE != kDefaultEra)               // signed year twice?
2761
0
                return MatchedReturn();
2762
2763
1.10k
            mbEraCE = false;  // BCE
2764
1.10k
        }
2765
405k
    }
2766
2767
1.35M
    const sal_Int32 nMonthStart = nPos;
2768
1.35M
    short nTempMonth = GetMonth(rString, nPos);     // month in the middle (10 Jan 94) or at the end (94 10 Jan)
2769
1.35M
    if (nTempMonth)
2770
18.4k
    {
2771
18.4k
        if (nMonth != 0)                            // month dup
2772
650
        {
2773
650
            return MatchedReturn();
2774
650
        }
2775
17.8k
        if ( eScannedType != SvNumFormatType::UNDEFINED &&  // already another type
2776
1.32k
             eScannedType != SvNumFormatType::DATE)         // except date
2777
146
        {
2778
146
            return MatchedReturn();
2779
146
        }
2780
17.6k
        if (nMonthStart > 0 && nPos < rString.getLength())  // 10Jan or Jan94 without separator are not dates
2781
10.2k
        {
2782
10.2k
            eScannedType = SvNumFormatType::DATE;       // !!! it IS a date
2783
10.2k
            nMonth = nTempMonth;
2784
10.2k
            if (nCurNumCount <= 1)
2785
7.74k
                nMonthPos = 2;                      // month in the middle
2786
2.55k
            else
2787
2.55k
                nMonthPos = 3;                      // month at the end
2788
10.2k
            if ( nMonth < 0 )
2789
10.1k
            {
2790
10.1k
                SkipChar( '.', rString, nPos );     // abbreviated
2791
10.1k
            }
2792
10.2k
            SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
2793
10.2k
            SkipBlanks(rString, nPos);
2794
10.2k
        }
2795
7.38k
        else
2796
7.38k
        {
2797
7.38k
            nPos = nMonthStart;                     // rewind month
2798
7.38k
        }
2799
17.6k
    }
2800
2801
1.35M
    if ( SkipChar('E', rString, nPos) ||            // 10E, 10e, 10,Ee
2802
1.24M
         SkipChar('e', rString, nPos) )
2803
109k
    {
2804
109k
        if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
2805
2.40k
        {
2806
2.40k
            return MatchedReturn();
2807
2.40k
        }
2808
106k
        else
2809
106k
        {
2810
106k
            SkipBlanks(rString, nPos);
2811
106k
            eScannedType = SvNumFormatType::SCIENTIFIC; // !!! it IS scientific
2812
106k
            if ( nThousand+2 == nNumericsCnt && nDecPos == 2 ) // special case 1.E2
2813
3.93k
            {
2814
3.93k
                nDecPos = 3;                        // 1,100.E2 1,100,100.E3
2815
3.93k
            }
2816
106k
        }
2817
106k
        nESign = GetESign(rString, nPos);           // signed exponent?
2818
106k
        SkipBlanks(rString, nPos);
2819
106k
    }
2820
2821
1.34M
    const OUString& rTime = pLoc->getTimeSep();
2822
1.34M
    if ( SkipString(rTime, rString, nPos) )         // time separator?
2823
198k
    {
2824
198k
        if (nDecPos)                                // already . => maybe error
2825
12.0k
        {
2826
12.0k
            if (bDecSepInDateSeps)                  // . also date sep
2827
0
            {
2828
0
                if ( eScannedType != SvNumFormatType::DATE &&    // already another type than date
2829
0
                     eScannedType != SvNumFormatType::DATETIME)  // or date time
2830
0
                {
2831
0
                    return MatchedReturn();
2832
0
                }
2833
0
                if (eScannedType == SvNumFormatType::DATE)
2834
0
                {
2835
0
                    nDecPos = 0;                    // reset for time transition
2836
0
                }
2837
0
            }
2838
12.0k
            else
2839
12.0k
            {
2840
12.0k
                return MatchedReturn();
2841
12.0k
            }
2842
12.0k
        }
2843
186k
        if ((eScannedType == SvNumFormatType::DATE ||        // already date type
2844
122k
             eScannedType == SvNumFormatType::DATETIME) &&   // or date time
2845
91.6k
            nNumericsCnt > 3)                                // and more than 3 numbers? (31.Dez.94 8:23)
2846
87.1k
        {
2847
87.1k
            SkipBlanks(rString, nPos);
2848
87.1k
            eScannedType = SvNumFormatType::DATETIME;   // !!! it IS date with time
2849
87.1k
        }
2850
99.2k
        else if ( eScannedType != SvNumFormatType::UNDEFINED &&  // already another type
2851
26.8k
                  eScannedType != SvNumFormatType::TIME)         // except time
2852
4.70k
        {
2853
4.70k
            return MatchedReturn();
2854
4.70k
        }
2855
94.5k
        else
2856
94.5k
        {
2857
94.5k
            SkipBlanks(rString, nPos);
2858
94.5k
            eScannedType = SvNumFormatType::TIME;       // !!! it IS a time
2859
94.5k
        }
2860
181k
        if ( !nTimePos )
2861
132k
        {
2862
132k
            nTimePos = nStringPos + 1;
2863
132k
        }
2864
181k
    }
2865
2866
1.33M
    if (nPos < rString.getLength())
2867
347k
    {
2868
347k
        switch (eScannedType)
2869
347k
        {
2870
84.6k
        case SvNumFormatType::DATE:
2871
84.6k
            if (nMonthPos == 1 && pLoc->getLongDateOrder() == LongDateOrder::MDY)
2872
4.16k
            {
2873
                // #68232# recognize long date separators like ", " in "September 5, 1999"
2874
4.16k
                if (SkipString( pLoc->getLongDateDaySep(), rString, nPos ))
2875
887
                {
2876
887
                    SkipBlanks( rString, nPos );
2877
887
                }
2878
4.16k
            }
2879
80.4k
            else if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
2880
37.2k
            {
2881
37.2k
                if (    (nStringPos == 5 && rString[0] == 'T') ||
2882
18.2k
                        (nStringPos == 6 && rString[0] == 'T' && sStrArray[0] == "-"))
2883
24.9k
                {
2884
                    // ISO 8601 combined date and time, yyyy-mm-ddThh:mm or -yyyy-mm-ddThh:mm
2885
24.9k
                    ++nPos;
2886
24.9k
                    bIso8601Tsep = true;
2887
24.9k
                }
2888
12.3k
                else if (nStringPos == 7 && rString[0] == ':')
2889
0
                {
2890
                    // ISO 8601 combined date and time, the time part; we reach
2891
                    // here if the locale's separator is not ':' so it couldn't
2892
                    // be detected above in the time block.
2893
0
                    if (nNumericsCnt >= 5)
2894
0
                        eScannedType = SvNumFormatType::DATETIME;
2895
0
                    ++nPos;
2896
0
                }
2897
37.2k
            }
2898
84.6k
            break;
2899
13.8k
        case SvNumFormatType::DATETIME:
2900
13.8k
            if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
2901
4.35k
            {
2902
4.35k
                if (nStringPos == 9 && rString[0] == ':')
2903
0
                {
2904
                    // ISO 8601 combined date and time, the time part continued.
2905
0
                    ++nPos;
2906
0
                }
2907
4.35k
            }
2908
#if NF_RECOGNIZE_ISO8601_TIMEZONES
2909
            else if (nPos == 0 && rString.getLength() == 1 && nStringPos >= 9 && MayBeIso8601())
2910
            {
2911
                // ISO 8601 timezone offset
2912
                switch (rString[ 0 ])
2913
                {
2914
                case '+':
2915
                case '-':
2916
                    if (nStringPos == nStringsCnt - 2 ||
2917
                        nStringPos == nStringsCnt - 4)
2918
                    {
2919
                        ++nPos;     // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy]
2920
                        // nTimezonePos needed for GetTimeRef()
2921
                        if (!nTimezonePos)
2922
                        {
2923
                            nTimezonePos = nStringPos + 1;
2924
                        }
2925
                    }
2926
                    break;
2927
                case ':':
2928
                    if (nTimezonePos && nStringPos >= 11 &&
2929
                        nStringPos == nStringsCnt - 2)
2930
                    {
2931
                        ++nPos;     // yyyy-mm-ddThh:mm[:ss]+xx:yy
2932
                    }
2933
                    break;
2934
                }
2935
            }
2936
#endif
2937
13.8k
            break;
2938
249k
        default: break;
2939
347k
        }
2940
347k
    }
2941
2942
1.33M
    if (nPos < rString.getLength()) // not everything consumed?
2943
322k
    {
2944
322k
        if ( nMatchedAllStrings & ~nMatchedVirgin )
2945
13.4k
        {
2946
13.4k
            eScannedType = eOldScannedType;
2947
13.4k
        }
2948
308k
        else
2949
308k
        {
2950
308k
            return false;
2951
308k
        }
2952
322k
    }
2953
2954
1.02M
    return true;
2955
1.33M
}
2956
2957
2958
/**
2959
 * Analyze the end
2960
 * All gone => true
2961
 * else     => false
2962
 */
2963
bool ImpSvNumberInputScan::ScanEndString( const OUString& rString )
2964
673k
{
2965
673k
    sal_Int32 nPos = 0;
2966
2967
673k
    if ( nMatchedAllStrings )
2968
477k
    {   // Match against format in any case, so later on for a "1-2-3-4" input
2969
        // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2970
        // format.
2971
477k
        if ( ScanStringNumFor( rString, 0, 0xFFFF ) )
2972
5.18k
        {
2973
5.18k
            nMatchedAllStrings |= nMatchedEndString;
2974
5.18k
        }
2975
472k
        else
2976
472k
        {
2977
472k
            nMatchedAllStrings = 0;
2978
472k
        }
2979
477k
    }
2980
2981
673k
    const sal_Int32 nStartBlanks = nPos;
2982
673k
    const bool bBlanks = SkipBlanks(rString, nPos);
2983
673k
    if (GetDecSep(rString, nPos))                   // decimal separator?
2984
42.1k
    {
2985
42.1k
        if (nDecPos == 1 || nDecPos == 3)           // .12.4 or 12.E4.
2986
4.01k
        {
2987
4.01k
            return MatchedReturn();
2988
4.01k
        }
2989
38.1k
        else if (nDecPos == 2)                      // . dup: 12.4.
2990
3.60k
        {
2991
3.60k
            bool bSignedYear = false;
2992
3.60k
            if (bDecSepInDateSeps ||                // . also date separator
2993
3.60k
                SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear))
2994
0
            {
2995
0
                if ( eScannedType != SvNumFormatType::UNDEFINED &&
2996
0
                     eScannedType != SvNumFormatType::DATE &&
2997
0
                     eScannedType != SvNumFormatType::DATETIME)  // already another type
2998
0
                {
2999
0
                    return MatchedReturn();
3000
0
                }
3001
0
                if (eScannedType == SvNumFormatType::UNDEFINED)
3002
0
                {
3003
0
                    eScannedType = SvNumFormatType::DATE;   // !!! it IS a date
3004
0
                }
3005
0
                SkipBlanks(rString, nPos);
3006
0
            }
3007
3.60k
            else
3008
3.60k
            {
3009
3.60k
                return MatchedReturn();
3010
3.60k
            }
3011
3.60k
        }
3012
34.5k
        else if (bBlanks)
3013
1.50k
        {
3014
            // not a decimal separator, reset
3015
1.50k
            nPos = nStartBlanks;
3016
1.50k
        }
3017
33.0k
        else
3018
33.0k
        {
3019
33.0k
            nDecPos = 3;                            // . in end string
3020
33.0k
            SkipBlanks(rString, nPos);
3021
33.0k
        }
3022
42.1k
    }
3023
3024
666k
    bool bSignDetectedHere = false;
3025
666k
    if ( nSign == 0  &&                             // conflict - not signed
3026
597k
         eScannedType != SvNumFormatType::DATE)         // and not date
3027
                                                    //!? catch time too?
3028
543k
    {                                               // not signed yet
3029
543k
        nSign = GetSign(rString, nPos);             // 1- DM
3030
543k
        if (bNegCheck)                              // '(' as sign
3031
3.05k
        {
3032
3.05k
            return MatchedReturn();
3033
3.05k
        }
3034
540k
        if (nSign)
3035
17.4k
        {
3036
17.4k
            bSignDetectedHere = true;
3037
17.4k
        }
3038
540k
    }
3039
3040
663k
    SkipBlanks(rString, nPos);
3041
663k
    if (bNegCheck && SkipChar(')', rString, nPos))  // skip ')' if appropriate
3042
7.04k
    {
3043
7.04k
        bNegCheck = false;
3044
7.04k
        SkipBlanks(rString, nPos);
3045
7.04k
    }
3046
3047
663k
    if ( GetCurrency(rString, nPos) )               // currency symbol?
3048
58.0k
    {
3049
58.0k
        if (eScannedType != SvNumFormatType::UNDEFINED) // currency dup
3050
3.14k
        {
3051
3.14k
            return MatchedReturn();
3052
3.14k
        }
3053
54.8k
        else
3054
54.8k
        {
3055
54.8k
            SkipBlanks(rString, nPos);
3056
54.8k
            eScannedType = SvNumFormatType::CURRENCY;
3057
54.8k
        }                                           // behind currency a '-' is allowed
3058
54.8k
        if (nSign == 0)                             // not signed yet
3059
37.7k
        {
3060
37.7k
            nSign = GetSign(rString, nPos);         // DM -
3061
37.7k
            SkipBlanks(rString, nPos);
3062
37.7k
            if (bNegCheck)                          // 3 DM (
3063
2.57k
            {
3064
2.57k
                return MatchedReturn();
3065
2.57k
            }
3066
37.7k
        }
3067
52.2k
        if ( bNegCheck && eScannedType == SvNumFormatType::CURRENCY &&
3068
12.8k
             SkipChar(')', rString, nPos) )
3069
8.56k
        {
3070
8.56k
            bNegCheck = false;                          // ')' skipped
3071
8.56k
            SkipBlanks(rString, nPos);              // only if currency
3072
8.56k
        }
3073
52.2k
    }
3074
3075
657k
    if ( SkipChar('%', rString, nPos) )             // 1%
3076
30.8k
    {
3077
30.8k
        if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
3078
2.62k
        {
3079
2.62k
            return MatchedReturn();
3080
2.62k
        }
3081
28.2k
        SkipBlanks(rString, nPos);
3082
28.2k
        eScannedType = SvNumFormatType::PERCENT;
3083
28.2k
    }
3084
3085
654k
    const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();
3086
654k
    const OUString& rTime = pLoc->getTimeSep();
3087
654k
    if ( SkipString(rTime, rString, nPos) )         // 10:
3088
195k
    {
3089
195k
        if (nDecPos)                                // already , => error
3090
3.64k
        {
3091
3.64k
            return MatchedReturn();
3092
3.64k
        }
3093
191k
        if (eScannedType == SvNumFormatType::DATE && nNumericsCnt > 2) // 31.Dez.94 8:
3094
22.2k
        {
3095
22.2k
            SkipBlanks(rString, nPos);
3096
22.2k
            eScannedType = SvNumFormatType::DATETIME;
3097
22.2k
        }
3098
169k
        else if (eScannedType != SvNumFormatType::UNDEFINED &&
3099
9.63k
                 eScannedType != SvNumFormatType::TIME) // already another type
3100
3.43k
        {
3101
3.43k
            return MatchedReturn();
3102
3.43k
        }
3103
166k
        else
3104
166k
        {
3105
166k
            SkipBlanks(rString, nPos);
3106
166k
            eScannedType = SvNumFormatType::TIME;
3107
166k
        }
3108
188k
        if ( !nTimePos )
3109
182k
        {
3110
182k
            nTimePos = nStringsCnt;
3111
182k
        }
3112
188k
    }
3113
3114
647k
    bool bSignedYear = false;
3115
647k
    bool bDate = SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear);   // 12/31  31.12.  12/31/1999  31.12.1999
3116
647k
    if (!bDate)
3117
646k
    {
3118
646k
        const OUString& rDate = mrCurrentLanguageData.GetDateSep();
3119
646k
        bDate = SkipString( rDate, rString, nPos);      // 10.  10-  10/
3120
646k
    }
3121
647k
    if (bDate && bSignDetectedHere)
3122
3.72k
    {
3123
3.72k
        nSign = 0;                                  // 'D-' takes precedence over signed date
3124
3.72k
    }
3125
647k
    if (bDate || ((MayBeIso8601() || MayBeMonthDate())
3126
36.4k
                  && SkipChar( '-', rString, nPos)))
3127
46.9k
    {
3128
46.9k
        if (eScannedType != SvNumFormatType::UNDEFINED &&
3129
16.0k
            eScannedType != SvNumFormatType::DATE)          // already another type
3130
4.31k
        {
3131
4.31k
            return MatchedReturn();
3132
4.31k
        }
3133
42.6k
        else
3134
42.6k
        {
3135
42.6k
            SkipBlanks(rString, nPos);
3136
42.6k
            eScannedType = SvNumFormatType::DATE;
3137
42.6k
        }
3138
42.6k
        short nTmpMonth = GetMonth(rString, nPos);  // 10. Jan
3139
42.6k
        if (nMonth && nTmpMonth)                    // month dup
3140
291
        {
3141
291
            return MatchedReturn();
3142
291
        }
3143
42.3k
        if (nTmpMonth)
3144
3.67k
        {
3145
3.67k
            nMonth = nTmpMonth;
3146
3.67k
            nMonthPos = 3;                          // month at end
3147
3.67k
            if ( nMonth < 0 )
3148
2.49k
            {
3149
2.49k
                SkipChar( '.', rString, nPos );     // abbreviated
3150
2.49k
            }
3151
3.67k
            SkipBlanks(rString, nPos);
3152
3.67k
        }
3153
42.3k
    }
3154
3155
643k
    const sal_Int32 nMonthStart = nPos;
3156
643k
    short nTempMonth = GetMonth(rString, nPos);     // 10 Jan
3157
643k
    if (nTempMonth)
3158
9.38k
    {
3159
9.38k
        if (nMonth)                                 // month dup
3160
335
        {
3161
335
            return MatchedReturn();
3162
335
        }
3163
9.05k
        if (eScannedType != SvNumFormatType::UNDEFINED &&
3164
2.73k
            eScannedType != SvNumFormatType::DATE)      // already another type
3165
611
        {
3166
611
            return MatchedReturn();
3167
611
        }
3168
8.44k
        if (nMonthStart > 0)                        // 10Jan without separator is not a date
3169
7.37k
        {
3170
7.37k
            eScannedType = SvNumFormatType::DATE;
3171
7.37k
            nMonth = nTempMonth;
3172
7.37k
            nMonthPos = 3;                          // month at end
3173
7.37k
            if ( nMonth < 0 )
3174
5.88k
            {
3175
5.88k
                SkipChar( '.', rString, nPos );     // abbreviated
3176
5.88k
            }
3177
7.37k
            SkipBlanks(rString, nPos);
3178
7.37k
        }
3179
1.07k
        else
3180
1.07k
        {
3181
1.07k
            nPos = nMonthStart;                     // rewind month
3182
1.07k
        }
3183
8.44k
    }
3184
3185
642k
    sal_Int32 nOrigPos = nPos;
3186
642k
    if (GetTimeAmPm(rString, nPos))
3187
25.7k
    {
3188
25.7k
        if (eScannedType != SvNumFormatType::UNDEFINED &&
3189
8.34k
            eScannedType != SvNumFormatType::TIME &&
3190
4.19k
            eScannedType != SvNumFormatType::DATETIME)  // already another type
3191
3.10k
        {
3192
3.10k
            return MatchedReturn();
3193
3.10k
        }
3194
22.6k
        else
3195
22.6k
        {
3196
            // If not already scanned as time, 6.78am does not result in 6
3197
            // seconds and 78 hundredths in the morning. Keep as suffix.
3198
22.6k
            if (eScannedType != SvNumFormatType::TIME && nDecPos == 2 && nNumericsCnt == 2)
3199
2.04k
            {
3200
2.04k
                nPos = nOrigPos; // rewind am/pm
3201
2.04k
            }
3202
20.6k
            else
3203
20.6k
            {
3204
20.6k
                SkipBlanks(rString, nPos);
3205
20.6k
                if ( eScannedType != SvNumFormatType::DATETIME )
3206
19.5k
                {
3207
19.5k
                    eScannedType = SvNumFormatType::TIME;
3208
19.5k
                }
3209
20.6k
            }
3210
22.6k
        }
3211
25.7k
    }
3212
3213
639k
    if ( bNegCheck && SkipChar(')', rString, nPos) )
3214
3.35k
    {
3215
3.35k
        if (eScannedType == SvNumFormatType::CURRENCY)  // only if currency
3216
0
        {
3217
0
            bNegCheck = false;                          // skip ')'
3218
0
            SkipBlanks(rString, nPos);
3219
0
        }
3220
3.35k
        else
3221
3.35k
        {
3222
3.35k
            return MatchedReturn();
3223
3.35k
        }
3224
3.35k
    }
3225
3226
635k
    if ( nPos < rString.getLength() &&
3227
314k
         (eScannedType == SvNumFormatType::DATE ||
3228
270k
          eScannedType == SvNumFormatType::DATETIME) )
3229
50.5k
    {
3230
        // day of week is just parsed away
3231
50.5k
        sal_Int32 nOldPos = nPos;
3232
50.5k
        const OUString& rSep = mrCurrentLanguageData.GetLocaleData()->getLongDateDayOfWeekSep();
3233
50.5k
        if ( StringContains( rSep, rString, nPos ) )
3234
4.59k
        {
3235
4.59k
            nPos = nPos + rSep.getLength();
3236
4.59k
            SkipBlanks(rString, nPos);
3237
4.59k
        }
3238
50.5k
        int nTempDayOfWeek = GetDayOfWeek( rString, nPos );
3239
50.5k
        if ( nTempDayOfWeek )
3240
1.34k
        {
3241
1.34k
            if ( nPos < rString.getLength() )
3242
1.18k
            {
3243
1.18k
                if ( nTempDayOfWeek < 0 )
3244
1.16k
                {   // short
3245
1.16k
                    if ( rString[ nPos ] == '.' )
3246
981
                    {
3247
981
                        ++nPos;
3248
981
                    }
3249
1.16k
                }
3250
1.18k
                SkipBlanks(rString, nPos);
3251
1.18k
            }
3252
1.34k
        }
3253
49.2k
        else
3254
49.2k
        {
3255
49.2k
            nPos = nOldPos;
3256
49.2k
        }
3257
50.5k
    }
3258
3259
#if NF_RECOGNIZE_ISO8601_TIMEZONES
3260
    if (nPos == 0 && eScannedType == SvNumFormatType::DATETIME &&
3261
        rString.getLength() == 1 && rString[ 0 ] == 'Z' && MayBeIso8601())
3262
    {
3263
        // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ
3264
        ++nPos;
3265
    }
3266
#endif
3267
3268
635k
    if (nPos < rString.getLength()) // everything consumed?
3269
314k
    {
3270
        // does input EndString equal EndString in Format?
3271
314k
        if ( !ScanStringNumFor( rString, nPos, 0xFFFF ) )
3272
308k
        {
3273
308k
            return false;
3274
308k
        }
3275
314k
    }
3276
3277
327k
    return true;
3278
635k
}
3279
3280
3281
bool ImpSvNumberInputScan::ScanStringNumFor( const OUString& rString,       // String to scan
3282
                                             sal_Int32 nPos,                // Position until which was consumed
3283
                                             sal_uInt16 nString,            // Substring of format, 0xFFFF => last
3284
                                             bool bDontDetectNegation)      // Suppress sign detection
3285
2.92M
{
3286
2.92M
    if ( !mpFormat )
3287
2.66M
    {
3288
2.66M
        return false;
3289
2.66M
    }
3290
267k
    const ::utl::TransliterationWrapper* pTransliteration = mrCurrentLanguageData.GetTransliteration();
3291
267k
    const OUString* pStr;
3292
267k
    OUString aString( rString );
3293
267k
    bool bFound = false;
3294
267k
    bool bFirst = true;
3295
267k
    bool bContinue = true;
3296
267k
    sal_uInt16 nSub;
3297
267k
    do
3298
341k
    {
3299
        // Don't try "lower" subformats ff the very first match was the second
3300
        // or third subformat.
3301
341k
        nSub = nStringScanNumFor;
3302
341k
        do
3303
917k
        {   // Step through subformats, first positive, then negative, then
3304
            // other, but not the last (text) subformat.
3305
917k
            pStr = mpFormat->GetNumForString( nSub, nString, true );
3306
917k
            if ( pStr && pTransliteration->isEqual( aString, *pStr ) )
3307
56.1k
            {
3308
56.1k
                bFound = true;
3309
56.1k
                bContinue = false;
3310
56.1k
            }
3311
860k
            else if ( nSub < 2 )
3312
575k
            {
3313
575k
                ++nSub;
3314
575k
            }
3315
285k
            else
3316
285k
            {
3317
285k
                bContinue = false;
3318
285k
            }
3319
917k
        }
3320
917k
        while ( bContinue );
3321
341k
        if ( !bFound && bFirst && nPos )
3322
74.4k
        {
3323
            // try remaining substring
3324
74.4k
            bFirst = false;
3325
74.4k
            aString = aString.copy(nPos);
3326
74.4k
            bContinue = true;
3327
74.4k
        }
3328
341k
    }
3329
341k
    while ( bContinue );
3330
3331
267k
    if ( !bFound )
3332
211k
    {
3333
211k
        if ( !bDontDetectNegation && (nString == 0) &&
3334
36.6k
             !bFirst && (nSign < 0) && mpFormat->IsSecondSubformatRealNegative() )
3335
16.6k
        {
3336
            // simply negated twice? --1
3337
16.6k
            aString = aString.replaceAll(" ", "");
3338
16.6k
            if ( (aString.getLength() == 1) && (aString[0] == '-') )
3339
12.3k
            {
3340
12.3k
                bFound = true;
3341
12.3k
                nStringScanSign = -1;
3342
12.3k
                nSub = 0; //! not 1
3343
12.3k
            }
3344
16.6k
        }
3345
211k
        if ( !bFound )
3346
198k
        {
3347
198k
            return false;
3348
198k
        }
3349
211k
    }
3350
56.1k
    else if ( !bDontDetectNegation && (nSub == 1) &&
3351
18.1k
              mpFormat->IsSecondSubformatRealNegative() )
3352
15.6k
    {
3353
        // negative
3354
15.6k
        if ( nStringScanSign < 0 )
3355
5.45k
        {
3356
5.45k
            if ( (nSign < 0) && (nStringScanNumFor != 1) )
3357
1.27k
            {
3358
1.27k
                nStringScanSign = 1; // triple negated --1 yyy
3359
1.27k
            }
3360
5.45k
        }
3361
10.2k
        else if ( nStringScanSign == 0 )
3362
10.2k
        {
3363
10.2k
            if ( nSign < 0 )
3364
5.32k
            {   // nSign and nStringScanSign will be combined later,
3365
                // flip sign if doubly negated
3366
5.32k
                if ( (nString == 0) && !bFirst &&
3367
2.78k
                     SvNumberformat::HasStringNegativeSign( aString ) )
3368
753
                {
3369
753
                    nStringScanSign = -1; // direct double negation
3370
753
                }
3371
4.57k
                else if ( mpFormat->IsNegativeWithoutSign() )
3372
3.44k
                {
3373
3.44k
                    nStringScanSign = -1; // indirect double negation
3374
3.44k
                }
3375
5.32k
            }
3376
4.89k
            else
3377
4.89k
            {
3378
4.89k
                nStringScanSign = -1;
3379
4.89k
            }
3380
10.2k
        }
3381
0
        else    // > 0
3382
0
        {
3383
0
            nStringScanSign = -1;
3384
0
        }
3385
15.6k
    }
3386
68.5k
    nStringScanNumFor = nSub;
3387
68.5k
    return true;
3388
267k
}
3389
3390
3391
/**
3392
 * Recognizes types of number, exponential, fraction, percent, currency, date, time.
3393
 * Else text => return false
3394
 */
3395
bool ImpSvNumberInputScan::IsNumberFormatMain( const OUString& rString,        // string to be analyzed
3396
                                               const SvNumberformat* pFormat ) // maybe number format set to match against
3397
4.85M
{
3398
4.85M
    Reset();
3399
4.85M
    mpFormat = pFormat;
3400
4.85M
    NumberStringDivision( rString );             // breakdown into strings and numbers
3401
4.85M
    if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS) // too many elements
3402
109k
    {
3403
109k
        return false;                            // Njet, Nope, ...
3404
109k
    }
3405
4.74M
    if (nNumericsCnt == 0)                           // no number in input
3406
2.06M
    {
3407
2.06M
        if ( nStringsCnt > 0 )
3408
2.06M
        {
3409
            // Here we may change the original, we don't need it anymore.
3410
            // This saves copies and ToUpper() in GetLogical() and is faster.
3411
2.06M
            sStrArray[0] = comphelper::string::strip(sStrArray[0], ' ');
3412
2.06M
            OUString& rStrArray = sStrArray[0];
3413
2.06M
            nLogical = GetLogical( rStrArray );
3414
2.06M
            if ( nLogical )
3415
1.35k
            {
3416
1.35k
                eScannedType = SvNumFormatType::LOGICAL; // !!! it's a BOOLEAN
3417
1.35k
                nMatchedAllStrings &= ~nMatchedVirgin;
3418
1.35k
                return true;
3419
1.35k
            }
3420
2.06M
            else
3421
2.06M
            {
3422
2.06M
                return false;                   // simple text
3423
2.06M
            }
3424
2.06M
        }
3425
0
        else
3426
0
        {
3427
0
            return false;                       // simple text
3428
0
        }
3429
2.06M
    }
3430
3431
2.68M
    sal_uInt16 i = 0;                           // mark any symbol
3432
2.68M
    sal_uInt16 j = 0;                           // mark only numbers
3433
3434
2.68M
    switch ( nNumericsCnt )
3435
2.68M
    {
3436
1.67M
    case 1 :                                // Exactly 1 number in input
3437
        // nStringsCnt >= 1
3438
1.67M
        if (GetNextNumber(i,j)) // i=1,0
3439
1.13M
        {   // Number at start
3440
1.13M
            if (eSetType == SvNumFormatType::FRACTION)  // Fraction 1 = 1/1
3441
979
            {
3442
979
                if (i >= nStringsCnt || // no end string nor decimal separator
3443
868
                    mrCurrentLanguageData.IsDecimalSep( sStrArray[i]))
3444
188
                {
3445
188
                    eScannedType = SvNumFormatType::FRACTION;
3446
188
                    nMatchedAllStrings &= ~nMatchedVirgin;
3447
188
                    return true;
3448
188
                }
3449
979
            }
3450
1.13M
        }
3451
534k
        else
3452
534k
        {                                   // Analyze start string
3453
534k
            if (!ScanStartString( sStrArray[i] ))  // i=0
3454
373k
            {
3455
373k
                return false;               // already an error
3456
373k
            }
3457
160k
            i++;                            // next symbol, i=1
3458
160k
        }
3459
1.29M
        GetNextNumber(i,j);                 // i=1,2
3460
1.29M
        if (eSetType == SvNumFormatType::FRACTION)  // Fraction -1 = -1/1
3461
2.98k
        {
3462
2.98k
            if (nSign && !bNegCheck &&      // Sign +, -
3463
1.77k
                eScannedType == SvNumFormatType::UNDEFINED &&   // not date or currency
3464
1.74k
                nDecPos == 0 &&             // no previous decimal separator
3465
1.41k
                (i >= nStringsCnt ||        // no end string nor decimal separator
3466
822
                 mrCurrentLanguageData.IsDecimalSep( sStrArray[i]))
3467
2.98k
                )
3468
805
            {
3469
805
                eScannedType = SvNumFormatType::FRACTION;
3470
805
                nMatchedAllStrings &= ~nMatchedVirgin;
3471
805
                return true;
3472
805
            }
3473
2.98k
        }
3474
1.29M
        if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3475
272k
        {
3476
272k
            return false;
3477
272k
        }
3478
1.02M
        break;
3479
1.02M
    case 2 :                                // Exactly 2 numbers in input
3480
                                            // nStringsCnt >= 3
3481
457k
        if (!GetNextNumber(i,j))            // i=1,0
3482
209k
        {                                   // Analyze start string
3483
209k
            if (!ScanStartString( sStrArray[i] ))
3484
124k
                return false;               // already an error
3485
84.9k
            i++;                            // i=1
3486
84.9k
        }
3487
332k
        GetNextNumber(i,j);                 // i=1,2
3488
332k
        if ( !ScanMidString( sStrArray[i], i, j ) )
3489
123k
        {
3490
123k
            return false;
3491
123k
        }
3492
209k
        i++;                                // next symbol, i=2,3
3493
209k
        GetNextNumber(i,j);                 // i=3,4
3494
209k
        if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3495
38.1k
        {
3496
38.1k
            return false;
3497
38.1k
        }
3498
171k
        if (eSetType == SvNumFormatType::FRACTION)  // -1,200. as fraction
3499
1.49k
        {
3500
1.49k
            if (!bNegCheck  &&                  // no sign '('
3501
1.25k
                eScannedType == SvNumFormatType::UNDEFINED &&
3502
925
                (nDecPos == 0 || nDecPos == 3)  // no decimal separator or at end
3503
1.49k
                )
3504
813
            {
3505
813
                eScannedType = SvNumFormatType::FRACTION;
3506
813
                nMatchedAllStrings &= ~nMatchedVirgin;
3507
813
                return true;
3508
813
            }
3509
1.49k
        }
3510
170k
        break;
3511
297k
    case 3 :                                // Exactly 3 numbers in input
3512
                                            // nStringsCnt >= 5
3513
297k
        if (!GetNextNumber(i,j))            // i=1,0
3514
106k
        {                                   // Analyze start string
3515
106k
            if (!ScanStartString( sStrArray[i] ))
3516
44.8k
            {
3517
44.8k
                return false;               // already an error
3518
44.8k
            }
3519
61.2k
            i++;                            // i=1
3520
61.2k
            if (nDecPos == 1)               // decimal separator at start => error
3521
3.58k
            {
3522
3.58k
                return false;
3523
3.58k
            }
3524
61.2k
        }
3525
248k
        GetNextNumber(i,j);                 // i=1,2
3526
248k
        if ( !ScanMidString( sStrArray[i], i, j ) )
3527
73.0k
        {
3528
73.0k
            return false;
3529
73.0k
        }
3530
175k
        i++;                                // i=2,3
3531
175k
        if (eScannedType == SvNumFormatType::SCIENTIFIC)    // E only at end
3532
4.90k
        {
3533
4.90k
            return false;
3534
4.90k
        }
3535
170k
        GetNextNumber(i,j);                 // i=3,4
3536
170k
        if ( !ScanMidString( sStrArray[i], i, j ) )
3537
29.7k
        {
3538
29.7k
            return false;
3539
29.7k
        }
3540
141k
        i++;                                // i=4,5
3541
141k
        GetNextNumber(i,j);                 // i=5,6
3542
141k
        if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3543
17.0k
        {
3544
17.0k
            return false;
3545
17.0k
        }
3546
123k
        if (eSetType == SvNumFormatType::FRACTION)  // -1,200,100. as fraction
3547
1.12k
        {
3548
1.12k
            if (!bNegCheck  &&                  // no sign '('
3549
983
                eScannedType == SvNumFormatType::UNDEFINED &&
3550
755
                (nDecPos == 0 || nDecPos == 3)  // no decimal separator or at end
3551
1.12k
                )
3552
537
            {
3553
537
                eScannedType = SvNumFormatType::FRACTION;
3554
537
                nMatchedAllStrings &= ~nMatchedVirgin;
3555
537
                return true;
3556
537
            }
3557
1.12k
        }
3558
123k
        if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
3559
544
        {
3560
544
            return false;                   // #36857# not a real fraction
3561
544
        }
3562
122k
        break;
3563
261k
    default:                                // More than 3 numbers in input
3564
                                            // nStringsCnt >= 7
3565
261k
        if (!GetNextNumber(i,j))            // i=1,0
3566
108k
        {                                   // Analyze startstring
3567
108k
            if (!ScanStartString( sStrArray[i] ))
3568
48.7k
                return false;               // already an error
3569
59.4k
            i++;                            // i=1
3570
59.4k
            if (nDecPos == 1)               // decimal separator at start => error
3571
3.40k
                return false;
3572
59.4k
        }
3573
209k
        GetNextNumber(i,j);                 // i=1,2
3574
209k
        if ( !ScanMidString( sStrArray[i], i, j ) )
3575
50.7k
        {
3576
50.7k
            return false;
3577
50.7k
        }
3578
158k
        i++;                                // i=2,3
3579
158k
        {
3580
158k
            sal_uInt16 nThOld = 10;                 // just not 0 or 1
3581
312k
            while (nThOld != nThousand && j < nNumericsCnt-1) // Execute at least one time
3582
                                                          // but leave one number.
3583
179k
            {                                             // Loop over group separators
3584
179k
                nThOld = nThousand;
3585
179k
                if (eScannedType == SvNumFormatType::SCIENTIFIC)    // E only at end
3586
3.40k
                {
3587
3.40k
                    return false;
3588
3.40k
                }
3589
175k
                GetNextNumber(i,j);
3590
175k
                if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i, j ) )
3591
21.6k
                {
3592
21.6k
                    return false;
3593
21.6k
                }
3594
154k
                i++;
3595
154k
            }
3596
158k
        }
3597
133k
        if (eScannedType == SvNumFormatType::DATE ||    // long date or
3598
43.0k
            eScannedType == SvNumFormatType::TIME ||    // long time or
3599
29.8k
            eScannedType == SvNumFormatType::UNDEFINED) // long number
3600
121k
        {
3601
313k
            for (sal_uInt16 k = j; k < nNumericsCnt-1; k++)
3602
237k
            {
3603
237k
                if (eScannedType == SvNumFormatType::SCIENTIFIC)    // E only at endd
3604
1.34k
                {
3605
1.34k
                    return false;
3606
1.34k
                }
3607
236k
                GetNextNumber(i,j);
3608
236k
                if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i, j ) )
3609
44.0k
                {
3610
44.0k
                    return false;
3611
44.0k
                }
3612
192k
                i++;
3613
192k
            }
3614
121k
        }
3615
88.0k
        GetNextNumber(i,j);
3616
88.0k
        if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3617
16.7k
        {
3618
16.7k
            return false;
3619
16.7k
        }
3620
71.3k
        if (eSetType == SvNumFormatType::FRACTION)  // -1,200,100. as fraction
3621
1.60k
        {
3622
1.60k
            if (!bNegCheck  &&                  // no sign '('
3623
1.41k
                eScannedType == SvNumFormatType::UNDEFINED &&
3624
974
                (nDecPos == 0 || nDecPos == 3)  // no decimal separator or at end
3625
1.60k
                )
3626
665
            {
3627
665
                eScannedType = SvNumFormatType::FRACTION;
3628
665
                nMatchedAllStrings &= ~nMatchedVirgin;
3629
665
                return true;
3630
665
            }
3631
1.60k
        }
3632
70.7k
        if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
3633
116
        {
3634
116
            return false;                       // #36857# not a real fraction
3635
116
        }
3636
70.6k
        break;
3637
2.68M
    }
3638
3639
1.38M
    if (eScannedType == SvNumFormatType::UNDEFINED)
3640
852k
    {
3641
852k
        nMatchedAllStrings &= ~nMatchedVirgin;
3642
        // did match including nMatchedUsedAsReturn
3643
852k
        bool bDidMatch = (nMatchedAllStrings != 0);
3644
852k
        if ( nMatchedAllStrings )
3645
15.8k
        {
3646
15.8k
            bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
3647
15.8k
                               nStringScanNumFor, nStringsCnt, nNumericsCnt );
3648
15.8k
            if ( !bMatch )
3649
2.64k
            {
3650
2.64k
                nMatchedAllStrings = 0;
3651
2.64k
            }
3652
15.8k
        }
3653
852k
        if ( nMatchedAllStrings )
3654
13.2k
        {
3655
            // A type DEFINED means that no category could be assigned to the
3656
            // overall format because of mixed type subformats. Use the scan
3657
            // matched subformat's type if any.
3658
13.2k
            SvNumFormatType eForType = eSetType;
3659
13.2k
            if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
3660
2.82k
                eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor);
3661
13.2k
            if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
3662
12.8k
                eScannedType = eForType;
3663
385
            else
3664
385
                eScannedType = SvNumFormatType::NUMBER;
3665
13.2k
        }
3666
838k
        else if ( bDidMatch )
3667
2.64k
        {
3668
            // Accept a plain fractional number like 123.45 as there may be a
3669
            // decimal separator also present as literal like in a 0"."0 weirdo
3670
            // format.
3671
2.64k
            if (nDecPos != 2 || nNumericsCnt != 2)
3672
2.25k
                return false;
3673
392
            eScannedType = SvNumFormatType::NUMBER;
3674
392
        }
3675
836k
        else
3676
836k
        {
3677
836k
            eScannedType = SvNumFormatType::NUMBER;
3678
            // everything else should have been recognized by now
3679
836k
        }
3680
852k
    }
3681
536k
    else if ( eScannedType == SvNumFormatType::DATE )
3682
103k
    {
3683
        // the very relaxed date input checks may interfere with a preset format
3684
103k
        nMatchedAllStrings &= ~nMatchedVirgin;
3685
103k
        bool bWasReturn = ((nMatchedAllStrings & nMatchedUsedAsReturn) != 0);
3686
103k
        if ( nMatchedAllStrings )
3687
5.95k
        {
3688
5.95k
            bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
3689
5.95k
                               nStringScanNumFor, nStringsCnt, nNumericsCnt );
3690
5.95k
            if ( !bMatch )
3691
671
            {
3692
671
                nMatchedAllStrings = 0;
3693
671
            }
3694
5.95k
        }
3695
103k
        if ( nMatchedAllStrings )
3696
5.27k
        {
3697
            // A type DEFINED means that no category could be assigned to the
3698
            // overall format because of mixed type subformats. Do not override
3699
            // the scanned type in this case. Otherwise in IsNumberFormat() the
3700
            // first numeric particle would be accepted as number.
3701
5.27k
            SvNumFormatType eForType = eSetType;
3702
5.27k
            if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
3703
448
                eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor);
3704
5.27k
            if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
3705
4.90k
                eScannedType = eForType;
3706
5.27k
        }
3707
97.9k
        else if ( bWasReturn )
3708
61
        {
3709
61
            return false;
3710
61
        }
3711
103k
    }
3712
433k
    else
3713
433k
    {
3714
433k
        nMatchedAllStrings = 0; // reset flag to no substrings matched
3715
433k
    }
3716
1.38M
    return true;
3717
1.38M
}
3718
3719
3720
/**
3721
 * Return true or false depending on the nMatched... state and remember usage
3722
 */
3723
bool ImpSvNumberInputScan::MatchedReturn()
3724
666k
{
3725
666k
    if ( nMatchedAllStrings & ~nMatchedVirgin )
3726
5.23k
    {
3727
5.23k
        nMatchedAllStrings |= nMatchedUsedAsReturn;
3728
5.23k
        return true;
3729
5.23k
    }
3730
661k
    return false;
3731
666k
}
3732
3733
3734
/**
3735
 * Initialize uppercase months and weekdays
3736
 */
3737
void ImpSvNumberInputScan::InitText()
3738
129k
{
3739
129k
    sal_Int32 j, nElems;
3740
129k
    const CharClass* pChrCls = mrCurrentLanguageData.GetCharClass();
3741
129k
    const CalendarWrapper* pCal = mrCurrentLanguageData.GetCalendar();
3742
3743
129k
    pUpperMonthText.reset();
3744
129k
    pUpperAbbrevMonthText.reset();
3745
129k
    css::uno::Sequence< css::i18n::CalendarItem2 > xElems = pCal->getMonths();
3746
129k
    nElems = xElems.getLength();
3747
129k
    pUpperMonthText.reset( new OUString[nElems] );
3748
129k
    pUpperAbbrevMonthText.reset( new OUString[nElems] );
3749
413k
    for ( j = 0; j < nElems; j++ )
3750
283k
    {
3751
283k
        pUpperMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3752
283k
        pUpperAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3753
283k
    }
3754
3755
129k
    pUpperGenitiveMonthText.reset();
3756
129k
    pUpperGenitiveAbbrevMonthText.reset();
3757
129k
    xElems = pCal->getGenitiveMonths();
3758
129k
    bScanGenitiveMonths = (nElems != xElems.getLength());
3759
129k
    nElems = xElems.getLength();
3760
129k
    pUpperGenitiveMonthText.reset( new OUString[nElems] );
3761
129k
    pUpperGenitiveAbbrevMonthText.reset( new OUString[nElems] );
3762
413k
    for ( j = 0; j < nElems; j++ )
3763
283k
    {
3764
283k
        pUpperGenitiveMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3765
283k
        pUpperGenitiveAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3766
283k
        if (!bScanGenitiveMonths &&
3767
283k
            (pUpperGenitiveMonthText[j] != pUpperMonthText[j] ||
3768
283k
             pUpperGenitiveAbbrevMonthText[j] != pUpperAbbrevMonthText[j]))
3769
0
        {
3770
0
            bScanGenitiveMonths = true;
3771
0
        }
3772
283k
    }
3773
3774
129k
    pUpperPartitiveMonthText.reset();
3775
129k
    pUpperPartitiveAbbrevMonthText.reset();
3776
129k
    xElems = pCal->getPartitiveMonths();
3777
129k
    bScanPartitiveMonths = (nElems != xElems.getLength());
3778
129k
    nElems = xElems.getLength();
3779
129k
    pUpperPartitiveMonthText.reset( new OUString[nElems] );
3780
129k
    pUpperPartitiveAbbrevMonthText.reset( new OUString[nElems] );
3781
413k
    for ( j = 0; j < nElems; j++ )
3782
283k
    {
3783
283k
        pUpperPartitiveMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3784
283k
        pUpperPartitiveAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3785
283k
        if (!bScanPartitiveMonths &&
3786
283k
            (pUpperPartitiveMonthText[j] != pUpperGenitiveMonthText[j] ||
3787
283k
             pUpperPartitiveAbbrevMonthText[j] != pUpperGenitiveAbbrevMonthText[j]))
3788
0
        {
3789
0
            bScanPartitiveMonths = true;
3790
0
        }
3791
283k
    }
3792
3793
129k
    pUpperDayText.reset();
3794
129k
    pUpperAbbrevDayText.reset();
3795
129k
    xElems = pCal->getDays();
3796
129k
    nElems = xElems.getLength();
3797
129k
    pUpperDayText.reset( new OUString[nElems] );
3798
129k
    pUpperAbbrevDayText.reset( new OUString[nElems] );
3799
294k
    for ( j = 0; j < nElems; j++ )
3800
165k
    {
3801
165k
        pUpperDayText[j] = pChrCls->uppercase( xElems[j].FullName );
3802
165k
        pUpperAbbrevDayText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3803
165k
    }
3804
3805
129k
    bTextInitialized = true;
3806
129k
}
3807
3808
3809
/**
3810
 * MUST be called if International/Locale is changed
3811
 */
3812
void ImpSvNumberInputScan::ChangeIntl()
3813
1.67M
{
3814
1.67M
    sal_Unicode cDecSep = mrCurrentLanguageData.GetNumDecimalSep()[0];
3815
1.67M
    bDecSepInDateSeps = ( cDecSep == '-' ||
3816
1.67M
                          cDecSep == mrCurrentLanguageData.GetDateSep()[0] );
3817
1.67M
    if (!bDecSepInDateSeps)
3818
1.67M
    {
3819
1.67M
        sal_Unicode cDecSepAlt = mrCurrentLanguageData.GetNumDecimalSepAlt().toChar();
3820
1.67M
        bDecSepInDateSeps = cDecSepAlt && (cDecSepAlt == '-' || cDecSepAlt == mrCurrentLanguageData.GetDateSep()[0]);
3821
1.67M
    }
3822
1.67M
    bTextInitialized = false;
3823
1.67M
    aUpperCurrSymbol.clear();
3824
1.67M
    InvalidateDateAcceptancePatterns();
3825
1.67M
}
3826
3827
3828
void ImpSvNumberInputScan::InvalidateDateAcceptancePatterns()
3829
1.67M
{
3830
1.67M
    if (sDateAcceptancePatterns.hasElements())
3831
77.9k
    {
3832
77.9k
        sDateAcceptancePatterns = css::uno::Sequence< OUString >();
3833
77.9k
    }
3834
1.67M
}
3835
3836
3837
void ImpSvNumberInputScan::ChangeNullDate( const sal_uInt16 Day,
3838
                                           const sal_uInt16 Month,
3839
                                           const sal_Int16 Year )
3840
189k
{
3841
189k
    maNullDate = Date(Day, Month, Year);
3842
189k
}
3843
3844
3845
/**
3846
 * Does rString represent a number (also date, time et al)
3847
 */
3848
bool ImpSvNumberInputScan::IsNumberFormat( const OUString& rString,         // string to be analyzed
3849
                                           SvNumFormatType& F_Type,         // IN: old type, OUT: new type
3850
                                           double& fOutNumber,              // OUT: number if convertible
3851
                                           const SvNumberformat* pFormat,   // maybe a number format to match against
3852
                                           const NativeNumberWrapper& rNatNum,
3853
                                           SvNumInputOptions eInputOptions )
3854
5.14M
{
3855
5.14M
    bool res; // return value
3856
5.14M
    sal_uInt16 k;
3857
5.14M
    eSetType = F_Type; // old type set
3858
3859
5.14M
    if ( !rString.getLength() )
3860
277k
    {
3861
277k
        res = false;
3862
277k
    }
3863
4.86M
    else if (rString.getLength() > 308) // arbitrary
3864
5.53k
    {
3865
5.53k
        res = false;
3866
5.53k
    }
3867
4.85M
    else
3868
4.85M
    {
3869
        // NoMoreUpperNeeded, all comparisons on UpperCase
3870
4.85M
        OUString aString = mrCurrentLanguageData.GetCharClass()->uppercase( rString );
3871
        // convert native number to ASCII if necessary
3872
4.85M
        TransformInput(rNatNum, mrCurrentLanguageData, aString);
3873
4.85M
        res = IsNumberFormatMain( aString, pFormat );
3874
4.85M
    }
3875
3876
5.14M
    if (res)
3877
1.39M
    {
3878
        // Accept signed date only for ISO date with at least four digits in
3879
        // year to not have an input of -M-D-Y arbitrarily recognized. The
3880
        // final order is only determined in GetDateRef().
3881
        // Also accept for Y/M/D date pattern match, i.e. if the first number
3882
        // is year.
3883
        // Accept only if the year immediately follows the sign character with
3884
        // no space in between.
3885
1.39M
        if (nSign && (eScannedType == SvNumFormatType::DATE ||
3886
86.5k
                      eScannedType == SvNumFormatType::DATETIME) && mbEraCE == kDefaultEra &&
3887
34.3k
                (IsDatePatternNumberOfType(0,'Y') || (MayBeIso8601() && sStrArray[nNums[0]].getLength() >= 4)))
3888
16.1k
        {
3889
16.1k
            const sal_Unicode c = sStrArray[0][sStrArray[0].getLength()-1];
3890
16.1k
            if (c == '-' || c == '+')
3891
12.8k
            {
3892
                // A '+' sign doesn't change the era.
3893
12.8k
                if (nSign < 0)
3894
10.6k
                    mbEraCE = false;  // BCE
3895
12.8k
                nSign = 0;
3896
12.8k
            }
3897
16.1k
        }
3898
1.39M
        if ( bNegCheck ||                             // ')' not found for '('
3899
1.37M
             (nSign && (eScannedType == SvNumFormatType::DATE ||
3900
68.2k
                        eScannedType == SvNumFormatType::DATETIME))) // signed date/datetime
3901
30.2k
        {
3902
30.2k
            res = false;
3903
30.2k
        }
3904
1.36M
        else
3905
1.36M
        {                                           // check count of partial number strings
3906
1.36M
            switch (eScannedType)
3907
1.36M
            {
3908
23.9k
            case SvNumFormatType::PERCENT:
3909
61.5k
            case SvNumFormatType::CURRENCY:
3910
896k
            case SvNumFormatType::NUMBER:
3911
896k
                if (nDecPos == 1)               // .05
3912
27.0k
                {
3913
                    // Matched MidStrings function like group separators, but
3914
                    // there can't be an integer part numeric input, so
3915
                    // effectively 0 thousands groups.
3916
27.0k
                    if ( nMatchedAllStrings )
3917
33
                    {
3918
33
                        nThousand = 0;
3919
33
                    }
3920
26.9k
                    else if ( nNumericsCnt != 1 )
3921
1.05k
                    {
3922
1.05k
                        res = false;
3923
1.05k
                    }
3924
27.0k
                }
3925
869k
                else if (nDecPos == 2)          // 1.05
3926
51.5k
                {
3927
                    // Matched MidStrings function like group separators, but
3928
                    // let a decimal separator override a literal separator
3929
                    // string; like 0"." with input 123.45
3930
51.5k
                    if ( nMatchedAllStrings )
3931
628
                    {
3932
628
                        if (nNumericsCnt == 2)
3933
206
                            nThousand = 0;
3934
422
                        else
3935
422
                        {
3936
                            // Assume that if there was a decimal separator
3937
                            // matching also a literal string then it was the
3938
                            // last. We could find the last possible match to
3939
                            // support literals in fractions, but really..
3940
422
                            nThousand = nNumericsCnt - 1;
3941
422
                        }
3942
628
                    }
3943
50.8k
                    else if ( nNumericsCnt != nThousand+2 )
3944
3.46k
                    {
3945
3.46k
                        res = false;
3946
3.46k
                    }
3947
51.5k
                }
3948
817k
                else                            // 1,100 or 1,100.
3949
817k
                {
3950
                    // matched MidStrings function like group separators
3951
817k
                    if ( nMatchedAllStrings )
3952
854
                    {
3953
854
                        nThousand = nNumericsCnt - 1;
3954
854
                    }
3955
816k
                    else if ( nNumericsCnt != nThousand+1 )
3956
10.3k
                    {
3957
10.3k
                        res = false;
3958
10.3k
                    }
3959
817k
                }
3960
896k
                break;
3961
3962
85.3k
            case SvNumFormatType::SCIENTIFIC:       // 1.0e-2
3963
85.3k
                if (nDecPos == 1)               // .05
3964
8.63k
                {
3965
8.63k
                    if (nNumericsCnt != 2)
3966
162
                    {
3967
162
                        res = false;
3968
162
                    }
3969
8.63k
                }
3970
76.6k
                else if (nDecPos == 2)          // 1.05
3971
33.1k
                {
3972
33.1k
                    if (nNumericsCnt != nThousand+3)
3973
2.46k
                    {
3974
2.46k
                        res = false;
3975
2.46k
                    }
3976
33.1k
                }
3977
43.5k
                else                            // 1,100 or 1,100.
3978
43.5k
                {
3979
43.5k
                    if (nNumericsCnt != nThousand+2)
3980
1.52k
                    {
3981
1.52k
                        res = false;
3982
1.52k
                    }
3983
43.5k
                }
3984
85.3k
                break;
3985
3986
85.9k
            case SvNumFormatType::DATE:
3987
85.9k
                if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt == 3)
3988
0
                {
3989
                    // If both, short month name and day of week name were
3990
                    // detected, and also numbers for full date, assume that we
3991
                    // have a day of week instead of month name.
3992
0
                    nMonth = 0;
3993
0
                    nMonthPos = 0;
3994
0
                }
3995
85.9k
                if (nMonth)
3996
24.6k
                {                               // month name and numbers
3997
24.6k
                    if (nNumericsCnt > 2)
3998
3.30k
                    {
3999
3.30k
                        res = false;
4000
3.30k
                    }
4001
24.6k
                }
4002
61.3k
                else
4003
61.3k
                {
4004
61.3k
                    if (nNumericsCnt > 3)
4005
7.60k
                    {
4006
7.60k
                        res = false;
4007
7.60k
                    }
4008
53.7k
                    else
4009
53.7k
                    {
4010
                        // Even if a date pattern was matched, for abbreviated
4011
                        // pattern like "D.M." an input of "D.M. #" was
4012
                        // accepted because # could had been a time. Here we do
4013
                        // not have a combined date/time input though and #
4014
                        // would be taken as Year in this example, which it is
4015
                        // not. The count of numbers in pattern must match the
4016
                        // count of numbers in input.
4017
53.7k
                        res = (GetDatePatternNumbers() == nNumericsCnt)
4018
44.2k
                            || IsAcceptableIso8601() || nMatchedAllStrings;
4019
53.7k
                    }
4020
61.3k
                }
4021
85.9k
                break;
4022
4023
214k
            case SvNumFormatType::TIME:
4024
214k
                if (nDecPos)
4025
22.1k
                {                               // hundredth seconds included
4026
22.1k
                    if (nNumericsCnt > 4)
4027
1.17k
                    {
4028
1.17k
                        res = false;
4029
1.17k
                    }
4030
22.1k
                }
4031
192k
                else
4032
192k
                {
4033
192k
                    if (nNumericsCnt > 3)
4034
2.51k
                    {
4035
2.51k
                        res = false;
4036
2.51k
                    }
4037
192k
                }
4038
214k
                break;
4039
4040
61.0k
            case SvNumFormatType::DATETIME:
4041
61.0k
                if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt >= 5)
4042
0
                {
4043
                    // If both, abbreviated month name and day of week name
4044
                    // were detected, and also at least numbers for full date
4045
                    // plus time including minutes, assume that we have a day
4046
                    // of week instead of month name.
4047
0
                    nMonth = 0;
4048
0
                    nMonthPos = 0;
4049
0
                }
4050
61.0k
                if (nMonth)
4051
10.6k
                {                               // month name and numbers
4052
10.6k
                    if (nDecPos)
4053
2.15k
                    {                           // hundredth seconds included
4054
2.15k
                        if (nNumericsCnt > 6)
4055
213
                        {
4056
213
                            res = false;
4057
213
                        }
4058
2.15k
                    }
4059
8.52k
                    else
4060
8.52k
                    {
4061
8.52k
                        if (nNumericsCnt > 5)
4062
294
                        {
4063
294
                            res = false;
4064
294
                        }
4065
8.52k
                    }
4066
10.6k
                }
4067
50.3k
                else
4068
50.3k
                {
4069
50.3k
                    if (nDecPos)
4070
4.93k
                    {                           // hundredth seconds included
4071
4.93k
                        if (nNumericsCnt > 7)
4072
1.28k
                        {
4073
1.28k
                            res = false;
4074
1.28k
                        }
4075
4.93k
                    }
4076
45.4k
                    else
4077
45.4k
                    {
4078
45.4k
                        if (nNumericsCnt > 6)
4079
2.04k
                        {
4080
2.04k
                            res = false;
4081
2.04k
                        }
4082
45.4k
                    }
4083
50.3k
                    if (res)
4084
47.0k
                    {
4085
47.0k
                        res = IsAcceptedDatePattern( nNums[0]) || MayBeIso8601() || nMatchedAllStrings;
4086
47.0k
                    }
4087
50.3k
                }
4088
61.0k
                break;
4089
4090
18.1k
            default:
4091
18.1k
                break;
4092
1.36M
            }   // switch
4093
1.36M
        }   // else
4094
1.39M
    }   // if (res)
4095
4096
5.14M
    OUStringBuffer sResString;
4097
4098
5.14M
    if (res)
4099
1.29M
    {                                       // we finally have a number
4100
1.29M
        switch (eScannedType)
4101
1.29M
        {
4102
1.35k
        case SvNumFormatType::LOGICAL:
4103
1.35k
            if (nLogical ==  1)
4104
756
            {
4105
756
                fOutNumber = 1.0;           // True
4106
756
            }
4107
601
            else if (nLogical == -1)
4108
601
            {
4109
601
                fOutNumber = 0.0;           // False
4110
601
            }
4111
0
            else
4112
0
            {
4113
0
                res = false;                // Oops
4114
0
            }
4115
1.35k
            break;
4116
4117
23.8k
        case SvNumFormatType::PERCENT:
4118
59.8k
        case SvNumFormatType::CURRENCY:
4119
881k
        case SvNumFormatType::NUMBER:
4120
962k
        case SvNumFormatType::SCIENTIFIC:
4121
962k
        case SvNumFormatType::DEFINED:          // if no category detected handle as number
4122
962k
            if ( nDecPos == 1 )             // . at start
4123
34.4k
            {
4124
34.4k
                sResString.append("0.");
4125
34.4k
            }
4126
4127
1.94M
            for ( k = 0; k <= nThousand; k++)
4128
978k
            {
4129
978k
                sResString.append(sStrArray[nNums[k]]);  // integer part
4130
978k
            }
4131
962k
            if ( nDecPos == 2 && k < nNumericsCnt )     // . somewhere
4132
78.3k
            {
4133
78.3k
                sResString.append('.');
4134
78.3k
                sal_uInt16 nStop = (eScannedType == SvNumFormatType::SCIENTIFIC ?
4135
47.6k
                                    nNumericsCnt-1 : nNumericsCnt);
4136
156k
                for ( ; k < nStop; k++)
4137
78.3k
                {
4138
78.3k
                    sResString.append(sStrArray[nNums[k]]);  // fractional part
4139
78.3k
                }
4140
78.3k
            }
4141
4142
962k
            if (eScannedType != SvNumFormatType::SCIENTIFIC)
4143
881k
            {
4144
881k
                fOutNumber = StringToDouble(sResString);
4145
881k
            }
4146
81.1k
            else
4147
81.1k
            {                                           // append exponent
4148
81.1k
                sResString.append('E');
4149
81.1k
                if ( nESign == -1 )
4150
34.9k
                {
4151
34.9k
                    sResString.append('-');
4152
34.9k
                }
4153
81.1k
                sResString.append(sStrArray[nNums[nNumericsCnt-1]]);
4154
81.1k
                rtl_math_ConversionStatus eStatus;
4155
81.1k
                fOutNumber = ::rtl::math::stringToDouble( sResString, '.', ',', &eStatus );
4156
81.1k
                if ( eStatus == rtl_math_ConversionStatus_OutOfRange )
4157
21.2k
                {
4158
21.2k
                    F_Type = SvNumFormatType::TEXT;         // overflow/underflow -> Text
4159
21.2k
                    if (nESign == -1)
4160
8.83k
                    {
4161
8.83k
                        fOutNumber = 0.0;
4162
8.83k
                    }
4163
12.4k
                    else
4164
12.4k
                    {
4165
12.4k
                        fOutNumber = DBL_MAX;
4166
12.4k
                    }
4167
21.2k
                    return true;
4168
21.2k
                }
4169
81.1k
            }
4170
4171
941k
            if ( nStringScanSign )
4172
12.9k
            {
4173
12.9k
                if ( nSign )
4174
11.8k
                {
4175
11.8k
                    nSign *= nStringScanSign;
4176
11.8k
                }
4177
1.05k
                else
4178
1.05k
                {
4179
1.05k
                    nSign = nStringScanSign;
4180
1.05k
                }
4181
12.9k
            }
4182
941k
            if ( nSign < 0 )
4183
29.9k
            {
4184
29.9k
                fOutNumber = -fOutNumber;
4185
29.9k
            }
4186
4187
941k
            if (eScannedType == SvNumFormatType::PERCENT)
4188
23.8k
            {
4189
23.8k
                fOutNumber/= 100.0;
4190
23.8k
            }
4191
941k
            break;
4192
4193
16.5k
        case SvNumFormatType::FRACTION:
4194
16.5k
            if (nNumericsCnt == 1)
4195
1.08k
            {
4196
1.08k
                fOutNumber = StringToDouble(sStrArray[nNums[0]]);
4197
1.08k
            }
4198
15.5k
            else if (nNumericsCnt == 2)
4199
1.10k
            {
4200
1.10k
                if (nThousand == 1)
4201
282
                {
4202
282
                    sResString = sStrArray[nNums[0]];
4203
282
                    sResString.append(sStrArray[nNums[1]]); // integer part
4204
282
                    fOutNumber = StringToDouble(sResString);
4205
282
                }
4206
823
                else
4207
823
                {
4208
823
                    double fNumerator = StringToDouble(sStrArray[nNums[0]]);
4209
823
                    double fDenominator = StringToDouble(sStrArray[nNums[1]]);
4210
823
                    if (fDenominator != 0.0)
4211
519
                    {
4212
519
                        fOutNumber = fNumerator/fDenominator;
4213
519
                    }
4214
304
                    else
4215
304
                    {
4216
304
                        res = false;
4217
304
                    }
4218
823
                }
4219
1.10k
            }
4220
14.4k
            else // nNumericsCnt > 2
4221
14.4k
            {
4222
14.4k
                k = 1;
4223
14.4k
                sResString = sStrArray[nNums[0]];
4224
14.4k
                if (nThousand > 0)
4225
554
                {
4226
1.66k
                    for (; k <= nThousand; k++)
4227
1.10k
                    {
4228
1.10k
                        sResString.append(sStrArray[nNums[k]]);
4229
1.10k
                    }
4230
554
                }
4231
14.4k
                fOutNumber = StringToDouble(sResString);
4232
4233
14.4k
                if (k == nNumericsCnt-2)
4234
13.3k
                {
4235
13.3k
                    double fNumerator = StringToDouble(sStrArray[nNums[k]]);
4236
13.3k
                    double fDenominator = StringToDouble(sStrArray[nNums[k + 1]]);
4237
13.3k
                    if (fDenominator != 0.0)
4238
5.05k
                    {
4239
5.05k
                        fOutNumber += fNumerator/fDenominator;
4240
5.05k
                    }
4241
8.29k
                    else
4242
8.29k
                    {
4243
8.29k
                        res = false;
4244
8.29k
                    }
4245
13.3k
                }
4246
14.4k
            }
4247
4248
16.5k
            if ( nStringScanSign )
4249
473
            {
4250
473
                if ( nSign )
4251
380
                {
4252
380
                    nSign *= nStringScanSign;
4253
380
                }
4254
93
                else
4255
93
                {
4256
93
                    nSign = nStringScanSign;
4257
93
                }
4258
473
            }
4259
16.5k
            if ( nSign < 0 )
4260
6.69k
            {
4261
6.69k
                fOutNumber = -fOutNumber;
4262
6.69k
            }
4263
16.5k
            break;
4264
4265
210k
        case SvNumFormatType::TIME:
4266
210k
            res = GetTimeRef(fOutNumber, 0, nNumericsCnt, eInputOptions);
4267
210k
            if ( nSign < 0 )
4268
11.2k
            {
4269
11.2k
                fOutNumber = -fOutNumber;
4270
11.2k
            }
4271
210k
            break;
4272
4273
52.6k
        case SvNumFormatType::DATE:
4274
52.6k
            res = GetDateRef( fOutNumber, k );
4275
52.6k
            break;
4276
4277
54.5k
        case SvNumFormatType::DATETIME:
4278
54.5k
            res = GetDateRef( fOutNumber, k );
4279
54.5k
            if ( res )
4280
24.4k
            {
4281
24.4k
                double fTime;
4282
24.4k
                res = GetTimeRef( fTime, k, nNumericsCnt - k, eInputOptions);
4283
24.4k
                fOutNumber += fTime;
4284
24.4k
            }
4285
54.5k
            break;
4286
4287
191
        default:
4288
191
            SAL_WARN( "svl.numbers", "Some number recognized but what's it?" );
4289
191
            fOutNumber = 0.0;
4290
191
            break;
4291
1.29M
        }
4292
1.29M
    }
4293
4294
5.12M
    if (res) // overflow/underflow -> Text
4295
1.17M
    {
4296
1.17M
        if (fOutNumber < -DBL_MAX) // -1.7E308
4297
0
        {
4298
0
            F_Type = SvNumFormatType::TEXT;
4299
0
            fOutNumber = -DBL_MAX;
4300
0
            return true;
4301
0
        }
4302
1.17M
        else if (fOutNumber >  DBL_MAX) // 1.7E308
4303
0
        {
4304
0
            F_Type = SvNumFormatType::TEXT;
4305
0
            fOutNumber = DBL_MAX;
4306
0
            return true;
4307
0
        }
4308
1.17M
    }
4309
4310
5.12M
    if (!res)
4311
3.94M
    {
4312
3.94M
        eScannedType = SvNumFormatType::TEXT;
4313
3.94M
        fOutNumber = 0.0;
4314
3.94M
    }
4315
4316
5.12M
    F_Type = eScannedType;
4317
5.12M
    return res;
4318
5.12M
}
4319
4320
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */