/libreoffice-3.6.0.2/svl/source/numbers/zforfind.cxx
C++ | 3239 lines | 2652 code | 259 blank | 328 comment | 889 complexity | ca47b952384c783acf069ae37e5ab611 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, AGPL-1.0, BSD-3-Clause-No-Nuclear-License-2014, GPL-3.0, LGPL-3.0
Large files files are truncated, but you can click here to view the full file
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /*************************************************************************
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * Copyright 2000, 2010 Oracle and/or its affiliates.
- *
- * OpenOffice.org - a multi-platform office productivity suite
- *
- * This file is part of OpenOffice.org.
- *
- * OpenOffice.org is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 3
- * only, as published by the Free Software Foundation.
- *
- * OpenOffice.org is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License version 3 for more details
- * (a copy is included in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU Lesser General Public License
- * version 3 along with OpenOffice.org. If not, see
- * <http://www.openoffice.org/license.html>
- * for a copy of the LGPLv3 License.
- *
- ************************************************************************/
- #include <ctype.h>
- #include <stdlib.h>
- #include <float.h>
- #include <errno.h>
- #include <comphelper/string.hxx>
- #include <tools/date.hxx>
- #include <tools/debug.hxx>
- #include <rtl/math.hxx>
- #include <unotools/charclass.hxx>
- #include <unotools/calendarwrapper.hxx>
- #include <unotools/localedatawrapper.hxx>
- #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
- #include <unotools/digitgroupingiterator.hxx>
- #include <svl/zforlist.hxx> // NUMBERFORMAT_XXX
- #include "zforscan.hxx"
- #include <svl/zformat.hxx>
- #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 <comphelper/processfactory.hxx>
- #include <com/sun/star/i18n/XCalendar3.hpp>
- #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 ),
- pUpperGenitiveMonthText( NULL ),
- pUpperGenitiveAbbrevMonthText( NULL ),
- pUpperPartitiveMonthText( NULL ),
- pUpperPartitiveAbbrevMonthText( NULL ),
- pUpperDayText( NULL ),
- pUpperAbbrevDayText( NULL ),
- bTextInitialized( false ),
- bScanGenitiveMonths( false ),
- bScanPartitiveMonths( false ),
- eScannedType( NUMBERFORMAT_UNDEFINED ),
- eSetType( NUMBERFORMAT_UNDEFINED )
- {
- pFormatter = pFormatterP;
- pNullDate = new Date(30,12,1899);
- nYear2000 = SvNumberFormatter::GetYear2000Default();
- Reset();
- ChangeIntl();
- }
- //---------------------------------------------------------------------------
- // Destruktor
- ImpSvNumberInputScan::~ImpSvNumberInputScan()
- {
- Reset();
- delete pNullDate;
- delete [] pUpperMonthText;
- delete [] pUpperAbbrevMonthText;
- delete [] pUpperGenitiveMonthText;
- delete [] pUpperGenitiveAbbrevMonthText;
- delete [] pUpperPartitiveMonthText;
- delete [] pUpperPartitiveAbbrevMonthText;
- delete [] pUpperDayText;
- delete [] pUpperAbbrevDayText;
- }
- //---------------------------------------------------------------------------
- // Reset
- void ImpSvNumberInputScan::Reset()
- {
- 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;
- nMayBeMonthDate = 0;
- nAcceptedDatePattern = -2;
- nDatePatternStart = 0;
- nCanForceToIso8601 = 0;
- }
- //---------------------------------------------------------------------------
- //
- // static
- inline bool ImpSvNumberInputScan::MyIsdigit( sal_Unicode c )
- {
- return c < 128 && isdigit( (unsigned char) c );
- }
- //---------------------------------------------------------------------------
- //
- 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, bool bForceFraction )
- {
- double fNum = 0.0;
- double fFrac = 0.0;
- int nExp = 0;
- xub_StrLen nPos = 0;
- xub_StrLen nLen = rStr.Len();
- bool bPreSep = !bForceFraction;
- while (nPos < nLen)
- {
- if (rStr.GetChar(nPos) == '.')
- bPreSep = 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
- };
- bool ImpSvNumberInputScan::NextNumberStringSymbol(
- const sal_Unicode*& pStr,
- String& rSymbol )
- {
- bool isNumber = false;
- sal_Unicode cToken;
- ScanState eState = SsStart;
- register const sal_Unicode* pHere = pStr;
- register xub_StrLen nChars = 0;
- while ( ((cToken = *pHere) != 0) && eState != SsStop)
- {
- pHere++;
- switch (eState)
- {
- case SsStart:
- if ( MyIsdigit( cToken ) )
- {
- eState = SsGetValue;
- isNumber = 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().
- bool ImpSvNumberInputScan::SkipThousands(
- const sal_Unicode*& pStr,
- String& rSymbol )
- {
- bool res = false;
- sal_Unicode cToken;
- const String& rThSep = pFormatter->GetNumThousandSep();
- register 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 = 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] = 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] = false;
- }
- nAnzStrings++;
- }
- }
- //---------------------------------------------------------------------------
- // Whether rString contains rWhat at nPos
- 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 false;
- }
- //---------------------------------------------------------------------------
- // Whether pString contains rWhat at nPos
- bool ImpSvNumberInputScan::StringPtrContainsImpl( const String& rWhat,
- const sal_Unicode* pString, xub_StrLen nPos )
- {
- if ( rWhat.Len() == 0 )
- return false;
- register const sal_Unicode* pWhat = rWhat.GetBuffer();
- register const sal_Unicode* const pEnd = pWhat + rWhat.Len();
- register const sal_Unicode* pStr = pString + nPos;
- while ( pWhat < pEnd )
- {
- if ( *pWhat != *pStr )
- return false;
- pWhat++;
- pStr++;
- }
- return true;
- }
- //---------------------------------------------------------------------------
- // SkipChar
- //
- // ueberspringt genau das angegebene Zeichen
- inline bool ImpSvNumberInputScan::SkipChar( sal_Unicode c, const String& rString,
- xub_StrLen& nPos )
- {
- if ((nPos < rString.Len()) && (rString.GetChar(nPos) == c))
- {
- nPos++;
- return true;
- }
- return false;
- }
- //---------------------------------------------------------------------------
- // SkipBlanks
- //
- // Ueberspringt Leerzeichen
- inline void ImpSvNumberInputScan::SkipBlanks( const String& rString,
- xub_StrLen& nPos )
- {
- if ( nPos < rString.Len() )
- {
- register const sal_Unicode* p = rString.GetBuffer() + nPos;
- while ( *p == ' ' )
- {
- nPos++;
- p++;
- }
- }
- }
- //---------------------------------------------------------------------------
- // SkipString
- //
- // jump over rWhat in rString at nPos
- inline bool ImpSvNumberInputScan::SkipString( const String& rWhat,
- const String& rString, xub_StrLen& nPos )
- {
- if ( StringContains( rWhat, rString, nPos ) )
- {
- nPos = nPos + rWhat.Len();
- return true;
- }
- return false;
- }
- //---------------------------------------------------------------------------
- // GetThousandSep
- //
- // recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping
- inline 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 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 true;
- }
- return false;
- }
- //---------------------------------------------------------------------------
- // GetLogical
- //
- // Conversion of text to logial value
- // "true" => 1:
- // "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 ( bScanGenitiveMonths && StringContains( pUpperGenitiveMonthText[i], rString, nPos ) )
- { // genitive full names first
- nPos = nPos + pUpperGenitiveMonthText[i].Len();
- res = i+1;
- break; // for
- }
- else if ( bScanGenitiveMonths && StringContains( pUpperGenitiveAbbrevMonthText[i], rString, nPos ) )
- { // genitive abbreviated
- nPos = nPos + pUpperGenitiveAbbrevMonthText[i].Len();
- res = sal::static_int_cast< short >(-(i+1)); // negative
- break; // for
- }
- else if ( bScanPartitiveMonths && StringContains( pUpperPartitiveMonthText[i], rString, nPos ) )
- { // partitive full names
- nPos = nPos + pUpperPartitiveMonthText[i].Len();
- res = i+1;
- break; // for
- }
- else if ( bScanPartitiveMonths && StringContains( pUpperPartitiveAbbrevMonthText[i], rString, nPos ) )
- { // partitive abbreviated
- nPos = nPos + pUpperPartitiveAbbrevMonthText[i].Len();
- res = sal::static_int_cast< short >(-(i+1)); // negative
- break; // for
- }
- else if ( StringContains( pUpperMonthText[i], rString, nPos ) )
- { // noun full names
- nPos = nPos + pUpperMonthText[i].Len();
- res = i+1;
- break; // for
- }
- else if ( StringContains( pUpperAbbrevMonthText[i], rString, nPos ) )
- { // noun 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
- // '$' => true
- // sonst => false
- 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()->uppercase(
- SvNumberFormatter::GetCurrencyEntry( eLang ).GetSymbol() );
- }
- if ( StringContains( aUpperCurrSymbol, rString, nPos ) )
- {
- nPos = nPos + aUpperCurrSymbol.Len();
- return true;
- }
- if ( pFormat )
- {
- String aSymbol, aExtension;
- if ( pFormat->GetNewCurrencySymbol( aSymbol, aExtension ) )
- {
- if ( aSymbol.Len() <= rString.Len() - nPos )
- {
- aSymbol = pFormatter->GetCharClass()->uppercase(aSymbol);
- if ( StringContains( aSymbol, rString, nPos ) )
- {
- nPos = nPos + aSymbol.Len();
- return true;
- }
- }
- }
- }
- }
- return false;
- }
- //---------------------------------------------------------------------------
- // GetTimeAmPm
- //
- // Lesen des Zeitsymbols (AM od. PM) f. kurze Zeitangabe
- //
- // Rueckgabe:
- // "AM" od. "PM" => true
- // sonst => false
- //
- // nAmPos:
- // "AM" => 1
- // "PM" => -1
- // sonst => 0
- 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->uppercase( pLoc->getTimeAM() ), rString, nPos ) )
- {
- nAmPm = 1;
- nPos = nPos + pLoc->getTimeAM().Len();
- return true;
- }
- else if ( StringContains( pChr->uppercase( pLoc->getTimePM() ), rString, nPos ) )
- {
- nAmPm = -1;
- nPos = nPos + pLoc->getTimePM().Len();
- return true;
- }
- }
- return false;
- }
- //---------------------------------------------------------------------------
- // GetDecSep
- //
- // Lesen eines Dezimaltrenners (',')
- // ',' => true
- // sonst => false
- inline 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 true;
- }
- }
- return false;
- }
- //---------------------------------------------------------------------------
- // read a hundredth seconds separator
- inline 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 true;
- }
- }
- return 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 bool ImpSvNumberInputScan::GetNextNumber( sal_uInt16& i, sal_uInt16& j )
- {
- if ( i < nAnzStrings && IsNum[i] )
- {
- j++;
- i++;
- return true;
- }
- return false;
- }
- //---------------------------------------------------------------------------
- // GetTimeRef
- bool 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
- {
- bool bRet = true;
- 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; j<nAnzNums; ++j)
- {
- if (nNums[j] == nTimezonePos)
- {
- // nAnz is not total count, but count of time relevant strings.
- if (nStartIndex < j && j - nStartIndex < nAnz)
- nAnz = j - nStartIndex;
- break; // for
- }
- }
- }
- if (nDecPos == 2 && (nAnz == 3 || nAnz == 2)) // 20:45.5 or 45.5
- nHour = 0;
- else if (nIndex - nStartIndex < nAnz)
- nHour = (sal_uInt16) sStrArray[nNums[nIndex++]].ToInt32();
- else
- {
- nHour = 0;
- bRet = false;
- SAL_WARN( "svl.items", "ImpSvNumberInputScan::GetTimeRef: bad number index");
- }
- if (nDecPos == 2 && nAnz == 2) // 45.5
- nMinute = 0;
- else if (nIndex - nStartIndex < nAnz)
- nMinute = (sal_uInt16) sStrArray[nNums[nIndex++]].ToInt32();
- if (nIndex - nStartIndex < nAnz)
- nSecond = (sal_uInt16) sStrArray[nNums[nIndex++]].ToInt32();
- if (nIndex - nStartIndex < nAnz)
- fSecond100 = StringToDouble( sStrArray[nNums[nIndex]], true );
- if (nAmPm && nHour > 12) // not a valid AM/PM clock time
- bRet = false;
- else if (nAmPm == -1 && nHour != 12) // PM
- nHour += 12;
- else if (nAmPm == 1 && nHour == 12) // 12 AM
- nHour = 0;
- fOutNumber = ((double)nHour*3600 +
- (double)nMinute*60 +
- (double)nSecond +
- fSecond100)/86400.0;
- return bRet;
- }
- //---------------------------------------------------------------------------
- // ImplGetDay
- sal_uInt16 ImpSvNumberInputScan::ImplGetDay( sal_uInt16 nIndex )
- {
- sal_uInt16 nRes = 0;
- if (sStrArray[nNums[nIndex]].Len() <= 2)
- {
- sal_uInt16 nNum = (sal_uInt16) sStrArray[nNums[nIndex]].ToInt32();
- if (nNum <= 31)
- nRes = nNum;
- }
- return nRes;
- }
- //---------------------------------------------------------------------------
- // ImplGetMonth
- sal_uInt16 ImpSvNumberInputScan::ImplGetMonth( sal_uInt16 nIndex )
- {
- // preset invalid month number
- sal_uInt16 nRes = pFormatter->GetCalendar()->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;
- xub_StrLen nLen = sStrArray[nNums[nIndex]].Len();
- if (nLen <= 4)
- {
- nYear = (sal_uInt16) sStrArray[nNums[nIndex]].ToInt32();
- // A year < 100 entered with at least 3 digits with leading 0 is taken
- // as is without expansion.
- if (nYear < 100 && nLen < 3)
- nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 );
- }
- return nYear;
- }
- //---------------------------------------------------------------------------
- bool ImpSvNumberInputScan::MayBeIso8601()
- {
- if (nMayBeIso8601 == 0)
- {
- nMayBeIso8601 = 1;
- xub_StrLen nLen = ((nAnzNums >= 1 && nNums[0] < nAnzStrings) ? sStrArray[nNums[0]].Len() : 0);
- if (nLen)
- {
- sal_Int32 n;
- if (nAnzNums >= 3 && nNums[2] < nAnzStrings &&
- sStrArray[nNums[0]+1] == '-' && // separator year-month
- (n = sStrArray[nNums[1]].ToInt32()) >= 1 && n <= 12 && // month
- sStrArray[nNums[1]+1] == '-' && // separator month-day
- (n = sStrArray[nNums[2]].ToInt32()) >= 1 && n <= 31) // day
- // Year (nNums[0]) value not checked, may be anything, but
- // length (number of digits) is checked.
- nMayBeIso8601 = (nLen >= 4 ? 4 : (nLen == 3 ? 3 : (nLen > 0 ? 2 : 1)));
- }
- }
- return nMayBeIso8601 > 1;
- }
- //---------------------------------------------------------------------------
- bool ImpSvNumberInputScan::CanForceToIso8601( DateFormat eDateFormat )
- {
- if (nCanForceToIso8601 == 0)
- {
- nCanForceToIso8601 = 1;
- do
- {
- if (!MayBeIso8601())
- break;
- if (nMayBeIso8601 >= 3)
- {
- nCanForceToIso8601 = 2; // at least 3 digits in year
- break;
- }
- if (pFormatter->GetDateSep() != '-')
- {
- nCanForceToIso8601 = 2; // date separator does not interfere
- break;
- }
- sal_Int32 n;
- switch (eDateFormat)
- {
- case DMY: // "day" value out of range => ISO 8601 year
- if ((n = sStrArray[nNums[0]].ToInt32()) < 1 || n > 31)
- nCanForceToIso8601 = 2;
- break;
- case MDY: // "month" value out of range => ISO 8601 year
- if ((n = sStrArray[nNums[0]].ToInt32()) < 1 || n > 12)
- nCanForceToIso8601 = 2;
- break;
- case YMD: // always possible
- nCanForceToIso8601 = 2;
- break;
- }
- } while (0);
- }
- return nCanForceToIso8601 > 1;
- }
- //---------------------------------------------------------------------------
- bool ImpSvNumberInputScan::MayBeMonthDate()
- {
- if (nMayBeMonthDate == 0)
- {
- nMayBeMonthDate = 1;
- if (nAnzNums >= 2 && nNums[1] < nAnzStrings)
- {
- // "-Jan-"
- const String& rM = sStrArray[nNums[0]+1];
- if (rM.Len() >= 3 && rM.GetChar(0) == '-' && rM.GetChar( rM.Len()-1) == '-')
- {
- // Check year length assuming at least 3 digits (including
- // leading zero). Two digit years 1..31 are out of luck here
- // and may be taken as day of month.
- bool bYear1 = (sStrArray[nNums[0]].Len() >= 3);
- bool bYear2 = (sStrArray[nNums[1]].Len() >= 3);
- sal_Int32 n;
- bool bDay1 = (!bYear1 && (n = sStrArray[nNums[0]].ToInt32()) >= 1 && n <= 31);
- bool bDay2 = (!bYear2 && (n = sStrArray[nNums[1]].ToInt32()) >= 1 && n <= 31);
- if (bDay1 && !bDay2)
- nMayBeMonthDate = 2; // dd-month-yy
- else if (!bDay1 && bDay2)
- nMayBeMonthDate = 3; // yy-month-dd
- else if (bDay1 && bDay2)
- {
- if (bYear1 && !bYear2)
- nMayBeMonthDate = 3; // yy-month-dd
- else if (!bYear1 && bYear2)
- nMayBeMonthDate = 2; // dd-month-yy
- }
- }
- }
- }
- return nMayBeMonthDate > 1;
- }
- //---------------------------------------------------------------------------
- bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
- {
- if (nAcceptedDatePattern >= -1)
- return (nAcceptedDatePattern >= 0);
- if (!nAnzNums)
- nAcceptedDatePattern = -1;
- else if (!sDateAcceptancePatterns.getLength())
- {
- sDateAcceptancePatterns = pFormatter->GetLocaleData()->getDateAcceptancePatterns();
- SAL_WARN_IF( !sDateAcceptancePatterns.getLength(), "nf.date", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
- nAcceptedDatePattern = (sDateAcceptancePatterns.getLength() ? -2 : -1);
- }
- if (nAcceptedDatePattern == -1)
- return false;
- nDatePatternStart = nStartPatternAt; // remember start particle
- for (sal_Int32 nPattern=0; nPattern < sDateAcceptancePatterns.getLength(); ++nPattern)
- {
- sal_uInt16 nNext = nDatePatternStart;
- bool bOk = true;
- const rtl::OUString& rPat = sDateAcceptancePatterns[nPattern];
- sal_Int32 nPat = 0;
- for ( ; nPat < rPat.getLength() && bOk && nNext < nAnzStrings; ++nPat, ++nNext)
- {
- switch (rPat[nPat])
- {
- case 'Y':
- case 'M':
- case 'D':
- bOk = IsNum[nNext];
- break;
- default:
- bOk = !IsNum[nNext];
- if (bOk)
- {
- const xub_StrLen nLen = sStrArray[nNext].Len();
- bOk = (rPat.indexOf( sStrArray[nNext], nPat) == nPat);
- if (bOk)
- nPat += nLen - 1;
- else if (nPat + nLen > rPat.getLength() && sStrArray[nNext].GetChar(nLen-1) == ' ')
- {
- // Trailing blanks in input.
- String aStr( sStrArray[nNext]);
- aStr.EraseTrailingChars(' ');
- // Expand again in case of pattern "M. D. " and
- // input "M. D. ", maybe fetched far, but..
- aStr.Expand( rPat.getLength() - nPat, ' ');
- bOk = (rPat.indexOf( aStr, nPat) == nPat);
- if (bOk)
- nPat += aStr.Len() - 1;
- }
- }
- break;
- }
- }
- if (bOk)
- {
- // Check for trailing characters mismatch.
- if (nNext < nAnzStrings)
- {
- // Pattern end but not input end.
- if (!IsNum[nNext])
- {
- // Trailing (or separating if time follows) blanks are ok.
- xub_StrLen nPos = 0;
- SkipBlanks( sStrArray[nNext], nPos);
- if (nPos == sStrArray[nNext].Len())
- {
- nAcceptedDatePattern = nPattern;
- return true;
- }
- }
- }
- else if (nPat == rPat.getLength())
- {
- // Input end and pattern end => match.
- nAcceptedDatePattern = nPattern;
- return true;
- }
- // else Input end but not pattern end, no match.
- }
- }
- nAcceptedDatePattern = -1;
- return false;
- }
- //---------------------------------------------------------------------------
- bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, xub_StrLen & rPos )
- {
- // If not initialized yet start with first number, if any.
- if (!IsAcceptedDatePattern( (nAnzNums ? nNums[0] : 0)))
- return false;
- if (nParticle < nDatePatternStart || nParticle >= nAnzStrings || IsNum[nParticle])
- return false;
- sal_uInt16 nNext = nDatePatternStart;
- const rtl::OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
- for (sal_Int32 nPat = 0; nPat < rPat.getLength() && nNext < nAnzStrings; ++nPat, ++nNext)
- {
- switch (rPat[nPat])
- {
- case 'Y':
- case 'M':
- case 'D':
- break;
- default:
- if (nNext == nParticle)
- {
- const xub_StrLen nLen = sStrArray[nNext].Len();
- bool bOk = (rPat.indexOf( sStrArray[nNext], nPat) == nPat);
- if (!bOk && (nPat + nLen > rPat.getLength() && sStrArray[nNext].GetChar(nLen-1) == ' '))
- {
- // The same ugly trailing blanks check as in
- // IsAcceptedDatePattern().
- String aStr( sStrArray[nNext]);
- aStr.EraseTrailingChars(' ');
- aStr.Expand( rPat.getLength() - nPat, ' ');
- bOk = (rPat.indexOf( aStr, nPat) == nPat);
- }
- if (bOk)
- {
- rPos = nLen; // yes, set, not add!
- return true;
- }
- else
- return false;
- }
- nPat += sStrArray[nNext].Len() - 1;
- break;
- }
- }
- return false;
- }
- //---------------------------------------------------------------------------
- sal_uInt32 ImpSvNumberInputScan::GetDatePatternOrder()
- {
- // If not initialized yet start with first number, if any.
- if (!IsAcceptedDatePattern( (nAnzNums ? nNums[0] : 0)))
- return 0;
- sal_uInt32 nOrder = 0;
- const rtl::OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
- for (sal_Int32 nPat = 0; nPat < rPat.getLength() && !(nOrder & 0xff0000); ++nPat)
- {
- switch (rPat[nPat])
- {
- case 'Y':
- case 'M':
- case 'D':
- nOrder = (nOrder << 8) | rPat[nPat];
- break;
- }
- }
- return nOrder;
- }
- //---------------------------------------------------------------------------
- DateFormat ImpSvNumberInputScan::GetDateOrder()
- {
- sal_uInt32 nOrder = GetDatePatternOrder();
- if (!nOrder)
- return pFormatter->GetLocaleData()->getDateFormat();
- switch ((nOrder & 0xff0000) >> 16)
- {
- case 'Y':
- if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'D'))
- return YMD;
- break;
- case 'M':
- if ((((nOrder & 0xff00) >> 8) == 'D') && ((nOrder & 0xff) == 'Y'))
- return MDY;
- break;
- case 'D':
- if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'Y'))
- return DMY;
- break;
- default:
- case 0:
- switch ((nOrder & 0xff00) >> 8)
- {
- case 'Y':
- switch ((nOrder & 0xff))
- {
- case 'M':
- return YMD;
- }
- break;
- case 'M':
- switch ((nOrder & 0xff))
- {
- case 'Y':
- return DMY;
- case 'D':
- return MDY;
- }
- break;
- case 'D':
- switch ((nOrder & 0xff))
- {
- case 'Y':
- return MDY;
- case 'M':
- return DMY;
- }
- break;
- }
- }
- SAL_WARN( "nf.date", "ImpSvNumberInputScan::GetDateOrder: undefined, falling back to locale's default");
- return pFormatter->GetLocaleData()->getDateFormat();
- }
- //---------------------------------------------------------------------------
- // GetDateRef
- 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;
- }
- bool res = true;
- const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
- CalendarWrapper* pCal = pFormatter->GetCalendar();
- for ( int nTryOrder = 1; nTryOrder <= nFormatOrder; nTryOrder++ )
- {
- pCal->setGregorianDateTime( Date( Date::SYSTEM ) ); // today
- String aOrgCalendar; // empty => not changed yet
- DateFormat DateFmt;
- bool bFormatTurn;
- switch ( eEDF )
- {
- case NF_EVALDATEFORMAT_INTL :
- bFormatTurn = false;
- DateFmt = GetDateOrder();
- break;
- case NF_EVALDATEFORMAT_FORMAT :
- bFormatTurn = true;
- DateFmt = pFormat->GetDateOrder();
- break;
- case NF_EVALDATEFORMAT_INTL_FORMAT :
- if ( nTryOrder == 1 )
- {
- bFormatTurn = false;
- DateFmt = GetDateOrder();
- }
- else
- {
- bFormatTurn = true;
- DateFmt = pFormat->GetDateOrder();
- }
- break;
- case NF_EVALDATEFORMAT_FORMAT_INTL :
- if ( nTryOrder == 2 )
- {
- bFormatTurn = false;
- DateFmt = GetDateOrder();
- }
- else
- {
- bFormatTurn = true;
- DateFmt = pFormat->GetDateOrder();
- }
- break;
- default:
- OSL_FAIL( "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
- DateFmt = YMD;
- bFormatTurn = false;
- }
- if ( bFormatTurn )
- {
- /* 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.
- */
- #ifdef THE_FUTURE
- if ( pFormat->IsOtherCalendar( nStringScanNumFor ) )
- pFormat->SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
- else
- pFormat->SwitchToSpecifiedCalendar( aOrgCalendar, fOrgDateTime,
- nStringScanNumFor );
- #endif
- }
- res = 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 = 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:
- {
- sal_uInt16 nDay = ImplGetDay(0);
- sal_uInt16 nYear = ImplGetYear(0);
- if (nDay == 0 || nDay > 32) {
- pCal->setValue( CalendarFieldIndex::YEAR, nYear);
- }
- else
- pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
- break;
- }
- case DMY:
- pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
- break;
- default:
- res = 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 = false;
- break;
- }
- break;
- default:
- res = false;
- break;
- } // switch (nMonthPos)
- break;
- case 2: // 2 numbers
- …
Large files files are truncated, but you can click here to view the full file