1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: zforfind.cxx,v $
10 * $Revision: 1.51.96.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svtools.hxx"
38 #include <tools/date.hxx>
39 #include <tools/debug.hxx>
40 #include <rtl/math.hxx>
41 #include <unotools/charclass.hxx>
42 #include <unotools/calendarwrapper.hxx>
43 #include <unotools/localedatawrapper.hxx>
44 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
45 #include <unotools/digitgroupingiterator.hxx>
47 #include <svtools/zforlist.hxx> // NUMBERFORMAT_XXX
48 #include "zforscan.hxx"
49 #include <svtools/zformat.hxx>
52 #include "zforfind.hxx"
57 #define NF_TEST_CALENDAR 0
59 #define NF_TEST_CALENDAR 0
62 #include <comphelper/processfactory.hxx>
63 #include <com/sun/star/i18n/XExtendedCalendar.hpp>
67 const BYTE
ImpSvNumberInputScan::nMatchedEndString
= 0x01;
68 const BYTE
ImpSvNumberInputScan::nMatchedMidString
= 0x02;
69 const BYTE
ImpSvNumberInputScan::nMatchedStartString
= 0x04;
70 const BYTE
ImpSvNumberInputScan::nMatchedVirgin
= 0x08;
71 const BYTE
ImpSvNumberInputScan::nMatchedUsedAsReturn
= 0x10;
73 /* It is not clear how we want timezones to be handled. Convert them to local
74 * time isn't wanted, as it isn't done in any other place and timezone
75 * information isn't stored anywhere. Ignoring them and pretending local time
76 * may be wrong too and might not be what the user expects. Keep the input as
77 * string so that no information is lost.
78 * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it
79 * would work, together with the nTimezonePos handling in GetTimeRef(). */
80 #define NF_RECOGNIZE_ISO8601_TIMEZONES 0
82 //---------------------------------------------------------------------------
85 ImpSvNumberInputScan::ImpSvNumberInputScan( SvNumberFormatter
* pFormatterP
)
87 pUpperMonthText( NULL
),
88 pUpperAbbrevMonthText( NULL
),
89 pUpperDayText( NULL
),
90 pUpperAbbrevDayText( NULL
)
92 pFormatter
= pFormatterP
;
93 pNullDate
= new Date(30,12,1899);
94 nYear2000
= SvNumberFormatter::GetYear2000Default();
100 //---------------------------------------------------------------------------
103 ImpSvNumberInputScan::~ImpSvNumberInputScan()
107 delete [] pUpperMonthText
;
108 delete [] pUpperAbbrevMonthText
;
109 delete [] pUpperDayText
;
110 delete [] pUpperAbbrevDayText
;
114 //---------------------------------------------------------------------------
117 void ImpSvNumberInputScan::Reset()
120 // ER 16.06.97 18:56 Vorbelegung erfolgt jetzt in NumberStringDivision,
121 // wozu immer alles loeschen wenn einiges wieder benutzt oder gar nicht
123 for (USHORT i
= 0; i
< SV_MAX_ANZ_INPUT_STRINGS
; i
++)
125 sStrArray
[i
].Erase();
126 nNums
[i
] = SV_MAX_ANZ_INPUT_STRINGS
-1;
140 eScannedType
= NUMBERFORMAT_UNDEFINED
;
142 nPosThousandString
= 0;
144 nStringScanNumFor
= 0;
146 nMatchedAllStrings
= nMatchedVirgin
;
152 //---------------------------------------------------------------------------
155 inline BOOL
ImpSvNumberInputScan::MyIsdigit( sal_Unicode c
)
157 // If the input string wouldn't be converted using TransformInput() we'd
158 // to use something similar to the following and to adapt many places.
160 // use faster isdigit() if possible
162 return isdigit( (unsigned char) c
) != 0;
166 return pFormatter
->GetCharClass()->isDigit( aTmp
, 0 );
168 return c
< 128 && isdigit( (unsigned char) c
);
173 //---------------------------------------------------------------------------
175 void ImpSvNumberInputScan::TransformInput( String
& rStr
)
177 xub_StrLen nPos
, nLen
;
178 for ( nPos
= 0, nLen
= rStr
.Len(); nPos
< nLen
; ++nPos
)
180 if ( 256 <= rStr
.GetChar( nPos
) &&
181 pFormatter
->GetCharClass()->isDigit( rStr
, nPos
) )
185 rStr
= pFormatter
->GetNatNum()->getNativeNumberString( rStr
,
186 pFormatter
->GetLocale(), 0 );
190 //---------------------------------------------------------------------------
193 // Only simple unsigned floating point values without any error detection,
194 // decimal separator has to be '.'
196 double ImpSvNumberInputScan::StringToDouble( const String
& rStr
, BOOL bForceFraction
)
202 xub_StrLen nLen
= rStr
.Len();
203 BOOL bPreSep
= !bForceFraction
;
207 if (rStr
.GetChar(nPos
) == '.')
210 fNum
= fNum
* 10.0 + (double) (rStr
.GetChar(nPos
) - '0');
213 fFrac
= fFrac
* 10.0 + (double) (rStr
.GetChar(nPos
) - '0');
219 return fNum
+ ::rtl::math::pow10Exp( fFrac
, nExp
);
224 //---------------------------------------------------------------------------
225 // NextNumberStringSymbol
227 // Zerlegt die Eingabe in Zahlen und Strings fuer die weitere
228 // Verarbeitung (Turing-Maschine).
229 //---------------------------------------------------------------------------
230 // Ausgangs Zustand = GetChar
231 //---------------+-------------------+-----------------------+---------------
232 // Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand
233 //---------------+-------------------+-----------------------+---------------
234 // GetChar | Ziffer | Symbol=Zeichen | GetValue
235 // | Sonst | Symbol=Zeichen | GetString
236 //---------------|-------------------+-----------------------+---------------
237 // GetValue | Ziffer | Symbol=Symbol+Zeichen | GetValue
238 // | Sonst | Dec(CharPos) | Stop
239 //---------------+-------------------+-----------------------+---------------
240 // GetString | Ziffer | Dec(CharPos) | Stop
241 // | Sonst | Symbol=Symbol+Zeichen | GetString
242 //---------------+-------------------+-----------------------+---------------
244 enum ScanState
// States der Turing-Maschine
252 BOOL
ImpSvNumberInputScan::NextNumberStringSymbol(
253 const sal_Unicode
*& pStr
,
256 BOOL isNumber
= FALSE
;
258 ScanState eState
= SsStart
;
259 register const sal_Unicode
* pHere
= pStr
;
260 register xub_StrLen nChars
= 0;
262 while ( ((cToken
= *pHere
) != 0) && eState
!= SsStop
)
268 if ( MyIsdigit( cToken
) )
274 eState
= SsGetString
;
278 if ( MyIsdigit( cToken
) )
287 if ( !MyIsdigit( cToken
) )
301 rSymbol
.Assign( pStr
, nChars
);
311 //---------------------------------------------------------------------------
314 // FIXME: should be grouping; it is only used though in case nAnzStrings is
315 // near SV_MAX_ANZ_INPUT_STRINGS, in NumberStringDivision().
317 BOOL
ImpSvNumberInputScan::SkipThousands(
318 const sal_Unicode
*& pStr
,
323 const String
& rThSep
= pFormatter
->GetNumThousandSep();
324 register const sal_Unicode
* pHere
= pStr
;
325 ScanState eState
= SsStart
;
326 xub_StrLen nCounter
= 0; // counts 3 digits
328 while ( ((cToken
= *pHere
) != 0) && eState
!= SsStop
)
334 if ( StringPtrContains( rThSep
, pHere
-1, 0 ) )
338 pHere
+= rThSep
.Len()-1;
347 if ( MyIsdigit( cToken
) )
354 res
= TRUE
; // .000 combination found
368 if (eState
== SsGetValue
) // break witth less than 3 digits
371 rSymbol
.Erase( rSymbol
.Len() - nCounter
, nCounter
);
372 pHere
-= nCounter
+ rThSep
.Len(); // put back ThSep also
380 //---------------------------------------------------------------------------
381 // NumberStringDivision
383 void ImpSvNumberInputScan::NumberStringDivision( const String
& rString
)
385 const sal_Unicode
* pStr
= rString
.GetBuffer();
386 const sal_Unicode
* const pEnd
= pStr
+ rString
.Len();
387 while ( pStr
< pEnd
&& nAnzStrings
< SV_MAX_ANZ_INPUT_STRINGS
)
389 if ( NextNumberStringSymbol( pStr
, sStrArray
[nAnzStrings
] ) )
391 IsNum
[nAnzStrings
] = TRUE
;
392 nNums
[nAnzNums
] = nAnzStrings
;
394 if (nAnzStrings
>= SV_MAX_ANZ_INPUT_STRINGS
- 7 &&
395 nPosThousandString
== 0) // nur einmal
396 if ( SkipThousands( pStr
, sStrArray
[nAnzStrings
] ) )
397 nPosThousandString
= nAnzStrings
;
401 IsNum
[nAnzStrings
] = FALSE
;
408 //---------------------------------------------------------------------------
409 // Whether rString contains rWhat at nPos
411 BOOL
ImpSvNumberInputScan::StringContainsImpl( const String
& rWhat
,
412 const String
& rString
, xub_StrLen nPos
)
414 if ( nPos
+ rWhat
.Len() <= rString
.Len() )
415 return StringPtrContainsImpl( rWhat
, rString
.GetBuffer(), nPos
);
420 //---------------------------------------------------------------------------
421 // Whether pString contains rWhat at nPos
423 BOOL
ImpSvNumberInputScan::StringPtrContainsImpl( const String
& rWhat
,
424 const sal_Unicode
* pString
, xub_StrLen nPos
)
426 if ( rWhat
.Len() == 0 )
428 register const sal_Unicode
* pWhat
= rWhat
.GetBuffer();
429 register const sal_Unicode
* const pEnd
= pWhat
+ rWhat
.Len();
430 register const sal_Unicode
* pStr
= pString
+ nPos
;
431 while ( pWhat
< pEnd
)
433 if ( *pWhat
!= *pStr
)
442 //---------------------------------------------------------------------------
445 // ueberspringt genau das angegebene Zeichen
447 inline BOOL
ImpSvNumberInputScan::SkipChar( sal_Unicode c
, const String
& rString
,
450 if ((nPos
< rString
.Len()) && (rString
.GetChar(nPos
) == c
))
459 //---------------------------------------------------------------------------
462 // Ueberspringt Leerzeichen
464 inline void ImpSvNumberInputScan::SkipBlanks( const String
& rString
,
467 if ( nPos
< rString
.Len() )
469 register const sal_Unicode
* p
= rString
.GetBuffer() + nPos
;
479 //---------------------------------------------------------------------------
482 // jump over rWhat in rString at nPos
484 inline BOOL
ImpSvNumberInputScan::SkipString( const String
& rWhat
,
485 const String
& rString
, xub_StrLen
& nPos
)
487 if ( StringContains( rWhat
, rString
, nPos
) )
489 nPos
= nPos
+ rWhat
.Len();
496 //---------------------------------------------------------------------------
499 // recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping
501 inline BOOL
ImpSvNumberInputScan::GetThousandSep(
502 const String
& rString
,
506 const String
& rSep
= pFormatter
->GetNumThousandSep();
507 // Is it an ordinary space instead of a non-breaking space?
508 bool bSpaceBreak
= rSep
.GetChar(0) == 0xa0 && rString
.GetChar(0) == 0x20 &&
509 rSep
.Len() == 1 && rString
.Len() == 1;
510 if (!( (rString
== rSep
|| bSpaceBreak
) // nothing else
511 && nStringPos
< nAnzStrings
- 1 // safety first!
512 && IsNum
[nStringPos
+1] )) // number follows
513 return FALSE
; // no? => out
515 utl::DigitGroupingIterator
aGrouping(
516 pFormatter
->GetLocaleData()->getDigitGrouping());
517 // Match ,### in {3} or ,## in {3,2}
518 /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or
519 * ,##,### and to match ,### in {3,2} only if it's the last. However,
520 * currently there is no track kept where group separators occur. In {3,2}
521 * #,###,### and #,##,## would be valid input, which maybe isn't even bad
522 * for #,###,###. Other combinations such as #,###,## maybe not. */
523 xub_StrLen nLen
= sStrArray
[nStringPos
+1].Len();
524 if (nLen
== aGrouping
.get() // with 3 (or so) digits
525 || nLen
== aGrouping
.advance().get() // or with 2 (or 3 or so) digits
526 || nPosThousandString
== nStringPos
+1 // or concatenated
529 nPos
= nPos
+ rSep
.Len();
536 //---------------------------------------------------------------------------
539 // Conversion of text to logial value
544 short ImpSvNumberInputScan::GetLogical( const String
& rString
)
548 const ImpSvNumberformatScan
* pFS
= pFormatter
->GetFormatScanner();
549 if ( rString
== pFS
->GetTrueString() )
551 else if ( rString
== pFS
->GetFalseString() )
560 //---------------------------------------------------------------------------
563 // Converts a string containing a month name (JAN, January) at nPos into the
564 // month number (negative if abbreviated), returns 0 if nothing found
566 short ImpSvNumberInputScan::GetMonth( const String
& rString
, xub_StrLen
& nPos
)
568 // #102136# The correct English form of month September abbreviated is
569 // SEPT, but almost every data contains SEP instead.
570 static const String
aSeptCorrect( RTL_CONSTASCII_USTRINGPARAM( "SEPT" ) );
571 static const String
aSepShortened( RTL_CONSTASCII_USTRINGPARAM( "SEP" ) );
573 short res
= 0; // no month found
575 if (rString
.Len() > nPos
) // only if needed
577 if ( !bTextInitialized
)
579 sal_Int16 nMonths
= pFormatter
->GetCalendar()->getNumberOfMonthsInYear();
580 for ( sal_Int16 i
= 0; i
< nMonths
; i
++ )
582 if ( StringContains( pUpperMonthText
[i
], rString
, nPos
) )
583 { // full names first
584 nPos
= nPos
+ pUpperMonthText
[i
].Len();
588 else if ( StringContains( pUpperAbbrevMonthText
[i
], rString
, nPos
) )
590 nPos
= nPos
+ pUpperAbbrevMonthText
[i
].Len();
591 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
594 else if ( i
== 8 && pUpperAbbrevMonthText
[i
] == aSeptCorrect
&&
595 StringContains( aSepShortened
, rString
, nPos
) )
596 { // #102136# SEPT/SEP
597 nPos
= nPos
+ aSepShortened
.Len();
598 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
608 //---------------------------------------------------------------------------
611 // Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the
612 // DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found
614 int ImpSvNumberInputScan::GetDayOfWeek( const String
& rString
, xub_StrLen
& nPos
)
616 int res
= 0; // no day found
618 if (rString
.Len() > nPos
) // only if needed
620 if ( !bTextInitialized
)
622 sal_Int16 nDays
= pFormatter
->GetCalendar()->getNumberOfDaysInWeek();
623 for ( sal_Int16 i
= 0; i
< nDays
; i
++ )
625 if ( StringContains( pUpperDayText
[i
], rString
, nPos
) )
626 { // full names first
627 nPos
= nPos
+ pUpperDayText
[i
].Len();
631 if ( StringContains( pUpperAbbrevDayText
[i
], rString
, nPos
) )
633 nPos
= nPos
+ pUpperAbbrevDayText
[i
].Len();
634 res
= -(i
+ 1); // negative
644 //---------------------------------------------------------------------------
647 // Lesen eines Waehrungssysmbols
651 BOOL
ImpSvNumberInputScan::GetCurrency( const String
& rString
, xub_StrLen
& nPos
,
652 const SvNumberformat
* pFormat
)
654 if ( rString
.Len() > nPos
)
656 if ( !aUpperCurrSymbol
.Len() )
657 { // if no format specified the currency of the initialized formatter
658 LanguageType eLang
= (pFormat
? pFormat
->GetLanguage() :
659 pFormatter
->GetLanguage());
660 aUpperCurrSymbol
= pFormatter
->GetCharClass()->upper(
661 SvNumberFormatter::GetCurrencyEntry( eLang
).GetSymbol() );
663 if ( StringContains( aUpperCurrSymbol
, rString
, nPos
) )
665 nPos
= nPos
+ aUpperCurrSymbol
.Len();
670 String aSymbol
, aExtension
;
671 if ( pFormat
->GetNewCurrencySymbol( aSymbol
, aExtension
) )
673 if ( aSymbol
.Len() <= rString
.Len() - nPos
)
675 pFormatter
->GetCharClass()->toUpper( aSymbol
);
676 if ( StringContains( aSymbol
, rString
, nPos
) )
678 nPos
= nPos
+ aSymbol
.Len();
690 //---------------------------------------------------------------------------
693 // Lesen des Zeitsymbols (AM od. PM) f. kurze Zeitangabe
696 // "AM" od. "PM" => TRUE
704 BOOL
ImpSvNumberInputScan::GetTimeAmPm( const String
& rString
, xub_StrLen
& nPos
)
707 if ( rString
.Len() > nPos
)
709 const CharClass
* pChr
= pFormatter
->GetCharClass();
710 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
711 if ( StringContains( pChr
->upper( pLoc
->getTimeAM() ), rString
, nPos
) )
714 nPos
= nPos
+ pLoc
->getTimeAM().Len();
717 else if ( StringContains( pChr
->upper( pLoc
->getTimePM() ), rString
, nPos
) )
720 nPos
= nPos
+ pLoc
->getTimePM().Len();
729 //---------------------------------------------------------------------------
732 // Lesen eines Dezimaltrenners (',')
736 inline BOOL
ImpSvNumberInputScan::GetDecSep( const String
& rString
, xub_StrLen
& nPos
)
738 if ( rString
.Len() > nPos
)
740 const String
& rSep
= pFormatter
->GetNumDecimalSep();
741 if ( rString
.Equals( rSep
, nPos
, rSep
.Len() ) )
743 nPos
= nPos
+ rSep
.Len();
751 //---------------------------------------------------------------------------
752 // read a hundredth seconds separator
754 inline BOOL
ImpSvNumberInputScan::GetTime100SecSep( const String
& rString
, xub_StrLen
& nPos
)
756 if ( rString
.Len() > nPos
)
758 const String
& rSep
= pFormatter
->GetLocaleData()->getTime100SecSep();
759 if ( rString
.Equals( rSep
, nPos
, rSep
.Len() ) )
761 nPos
= nPos
+ rSep
.Len();
769 //---------------------------------------------------------------------------
772 // Lesen eines Vorzeichens, auch Klammer !?!
775 // '(' => -1, nNegCheck = 1
778 int ImpSvNumberInputScan::GetSign( const String
& rString
, xub_StrLen
& nPos
)
780 if (rString
.Len() > nPos
)
781 switch (rString
.GetChar(nPos
))
786 case '(': // '(' aehnlich wie '-' ?!?
800 //---------------------------------------------------------------------------
803 // Lesen eines Vorzeichens, gedacht fuer Exponent ?!?
808 short ImpSvNumberInputScan::GetESign( const String
& rString
, xub_StrLen
& nPos
)
810 if (rString
.Len() > nPos
)
811 switch (rString
.GetChar(nPos
))
827 //---------------------------------------------------------------------------
830 // i counts string portions, j counts numbers thereof.
831 // It should had been called SkipNumber instead.
833 inline BOOL
ImpSvNumberInputScan::GetNextNumber( USHORT
& i
, USHORT
& j
)
835 if ( i
< nAnzStrings
&& IsNum
[i
] )
845 //---------------------------------------------------------------------------
848 void ImpSvNumberInputScan::GetTimeRef(
850 USHORT nIndex
, // j-value of the first numeric time part of input, default 0
851 USHORT nAnz
) // count of numeric time parts
856 double fSecond100
= 0.0;
857 USHORT nStartIndex
= nIndex
;
861 // find first timezone number index and adjust count
862 for (USHORT j
=0; j
<nAnzNums
; ++j
)
864 if (nNums
[j
] == nTimezonePos
)
866 // nAnz is not total count, but count of time relevant strings.
867 if (nStartIndex
< j
&& j
- nStartIndex
< nAnz
)
868 nAnz
= j
- nStartIndex
;
874 if (nDecPos
== 2 && (nAnz
== 3 || nAnz
== 2)) // 20:45.5 or 45.5
876 else if (nIndex
- nStartIndex
< nAnz
)
877 nHour
= (USHORT
) sStrArray
[nNums
[nIndex
++]].ToInt32();
881 DBG_ERRORFILE( "ImpSvNumberInputScan::GetTimeRef: bad number index");
883 if (nDecPos
== 2 && nAnz
== 2) // 45.5
885 else if (nIndex
- nStartIndex
< nAnz
)
886 nMinute
= (USHORT
) sStrArray
[nNums
[nIndex
++]].ToInt32();
887 if (nIndex
- nStartIndex
< nAnz
)
888 nSecond
= (USHORT
) sStrArray
[nNums
[nIndex
++]].ToInt32();
889 if (nIndex
- nStartIndex
< nAnz
)
890 fSecond100
= StringToDouble( sStrArray
[nNums
[nIndex
]], TRUE
);
891 if (nAmPm
== -1 && nHour
!= 12) // PM
893 else if (nAmPm
== 1 && nHour
== 12) // 12 AM
896 fOutNumber
= ((double)nHour
*3600 +
903 //---------------------------------------------------------------------------
906 USHORT
ImpSvNumberInputScan::ImplGetDay( USHORT nIndex
)
910 if (sStrArray
[nNums
[nIndex
]].Len() <= 2)
912 USHORT nNum
= (USHORT
) sStrArray
[nNums
[nIndex
]].ToInt32();
921 //---------------------------------------------------------------------------
924 USHORT
ImpSvNumberInputScan::ImplGetMonth( USHORT nIndex
)
926 // preset invalid month number
927 USHORT nRes
= pFormatter
->GetCalendar()->getNumberOfMonthsInYear();
929 if (sStrArray
[nNums
[nIndex
]].Len() <= 2)
931 USHORT nNum
= (USHORT
) sStrArray
[nNums
[nIndex
]].ToInt32();
932 if ( 0 < nNum
&& nNum
<= nRes
)
933 nRes
= nNum
- 1; // zero based for CalendarFieldIndex::MONTH
940 //---------------------------------------------------------------------------
943 // 30 -> 1930, 29 -> 2029, oder 56 -> 1756, 55 -> 1855, ...
945 USHORT
ImpSvNumberInputScan::ImplGetYear( USHORT nIndex
)
949 if (sStrArray
[nNums
[nIndex
]].Len() <= 4)
951 nYear
= (USHORT
) sStrArray
[nNums
[nIndex
]].ToInt32();
952 nYear
= SvNumberFormatter::ExpandTwoDigitYear( nYear
, nYear2000
);
958 //---------------------------------------------------------------------------
960 bool ImpSvNumberInputScan::MayBeIso8601()
962 if (nMayBeIso8601
== 0)
964 if (nAnzNums
>= 3 && nNums
[0] < nAnzStrings
&&
965 sStrArray
[nNums
[0]].ToInt32() > 31)
970 return nMayBeIso8601
== 1;
973 //---------------------------------------------------------------------------
976 BOOL
ImpSvNumberInputScan::GetDateRef( double& fDays
, USHORT
& nCounter
,
977 const SvNumberformat
* pFormat
)
979 using namespace ::com::sun::star::i18n
;
980 NfEvalDateFormat eEDF
;
982 if ( pFormat
&& ((pFormat
->GetType() & NUMBERFORMAT_DATE
) == NUMBERFORMAT_DATE
) )
984 eEDF
= pFormatter
->GetEvalDateFormat();
987 case NF_EVALDATEFORMAT_INTL
:
988 case NF_EVALDATEFORMAT_FORMAT
:
989 nFormatOrder
= 1; // only one loop
993 if ( nMatchedAllStrings
)
994 eEDF
= NF_EVALDATEFORMAT_FORMAT_INTL
;
995 // we have a complete match, use it
1000 eEDF
= NF_EVALDATEFORMAT_INTL
;
1005 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
1006 CalendarWrapper
* pCal
= pFormatter
->GetCalendar();
1007 for ( int nTryOrder
= 1; nTryOrder
<= nFormatOrder
; nTryOrder
++ )
1009 pCal
->setGregorianDateTime( Date() ); // today
1010 String aOrgCalendar
; // empty => not changed yet
1015 case NF_EVALDATEFORMAT_INTL
:
1016 bFormatTurn
= FALSE
;
1017 DateFmt
= pLoc
->getDateFormat();
1019 case NF_EVALDATEFORMAT_FORMAT
:
1021 DateFmt
= pFormat
->GetDateOrder();
1023 case NF_EVALDATEFORMAT_INTL_FORMAT
:
1024 if ( nTryOrder
== 1 )
1026 bFormatTurn
= FALSE
;
1027 DateFmt
= pLoc
->getDateFormat();
1032 DateFmt
= pFormat
->GetDateOrder();
1035 case NF_EVALDATEFORMAT_FORMAT_INTL
:
1036 if ( nTryOrder
== 2 )
1038 bFormatTurn
= FALSE
;
1039 DateFmt
= pLoc
->getDateFormat();
1044 DateFmt
= pFormat
->GetDateOrder();
1048 DBG_ERROR( "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
1050 bFormatTurn
= FALSE
;
1056 We are currently not able to fully support a switch to another calendar during
1057 input for the following reasons:
1058 1. We do have a problem if both (locale's default and format's) calendars
1059 define the same YMD order and use the same date separator, there is no way
1060 to distinguish between them if the input results in valid calendar input for
1061 both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should
1062 it always be set to NF_EVALDATEFORMAT_FORMAT_INTL and thus the format's
1063 calendar be preferred? This could be confusing if a Calc cell was formatted
1064 different to the locale's default and has no content yet, then the user has
1065 no clue about the format or calendar being set.
1066 2. In Calc cell edit mode a date is always displayed and edited using the
1067 default edit format of the default calendar (normally being Gregorian). If
1068 input was ambiguous due to issue #1 we'd need a mechanism to tell that a
1069 date was edited and not newly entered. Not feasible. Otherwise we'd need a
1070 mechanism to use a specific edit format with a specific calendar according
1072 3. For some calendars like Japanese Gengou we'd need era input, which isn't
1073 implemented at all. Though this is a rare and special case, forcing a
1074 calendar dependent edit format as suggested in item #2 might require era
1075 input, if it shouldn't result in a fallback to Gregorian calendar.
1076 4. Last and least: the GetMonth() method currently only matches month names of
1077 the default calendar. Alternating month names of the actual format's
1078 calendar would have to be implemented. No problem.
1081 if ( pFormat
->IsOtherCalendar( nStringScanNumFor
) )
1082 pFormat
->SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
1084 pFormat
->SwitchToSpecifiedCalendar( aOrgCalendar
, fOrgDateTime
,
1085 nStringScanNumFor
);
1091 // For incomplete dates, always assume first day of month if not specified.
1092 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
1094 switch (nAnzNums
) // count of numbers in string
1097 if (nMonthPos
) // only month (Jan)
1098 pCal
->setValue( CalendarFieldIndex::MONTH
, Abs(nMonth
)-1 );
1103 case 1: // only one number
1105 switch (nMonthPos
) // where is the month
1107 case 0: // not found => only day entered
1108 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1110 case 1: // month at the beginning (Jan 01)
1111 pCal
->setValue( CalendarFieldIndex::MONTH
, Abs(nMonth
)-1 );
1116 USHORT nDay
= ImplGetDay(0);
1117 USHORT nYear
= ImplGetYear(0);
1118 if (nDay
== 0 || nDay
> 32) {
1119 pCal
->setValue( CalendarFieldIndex::YEAR
, nYear
);
1122 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1126 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1133 case 3: // month at the end (10 Jan)
1134 pCal
->setValue( CalendarFieldIndex::MONTH
, Abs(nMonth
)-1 );
1138 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1141 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1151 } // switch (nMonthPos)
1154 case 2: // 2 numbers
1156 switch (nMonthPos
) // where is the month
1158 case 0: // not found
1161 sal_uInt32 nExactDateOrder
= (bFormatTurn
? pFormat
->GetExactDateOrder() : 0);
1162 if ( 0xff < nExactDateOrder
&& nExactDateOrder
<= 0xffff )
1163 { // formatted as date and exactly 2 parts
1165 switch ( (nExactDateOrder
>> 8) & 0xff )
1168 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1171 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1174 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1179 switch ( nExactDateOrder
& 0xff )
1182 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1185 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
1188 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1196 if ( !bHadExact
|| !pCal
->isValid() )
1198 if ( !bHadExact
&& nExactDateOrder
)
1199 pCal
->setGregorianDateTime( Date() ); // reset today
1204 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1205 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1206 if ( !pCal
->isValid() ) // 2nd try
1208 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
1209 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1210 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1215 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1216 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
1217 if ( !pCal
->isValid() ) // 2nd try
1219 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
1220 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1221 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1226 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1227 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1228 if ( !pCal
->isValid() ) // 2nd try
1230 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
1231 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
1232 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1242 case 1: // month at the beginning (Jan 01 01)
1244 // The input is valid as MDY in almost any
1245 // constellation, there is no date order (M)YD except if
1246 // set in a format applied.
1247 pCal
->setValue( CalendarFieldIndex::MONTH
, Abs(nMonth
)-1 );
1248 sal_uInt32 nExactDateOrder
= (bFormatTurn
? pFormat
->GetExactDateOrder() : 0);
1249 if ((((nExactDateOrder
>> 8) & 0xff) == 'Y') && ((nExactDateOrder
& 0xff) == 'D'))
1251 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1252 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1256 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1257 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1261 case 2: // month in the middle (10 Jan 94)
1262 pCal
->setValue( CalendarFieldIndex::MONTH
, Abs(nMonth
)-1 );
1265 case MDY
: // yes, "10-Jan-94" is valid
1267 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1268 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1271 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1272 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1279 default: // else, e.g. month at the end (94 10 Jan)
1282 } // switch (nMonthPos)
1285 default: // more than two numbers (31.12.94 8:23) (31.12. 8:23)
1286 switch (nMonthPos
) // where is the month
1288 case 0: // not found
1292 { // find first time number index (should only be 3 or 2 anyway)
1293 for ( USHORT j
= 0; j
< nAnzNums
; j
++ )
1295 if ( nNums
[j
] == nTimePos
- 2 )
1302 // ISO 8601 yyyy-mm-dd forced recognition
1303 DateFormat eDF
= (MayBeIso8601() ? YMD
: DateFmt
);
1307 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1308 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1310 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(2) );
1313 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1314 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
1316 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(2) );
1320 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(2) );
1321 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
1322 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1330 case 1: // month at the beginning (Jan 01 01 8:23)
1335 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1336 pCal
->setValue( CalendarFieldIndex::MONTH
, Abs(nMonth
)-1 );
1337 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1344 case 2: // month in the middle (10 Jan 94 8:23)
1346 pCal
->setValue( CalendarFieldIndex::MONTH
, Abs(nMonth
)-1 );
1350 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1351 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1354 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1355 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1362 default: // else, e.g. month at the end (94 10 Jan 8:23)
1366 } // switch (nMonthPos)
1368 } // switch (nAnzNums)
1370 if ( res
&& pCal
->isValid() )
1372 double fDiff
= DateTime(*pNullDate
) - pCal
->getEpochStart();
1373 fDays
= ::rtl::math::approxFloor( pCal
->getLocalDateTime() );
1375 nTryOrder
= nFormatOrder
; // break for
1380 if ( aOrgCalendar
.Len() )
1381 pCal
->loadCalendar( aOrgCalendar
, pLoc
->getLocale() ); // restore calendar
1383 #if NF_TEST_CALENDAR
1385 using namespace ::com::sun::star
;
1386 struct entry
{ const char* lan
; const char* cou
; const char* cal
; };
1387 const entry cals
[] = {
1388 { "en", "US", "gregorian" },
1389 { "ar", "TN", "hijri" },
1390 { "he", "IL", "jewish" },
1391 { "ja", "JP", "gengou" },
1392 { "ko", "KR", "hanja_yoil" },
1393 { "th", "TH", "buddhist" },
1394 { "zh", "TW", "ROC" },
1397 lang::Locale aLocale
;
1399 sal_Int16 nDay
, nMyMonth
, nYear
, nHour
, nMinute
, nSecond
;
1400 sal_Int16 nDaySet
, nMonthSet
, nYearSet
, nHourSet
, nMinuteSet
, nSecondSet
;
1401 sal_Int16 nZO
, nDST1
, nDST2
, nDST
, nZOmillis
, nDST1millis
, nDST2millis
, nDSTmillis
;
1402 sal_Int32 nZoneInMillis
, nDST1InMillis
, nDST2InMillis
;
1403 uno::Reference
< lang::XMultiServiceFactory
> xSMgr
=
1404 ::comphelper::getProcessServiceFactory();
1405 uno::Reference
< ::com::sun::star::i18n::XExtendedCalendar
> xCal(
1406 xSMgr
->createInstance( ::rtl::OUString(
1407 RTL_CONSTASCII_USTRINGPARAM(
1408 "com.sun.star.i18n.LocaleCalendar" ) ) ),
1410 for ( const entry
* p
= cals
; p
->lan
; ++p
)
1412 aLocale
.Language
= ::rtl::OUString::createFromAscii( p
->lan
);
1413 aLocale
.Country
= ::rtl::OUString::createFromAscii( p
->cou
);
1414 xCal
->loadCalendar( ::rtl::OUString::createFromAscii( p
->cal
),
1416 double nDateTime
= 0.0; // 1-Jan-1970 00:00:00
1417 nZO
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET
);
1418 nZOmillis
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS
);
1419 nZoneInMillis
= static_cast<sal_Int32
>(nZO
) * 60000 +
1420 (nZO
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nZOmillis
);
1421 nDST1
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
1422 nDST1millis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
1423 nDST1InMillis
= static_cast<sal_Int32
>(nDST1
) * 60000 +
1424 (nDST1
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nDST1millis
);
1425 nDateTime
-= (double)(nZoneInMillis
+ nDST1InMillis
) / 1000.0 / 60.0 / 60.0 / 24.0;
1426 xCal
->setDateTime( nDateTime
);
1427 nDST2
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
1428 nDST2millis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
1429 nDST2InMillis
= static_cast<sal_Int32
>(nDST2
) * 60000 +
1430 (nDST2
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nDST2millis
);
1431 if ( nDST1InMillis
!= nDST2InMillis
)
1433 nDateTime
= 0.0 - (double)(nZoneInMillis
+ nDST2InMillis
) / 1000.0 / 60.0 / 60.0 / 24.0;
1434 xCal
->setDateTime( nDateTime
);
1436 nDaySet
= xCal
->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
);
1437 nMonthSet
= xCal
->getValue( i18n::CalendarFieldIndex::MONTH
);
1438 nYearSet
= xCal
->getValue( i18n::CalendarFieldIndex::YEAR
);
1439 nHourSet
= xCal
->getValue( i18n::CalendarFieldIndex::HOUR
);
1440 nMinuteSet
= xCal
->getValue( i18n::CalendarFieldIndex::MINUTE
);
1441 nSecondSet
= xCal
->getValue( i18n::CalendarFieldIndex::SECOND
);
1442 nZO
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET
);
1443 nZOmillis
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS
);
1444 nDST
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
1445 nDSTmillis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
1446 xCal
->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
, nDaySet
);
1447 xCal
->setValue( i18n::CalendarFieldIndex::MONTH
, nMonthSet
);
1448 xCal
->setValue( i18n::CalendarFieldIndex::YEAR
, nYearSet
);
1449 xCal
->setValue( i18n::CalendarFieldIndex::HOUR
, nHourSet
);
1450 xCal
->setValue( i18n::CalendarFieldIndex::MINUTE
, nMinuteSet
);
1451 xCal
->setValue( i18n::CalendarFieldIndex::SECOND
, nSecondSet
);
1452 bValid
= xCal
->isValid();
1453 nDay
= xCal
->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
);
1454 nMyMonth
= xCal
->getValue( i18n::CalendarFieldIndex::MONTH
);
1455 nYear
= xCal
->getValue( i18n::CalendarFieldIndex::YEAR
);
1456 nHour
= xCal
->getValue( i18n::CalendarFieldIndex::HOUR
);
1457 nMinute
= xCal
->getValue( i18n::CalendarFieldIndex::MINUTE
);
1458 nSecond
= xCal
->getValue( i18n::CalendarFieldIndex::SECOND
);
1459 bValid
= bValid
&& nDay
== nDaySet
&& nMyMonth
== nMonthSet
&& nYear
==
1460 nYearSet
&& nHour
== nHourSet
&& nMinute
== nMinuteSet
&& nSecond
1464 #endif // NF_TEST_CALENDAR
1472 //---------------------------------------------------------------------------
1475 // ersten String analysieren
1476 // Alles weg => TRUE
1479 BOOL
ImpSvNumberInputScan::ScanStartString( const String
& rString
,
1480 const SvNumberformat
* pFormat
)
1482 xub_StrLen nPos
= 0;
1485 // First of all, eat leading blanks
1486 SkipBlanks(rString
, nPos
);
1488 // Yes, nMatchedAllStrings should know about the sign position
1489 nSign
= GetSign(rString
, nPos
);
1490 if ( nSign
) // sign?
1491 SkipBlanks(rString
, nPos
);
1493 // #102371# match against format string only if start string is not a sign character
1494 if ( nMatchedAllStrings
&& !(nSign
&& rString
.Len() == 1) )
1495 { // Match against format in any case, so later on for a "x1-2-3" input
1496 // we may distinguish between a xy-m-d (or similar) date and a x0-0-0
1497 // format. No sign detection here!
1498 if ( ScanStringNumFor( rString
, nPos
, pFormat
, 0, TRUE
) )
1499 nMatchedAllStrings
|= nMatchedStartString
;
1501 nMatchedAllStrings
= 0;
1504 if ( GetDecSep(rString
, nPos
) ) // decimal separator in start string
1507 SkipBlanks(rString
, nPos
);
1509 else if ( GetCurrency(rString
, nPos
, pFormat
) ) // currency (DM 1)?
1511 eScannedType
= NUMBERFORMAT_CURRENCY
; // !!! it IS currency !!!
1512 SkipBlanks(rString
, nPos
);
1513 if (nSign
== 0) // no sign yet
1515 nSign
= GetSign(rString
, nPos
);
1516 if ( nSign
) // DM -1
1517 SkipBlanks(rString
, nPos
);
1522 nMonth
= GetMonth(rString
, nPos
);
1523 if ( nMonth
) // month (Jan 1)?
1525 eScannedType
= NUMBERFORMAT_DATE
; // !!! it IS a date !!!
1526 nMonthPos
= 1; // month at the beginning
1528 SkipChar( '.', rString
, nPos
); // abbreviated
1529 SkipBlanks(rString
, nPos
);
1533 nDayOfWeek
= GetDayOfWeek( rString
, nPos
);
1535 { // day of week is just parsed away
1536 eScannedType
= NUMBERFORMAT_DATE
; // !!! it IS a date !!!
1537 if ( nPos
< rString
.Len() )
1539 if ( nDayOfWeek
< 0 )
1541 if ( rString
.GetChar( nPos
) == '.' )
1546 SkipBlanks(rString
, nPos
);
1547 SkipString( pFormatter
->GetLocaleData()->getLongDateDayOfWeekSep(), rString
, nPos
);
1549 SkipBlanks(rString
, nPos
);
1550 nMonth
= GetMonth(rString
, nPos
);
1551 if ( nMonth
) // month (Jan 1)?
1553 nMonthPos
= 1; // month a the beginning
1555 SkipChar( '.', rString
, nPos
); // abbreviated
1556 SkipBlanks(rString
, nPos
);
1563 // skip any trailing '-' or '/' chars
1564 if (nPos
< rString
.Len())
1566 while (SkipChar ('-', rString
, nPos
) || SkipChar ('/', rString
, nPos
)) {
1570 if (nPos
< rString
.Len()) // not everything consumed
1572 // Does input StartString equal StartString of format?
1573 // This time with sign detection!
1574 if ( !ScanStringNumFor( rString
, nPos
, pFormat
, 0 ) )
1575 return MatchedReturn();
1582 //---------------------------------------------------------------------------
1585 // String in der Mitte analysieren
1586 // Alles weg => TRUE
1589 BOOL
ImpSvNumberInputScan::ScanMidString( const String
& rString
,
1590 USHORT nStringPos
, const SvNumberformat
* pFormat
)
1592 xub_StrLen nPos
= 0;
1593 short eOldScannedType
= eScannedType
;
1595 if ( nMatchedAllStrings
)
1596 { // Match against format in any case, so later on for a "1-2-3-4" input
1597 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
1599 if ( ScanStringNumFor( rString
, 0, pFormat
, nStringPos
) )
1600 nMatchedAllStrings
|= nMatchedMidString
;
1602 nMatchedAllStrings
= 0;
1605 SkipBlanks(rString
, nPos
);
1606 if (GetDecSep(rString
, nPos
)) // decimal separator?
1608 if (nDecPos
== 1 || nDecPos
== 3) // .12.4 or 1.E2.1
1609 return MatchedReturn();
1610 else if (nDecPos
== 2) // . dup: 12.4.
1612 if (bDecSepInDateSeps
) // . also date separator
1614 if ( eScannedType
!= NUMBERFORMAT_UNDEFINED
&&
1615 eScannedType
!= NUMBERFORMAT_DATE
&&
1616 eScannedType
!= NUMBERFORMAT_DATETIME
) // already another type
1617 return MatchedReturn();
1618 if (eScannedType
== NUMBERFORMAT_UNDEFINED
)
1619 eScannedType
= NUMBERFORMAT_DATE
; // !!! it IS a date
1620 SkipBlanks(rString
, nPos
);
1623 return MatchedReturn();
1627 nDecPos
= 2; // . in mid string
1628 SkipBlanks(rString
, nPos
);
1631 else if ( ((eScannedType
& NUMBERFORMAT_TIME
) == NUMBERFORMAT_TIME
)
1632 && GetTime100SecSep( rString
, nPos
) )
1633 { // hundredth seconds separator
1635 return MatchedReturn();
1636 nDecPos
= 2; // . in mid string
1637 SkipBlanks(rString
, nPos
);
1640 if (SkipChar('/', rString
, nPos
)) // fraction?
1642 if ( eScannedType
!= NUMBERFORMAT_UNDEFINED
// already another type
1643 && eScannedType
!= NUMBERFORMAT_DATE
) // except date
1644 return MatchedReturn(); // => jan/31/1994
1645 else if ( eScannedType
!= NUMBERFORMAT_DATE
// analyzed date until now
1646 && ( eSetType
== NUMBERFORMAT_FRACTION
// and preset was fraction
1647 || (nAnzNums
== 3 // or 3 numbers
1648 && nStringPos
> 2) ) ) // and what ???
1650 SkipBlanks(rString
, nPos
);
1651 eScannedType
= NUMBERFORMAT_FRACTION
; // !!! it IS a fraction
1654 nPos
--; // put '/' back
1657 if (GetThousandSep(rString
, nPos
, nStringPos
)) // 1,000
1659 if ( eScannedType
!= NUMBERFORMAT_UNDEFINED
// already another type
1660 && eScannedType
!= NUMBERFORMAT_CURRENCY
) // except currency
1661 return MatchedReturn();
1665 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
1666 const String
& rDate
= pFormatter
->GetDateSep();
1667 const String
& rTime
= pLoc
->getTimeSep();
1668 sal_Unicode cTime
= rTime
.GetChar(0);
1669 SkipBlanks(rString
, nPos
);
1670 if ( SkipString(rDate
, rString
, nPos
) // 10., 10-, 10/
1671 || ((cTime
!= '.') && SkipChar('.', rString
, nPos
)) // TRICKY:
1672 || ((cTime
!= '/') && SkipChar('/', rString
, nPos
)) // short boolean
1673 || ((cTime
!= '-') && SkipChar('-', rString
, nPos
)) ) // evaluation!
1675 if ( eScannedType
!= NUMBERFORMAT_UNDEFINED
// already another type
1676 && eScannedType
!= NUMBERFORMAT_DATE
) // except date
1677 return MatchedReturn();
1678 SkipBlanks(rString
, nPos
);
1679 eScannedType
= NUMBERFORMAT_DATE
; // !!! it IS a date
1680 short nTmpMonth
= GetMonth(rString
, nPos
); // 10. Jan 94
1681 if (nMonth
&& nTmpMonth
) // month dup
1682 return MatchedReturn();
1686 nMonthPos
= 2; // month in the middle
1687 if ( nMonth
< 0 && SkipChar( '.', rString
, nPos
) )
1688 ; // short month may be abbreviated Jan.
1689 else if ( SkipChar( '-', rString
, nPos
) )
1690 ; // #79632# recognize 17-Jan-2001 to be a date
1691 // #99065# short and long month name
1693 SkipString( pLoc
->getLongDateMonthSep(), rString
, nPos
);
1694 SkipBlanks(rString
, nPos
);
1698 short nTempMonth
= GetMonth(rString
, nPos
); // month in the middle (10 Jan 94)
1701 if (nMonth
!= 0) // month dup
1702 return MatchedReturn();
1703 if ( eScannedType
!= NUMBERFORMAT_UNDEFINED
// already another type
1704 && eScannedType
!= NUMBERFORMAT_DATE
) // except date
1705 return MatchedReturn();
1706 eScannedType
= NUMBERFORMAT_DATE
; // !!! it IS a date
1707 nMonth
= nTempMonth
;
1708 nMonthPos
= 2; // month in the middle
1710 SkipChar( '.', rString
, nPos
); // abbreviated
1711 SkipString( pLoc
->getLongDateMonthSep(), rString
, nPos
);
1712 SkipBlanks(rString
, nPos
);
1715 if ( SkipChar('E', rString
, nPos
) // 10E, 10e, 10,Ee
1716 || SkipChar('e', rString
, nPos
) )
1718 if (eScannedType
!= NUMBERFORMAT_UNDEFINED
) // already another type
1719 return MatchedReturn();
1722 SkipBlanks(rString
, nPos
);
1723 eScannedType
= NUMBERFORMAT_SCIENTIFIC
; // !!! it IS scientific
1724 if ( nThousand
+2 == nAnzNums
// special case 1.E2
1726 nDecPos
= 3; // 1,100.E2 1,100,100.E3
1728 nESign
= GetESign(rString
, nPos
); // signed exponent?
1729 SkipBlanks(rString
, nPos
);
1732 if ( SkipString(rTime
, rString
, nPos
) ) // time separator?
1734 if (nDecPos
) // already . => maybe error
1736 if (bDecSepInDateSeps
) // . also date sep
1738 if ( eScannedType
!= NUMBERFORMAT_DATE
&& // already another type than date
1739 eScannedType
!= NUMBERFORMAT_DATETIME
) // or date time
1740 return MatchedReturn();
1741 if (eScannedType
== NUMBERFORMAT_DATE
)
1742 nDecPos
= 0; // reset for time transition
1745 return MatchedReturn();
1747 if ( ( eScannedType
== NUMBERFORMAT_DATE
// already date type
1748 || eScannedType
== NUMBERFORMAT_DATETIME
) // or date time
1749 && nAnzNums
> 3) // and more than 3 numbers? (31.Dez.94 8:23)
1751 SkipBlanks(rString
, nPos
);
1752 eScannedType
= NUMBERFORMAT_DATETIME
; // !!! it IS date with time
1754 else if ( eScannedType
!= NUMBERFORMAT_UNDEFINED
// already another type
1755 && eScannedType
!= NUMBERFORMAT_TIME
) // except time
1756 return MatchedReturn();
1759 SkipBlanks(rString
, nPos
);
1760 eScannedType
= NUMBERFORMAT_TIME
; // !!! it IS a time
1763 nTimePos
= nStringPos
+ 1;
1766 if (nPos
< rString
.Len())
1768 switch (eScannedType
)
1770 case NUMBERFORMAT_DATE
:
1771 if (nMonthPos
== 1 && pLoc
->getLongDateFormat() == MDY
)
1773 // #68232# recognize long date separators like ", " in "September 5, 1999"
1774 if (SkipString( pLoc
->getLongDateDaySep(), rString
, nPos
))
1775 SkipBlanks( rString
, nPos
);
1777 else if (nStringPos
== 5 && nPos
== 0 && rString
.Len() == 1 &&
1778 rString
.GetChar(0) == 'T' && MayBeIso8601())
1780 // ISO 8601 combined date and time, yyyy-mm-ddThh:mm
1784 #if NF_RECOGNIZE_ISO8601_TIMEZONES
1785 case NUMBERFORMAT_DATETIME
:
1786 if (nPos
== 0 && rString
.Len() == 1 && nStringPos
>= 9 &&
1789 // ISO 8601 timezone offset
1790 switch (rString
.GetChar(0))
1794 if (nStringPos
== nAnzStrings
-2 ||
1795 nStringPos
== nAnzStrings
-4)
1797 ++nPos
; // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy]
1798 // nTimezonePos needed for GetTimeRef()
1800 nTimezonePos
= nStringPos
+ 1;
1804 if (nTimezonePos
&& nStringPos
>= 11 &&
1805 nStringPos
== nAnzStrings
-2)
1806 ++nPos
; // yyyy-mm-ddThh:mm[:ss]+xx:yy
1815 if (nPos
< rString
.Len()) // not everything consumed?
1817 if ( nMatchedAllStrings
& ~nMatchedVirgin
)
1818 eScannedType
= eOldScannedType
;
1827 //---------------------------------------------------------------------------
1830 // Schlussteil analysieren
1831 // Alles weg => TRUE
1834 BOOL
ImpSvNumberInputScan::ScanEndString( const String
& rString
,
1835 const SvNumberformat
* pFormat
)
1837 xub_StrLen nPos
= 0;
1839 if ( nMatchedAllStrings
)
1840 { // Match against format in any case, so later on for a "1-2-3-4" input
1841 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
1843 if ( ScanStringNumFor( rString
, 0, pFormat
, 0xFFFF ) )
1844 nMatchedAllStrings
|= nMatchedEndString
;
1846 nMatchedAllStrings
= 0;
1849 SkipBlanks(rString
, nPos
);
1850 if (GetDecSep(rString
, nPos
)) // decimal separator?
1852 if (nDecPos
== 1 || nDecPos
== 3) // .12.4 or 12.E4.
1853 return MatchedReturn();
1854 else if (nDecPos
== 2) // . dup: 12.4.
1856 if (bDecSepInDateSeps
) // . also date sep
1858 if ( eScannedType
!= NUMBERFORMAT_UNDEFINED
&&
1859 eScannedType
!= NUMBERFORMAT_DATE
&&
1860 eScannedType
!= NUMBERFORMAT_DATETIME
) // already another type
1861 return MatchedReturn();
1862 if (eScannedType
== NUMBERFORMAT_UNDEFINED
)
1863 eScannedType
= NUMBERFORMAT_DATE
; // !!! it IS a date
1864 SkipBlanks(rString
, nPos
);
1867 return MatchedReturn();
1871 nDecPos
= 3; // . in end string
1872 SkipBlanks(rString
, nPos
);
1876 if ( nSign
== 0 // conflict - not signed
1877 && eScannedType
!= NUMBERFORMAT_DATE
) // and not date
1878 //!? catch time too?
1880 nSign
= GetSign(rString
, nPos
); // 1- DM
1881 if (nNegCheck
) // '(' as sign
1882 return MatchedReturn();
1885 SkipBlanks(rString
, nPos
);
1886 if (nNegCheck
&& SkipChar(')', rString
, nPos
)) // skip ')' if appropriate
1889 SkipBlanks(rString
, nPos
);
1892 if ( GetCurrency(rString
, nPos
, pFormat
) ) // currency symbol?
1894 if (eScannedType
!= NUMBERFORMAT_UNDEFINED
) // currency dup
1895 return MatchedReturn();
1898 SkipBlanks(rString
, nPos
);
1899 eScannedType
= NUMBERFORMAT_CURRENCY
;
1900 } // behind currency a '-' is allowed
1901 if (nSign
== 0) // not signed yet
1903 nSign
= GetSign(rString
, nPos
); // DM -
1904 SkipBlanks(rString
, nPos
);
1905 if (nNegCheck
) // 3 DM (
1906 return MatchedReturn();
1908 if ( nNegCheck
&& eScannedType
== NUMBERFORMAT_CURRENCY
1909 && SkipChar(')', rString
, nPos
) )
1911 nNegCheck
= 0; // ')' skipped
1912 SkipBlanks(rString
, nPos
); // only if currency
1916 if ( SkipChar('%', rString
, nPos
) ) // 1 %
1918 if (eScannedType
!= NUMBERFORMAT_UNDEFINED
) // already another type
1919 return MatchedReturn();
1920 SkipBlanks(rString
, nPos
);
1921 eScannedType
= NUMBERFORMAT_PERCENT
;
1924 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
1925 const String
& rDate
= pFormatter
->GetDateSep();
1926 const String
& rTime
= pLoc
->getTimeSep();
1927 if ( SkipString(rTime
, rString
, nPos
) ) // 10:
1929 if (nDecPos
) // already , => error
1930 return MatchedReturn();
1931 if (eScannedType
== NUMBERFORMAT_DATE
&& nAnzNums
> 2) // 31.Dez.94 8:
1933 SkipBlanks(rString
, nPos
);
1934 eScannedType
= NUMBERFORMAT_DATETIME
;
1936 else if (eScannedType
!= NUMBERFORMAT_UNDEFINED
&&
1937 eScannedType
!= NUMBERFORMAT_TIME
) // already another type
1938 return MatchedReturn();
1941 SkipBlanks(rString
, nPos
);
1942 eScannedType
= NUMBERFORMAT_TIME
;
1945 nTimePos
= nAnzStrings
;
1948 sal_Unicode cTime
= rTime
.GetChar(0);
1949 if ( SkipString(rDate
, rString
, nPos
) // 10., 10-, 10/
1950 || ((cTime
!= '.') && SkipChar('.', rString
, nPos
)) // TRICKY:
1951 || ((cTime
!= '/') && SkipChar('/', rString
, nPos
)) // short boolean
1952 || ((cTime
!= '-') && SkipChar('-', rString
, nPos
)) ) // evaluation!
1954 if (eScannedType
!= NUMBERFORMAT_UNDEFINED
&&
1955 eScannedType
!= NUMBERFORMAT_DATE
) // already another type
1956 return MatchedReturn();
1959 SkipBlanks(rString
, nPos
);
1960 eScannedType
= NUMBERFORMAT_DATE
;
1962 short nTmpMonth
= GetMonth(rString
, nPos
); // 10. Jan
1963 if (nMonth
&& nTmpMonth
) // month dup
1964 return MatchedReturn();
1968 nMonthPos
= 3; // month at end
1970 SkipChar( '.', rString
, nPos
); // abbreviated
1971 SkipBlanks(rString
, nPos
);
1975 short nTempMonth
= GetMonth(rString
, nPos
); // 10 Jan
1978 if (nMonth
) // month dup
1979 return MatchedReturn();
1980 if (eScannedType
!= NUMBERFORMAT_UNDEFINED
&&
1981 eScannedType
!= NUMBERFORMAT_DATE
) // already another type
1982 return MatchedReturn();
1983 eScannedType
= NUMBERFORMAT_DATE
;
1984 nMonth
= nTempMonth
;
1985 nMonthPos
= 3; // month at end
1987 SkipChar( '.', rString
, nPos
); // abbreviated
1988 SkipBlanks(rString
, nPos
);
1991 xub_StrLen nOrigPos
= nPos
;
1992 if (GetTimeAmPm(rString
, nPos
))
1994 if (eScannedType
!= NUMBERFORMAT_UNDEFINED
&&
1995 eScannedType
!= NUMBERFORMAT_TIME
&&
1996 eScannedType
!= NUMBERFORMAT_DATETIME
) // already another type
1997 return MatchedReturn();
2000 // If not already scanned as time, 6.78am does not result in 6
2001 // seconds and 78 hundredths in the morning. Keep as suffix.
2002 if (eScannedType
!= NUMBERFORMAT_TIME
&& nDecPos
== 2 && nAnzNums
== 2)
2003 nPos
= nOrigPos
; // rewind am/pm
2006 SkipBlanks(rString
, nPos
);
2007 if ( eScannedType
!= NUMBERFORMAT_DATETIME
)
2008 eScannedType
= NUMBERFORMAT_TIME
;
2013 if ( nNegCheck
&& SkipChar(')', rString
, nPos
) )
2015 if (eScannedType
== NUMBERFORMAT_CURRENCY
) // only if currency
2017 nNegCheck
= 0; // skip ')'
2018 SkipBlanks(rString
, nPos
);
2021 return MatchedReturn();
2024 if ( nPos
< rString
.Len() &&
2025 (eScannedType
== NUMBERFORMAT_DATE
2026 || eScannedType
== NUMBERFORMAT_DATETIME
) )
2027 { // day of week is just parsed away
2028 xub_StrLen nOldPos
= nPos
;
2029 const String
& rSep
= pFormatter
->GetLocaleData()->getLongDateDayOfWeekSep();
2030 if ( StringContains( rSep
, rString
, nPos
) )
2032 nPos
= nPos
+ rSep
.Len();
2033 SkipBlanks(rString
, nPos
);
2035 int nDayOfWeek
= GetDayOfWeek( rString
, nPos
);
2038 if ( nPos
< rString
.Len() )
2040 if ( nDayOfWeek
< 0 )
2042 if ( rString
.GetChar( nPos
) == '.' )
2045 SkipBlanks(rString
, nPos
);
2052 #if NF_RECOGNIZE_ISO8601_TIMEZONES
2053 if (nPos
== 0 && eScannedType
== NUMBERFORMAT_DATETIME
&&
2054 rString
.Len() == 1 && rString
.GetChar(0) == 'Z' && MayBeIso8601())
2056 // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ
2061 if (nPos
< rString
.Len()) // everything consumed?
2063 // does input EndString equal EndString in Format?
2064 if ( !ScanStringNumFor( rString
, nPos
, pFormat
, 0xFFFF ) )
2072 BOOL
ImpSvNumberInputScan::ScanStringNumFor(
2073 const String
& rString
, // String to scan
2074 xub_StrLen nPos
, // Position until which was consumed
2075 const SvNumberformat
* pFormat
, // The format to match
2076 USHORT nString
, // Substring of format, 0xFFFF => last
2077 BOOL bDontDetectNegation
// Suppress sign detection
2082 const ::utl::TransliterationWrapper
* pTransliteration
= pFormatter
->GetTransliteration();
2084 String
aString( rString
);
2085 BOOL bFound
= FALSE
;
2087 BOOL bContinue
= TRUE
;
2091 // Don't try "lower" subformats ff the very first match was the second
2092 // or third subformat.
2093 nSub
= nStringScanNumFor
;
2095 { // Step through subformats, first positive, then negative, then
2096 // other, but not the last (text) subformat.
2097 pStr
= pFormat
->GetNumForString( nSub
, nString
, TRUE
);
2098 if ( pStr
&& pTransliteration
->isEqual( aString
, *pStr
) )
2103 else if ( nSub
< 2 )
2107 } while ( bContinue
);
2108 if ( !bFound
&& bFirst
&& nPos
)
2109 { // try remaining substring
2111 aString
.Erase( 0, nPos
);
2114 } while ( bContinue
);
2118 if ( !bDontDetectNegation
&& (nString
== 0) && !bFirst
&& (nSign
< 0)
2119 && pFormat
->IsNegativeRealNegative() )
2120 { // simply negated twice? --1
2121 aString
.EraseAllChars( ' ' );
2122 if ( (aString
.Len() == 1) && (aString
.GetChar(0) == '-') )
2125 nStringScanSign
= -1;
2132 else if ( !bDontDetectNegation
&& (nSub
== 1) &&
2133 pFormat
->IsNegativeRealNegative() )
2135 if ( nStringScanSign
< 0 )
2137 if ( (nSign
< 0) && (nStringScanNumFor
!= 1) )
2138 nStringScanSign
= 1; // triple negated --1 yyy
2140 else if ( nStringScanSign
== 0 )
2143 { // nSign and nStringScanSign will be combined later,
2144 // flip sign if doubly negated
2145 if ( (nString
== 0) && !bFirst
2146 && SvNumberformat::HasStringNegativeSign( aString
) )
2147 nStringScanSign
= -1; // direct double negation
2148 else if ( pFormat
->IsNegativeWithoutSign() )
2149 nStringScanSign
= -1; // indirect double negation
2152 nStringScanSign
= -1;
2155 nStringScanSign
= -1;
2157 nStringScanNumFor
= nSub
;
2162 //---------------------------------------------------------------------------
2163 // IsNumberFormatMain
2165 // Recognizes types of number, exponential, fraction, percent, currency, date, time.
2166 // Else text => return FALSE
2168 BOOL
ImpSvNumberInputScan::IsNumberFormatMain(
2169 const String
& rString
, // string to be analyzed
2170 double& , // OUT: result as number, if possible
2171 const SvNumberformat
* pFormat
) // maybe number format set to match against
2174 NumberStringDivision( rString
); // breakdown into strings and numbers
2175 if (nAnzStrings
>= SV_MAX_ANZ_INPUT_STRINGS
) // too many elements
2176 return FALSE
; // Njet, Nope, ...
2178 if (nAnzNums
== 0) // no number in input
2180 if ( nAnzStrings
> 0 )
2182 // Here we may change the original, we don't need it anymore.
2183 // This saves copies and ToUpper() in GetLogical() and is faster.
2184 String
& rStrArray
= sStrArray
[0];
2185 rStrArray
.EraseTrailingChars( ' ' );
2186 rStrArray
.EraseLeadingChars( ' ' );
2187 nLogical
= GetLogical( rStrArray
);
2190 eScannedType
= NUMBERFORMAT_LOGICAL
; // !!! it's a BOOLEAN
2191 nMatchedAllStrings
&= ~nMatchedVirgin
;
2195 return FALSE
; // simple text
2198 return FALSE
; // simple text
2201 USHORT i
= 0; // mark any symbol
2202 USHORT j
= 0; // mark only numbers
2206 case 1 : // Exactly 1 number in input
2207 { // nAnzStrings >= 1
2208 if (GetNextNumber(i
,j
)) // i=1,0
2209 { // Number at start
2210 if (eSetType
== NUMBERFORMAT_FRACTION
) // Fraction 1 = 1/1
2212 if (i
>= nAnzStrings
|| // no end string nor decimal separator
2213 sStrArray
[i
] == pFormatter
->GetNumDecimalSep())
2215 eScannedType
= NUMBERFORMAT_FRACTION
;
2216 nMatchedAllStrings
&= ~nMatchedVirgin
;
2222 { // Analyze start string
2223 if (!ScanStartString( sStrArray
[i
], pFormat
)) // i=0
2224 return FALSE
; // already an error
2225 i
++; // next symbol, i=1
2227 GetNextNumber(i
,j
); // i=1,2
2228 if (eSetType
== NUMBERFORMAT_FRACTION
) // Fraction -1 = -1/1
2230 if (nSign
&& !nNegCheck
&& // Sign +, -
2231 eScannedType
== NUMBERFORMAT_UNDEFINED
&& // not date or currency
2232 nDecPos
== 0 && // no previous decimal separator
2233 (i
>= nAnzStrings
|| // no end string nor decimal separator
2234 sStrArray
[i
] == pFormatter
->GetNumDecimalSep())
2237 eScannedType
= NUMBERFORMAT_FRACTION
;
2238 nMatchedAllStrings
&= ~nMatchedVirgin
;
2242 if (i
< nAnzStrings
&& !ScanEndString( sStrArray
[i
], pFormat
))
2246 case 2 : // Exactly 2 numbers in input
2247 { // nAnzStrings >= 3
2248 if (!GetNextNumber(i
,j
)) // i=1,0
2249 { // Analyze start string
2250 if (!ScanStartString( sStrArray
[i
], pFormat
))
2251 return FALSE
; // already an error
2254 GetNextNumber(i
,j
); // i=1,2
2255 if ( !ScanMidString( sStrArray
[i
], i
, pFormat
) )
2257 i
++; // next symbol, i=2,3
2258 GetNextNumber(i
,j
); // i=3,4
2259 if (i
< nAnzStrings
&& !ScanEndString( sStrArray
[i
], pFormat
))
2261 if (eSetType
== NUMBERFORMAT_FRACTION
) // -1,200. as fraction
2263 if (!nNegCheck
&& // no sign '('
2264 eScannedType
== NUMBERFORMAT_UNDEFINED
&&
2265 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
2268 eScannedType
= NUMBERFORMAT_FRACTION
;
2269 nMatchedAllStrings
&= ~nMatchedVirgin
;
2275 case 3 : // Exactly 3 numbers in input
2276 { // nAnzStrings >= 5
2277 if (!GetNextNumber(i
,j
)) // i=1,0
2278 { // Analyze start string
2279 if (!ScanStartString( sStrArray
[i
], pFormat
))
2280 return FALSE
; // already an error
2282 if (nDecPos
== 1) // decimal separator at start => error
2285 GetNextNumber(i
,j
); // i=1,2
2286 if ( !ScanMidString( sStrArray
[i
], i
, pFormat
) )
2289 if (eScannedType
== NUMBERFORMAT_SCIENTIFIC
) // E only at end
2291 GetNextNumber(i
,j
); // i=3,4
2292 if ( !ScanMidString( sStrArray
[i
], i
, pFormat
) )
2295 GetNextNumber(i
,j
); // i=5,6
2296 if (i
< nAnzStrings
&& !ScanEndString( sStrArray
[i
], pFormat
))
2298 if (eSetType
== NUMBERFORMAT_FRACTION
) // -1,200,100. as fraction
2300 if (!nNegCheck
&& // no sign '('
2301 eScannedType
== NUMBERFORMAT_UNDEFINED
&&
2302 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
2305 eScannedType
= NUMBERFORMAT_FRACTION
;
2306 nMatchedAllStrings
&= ~nMatchedVirgin
;
2310 if ( eScannedType
== NUMBERFORMAT_FRACTION
&& nDecPos
)
2311 return FALSE
; // #36857# not a real fraction
2314 default: // More than 3 numbers in input
2315 { // nAnzStrings >= 7
2316 if (!GetNextNumber(i
,j
)) // i=1,0
2317 { // Analyze startstring
2318 if (!ScanStartString( sStrArray
[i
], pFormat
))
2319 return FALSE
; // already an error
2321 if (nDecPos
== 1) // decimal separator at start => error
2324 GetNextNumber(i
,j
); // i=1,2
2325 if ( !ScanMidString( sStrArray
[i
], i
, pFormat
) )
2328 USHORT nThOld
= 10; // just not 0 or 1
2329 while (nThOld
!= nThousand
&& j
< nAnzNums
-1)
2330 // Execute at least one time
2331 // but leave one number.
2332 { // Loop over group separators
2334 if (eScannedType
== NUMBERFORMAT_SCIENTIFIC
) // E only at end
2337 if ( i
< nAnzStrings
&& !ScanMidString( sStrArray
[i
], i
, pFormat
) )
2341 if (eScannedType
== NUMBERFORMAT_DATE
|| // long date or
2342 eScannedType
== NUMBERFORMAT_TIME
|| // long time or
2343 eScannedType
== NUMBERFORMAT_UNDEFINED
) // long number
2345 for (USHORT k
= j
; k
< nAnzNums
-1; k
++)
2347 if (eScannedType
== NUMBERFORMAT_SCIENTIFIC
) // E only at endd
2350 if ( i
< nAnzStrings
&& !ScanMidString( sStrArray
[i
], i
, pFormat
) )
2356 if (i
< nAnzStrings
&& !ScanEndString( sStrArray
[i
], pFormat
))
2358 if (eSetType
== NUMBERFORMAT_FRACTION
) // -1,200,100. as fraction
2360 if (!nNegCheck
&& // no sign '('
2361 eScannedType
== NUMBERFORMAT_UNDEFINED
&&
2362 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
2365 eScannedType
= NUMBERFORMAT_FRACTION
;
2366 nMatchedAllStrings
&= ~nMatchedVirgin
;
2370 if ( eScannedType
== NUMBERFORMAT_FRACTION
&& nDecPos
)
2371 return FALSE
; // #36857# not a real fraction
2375 if (eScannedType
== NUMBERFORMAT_UNDEFINED
)
2377 nMatchedAllStrings
&= ~nMatchedVirgin
;
2378 // did match including nMatchedUsedAsReturn
2379 BOOL bDidMatch
= (nMatchedAllStrings
!= 0);
2380 if ( nMatchedAllStrings
)
2382 BOOL bMatch
= (pFormat
? pFormat
->IsNumForStringElementCountEqual(
2383 nStringScanNumFor
, nAnzStrings
, nAnzNums
) : FALSE
);
2385 nMatchedAllStrings
= 0;
2387 if ( nMatchedAllStrings
)
2388 eScannedType
= eSetType
;
2389 else if ( bDidMatch
)
2392 eScannedType
= NUMBERFORMAT_NUMBER
;
2393 // everything else should have been recognized by now
2395 else if ( eScannedType
== NUMBERFORMAT_DATE
)
2396 { // the very relaxed date input checks may interfere with a preset format
2397 nMatchedAllStrings
&= ~nMatchedVirgin
;
2398 BOOL bWasReturn
= ((nMatchedAllStrings
& nMatchedUsedAsReturn
) != 0);
2399 if ( nMatchedAllStrings
)
2401 BOOL bMatch
= (pFormat
? pFormat
->IsNumForStringElementCountEqual(
2402 nStringScanNumFor
, nAnzStrings
, nAnzNums
) : FALSE
);
2404 nMatchedAllStrings
= 0;
2406 if ( nMatchedAllStrings
)
2407 eScannedType
= eSetType
;
2408 else if ( bWasReturn
)
2412 nMatchedAllStrings
= 0; // reset flag to no substrings matched
2418 //---------------------------------------------------------------------------
2419 // return TRUE or FALSE depending on the nMatched... state and remember usage
2420 BOOL
ImpSvNumberInputScan::MatchedReturn()
2422 if ( nMatchedAllStrings
& ~nMatchedVirgin
)
2424 nMatchedAllStrings
|= nMatchedUsedAsReturn
;
2431 //---------------------------------------------------------------------------
2432 // Initialize uppercase months and weekdays
2434 void ImpSvNumberInputScan::InitText()
2436 sal_Int32 j
, nElems
;
2437 const CharClass
* pChrCls
= pFormatter
->GetCharClass();
2438 const CalendarWrapper
* pCal
= pFormatter
->GetCalendar();
2439 delete [] pUpperMonthText
;
2440 delete [] pUpperAbbrevMonthText
;
2441 ::com::sun::star::uno::Sequence
< ::com::sun::star::i18n::CalendarItem
> xElems
2442 = pCal
->getMonths();
2443 nElems
= xElems
.getLength();
2444 pUpperMonthText
= new String
[nElems
];
2445 pUpperAbbrevMonthText
= new String
[nElems
];
2446 for ( j
=0; j
<nElems
; j
++ )
2448 pUpperMonthText
[j
] = pChrCls
->upper( xElems
[j
].FullName
);
2449 pUpperAbbrevMonthText
[j
] = pChrCls
->upper( xElems
[j
].AbbrevName
);
2451 delete [] pUpperDayText
;
2452 delete [] pUpperAbbrevDayText
;
2453 xElems
= pCal
->getDays();
2454 nElems
= xElems
.getLength();
2455 pUpperDayText
= new String
[nElems
];
2456 pUpperAbbrevDayText
= new String
[nElems
];
2457 for ( j
=0; j
<nElems
; j
++ )
2459 pUpperDayText
[j
] = pChrCls
->upper( xElems
[j
].FullName
);
2460 pUpperAbbrevDayText
[j
] = pChrCls
->upper( xElems
[j
].AbbrevName
);
2462 bTextInitialized
= TRUE
;
2466 //===========================================================================
2469 //---------------------------------------------------------------------------
2472 // MUST be called if International/Locale is changed
2474 void ImpSvNumberInputScan::ChangeIntl()
2476 sal_Unicode cDecSep
= pFormatter
->GetNumDecimalSep().GetChar(0);
2477 bDecSepInDateSeps
= ( cDecSep
== '-' ||
2480 cDecSep
== pFormatter
->GetDateSep().GetChar(0) );
2481 bTextInitialized
= FALSE
;
2482 aUpperCurrSymbol
.Erase();
2486 //---------------------------------------------------------------------------
2489 void ImpSvNumberInputScan::ChangeNullDate(
2495 *pNullDate
= Date(Day
, Month
, Year
);
2497 pNullDate
= new Date(Day
, Month
, Year
);
2501 //---------------------------------------------------------------------------
2504 // => does rString represent a number (also date, time et al)
2506 BOOL
ImpSvNumberInputScan::IsNumberFormat(
2507 const String
& rString
, // string to be analyzed
2508 short& F_Type
, // IN: old type, OUT: new type
2509 double& fOutNumber
, // OUT: number if convertable
2510 const SvNumberformat
* pFormat
) // maybe a number format to match against
2514 BOOL res
; // return value
2515 eSetType
= F_Type
; // old type set
2517 if ( !rString
.Len() )
2519 else if (rString
.Len() > 308) // arbitrary
2523 // NoMoreUpperNeeded, all comparisons on UpperCase
2524 aString
= pFormatter
->GetCharClass()->upper( rString
);
2525 // convert native number to ASCII if necessary
2526 TransformInput( aString
);
2527 res
= IsNumberFormatMain( aString
, fOutNumber
, pFormat
);
2532 if ( nNegCheck
// ')' not found for '('
2533 || (nSign
&& (eScannedType
== NUMBERFORMAT_DATE
2534 || eScannedType
== NUMBERFORMAT_DATETIME
))
2535 ) // signed date/datetime
2538 { // check count of partial number strings
2539 switch (eScannedType
)
2541 case NUMBERFORMAT_PERCENT
:
2542 case NUMBERFORMAT_CURRENCY
:
2543 case NUMBERFORMAT_NUMBER
:
2544 if (nDecPos
== 1) // .05
2546 // matched MidStrings function like group separators
2547 if ( nMatchedAllStrings
)
2548 nThousand
= nAnzNums
- 1;
2549 else if ( nAnzNums
!= 1 )
2552 else if (nDecPos
== 2) // 1.05
2554 // matched MidStrings function like group separators
2555 if ( nMatchedAllStrings
)
2556 nThousand
= nAnzNums
- 1;
2557 else if ( nAnzNums
!= nThousand
+2 )
2560 else // 1,100 or 1,100.
2562 // matched MidStrings function like group separators
2563 if ( nMatchedAllStrings
)
2564 nThousand
= nAnzNums
- 1;
2565 else if ( nAnzNums
!= nThousand
+1 )
2570 case NUMBERFORMAT_SCIENTIFIC
: // 1.0e-2
2571 if (nDecPos
== 1) // .05
2576 else if (nDecPos
== 2) // 1.05
2578 if (nAnzNums
!= nThousand
+3)
2581 else // 1,100 or 1,100.
2583 if (nAnzNums
!= nThousand
+2)
2588 case NUMBERFORMAT_DATE
:
2590 { // month name and numbers
2601 case NUMBERFORMAT_TIME
:
2603 { // hundredth seconds included
2614 case NUMBERFORMAT_DATETIME
:
2616 { // month name and numbers
2618 { // hundredth seconds included
2631 { // hundredth seconds included
2650 { // we finally have a number
2651 switch (eScannedType
)
2653 case NUMBERFORMAT_LOGICAL
:
2655 fOutNumber
= 1.0; // True
2656 else if (nLogical
== -1)
2657 fOutNumber
= 0.0; // False
2659 res
= FALSE
; // Oops
2662 case NUMBERFORMAT_PERCENT
:
2663 case NUMBERFORMAT_CURRENCY
:
2664 case NUMBERFORMAT_NUMBER
:
2665 case NUMBERFORMAT_SCIENTIFIC
:
2666 case NUMBERFORMAT_DEFINED
: // if no category detected handle as number
2668 if ( nDecPos
== 1 ) // . at start
2669 sResString
.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "0." ) );
2673 for ( k
= 0; k
<= nThousand
; k
++)
2674 sResString
+= sStrArray
[nNums
[k
]]; // integer part
2675 if ( nDecPos
== 2 && k
< nAnzNums
) // . somewhere
2678 USHORT nStop
= (eScannedType
== NUMBERFORMAT_SCIENTIFIC
?
2679 nAnzNums
-1 : nAnzNums
);
2680 for ( ; k
< nStop
; k
++)
2681 sResString
+= sStrArray
[nNums
[k
]]; // fractional part
2684 if (eScannedType
!= NUMBERFORMAT_SCIENTIFIC
)
2685 fOutNumber
= StringToDouble(sResString
);
2687 { // append exponent
2691 sResString
+= sStrArray
[nNums
[nAnzNums
-1]];
2692 rtl_math_ConversionStatus eStatus
;
2693 fOutNumber
= ::rtl::math::stringToDouble(
2694 sResString
, '.', ',', &eStatus
, NULL
);
2695 if ( eStatus
== rtl_math_ConversionStatus_OutOfRange
)
2697 F_Type
= NUMBERFORMAT_TEXT
; // overflow/underflow -> Text
2701 fOutNumber
= DBL_MAX
;
2706 if ( nStringScanSign
)
2709 nSign
*= nStringScanSign
;
2711 nSign
= nStringScanSign
;
2714 fOutNumber
= -fOutNumber
;
2716 if (eScannedType
== NUMBERFORMAT_PERCENT
)
2721 case NUMBERFORMAT_FRACTION
:
2723 fOutNumber
= StringToDouble(sStrArray
[nNums
[0]]);
2724 else if (nAnzNums
== 2)
2728 sResString
= sStrArray
[nNums
[0]];
2729 sResString
+= sStrArray
[nNums
[1]]; // integer part
2730 fOutNumber
= StringToDouble(sResString
);
2734 double fZaehler
= StringToDouble(sStrArray
[nNums
[0]]);
2735 double fNenner
= StringToDouble(sStrArray
[nNums
[1]]);
2737 fOutNumber
= fZaehler
/fNenner
;
2742 else // nAnzNums > 2
2745 sResString
= sStrArray
[nNums
[0]];
2747 for (k
= 1; k
<= nThousand
; k
++)
2748 sResString
+= sStrArray
[nNums
[k
]];
2749 fOutNumber
= StringToDouble(sResString
);
2751 if (k
== nAnzNums
-2)
2753 double fZaehler
= StringToDouble(sStrArray
[nNums
[k
]]);
2754 double fNenner
= StringToDouble(sStrArray
[nNums
[k
+1]]);
2756 fOutNumber
+= fZaehler
/fNenner
;
2762 if ( nStringScanSign
)
2765 nSign
*= nStringScanSign
;
2767 nSign
= nStringScanSign
;
2770 fOutNumber
= -fOutNumber
;
2773 case NUMBERFORMAT_TIME
:
2774 GetTimeRef(fOutNumber
, 0, nAnzNums
);
2776 fOutNumber
= -fOutNumber
;
2779 case NUMBERFORMAT_DATE
:
2781 USHORT nCounter
= 0; // dummy here
2782 res
= GetDateRef( fOutNumber
, nCounter
, pFormat
);
2786 case NUMBERFORMAT_DATETIME
:
2788 USHORT nCounter
= 0; // needed here
2789 res
= GetDateRef( fOutNumber
, nCounter
, pFormat
);
2793 GetTimeRef( fTime
, nCounter
, nAnzNums
- nCounter
);
2794 fOutNumber
+= fTime
;
2800 DBG_ERRORFILE( "Some number recognized but what's it?" );
2806 if (res
) // overflow/underflow -> Text
2808 if (fOutNumber
< -DBL_MAX
) // -1.7E308
2810 F_Type
= NUMBERFORMAT_TEXT
;
2811 fOutNumber
= -DBL_MAX
;
2814 else if (fOutNumber
> DBL_MAX
) // 1.7E308
2816 F_Type
= NUMBERFORMAT_TEXT
;
2817 fOutNumber
= DBL_MAX
;
2824 eScannedType
= NUMBERFORMAT_TEXT
;
2828 F_Type
= eScannedType
;