/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_svl.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include // NUMBERFORMAT_XXX #include "zforscan.hxx" #include #define _ZFORFIND_CXX #include "zforfind.hxx" #undef _ZFORFIND_CXX #ifndef DBG_UTIL #define NF_TEST_CALENDAR 0 #else #define NF_TEST_CALENDAR 0 #endif #if NF_TEST_CALENDAR #include #include #endif const sal_uInt8 ImpSvNumberInputScan::nMatchedEndString = 0x01; const sal_uInt8 ImpSvNumberInputScan::nMatchedMidString = 0x02; const sal_uInt8 ImpSvNumberInputScan::nMatchedStartString = 0x04; const sal_uInt8 ImpSvNumberInputScan::nMatchedVirgin = 0x08; const sal_uInt8 ImpSvNumberInputScan::nMatchedUsedAsReturn = 0x10; /* It is not clear how we want timezones to be handled. Convert them to local * time isn't wanted, as it isn't done in any other place and timezone * information isn't stored anywhere. Ignoring them and pretending local time * may be wrong too and might not be what the user expects. Keep the input as * string so that no information is lost. * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it * would work, together with the nTimezonePos handling in GetTimeRef(). */ #define NF_RECOGNIZE_ISO8601_TIMEZONES 0 //--------------------------------------------------------------------------- // Konstruktor ImpSvNumberInputScan::ImpSvNumberInputScan( SvNumberFormatter* pFormatterP ) : pUpperMonthText( NULL ), pUpperAbbrevMonthText( NULL ), pUpperDayText( NULL ), pUpperAbbrevDayText( NULL ) { pFormatter = pFormatterP; pNullDate = new Date(30,12,1899); nYear2000 = SvNumberFormatter::GetYear2000Default(); Reset(); ChangeIntl(); } //--------------------------------------------------------------------------- // Destruktor ImpSvNumberInputScan::~ImpSvNumberInputScan() { Reset(); delete pNullDate; delete [] pUpperMonthText; delete [] pUpperAbbrevMonthText; delete [] pUpperDayText; delete [] pUpperAbbrevDayText; } //--------------------------------------------------------------------------- // Reset void ImpSvNumberInputScan::Reset() { #if 0 // ER 16.06.97 18:56 Vorbelegung erfolgt jetzt in NumberStringDivision, // wozu immer alles loeschen wenn einiges wieder benutzt oder gar nicht // gebraucht wird.. for (sal_uInt16 i = 0; i < SV_MAX_ANZ_INPUT_STRINGS; i++) { sStrArray[i].Erase(); nNums[i] = SV_MAX_ANZ_INPUT_STRINGS-1; IsNum[i] = sal_False; } #endif nMonth = 0; nMonthPos = 0; nTimePos = 0; nSign = 0; nESign = 0; nDecPos = 0; nNegCheck = 0; nAnzStrings = 0; nAnzNums = 0; nThousand = 0; eScannedType = NUMBERFORMAT_UNDEFINED; nAmPm = 0; nPosThousandString = 0; nLogical = 0; nStringScanNumFor = 0; nStringScanSign = 0; nMatchedAllStrings = nMatchedVirgin; nMayBeIso8601 = 0; nTimezonePos = 0; } //--------------------------------------------------------------------------- // // static inline sal_Bool ImpSvNumberInputScan::MyIsdigit( sal_Unicode c ) { // If the input string wouldn't be converted using TransformInput() we'd // to use something similar to the following and to adapt many places. #if 0 // use faster isdigit() if possible if ( c < 128 ) return isdigit( (unsigned char) c ) != 0; if ( c < 256 ) return sal_False; String aTmp( c ); return pFormatter->GetCharClass()->isDigit( aTmp, 0 ); #else return c < 128 && isdigit( (unsigned char) c ); #endif } //--------------------------------------------------------------------------- // void ImpSvNumberInputScan::TransformInput( String& rStr ) { xub_StrLen nPos, nLen; for ( nPos = 0, nLen = rStr.Len(); nPos < nLen; ++nPos ) { if ( 256 <= rStr.GetChar( nPos ) && pFormatter->GetCharClass()->isDigit( rStr, nPos ) ) break; } if ( nPos < nLen ) rStr = pFormatter->GetNatNum()->getNativeNumberString( rStr, pFormatter->GetLocale(), 0 ); } //--------------------------------------------------------------------------- // StringToDouble // // Only simple unsigned floating point values without any error detection, // decimal separator has to be '.' double ImpSvNumberInputScan::StringToDouble( const String& rStr, sal_Bool bForceFraction ) { double fNum = 0.0; double fFrac = 0.0; int nExp = 0; xub_StrLen nPos = 0; xub_StrLen nLen = rStr.Len(); sal_Bool bPreSep = !bForceFraction; while (nPos < nLen) { if (rStr.GetChar(nPos) == '.') bPreSep = sal_False; else if (bPreSep) fNum = fNum * 10.0 + (double) (rStr.GetChar(nPos) - '0'); else { fFrac = fFrac * 10.0 + (double) (rStr.GetChar(nPos) - '0'); --nExp; } nPos++; } if ( fFrac ) return fNum + ::rtl::math::pow10Exp( fFrac, nExp ); return fNum; } //--------------------------------------------------------------------------- // NextNumberStringSymbol // // Zerlegt die Eingabe in Zahlen und Strings fuer die weitere // Verarbeitung (Turing-Maschine). //--------------------------------------------------------------------------- // Ausgangs Zustand = GetChar //---------------+-------------------+-----------------------+--------------- // Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand //---------------+-------------------+-----------------------+--------------- // GetChar | Ziffer | Symbol=Zeichen | GetValue // | Sonst | Symbol=Zeichen | GetString //---------------|-------------------+-----------------------+--------------- // GetValue | Ziffer | Symbol=Symbol+Zeichen | GetValue // | Sonst | Dec(CharPos) | Stop //---------------+-------------------+-----------------------+--------------- // GetString | Ziffer | Dec(CharPos) | Stop // | Sonst | Symbol=Symbol+Zeichen | GetString //---------------+-------------------+-----------------------+--------------- enum ScanState // States der Turing-Maschine { SsStop = 0, SsStart = 1, SsGetValue = 2, SsGetString = 3 }; sal_Bool ImpSvNumberInputScan::NextNumberStringSymbol( const sal_Unicode*& pStr, String& rSymbol ) { sal_Bool isNumber = sal_False; sal_Unicode cToken; ScanState eState = SsStart; const sal_Unicode* pHere = pStr; xub_StrLen nChars = 0; while ( ((cToken = *pHere) != 0) && eState != SsStop) { pHere++; switch (eState) { case SsStart: if ( MyIsdigit( cToken ) ) { eState = SsGetValue; isNumber = sal_True; } else eState = SsGetString; nChars++; break; case SsGetValue: if ( MyIsdigit( cToken ) ) nChars++; else { eState = SsStop; pHere--; } break; case SsGetString: if ( !MyIsdigit( cToken ) ) nChars++; else { eState = SsStop; pHere--; } break; default: break; } // switch } // while if ( nChars ) rSymbol.Assign( pStr, nChars ); else rSymbol.Erase(); pStr = pHere; return isNumber; } //--------------------------------------------------------------------------- // SkipThousands // FIXME: should be grouping; it is only used though in case nAnzStrings is // near SV_MAX_ANZ_INPUT_STRINGS, in NumberStringDivision(). sal_Bool ImpSvNumberInputScan::SkipThousands( const sal_Unicode*& pStr, String& rSymbol ) { sal_Bool res = sal_False; sal_Unicode cToken; const String& rThSep = pFormatter->GetNumThousandSep(); const sal_Unicode* pHere = pStr; ScanState eState = SsStart; xub_StrLen nCounter = 0; // counts 3 digits while ( ((cToken = *pHere) != 0) && eState != SsStop) { pHere++; switch (eState) { case SsStart: if ( StringPtrContains( rThSep, pHere-1, 0 ) ) { nCounter = 0; eState = SsGetValue; pHere += rThSep.Len()-1; } else { eState = SsStop; pHere--; } break; case SsGetValue: if ( MyIsdigit( cToken ) ) { rSymbol += cToken; nCounter++; if (nCounter == 3) { eState = SsStart; res = sal_True; // .000 combination found } } else { eState = SsStop; pHere--; } break; default: break; } // switch } // while if (eState == SsGetValue) // break witth less than 3 digits { if ( nCounter ) rSymbol.Erase( rSymbol.Len() - nCounter, nCounter ); pHere -= nCounter + rThSep.Len(); // put back ThSep also } pStr = pHere; return res; } //--------------------------------------------------------------------------- // NumberStringDivision void ImpSvNumberInputScan::NumberStringDivision( const String& rString ) { const sal_Unicode* pStr = rString.GetBuffer(); const sal_Unicode* const pEnd = pStr + rString.Len(); while ( pStr < pEnd && nAnzStrings < SV_MAX_ANZ_INPUT_STRINGS ) { if ( NextNumberStringSymbol( pStr, sStrArray[nAnzStrings] ) ) { // Zahl IsNum[nAnzStrings] = sal_True; nNums[nAnzNums] = nAnzStrings; nAnzNums++; if (nAnzStrings >= SV_MAX_ANZ_INPUT_STRINGS - 7 && nPosThousandString == 0) // nur einmal if ( SkipThousands( pStr, sStrArray[nAnzStrings] ) ) nPosThousandString = nAnzStrings; } else { IsNum[nAnzStrings] = sal_False; } nAnzStrings++; } } //--------------------------------------------------------------------------- // Whether rString contains rWhat at nPos sal_Bool ImpSvNumberInputScan::StringContainsImpl( const String& rWhat, const String& rString, xub_StrLen nPos ) { if ( nPos + rWhat.Len() <= rString.Len() ) return StringPtrContainsImpl( rWhat, rString.GetBuffer(), nPos ); return sal_False; } //--------------------------------------------------------------------------- // Whether pString contains rWhat at nPos sal_Bool ImpSvNumberInputScan::StringPtrContainsImpl( const String& rWhat, const sal_Unicode* pString, xub_StrLen nPos ) { if ( rWhat.Len() == 0 ) return sal_False; const sal_Unicode* pWhat = rWhat.GetBuffer(); const sal_Unicode* const pEnd = pWhat + rWhat.Len(); const sal_Unicode* pStr = pString + nPos; while ( pWhat < pEnd ) { if ( *pWhat != *pStr ) return sal_False; pWhat++; pStr++; } return sal_True; } //--------------------------------------------------------------------------- // SkipChar // // ueberspringt genau das angegebene Zeichen inline sal_Bool ImpSvNumberInputScan::SkipChar( sal_Unicode c, const String& rString, xub_StrLen& nPos ) { if ((nPos < rString.Len()) && (rString.GetChar(nPos) == c)) { nPos++; return sal_True; } return sal_False; } //--------------------------------------------------------------------------- // SkipBlanks // // Ueberspringt Leerzeichen inline void ImpSvNumberInputScan::SkipBlanks( const String& rString, xub_StrLen& nPos ) { if ( nPos < rString.Len() ) { const sal_Unicode* p = rString.GetBuffer() + nPos; while ( *p == ' ' ) { nPos++; p++; } } } //--------------------------------------------------------------------------- // SkipString // // jump over rWhat in rString at nPos inline sal_Bool ImpSvNumberInputScan::SkipString( const String& rWhat, const String& rString, xub_StrLen& nPos ) { if ( StringContains( rWhat, rString, nPos ) ) { nPos = nPos + rWhat.Len(); return sal_True; } return sal_False; } //--------------------------------------------------------------------------- // GetThousandSep // // recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping inline sal_Bool ImpSvNumberInputScan::GetThousandSep( const String& rString, xub_StrLen& nPos, sal_uInt16 nStringPos ) { const String& rSep = pFormatter->GetNumThousandSep(); // Is it an ordinary space instead of a non-breaking space? bool bSpaceBreak = rSep.GetChar(0) == 0xa0 && rString.GetChar(0) == 0x20 && rSep.Len() == 1 && rString.Len() == 1; if (!( (rString == rSep || bSpaceBreak) // nothing else && nStringPos < nAnzStrings - 1 // safety first! && IsNum[nStringPos+1] )) // number follows return sal_False; // no? => out utl::DigitGroupingIterator aGrouping( pFormatter->GetLocaleData()->getDigitGrouping()); // Match ,### in {3} or ,## in {3,2} /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or * ,##,### and to match ,### in {3,2} only if it's the last. However, * currently there is no track kept where group separators occur. In {3,2} * #,###,### and #,##,## would be valid input, which maybe isn't even bad * for #,###,###. Other combinations such as #,###,## maybe not. */ xub_StrLen nLen = sStrArray[nStringPos+1].Len(); if (nLen == aGrouping.get() // with 3 (or so) digits || nLen == aGrouping.advance().get() // or with 2 (or 3 or so) digits || nPosThousandString == nStringPos+1 // or concatenated ) { nPos = nPos + rSep.Len(); return sal_True; } return sal_False; } //--------------------------------------------------------------------------- // GetLogical // // Conversion of text to logial value // "sal_True" => 1: // "sal_False"=> -1: // else => 0: short ImpSvNumberInputScan::GetLogical( const String& rString ) { short res; const ImpSvNumberformatScan* pFS = pFormatter->GetFormatScanner(); if ( rString == pFS->GetTrueString() ) res = 1; else if ( rString == pFS->GetFalseString() ) res = -1; else res = 0; return res; } //--------------------------------------------------------------------------- // GetMonth // // Converts a string containing a month name (JAN, January) at nPos into the // month number (negative if abbreviated), returns 0 if nothing found short ImpSvNumberInputScan::GetMonth( const String& rString, xub_StrLen& nPos ) { // #102136# The correct English form of month September abbreviated is // SEPT, but almost every data contains SEP instead. static const String aSeptCorrect( RTL_CONSTASCII_USTRINGPARAM( "SEPT" ) ); static const String aSepShortened( RTL_CONSTASCII_USTRINGPARAM( "SEP" ) ); short res = 0; // no month found if (rString.Len() > nPos) // only if needed { if ( !bTextInitialized ) InitText(); sal_Int16 nMonths = pFormatter->GetCalendar()->getNumberOfMonthsInYear(); for ( sal_Int16 i = 0; i < nMonths; i++ ) { if ( StringContains( pUpperMonthText[i], rString, nPos ) ) { // full names first nPos = nPos + pUpperMonthText[i].Len(); res = i+1; break; // for } else if ( StringContains( pUpperAbbrevMonthText[i], rString, nPos ) ) { // abbreviated nPos = nPos + pUpperAbbrevMonthText[i].Len(); res = sal::static_int_cast< short >(-(i+1)); // negative break; // for } else if ( i == 8 && pUpperAbbrevMonthText[i] == aSeptCorrect && StringContains( aSepShortened, rString, nPos ) ) { // #102136# SEPT/SEP nPos = nPos + aSepShortened.Len(); res = sal::static_int_cast< short >(-(i+1)); // negative break; // for } } } return res; } //--------------------------------------------------------------------------- // GetDayOfWeek // // Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the // DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found int ImpSvNumberInputScan::GetDayOfWeek( const String& rString, xub_StrLen& nPos ) { int res = 0; // no day found if (rString.Len() > nPos) // only if needed { if ( !bTextInitialized ) InitText(); sal_Int16 nDays = pFormatter->GetCalendar()->getNumberOfDaysInWeek(); for ( sal_Int16 i = 0; i < nDays; i++ ) { if ( StringContains( pUpperDayText[i], rString, nPos ) ) { // full names first nPos = nPos + pUpperDayText[i].Len(); res = i + 1; break; // for } if ( StringContains( pUpperAbbrevDayText[i], rString, nPos ) ) { // abbreviated nPos = nPos + pUpperAbbrevDayText[i].Len(); res = -(i + 1); // negative break; // for } } } return res; } //--------------------------------------------------------------------------- // GetCurrency // // Lesen eines Waehrungssysmbols // '$' => sal_True // sonst => sal_False sal_Bool ImpSvNumberInputScan::GetCurrency( const String& rString, xub_StrLen& nPos, const SvNumberformat* pFormat ) { if ( rString.Len() > nPos ) { if ( !aUpperCurrSymbol.Len() ) { // if no format specified the currency of the initialized formatter LanguageType eLang = (pFormat ? pFormat->GetLanguage() : pFormatter->GetLanguage()); aUpperCurrSymbol = pFormatter->GetCharClass()->upper( SvNumberFormatter::GetCurrencyEntry( eLang ).GetSymbol() ); } if ( StringContains( aUpperCurrSymbol, rString, nPos ) ) { nPos = nPos + aUpperCurrSymbol.Len(); return sal_True; } if ( pFormat ) { String aSymbol, aExtension; if ( pFormat->GetNewCurrencySymbol( aSymbol, aExtension ) ) { if ( aSymbol.Len() <= rString.Len() - nPos ) { pFormatter->GetCharClass()->toUpper( aSymbol ); if ( StringContains( aSymbol, rString, nPos ) ) { nPos = nPos + aSymbol.Len(); return sal_True; } } } } } return sal_False; } //--------------------------------------------------------------------------- // GetTimeAmPm // // Lesen des Zeitsymbols (AM od. PM) f. kurze Zeitangabe // // Rueckgabe: // "AM" od. "PM" => sal_True // sonst => sal_False // // nAmPos: // "AM" => 1 // "PM" => -1 // sonst => 0 sal_Bool ImpSvNumberInputScan::GetTimeAmPm( const String& rString, xub_StrLen& nPos ) { if ( rString.Len() > nPos ) { const CharClass* pChr = pFormatter->GetCharClass(); const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); if ( StringContains( pChr->upper( pLoc->getTimeAM() ), rString, nPos ) ) { nAmPm = 1; nPos = nPos + pLoc->getTimeAM().Len(); return sal_True; } else if ( StringContains( pChr->upper( pLoc->getTimePM() ), rString, nPos ) ) { nAmPm = -1; nPos = nPos + pLoc->getTimePM().Len(); return sal_True; } } return sal_False; } //--------------------------------------------------------------------------- // GetDecSep // // Lesen eines Dezimaltrenners (',') // ',' => sal_True // sonst => sal_False inline sal_Bool ImpSvNumberInputScan::GetDecSep( const String& rString, xub_StrLen& nPos ) { if ( rString.Len() > nPos ) { const String& rSep = pFormatter->GetNumDecimalSep(); if ( rString.Equals( rSep, nPos, rSep.Len() ) ) { nPos = nPos + rSep.Len(); return sal_True; } } return sal_False; } //--------------------------------------------------------------------------- // read a hundredth seconds separator inline sal_Bool ImpSvNumberInputScan::GetTime100SecSep( const String& rString, xub_StrLen& nPos ) { if ( rString.Len() > nPos ) { const String& rSep = pFormatter->GetLocaleData()->getTime100SecSep(); if ( rString.Equals( rSep, nPos, rSep.Len() ) ) { nPos = nPos + rSep.Len(); return sal_True; } } return sal_False; } //--------------------------------------------------------------------------- // GetSign // // Lesen eines Vorzeichens, auch Klammer !?! // '+' => 1 // '-' => -1 // '(' => -1, nNegCheck = 1 // sonst => 0 int ImpSvNumberInputScan::GetSign( const String& rString, xub_StrLen& nPos ) { if (rString.Len() > nPos) switch (rString.GetChar(nPos)) { case '+': nPos++; return 1; case '(': // '(' aehnlich wie '-' ?!? nNegCheck = 1; //! fallthru case '-': nPos++; return -1; default: break; } return 0; } //--------------------------------------------------------------------------- // GetESign // // Lesen eines Vorzeichens, gedacht fuer Exponent ?!? // '+' => 1 // '-' => -1 // sonst => 0 short ImpSvNumberInputScan::GetESign( const String& rString, xub_StrLen& nPos ) { if (rString.Len() > nPos) switch (rString.GetChar(nPos)) { case '+': nPos++; return 1; case '-': nPos++; return -1; default: break; } return 0; } //--------------------------------------------------------------------------- // GetNextNumber // // i counts string portions, j counts numbers thereof. // It should had been called SkipNumber instead. inline sal_Bool ImpSvNumberInputScan::GetNextNumber( sal_uInt16& i, sal_uInt16& j ) { if ( i < nAnzStrings && IsNum[i] ) { j++; i++; return sal_True; } return sal_False; } //--------------------------------------------------------------------------- // GetTimeRef void ImpSvNumberInputScan::GetTimeRef( double& fOutNumber, sal_uInt16 nIndex, // j-value of the first numeric time part of input, default 0 sal_uInt16 nAnz ) // count of numeric time parts { sal_uInt16 nHour; sal_uInt16 nMinute = 0; sal_uInt16 nSecond = 0; double fSecond100 = 0.0; sal_uInt16 nStartIndex = nIndex; if (nTimezonePos) { // find first timezone number index and adjust count for (sal_uInt16 j=0; jGetCalendar()->getNumberOfMonthsInYear(); if (sStrArray[nNums[nIndex]].Len() <= 2) { sal_uInt16 nNum = (sal_uInt16) sStrArray[nNums[nIndex]].ToInt32(); if ( 0 < nNum && nNum <= nRes ) nRes = nNum - 1; // zero based for CalendarFieldIndex::MONTH } return nRes; } //--------------------------------------------------------------------------- // ImplGetYear // // 30 -> 1930, 29 -> 2029, oder 56 -> 1756, 55 -> 1855, ... sal_uInt16 ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex ) { sal_uInt16 nYear = 0; if (sStrArray[nNums[nIndex]].Len() <= 4) { nYear = (sal_uInt16) sStrArray[nNums[nIndex]].ToInt32(); nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 ); } return nYear; } //--------------------------------------------------------------------------- bool ImpSvNumberInputScan::MayBeIso8601() { if (nMayBeIso8601 == 0) { if (nAnzNums >= 3 && nNums[0] < nAnzStrings && sStrArray[nNums[0]].ToInt32() > 31) nMayBeIso8601 = 1; else nMayBeIso8601 = 2; } return nMayBeIso8601 == 1; } //--------------------------------------------------------------------------- // GetDateRef sal_Bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter, const SvNumberformat* pFormat ) { using namespace ::com::sun::star::i18n; NfEvalDateFormat eEDF; int nFormatOrder; if ( pFormat && ((pFormat->GetType() & NUMBERFORMAT_DATE) == NUMBERFORMAT_DATE) ) { eEDF = pFormatter->GetEvalDateFormat(); switch ( eEDF ) { case NF_EVALDATEFORMAT_INTL : case NF_EVALDATEFORMAT_FORMAT : nFormatOrder = 1; // only one loop break; default: nFormatOrder = 2; if ( nMatchedAllStrings ) eEDF = NF_EVALDATEFORMAT_FORMAT_INTL; // we have a complete match, use it } } else { eEDF = NF_EVALDATEFORMAT_INTL; nFormatOrder = 1; } sal_Bool res = sal_True; const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); CalendarWrapper* pCal = pFormatter->GetCalendar(); for ( int nTryOrder = 1; nTryOrder <= nFormatOrder; nTryOrder++ ) { pCal->setGregorianDateTime( Date() ); // today String aOrgCalendar; // empty => not changed yet DateFormat DateFmt; sal_Bool bFormatTurn; switch ( eEDF ) { case NF_EVALDATEFORMAT_INTL : bFormatTurn = sal_False; DateFmt = pLoc->getDateFormat(); break; case NF_EVALDATEFORMAT_FORMAT : bFormatTurn = sal_True; DateFmt = pFormat->GetDateOrder(); break; case NF_EVALDATEFORMAT_INTL_FORMAT : if ( nTryOrder == 1 ) { bFormatTurn = sal_False; DateFmt = pLoc->getDateFormat(); } else { bFormatTurn = sal_True; DateFmt = pFormat->GetDateOrder(); } break; case NF_EVALDATEFORMAT_FORMAT_INTL : if ( nTryOrder == 2 ) { bFormatTurn = sal_False; DateFmt = pLoc->getDateFormat(); } else { bFormatTurn = sal_True; DateFmt = pFormat->GetDateOrder(); } break; default: DBG_ERROR( "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" ); DateFmt = YMD; bFormatTurn = sal_False; } if ( bFormatTurn ) { #if 0 /* TODO: We are currently not able to fully support a switch to another calendar during input for the following reasons: 1. We do have a problem if both (locale's default and format's) calendars define the same YMD order and use the same date separator, there is no way to distinguish between them if the input results in valid calendar input for both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should it always be set to NF_EVALDATEFORMAT_FORMAT_INTL and thus the format's calendar be preferred? This could be confusing if a Calc cell was formatted different to the locale's default and has no content yet, then the user has no clue about the format or calendar being set. 2. In Calc cell edit mode a date is always displayed and edited using the default edit format of the default calendar (normally being Gregorian). If input was ambiguous due to issue #1 we'd need a mechanism to tell that a date was edited and not newly entered. Not feasible. Otherwise we'd need a mechanism to use a specific edit format with a specific calendar according to the format set. 3. For some calendars like Japanese Gengou we'd need era input, which isn't implemented at all. Though this is a rare and special case, forcing a calendar dependent edit format as suggested in item #2 might require era input, if it shouldn't result in a fallback to Gregorian calendar. 4. Last and least: the GetMonth() method currently only matches month names of the default calendar. Alternating month names of the actual format's calendar would have to be implemented. No problem. */ if ( pFormat->IsOtherCalendar( nStringScanNumFor ) ) pFormat->SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime ); else pFormat->SwitchToSpecifiedCalendar( aOrgCalendar, fOrgDateTime, nStringScanNumFor ); #endif } res = sal_True; nCounter = 0; // For incomplete dates, always assume first day of month if not specified. pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 ); switch (nAnzNums) // count of numbers in string { case 0: // none if (nMonthPos) // only month (Jan) pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); else res = sal_False; break; case 1: // only one number nCounter = 1; switch (nMonthPos) // where is the month { case 0: // not found => only day entered pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); break; case 1: // month at the beginning (Jan 01) pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); switch (DateFmt) { case MDY: case YMD: pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); break; case DMY: pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; default: res = sal_False; break; } break; case 3: // month at the end (10 Jan) pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); switch (DateFmt) { case DMY: pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); break; case YMD: pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; default: res = sal_False; break; } break; default: res = sal_False; break; } // switch (nMonthPos) break; case 2: // 2 numbers nCounter = 2; switch (nMonthPos) // where is the month { case 0: // not found { bool bHadExact; sal_uInt32 nExactDateOrder = (bFormatTurn ? pFormat->GetExactDateOrder() : 0); if ( 0xff < nExactDateOrder && nExactDateOrder <= 0xffff ) { // formatted as date and exactly 2 parts bHadExact = true; switch ( (nExactDateOrder >> 8) & 0xff ) { case 'Y': pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; case 'M': pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); break; case 'D': pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); break; default: bHadExact = false; } switch ( nExactDateOrder & 0xff ) { case 'Y': pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); break; case 'M': pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) ); break; case 'D': pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); break; default: bHadExact = false; } } else bHadExact = false; if ( !bHadExact || !pCal->isValid() ) { if ( !bHadExact && nExactDateOrder ) pCal->setGregorianDateTime( Date() ); // reset today switch (DateFmt) { case MDY: // M D pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); if ( !pCal->isValid() ) // 2nd try { // M Y pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 ); pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); } break; case DMY: // D M pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) ); if ( !pCal->isValid() ) // 2nd try { // M Y pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 ); pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); } break; case YMD: // M D pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); if ( !pCal->isValid() ) // 2nd try { // Y M pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 ); pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); } break; default: res = sal_False; break; } } } break; case 1: // month at the beginning (Jan 01 01) { // The input is valid as MDY in almost any // constellation, there is no date order (M)YD except if // set in a format applied. pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); sal_uInt32 nExactDateOrder = (bFormatTurn ? pFormat->GetExactDateOrder() : 0); if ((((nExactDateOrder >> 8) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D')) { pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); } else { pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); } } break; case 2: // month in the middle (10 Jan 94) pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); switch (DateFmt) { case MDY: // yes, "10-Jan-94" is valid case DMY: pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); break; case YMD: pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; default: res = sal_False; break; } break; default: // else, e.g. month at the end (94 10 Jan) res = sal_False; break; } // switch (nMonthPos) break; default: // more than two numbers (31.12.94 8:23) (31.12. 8:23) switch (nMonthPos) // where is the month { case 0: // not found { nCounter = 3; if ( nTimePos > 1 ) { // find first time number index (should only be 3 or 2 anyway) for ( sal_uInt16 j = 0; j < nAnzNums; j++ ) { if ( nNums[j] == nTimePos - 2 ) { nCounter = j; break; // for } } } // ISO 8601 yyyy-mm-dd forced recognition DateFormat eDF = (MayBeIso8601() ? YMD : DateFmt); switch (eDF) { case MDY: pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); if ( nCounter > 2 ) pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) ); break; case DMY: pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) ); if ( nCounter > 2 ) pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) ); break; case YMD: if ( nCounter > 2 ) pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(2) ); pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; default: res = sal_False; break; } } break; case 1: // month at the beginning (Jan 01 01 8:23) nCounter = 2; switch (DateFmt) { case MDY: pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); break; default: res = sal_False; break; } break; case 2: // month in the middle (10 Jan 94 8:23) nCounter = 2; pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 ); switch (DateFmt) { case DMY: pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); break; case YMD: pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) ); pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; default: res = sal_False; break; } break; default: // else, e.g. month at the end (94 10 Jan 8:23) nCounter = 2; res = sal_False; break; } // switch (nMonthPos) break; } // switch (nAnzNums) if ( res && pCal->isValid() ) { double fDiff = DateTime(*pNullDate) - pCal->getEpochStart(); fDays = ::rtl::math::approxFloor( pCal->getLocalDateTime() ); fDays -= fDiff; nTryOrder = nFormatOrder; // break for } else res = sal_False; if ( aOrgCalendar.Len() ) pCal->loadCalendar( aOrgCalendar, pLoc->getLocale() ); // restore calendar #if NF_TEST_CALENDAR { using namespace ::com::sun::star; struct entry { const char* lan; const char* cou; const char* cal; }; const entry cals[] = { { "en", "US", "gregorian" }, { "ar", "TN", "hijri" }, { "he", "IL", "jewish" }, { "ja", "JP", "gengou" }, { "ko", "KR", "hanja_yoil" }, { "th", "TH", "buddhist" }, { "zh", "TW", "ROC" }, {0,0,0} }; lang::Locale aLocale; sal_Bool bValid; sal_Int16 nDay, nMyMonth, nYear, nHour, nMinute, nSecond; sal_Int16 nDaySet, nMonthSet, nYearSet, nHourSet, nMinuteSet, nSecondSet; sal_Int16 nZO, nDST1, nDST2, nDST, nZOmillis, nDST1millis, nDST2millis, nDSTmillis; sal_Int32 nZoneInMillis, nDST1InMillis, nDST2InMillis; uno::Reference< lang::XMultiServiceFactory > xSMgr = ::comphelper::getProcessServiceFactory(); uno::Reference< ::com::sun::star::i18n::XExtendedCalendar > xCal( xSMgr->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.LocaleCalendar" ) ) ), uno::UNO_QUERY ); for ( const entry* p = cals; p->lan; ++p ) { aLocale.Language = ::rtl::OUString::createFromAscii( p->lan ); aLocale.Country = ::rtl::OUString::createFromAscii( p->cou ); xCal->loadCalendar( ::rtl::OUString::createFromAscii( p->cal ), aLocale ); double nDateTime = 0.0; // 1-Jan-1970 00:00:00 nZO = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET ); nZOmillis = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS ); nZoneInMillis = static_cast(nZO) * 60000 + (nZO < 0 ? -1 : 1) * static_cast(nZOmillis); nDST1 = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET ); nDST1millis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS ); nDST1InMillis = static_cast(nDST1) * 60000 + (nDST1 < 0 ? -1 : 1) * static_cast(nDST1millis); nDateTime -= (double)(nZoneInMillis + nDST1InMillis) / 1000.0 / 60.0 / 60.0 / 24.0; xCal->setDateTime( nDateTime ); nDST2 = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET ); nDST2millis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS ); nDST2InMillis = static_cast(nDST2) * 60000 + (nDST2 < 0 ? -1 : 1) * static_cast(nDST2millis); if ( nDST1InMillis != nDST2InMillis ) { nDateTime = 0.0 - (double)(nZoneInMillis + nDST2InMillis) / 1000.0 / 60.0 / 60.0 / 24.0; xCal->setDateTime( nDateTime ); } nDaySet = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH ); nMonthSet = xCal->getValue( i18n::CalendarFieldIndex::MONTH ); nYearSet = xCal->getValue( i18n::CalendarFieldIndex::YEAR ); nHourSet = xCal->getValue( i18n::CalendarFieldIndex::HOUR ); nMinuteSet = xCal->getValue( i18n::CalendarFieldIndex::MINUTE ); nSecondSet = xCal->getValue( i18n::CalendarFieldIndex::SECOND ); nZO = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET ); nZOmillis = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS ); nDST = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET ); nDSTmillis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS ); xCal->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDaySet ); xCal->setValue( i18n::CalendarFieldIndex::MONTH, nMonthSet ); xCal->setValue( i18n::CalendarFieldIndex::YEAR, nYearSet ); xCal->setValue( i18n::CalendarFieldIndex::HOUR, nHourSet ); xCal->setValue( i18n::CalendarFieldIndex::MINUTE, nMinuteSet ); xCal->setValue( i18n::CalendarFieldIndex::SECOND, nSecondSet ); bValid = xCal->isValid(); nDay = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH ); nMyMonth= xCal->getValue( i18n::CalendarFieldIndex::MONTH ); nYear = xCal->getValue( i18n::CalendarFieldIndex::YEAR ); nHour = xCal->getValue( i18n::CalendarFieldIndex::HOUR ); nMinute = xCal->getValue( i18n::CalendarFieldIndex::MINUTE ); nSecond = xCal->getValue( i18n::CalendarFieldIndex::SECOND ); bValid = bValid && nDay == nDaySet && nMyMonth == nMonthSet && nYear == nYearSet && nHour == nHourSet && nMinute == nMinuteSet && nSecond == nSecondSet; } } #endif // NF_TEST_CALENDAR } return res; } //--------------------------------------------------------------------------- // ScanStartString // // ersten String analysieren // Alles weg => sal_True // sonst => sal_False sal_Bool ImpSvNumberInputScan::ScanStartString( const String& rString, const SvNumberformat* pFormat ) { xub_StrLen nPos = 0; int nDayOfWeek; // First of all, eat leading blanks SkipBlanks(rString, nPos); // Yes, nMatchedAllStrings should know about the sign position nSign = GetSign(rString, nPos); if ( nSign ) // sign? SkipBlanks(rString, nPos); // #102371# match against format string only if start string is not a sign character if ( nMatchedAllStrings && !(nSign && rString.Len() == 1) ) { // Match against format in any case, so later on for a "x1-2-3" input // we may distinguish between a xy-m-d (or similar) date and a x0-0-0 // format. No sign detection here! if ( ScanStringNumFor( rString, nPos, pFormat, 0, sal_True ) ) nMatchedAllStrings |= nMatchedStartString; else nMatchedAllStrings = 0; } if ( GetDecSep(rString, nPos) ) // decimal separator in start string { nDecPos = 1; SkipBlanks(rString, nPos); } else if ( GetCurrency(rString, nPos, pFormat) ) // currency (DM 1)? { eScannedType = NUMBERFORMAT_CURRENCY; // !!! it IS currency !!! SkipBlanks(rString, nPos); if (nSign == 0) // no sign yet { nSign = GetSign(rString, nPos); if ( nSign ) // DM -1 SkipBlanks(rString, nPos); } } else { nMonth = GetMonth(rString, nPos); if ( nMonth ) // month (Jan 1)? { eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date !!! nMonthPos = 1; // month at the beginning if ( nMonth < 0 ) SkipChar( '.', rString, nPos ); // abbreviated SkipBlanks(rString, nPos); } else { nDayOfWeek = GetDayOfWeek( rString, nPos ); if ( nDayOfWeek ) { // day of week is just parsed away eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date !!! if ( nPos < rString.Len() ) { if ( nDayOfWeek < 0 ) { // abbreviated if ( rString.GetChar( nPos ) == '.' ) ++nPos; } else { // full long name SkipBlanks(rString, nPos); SkipString( pFormatter->GetLocaleData()->getLongDateDayOfWeekSep(), rString, nPos ); } SkipBlanks(rString, nPos); nMonth = GetMonth(rString, nPos); if ( nMonth ) // month (Jan 1)? { nMonthPos = 1; // month a the beginning if ( nMonth < 0 ) SkipChar( '.', rString, nPos ); // abbreviated SkipBlanks(rString, nPos); } } } } } if (nPos < rString.Len()) // not everything consumed { // Does input StartString equal StartString of format? // This time with sign detection! if ( !ScanStringNumFor( rString, nPos, pFormat, 0 ) ) return MatchedReturn(); } return sal_True; } //--------------------------------------------------------------------------- // ScanMidString // // String in der Mitte analysieren // Alles weg => sal_True // sonst => sal_False sal_Bool ImpSvNumberInputScan::ScanMidString( const String& rString, sal_uInt16 nStringPos, const SvNumberformat* pFormat ) { xub_StrLen nPos = 0; short eOldScannedType = eScannedType; if ( nMatchedAllStrings ) { // Match against format in any case, so later on for a "1-2-3-4" input // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0 // format. if ( ScanStringNumFor( rString, 0, pFormat, nStringPos ) ) nMatchedAllStrings |= nMatchedMidString; else nMatchedAllStrings = 0; } SkipBlanks(rString, nPos); if (GetDecSep(rString, nPos)) // decimal separator? { if (nDecPos == 1 || nDecPos == 3) // .12.4 or 1.E2.1 return MatchedReturn(); else if (nDecPos == 2) // . dup: 12.4. { if (bDecSepInDateSeps) // . also date separator { if ( eScannedType != NUMBERFORMAT_UNDEFINED && eScannedType != NUMBERFORMAT_DATE && eScannedType != NUMBERFORMAT_DATETIME) // already another type return MatchedReturn(); if (eScannedType == NUMBERFORMAT_UNDEFINED) eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date SkipBlanks(rString, nPos); } else return MatchedReturn(); } else { nDecPos = 2; // . in mid string SkipBlanks(rString, nPos); } } else if ( ((eScannedType & NUMBERFORMAT_TIME) == NUMBERFORMAT_TIME) && GetTime100SecSep( rString, nPos ) ) { // hundredth seconds separator if ( nDecPos ) return MatchedReturn(); nDecPos = 2; // . in mid string SkipBlanks(rString, nPos); } if (SkipChar('/', rString, nPos)) // fraction? { if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type && eScannedType != NUMBERFORMAT_DATE) // except date return MatchedReturn(); // => jan/31/1994 else if ( eScannedType != NUMBERFORMAT_DATE // analyzed date until now && ( eSetType == NUMBERFORMAT_FRACTION // and preset was fraction || (nAnzNums == 3 // or 3 numbers && nStringPos > 2) ) ) // and what ??? { SkipBlanks(rString, nPos); eScannedType = NUMBERFORMAT_FRACTION; // !!! it IS a fraction } else nPos--; // put '/' back } if (GetThousandSep(rString, nPos, nStringPos)) // 1,000 { if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type && eScannedType != NUMBERFORMAT_CURRENCY) // except currency return MatchedReturn(); nThousand++; } const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); const String& rDate = pFormatter->GetDateSep(); const String& rTime = pLoc->getTimeSep(); sal_Unicode cTime = rTime.GetChar(0); SkipBlanks(rString, nPos); if ( SkipString(rDate, rString, nPos) // 10., 10-, 10/ || ((cTime != '.') && SkipChar('.', rString, nPos)) // TRICKY: || ((cTime != '/') && SkipChar('/', rString, nPos)) // short boolean || ((cTime != '-') && SkipChar('-', rString, nPos)) ) // evaluation! { if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type && eScannedType != NUMBERFORMAT_DATE) // except date return MatchedReturn(); SkipBlanks(rString, nPos); eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date short nTmpMonth = GetMonth(rString, nPos); // 10. Jan 94 if (nMonth && nTmpMonth) // month dup return MatchedReturn(); if (nTmpMonth) { nMonth = nTmpMonth; nMonthPos = 2; // month in the middle if ( nMonth < 0 && SkipChar( '.', rString, nPos ) ) ; // short month may be abbreviated Jan. else if ( SkipChar( '-', rString, nPos ) ) ; // #79632# recognize 17-Jan-2001 to be a date // #99065# short and long month name else SkipString( pLoc->getLongDateMonthSep(), rString, nPos ); SkipBlanks(rString, nPos); } } short nTempMonth = GetMonth(rString, nPos); // month in the middle (10 Jan 94) if (nTempMonth) { if (nMonth != 0) // month dup return MatchedReturn(); if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type && eScannedType != NUMBERFORMAT_DATE) // except date return MatchedReturn(); eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date nMonth = nTempMonth; nMonthPos = 2; // month in the middle if ( nMonth < 0 ) SkipChar( '.', rString, nPos ); // abbreviated SkipString( pLoc->getLongDateMonthSep(), rString, nPos ); SkipBlanks(rString, nPos); } if ( SkipChar('E', rString, nPos) // 10E, 10e, 10,Ee || SkipChar('e', rString, nPos) ) { if (eScannedType != NUMBERFORMAT_UNDEFINED) // already another type return MatchedReturn(); else { SkipBlanks(rString, nPos); eScannedType = NUMBERFORMAT_SCIENTIFIC; // !!! it IS scientific if ( nThousand+2 == nAnzNums // special case 1.E2 && nDecPos == 2 ) nDecPos = 3; // 1,100.E2 1,100,100.E3 } nESign = GetESign(rString, nPos); // signed exponent? SkipBlanks(rString, nPos); } if ( SkipString(rTime, rString, nPos) ) // time separator? { if (nDecPos) // already . => maybe error { if (bDecSepInDateSeps) // . also date sep { if ( eScannedType != NUMBERFORMAT_DATE && // already another type than date eScannedType != NUMBERFORMAT_DATETIME) // or date time return MatchedReturn(); if (eScannedType == NUMBERFORMAT_DATE) nDecPos = 0; // reset for time transition } else return MatchedReturn(); } if ( ( eScannedType == NUMBERFORMAT_DATE // already date type || eScannedType == NUMBERFORMAT_DATETIME) // or date time && nAnzNums > 3) // and more than 3 numbers? (31.Dez.94 8:23) { SkipBlanks(rString, nPos); eScannedType = NUMBERFORMAT_DATETIME; // !!! it IS date with time } else if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type && eScannedType != NUMBERFORMAT_TIME) // except time return MatchedReturn(); else { SkipBlanks(rString, nPos); eScannedType = NUMBERFORMAT_TIME; // !!! it IS a time } if ( !nTimePos ) nTimePos = nStringPos + 1; } if (nPos < rString.Len()) { switch (eScannedType) { case NUMBERFORMAT_DATE: if (nMonthPos == 1 && pLoc->getLongDateFormat() == MDY) { // #68232# recognize long date separators like ", " in "September 5, 1999" if (SkipString( pLoc->getLongDateDaySep(), rString, nPos )) SkipBlanks( rString, nPos ); } else if (nStringPos == 5 && nPos == 0 && rString.Len() == 1 && rString.GetChar(0) == 'T' && MayBeIso8601()) { // ISO 8601 combined date and time, yyyy-mm-ddThh:mm ++nPos; } break; #if NF_RECOGNIZE_ISO8601_TIMEZONES case NUMBERFORMAT_DATETIME: if (nPos == 0 && rString.Len() == 1 && nStringPos >= 9 && MayBeIso8601()) { // ISO 8601 timezone offset switch (rString.GetChar(0)) { case '+': case '-': if (nStringPos == nAnzStrings-2 || nStringPos == nAnzStrings-4) { ++nPos; // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy] // nTimezonePos needed for GetTimeRef() if (!nTimezonePos) nTimezonePos = nStringPos + 1; } break; case ':': if (nTimezonePos && nStringPos >= 11 && nStringPos == nAnzStrings-2) ++nPos; // yyyy-mm-ddThh:mm[:ss]+xx:yy break; } } break; #endif } } if (nPos < rString.Len()) // not everything consumed? { if ( nMatchedAllStrings & ~nMatchedVirgin ) eScannedType = eOldScannedType; else return sal_False; } return sal_True; } //--------------------------------------------------------------------------- // ScanEndString // // Schlussteil analysieren // Alles weg => sal_True // sonst => sal_False sal_Bool ImpSvNumberInputScan::ScanEndString( const String& rString, const SvNumberformat* pFormat ) { xub_StrLen nPos = 0; if ( nMatchedAllStrings ) { // Match against format in any case, so later on for a "1-2-3-4" input // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0 // format. if ( ScanStringNumFor( rString, 0, pFormat, 0xFFFF ) ) nMatchedAllStrings |= nMatchedEndString; else nMatchedAllStrings = 0; } SkipBlanks(rString, nPos); if (GetDecSep(rString, nPos)) // decimal separator? { if (nDecPos == 1 || nDecPos == 3) // .12.4 or 12.E4. return MatchedReturn(); else if (nDecPos == 2) // . dup: 12.4. { if (bDecSepInDateSeps) // . also date sep { if ( eScannedType != NUMBERFORMAT_UNDEFINED && eScannedType != NUMBERFORMAT_DATE && eScannedType != NUMBERFORMAT_DATETIME) // already another type return MatchedReturn(); if (eScannedType == NUMBERFORMAT_UNDEFINED) eScannedType = NUMBERFORMAT_DATE; // !!! it IS a date SkipBlanks(rString, nPos); } else return MatchedReturn(); } else { nDecPos = 3; // . in end string SkipBlanks(rString, nPos); } } if ( nSign == 0 // conflict - not signed && eScannedType != NUMBERFORMAT_DATE) // and not date //!? catch time too? { // not signed yet nSign = GetSign(rString, nPos); // 1- DM if (nNegCheck) // '(' as sign return MatchedReturn(); } SkipBlanks(rString, nPos); if (nNegCheck && SkipChar(')', rString, nPos)) // skip ')' if appropriate { nNegCheck = 0; SkipBlanks(rString, nPos); } if ( GetCurrency(rString, nPos, pFormat) ) // currency symbol? { if (eScannedType != NUMBERFORMAT_UNDEFINED) // currency dup return MatchedReturn(); else { SkipBlanks(rString, nPos); eScannedType = NUMBERFORMAT_CURRENCY; } // behind currency a '-' is allowed if (nSign == 0) // not signed yet { nSign = GetSign(rString, nPos); // DM - SkipBlanks(rString, nPos); if (nNegCheck) // 3 DM ( return MatchedReturn(); } if ( nNegCheck && eScannedType == NUMBERFORMAT_CURRENCY && SkipChar(')', rString, nPos) ) { nNegCheck = 0; // ')' skipped SkipBlanks(rString, nPos); // only if currency } } if ( SkipChar('%', rString, nPos) ) // 1 % { if (eScannedType != NUMBERFORMAT_UNDEFINED) // already another type return MatchedReturn(); SkipBlanks(rString, nPos); eScannedType = NUMBERFORMAT_PERCENT; } const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData(); const String& rDate = pFormatter->GetDateSep(); const String& rTime = pLoc->getTimeSep(); if ( SkipString(rTime, rString, nPos) ) // 10: { if (nDecPos) // already , => error return MatchedReturn(); if (eScannedType == NUMBERFORMAT_DATE && nAnzNums > 2) // 31.Dez.94 8: { SkipBlanks(rString, nPos); eScannedType = NUMBERFORMAT_DATETIME; } else if (eScannedType != NUMBERFORMAT_UNDEFINED && eScannedType != NUMBERFORMAT_TIME) // already another type return MatchedReturn(); else { SkipBlanks(rString, nPos); eScannedType = NUMBERFORMAT_TIME; } if ( !nTimePos ) nTimePos = nAnzStrings; } sal_Unicode cTime = rTime.GetChar(0); if ( SkipString(rDate, rString, nPos) // 10., 10-, 10/ || ((cTime != '.') && SkipChar('.', rString, nPos)) // TRICKY: || ((cTime != '/') && SkipChar('/', rString, nPos)) // short boolean || ((cTime != '-') && SkipChar('-', rString, nPos)) ) // evaluation! { if (eScannedType != NUMBERFORMAT_UNDEFINED && eScannedType != NUMBERFORMAT_DATE) // already another type return MatchedReturn(); else { SkipBlanks(rString, nPos); eScannedType = NUMBERFORMAT_DATE; } short nTmpMonth = GetMonth(rString, nPos); // 10. Jan if (nMonth && nTmpMonth) // month dup return MatchedReturn(); if (nTmpMonth) { nMonth = nTmpMonth; nMonthPos = 3; // month at end if ( nMonth < 0 ) SkipChar( '.', rString, nPos ); // abbreviated SkipBlanks(rString, nPos); } } short nTempMonth = GetMonth(rString, nPos); // 10 Jan if (nTempMonth) { if (nMonth) // month dup return MatchedReturn(); if (eScannedType != NUMBERFORMAT_UNDEFINED && eScannedType != NUMBERFORMAT_DATE) // already another type return MatchedReturn(); eScannedType = NUMBERFORMAT_DATE; nMonth = nTempMonth; nMonthPos = 3; // month at end if ( nMonth < 0 ) SkipChar( '.', rString, nPos ); // abbreviated SkipBlanks(rString, nPos); } xub_StrLen nOrigPos = nPos; if (GetTimeAmPm(rString, nPos)) { if (eScannedType != NUMBERFORMAT_UNDEFINED && eScannedType != NUMBERFORMAT_TIME && eScannedType != NUMBERFORMAT_DATETIME) // already another type return MatchedReturn(); else { // If not already scanned as time, 6.78am does not result in 6 // seconds and 78 hundredths in the morning. Keep as suffix. if (eScannedType != NUMBERFORMAT_TIME && nDecPos == 2 && nAnzNums == 2) nPos = nOrigPos; // rewind am/pm else { SkipBlanks(rString, nPos); if ( eScannedType != NUMBERFORMAT_DATETIME ) eScannedType = NUMBERFORMAT_TIME; } } } if ( nNegCheck && SkipChar(')', rString, nPos) ) { if (eScannedType == NUMBERFORMAT_CURRENCY) // only if currency { nNegCheck = 0; // skip ')' SkipBlanks(rString, nPos); } else return MatchedReturn(); } if ( nPos < rString.Len() && (eScannedType == NUMBERFORMAT_DATE || eScannedType == NUMBERFORMAT_DATETIME) ) { // day of week is just parsed away xub_StrLen nOldPos = nPos; const String& rSep = pFormatter->GetLocaleData()->getLongDateDayOfWeekSep(); if ( StringContains( rSep, rString, nPos ) ) { nPos = nPos + rSep.Len(); SkipBlanks(rString, nPos); } int nDayOfWeek = GetDayOfWeek( rString, nPos ); if ( nDayOfWeek ) { if ( nPos < rString.Len() ) { if ( nDayOfWeek < 0 ) { // short if ( rString.GetChar( nPos ) == '.' ) ++nPos; } SkipBlanks(rString, nPos); } } else nPos = nOldPos; } #if NF_RECOGNIZE_ISO8601_TIMEZONES if (nPos == 0 && eScannedType == NUMBERFORMAT_DATETIME && rString.Len() == 1 && rString.GetChar(0) == 'Z' && MayBeIso8601()) { // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ ++nPos; } #endif if (nPos < rString.Len()) // everything consumed? { // does input EndString equal EndString in Format? if ( !ScanStringNumFor( rString, nPos, pFormat, 0xFFFF ) ) return sal_False; } return sal_True; } sal_Bool ImpSvNumberInputScan::ScanStringNumFor( const String& rString, // String to scan xub_StrLen nPos, // Position until which was consumed const SvNumberformat* pFormat, // The format to match sal_uInt16 nString, // Substring of format, 0xFFFF => last sal_Bool bDontDetectNegation // Suppress sign detection ) { if ( !pFormat ) return sal_False; const ::utl::TransliterationWrapper* pTransliteration = pFormatter->GetTransliteration(); const String* pStr; String aString( rString ); sal_Bool bFound = sal_False; sal_Bool bFirst = sal_True; sal_Bool bContinue = sal_True; sal_uInt16 nSub; do { // Don't try "lower" subformats ff the very first match was the second // or third subformat. nSub = nStringScanNumFor; do { // Step through subformats, first positive, then negative, then // other, but not the last (text) subformat. pStr = pFormat->GetNumForString( nSub, nString, sal_True ); if ( pStr && pTransliteration->isEqual( aString, *pStr ) ) { bFound = sal_True; bContinue = sal_False; } else if ( nSub < 2 ) ++nSub; else bContinue = sal_False; } while ( bContinue ); if ( !bFound && bFirst && nPos ) { // try remaining substring bFirst = sal_False; aString.Erase( 0, nPos ); bContinue = sal_True; } } while ( bContinue ); if ( !bFound ) { if ( !bDontDetectNegation && (nString == 0) && !bFirst && (nSign < 0) && pFormat->IsNegativeRealNegative() ) { // simply negated twice? --1 aString.EraseAllChars( ' ' ); if ( (aString.Len() == 1) && (aString.GetChar(0) == '-') ) { bFound = sal_True; nStringScanSign = -1; nSub = 0; //! not 1 } } if ( !bFound ) return sal_False; } else if ( !bDontDetectNegation && (nSub == 1) && pFormat->IsNegativeRealNegative() ) { // negative if ( nStringScanSign < 0 ) { if ( (nSign < 0) && (nStringScanNumFor != 1) ) nStringScanSign = 1; // triple negated --1 yyy } else if ( nStringScanSign == 0 ) { if ( nSign < 0 ) { // nSign and nStringScanSign will be combined later, // flip sign if doubly negated if ( (nString == 0) && !bFirst && SvNumberformat::HasStringNegativeSign( aString ) ) nStringScanSign = -1; // direct double negation else if ( pFormat->IsNegativeWithoutSign() ) nStringScanSign = -1; // indirect double negation } else nStringScanSign = -1; } else // > 0 nStringScanSign = -1; } nStringScanNumFor = nSub; return sal_True; } //--------------------------------------------------------------------------- // IsNumberFormatMain // // Recognizes types of number, exponential, fraction, percent, currency, date, time. // Else text => return sal_False sal_Bool ImpSvNumberInputScan::IsNumberFormatMain( const String& rString, // string to be analyzed double& , // OUT: result as number, if possible const SvNumberformat* pFormat ) // maybe number format set to match against { Reset(); NumberStringDivision( rString ); // breakdown into strings and numbers if (nAnzStrings >= SV_MAX_ANZ_INPUT_STRINGS) // too many elements return sal_False; // Njet, Nope, ... if (nAnzNums == 0) // no number in input { if ( nAnzStrings > 0 ) { // Here we may change the original, we don't need it anymore. // This saves copies and ToUpper() in GetLogical() and is faster. String& rStrArray = sStrArray[0]; rStrArray.EraseTrailingChars( ' ' ); rStrArray.EraseLeadingChars( ' ' ); nLogical = GetLogical( rStrArray ); if ( nLogical ) { eScannedType = NUMBERFORMAT_LOGICAL; // !!! it's a BOOLEAN nMatchedAllStrings &= ~nMatchedVirgin; return sal_True; } else return sal_False; // simple text } else return sal_False; // simple text } sal_uInt16 i = 0; // mark any symbol sal_uInt16 j = 0; // mark only numbers switch ( nAnzNums ) { case 1 : // Exactly 1 number in input { // nAnzStrings >= 1 if (GetNextNumber(i,j)) // i=1,0 { // Number at start if (eSetType == NUMBERFORMAT_FRACTION) // Fraction 1 = 1/1 { if (i >= nAnzStrings || // no end string nor decimal separator sStrArray[i] == pFormatter->GetNumDecimalSep()) { eScannedType = NUMBERFORMAT_FRACTION; nMatchedAllStrings &= ~nMatchedVirgin; return sal_True; } } } else { // Analyze start string if (!ScanStartString( sStrArray[i], pFormat )) // i=0 return sal_False; // already an error i++; // next symbol, i=1 } GetNextNumber(i,j); // i=1,2 if (eSetType == NUMBERFORMAT_FRACTION) // Fraction -1 = -1/1 { if (nSign && !nNegCheck && // Sign +, - eScannedType == NUMBERFORMAT_UNDEFINED && // not date or currency nDecPos == 0 && // no previous decimal separator (i >= nAnzStrings || // no end string nor decimal separator sStrArray[i] == pFormatter->GetNumDecimalSep()) ) { eScannedType = NUMBERFORMAT_FRACTION; nMatchedAllStrings &= ~nMatchedVirgin; return sal_True; } } if (i < nAnzStrings && !ScanEndString( sStrArray[i], pFormat )) return sal_False; } break; case 2 : // Exactly 2 numbers in input { // nAnzStrings >= 3 if (!GetNextNumber(i,j)) // i=1,0 { // Analyze start string if (!ScanStartString( sStrArray[i], pFormat )) return sal_False; // already an error i++; // i=1 } GetNextNumber(i,j); // i=1,2 if ( !ScanMidString( sStrArray[i], i, pFormat ) ) return sal_False; i++; // next symbol, i=2,3 GetNextNumber(i,j); // i=3,4 if (i < nAnzStrings && !ScanEndString( sStrArray[i], pFormat )) return sal_False; if (eSetType == NUMBERFORMAT_FRACTION) // -1,200. as fraction { if (!nNegCheck && // no sign '(' eScannedType == NUMBERFORMAT_UNDEFINED && (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end ) { eScannedType = NUMBERFORMAT_FRACTION; nMatchedAllStrings &= ~nMatchedVirgin; return sal_True; } } } break; case 3 : // Exactly 3 numbers in input { // nAnzStrings >= 5 if (!GetNextNumber(i,j)) // i=1,0 { // Analyze start string if (!ScanStartString( sStrArray[i], pFormat )) return sal_False; // already an error i++; // i=1 if (nDecPos == 1) // decimal separator at start => error return sal_False; } GetNextNumber(i,j); // i=1,2 if ( !ScanMidString( sStrArray[i], i, pFormat ) ) return sal_False; i++; // i=2,3 if (eScannedType == NUMBERFORMAT_SCIENTIFIC) // E only at end return sal_False; GetNextNumber(i,j); // i=3,4 if ( !ScanMidString( sStrArray[i], i, pFormat ) ) return sal_False; i++; // i=4,5 GetNextNumber(i,j); // i=5,6 if (i < nAnzStrings && !ScanEndString( sStrArray[i], pFormat )) return sal_False; if (eSetType == NUMBERFORMAT_FRACTION) // -1,200,100. as fraction { if (!nNegCheck && // no sign '(' eScannedType == NUMBERFORMAT_UNDEFINED && (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end ) { eScannedType = NUMBERFORMAT_FRACTION; nMatchedAllStrings &= ~nMatchedVirgin; return sal_True; } } if ( eScannedType == NUMBERFORMAT_FRACTION && nDecPos ) return sal_False; // #36857# not a real fraction } break; default: // More than 3 numbers in input { // nAnzStrings >= 7 if (!GetNextNumber(i,j)) // i=1,0 { // Analyze startstring if (!ScanStartString( sStrArray[i], pFormat )) return sal_False; // already an error i++; // i=1 if (nDecPos == 1) // decimal separator at start => error return sal_False; } GetNextNumber(i,j); // i=1,2 if ( !ScanMidString( sStrArray[i], i, pFormat ) ) return sal_False; i++; // i=2,3 sal_uInt16 nThOld = 10; // just not 0 or 1 while (nThOld != nThousand && j < nAnzNums-1) // Execute at least one time // but leave one number. { // Loop over group separators nThOld = nThousand; if (eScannedType == NUMBERFORMAT_SCIENTIFIC) // E only at end return sal_False; GetNextNumber(i,j); if ( i < nAnzStrings && !ScanMidString( sStrArray[i], i, pFormat ) ) return sal_False; i++; } if (eScannedType == NUMBERFORMAT_DATE || // long date or eScannedType == NUMBERFORMAT_TIME || // long time or eScannedType == NUMBERFORMAT_UNDEFINED) // long number { for (sal_uInt16 k = j; k < nAnzNums-1; k++) { if (eScannedType == NUMBERFORMAT_SCIENTIFIC) // E only at endd return sal_False; GetNextNumber(i,j); if ( i < nAnzStrings && !ScanMidString( sStrArray[i], i, pFormat ) ) return sal_False; i++; } } GetNextNumber(i,j); if (i < nAnzStrings && !ScanEndString( sStrArray[i], pFormat )) return sal_False; if (eSetType == NUMBERFORMAT_FRACTION) // -1,200,100. as fraction { if (!nNegCheck && // no sign '(' eScannedType == NUMBERFORMAT_UNDEFINED && (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end ) { eScannedType = NUMBERFORMAT_FRACTION; nMatchedAllStrings &= ~nMatchedVirgin; return sal_True; } } if ( eScannedType == NUMBERFORMAT_FRACTION && nDecPos ) return sal_False; // #36857# not a real fraction } } if (eScannedType == NUMBERFORMAT_UNDEFINED) { nMatchedAllStrings &= ~nMatchedVirgin; // did match including nMatchedUsedAsReturn sal_Bool bDidMatch = (nMatchedAllStrings != 0); if ( nMatchedAllStrings ) { sal_Bool bMatch = (pFormat ? pFormat->IsNumForStringElementCountEqual( nStringScanNumFor, nAnzStrings, nAnzNums ) : sal_False); if ( !bMatch ) nMatchedAllStrings = 0; } if ( nMatchedAllStrings ) eScannedType = eSetType; else if ( bDidMatch ) return sal_False; else eScannedType = NUMBERFORMAT_NUMBER; // everything else should have been recognized by now } else if ( eScannedType == NUMBERFORMAT_DATE ) { // the very relaxed date input checks may interfere with a preset format nMatchedAllStrings &= ~nMatchedVirgin; sal_Bool bWasReturn = ((nMatchedAllStrings & nMatchedUsedAsReturn) != 0); if ( nMatchedAllStrings ) { sal_Bool bMatch = (pFormat ? pFormat->IsNumForStringElementCountEqual( nStringScanNumFor, nAnzStrings, nAnzNums ) : sal_False); if ( !bMatch ) nMatchedAllStrings = 0; } if ( nMatchedAllStrings ) eScannedType = eSetType; else if ( bWasReturn ) return sal_False; } else nMatchedAllStrings = 0; // reset flag to no substrings matched return sal_True; } //--------------------------------------------------------------------------- // return sal_True or sal_False depending on the nMatched... state and remember usage sal_Bool ImpSvNumberInputScan::MatchedReturn() { if ( nMatchedAllStrings & ~nMatchedVirgin ) { nMatchedAllStrings |= nMatchedUsedAsReturn; return sal_True; } return sal_False; } //--------------------------------------------------------------------------- // Initialize uppercase months and weekdays void ImpSvNumberInputScan::InitText() { sal_Int32 j, nElems; const CharClass* pChrCls = pFormatter->GetCharClass(); const CalendarWrapper* pCal = pFormatter->GetCalendar(); delete [] pUpperMonthText; delete [] pUpperAbbrevMonthText; ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::CalendarItem > xElems = pCal->getMonths(); nElems = xElems.getLength(); pUpperMonthText = new String[nElems]; pUpperAbbrevMonthText = new String[nElems]; for ( j=0; jupper( xElems[j].FullName ); pUpperAbbrevMonthText[j] = pChrCls->upper( xElems[j].AbbrevName ); } delete [] pUpperDayText; delete [] pUpperAbbrevDayText; xElems = pCal->getDays(); nElems = xElems.getLength(); pUpperDayText = new String[nElems]; pUpperAbbrevDayText = new String[nElems]; for ( j=0; jupper( xElems[j].FullName ); pUpperAbbrevDayText[j] = pChrCls->upper( xElems[j].AbbrevName ); } bTextInitialized = sal_True; } //=========================================================================== // P U B L I C //--------------------------------------------------------------------------- // ChangeIntl // // MUST be called if International/Locale is changed void ImpSvNumberInputScan::ChangeIntl() { sal_Unicode cDecSep = pFormatter->GetNumDecimalSep().GetChar(0); bDecSepInDateSeps = ( cDecSep == '-' || cDecSep == '/' || cDecSep == '.' || cDecSep == pFormatter->GetDateSep().GetChar(0) ); bTextInitialized = sal_False; aUpperCurrSymbol.Erase(); } //--------------------------------------------------------------------------- // ChangeNullDate void ImpSvNumberInputScan::ChangeNullDate( const sal_uInt16 Day, const sal_uInt16 Month, const sal_uInt16 Year ) { if ( pNullDate ) *pNullDate = Date(Day, Month, Year); else pNullDate = new Date(Day, Month, Year); } //--------------------------------------------------------------------------- // IsNumberFormat // // => does rString represent a number (also date, time et al) sal_Bool ImpSvNumberInputScan::IsNumberFormat( const String& rString, // string to be analyzed short& F_Type, // IN: old type, OUT: new type double& fOutNumber, // OUT: number if convertable const SvNumberformat* pFormat ) // maybe a number format to match against { String sResString; String aString; sal_Bool res; // return value eSetType = F_Type; // old type set if ( !rString.Len() ) res = sal_False; else if (rString.Len() > 308) // arbitrary res = sal_False; else { // NoMoreUpperNeeded, all comparisons on UpperCase aString = pFormatter->GetCharClass()->upper( rString ); // convert native number to ASCII if necessary TransformInput( aString ); res = IsNumberFormatMain( aString, fOutNumber, pFormat ); } if (res) { if ( nNegCheck // ')' not found for '(' || (nSign && (eScannedType == NUMBERFORMAT_DATE || eScannedType == NUMBERFORMAT_DATETIME)) ) // signed date/datetime res = sal_False; else { // check count of partial number strings switch (eScannedType) { case NUMBERFORMAT_PERCENT: case NUMBERFORMAT_CURRENCY: case NUMBERFORMAT_NUMBER: if (nDecPos == 1) // .05 { // matched MidStrings function like group separators if ( nMatchedAllStrings ) nThousand = nAnzNums - 1; else if ( nAnzNums != 1 ) res = sal_False; } else if (nDecPos == 2) // 1.05 { // matched MidStrings function like group separators if ( nMatchedAllStrings ) nThousand = nAnzNums - 1; else if ( nAnzNums != nThousand+2 ) res = sal_False; } else // 1,100 or 1,100. { // matched MidStrings function like group separators if ( nMatchedAllStrings ) nThousand = nAnzNums - 1; else if ( nAnzNums != nThousand+1 ) res = sal_False; } break; case NUMBERFORMAT_SCIENTIFIC: // 1.0e-2 if (nDecPos == 1) // .05 { if (nAnzNums != 2) res = sal_False; } else if (nDecPos == 2) // 1.05 { if (nAnzNums != nThousand+3) res = sal_False; } else // 1,100 or 1,100. { if (nAnzNums != nThousand+2) res = sal_False; } break; case NUMBERFORMAT_DATE: if (nMonth) { // month name and numbers if (nAnzNums > 2) res = sal_False; } else { if (nAnzNums > 3) res = sal_False; } break; case NUMBERFORMAT_TIME: if (nDecPos) { // hundredth seconds included if (nAnzNums > 4) res = sal_False; } else { if (nAnzNums > 3) res = sal_False; } break; case NUMBERFORMAT_DATETIME: if (nMonth) { // month name and numbers if (nDecPos) { // hundredth seconds included if (nAnzNums > 6) res = sal_False; } else { if (nAnzNums > 5) res = sal_False; } } else { if (nDecPos) { // hundredth seconds included if (nAnzNums > 7) res = sal_False; } else { if (nAnzNums > 6) res = sal_False; } } break; default: break; } // switch } // else } // if (res) if (res) { // we finally have a number switch (eScannedType) { case NUMBERFORMAT_LOGICAL: if (nLogical == 1) fOutNumber = 1.0; // True else if (nLogical == -1) fOutNumber = 0.0; // False else res = sal_False; // Oops break; case NUMBERFORMAT_PERCENT: case NUMBERFORMAT_CURRENCY: case NUMBERFORMAT_NUMBER: case NUMBERFORMAT_SCIENTIFIC: case NUMBERFORMAT_DEFINED: // if no category detected handle as number { if ( nDecPos == 1 ) // . at start sResString.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "0." ) ); else sResString.Erase(); sal_uInt16 k; for ( k = 0; k <= nThousand; k++) sResString += sStrArray[nNums[k]]; // integer part if ( nDecPos == 2 && k < nAnzNums ) // . somewhere { sResString += '.'; sal_uInt16 nStop = (eScannedType == NUMBERFORMAT_SCIENTIFIC ? nAnzNums-1 : nAnzNums); for ( ; k < nStop; k++) sResString += sStrArray[nNums[k]]; // fractional part } if (eScannedType != NUMBERFORMAT_SCIENTIFIC) fOutNumber = StringToDouble(sResString); else { // append exponent sResString += 'E'; if ( nESign == -1 ) sResString += '-'; sResString += sStrArray[nNums[nAnzNums-1]]; rtl_math_ConversionStatus eStatus; fOutNumber = ::rtl::math::stringToDouble( sResString, '.', ',', &eStatus, NULL ); if ( eStatus == rtl_math_ConversionStatus_OutOfRange ) { F_Type = NUMBERFORMAT_TEXT; // overflow/underflow -> Text if (nESign == -1) fOutNumber = 0.0; else fOutNumber = DBL_MAX; /*!*/ return sal_True; } } if ( nStringScanSign ) { if ( nSign ) nSign *= nStringScanSign; else nSign = nStringScanSign; } if ( nSign < 0 ) fOutNumber = -fOutNumber; if (eScannedType == NUMBERFORMAT_PERCENT) fOutNumber/= 100.0; } break; case NUMBERFORMAT_FRACTION: if (nAnzNums == 1) fOutNumber = StringToDouble(sStrArray[nNums[0]]); else if (nAnzNums == 2) { if (nThousand == 1) { sResString = sStrArray[nNums[0]]; sResString += sStrArray[nNums[1]]; // integer part fOutNumber = StringToDouble(sResString); } else { double fZaehler = StringToDouble(sStrArray[nNums[0]]); double fNenner = StringToDouble(sStrArray[nNums[1]]); if (fNenner != 0.0) fOutNumber = fZaehler/fNenner; else res = sal_False; } } else // nAnzNums > 2 { sal_uInt16 k = 1; sResString = sStrArray[nNums[0]]; if (nThousand > 0) for (k = 1; k <= nThousand; k++) sResString += sStrArray[nNums[k]]; fOutNumber = StringToDouble(sResString); if (k == nAnzNums-2) { double fZaehler = StringToDouble(sStrArray[nNums[k]]); double fNenner = StringToDouble(sStrArray[nNums[k+1]]); if (fNenner != 0.0) fOutNumber += fZaehler/fNenner; else res = sal_False; } } if ( nStringScanSign ) { if ( nSign ) nSign *= nStringScanSign; else nSign = nStringScanSign; } if ( nSign < 0 ) fOutNumber = -fOutNumber; break; case NUMBERFORMAT_TIME: GetTimeRef(fOutNumber, 0, nAnzNums); if ( nSign < 0 ) fOutNumber = -fOutNumber; break; case NUMBERFORMAT_DATE: { sal_uInt16 nCounter = 0; // dummy here res = GetDateRef( fOutNumber, nCounter, pFormat ); } break; case NUMBERFORMAT_DATETIME: { sal_uInt16 nCounter = 0; // needed here res = GetDateRef( fOutNumber, nCounter, pFormat ); if ( res ) { double fTime; GetTimeRef( fTime, nCounter, nAnzNums - nCounter ); fOutNumber += fTime; } } break; default: DBG_ERRORFILE( "Some number recognized but what's it?" ); fOutNumber = 0.0; break; } } if (res) // overflow/underflow -> Text { if (fOutNumber < -DBL_MAX) // -1.7E308 { F_Type = NUMBERFORMAT_TEXT; fOutNumber = -DBL_MAX; return sal_True; } else if (fOutNumber > DBL_MAX) // 1.7E308 { F_Type = NUMBERFORMAT_TEXT; fOutNumber = DBL_MAX; return sal_True; } } if (res == sal_False) { eScannedType = NUMBERFORMAT_TEXT; fOutNumber = 0.0; } F_Type = eScannedType; return res; }