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