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