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