turn gb_CustomTarget_get_workdir into error and remove old _repo_targets
[LibreOffice.git] / svl / source / numbers / zforfind.cxx
blob3d7333f3a1c637362b0e01dcf45ee84eb4ea5a8f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <cstdlib>
21 #include <dtoa.h>
22 #include <float.h>
23 #include <comphelper/string.hxx>
24 #include <o3tl/string_view.hxx>
25 #include <sal/log.hxx>
26 #include <tools/date.hxx>
27 #include <rtl/math.hxx>
28 #include <rtl/character.hxx>
29 #include <unotools/charclass.hxx>
30 #include <unotools/calendarwrapper.hxx>
31 #include <unotools/localedatawrapper.hxx>
32 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
33 #include <com/sun/star/i18n/LocaleCalendar2.hpp>
34 #include <unotools/digitgroupingiterator.hxx>
35 #include <comphelper/sequence.hxx>
37 #include <svl/zforlist.hxx>
38 #include "zforscan.hxx"
39 #include <svl/zformat.hxx>
41 #include <memory>
43 #include "zforfind.hxx"
45 #ifndef DBG_UTIL
46 #define NF_TEST_CALENDAR 0
47 #else
48 #define NF_TEST_CALENDAR 0
49 #endif
50 #if NF_TEST_CALENDAR
51 #include <comphelper/processfactory.hxx>
52 #include <com/sun/star/i18n/XCalendar4.hpp>
53 #endif
56 const sal_uInt8 ImpSvNumberInputScan::nMatchedEndString = 0x01;
57 const sal_uInt8 ImpSvNumberInputScan::nMatchedMidString = 0x02;
58 const sal_uInt8 ImpSvNumberInputScan::nMatchedStartString = 0x04;
59 const sal_uInt8 ImpSvNumberInputScan::nMatchedVirgin = 0x08;
60 const sal_uInt8 ImpSvNumberInputScan::nMatchedUsedAsReturn = 0x10;
62 /* It is not clear how we want timezones to be handled. Convert them to local
63 * time isn't wanted, as it isn't done in any other place and timezone
64 * information isn't stored anywhere. Ignoring them and pretending local time
65 * may be wrong too and might not be what the user expects. Keep the input as
66 * string so that no information is lost.
67 * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it
68 * would work, together with the nTimezonePos handling in GetTimeRef(). */
69 #define NF_RECOGNIZE_ISO8601_TIMEZONES 0
71 const sal_Unicode cNoBreakSpace = 0xA0;
72 const sal_Unicode cNarrowNoBreakSpace = 0x202F;
73 const bool kDefaultEra = true; // Gregorian CE, positive year
75 ImpSvNumberInputScan::ImpSvNumberInputScan(SvNFLanguageData& rCurrentLanguage)
77 mrCurrentLanguageData(rCurrentLanguage),
78 bTextInitialized( false ),
79 bScanGenitiveMonths( false ),
80 bScanPartitiveMonths( false ),
81 eScannedType( SvNumFormatType::UNDEFINED ),
82 eSetType( SvNumFormatType::UNDEFINED )
84 moNullDate.emplace( 30,12,1899 );
85 nYear2000 = SvNumberFormatter::GetYear2000Default();
86 Reset();
87 ChangeIntl();
90 ImpSvNumberInputScan::~ImpSvNumberInputScan()
95 void ImpSvNumberInputScan::Reset()
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;
124 for (sal_uInt32 i = 0; i < SV_MAX_COUNT_INPUT_STRINGS; i++)
126 IsNum[i] = false;
127 nNums[i] = 0;
131 // native number transliteration if necessary
132 static void TransformInput(const NativeNumberWrapper& rNatNum, const SvNFLanguageData& rCurrentLanguage, OUString& rStr)
134 sal_Int32 nPos, nLen;
135 for ( nPos = 0, nLen = rStr.getLength(); nPos < nLen; ++nPos )
137 if ( 256 <= rStr[ nPos ] &&
138 rCurrentLanguage.GetCharClass()->isDigit( rStr, nPos ) )
140 break;
143 if ( nPos < nLen )
145 rStr = rNatNum.getNativeNumberString(rStr, rCurrentLanguage.GetLanguageTag().getLocale(), 0);
151 * Only simple unsigned floating point values without any error detection,
152 * decimal separator has to be '.'
154 double ImpSvNumberInputScan::StringToDouble( std::u16string_view aStr, bool bForceFraction )
156 std::unique_ptr<char[]> bufInHeap;
157 constexpr int bufOnStackSize = 256;
158 char bufOnStack[bufOnStackSize];
159 char* buf = bufOnStack;
160 const sal_Int32 bufsize = aStr.size() + (bForceFraction ? 2 : 1);
161 if (bufsize > bufOnStackSize)
163 bufInHeap = std::make_unique<char[]>(bufsize);
164 buf = bufInHeap.get();
166 char* p = buf;
167 if (bForceFraction)
168 *p++ = '.';
169 for (size_t nPos = 0; nPos < aStr.size(); ++nPos)
171 sal_Unicode c = aStr[nPos];
172 if (c == '.' || (c >= '0' && c <= '9'))
173 *p++ = static_cast<char>(c);
174 else
175 break;
177 *p = '\0';
179 return strtod_nolocale(buf, nullptr);
182 namespace {
185 * Splits up the input into numbers and strings for further processing
186 * (by the Turing machine).
188 * Starting state = GetChar
189 * ---------------+-------------------+-----------------------------+---------------
190 * Old State | Character read | Event | New state
191 * ---------------+-------------------+-----------------------------+---------------
192 * GetChar | Number | Symbol = Character | GetValue
193 * | Else | Symbol = Character | GetString
194 * ---------------|-------------------+-----------------------------+---------------
195 * GetValue | Number | Symbol = Symbol + Character | GetValue
196 * | Else | Dec(CharPos) | Stop
197 * ---------------+-------------------+-----------------------------+---------------
198 * GetString | Number | Dec(CharPos) | Stop
199 * | Else | Symbol = Symbol + Character | GetString
200 * ---------------+-------------------+-----------------------------+---------------
202 enum ScanState // States of the Turing machine
204 SsStop = 0,
205 SsStart = 1,
206 SsGetValue = 2,
207 SsGetString = 3
212 bool ImpSvNumberInputScan::NextNumberStringSymbol( const sal_Unicode*& pStr,
213 OUString& rSymbol )
215 bool isNumber = false;
216 sal_Unicode cToken;
217 ScanState eState = SsStart;
218 const sal_Unicode* pHere = pStr;
219 sal_Int32 nChars = 0;
221 for (;;)
223 cToken = *pHere;
224 if (cToken == 0 || eState == SsStop)
225 break;
226 pHere++;
227 switch (eState)
229 case SsStart:
230 if ( rtl::isAsciiDigit( cToken ) )
232 eState = SsGetValue;
233 isNumber = true;
235 else
237 eState = SsGetString;
239 nChars++;
240 break;
241 case SsGetValue:
242 if ( rtl::isAsciiDigit( cToken ) )
244 nChars++;
246 else
248 eState = SsStop;
249 pHere--;
251 break;
252 case SsGetString:
253 if ( !rtl::isAsciiDigit( cToken ) )
255 nChars++;
257 else
259 eState = SsStop;
260 pHere--;
262 break;
263 default:
264 break;
265 } // switch
266 } // while
268 if ( nChars )
270 rSymbol = OUString( pStr, nChars );
272 else
274 rSymbol.clear();
277 pStr = pHere;
279 return isNumber;
283 // FIXME: should be grouping; it is only used though in case nStringsCnt is
284 // near SV_MAX_COUNT_INPUT_STRINGS, in NumberStringDivision().
286 bool ImpSvNumberInputScan::SkipThousands( const sal_Unicode*& pStr,
287 OUString& rSymbol ) const
289 bool res = false;
290 OUStringBuffer sBuff(rSymbol);
291 sal_Unicode cToken;
292 const OUString& rThSep = mrCurrentLanguageData.GetNumThousandSep();
293 const sal_Unicode* pHere = pStr;
294 ScanState eState = SsStart;
295 sal_Int32 nCounter = 0; // counts 3 digits
297 for (;;)
299 cToken = *pHere;
300 if (cToken == 0 || eState == SsStop)
301 break;
302 pHere++;
303 switch (eState)
305 case SsStart:
306 if ( StringPtrContains( rThSep, pHere-1, 0 ) )
308 nCounter = 0;
309 eState = SsGetValue;
310 pHere += rThSep.getLength() - 1;
312 else
314 eState = SsStop;
315 pHere--;
317 break;
318 case SsGetValue:
319 if ( rtl::isAsciiDigit( cToken ) )
321 sBuff.append(cToken);
322 nCounter++;
323 if (nCounter == 3)
325 eState = SsStart;
326 res = true; // .000 combination found
329 else
331 eState = SsStop;
332 pHere--;
334 break;
335 default:
336 break;
337 } // switch
338 } // while
340 if (eState == SsGetValue) // break with less than 3 digits
342 if ( nCounter )
344 sBuff.remove( sBuff.getLength() - nCounter, nCounter );
346 pHere -= nCounter + rThSep.getLength(); // put back ThSep also
348 rSymbol = sBuff.makeStringAndClear();
349 pStr = pHere;
351 return res;
355 void ImpSvNumberInputScan::NumberStringDivision( const OUString& rString )
357 const sal_Unicode* pStr = rString.getStr();
358 const sal_Unicode* const pEnd = pStr + rString.getLength();
359 while ( pStr < pEnd && nStringsCnt < SV_MAX_COUNT_INPUT_STRINGS )
361 if ( NextNumberStringSymbol( pStr, sStrArray[nStringsCnt] ) )
362 { // Number
363 IsNum[nStringsCnt] = true;
364 nNums[nNumericsCnt] = nStringsCnt;
365 nNumericsCnt++;
366 if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS - 7 &&
367 nPosThousandString == 0) // Only once
369 if ( SkipThousands( pStr, sStrArray[nStringsCnt] ) )
371 nPosThousandString = nStringsCnt;
375 else
377 IsNum[nStringsCnt] = false;
379 nStringsCnt++;
385 * Whether rString contains rWhat at nPos
387 bool ImpSvNumberInputScan::StringContainsImpl( const OUString& rWhat,
388 const OUString& rString, sal_Int32 nPos )
390 if ( nPos + rWhat.getLength() <= rString.getLength() )
392 return StringPtrContainsImpl( rWhat, rString.getStr(), nPos );
394 return false;
399 * Whether pString contains rWhat at nPos
401 bool ImpSvNumberInputScan::StringPtrContainsImpl( const OUString& rWhat,
402 const sal_Unicode* pString, sal_Int32 nPos )
404 if ( rWhat.isEmpty() )
406 return false;
408 const sal_Unicode* pWhat = rWhat.getStr();
409 const sal_Unicode* const pEnd = pWhat + rWhat.getLength();
410 const sal_Unicode* pStr = pString + nPos;
411 while ( pWhat < pEnd )
413 if ( *pWhat != *pStr )
415 return false;
417 pWhat++;
418 pStr++;
420 return true;
425 * Whether rString contains word rWhat at nPos
427 bool ImpSvNumberInputScan::StringContainsWord( const OUString& rWhat,
428 const OUString& rString, sal_Int32 nPos ) const
430 if (rWhat.isEmpty() || rString.getLength() < nPos + rWhat.getLength())
431 return false;
433 if (StringPtrContainsImpl( rWhat, rString.getStr(), nPos))
435 nPos += rWhat.getLength();
436 if (nPos == rString.getLength())
437 return true; // word at end of string
439 /* TODO: we COULD invoke bells and whistles word break iterator to find
440 * the next boundary, but really ... this is called for date input, so
441 * how many languages do not separate the day and month names in some
442 * form? */
444 // Check simple ASCII first before invoking i18n or anything else.
445 const sal_Unicode c = rString[nPos];
447 // Common separating ASCII characters in date context.
448 switch (c)
450 case ' ':
451 case '-':
452 case '.':
453 case '/':
454 return true;
455 default:
456 ; // nothing
459 if (rtl::isAsciiAlphanumeric( c ))
460 return false; // Alpha or numeric is not word gap.
462 sal_Int32 nIndex = nPos;
463 rString.iterateCodePoints( &nIndex);
464 if (nPos+1 < nIndex)
465 return true; // Surrogate, assume these to be new words.
467 const sal_Int32 nType = mrCurrentLanguageData.GetCharClass()->getCharacterType( rString, nPos);
468 using namespace ::com::sun::star::i18n;
470 if ((nType & (KCharacterType::UPPER | KCharacterType::LOWER | KCharacterType::DIGIT)) != 0)
471 return false; // Alpha or numeric is not word gap.
473 if (nType & KCharacterType::LETTER)
474 return true; // Letter other than alpha is new word. (Is it?)
476 return true; // Catch all remaining as gap until we know better.
479 return false;
484 * Skips the supplied char
486 inline bool ImpSvNumberInputScan::SkipChar( sal_Unicode c, std::u16string_view rString,
487 sal_Int32& nPos )
489 if ((nPos < static_cast<sal_Int32>(rString.size())) && (rString[nPos] == c))
491 nPos++;
492 return true;
494 return false;
499 * Skips blanks
501 inline bool ImpSvNumberInputScan::SkipBlanks( const OUString& rString,
502 sal_Int32& nPos )
504 sal_Int32 nHere = nPos;
505 if ( nPos < rString.getLength() )
507 const sal_Unicode* p = rString.getStr() + nPos;
508 while ( *p == ' ' || *p == cNoBreakSpace || *p == cNarrowNoBreakSpace )
510 nPos++;
511 p++;
514 return nHere < nPos;
519 * jump over rWhat in rString at nPos
521 inline bool ImpSvNumberInputScan::SkipString( const OUString& rWhat,
522 const OUString& rString, sal_Int32& nPos )
524 if ( StringContains( rWhat, rString, nPos ) )
526 nPos = nPos + rWhat.getLength();
527 return true;
529 return false;
534 * Recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping
536 inline bool ImpSvNumberInputScan::GetThousandSep( std::u16string_view rString,
537 sal_Int32& nPos,
538 sal_uInt16 nStringPos ) const
540 const OUString& rSep = mrCurrentLanguageData.GetNumThousandSep();
541 // Is it an ordinary space instead of a no-break space?
542 bool bSpaceBreak = (rSep[0] == cNoBreakSpace || rSep[0] == cNarrowNoBreakSpace) &&
543 rString[0] == u' ' &&
544 rSep.getLength() == 1 && rString.size() == 1;
545 if (!((rString == rSep || bSpaceBreak) && // nothing else
546 nStringPos < nStringsCnt - 1 && // safety first!
547 IsNum[ nStringPos + 1 ] )) // number follows
549 return false; // no? => out
552 utl::DigitGroupingIterator aGrouping( mrCurrentLanguageData.GetLocaleData()->getDigitGrouping());
553 // Match ,### in {3} or ,## in {3,2}
554 /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or
555 * ,##,### and to match ,### in {3,2} only if it's the last. However,
556 * currently there is no track kept where group separators occur. In {3,2}
557 * #,###,### and #,##,## would be valid input, which maybe isn't even bad
558 * for #,###,###. Other combinations such as #,###,## maybe not. */
559 sal_Int32 nLen = sStrArray[ nStringPos + 1 ].getLength();
560 if (nLen == aGrouping.get() || // with 3 (or so) digits
561 nLen == aGrouping.advance().get() || // or with 2 (or 3 or so) digits
562 nPosThousandString == nStringPos + 1 ) // or concatenated
564 nPos = nPos + rSep.getLength();
565 return true;
567 return false;
572 * Conversion of text to logical value
573 * "true" => 1:
574 * "false"=> -1:
575 * else => 0:
577 short ImpSvNumberInputScan::GetLogical( std::u16string_view rString ) const
579 short res;
581 const ImpSvNumberformatScan* pFS = mrCurrentLanguageData.GetFormatScanner();
582 if ( rString == pFS->GetTrueString() )
584 res = 1;
586 else if ( rString == pFS->GetFalseString() )
588 res = -1;
590 else
592 res = 0;
594 return res;
599 * Converts a string containing a month name (JAN, January) at nPos into the
600 * month number (negative if abbreviated), returns 0 if nothing found
602 short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
604 short res = 0; // no month found
606 if (rString.getLength() > nPos) // only if needed
608 if ( !bTextInitialized )
610 InitText();
612 sal_Int16 nMonths = mrCurrentLanguageData.GetCalendar()->getNumberOfMonthsInYear();
613 for ( sal_Int16 i = 0; i < nMonths; i++ )
615 if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveMonthText[i], rString, nPos ) )
616 { // genitive full names first
617 nPos = nPos + pUpperGenitiveMonthText[i].getLength();
618 res = i + 1;
619 break; // for
621 else if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveAbbrevMonthText[i], rString, nPos ) )
622 { // genitive abbreviated
623 nPos = nPos + pUpperGenitiveAbbrevMonthText[i].getLength();
624 res = sal::static_int_cast< short >(-(i+1)); // negative
625 break; // for
627 else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveMonthText[i], rString, nPos ) )
628 { // partitive full names
629 nPos = nPos + pUpperPartitiveMonthText[i].getLength();
630 res = i+1;
631 break; // for
633 else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveAbbrevMonthText[i], rString, nPos ) )
634 { // partitive abbreviated
635 nPos = nPos + pUpperPartitiveAbbrevMonthText[i].getLength();
636 res = sal::static_int_cast< short >(-(i+1)); // negative
637 break; // for
639 else if ( StringContainsWord( pUpperMonthText[i], rString, nPos ) )
640 { // noun full names
641 nPos = nPos + pUpperMonthText[i].getLength();
642 res = i+1;
643 break; // for
645 else if ( StringContainsWord( pUpperAbbrevMonthText[i], rString, nPos ) )
646 { // noun abbreviated
647 nPos = nPos + pUpperAbbrevMonthText[i].getLength();
648 res = sal::static_int_cast< short >(-(i+1)); // negative
649 break; // for
651 else if (i == 2 && mrCurrentLanguageData.GetLanguageTag().getLanguage() == "de")
653 if (pUpperAbbrevMonthText[i] == u"M\u00C4R" && StringContainsWord( "MRZ", rString, nPos))
654 { // Accept MRZ for MÄR
655 nPos = nPos + 3;
656 res = sal::static_int_cast< short >(-(i+1)); // negative
657 break; // for
659 else if (pUpperAbbrevMonthText[i] == "MRZ" && StringContainsWord( u"M\u00C4R"_ustr, rString, nPos))
660 { // And vice versa, accept MÄR for MRZ
661 nPos = nPos + 3;
662 res = sal::static_int_cast< short >(-(i+1)); // negative
663 break; // for
666 else if (i == 8)
668 // This assumes the weirdness is applicable to all locales.
669 // It is the case for at least en-* and de-* locales.
670 if (pUpperAbbrevMonthText[i] == "SEPT" && StringContainsWord( "SEP", rString, nPos))
671 { // #102136# The correct English form of month September abbreviated is
672 // SEPT, but almost every data contains SEP instead.
673 nPos = nPos + 3;
674 res = sal::static_int_cast< short >(-(i+1)); // negative
675 break; // for
677 else if (pUpperAbbrevMonthText[i] == "SEP" && StringContainsWord( "SEPT", rString, nPos))
678 { // And vice versa, accept SEPT for SEP
679 nPos = nPos + 4;
680 res = sal::static_int_cast< short >(-(i+1)); // negative
681 break; // for
685 if (!res)
687 // Brutal hack for German locales that know "Januar" or "Jänner".
688 /* TODO: add alternative month names to locale data? if there are
689 * more languages... */
690 const LanguageTag& rLanguageTag = mrCurrentLanguageData.GetLanguageTag();
691 if (rLanguageTag.getLanguage() == "de")
693 if (rLanguageTag.getCountry() == "AT")
695 // Locale data has Jänner/Jän
696 assert(pUpperMonthText[0] == u"J\u00C4NNER");
697 if (StringContainsWord( "JANUAR", rString, nPos))
699 nPos += 6;
700 res = 1;
702 else if (StringContainsWord( "JAN", rString, nPos))
704 nPos += 3;
705 res = -1;
708 else
710 // Locale data has Januar/Jan
711 assert(pUpperMonthText[0] == "JANUAR");
712 if (StringContainsWord( u"J\u00C4NNER"_ustr, rString, nPos))
714 nPos += 6;
715 res = 1;
717 else if (StringContainsWord( u"J\u00C4N"_ustr, rString, nPos))
719 nPos += 3;
720 res = -1;
727 return res;
732 * Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the
733 * DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found
735 int ImpSvNumberInputScan::GetDayOfWeek( const OUString& rString, sal_Int32& nPos )
737 int res = 0; // no day found
739 if (rString.getLength() > nPos) // only if needed
741 if ( !bTextInitialized )
743 InitText();
745 sal_Int16 nDays = mrCurrentLanguageData.GetCalendar()->getNumberOfDaysInWeek();
746 for ( sal_Int16 i = 0; i < nDays; i++ )
748 if ( StringContainsWord( pUpperDayText[i], rString, nPos ) )
749 { // full names first
750 nPos = nPos + pUpperDayText[i].getLength();
751 res = i + 1;
752 break; // for
754 if ( StringContainsWord( pUpperAbbrevDayText[i], rString, nPos ) )
755 { // abbreviated
756 nPos = nPos + pUpperAbbrevDayText[i].getLength();
757 res = -(i + 1); // negative
758 break; // for
763 return res;
768 * Reading a currency symbol
769 * '$' => true
770 * else => false
772 bool ImpSvNumberInputScan::GetCurrency( const OUString& rString, sal_Int32& nPos )
774 if ( rString.getLength() > nPos )
776 if ( !aUpperCurrSymbol.getLength() )
777 { // If no format specified the currency of the currently active locale.
778 LanguageType eLang = (mpFormat ? mpFormat->GetLanguage() :
779 mrCurrentLanguageData.GetLocaleData()->getLanguageTag().getLanguageType());
780 aUpperCurrSymbol = mrCurrentLanguageData.GetCharClass()->uppercase(
781 SvNumberFormatter::GetCurrencyEntry( eLang ).GetSymbol() );
783 if ( StringContains( aUpperCurrSymbol, rString, nPos ) )
785 nPos = nPos + aUpperCurrSymbol.getLength();
786 return true;
788 if ( mpFormat )
790 OUString aSymbol, aExtension;
791 if ( mpFormat->GetNewCurrencySymbol( aSymbol, aExtension ) )
793 if ( aSymbol.getLength() <= rString.getLength() - nPos )
795 aSymbol = mrCurrentLanguageData.GetCharClass()->uppercase(aSymbol);
796 if ( StringContains( aSymbol, rString, nPos ) )
798 nPos = nPos + aSymbol.getLength();
799 return true;
806 return false;
811 * Reading the time period specifier (AM/PM) for the 12 hour clock
813 * Returns:
814 * "AM" or "PM" => true
815 * else => false
817 * nAmPos:
818 * "AM" => 1
819 * "PM" => -1
820 * else => 0
822 bool ImpSvNumberInputScan::GetTimeAmPm( const OUString& rString, sal_Int32& nPos )
825 if ( rString.getLength() > nPos )
827 const CharClass* pChr = mrCurrentLanguageData.GetCharClass();
828 const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();
829 if ( StringContains( pChr->uppercase( pLoc->getTimeAM() ), rString, nPos ) )
831 nAmPm = 1;
832 nPos = nPos + pLoc->getTimeAM().getLength();
833 return true;
835 else if ( StringContains( pChr->uppercase( pLoc->getTimePM() ), rString, nPos ) )
837 nAmPm = -1;
838 nPos = nPos + pLoc->getTimePM().getLength();
839 return true;
843 return false;
848 * Read a decimal separator (',')
849 * ',' => true
850 * else => false
852 inline bool ImpSvNumberInputScan::GetDecSep( std::u16string_view rString, sal_Int32& nPos ) const
854 if ( static_cast<sal_Int32>(rString.size()) > nPos )
856 const OUString& rSep = mrCurrentLanguageData.GetNumDecimalSep();
857 if ( o3tl::starts_with(rString.substr(nPos), rSep) )
859 nPos = nPos + rSep.getLength();
860 return true;
862 const OUString& rSepAlt = mrCurrentLanguageData.GetNumDecimalSepAlt();
863 if ( !rSepAlt.isEmpty() && o3tl::starts_with(rString.substr(nPos), rSepAlt) )
865 nPos = nPos + rSepAlt.getLength();
866 return true;
869 return false;
874 * Reading a hundredth seconds separator
876 inline bool ImpSvNumberInputScan::GetTime100SecSep( std::u16string_view rString, sal_Int32& nPos ) const
878 if ( static_cast<sal_Int32>(rString.size()) > nPos )
880 if (bIso8601Tsep)
882 // ISO 8601 specifies both '.' dot and ',' comma as fractional
883 // separator.
884 if (rString[nPos] == '.' || rString[nPos] == ',')
886 ++nPos;
887 return true;
890 // Even in an otherwise ISO 8601 string be lenient and accept the
891 // locale defined separator.
892 const OUString& rSep = mrCurrentLanguageData.GetLocaleData()->getTime100SecSep();
893 if ( o3tl::starts_with(rString.substr(nPos), rSep))
895 nPos = nPos + rSep.getLength();
896 return true;
899 return false;
904 * Read a sign including brackets
905 * '+' => 1
906 * '-' => -1
907 * u'−' => -1
908 * '(' => -1, bNegCheck = 1
909 * else => 0
911 int ImpSvNumberInputScan::GetSign( std::u16string_view rString, sal_Int32& nPos )
913 if (static_cast<sal_Int32>(rString.size()) > nPos)
914 switch (rString[ nPos ])
916 case '+':
917 nPos++;
918 return 1;
919 case '(': // '(' similar to '-' ?!?
920 bNegCheck = true;
921 [[fallthrough]];
922 case '-':
923 // tdf#117037 - unicode minus (0x2212)
924 case u'−':
925 nPos++;
926 return -1;
927 default:
928 break;
931 return 0;
936 * Read a sign with an exponent
937 * '+' => 1
938 * '-' => -1
939 * else => 0
941 short ImpSvNumberInputScan::GetESign( std::u16string_view rString, sal_Int32& nPos )
943 if (static_cast<sal_Int32>(rString.size()) > nPos)
945 switch (rString[nPos])
947 case '+':
948 nPos++;
949 return 1;
950 case '-':
951 nPos++;
952 return -1;
953 default:
954 break;
957 return 0;
962 * i counts string portions, j counts numbers thereof.
963 * It should had been called SkipNumber instead.
965 inline bool ImpSvNumberInputScan::GetNextNumber( sal_uInt16& i, sal_uInt16& j ) const
967 if ( i < nStringsCnt && IsNum[i] )
969 j++;
970 i++;
971 return true;
973 return false;
977 bool ImpSvNumberInputScan::GetTimeRef( double& fOutNumber,
978 sal_uInt16 nIndex, // j-value of the first numeric time part of input, default 0
979 sal_uInt16 nCnt, // count of numeric time parts
980 SvNumInputOptions eInputOptions
981 ) const
983 bool bRet = true;
984 sal_Int32 nHour;
985 sal_Int32 nMinute = 0;
986 sal_Int32 nSecond = 0;
987 double fSecond100 = 0.0;
988 sal_uInt16 nStartIndex = nIndex;
990 if (nDecPos == 2 && (nCnt == 3 || nCnt == 2)) // 20:45.5 or 45.5
992 nHour = 0;
994 else if (mpFormat && nDecPos == 0 && nCnt == 2 && mpFormat->IsMinuteSecondFormat())
996 // Input on MM:SS format, instead of doing HH:MM:00
997 nHour = 0;
999 else if (nIndex - nStartIndex < nCnt)
1001 const OUString& rValStr = sStrArray[nNums[nIndex++]];
1002 nHour = rValStr.toInt32();
1003 if (nHour == 0 && rValStr != "0" && rValStr != "00")
1004 bRet = false; // overflow -> Text
1006 else
1008 nHour = 0;
1009 bRet = false;
1010 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetTimeRef: bad number index");
1013 // 0:123 or 0:0:123 or 0:123:59 is valid
1014 bool bAllowDuration = (nHour == 0 && !nAmPm);
1016 if (nAmPm && nHour > 12) // not a valid AM/PM clock time
1018 bRet = false;
1020 else if (nAmPm == -1 && nHour != 12) // PM
1022 nHour += 12;
1024 else if (nAmPm == 1 && nHour == 12) // 12 AM
1026 nHour = 0;
1029 if (nDecPos == 2 && nCnt == 2) // 45.5
1031 nMinute = 0;
1033 else if (nIndex - nStartIndex < nCnt)
1035 const OUString& rValStr = sStrArray[nNums[nIndex++]];
1036 nMinute = rValStr.toInt32();
1037 if (nMinute == 0 && rValStr != "0" && rValStr != "00")
1038 bRet = false; // overflow -> Text
1039 if (!(eInputOptions & SvNumInputOptions::LAX_TIME) && !bAllowDuration
1040 && nIndex > 1 && nMinute > 59)
1041 bRet = false; // 1:60 or 1:123 is invalid, 123:1 or 0:123 is valid
1042 if (bAllowDuration)
1043 bAllowDuration = (nMinute == 0);
1045 if (nIndex - nStartIndex < nCnt)
1047 const OUString& rValStr = sStrArray[nNums[nIndex++]];
1048 nSecond = rValStr.toInt32();
1049 if (nSecond == 0 && rValStr != "0" && rValStr != "00")
1050 bRet = false; // overflow -> Text
1051 if (!(eInputOptions & SvNumInputOptions::LAX_TIME) && !bAllowDuration
1052 && nIndex > 1 && nSecond > 59 && !(nHour == 23 && nMinute == 59 && nSecond == 60))
1053 bRet = false; // 1:60 or 1:123 or 1:1:123 is invalid, 123:1 or 123:1:1 or 0:0:123 is valid, or leap second
1055 if (nIndex - nStartIndex < nCnt)
1057 fSecond100 = StringToDouble( sStrArray[nNums[nIndex]], true );
1059 fOutNumber = (static_cast<double>(nHour)*3600 +
1060 static_cast<double>(nMinute)*60 +
1061 static_cast<double>(nSecond) +
1062 fSecond100)/86400.0;
1063 return bRet;
1067 sal_uInt16 ImpSvNumberInputScan::ImplGetDay( sal_uInt16 nIndex ) const
1069 sal_uInt16 nRes = 0;
1071 if (sStrArray[nNums[nIndex]].getLength() <= 2)
1073 sal_uInt16 nNum = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1074 if (nNum <= 31)
1076 nRes = nNum;
1080 return nRes;
1084 sal_uInt16 ImpSvNumberInputScan::ImplGetMonth( sal_uInt16 nIndex ) const
1086 // Preset invalid month number
1087 sal_uInt16 nRes = mrCurrentLanguageData.GetCalendar()->getNumberOfMonthsInYear();
1089 if (sStrArray[nNums[nIndex]].getLength() <= 2)
1091 sal_uInt16 nNum = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1092 if ( 0 < nNum && nNum <= nRes )
1094 nRes = nNum - 1; // zero based for CalendarFieldIndex::MONTH
1098 return nRes;
1103 * 30 -> 1930, 29 -> 2029, or 56 -> 1756, 55 -> 1855, ...
1105 sal_uInt16 ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex )
1107 sal_uInt16 nYear = 0;
1109 sal_Int32 nLen = sStrArray[nNums[nIndex]].getLength();
1110 // 16-bit integer year width can have 5 digits, allow for one additional
1111 // leading zero as convention.
1112 if (nLen <= 6)
1114 nYear = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1115 // A year in another, not Gregorian CE era is never expanded.
1116 // A year < 100 entered with at least 3 digits with leading 0 is taken
1117 // as is without expansion.
1118 if (mbEraCE == kDefaultEra && nYear < 100 && nLen < 3)
1120 nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 );
1124 return nYear;
1128 bool ImpSvNumberInputScan::MayBeIso8601()
1130 if (nMayBeIso8601 == 0)
1132 nMayBeIso8601 = 1;
1133 sal_Int32 nLen = ((nNumericsCnt >= 1 && nNums[0] < nStringsCnt) ? sStrArray[nNums[0]].getLength() : 0);
1134 if (nLen)
1136 sal_Int32 n;
1137 if (nNumericsCnt >= 3 && nNums[2] < nStringsCnt &&
1138 sStrArray[nNums[0]+1] == "-" && // separator year-month
1139 (n = sStrArray[nNums[1]].toInt32()) >= 1 && n <= 12 && // month
1140 sStrArray[nNums[1]+1] == "-" && // separator month-day
1141 (n = sStrArray[nNums[2]].toInt32()) >= 1 && n <= 31) // day
1143 // Year (nNums[0]) value not checked, may be anything, but
1144 // length (number of digits) is checked.
1145 nMayBeIso8601 = (nLen >= 4 ? 4 : (nLen == 3 ? 3 : (nLen > 0 ? 2 : 1)));
1149 return nMayBeIso8601 > 1;
1153 bool ImpSvNumberInputScan::CanForceToIso8601( DateOrder eDateOrder )
1155 int nCanForceToIso8601 = 0;
1156 if (!MayBeIso8601())
1158 return false;
1160 else if (nMayBeIso8601 >= 3)
1162 return true; // at least 3 digits in year
1164 else
1166 if (eDateOrder == DateOrder::Invalid)
1168 // As if any of the cases below can be applied, but only if a
1169 // locale dependent date pattern was not matched.
1170 if ((GetDatePatternNumbers() == nNumericsCnt) && IsDatePatternNumberOfType(0,'Y'))
1171 return false;
1172 eDateOrder = GetDateOrder();
1175 nCanForceToIso8601 = 1;
1178 sal_Int32 n;
1179 switch (eDateOrder)
1181 case DateOrder::DMY: // "day" value out of range => ISO 8601 year
1182 n = sStrArray[nNums[0]].toInt32();
1183 if (n < 1 || n > 31)
1185 nCanForceToIso8601 = 2;
1187 break;
1188 case DateOrder::MDY: // "month" value out of range => ISO 8601 year
1189 n = sStrArray[nNums[0]].toInt32();
1190 if (n < 1 || n > 12)
1192 nCanForceToIso8601 = 2;
1194 break;
1195 case DateOrder::YMD: // always possible
1196 nCanForceToIso8601 = 2;
1197 break;
1198 default: break;
1200 return nCanForceToIso8601 > 1;
1204 bool ImpSvNumberInputScan::IsAcceptableIso8601()
1206 if (mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE))
1208 switch (mrCurrentLanguageData.GetEvalDateFormat())
1210 case NF_EVALDATEFORMAT_INTL:
1211 return CanForceToIso8601( GetDateOrder());
1212 case NF_EVALDATEFORMAT_FORMAT:
1213 return CanForceToIso8601( mpFormat->GetDateOrder());
1214 default:
1215 return CanForceToIso8601( GetDateOrder()) || CanForceToIso8601( mpFormat->GetDateOrder());
1218 return CanForceToIso8601( GetDateOrder());
1222 bool ImpSvNumberInputScan::MayBeMonthDate()
1224 if (nMayBeMonthDate == 0)
1226 nMayBeMonthDate = 1;
1227 if (nNumericsCnt >= 2 && nNums[1] < nStringsCnt)
1229 // "-Jan-"
1230 const OUString& rM = sStrArray[ nNums[ 0 ] + 1 ];
1231 if (rM.getLength() >= 3 && rM[0] == '-' && rM[ rM.getLength() - 1] == '-')
1233 // Check year length assuming at least 3 digits (including
1234 // leading zero). Two digit years 1..31 are out of luck here
1235 // and may be taken as day of month.
1236 bool bYear1 = (sStrArray[nNums[0]].getLength() >= 3);
1237 bool bYear2 = (sStrArray[nNums[1]].getLength() >= 3);
1238 sal_Int32 n;
1239 bool bDay1 = !bYear1;
1240 if (bDay1)
1242 n = sStrArray[nNums[0]].toInt32();
1243 bDay1 = n >= 1 && n <= 31;
1245 bool bDay2 = !bYear2;
1246 if (bDay2)
1248 n = sStrArray[nNums[1]].toInt32();
1249 bDay2 = n >= 1 && n <= 31;
1252 if (bDay1 && !bDay2)
1254 nMayBeMonthDate = 2; // dd-month-yy
1256 else if (!bDay1 && bDay2)
1258 nMayBeMonthDate = 3; // yy-month-dd
1260 else if (bDay1 && bDay2)
1262 // Ambiguous ##-MMM-## date, but some big vendor's database
1263 // reports write this crap, assume this always to be
1264 nMayBeMonthDate = 2; // dd-month-yy
1269 return nMayBeMonthDate > 1;
1273 /** If a string is a separator plus '-' minus sign preceding a 'Y' year in
1274 a date pattern at position nPat.
1276 static bool lcl_IsSignedYearSep( std::u16string_view rStr, std::u16string_view rPat, sal_Int32 nPat )
1278 bool bOk = false;
1279 sal_Int32 nLen = rStr.size();
1280 if (nLen > 1 && rStr[nLen-1] == '-')
1282 --nLen;
1283 if (nPat + nLen < static_cast<sal_Int32>(rPat.size()) && rPat[nPat+nLen] == 'Y')
1285 // Signed year is possible.
1286 bOk = (rPat.find( rStr.substr( 0, nLen), nPat) == static_cast<size_t>(nPat));
1289 return bOk;
1293 /** Length of separator usually is 1 but theoretically could be anything. */
1294 static sal_Int32 lcl_getPatternSeparatorLength( std::u16string_view rPat, sal_Int32 nPat )
1296 sal_Int32 nSep = nPat;
1297 sal_Unicode c;
1298 while (nSep < static_cast<sal_Int32>(rPat.size()) && (c = rPat[nSep]) != 'D' && c != 'M' && c != 'Y')
1299 ++nSep;
1300 return nSep - nPat;
1304 bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
1306 if (nAcceptedDatePattern >= -1)
1308 return (nAcceptedDatePattern >= 0);
1310 if (!nNumericsCnt)
1312 nAcceptedDatePattern = -1;
1314 else if (!sDateAcceptancePatterns.hasElements())
1316 // The current locale is the format's locale, if a format is present.
1317 const NfEvalDateFormat eEDF = mrCurrentLanguageData.GetEvalDateFormat();
1318 if (!mpFormat || eEDF == NF_EVALDATEFORMAT_FORMAT || mpFormat->GetLanguage() == mrCurrentLanguageData.GetIniLanguage())
1320 sDateAcceptancePatterns = mrCurrentLanguageData.GetLocaleData()->getDateAcceptancePatterns();
1322 else
1324 OnDemandLocaleDataWrapper& xLocaleData = mrCurrentLanguageData.GetOnDemandLocaleDataWrapper(
1325 SvNFLanguageData::InputScannerPrivateAccess());
1326 const LanguageTag aSaveLocale( xLocaleData->getLanguageTag() );
1327 assert(mpFormat->GetLanguage() == aSaveLocale.getLanguageType()); // prerequisite
1328 // Obtain formatter's locale's (e.g. system) patterns.
1329 xLocaleData.changeLocale( LanguageTag( mrCurrentLanguageData.GetIniLanguage()));
1330 const css::uno::Sequence<OUString> aLocalePatterns( xLocaleData->getDateAcceptancePatterns());
1331 // Reset to format's locale.
1332 xLocaleData.changeLocale( aSaveLocale);
1333 // When concatenating don't care about duplicates, combining
1334 // weeding those out reallocs yet another time and probably doesn't
1335 // take less time than looping over two additional patterns below...
1336 switch (eEDF)
1338 case NF_EVALDATEFORMAT_FORMAT:
1339 assert(!"shouldn't reach here");
1340 break;
1341 case NF_EVALDATEFORMAT_INTL:
1342 sDateAcceptancePatterns = aLocalePatterns;
1343 break;
1344 case NF_EVALDATEFORMAT_INTL_FORMAT:
1345 sDateAcceptancePatterns = comphelper::concatSequences(
1346 aLocalePatterns,
1347 xLocaleData->getDateAcceptancePatterns());
1348 break;
1349 case NF_EVALDATEFORMAT_FORMAT_INTL:
1350 sDateAcceptancePatterns = comphelper::concatSequences(
1351 xLocaleData->getDateAcceptancePatterns(),
1352 aLocalePatterns);
1353 break;
1356 SAL_WARN_IF( !sDateAcceptancePatterns.hasElements(), "svl.numbers", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
1357 nAcceptedDatePattern = (sDateAcceptancePatterns.hasElements() ? -2 : -1);
1360 if (nAcceptedDatePattern == -1)
1362 return false;
1364 nDatePatternStart = nStartPatternAt; // remember start particle
1366 const sal_Int32 nMonthsInYear = mrCurrentLanguageData.GetCalendar()->getNumberOfMonthsInYear();
1368 for (sal_Int32 nPattern=0; nPattern < sDateAcceptancePatterns.getLength(); ++nPattern)
1370 const OUString& rPat = sDateAcceptancePatterns[nPattern];
1371 if (rPat.getLength() == 3)
1373 // Ignore a pattern that would match numeric input with decimal
1374 // separator. It may had been read from configuration or resulted
1375 // from the locales' patterns concatenation above.
1376 if ( rPat[1] == mrCurrentLanguageData.GetLocaleData()->getNumDecimalSep().toChar()
1377 || rPat[1] == mrCurrentLanguageData.GetLocaleData()->getNumDecimalSepAlt().toChar())
1379 SAL_WARN("svl.numbers", "ignoring date acceptance pattern with decimal separator ambiguity: " << rPat);
1380 continue; // for, next pattern
1383 sal_uInt16 nNext = nDatePatternStart;
1384 nDatePatternNumbers = 0;
1385 bool bOk = true;
1386 sal_Int32 nPat = 0;
1387 for ( ; nPat < rPat.getLength() && bOk && nNext < nStringsCnt; ++nPat, ++nNext)
1389 const sal_Unicode c = rPat[nPat];
1390 switch (c)
1392 case 'Y':
1393 case 'M':
1394 case 'D':
1395 bOk = IsNum[nNext];
1396 if (bOk && (c == 'M' || c == 'D'))
1398 // Check the D and M cases for plausibility. This also
1399 // prevents recognition of date instead of number with a
1400 // numeric group input if date separator is identical to
1401 // group separator, for example with D.M as a pattern and
1402 // #.### as a group.
1403 sal_Int32 nMaxLen, nMaxVal;
1404 switch (c)
1406 case 'M':
1407 nMaxLen = 2;
1408 nMaxVal = nMonthsInYear;
1409 break;
1410 case 'D':
1411 nMaxLen = 2;
1412 nMaxVal = 31;
1413 break;
1414 default:
1415 // This merely exists against
1416 // -Werror=maybe-uninitialized, which is nonsense
1417 // after the (c == 'M' || c == 'D') check above,
1418 // but ...
1419 nMaxLen = 2;
1420 nMaxVal = 31;
1422 bOk = (sStrArray[nNext].getLength() <= nMaxLen);
1423 if (bOk)
1425 sal_Int32 nNum = sStrArray[nNext].toInt32();
1426 bOk = (1 <= nNum && nNum <= nMaxVal);
1429 if (bOk)
1430 ++nDatePatternNumbers;
1431 break;
1432 default:
1433 bOk = !IsNum[nNext];
1434 if (bOk)
1436 const sal_Int32 nSepLen = lcl_getPatternSeparatorLength( rPat, nPat);
1437 // Non-numeric input must match separator exactly to be
1438 // accepted as such.
1439 const sal_Int32 nLen = sStrArray[nNext].getLength();
1440 bOk = (nLen == nSepLen && rPat.indexOf( sStrArray[nNext], nPat) == nPat);
1441 if (bOk)
1443 nPat += nLen - 1;
1445 else if ((bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat)))
1447 nPat += nLen - 2;
1449 else if (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' ')
1451 using namespace comphelper::string;
1452 // Trailing blanks in input.
1453 OUStringBuffer aBuf(sStrArray[nNext]);
1454 aBuf.stripEnd();
1455 // Expand again in case of pattern "M. D. " and
1456 // input "M. D. ", maybe fetched far, but...
1457 padToLength(aBuf, rPat.getLength() - nPat, ' ');
1458 bOk = (rPat.indexOf( aBuf, nPat) == nPat);
1459 if (bOk)
1461 nPat += aBuf.getLength() - 1;
1465 break;
1468 if (bOk)
1470 // Check for trailing characters mismatch.
1471 if (nNext < nStringsCnt)
1473 // Pattern end but not input end.
1474 // A trailing blank may be part of the current pattern input,
1475 // if pattern is "D.M." and input is "D.M. hh:mm" last was
1476 // ". ", or may be following the current pattern input, if
1477 // pattern is "D.M" and input is "D.M hh:mm" last was "M".
1478 sal_Int32 nPos = 0;
1479 sal_uInt16 nCheck;
1480 if (nPat > 0 && nNext > 0)
1482 // nPat is one behind after the for loop.
1483 sal_Int32 nPatCheck = nPat - 1;
1484 switch (rPat[nPatCheck])
1486 case 'Y':
1487 case 'M':
1488 case 'D':
1489 nCheck = nNext;
1490 break;
1491 default:
1493 nCheck = nNext - 1;
1494 // Advance position in input to match length of
1495 // non-YMD (separator) characters in pattern.
1496 sal_Unicode c;
1499 ++nPos;
1500 c = rPat[--nPatCheck];
1501 } while (c != 'Y' && c != 'M' && c != 'D' && nPatCheck > 0);
1505 else
1507 nCheck = nNext;
1509 if (!IsNum[nCheck])
1511 // Trailing (or separating if time follows) blanks are ok.
1512 // No blank and a following number is not.
1513 const bool bBlanks = SkipBlanks( sStrArray[nCheck], nPos);
1514 if (nPos == sStrArray[nCheck].getLength() && (bBlanks || !IsNum[nNext]))
1516 nAcceptedDatePattern = nPattern;
1517 return true;
1521 else if (nPat == rPat.getLength())
1523 // Input end and pattern end => match.
1524 nAcceptedDatePattern = nPattern;
1525 return true;
1527 // else Input end but not pattern end, no match.
1530 nAcceptedDatePattern = -1;
1531 return false;
1535 bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, sal_Int32 & rPos, bool & rSignedYear )
1537 // If not initialized yet start with first number, if any.
1538 if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1540 return false;
1542 if (nParticle < nDatePatternStart || nParticle >= nStringsCnt || IsNum[nParticle])
1544 return false;
1546 sal_uInt16 nNext = nDatePatternStart;
1547 const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1548 for (sal_Int32 nPat = 0; nPat < rPat.getLength() && nNext < nStringsCnt; ++nPat, ++nNext)
1550 switch (rPat[nPat])
1552 case 'Y':
1553 case 'M':
1554 case 'D':
1555 break;
1556 default:
1557 if (nNext == nParticle)
1559 const sal_Int32 nSepLen = lcl_getPatternSeparatorLength( rPat, nPat);
1560 const sal_Int32 nLen = sStrArray[nNext].getLength();
1561 bool bOk = (nLen == nSepLen && rPat.indexOf( sStrArray[nNext], nPat) == nPat);
1562 if (!bOk)
1564 bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat);
1565 if (bOk)
1566 rSignedYear = true;
1568 if (!bOk && (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' '))
1570 // The same ugly trailing blanks check as in
1571 // IsAcceptedDatePattern().
1572 using namespace comphelper::string;
1573 OUStringBuffer aBuf(sStrArray[nNext]);
1574 aBuf.stripEnd();
1575 padToLength(aBuf, rPat.getLength() - nPat, ' ');
1576 bOk = (rPat.indexOf(aBuf, nPat) == nPat);
1578 if (bOk)
1580 rPos = nLen; // yes, set, not add!
1581 return true;
1583 else
1584 return false;
1586 nPat += sStrArray[nNext].getLength() - 1;
1587 break;
1590 return false;
1594 sal_uInt16 ImpSvNumberInputScan::GetDatePatternNumbers()
1596 // If not initialized yet start with first number, if any.
1597 if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1599 return 0;
1601 return nDatePatternNumbers;
1605 bool ImpSvNumberInputScan::IsDatePatternNumberOfType( sal_uInt16 nNumber, sal_Unicode cType )
1607 if (GetDatePatternNumbers() <= nNumber)
1608 return false;
1610 sal_uInt16 nNum = 0;
1611 const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1612 for (sal_Int32 nPat = 0; nPat < rPat.getLength(); ++nPat)
1614 switch (rPat[nPat])
1616 case 'Y':
1617 case 'M':
1618 case 'D':
1619 if (nNum == nNumber)
1620 return rPat[nPat] == cType;
1621 ++nNum;
1622 break;
1625 return false;
1629 sal_uInt32 ImpSvNumberInputScan::GetDatePatternOrder()
1631 // If not initialized yet start with first number, if any.
1632 if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1634 return 0;
1636 sal_uInt32 nOrder = 0;
1637 const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1638 for (sal_Int32 nPat = 0; nPat < rPat.getLength() && !(nOrder & 0xff0000); ++nPat)
1640 switch (rPat[nPat])
1642 case 'Y':
1643 case 'M':
1644 case 'D':
1645 nOrder = (nOrder << 8) | rPat[nPat];
1646 break;
1649 return nOrder;
1653 DateOrder ImpSvNumberInputScan::GetDateOrder( bool bFromFormatIfNoPattern )
1655 sal_uInt32 nOrder = GetDatePatternOrder();
1656 if (!nOrder)
1658 if (bFromFormatIfNoPattern && mpFormat)
1659 return mpFormat->GetDateOrder();
1660 else
1661 return mrCurrentLanguageData.GetLocaleData()->getDateOrder();
1663 switch ((nOrder & 0xff0000) >> 16)
1665 case 'Y':
1666 if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'D'))
1668 return DateOrder::YMD;
1670 break;
1671 case 'M':
1672 if ((((nOrder & 0xff00) >> 8) == 'D') && ((nOrder & 0xff) == 'Y'))
1674 return DateOrder::MDY;
1676 break;
1677 case 'D':
1678 if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'Y'))
1680 return DateOrder::DMY;
1682 break;
1683 default:
1684 case 0:
1685 switch ((nOrder & 0xff00) >> 8)
1687 case 'Y':
1688 switch (nOrder & 0xff)
1690 case 'M':
1691 return DateOrder::YMD;
1693 break;
1694 case 'M':
1695 switch (nOrder & 0xff)
1697 case 'Y':
1698 return DateOrder::DMY;
1699 case 'D':
1700 return DateOrder::MDY;
1702 break;
1703 case 'D':
1704 switch (nOrder & 0xff)
1706 case 'Y':
1707 return DateOrder::MDY;
1708 case 'M':
1709 return DateOrder::DMY;
1711 break;
1712 default:
1713 case 0:
1714 switch (nOrder & 0xff)
1716 case 'Y':
1717 return DateOrder::YMD;
1718 case 'M':
1719 return DateOrder::MDY;
1720 case 'D':
1721 return DateOrder::DMY;
1723 break;
1726 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateOrder: undefined, falling back to locale's default");
1727 return mrCurrentLanguageData.GetLocaleData()->getDateOrder();
1730 LongDateOrder ImpSvNumberInputScan::GetMiddleMonthLongDateOrder( bool bFormatTurn,
1731 const LocaleDataWrapper* pLoc,
1732 DateOrder eDateOrder )
1734 if (MayBeMonthDate())
1735 return (nMayBeMonthDate == 2) ? LongDateOrder::DMY : LongDateOrder::YMD;
1737 LongDateOrder eLDO;
1738 const sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->GetExactDateOrder() : 0);
1739 if (!nExactDateOrder)
1740 eLDO = pLoc->getLongDateOrder();
1741 else if ((((nExactDateOrder >> 16) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D'))
1742 eLDO = LongDateOrder::YMD;
1743 else if ((((nExactDateOrder >> 16) & 0xff) == 'D') && ((nExactDateOrder & 0xff) == 'Y'))
1744 eLDO = LongDateOrder::DMY;
1745 else
1746 eLDO = pLoc->getLongDateOrder();
1747 if (eLDO != LongDateOrder::YMD && eLDO != LongDateOrder::DMY)
1749 switch (eDateOrder)
1751 case DateOrder::YMD:
1752 eLDO = LongDateOrder::YMD;
1753 break;
1754 case DateOrder::DMY:
1755 eLDO = LongDateOrder::DMY;
1756 break;
1757 default:
1758 ; // nothing, not a date
1761 else if (eLDO == LongDateOrder::DMY && eDateOrder == DateOrder::YMD)
1763 // Check possible order and maybe switch.
1764 if (!ImplGetDay(0) && ImplGetDay(1))
1765 eLDO = LongDateOrder::YMD;
1767 else if (eLDO == LongDateOrder::YMD && eDateOrder == DateOrder::DMY)
1769 // Check possible order and maybe switch.
1770 if (!ImplGetDay(1) && ImplGetDay(0))
1771 eLDO = LongDateOrder::DMY;
1773 return eLDO;
1776 bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter )
1778 using namespace ::com::sun::star::i18n;
1779 NfEvalDateFormat eEDF;
1780 int nFormatOrder;
1781 if ( mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE) )
1783 eEDF = mrCurrentLanguageData.GetEvalDateFormat();
1784 switch ( eEDF )
1786 case NF_EVALDATEFORMAT_INTL :
1787 case NF_EVALDATEFORMAT_FORMAT :
1788 nFormatOrder = 1; // only one loop
1789 break;
1790 default:
1791 nFormatOrder = 2;
1792 if ( nMatchedAllStrings )
1794 eEDF = NF_EVALDATEFORMAT_FORMAT_INTL;
1795 // we have a complete match, use it
1799 else
1801 eEDF = NF_EVALDATEFORMAT_INTL;
1802 nFormatOrder = 1;
1804 bool res = true;
1806 const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();
1807 CalendarWrapper* pCal = mrCurrentLanguageData.GetCalendar();
1808 for ( int nTryOrder = 1; nTryOrder <= nFormatOrder; nTryOrder++ )
1810 pCal->setGregorianDateTime( Date( Date::SYSTEM ) ); // today
1811 OUString aOrgCalendar; // empty => not changed yet
1812 DateOrder DateFmt;
1813 bool bFormatTurn;
1814 switch ( eEDF )
1816 case NF_EVALDATEFORMAT_INTL :
1817 bFormatTurn = false;
1818 DateFmt = GetDateOrder();
1819 break;
1820 case NF_EVALDATEFORMAT_FORMAT :
1821 bFormatTurn = true;
1822 DateFmt = mpFormat->GetDateOrder();
1823 break;
1824 case NF_EVALDATEFORMAT_INTL_FORMAT :
1825 if ( nTryOrder == 1 )
1827 bFormatTurn = false;
1828 DateFmt = GetDateOrder();
1830 else
1832 bFormatTurn = true;
1833 DateFmt = mpFormat->GetDateOrder();
1835 break;
1836 case NF_EVALDATEFORMAT_FORMAT_INTL :
1837 if ( nTryOrder == 2 )
1839 bFormatTurn = false;
1840 DateFmt = GetDateOrder();
1842 else
1844 bFormatTurn = true;
1845 // Even if the format pattern is to be preferred, the input may
1846 // have matched a pattern of the current locale, which then
1847 // again is to be preferred. Both date orders can be different
1848 // so we need to obtain the actual match. For example ISO
1849 // YYYY-MM-DD format vs locale's DD.MM.YY input.
1850 // If no pattern was matched, obtain from format.
1851 // Note that patterns may have been constructed from the
1852 // format's locale and prepended to the current locale's
1853 // patterns, it doesn't necessarily mean a current locale's
1854 // pattern was matched, but may if the format's locale's
1855 // patterns didn't match, which were tried first.
1856 DateFmt = GetDateOrder(true);
1858 break;
1859 default:
1860 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
1861 DateFmt = DateOrder::YMD;
1862 bFormatTurn = false;
1864 if ( bFormatTurn )
1866 /* TODO:
1867 We are currently not able to fully support a switch to another calendar during
1868 input for the following reasons:
1869 1. We do have a problem if both (locale's default and format's) calendars
1870 define the same YMD order and use the same date separator, there is no way
1871 to distinguish between them if the input results in valid calendar input for
1872 both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should
1873 it always be set to NF_EVALDATEFORMAT_FORMAT_INTL and thus the format's
1874 calendar be preferred? This could be confusing if a Calc cell was formatted
1875 different to the locale's default and has no content yet, then the user has
1876 no clue about the format or calendar being set.
1877 2. In Calc cell edit mode a date is always displayed and edited using the
1878 default edit format of the default calendar (normally being Gregorian). If
1879 input was ambiguous due to issue #1 we'd need a mechanism to tell that a
1880 date was edited and not newly entered. Not feasible. Otherwise we'd need a
1881 mechanism to use a specific edit format with a specific calendar according
1882 to the format set.
1883 3. For some calendars like Japanese Gengou we'd need era input, which isn't
1884 implemented at all. Though this is a rare and special case, forcing a
1885 calendar dependent edit format as suggested in item #2 might require era
1886 input, if it shouldn't result in a fallback to Gregorian calendar.
1887 4. Last and least: the GetMonth() method currently only matches month names of
1888 the default calendar. Alternating month names of the actual format's
1889 calendar would have to be implemented. No problem.
1892 #ifdef THE_FUTURE
1893 if ( mpFormat->IsOtherCalendar( nStringScanNumFor ) )
1895 mpFormat->SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
1897 else
1899 mpFormat->SwitchToSpecifiedCalendar( aOrgCalendar, fOrgDateTime,
1900 nStringScanNumFor );
1902 #endif
1905 res = true;
1906 nCounter = 0;
1907 // For incomplete dates, always assume first day of month if not specified.
1908 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1910 switch (nNumericsCnt) // count of numbers in string
1912 case 0: // none
1913 if (nMonthPos) // only month (Jan)
1915 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1917 else
1919 res = false;
1921 break;
1923 case 1: // only one number
1924 nCounter = 1;
1925 switch (nMonthPos) // where is the month
1927 case 0: // not found
1929 // If input matched a date pattern, use the pattern
1930 // to determine if it is a day, month or year. The
1931 // pattern should have only one single value then,
1932 // 'D-', 'M-' or 'Y-'. If input did not match a
1933 // pattern assume the usual day of current month.
1934 sal_uInt32 nDateOrder = (bFormatTurn ?
1935 mpFormat->GetExactDateOrder() :
1936 GetDatePatternOrder());
1937 switch (nDateOrder)
1939 case 'Y':
1940 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1941 break;
1942 case 'M':
1943 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1944 break;
1945 case 'D':
1946 default:
1947 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1948 break;
1950 break;
1952 case 1: // month at the beginning (Jan 01)
1953 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1954 switch (DateFmt)
1956 case DateOrder::MDY:
1957 case DateOrder::YMD:
1959 sal_uInt16 nDay = ImplGetDay(0);
1960 sal_uInt16 nYear = ImplGetYear(0);
1961 if (nDay == 0 || nDay > 32)
1963 pCal->setValue( CalendarFieldIndex::YEAR, nYear);
1965 else
1967 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1969 break;
1971 case DateOrder::DMY:
1972 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1973 break;
1974 default:
1975 res = false;
1976 break;
1978 break;
1979 case 3: // month at the end (10 Jan)
1980 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1981 switch (DateFmt)
1983 case DateOrder::DMY:
1984 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1985 break;
1986 case DateOrder::YMD:
1987 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1988 break;
1989 default:
1990 res = false;
1991 break;
1993 break;
1994 default:
1995 res = false;
1996 break;
1997 } // switch (nMonthPos)
1998 break;
2000 case 2: // 2 numbers
2001 nCounter = 2;
2002 switch (nMonthPos) // where is the month
2004 case 0: // not found
2006 sal_uInt32 nExactDateOrder = (bFormatTurn ?
2007 mpFormat->GetExactDateOrder() :
2008 GetDatePatternOrder());
2009 bool bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
2010 if (!bIsExact && bFormatTurn && IsAcceptedDatePattern( nNums[0]))
2012 // If input does not match format but pattern, use pattern
2013 // instead, even if eEDF==NF_EVALDATEFORMAT_FORMAT_INTL.
2014 // For example, format has "Y-M-D" and pattern is "D.M.",
2015 // input with 2 numbers can't match format and 31.12. would
2016 // lead to 1931-12-01 (fdo#54344)
2017 nExactDateOrder = GetDatePatternOrder();
2018 bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
2020 bool bHadExact;
2021 if (bIsExact)
2023 // formatted as date and exactly 2 parts
2024 bHadExact = true;
2025 switch ( (nExactDateOrder >> 8) & 0xff )
2027 case 'Y':
2028 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2029 break;
2030 case 'M':
2031 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2032 break;
2033 case 'D':
2034 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2035 break;
2036 default:
2037 bHadExact = false;
2039 switch ( nExactDateOrder & 0xff )
2041 case 'Y':
2042 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2043 break;
2044 case 'M':
2045 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2046 break;
2047 case 'D':
2048 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2049 break;
2050 default:
2051 bHadExact = false;
2053 SAL_WARN_IF( !bHadExact, "svl.numbers", "ImpSvNumberInputScan::GetDateRef: error in exact date order");
2055 else
2057 bHadExact = false;
2059 // If input matched against a date acceptance pattern
2060 // do not attempt to mess around with guessing the
2061 // order, either it matches or it doesn't.
2062 if ((bFormatTurn || !bIsExact) && (!bHadExact || !pCal->isValid()))
2064 if ( !bHadExact && nExactDateOrder )
2066 pCal->setGregorianDateTime( Date( Date::SYSTEM ) ); // reset today
2068 switch (DateFmt)
2070 case DateOrder::MDY:
2071 // M D
2072 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2073 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2074 if ( !pCal->isValid() ) // 2nd try
2075 { // M Y
2076 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
2077 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2078 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2080 break;
2081 case DateOrder::DMY:
2082 // D M
2083 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2084 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2085 if ( !pCal->isValid() ) // 2nd try
2086 { // M Y
2087 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
2088 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2089 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2091 break;
2092 case DateOrder::YMD:
2093 // M D
2094 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2095 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2096 if ( !pCal->isValid() ) // 2nd try
2097 { // Y M
2098 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
2099 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2100 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2102 break;
2103 default:
2104 res = false;
2105 break;
2109 break;
2110 case 1: // month at the beginning (Jan 01 01)
2112 // The input is valid as MDY in almost any
2113 // constellation, there is no date order (M)YD except if
2114 // set in a format applied.
2115 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2116 sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->GetExactDateOrder() : 0);
2117 if ((((nExactDateOrder >> 8) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D'))
2119 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2120 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2122 else
2124 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2125 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2127 break;
2129 case 2: // month in the middle (10 Jan 94)
2131 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2132 const LongDateOrder eLDO = GetMiddleMonthLongDateOrder( bFormatTurn, pLoc, DateFmt);
2133 switch (eLDO)
2135 case LongDateOrder::DMY:
2136 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2137 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2138 break;
2139 case LongDateOrder::YMD:
2140 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2141 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2142 break;
2143 default:
2144 res = false;
2145 break;
2147 break;
2149 case 3: // month at the end (94 10 Jan)
2150 if (pLoc->getLongDateOrder() != LongDateOrder::YDM)
2151 res = false;
2152 else
2154 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2155 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2156 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2158 break;
2159 default:
2160 res = false;
2161 break;
2162 } // switch (nMonthPos)
2163 break;
2165 default: // more than two numbers (31.12.94 8:23) (31.12. 8:23)
2166 switch (nMonthPos) // where is the month
2168 case 0: // not found
2170 nCounter = 3;
2171 if ( nTimePos > 1 )
2172 { // find first time number index (should only be 3 or 2 anyway)
2173 for ( sal_uInt16 j = 0; j < nNumericsCnt; j++ )
2175 if ( nNums[j] == nTimePos - 2 )
2177 nCounter = j;
2178 break; // for
2182 // ISO 8601 yyyy-mm-dd forced recognition
2183 DateOrder eDF = (CanForceToIso8601( DateFmt) ? DateOrder::YMD : DateFmt);
2184 switch (eDF)
2186 case DateOrder::MDY:
2187 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2188 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2189 if ( nCounter > 2 )
2190 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) );
2191 break;
2192 case DateOrder::DMY:
2193 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2194 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2195 if ( nCounter > 2 )
2196 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) );
2197 break;
2198 case DateOrder::YMD:
2199 if ( nCounter > 2 )
2200 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(2) );
2201 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2202 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2203 break;
2204 default:
2205 res = false;
2206 break;
2208 break;
2210 case 1: // month at the beginning (Jan 01 01 8:23)
2212 nCounter = 2;
2213 // The input is valid as MDY in almost any
2214 // constellation, there is no date order (M)YD except if
2215 // set in a format applied.
2216 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2217 sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->GetExactDateOrder() : 0);
2218 if ((((nExactDateOrder >> 8) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D'))
2220 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2221 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2223 else
2225 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2226 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2228 break;
2230 case 2: // month in the middle (10 Jan 94 8:23)
2232 nCounter = 2;
2233 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2234 const LongDateOrder eLDO = GetMiddleMonthLongDateOrder( bFormatTurn, pLoc, DateFmt);
2235 switch (eLDO)
2237 case LongDateOrder::DMY:
2238 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2239 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2240 break;
2241 case LongDateOrder::YMD:
2242 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2243 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2244 break;
2245 default:
2246 res = false;
2247 break;
2249 break;
2251 case 3: // month at the end (94 10 Jan 8:23)
2252 nCounter = 2;
2253 if (pLoc->getLongDateOrder() != LongDateOrder::YDM)
2254 res = false;
2255 else
2257 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2258 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2259 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2261 break;
2262 default:
2263 nCounter = 2;
2264 res = false;
2265 break;
2266 } // switch (nMonthPos)
2267 break;
2268 } // switch (nNumericsCnt)
2270 if (mbEraCE != kDefaultEra)
2271 pCal->setValue( CalendarFieldIndex::ERA, mbEraCE ? 1 : 0);
2273 if ( res && pCal->isValid() )
2275 double fDiff = DateTime::Sub( DateTime(*moNullDate), pCal->getEpochStart());
2276 fDays = ::rtl::math::approxFloor( pCal->getLocalDateTime() );
2277 fDays -= fDiff;
2278 nTryOrder = nFormatOrder; // break for
2280 else
2282 res = false;
2284 if ( aOrgCalendar.getLength() )
2286 pCal->loadCalendar( aOrgCalendar, pLoc->getLanguageTag().getLocale() ); // restore calendar
2288 #if NF_TEST_CALENDAR
2290 using namespace ::com::sun::star;
2291 struct entry { const char* lan; const char* cou; const char* cal; };
2292 const entry cals[] = {
2293 { "en", "US", "gregorian" },
2294 { "ar", "TN", "hijri" },
2295 { "he", "IL", "jewish" },
2296 { "ja", "JP", "gengou" },
2297 { "ko", "KR", "hanja_yoil" },
2298 { "th", "TH", "buddhist" },
2299 { "zh", "TW", "ROC" },
2300 {0,0,0}
2302 lang::Locale aLocale;
2303 bool bValid;
2304 sal_Int16 nDay, nMyMonth, nYear, nHour, nMinute, nSecond;
2305 sal_Int16 nDaySet, nMonthSet, nYearSet, nHourSet, nMinuteSet, nSecondSet;
2306 sal_Int16 nZO, nDST1, nDST2, nDST, nZOmillis, nDST1millis, nDST2millis, nDSTmillis;
2307 sal_Int32 nZoneInMillis, nDST1InMillis, nDST2InMillis;
2308 uno::Reference< uno::XComponentContext > xContext =
2309 ::comphelper::getProcessComponentContext();
2310 uno::Reference< i18n::XCalendar4 > xCal = i18n::LocaleCalendar2::create(xContext);
2311 for ( const entry* p = cals; p->lan; ++p )
2313 aLocale.Language = OUString::createFromAscii( p->lan );
2314 aLocale.Country = OUString::createFromAscii( p->cou );
2315 xCal->loadCalendar( OUString::createFromAscii( p->cal ),
2316 aLocale );
2317 double nDateTime = 0.0; // 1-Jan-1970 00:00:00
2318 nZO = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET );
2319 nZOmillis = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS );
2320 nZoneInMillis = static_cast<sal_Int32>(nZO) * 60000 +
2321 (nZO < 0 ? -1 : 1) * static_cast<sal_uInt16>(nZOmillis);
2322 nDST1 = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2323 nDST1millis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2324 nDST1InMillis = static_cast<sal_Int32>(nDST1) * 60000 +
2325 (nDST1 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST1millis);
2326 nDateTime -= (double)(nZoneInMillis + nDST1InMillis) / 1000.0 / 60.0 / 60.0 / 24.0;
2327 xCal->setDateTime( nDateTime );
2328 nDST2 = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2329 nDST2millis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2330 nDST2InMillis = static_cast<sal_Int32>(nDST2) * 60000 +
2331 (nDST2 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST2millis);
2332 if ( nDST1InMillis != nDST2InMillis )
2334 nDateTime = 0.0 - (double)(nZoneInMillis + nDST2InMillis) / 1000.0 / 60.0 / 60.0 / 24.0;
2335 xCal->setDateTime( nDateTime );
2337 nDaySet = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH );
2338 nMonthSet = xCal->getValue( i18n::CalendarFieldIndex::MONTH );
2339 nYearSet = xCal->getValue( i18n::CalendarFieldIndex::YEAR );
2340 nHourSet = xCal->getValue( i18n::CalendarFieldIndex::HOUR );
2341 nMinuteSet = xCal->getValue( i18n::CalendarFieldIndex::MINUTE );
2342 nSecondSet = xCal->getValue( i18n::CalendarFieldIndex::SECOND );
2343 nZO = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET );
2344 nZOmillis = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS );
2345 nDST = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2346 nDSTmillis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2347 xCal->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDaySet );
2348 xCal->setValue( i18n::CalendarFieldIndex::MONTH, nMonthSet );
2349 xCal->setValue( i18n::CalendarFieldIndex::YEAR, nYearSet );
2350 xCal->setValue( i18n::CalendarFieldIndex::HOUR, nHourSet );
2351 xCal->setValue( i18n::CalendarFieldIndex::MINUTE, nMinuteSet );
2352 xCal->setValue( i18n::CalendarFieldIndex::SECOND, nSecondSet );
2353 bValid = xCal->isValid();
2354 nDay = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH );
2355 nMyMonth= xCal->getValue( i18n::CalendarFieldIndex::MONTH );
2356 nYear = xCal->getValue( i18n::CalendarFieldIndex::YEAR );
2357 nHour = xCal->getValue( i18n::CalendarFieldIndex::HOUR );
2358 nMinute = xCal->getValue( i18n::CalendarFieldIndex::MINUTE );
2359 nSecond = xCal->getValue( i18n::CalendarFieldIndex::SECOND );
2360 bValid = bValid && nDay == nDaySet && nMyMonth == nMonthSet && nYear ==
2361 nYearSet && nHour == nHourSet && nMinute == nMinuteSet && nSecond
2362 == nSecondSet;
2365 #endif // NF_TEST_CALENDAR
2369 return res;
2374 * Analyze first string
2375 * All gone => true
2376 * else => false
2378 bool ImpSvNumberInputScan::ScanStartString( const OUString& rString )
2380 sal_Int32 nPos = 0;
2382 // First of all, eat leading blanks
2383 SkipBlanks(rString, nPos);
2385 // Yes, nMatchedAllStrings should know about the sign position
2386 nSign = GetSign(rString, nPos);
2387 if ( nSign ) // sign?
2389 SkipBlanks(rString, nPos);
2391 // #102371# match against format string only if start string is not a sign character
2392 if ( nMatchedAllStrings && !(nSign && rString.getLength() == 1) )
2394 // Match against format in any case, so later on for a "x1-2-3" input
2395 // we may distinguish between a xy-m-d (or similar) date and a x0-0-0
2396 // format. No sign detection here!
2397 if ( ScanStringNumFor( rString, nPos, 0, true ) )
2399 nMatchedAllStrings |= nMatchedStartString;
2401 else
2403 nMatchedAllStrings = 0;
2407 // Bail out early for just a sign.
2408 if (nSign && nPos == rString.getLength())
2409 return true;
2411 const sal_Int32 nStartBlanks = nPos;
2412 if ( GetDecSep(rString, nPos) ) // decimal separator in start string
2414 if (SkipBlanks(rString, nPos))
2415 nPos = nStartBlanks; // `. 2` not a decimal separator
2416 else
2417 nDecPos = 1; // leading decimal separator
2419 else if ( GetCurrency(rString, nPos) ) // currency (DM 1)?
2421 eScannedType = SvNumFormatType::CURRENCY; // !!! it IS currency !!!
2422 SkipBlanks(rString, nPos);
2423 if (nSign == 0) // no sign yet
2425 nSign = GetSign(rString, nPos);
2426 if ( nSign ) // DM -1
2428 SkipBlanks(rString, nPos);
2431 if ( GetDecSep(rString, nPos) ) // decimal separator follows currency
2433 if (SkipBlanks(rString, nPos))
2435 nPos = nStartBlanks; // `DM . 2` not a decimal separator
2436 eScannedType = SvNumFormatType::UNDEFINED; // !!! it is NOT currency !!!
2438 else
2439 nDecPos = 1; // leading decimal separator
2442 else
2444 const sal_Int32 nMonthStart = nPos;
2445 short nTempMonth = GetMonth(rString, nPos);
2446 if (nTempMonth < 0)
2448 // Short month and day names may be identical in some locales, e.g.
2449 // "mar" for "martes" or "marzo" in Spanish.
2450 // Do not let a month name immediately take precedence if a day
2451 // name was meant instead. Assume that both could be valid, until
2452 // encountered differently or the final evaluation in
2453 // IsNumberFormat() checks, but continue with weighing the month
2454 // name higher unless we have both day of week and month name here.
2455 sal_Int32 nTempPos = nMonthStart;
2456 nDayOfWeek = GetDayOfWeek( rString, nTempPos);
2457 if (nDayOfWeek < 0)
2459 SkipChar( '.', rString, nTempPos ); // abbreviated
2460 SkipString( mrCurrentLanguageData.GetLocaleData()->getLongDateDayOfWeekSep(), rString, nTempPos );
2461 SkipBlanks( rString, nTempPos);
2462 short nTempTempMonth = GetMonth( rString, nTempPos);
2463 if (nTempTempMonth)
2465 // Fall into the else branch below that handles both.
2466 nTempMonth = 0;
2467 nPos = nMonthStart;
2468 nDayOfWeek = 0;
2469 // Do not set nDayOfWeek hereafter, anywhere.
2473 if ( nTempMonth ) // month (Jan 1)?
2475 // Jan1 without separator is not a date, unless it is followed by a
2476 // separator and a (year) number.
2477 if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
2479 eScannedType = SvNumFormatType::DATE; // !!! it IS a date !!!
2480 nMonth = nTempMonth;
2481 nMonthPos = 1; // month at the beginning
2482 if ( nMonth < 0 )
2484 SkipChar( '.', rString, nPos ); // abbreviated
2486 SkipBlanks(rString, nPos);
2488 else
2490 nPos = nMonthStart; // rewind month
2493 else
2495 int nTempDayOfWeek = GetDayOfWeek( rString, nPos );
2496 if ( nTempDayOfWeek )
2498 // day of week is just parsed away
2499 eScannedType = SvNumFormatType::DATE; // !!! it IS a date !!!
2500 if ( nPos < rString.getLength() )
2502 if ( nTempDayOfWeek < 0 )
2504 // abbreviated
2505 if ( rString[ nPos ] == '.' )
2507 ++nPos;
2510 else
2512 // full long name
2513 SkipBlanks(rString, nPos);
2514 SkipString( mrCurrentLanguageData.GetLocaleData()->getLongDateDayOfWeekSep(), rString, nPos );
2516 SkipBlanks(rString, nPos);
2517 nTempMonth = GetMonth(rString, nPos);
2518 if ( nTempMonth ) // month (Jan 1)?
2520 // Jan1 without separator is not a date, unless it is followed by a
2521 // separator and a (year) number.
2522 if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
2524 nMonth = nTempMonth;
2525 nMonthPos = 1; // month at the beginning
2526 if ( nMonth < 0 )
2528 SkipChar( '.', rString, nPos ); // abbreviated
2530 SkipBlanks(rString, nPos);
2532 else
2534 nPos = nMonthStart; // rewind month
2538 if (!nMonth)
2540 // Determine and remember following date pattern, if any.
2541 IsAcceptedDatePattern( 1);
2545 // Skip one trailing '-' or '/' character to recognize June-2007
2546 if (nMonth && nPos + 1 == rString.getLength())
2548 SkipChar('-', rString, nPos) || SkipChar('/', rString, nPos);
2552 if (nPos < rString.getLength()) // not everything consumed
2554 // Does input StartString equal StartString of format?
2555 // This time with sign detection!
2556 if ( !ScanStringNumFor( rString, nPos, 0 ) )
2558 return MatchedReturn();
2562 return true;
2567 * Analyze string in the middle
2568 * All gone => true
2569 * else => false
2571 bool ImpSvNumberInputScan::ScanMidString( const OUString& rString, sal_uInt16 nStringPos, sal_uInt16 nCurNumCount )
2573 sal_Int32 nPos = 0;
2574 SvNumFormatType eOldScannedType = eScannedType;
2576 if ( nMatchedAllStrings )
2577 { // Match against format in any case, so later on for a "1-2-3-4" input
2578 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2579 // format.
2580 if ( ScanStringNumFor( rString, 0, nStringPos ) )
2582 nMatchedAllStrings |= nMatchedMidString;
2584 else
2586 nMatchedAllStrings = 0;
2590 const sal_Int32 nStartBlanks = nPos;
2591 const bool bBlanks = SkipBlanks(rString, nPos);
2592 if (GetDecSep(rString, nPos)) // decimal separator?
2594 if (nDecPos == 1 || nDecPos == 3) // .12.4 or 1.E2.1
2596 return MatchedReturn();
2598 else if (nDecPos == 2) // . dup: 12.4.
2600 bool bSignedYear = false;
2601 if (bDecSepInDateSeps || // . also date separator
2602 SkipDatePatternSeparator( nStringPos, nPos, bSignedYear))
2604 if ( eScannedType != SvNumFormatType::UNDEFINED &&
2605 eScannedType != SvNumFormatType::DATE &&
2606 eScannedType != SvNumFormatType::DATETIME) // already another type
2608 return MatchedReturn();
2610 if (eScannedType == SvNumFormatType::UNDEFINED)
2612 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2614 SkipBlanks(rString, nPos);
2616 else
2618 return MatchedReturn();
2621 else if (bBlanks)
2623 // `1 .2` or `1 . 2` not a decimal separator, reset
2624 nPos = nStartBlanks;
2626 else if (SkipBlanks(rString, nPos))
2628 // `1. 2` not a decimal separator, reset
2629 nPos = nStartBlanks;
2631 else
2633 nDecPos = 2; // . in mid string
2636 else if ( (eScannedType & SvNumFormatType::TIME) &&
2637 GetTime100SecSep( rString, nPos ) )
2638 { // hundredth seconds separator
2639 if ( nDecPos )
2641 return MatchedReturn();
2643 nDecPos = 2; // . in mid string
2645 // If this is exactly an ISO 8601 fractional seconds separator, bail
2646 // out early to not get confused by later checks for group separator or
2647 // other.
2648 if (bIso8601Tsep && nPos == rString.getLength() &&
2649 eScannedType == SvNumFormatType::DATETIME && (rString == "." || rString == ","))
2650 return true;
2652 SkipBlanks(rString, nPos);
2655 if (SkipChar('/', rString, nPos)) // fraction?
2657 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2658 eScannedType != SvNumFormatType::DATE) // except date
2660 return MatchedReturn(); // => jan/31/1994
2662 else if (eScannedType != SvNumFormatType::DATE && // analyzed no date until now
2663 (eSetType == SvNumFormatType::FRACTION || // and preset was fraction
2664 (nNumericsCnt == 3 && // or 3 numbers
2665 (nStringPos == 3 || // and 4th string particle
2666 (nStringPos == 4 && nSign)) && // or 5th if signed
2667 sStrArray[nStringPos-2].indexOf('/') == -1))) // and not 23/11/1999
2668 // that was not accepted as date yet
2670 SkipBlanks(rString, nPos);
2671 if (nPos == rString.getLength())
2673 eScannedType = SvNumFormatType::FRACTION; // !!! it IS a fraction (so far)
2674 if (eSetType == SvNumFormatType::FRACTION &&
2675 nNumericsCnt == 2 &&
2676 (nStringPos == 1 || // for 4/5
2677 (nStringPos == 2 && nSign))) // or signed -4/5
2679 return true; // don't fall into date trap
2683 else
2685 nPos--; // put '/' back
2689 if (GetThousandSep(rString, nPos, nStringPos)) // 1,000
2691 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2692 eScannedType != SvNumFormatType::CURRENCY) // except currency
2694 return MatchedReturn();
2696 nThousand++;
2699 const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();
2700 bool bSignedYear = false;
2701 bool bDate = SkipDatePatternSeparator( nStringPos, nPos, bSignedYear); // 12/31 31.12. 12/31/1999 31.12.1999
2702 if (!bDate)
2704 const OUString& rDate = mrCurrentLanguageData.GetDateSep();
2705 SkipBlanks(rString, nPos);
2706 bDate = SkipString( rDate, rString, nPos); // 10. 10- 10/
2708 if (!bDate && nStringPos == 1 && mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE))
2710 // If a DMY format was given and a mid string starts with a literal
2711 // ". " dot+space and could contain a following month name and ends
2712 // with a space or LongDateMonthSeparator, like it's scanned in
2713 // `14". AUG "18`, then it may be a date as well. Regardless whether
2714 // defined such by the locale or not.
2715 // This *could* check for presence of ". "MMM or ". "MMMM in the actual
2716 // format code for further restriction to match only if present, but..
2718 const sal_uInt32 nExactDateOrder = mpFormat->GetExactDateOrder();
2719 // Exactly DMY.
2720 if (((nExactDateOrder & 0xff) == 'Y') && (((nExactDateOrder >> 8) & 0xff) == 'M')
2721 && (((nExactDateOrder >> 16) & 0xff) == 'D'))
2723 const sal_Int32 nTmpPos = nPos;
2724 if (SkipChar('.', rString, nPos) && SkipBlanks(rString, nPos) && nPos + 2 < rString.getLength()
2725 && (rString.endsWith(" ") || rString.endsWith( pLoc->getLongDateMonthSep())))
2726 bDate = true;
2727 else
2728 nPos = nTmpPos;
2731 if (bDate || ((MayBeIso8601() || MayBeMonthDate()) && // 1999-12-31 31-Dec-1999
2732 SkipChar( '-', rString, nPos)))
2734 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2735 eScannedType != SvNumFormatType::DATE) // except date
2737 return MatchedReturn();
2739 SkipBlanks(rString, nPos);
2740 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2741 short nTmpMonth = GetMonth(rString, nPos); // 10. Jan 94
2742 if (nMonth && nTmpMonth) // month dup
2744 return MatchedReturn();
2746 if (nTmpMonth)
2748 nMonth = nTmpMonth;
2749 nMonthPos = 2; // month in the middle
2750 if ( nMonth < 0 && SkipChar( '.', rString, nPos ) )
2751 ; // short month may be abbreviated Jan.
2752 else if ( SkipChar( '-', rString, nPos ) )
2753 ; // #79632# recognize 17-Jan-2001 to be a date
2754 // #99065# short and long month name
2755 else
2757 SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
2759 SkipBlanks(rString, nPos);
2761 if (bSignedYear)
2763 if (mbEraCE != kDefaultEra) // signed year twice?
2764 return MatchedReturn();
2766 mbEraCE = false; // BCE
2770 const sal_Int32 nMonthStart = nPos;
2771 short nTempMonth = GetMonth(rString, nPos); // month in the middle (10 Jan 94) or at the end (94 10 Jan)
2772 if (nTempMonth)
2774 if (nMonth != 0) // month dup
2776 return MatchedReturn();
2778 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2779 eScannedType != SvNumFormatType::DATE) // except date
2781 return MatchedReturn();
2783 if (nMonthStart > 0 && nPos < rString.getLength()) // 10Jan or Jan94 without separator are not dates
2785 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2786 nMonth = nTempMonth;
2787 if (nCurNumCount <= 1)
2788 nMonthPos = 2; // month in the middle
2789 else
2790 nMonthPos = 3; // month at the end
2791 if ( nMonth < 0 )
2793 SkipChar( '.', rString, nPos ); // abbreviated
2795 SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
2796 SkipBlanks(rString, nPos);
2798 else
2800 nPos = nMonthStart; // rewind month
2804 if ( SkipChar('E', rString, nPos) || // 10E, 10e, 10,Ee
2805 SkipChar('e', rString, nPos) )
2807 if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
2809 return MatchedReturn();
2811 else
2813 SkipBlanks(rString, nPos);
2814 eScannedType = SvNumFormatType::SCIENTIFIC; // !!! it IS scientific
2815 if ( nThousand+2 == nNumericsCnt && nDecPos == 2 ) // special case 1.E2
2817 nDecPos = 3; // 1,100.E2 1,100,100.E3
2820 nESign = GetESign(rString, nPos); // signed exponent?
2821 SkipBlanks(rString, nPos);
2824 const OUString& rTime = pLoc->getTimeSep();
2825 if ( SkipString(rTime, rString, nPos) ) // time separator?
2827 if (nDecPos) // already . => maybe error
2829 if (bDecSepInDateSeps) // . also date sep
2831 if ( eScannedType != SvNumFormatType::DATE && // already another type than date
2832 eScannedType != SvNumFormatType::DATETIME) // or date time
2834 return MatchedReturn();
2836 if (eScannedType == SvNumFormatType::DATE)
2838 nDecPos = 0; // reset for time transition
2841 else
2843 return MatchedReturn();
2846 if ((eScannedType == SvNumFormatType::DATE || // already date type
2847 eScannedType == SvNumFormatType::DATETIME) && // or date time
2848 nNumericsCnt > 3) // and more than 3 numbers? (31.Dez.94 8:23)
2850 SkipBlanks(rString, nPos);
2851 eScannedType = SvNumFormatType::DATETIME; // !!! it IS date with time
2853 else if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2854 eScannedType != SvNumFormatType::TIME) // except time
2856 return MatchedReturn();
2858 else
2860 SkipBlanks(rString, nPos);
2861 eScannedType = SvNumFormatType::TIME; // !!! it IS a time
2863 if ( !nTimePos )
2865 nTimePos = nStringPos + 1;
2869 if (nPos < rString.getLength())
2871 switch (eScannedType)
2873 case SvNumFormatType::DATE:
2874 if (nMonthPos == 1 && pLoc->getLongDateOrder() == LongDateOrder::MDY)
2876 // #68232# recognize long date separators like ", " in "September 5, 1999"
2877 if (SkipString( pLoc->getLongDateDaySep(), rString, nPos ))
2879 SkipBlanks( rString, nPos );
2882 else if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
2884 if ( (nStringPos == 5 && rString[0] == 'T') ||
2885 (nStringPos == 6 && rString[0] == 'T' && sStrArray[0] == "-"))
2887 // ISO 8601 combined date and time, yyyy-mm-ddThh:mm or -yyyy-mm-ddThh:mm
2888 ++nPos;
2889 bIso8601Tsep = true;
2891 else if (nStringPos == 7 && rString[0] == ':')
2893 // ISO 8601 combined date and time, the time part; we reach
2894 // here if the locale's separator is not ':' so it couldn't
2895 // be detected above in the time block.
2896 if (nNumericsCnt >= 5)
2897 eScannedType = SvNumFormatType::DATETIME;
2898 ++nPos;
2901 break;
2902 case SvNumFormatType::DATETIME:
2903 if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
2905 if (nStringPos == 9 && rString[0] == ':')
2907 // ISO 8601 combined date and time, the time part continued.
2908 ++nPos;
2911 #if NF_RECOGNIZE_ISO8601_TIMEZONES
2912 else if (nPos == 0 && rString.getLength() == 1 && nStringPos >= 9 && MayBeIso8601())
2914 // ISO 8601 timezone offset
2915 switch (rString[ 0 ])
2917 case '+':
2918 case '-':
2919 if (nStringPos == nStringsCnt - 2 ||
2920 nStringPos == nStringsCnt - 4)
2922 ++nPos; // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy]
2923 // nTimezonePos needed for GetTimeRef()
2924 if (!nTimezonePos)
2926 nTimezonePos = nStringPos + 1;
2929 break;
2930 case ':':
2931 if (nTimezonePos && nStringPos >= 11 &&
2932 nStringPos == nStringsCnt - 2)
2934 ++nPos; // yyyy-mm-ddThh:mm[:ss]+xx:yy
2936 break;
2939 #endif
2940 break;
2941 default: break;
2945 if (nPos < rString.getLength()) // not everything consumed?
2947 if ( nMatchedAllStrings & ~nMatchedVirgin )
2949 eScannedType = eOldScannedType;
2951 else
2953 return false;
2957 return true;
2962 * Analyze the end
2963 * All gone => true
2964 * else => false
2966 bool ImpSvNumberInputScan::ScanEndString( const OUString& rString )
2968 sal_Int32 nPos = 0;
2970 if ( nMatchedAllStrings )
2971 { // Match against format in any case, so later on for a "1-2-3-4" input
2972 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2973 // format.
2974 if ( ScanStringNumFor( rString, 0, 0xFFFF ) )
2976 nMatchedAllStrings |= nMatchedEndString;
2978 else
2980 nMatchedAllStrings = 0;
2984 const sal_Int32 nStartBlanks = nPos;
2985 const bool bBlanks = SkipBlanks(rString, nPos);
2986 if (GetDecSep(rString, nPos)) // decimal separator?
2988 if (nDecPos == 1 || nDecPos == 3) // .12.4 or 12.E4.
2990 return MatchedReturn();
2992 else if (nDecPos == 2) // . dup: 12.4.
2994 bool bSignedYear = false;
2995 if (bDecSepInDateSeps || // . also date separator
2996 SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear))
2998 if ( eScannedType != SvNumFormatType::UNDEFINED &&
2999 eScannedType != SvNumFormatType::DATE &&
3000 eScannedType != SvNumFormatType::DATETIME) // already another type
3002 return MatchedReturn();
3004 if (eScannedType == SvNumFormatType::UNDEFINED)
3006 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
3008 SkipBlanks(rString, nPos);
3010 else
3012 return MatchedReturn();
3015 else if (bBlanks)
3017 // not a decimal separator, reset
3018 nPos = nStartBlanks;
3020 else
3022 nDecPos = 3; // . in end string
3023 SkipBlanks(rString, nPos);
3027 bool bSignDetectedHere = false;
3028 if ( nSign == 0 && // conflict - not signed
3029 eScannedType != SvNumFormatType::DATE) // and not date
3030 //!? catch time too?
3031 { // not signed yet
3032 nSign = GetSign(rString, nPos); // 1- DM
3033 if (bNegCheck) // '(' as sign
3035 return MatchedReturn();
3037 if (nSign)
3039 bSignDetectedHere = true;
3043 SkipBlanks(rString, nPos);
3044 if (bNegCheck && SkipChar(')', rString, nPos)) // skip ')' if appropriate
3046 bNegCheck = false;
3047 SkipBlanks(rString, nPos);
3050 if ( GetCurrency(rString, nPos) ) // currency symbol?
3052 if (eScannedType != SvNumFormatType::UNDEFINED) // currency dup
3054 return MatchedReturn();
3056 else
3058 SkipBlanks(rString, nPos);
3059 eScannedType = SvNumFormatType::CURRENCY;
3060 } // behind currency a '-' is allowed
3061 if (nSign == 0) // not signed yet
3063 nSign = GetSign(rString, nPos); // DM -
3064 SkipBlanks(rString, nPos);
3065 if (bNegCheck) // 3 DM (
3067 return MatchedReturn();
3070 if ( bNegCheck && eScannedType == SvNumFormatType::CURRENCY &&
3071 SkipChar(')', rString, nPos) )
3073 bNegCheck = false; // ')' skipped
3074 SkipBlanks(rString, nPos); // only if currency
3078 if ( SkipChar('%', rString, nPos) ) // 1%
3080 if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
3082 return MatchedReturn();
3084 SkipBlanks(rString, nPos);
3085 eScannedType = SvNumFormatType::PERCENT;
3088 const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();
3089 const OUString& rTime = pLoc->getTimeSep();
3090 if ( SkipString(rTime, rString, nPos) ) // 10:
3092 if (nDecPos) // already , => error
3094 return MatchedReturn();
3096 if (eScannedType == SvNumFormatType::DATE && nNumericsCnt > 2) // 31.Dez.94 8:
3098 SkipBlanks(rString, nPos);
3099 eScannedType = SvNumFormatType::DATETIME;
3101 else if (eScannedType != SvNumFormatType::UNDEFINED &&
3102 eScannedType != SvNumFormatType::TIME) // already another type
3104 return MatchedReturn();
3106 else
3108 SkipBlanks(rString, nPos);
3109 eScannedType = SvNumFormatType::TIME;
3111 if ( !nTimePos )
3113 nTimePos = nStringsCnt;
3117 bool bSignedYear = false;
3118 bool bDate = SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear); // 12/31 31.12. 12/31/1999 31.12.1999
3119 if (!bDate)
3121 const OUString& rDate = mrCurrentLanguageData.GetDateSep();
3122 bDate = SkipString( rDate, rString, nPos); // 10. 10- 10/
3124 if (bDate && bSignDetectedHere)
3126 nSign = 0; // 'D-' takes precedence over signed date
3128 if (bDate || ((MayBeIso8601() || MayBeMonthDate())
3129 && SkipChar( '-', rString, nPos)))
3131 if (eScannedType != SvNumFormatType::UNDEFINED &&
3132 eScannedType != SvNumFormatType::DATE) // already another type
3134 return MatchedReturn();
3136 else
3138 SkipBlanks(rString, nPos);
3139 eScannedType = SvNumFormatType::DATE;
3141 short nTmpMonth = GetMonth(rString, nPos); // 10. Jan
3142 if (nMonth && nTmpMonth) // month dup
3144 return MatchedReturn();
3146 if (nTmpMonth)
3148 nMonth = nTmpMonth;
3149 nMonthPos = 3; // month at end
3150 if ( nMonth < 0 )
3152 SkipChar( '.', rString, nPos ); // abbreviated
3154 SkipBlanks(rString, nPos);
3158 const sal_Int32 nMonthStart = nPos;
3159 short nTempMonth = GetMonth(rString, nPos); // 10 Jan
3160 if (nTempMonth)
3162 if (nMonth) // month dup
3164 return MatchedReturn();
3166 if (eScannedType != SvNumFormatType::UNDEFINED &&
3167 eScannedType != SvNumFormatType::DATE) // already another type
3169 return MatchedReturn();
3171 if (nMonthStart > 0) // 10Jan without separator is not a date
3173 eScannedType = SvNumFormatType::DATE;
3174 nMonth = nTempMonth;
3175 nMonthPos = 3; // month at end
3176 if ( nMonth < 0 )
3178 SkipChar( '.', rString, nPos ); // abbreviated
3180 SkipBlanks(rString, nPos);
3182 else
3184 nPos = nMonthStart; // rewind month
3188 sal_Int32 nOrigPos = nPos;
3189 if (GetTimeAmPm(rString, nPos))
3191 if (eScannedType != SvNumFormatType::UNDEFINED &&
3192 eScannedType != SvNumFormatType::TIME &&
3193 eScannedType != SvNumFormatType::DATETIME) // already another type
3195 return MatchedReturn();
3197 else
3199 // If not already scanned as time, 6.78am does not result in 6
3200 // seconds and 78 hundredths in the morning. Keep as suffix.
3201 if (eScannedType != SvNumFormatType::TIME && nDecPos == 2 && nNumericsCnt == 2)
3203 nPos = nOrigPos; // rewind am/pm
3205 else
3207 SkipBlanks(rString, nPos);
3208 if ( eScannedType != SvNumFormatType::DATETIME )
3210 eScannedType = SvNumFormatType::TIME;
3216 if ( bNegCheck && SkipChar(')', rString, nPos) )
3218 if (eScannedType == SvNumFormatType::CURRENCY) // only if currency
3220 bNegCheck = false; // skip ')'
3221 SkipBlanks(rString, nPos);
3223 else
3225 return MatchedReturn();
3229 if ( nPos < rString.getLength() &&
3230 (eScannedType == SvNumFormatType::DATE ||
3231 eScannedType == SvNumFormatType::DATETIME) )
3233 // day of week is just parsed away
3234 sal_Int32 nOldPos = nPos;
3235 const OUString& rSep = mrCurrentLanguageData.GetLocaleData()->getLongDateDayOfWeekSep();
3236 if ( StringContains( rSep, rString, nPos ) )
3238 nPos = nPos + rSep.getLength();
3239 SkipBlanks(rString, nPos);
3241 int nTempDayOfWeek = GetDayOfWeek( rString, nPos );
3242 if ( nTempDayOfWeek )
3244 if ( nPos < rString.getLength() )
3246 if ( nTempDayOfWeek < 0 )
3247 { // short
3248 if ( rString[ nPos ] == '.' )
3250 ++nPos;
3253 SkipBlanks(rString, nPos);
3256 else
3258 nPos = nOldPos;
3262 #if NF_RECOGNIZE_ISO8601_TIMEZONES
3263 if (nPos == 0 && eScannedType == SvNumFormatType::DATETIME &&
3264 rString.getLength() == 1 && rString[ 0 ] == 'Z' && MayBeIso8601())
3266 // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ
3267 ++nPos;
3269 #endif
3271 if (nPos < rString.getLength()) // everything consumed?
3273 // does input EndString equal EndString in Format?
3274 if ( !ScanStringNumFor( rString, nPos, 0xFFFF ) )
3276 return false;
3280 return true;
3284 bool ImpSvNumberInputScan::ScanStringNumFor( const OUString& rString, // String to scan
3285 sal_Int32 nPos, // Position until which was consumed
3286 sal_uInt16 nString, // Substring of format, 0xFFFF => last
3287 bool bDontDetectNegation) // Suppress sign detection
3289 if ( !mpFormat )
3291 return false;
3293 const ::utl::TransliterationWrapper* pTransliteration = mrCurrentLanguageData.GetTransliteration();
3294 const OUString* pStr;
3295 OUString aString( rString );
3296 bool bFound = false;
3297 bool bFirst = true;
3298 bool bContinue = true;
3299 sal_uInt16 nSub;
3302 // Don't try "lower" subformats ff the very first match was the second
3303 // or third subformat.
3304 nSub = nStringScanNumFor;
3306 { // Step through subformats, first positive, then negative, then
3307 // other, but not the last (text) subformat.
3308 pStr = mpFormat->GetNumForString( nSub, nString, true );
3309 if ( pStr && pTransliteration->isEqual( aString, *pStr ) )
3311 bFound = true;
3312 bContinue = false;
3314 else if ( nSub < 2 )
3316 ++nSub;
3318 else
3320 bContinue = false;
3323 while ( bContinue );
3324 if ( !bFound && bFirst && nPos )
3326 // try remaining substring
3327 bFirst = false;
3328 aString = aString.copy(nPos);
3329 bContinue = true;
3332 while ( bContinue );
3334 if ( !bFound )
3336 if ( !bDontDetectNegation && (nString == 0) &&
3337 !bFirst && (nSign < 0) && mpFormat->IsSecondSubformatRealNegative() )
3339 // simply negated twice? --1
3340 aString = aString.replaceAll(" ", "");
3341 if ( (aString.getLength() == 1) && (aString[0] == '-') )
3343 bFound = true;
3344 nStringScanSign = -1;
3345 nSub = 0; //! not 1
3348 if ( !bFound )
3350 return false;
3353 else if ( !bDontDetectNegation && (nSub == 1) &&
3354 mpFormat->IsSecondSubformatRealNegative() )
3356 // negative
3357 if ( nStringScanSign < 0 )
3359 if ( (nSign < 0) && (nStringScanNumFor != 1) )
3361 nStringScanSign = 1; // triple negated --1 yyy
3364 else if ( nStringScanSign == 0 )
3366 if ( nSign < 0 )
3367 { // nSign and nStringScanSign will be combined later,
3368 // flip sign if doubly negated
3369 if ( (nString == 0) && !bFirst &&
3370 SvNumberformat::HasStringNegativeSign( aString ) )
3372 nStringScanSign = -1; // direct double negation
3374 else if ( mpFormat->IsNegativeWithoutSign() )
3376 nStringScanSign = -1; // indirect double negation
3379 else
3381 nStringScanSign = -1;
3384 else // > 0
3386 nStringScanSign = -1;
3389 nStringScanNumFor = nSub;
3390 return true;
3395 * Recognizes types of number, exponential, fraction, percent, currency, date, time.
3396 * Else text => return false
3398 bool ImpSvNumberInputScan::IsNumberFormatMain( const OUString& rString, // string to be analyzed
3399 const SvNumberformat* pFormat ) // maybe number format set to match against
3401 Reset();
3402 mpFormat = pFormat;
3403 NumberStringDivision( rString ); // breakdown into strings and numbers
3404 if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS) // too many elements
3406 return false; // Njet, Nope, ...
3408 if (nNumericsCnt == 0) // no number in input
3410 if ( nStringsCnt > 0 )
3412 // Here we may change the original, we don't need it anymore.
3413 // This saves copies and ToUpper() in GetLogical() and is faster.
3414 sStrArray[0] = comphelper::string::strip(sStrArray[0], ' ');
3415 OUString& rStrArray = sStrArray[0];
3416 nLogical = GetLogical( rStrArray );
3417 if ( nLogical )
3419 eScannedType = SvNumFormatType::LOGICAL; // !!! it's a BOOLEAN
3420 nMatchedAllStrings &= ~nMatchedVirgin;
3421 return true;
3423 else
3425 return false; // simple text
3428 else
3430 return false; // simple text
3434 sal_uInt16 i = 0; // mark any symbol
3435 sal_uInt16 j = 0; // mark only numbers
3437 switch ( nNumericsCnt )
3439 case 1 : // Exactly 1 number in input
3440 // nStringsCnt >= 1
3441 if (GetNextNumber(i,j)) // i=1,0
3442 { // Number at start
3443 if (eSetType == SvNumFormatType::FRACTION) // Fraction 1 = 1/1
3445 if (i >= nStringsCnt || // no end string nor decimal separator
3446 mrCurrentLanguageData.IsDecimalSep( sStrArray[i]))
3448 eScannedType = SvNumFormatType::FRACTION;
3449 nMatchedAllStrings &= ~nMatchedVirgin;
3450 return true;
3454 else
3455 { // Analyze start string
3456 if (!ScanStartString( sStrArray[i] )) // i=0
3458 return false; // already an error
3460 i++; // next symbol, i=1
3462 GetNextNumber(i,j); // i=1,2
3463 if (eSetType == SvNumFormatType::FRACTION) // Fraction -1 = -1/1
3465 if (nSign && !bNegCheck && // Sign +, -
3466 eScannedType == SvNumFormatType::UNDEFINED && // not date or currency
3467 nDecPos == 0 && // no previous decimal separator
3468 (i >= nStringsCnt || // no end string nor decimal separator
3469 mrCurrentLanguageData.IsDecimalSep( sStrArray[i]))
3472 eScannedType = SvNumFormatType::FRACTION;
3473 nMatchedAllStrings &= ~nMatchedVirgin;
3474 return true;
3477 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3479 return false;
3481 break;
3482 case 2 : // Exactly 2 numbers in input
3483 // nStringsCnt >= 3
3484 if (!GetNextNumber(i,j)) // i=1,0
3485 { // Analyze start string
3486 if (!ScanStartString( sStrArray[i] ))
3487 return false; // already an error
3488 i++; // i=1
3490 GetNextNumber(i,j); // i=1,2
3491 if ( !ScanMidString( sStrArray[i], i, j ) )
3493 return false;
3495 i++; // next symbol, i=2,3
3496 GetNextNumber(i,j); // i=3,4
3497 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3499 return false;
3501 if (eSetType == SvNumFormatType::FRACTION) // -1,200. as fraction
3503 if (!bNegCheck && // no sign '('
3504 eScannedType == SvNumFormatType::UNDEFINED &&
3505 (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
3508 eScannedType = SvNumFormatType::FRACTION;
3509 nMatchedAllStrings &= ~nMatchedVirgin;
3510 return true;
3513 break;
3514 case 3 : // Exactly 3 numbers in input
3515 // nStringsCnt >= 5
3516 if (!GetNextNumber(i,j)) // i=1,0
3517 { // Analyze start string
3518 if (!ScanStartString( sStrArray[i] ))
3520 return false; // already an error
3522 i++; // i=1
3523 if (nDecPos == 1) // decimal separator at start => error
3525 return false;
3528 GetNextNumber(i,j); // i=1,2
3529 if ( !ScanMidString( sStrArray[i], i, j ) )
3531 return false;
3533 i++; // i=2,3
3534 if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at end
3536 return false;
3538 GetNextNumber(i,j); // i=3,4
3539 if ( !ScanMidString( sStrArray[i], i, j ) )
3541 return false;
3543 i++; // i=4,5
3544 GetNextNumber(i,j); // i=5,6
3545 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3547 return false;
3549 if (eSetType == SvNumFormatType::FRACTION) // -1,200,100. as fraction
3551 if (!bNegCheck && // no sign '('
3552 eScannedType == SvNumFormatType::UNDEFINED &&
3553 (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
3556 eScannedType = SvNumFormatType::FRACTION;
3557 nMatchedAllStrings &= ~nMatchedVirgin;
3558 return true;
3561 if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
3563 return false; // #36857# not a real fraction
3565 break;
3566 default: // More than 3 numbers in input
3567 // nStringsCnt >= 7
3568 if (!GetNextNumber(i,j)) // i=1,0
3569 { // Analyze startstring
3570 if (!ScanStartString( sStrArray[i] ))
3571 return false; // already an error
3572 i++; // i=1
3573 if (nDecPos == 1) // decimal separator at start => error
3574 return false;
3576 GetNextNumber(i,j); // i=1,2
3577 if ( !ScanMidString( sStrArray[i], i, j ) )
3579 return false;
3581 i++; // i=2,3
3583 sal_uInt16 nThOld = 10; // just not 0 or 1
3584 while (nThOld != nThousand && j < nNumericsCnt-1) // Execute at least one time
3585 // but leave one number.
3586 { // Loop over group separators
3587 nThOld = nThousand;
3588 if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at end
3590 return false;
3592 GetNextNumber(i,j);
3593 if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i, j ) )
3595 return false;
3597 i++;
3600 if (eScannedType == SvNumFormatType::DATE || // long date or
3601 eScannedType == SvNumFormatType::TIME || // long time or
3602 eScannedType == SvNumFormatType::UNDEFINED) // long number
3604 for (sal_uInt16 k = j; k < nNumericsCnt-1; k++)
3606 if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at endd
3608 return false;
3610 GetNextNumber(i,j);
3611 if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i, j ) )
3613 return false;
3615 i++;
3618 GetNextNumber(i,j);
3619 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3621 return false;
3623 if (eSetType == SvNumFormatType::FRACTION) // -1,200,100. as fraction
3625 if (!bNegCheck && // no sign '('
3626 eScannedType == SvNumFormatType::UNDEFINED &&
3627 (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
3630 eScannedType = SvNumFormatType::FRACTION;
3631 nMatchedAllStrings &= ~nMatchedVirgin;
3632 return true;
3635 if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
3637 return false; // #36857# not a real fraction
3639 break;
3642 if (eScannedType == SvNumFormatType::UNDEFINED)
3644 nMatchedAllStrings &= ~nMatchedVirgin;
3645 // did match including nMatchedUsedAsReturn
3646 bool bDidMatch = (nMatchedAllStrings != 0);
3647 if ( nMatchedAllStrings )
3649 bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
3650 nStringScanNumFor, nStringsCnt, nNumericsCnt );
3651 if ( !bMatch )
3653 nMatchedAllStrings = 0;
3656 if ( nMatchedAllStrings )
3658 // A type DEFINED means that no category could be assigned to the
3659 // overall format because of mixed type subformats. Use the scan
3660 // matched subformat's type if any.
3661 SvNumFormatType eForType = eSetType;
3662 if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
3663 eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor);
3664 if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
3665 eScannedType = eForType;
3666 else
3667 eScannedType = SvNumFormatType::NUMBER;
3669 else if ( bDidMatch )
3671 // Accept a plain fractional number like 123.45 as there may be a
3672 // decimal separator also present as literal like in a 0"."0 weirdo
3673 // format.
3674 if (nDecPos != 2 || nNumericsCnt != 2)
3675 return false;
3676 eScannedType = SvNumFormatType::NUMBER;
3678 else
3680 eScannedType = SvNumFormatType::NUMBER;
3681 // everything else should have been recognized by now
3684 else if ( eScannedType == SvNumFormatType::DATE )
3686 // the very relaxed date input checks may interfere with a preset format
3687 nMatchedAllStrings &= ~nMatchedVirgin;
3688 bool bWasReturn = ((nMatchedAllStrings & nMatchedUsedAsReturn) != 0);
3689 if ( nMatchedAllStrings )
3691 bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
3692 nStringScanNumFor, nStringsCnt, nNumericsCnt );
3693 if ( !bMatch )
3695 nMatchedAllStrings = 0;
3698 if ( nMatchedAllStrings )
3700 // A type DEFINED means that no category could be assigned to the
3701 // overall format because of mixed type subformats. Do not override
3702 // the scanned type in this case. Otherwise in IsNumberFormat() the
3703 // first numeric particle would be accepted as number.
3704 SvNumFormatType eForType = eSetType;
3705 if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
3706 eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor);
3707 if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
3708 eScannedType = eForType;
3710 else if ( bWasReturn )
3712 return false;
3715 else
3717 nMatchedAllStrings = 0; // reset flag to no substrings matched
3719 return true;
3724 * Return true or false depending on the nMatched... state and remember usage
3726 bool ImpSvNumberInputScan::MatchedReturn()
3728 if ( nMatchedAllStrings & ~nMatchedVirgin )
3730 nMatchedAllStrings |= nMatchedUsedAsReturn;
3731 return true;
3733 return false;
3738 * Initialize uppercase months and weekdays
3740 void ImpSvNumberInputScan::InitText()
3742 sal_Int32 j, nElems;
3743 const CharClass* pChrCls = mrCurrentLanguageData.GetCharClass();
3744 const CalendarWrapper* pCal = mrCurrentLanguageData.GetCalendar();
3746 pUpperMonthText.reset();
3747 pUpperAbbrevMonthText.reset();
3748 css::uno::Sequence< css::i18n::CalendarItem2 > xElems = pCal->getMonths();
3749 nElems = xElems.getLength();
3750 pUpperMonthText.reset( new OUString[nElems] );
3751 pUpperAbbrevMonthText.reset( new OUString[nElems] );
3752 for ( j = 0; j < nElems; j++ )
3754 pUpperMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3755 pUpperAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3758 pUpperGenitiveMonthText.reset();
3759 pUpperGenitiveAbbrevMonthText.reset();
3760 xElems = pCal->getGenitiveMonths();
3761 bScanGenitiveMonths = (nElems != xElems.getLength());
3762 nElems = xElems.getLength();
3763 pUpperGenitiveMonthText.reset( new OUString[nElems] );
3764 pUpperGenitiveAbbrevMonthText.reset( new OUString[nElems] );
3765 for ( j = 0; j < nElems; j++ )
3767 pUpperGenitiveMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3768 pUpperGenitiveAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3769 if (!bScanGenitiveMonths &&
3770 (pUpperGenitiveMonthText[j] != pUpperMonthText[j] ||
3771 pUpperGenitiveAbbrevMonthText[j] != pUpperAbbrevMonthText[j]))
3773 bScanGenitiveMonths = true;
3777 pUpperPartitiveMonthText.reset();
3778 pUpperPartitiveAbbrevMonthText.reset();
3779 xElems = pCal->getPartitiveMonths();
3780 bScanPartitiveMonths = (nElems != xElems.getLength());
3781 nElems = xElems.getLength();
3782 pUpperPartitiveMonthText.reset( new OUString[nElems] );
3783 pUpperPartitiveAbbrevMonthText.reset( new OUString[nElems] );
3784 for ( j = 0; j < nElems; j++ )
3786 pUpperPartitiveMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3787 pUpperPartitiveAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3788 if (!bScanPartitiveMonths &&
3789 (pUpperPartitiveMonthText[j] != pUpperGenitiveMonthText[j] ||
3790 pUpperPartitiveAbbrevMonthText[j] != pUpperGenitiveAbbrevMonthText[j]))
3792 bScanPartitiveMonths = true;
3796 pUpperDayText.reset();
3797 pUpperAbbrevDayText.reset();
3798 xElems = pCal->getDays();
3799 nElems = xElems.getLength();
3800 pUpperDayText.reset( new OUString[nElems] );
3801 pUpperAbbrevDayText.reset( new OUString[nElems] );
3802 for ( j = 0; j < nElems; j++ )
3804 pUpperDayText[j] = pChrCls->uppercase( xElems[j].FullName );
3805 pUpperAbbrevDayText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3808 bTextInitialized = true;
3813 * MUST be called if International/Locale is changed
3815 void ImpSvNumberInputScan::ChangeIntl()
3817 sal_Unicode cDecSep = mrCurrentLanguageData.GetNumDecimalSep()[0];
3818 bDecSepInDateSeps = ( cDecSep == '-' ||
3819 cDecSep == mrCurrentLanguageData.GetDateSep()[0] );
3820 if (!bDecSepInDateSeps)
3822 sal_Unicode cDecSepAlt = mrCurrentLanguageData.GetNumDecimalSepAlt().toChar();
3823 bDecSepInDateSeps = cDecSepAlt && (cDecSepAlt == '-' || cDecSepAlt == mrCurrentLanguageData.GetDateSep()[0]);
3825 bTextInitialized = false;
3826 aUpperCurrSymbol.clear();
3827 InvalidateDateAcceptancePatterns();
3831 void ImpSvNumberInputScan::InvalidateDateAcceptancePatterns()
3833 if (sDateAcceptancePatterns.hasElements())
3835 sDateAcceptancePatterns = css::uno::Sequence< OUString >();
3840 void ImpSvNumberInputScan::ChangeNullDate( const sal_uInt16 Day,
3841 const sal_uInt16 Month,
3842 const sal_Int16 Year )
3844 moNullDate = Date(Day, Month, Year);
3849 * Does rString represent a number (also date, time et al)
3851 bool ImpSvNumberInputScan::IsNumberFormat( const OUString& rString, // string to be analyzed
3852 SvNumFormatType& F_Type, // IN: old type, OUT: new type
3853 double& fOutNumber, // OUT: number if convertible
3854 const SvNumberformat* pFormat, // maybe a number format to match against
3855 const NativeNumberWrapper& rNatNum,
3856 SvNumInputOptions eInputOptions )
3858 bool res; // return value
3859 sal_uInt16 k;
3860 eSetType = F_Type; // old type set
3862 if ( !rString.getLength() )
3864 res = false;
3866 else if (rString.getLength() > 308) // arbitrary
3868 res = false;
3870 else
3872 // NoMoreUpperNeeded, all comparisons on UpperCase
3873 OUString aString = mrCurrentLanguageData.GetCharClass()->uppercase( rString );
3874 // convert native number to ASCII if necessary
3875 TransformInput(rNatNum, mrCurrentLanguageData, aString);
3876 res = IsNumberFormatMain( aString, pFormat );
3879 if (res)
3881 // Accept signed date only for ISO date with at least four digits in
3882 // year to not have an input of -M-D-Y arbitrarily recognized. The
3883 // final order is only determined in GetDateRef().
3884 // Also accept for Y/M/D date pattern match, i.e. if the first number
3885 // is year.
3886 // Accept only if the year immediately follows the sign character with
3887 // no space in between.
3888 if (nSign && (eScannedType == SvNumFormatType::DATE ||
3889 eScannedType == SvNumFormatType::DATETIME) && mbEraCE == kDefaultEra &&
3890 (IsDatePatternNumberOfType(0,'Y') || (MayBeIso8601() && sStrArray[nNums[0]].getLength() >= 4)))
3892 const sal_Unicode c = sStrArray[0][sStrArray[0].getLength()-1];
3893 if (c == '-' || c == '+')
3895 // A '+' sign doesn't change the era.
3896 if (nSign < 0)
3897 mbEraCE = false; // BCE
3898 nSign = 0;
3901 if ( bNegCheck || // ')' not found for '('
3902 (nSign && (eScannedType == SvNumFormatType::DATE ||
3903 eScannedType == SvNumFormatType::DATETIME))) // signed date/datetime
3905 res = false;
3907 else
3908 { // check count of partial number strings
3909 switch (eScannedType)
3911 case SvNumFormatType::PERCENT:
3912 case SvNumFormatType::CURRENCY:
3913 case SvNumFormatType::NUMBER:
3914 if (nDecPos == 1) // .05
3916 // Matched MidStrings function like group separators, but
3917 // there can't be an integer part numeric input, so
3918 // effectively 0 thousands groups.
3919 if ( nMatchedAllStrings )
3921 nThousand = 0;
3923 else if ( nNumericsCnt != 1 )
3925 res = false;
3928 else if (nDecPos == 2) // 1.05
3930 // Matched MidStrings function like group separators, but
3931 // let a decimal separator override a literal separator
3932 // string; like 0"." with input 123.45
3933 if ( nMatchedAllStrings )
3935 if (nNumericsCnt == 2)
3936 nThousand = 0;
3937 else
3939 // Assume that if there was a decimal separator
3940 // matching also a literal string then it was the
3941 // last. We could find the last possible match to
3942 // support literals in fractions, but really..
3943 nThousand = nNumericsCnt - 1;
3946 else if ( nNumericsCnt != nThousand+2 )
3948 res = false;
3951 else // 1,100 or 1,100.
3953 // matched MidStrings function like group separators
3954 if ( nMatchedAllStrings )
3956 nThousand = nNumericsCnt - 1;
3958 else if ( nNumericsCnt != nThousand+1 )
3960 res = false;
3963 break;
3965 case SvNumFormatType::SCIENTIFIC: // 1.0e-2
3966 if (nDecPos == 1) // .05
3968 if (nNumericsCnt != 2)
3970 res = false;
3973 else if (nDecPos == 2) // 1.05
3975 if (nNumericsCnt != nThousand+3)
3977 res = false;
3980 else // 1,100 or 1,100.
3982 if (nNumericsCnt != nThousand+2)
3984 res = false;
3987 break;
3989 case SvNumFormatType::DATE:
3990 if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt == 3)
3992 // If both, short month name and day of week name were
3993 // detected, and also numbers for full date, assume that we
3994 // have a day of week instead of month name.
3995 nMonth = 0;
3996 nMonthPos = 0;
3998 if (nMonth)
3999 { // month name and numbers
4000 if (nNumericsCnt > 2)
4002 res = false;
4005 else
4007 if (nNumericsCnt > 3)
4009 res = false;
4011 else
4013 // Even if a date pattern was matched, for abbreviated
4014 // pattern like "D.M." an input of "D.M. #" was
4015 // accepted because # could had been a time. Here we do
4016 // not have a combined date/time input though and #
4017 // would be taken as Year in this example, which it is
4018 // not. The count of numbers in pattern must match the
4019 // count of numbers in input.
4020 res = (GetDatePatternNumbers() == nNumericsCnt)
4021 || IsAcceptableIso8601() || nMatchedAllStrings;
4024 break;
4026 case SvNumFormatType::TIME:
4027 if (nDecPos)
4028 { // hundredth seconds included
4029 if (nNumericsCnt > 4)
4031 res = false;
4034 else
4036 if (nNumericsCnt > 3)
4038 res = false;
4041 break;
4043 case SvNumFormatType::DATETIME:
4044 if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt >= 5)
4046 // If both, abbreviated month name and day of week name
4047 // were detected, and also at least numbers for full date
4048 // plus time including minutes, assume that we have a day
4049 // of week instead of month name.
4050 nMonth = 0;
4051 nMonthPos = 0;
4053 if (nMonth)
4054 { // month name and numbers
4055 if (nDecPos)
4056 { // hundredth seconds included
4057 if (nNumericsCnt > 6)
4059 res = false;
4062 else
4064 if (nNumericsCnt > 5)
4066 res = false;
4070 else
4072 if (nDecPos)
4073 { // hundredth seconds included
4074 if (nNumericsCnt > 7)
4076 res = false;
4079 else
4081 if (nNumericsCnt > 6)
4083 res = false;
4086 if (res)
4088 res = IsAcceptedDatePattern( nNums[0]) || MayBeIso8601() || nMatchedAllStrings;
4091 break;
4093 default:
4094 break;
4095 } // switch
4096 } // else
4097 } // if (res)
4099 OUStringBuffer sResString;
4101 if (res)
4102 { // we finally have a number
4103 switch (eScannedType)
4105 case SvNumFormatType::LOGICAL:
4106 if (nLogical == 1)
4108 fOutNumber = 1.0; // True
4110 else if (nLogical == -1)
4112 fOutNumber = 0.0; // False
4114 else
4116 res = false; // Oops
4118 break;
4120 case SvNumFormatType::PERCENT:
4121 case SvNumFormatType::CURRENCY:
4122 case SvNumFormatType::NUMBER:
4123 case SvNumFormatType::SCIENTIFIC:
4124 case SvNumFormatType::DEFINED: // if no category detected handle as number
4125 if ( nDecPos == 1 ) // . at start
4127 sResString.append("0.");
4130 for ( k = 0; k <= nThousand; k++)
4132 sResString.append(sStrArray[nNums[k]]); // integer part
4134 if ( nDecPos == 2 && k < nNumericsCnt ) // . somewhere
4136 sResString.append('.');
4137 sal_uInt16 nStop = (eScannedType == SvNumFormatType::SCIENTIFIC ?
4138 nNumericsCnt-1 : nNumericsCnt);
4139 for ( ; k < nStop; k++)
4141 sResString.append(sStrArray[nNums[k]]); // fractional part
4145 if (eScannedType != SvNumFormatType::SCIENTIFIC)
4147 fOutNumber = StringToDouble(sResString);
4149 else
4150 { // append exponent
4151 sResString.append('E');
4152 if ( nESign == -1 )
4154 sResString.append('-');
4156 sResString.append(sStrArray[nNums[nNumericsCnt-1]]);
4157 rtl_math_ConversionStatus eStatus;
4158 fOutNumber = ::rtl::math::stringToDouble( sResString, '.', ',', &eStatus );
4159 if ( eStatus == rtl_math_ConversionStatus_OutOfRange )
4161 F_Type = SvNumFormatType::TEXT; // overflow/underflow -> Text
4162 if (nESign == -1)
4164 fOutNumber = 0.0;
4166 else
4168 fOutNumber = DBL_MAX;
4170 return true;
4174 if ( nStringScanSign )
4176 if ( nSign )
4178 nSign *= nStringScanSign;
4180 else
4182 nSign = nStringScanSign;
4185 if ( nSign < 0 )
4187 fOutNumber = -fOutNumber;
4190 if (eScannedType == SvNumFormatType::PERCENT)
4192 fOutNumber/= 100.0;
4194 break;
4196 case SvNumFormatType::FRACTION:
4197 if (nNumericsCnt == 1)
4199 fOutNumber = StringToDouble(sStrArray[nNums[0]]);
4201 else if (nNumericsCnt == 2)
4203 if (nThousand == 1)
4205 sResString = sStrArray[nNums[0]];
4206 sResString.append(sStrArray[nNums[1]]); // integer part
4207 fOutNumber = StringToDouble(sResString);
4209 else
4211 double fNumerator = StringToDouble(sStrArray[nNums[0]]);
4212 double fDenominator = StringToDouble(sStrArray[nNums[1]]);
4213 if (fDenominator != 0.0)
4215 fOutNumber = fNumerator/fDenominator;
4217 else
4219 res = false;
4223 else // nNumericsCnt > 2
4225 k = 1;
4226 sResString = sStrArray[nNums[0]];
4227 if (nThousand > 0)
4229 for (; k <= nThousand; k++)
4231 sResString.append(sStrArray[nNums[k]]);
4234 fOutNumber = StringToDouble(sResString);
4236 if (k == nNumericsCnt-2)
4238 double fNumerator = StringToDouble(sStrArray[nNums[k]]);
4239 double fDenominator = StringToDouble(sStrArray[nNums[k + 1]]);
4240 if (fDenominator != 0.0)
4242 fOutNumber += fNumerator/fDenominator;
4244 else
4246 res = false;
4251 if ( nStringScanSign )
4253 if ( nSign )
4255 nSign *= nStringScanSign;
4257 else
4259 nSign = nStringScanSign;
4262 if ( nSign < 0 )
4264 fOutNumber = -fOutNumber;
4266 break;
4268 case SvNumFormatType::TIME:
4269 res = GetTimeRef(fOutNumber, 0, nNumericsCnt, eInputOptions);
4270 if ( nSign < 0 )
4272 fOutNumber = -fOutNumber;
4274 break;
4276 case SvNumFormatType::DATE:
4277 res = GetDateRef( fOutNumber, k );
4278 break;
4280 case SvNumFormatType::DATETIME:
4281 res = GetDateRef( fOutNumber, k );
4282 if ( res )
4284 double fTime;
4285 res = GetTimeRef( fTime, k, nNumericsCnt - k, eInputOptions);
4286 fOutNumber += fTime;
4288 break;
4290 default:
4291 SAL_WARN( "svl.numbers", "Some number recognized but what's it?" );
4292 fOutNumber = 0.0;
4293 break;
4297 if (res) // overflow/underflow -> Text
4299 if (fOutNumber < -DBL_MAX) // -1.7E308
4301 F_Type = SvNumFormatType::TEXT;
4302 fOutNumber = -DBL_MAX;
4303 return true;
4305 else if (fOutNumber > DBL_MAX) // 1.7E308
4307 F_Type = SvNumFormatType::TEXT;
4308 fOutNumber = DBL_MAX;
4309 return true;
4313 if (!res)
4315 eScannedType = SvNumFormatType::TEXT;
4316 fOutNumber = 0.0;
4319 F_Type = eScannedType;
4320 return res;
4323 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */