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