/* It is not clear how we want timezones to be handled. Convert them to local *timeisn'twanted,asitisn'tdoneinanyotherplaceandtimezone *informationisn'tstoredanywhere.Ignoringthemandpretendinglocaltime *maybewrongtooandmightnotbewhattheuserexpects.Keeptheinputas *stringsothatnoinformationislost. *Anyway,definingNF_RECOGNIZE_ISO8601_TIMEZONESto1wouldbethewayhowit
* would work, together with the nTimezonePos handling in GetTimeRef(). */ #define NF_RECOGNIZE_ISO8601_TIMEZONES 0
const sal_Unicode cNoBreakSpace = 0xA0; const sal_Unicode cNarrowNoBreakSpace = 0x202F; constbool kDefaultEra = true; // Gregorian CE, positive year
if (StringPtrContainsImpl( rWhat, rString.getStr(), nPos))
{
nPos += rWhat.getLength(); if (nPos == rString.getLength()) returntrue; // word at end of string
/* TODO: we COULD invoke bells and whistles word break iterator to find *thenextboundary,butreally...thisiscalledfordateinput,so *howmanylanguagesdonotseparatethedayandmonthnamesinsome
* form? */
// Check simple ASCII first before invoking i18n or anything else. const sal_Unicode c = rString[nPos];
// Common separating ASCII characters in date context. switch (c)
{ case' ': case'-': case'.': case'/': returntrue; default:
; // nothing
}
if (rtl::isAsciiAlphanumeric( c )) returnfalse; // Alpha or numeric is not word gap.
sal_Int32 nIndex = nPos;
rString.iterateCodePoints( &nIndex); if (nPos+1 < nIndex) returntrue; // Surrogate, assume these to be new words.
/** *Recognizesexactly,111in{3}and{3,2}or,11in{3,2}grouping
*/ inlinebool ImpSvNumberInputScan::GetThousandSep( std::u16string_view rString,
sal_Int32& nPos,
sal_uInt16 nStringPos ) const
{ const OUString& rSep = mrCurrentLanguageData.GetNumThousandSep(); // Is it an ordinary space instead of a no-break space? bool bSpaceBreak = (rSep[0] == cNoBreakSpace || rSep[0] == cNarrowNoBreakSpace) &&
rString[0] == u' ' &&
rSep.getLength() == 1 && rString.size() == 1; if (!((rString == rSep || bSpaceBreak) && // nothing else
nStringPos < nStringsCnt - 1 && // safety first!
IsNum[ nStringPos + 1 ] )) // number follows
{ returnfalse; // no? => out
}
utl::DigitGroupingIterator aGrouping( mrCurrentLanguageData.GetLocaleData()->getDigitGrouping()); // Match ,### in {3} or ,## in {3,2} /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or *,##,###andtomatch,###in{3,2}onlyifit'sthelast.However, *currentlythereisnotrackkeptwheregroupseparatorsoccur.In{3,2} *#,###,###and#,##,##wouldbevalidinput,whichmaybeisn'tevenbad
* for #,###,###. Other combinations such as #,###,## maybe not. */
sal_Int32 nLen = sStrArray[ nStringPos + 1 ].getLength(); 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.getLength(); returntrue;
} returnfalse;
}
/** *Conversionoftexttologicalvalue *"true"=>1: *"false"=>-1: *else=>0:
*/ short ImpSvNumberInputScan::GetLogical( std::u16string_view rString ) const
{ short res;
const ImpSvNumberformatScan* pFS = mrCurrentLanguageData.GetFormatScanner(); if ( rString == pFS->GetTrueString() )
{
res = 1;
} elseif ( rString == pFS->GetFalseString() )
{
res = -1;
} else
{
res = 0;
} return res;
}
/** *Convertsastringcontainingamonthname(JAN,January)atnPosintothe *monthnumber(negativeifabbreviated),returns0ifnothingfound
*/ short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
{ short res = 0; // no month found
if (rString.getLength() > nPos) // only if needed
{ if ( !bTextInitialized )
{
InitText();
}
sal_Int16 nMonths = mrCurrentLanguageData.GetCalendar()->getNumberOfMonthsInYear(); for ( sal_Int16 i = 0; i < nMonths; i++ )
{ if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveMonthText[i], rString, nPos ) )
{ // genitive full names first
nPos = nPos + pUpperGenitiveMonthText[i].getLength();
res = i + 1; break; // for
} elseif ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveAbbrevMonthText[i], rString, nPos ) )
{ // genitive abbreviated
nPos = nPos + pUpperGenitiveAbbrevMonthText[i].getLength();
res = sal::static_int_cast< short >(-(i+1)); // negative break; // for
} elseif ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveMonthText[i], rString, nPos ) )
{ // partitive full names
nPos = nPos + pUpperPartitiveMonthText[i].getLength();
res = i+1; break; // for
} elseif ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveAbbrevMonthText[i], rString, nPos ) )
{ // partitive abbreviated
nPos = nPos + pUpperPartitiveAbbrevMonthText[i].getLength();
res = sal::static_int_cast< short >(-(i+1)); // negative break; // for
} elseif ( StringContainsWord( pUpperMonthText[i], rString, nPos ) )
{ // noun full names
nPos = nPos + pUpperMonthText[i].getLength();
res = i+1; break; // for
} elseif ( StringContainsWord( pUpperAbbrevMonthText[i], rString, nPos ) )
{ // noun abbreviated
nPos = nPos + pUpperAbbrevMonthText[i].getLength();
res = sal::static_int_cast< short >(-(i+1)); // negative break; // for
} elseif (i == 2 && mrCurrentLanguageData.GetLanguageTag().getLanguage() == "de")
{ if (pUpperAbbrevMonthText[i] == u"M\u00C4R" && StringContainsWord( u"MRZ"_ustr, rString, nPos))
{ // Accept MRZ for MÄR
nPos = nPos + 3;
res = sal::static_int_cast< short >(-(i+1)); // negative break; // for
} elseif (pUpperAbbrevMonthText[i] == "MRZ" && StringContainsWord( u"M\u00C4R"_ustr, rString, nPos))
{ // And vice versa, accept MÄR for MRZ
nPos = nPos + 3;
res = sal::static_int_cast< short >(-(i+1)); // negative break; // for
}
} elseif (i == 8)
{ // This assumes the weirdness is applicable to all locales. // It is the case for at least en-* and de-* locales. if (pUpperAbbrevMonthText[i] == "SEPT" && StringContainsWord( u"SEP"_ustr, rString, nPos))
{ // #102136# The correct English form of month September abbreviated is // SEPT, but almost every data contains SEP instead.
nPos = nPos + 3;
res = sal::static_int_cast< short >(-(i+1)); // negative break; // for
} elseif (pUpperAbbrevMonthText[i] == "SEP" && StringContainsWord( u"SEPT"_ustr, rString, nPos))
{ // And vice versa, accept SEPT for SEP
nPos = nPos + 4;
res = sal::static_int_cast< short >(-(i+1)); // negative break; // for
}
}
} if (!res)
{ // Brutal hack for German locales that know "Januar" or "Jänner". /* TODO: add alternative month names to locale data? if there are
* more languages... */ const LanguageTag& rLanguageTag = mrCurrentLanguageData.GetLanguageTag(); if (rLanguageTag.getLanguage() == "de")
{ if (rLanguageTag.getCountry() == "AT")
{ // Locale data has Jänner/Jän
assert(pUpperMonthText[0] == u"J\u00C4NNER"); if (StringContainsWord( u"JANUAR"_ustr, rString, nPos))
{
nPos += 6;
res = 1;
} elseif (StringContainsWord( u"JAN"_ustr, rString, nPos))
{
nPos += 3;
res = -1;
}
} else
{ // Locale data has Januar/Jan
assert(pUpperMonthText[0] == "JANUAR"); if (StringContainsWord( u"J\u00C4NNER"_ustr, rString, nPos))
{
nPos += 6;
res = 1;
} elseif (StringContainsWord( u"J\u00C4N"_ustr, rString, nPos))
{
nPos += 3;
res = -1;
}
}
}
}
}
return res;
}
/** *ConvertsastringcontainingaDayOfWeekname(Mon,Monday)atnPosintothe *DayOfWeeknumber+1(negativeifabbreviated),returns0ifnothingfound
*/ int ImpSvNumberInputScan::GetDayOfWeek( const OUString& rString, sal_Int32& nPos )
{ int res = 0; // no day found
if (rString.getLength() > nPos) // only if needed
{ if ( !bTextInitialized )
{
InitText();
}
sal_Int16 nDays = mrCurrentLanguageData.GetCalendar()->getNumberOfDaysInWeek(); for ( sal_Int16 i = 0; i < nDays; i++ )
{ if ( StringContainsWord( pUpperDayText[i], rString, nPos ) )
{ // full names first
nPos = nPos + pUpperDayText[i].getLength();
res = i + 1; break; // for
} if ( StringContainsWord( pUpperAbbrevDayText[i], rString, nPos ) )
{ // abbreviated
nPos = nPos + pUpperAbbrevDayText[i].getLength();
res = -(i + 1); // negative break; // for
}
}
}
return res;
}
/** *Readingacurrencysymbol *'$'=>true *else=>false
*/ bool ImpSvNumberInputScan::GetCurrency( const OUString& rString, sal_Int32& nPos )
{ if ( rString.getLength() > nPos )
{ if ( !aUpperCurrSymbol.getLength() )
{ // If no format specified the currency of the currently active locale.
LanguageType eLang = (mpFormat ? mpFormat->GetLanguage() :
mrCurrentLanguageData.GetLocaleData()->getLanguageTag().getLanguageType());
aUpperCurrSymbol = mrCurrentLanguageData.GetCharClass()->uppercase(
SvNumberFormatter::GetCurrencyEntry( eLang ).GetSymbol() );
} if ( StringContains( aUpperCurrSymbol, rString, nPos ) )
{
nPos = nPos + aUpperCurrSymbol.getLength(); returntrue;
} if ( mpFormat )
{
OUString aSymbol, aExtension; if ( mpFormat->GetNewCurrencySymbol( aSymbol, aExtension ) )
{ if ( aSymbol.getLength() <= rString.getLength() - nPos )
{
aSymbol = mrCurrentLanguageData.GetCharClass()->uppercase(aSymbol); if ( StringContains( aSymbol, rString, nPos ) )
{
nPos = nPos + aSymbol.getLength(); returntrue;
}
}
}
}
}
sal_Int32 nLen = sStrArray[nNums[nIndex]].getLength(); // 16-bit integer year width can have 5 digits, allow for one additional // leading zero as convention. if (nLen <= 6)
{
nYear = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32()); // A year in another, not Gregorian CE era is never expanded. // A year < 100 entered with at least 3 digits with leading 0 is taken // as is without expansion. if (mbEraCE == kDefaultEra && nYear < 100 && nLen < 3)
{
nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 );
}
}
return nYear;
}
bool ImpSvNumberInputScan::MayBeIso8601()
{ if (nMayBeIso8601 == 0)
{
nMayBeIso8601 = 1;
sal_Int32 nLen = ((nNumericsCnt >= 1 && nNums[0] < nStringsCnt) ? sStrArray[nNums[0]].getLength() : 0); if (nLen)
{
sal_Int32 n; if (nNumericsCnt >= 3 && nNums[2] < nStringsCnt &&
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( DateOrder eDateOrder )
{ int nCanForceToIso8601 = 0; if (!MayBeIso8601())
{ returnfalse;
} elseif (nMayBeIso8601 >= 3)
{ returntrue; // at least 3 digits in year
} else
{ if (eDateOrder == DateOrder::Invalid)
{ // As if any of the cases below can be applied, but only if a // locale dependent date pattern was not matched. if ((GetDatePatternNumbers() == nNumericsCnt) && IsDatePatternNumberOfType(0,'Y')) returnfalse;
eDateOrder = GetDateOrder();
}
// No date pattern matched at all can be forced to ISO 8601 here as is. if (GetDatePatternNumbers() == 0) returntrue;
nCanForceToIso8601 = 1;
}
sal_Int32 n; switch (eDateOrder)
{ case DateOrder::DMY: // "day" value out of range => ISO 8601 year
n = sStrArray[nNums[0]].toInt32(); if (n < 1 || n > 31)
{
nCanForceToIso8601 = 2;
} break; case DateOrder::MDY: // "month" value out of range => ISO 8601 year
n = sStrArray[nNums[0]].toInt32(); if (n < 1 || n > 12)
{
nCanForceToIso8601 = 2;
} break; case DateOrder::YMD: // always possible
nCanForceToIso8601 = 2; break; default: break;
} return nCanForceToIso8601 > 1;
}
bool ImpSvNumberInputScan::MayBeMonthDate()
{ if (nMayBeMonthDate == 0)
{
nMayBeMonthDate = 1; if (nNumericsCnt >= 2 && nNums[1] < nStringsCnt)
{ // "-Jan-" const OUString& rM = sStrArray[ nNums[ 0 ] + 1 ]; if (rM.getLength() >= 3 && rM[0] == '-' && rM[ rM.getLength() - 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]].getLength() >= 3); bool bYear2 = (sStrArray[nNums[1]].getLength() >= 3);
sal_Int32 n; bool bDay1 = !bYear1; if (bDay1)
{
n = sStrArray[nNums[0]].toInt32();
bDay1 = n >= 1 && n <= 31;
} bool bDay2 = !bYear2; if (bDay2)
{
n = sStrArray[nNums[1]].toInt32();
bDay2 = n >= 1 && n <= 31;
}
if (bDay1 && !bDay2)
{
nMayBeMonthDate = 2; // dd-month-yy
} elseif (!bDay1 && bDay2)
{
nMayBeMonthDate = 3; // yy-month-dd
} elseif (bDay1 && bDay2)
{ // Ambiguous ##-MMM-## date, but some big vendor's database // reports write this crap, assume this always to be
nMayBeMonthDate = 2; // dd-month-yy
}
}
}
} return nMayBeMonthDate > 1;
}
/** If a string is a separator plus '-' minus sign preceding a 'Y' year in adatepatternatpositionnPat.
*/ staticbool lcl_IsSignedYearSep( std::u16string_view rStr, std::u16string_view rPat, sal_Int32 nPat )
{ bool bOk = false;
sal_Int32 nLen = rStr.size(); if (nLen > 1 && rStr[nLen-1] == '-')
{
--nLen; if (nPat + nLen < static_cast<sal_Int32>(rPat.size()) && rPat[nPat+nLen] == 'Y')
{ // Signed year is possible.
bOk = (rPat.find( rStr.substr( 0, nLen), nPat) == static_cast<size_t>(nPat));
}
} return bOk;
}
/** Length of separator usually is 1 but theoretically could be anything. */ static sal_Int32 lcl_getPatternSeparatorLength( std::u16string_view rPat, sal_Int32 nPat )
{
sal_Int32 nSep = nPat;
sal_Unicode c; while (nSep < static_cast<sal_Int32>(rPat.size()) && (c = rPat[nSep]) != 'D' && c != 'M' && c != 'Y')
++nSep; return nSep - nPat;
}
bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
{ if (nAcceptedDatePattern >= -1)
{ return (nAcceptedDatePattern >= 0);
} if (!nNumericsCnt)
{
nAcceptedDatePattern = -1;
} elseif (!sDateAcceptancePatterns.hasElements())
{ // The current locale is the format's locale, if a format is present. const NfEvalDateFormat eEDF = mrCurrentLanguageData.GetEvalDateFormat(); if (!mpFormat || eEDF == NF_EVALDATEFORMAT_FORMAT || mpFormat->GetLanguage() == mrCurrentLanguageData.GetIniLanguage())
{
sDateAcceptancePatterns = mrCurrentLanguageData.GetLocaleData()->getDateAcceptancePatterns();
} else
{
OnDemandLocaleDataWrapper& xLocaleData = mrCurrentLanguageData.GetOnDemandLocaleDataWrapper(
SvNFLanguageData::InputScannerPrivateAccess()); const LanguageTag aSaveLocale( xLocaleData->getLanguageTag() );
assert(mpFormat->GetLanguage() == aSaveLocale.getLanguageType()); // prerequisite // Obtain formatter's locale's (e.g. system) patterns.
xLocaleData.changeLocale( LanguageTag( mrCurrentLanguageData.GetIniLanguage())); const css::uno::Sequence<OUString> aLocalePatterns( xLocaleData->getDateAcceptancePatterns()); // Reset to format's locale.
xLocaleData.changeLocale( aSaveLocale); // When concatenating don't care about duplicates, combining // weeding those out reallocs yet another time and probably doesn't // take less time than looping over two additional patterns below... switch (eEDF)
{ case NF_EVALDATEFORMAT_FORMAT:
assert(!"shouldn't reach here"); break; case NF_EVALDATEFORMAT_INTL:
sDateAcceptancePatterns = aLocalePatterns; break; case NF_EVALDATEFORMAT_INTL_FORMAT:
sDateAcceptancePatterns = comphelper::concatSequences(
aLocalePatterns,
xLocaleData->getDateAcceptancePatterns()); break; case NF_EVALDATEFORMAT_FORMAT_INTL:
sDateAcceptancePatterns = comphelper::concatSequences(
xLocaleData->getDateAcceptancePatterns(),
aLocalePatterns); break;
}
}
SAL_WARN_IF( !sDateAcceptancePatterns.hasElements(), "svl.numbers", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
nAcceptedDatePattern = (sDateAcceptancePatterns.hasElements() ? -2 : -1);
}
for (sal_Int32 nPattern=0; nPattern < sDateAcceptancePatterns.getLength(); ++nPattern)
{ const OUString& rPat = sDateAcceptancePatterns[nPattern]; if (rPat.getLength() == 3)
{ // Ignore a pattern that would match numeric input with decimal // separator. It may had been read from configuration or resulted // from the locales' patterns concatenation above. if ( rPat[1] == mrCurrentLanguageData.GetLocaleData()->getNumDecimalSep().toChar()
|| rPat[1] == mrCurrentLanguageData.GetLocaleData()->getNumDecimalSepAlt().toChar())
{
SAL_WARN("svl.numbers", "ignoring date acceptance pattern with decimal separator ambiguity: " << rPat); continue; // for, next pattern
}
}
sal_uInt16 nNext = nDatePatternStart;
nDatePatternNumbers = 0; bool bOk = true;
sal_Int32 nPat = 0; for ( ; nPat < rPat.getLength() && bOk && nNext < nStringsCnt; ++nPat, ++nNext)
{ const sal_Unicode c = rPat[nPat]; switch (c)
{ case'Y': case'M': case'D':
bOk = IsNum[nNext]; if (bOk && (c == 'M' || c == 'D'))
{ // Check the D and M cases for plausibility. This also // prevents recognition of date instead of number with a // numeric group input if date separator is identical to // group separator, for example with D.M as a pattern and // #.### as a group.
sal_Int32 nMaxLen, nMaxVal; switch (c)
{ case'M':
nMaxLen = 2;
nMaxVal = nMonthsInYear; break; case'D':
nMaxLen = 2;
nMaxVal = 31; break; default: // This merely exists against // -Werror=maybe-uninitialized, which is nonsense // after the (c == 'M' || c == 'D') check above, // but ...
nMaxLen = 2;
nMaxVal = 31;
}
bOk = (sStrArray[nNext].getLength() <= nMaxLen); if (bOk)
{
sal_Int32 nNum = sStrArray[nNext].toInt32();
bOk = (1 <= nNum && nNum <= nMaxVal);
}
} if (bOk)
++nDatePatternNumbers; break; default:
bOk = !IsNum[nNext]; if (bOk)
{ const sal_Int32 nSepLen = lcl_getPatternSeparatorLength( rPat, nPat); // Non-numeric input must match separator exactly to be // accepted as such. const sal_Int32 nLen = sStrArray[nNext].getLength();
bOk = (nLen == nSepLen && rPat.indexOf( sStrArray[nNext], nPat) == nPat); if (bOk)
{
nPat += nLen - 1;
} elseif ((bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat)))
{
nPat += nLen - 2;
} elseif (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' ')
{ usingnamespace comphelper::string; // Trailing blanks in input.
OUStringBuffer aBuf(sStrArray[nNext]);
aBuf.stripEnd(); // Expand again in case of pattern "M. D. " and // input "M. D. ", maybe fetched far, but...
padToLength(aBuf, rPat.getLength() - nPat, ' ');
bOk = (rPat.indexOf( aBuf, nPat) == nPat); if (bOk)
{
nPat += aBuf.getLength() - 1;
}
}
} break;
}
} if (bOk)
{ // Check for trailing characters mismatch. if (nNext < nStringsCnt)
{ // Pattern end but not input end. // A trailing blank may be part of the current pattern input, // if pattern is "D.M." and input is "D.M. hh:mm" last was // ". ", or may be following the current pattern input, if // pattern is "D.M" and input is "D.M hh:mm" last was "M".
sal_Int32 nPos = 0;
sal_uInt16 nCheck; if (nPat > 0 && nNext > 0)
{ // nPat is one behind after the for loop.
sal_Int32 nPatCheck = nPat - 1; switch (rPat[nPatCheck])
{ case'Y': case'M': case'D':
nCheck = nNext; break; default:
{
nCheck = nNext - 1; // Advance position in input to match length of // non-YMD (separator) characters in pattern.
sal_Unicode c; do
{
++nPos;
c = rPat[--nPatCheck];
} while (c != 'Y' && c != 'M' && c != 'D' && nPatCheck > 0);
}
}
} else
{
nCheck = nNext;
} if (!IsNum[nCheck])
{ // Trailing (or separating if time follows) blanks are ok. // No blank and a following number is not. constbool bBlanks = SkipBlanks( sStrArray[nCheck], nPos); if (nPos == sStrArray[nCheck].getLength() && (bBlanks || !IsNum[nNext]))
{
nAcceptedDatePattern = nPattern; returntrue;
}
}
} elseif (nPat == rPat.getLength())
{ // Input end and pattern end => match.
nAcceptedDatePattern = nPattern; returntrue;
} // else Input end but not pattern end, no match.
}
}
nAcceptedDatePattern = -1; returnfalse;
}
bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, sal_Int32 & rPos, bool & rSignedYear )
{ // If not initialized yet start with first number, if any. if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
{ returnfalse;
} if (nParticle < nDatePatternStart || nParticle >= nStringsCnt || IsNum[nParticle])
{ returnfalse;
}
sal_uInt16 nNext = nDatePatternStart; const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern]; for (sal_Int32 nPat = 0; nPat < rPat.getLength() && nNext < nStringsCnt; ++nPat, ++nNext)
{ switch (rPat[nPat])
{ case'Y': case'M': case'D': break; default: if (nNext == nParticle)
{ const sal_Int32 nSepLen = lcl_getPatternSeparatorLength( rPat, nPat); const sal_Int32 nLen = sStrArray[nNext].getLength(); bool bOk = (nLen == nSepLen && rPat.indexOf( sStrArray[nNext], nPat) == nPat); if (!bOk)
{
bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat); if (bOk)
rSignedYear = true;
} if (!bOk && (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' '))
{ // The same ugly trailing blanks check as in // IsAcceptedDatePattern(). usingnamespace comphelper::string;
OUStringBuffer aBuf(sStrArray[nNext]);
aBuf.stripEnd();
padToLength(aBuf, rPat.getLength() - nPat, ' ');
bOk = (rPat.indexOf(aBuf, nPat) == nPat);
} if (bOk)
{
rPos = nLen; // yes, set, not add! returntrue;
} else returnfalse;
}
nPat += sStrArray[nNext].getLength() - 1; break;
}
} returnfalse;
}
sal_uInt16 ImpSvNumberInputScan::GetDatePatternNumbers()
{ // If not initialized yet start with first number, if any. if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
{ return0;
} return nDatePatternNumbers;
}
LongDateOrder eLDO; const sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->GetExactDateOrder() : 0); if (!nExactDateOrder)
eLDO = pLoc->getLongDateOrder(); elseif ((((nExactDateOrder >> 16) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D'))
eLDO = LongDateOrder::YMD; elseif ((((nExactDateOrder >> 16) & 0xff) == 'D') && ((nExactDateOrder & 0xff) == 'Y'))
eLDO = LongDateOrder::DMY; else
eLDO = pLoc->getLongDateOrder(); if (eLDO != LongDateOrder::YMD && eLDO != LongDateOrder::DMY)
{ switch (eDateOrder)
{ case DateOrder::YMD:
eLDO = LongDateOrder::YMD; break; case DateOrder::DMY:
eLDO = LongDateOrder::DMY; break; default:
; // nothing, not a date
}
} elseif (eLDO == LongDateOrder::DMY && eDateOrder == DateOrder::YMD)
{ // Check possible order and maybe switch. if (!ImplGetDay(0) && ImplGetDay(1))
eLDO = LongDateOrder::YMD;
} elseif (eLDO == LongDateOrder::YMD && eDateOrder == DateOrder::DMY)
{ // Check possible order and maybe switch. if (!ImplGetDay(1) && ImplGetDay(0))
eLDO = LongDateOrder::DMY;
} return eLDO;
}
bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter )
{ usingnamespace ::com::sun::star::i18n;
NfEvalDateFormat eEDF; int nFormatOrder; if ( mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE) )
{
eEDF = mrCurrentLanguageData.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 = mrCurrentLanguageData.GetLocaleData();
CalendarWrapper* pCal = mrCurrentLanguageData.GetCalendar(); const DateTime aToday{ DateTime( Date( Date::SYSTEM))}; for ( int nTryOrder = 1; nTryOrder <= nFormatOrder; nTryOrder++ )
{
pCal->setGregorianDateTime( aToday); // today
OUString aOrgCalendar; // empty => not changed yet
DateOrder DateFmt; bool bFormatTurn; switch ( eEDF )
{ case NF_EVALDATEFORMAT_INTL :
bFormatTurn = false;
DateFmt = GetDateOrder(); break; case NF_EVALDATEFORMAT_FORMAT :
bFormatTurn = true;
DateFmt = mpFormat->GetDateOrder(); break; case NF_EVALDATEFORMAT_INTL_FORMAT : if ( nTryOrder == 1 )
{
bFormatTurn = false;
DateFmt = GetDateOrder();
} else
{
bFormatTurn = true;
DateFmt = mpFormat->GetDateOrder();
} break; case NF_EVALDATEFORMAT_FORMAT_INTL : if ( nTryOrder == 2 )
{
bFormatTurn = false;
DateFmt = GetDateOrder();
} else
{
bFormatTurn = true; // Even if the format pattern is to be preferred, the input may // have matched a pattern of the current locale, which then // again is to be preferred. Both date orders can be different // so we need to obtain the actual match. For example ISO // YYYY-MM-DD format vs locale's DD.MM.YY input. // If no pattern was matched, obtain from format. // Note that patterns may have been constructed from the // format's locale and prepended to the current locale's // patterns, it doesn't necessarily mean a current locale's // pattern was matched, but may if the format's locale's // patterns didn't match, which were tried first.
DateFmt = GetDateOrder(true);
} break; default:
SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
DateFmt = DateOrder::YMD;
bFormatTurn = false;
} if ( bFormatTurn )
{ /* TODO: Wearecurrentlynotabletofullysupportaswitchtoanothercalendarduring inputforthefollowingreasons: 1.Wedohaveaproblemifboth(locale'sdefaultandformat's)calendars definethesameYMDorderandusethesamedateseparator,thereisnoway todistinguishbetweenthemiftheinputresultsinvalidcalendarinputfor bothcalendars.Howtosolve?WouldNfEvalDateFormatbesufficient?Should italwaysbesettoNF_EVALDATEFORMAT_FORMAT_INTLandthustheformat's calendarbepreferred?ThiscouldbeconfusingifaCalccellwasformatted differenttothelocale'sdefaultandhasnocontentyet,thentheuserhas noclueabouttheformatorcalendarbeingset. 2.InCalccelleditmodeadateisalwaysdisplayedandeditedusingthe defaulteditformatofthedefaultcalendar(normallybeingGregorian).If inputwasambiguousduetoissue#1we'dneedamechanismtotellthata datewaseditedandnotnewlyentered.Notfeasible.Otherwisewe'dneeda mechanismtouseaspecificeditformatwithaspecificcalendaraccording totheformatset. 3.ForsomecalendarslikeJapaneseGengouwe'dneederainput,whichisn't implementedatall.Thoughthisisarareandspecialcase,forcinga calendardependenteditformatassuggestedinitem#2mightrequireera input,ifitshouldn'tresultinafallbacktoGregoriancalendar. 4.Lastandleast:theGetMonth()methodcurrentlyonlymatchesmonthnamesof thedefaultcalendar.Alternatingmonthnamesoftheactualformat's calendarwouldhavetobeimplemented.Noproblem.
res = true;
nCounter = 0; // For incomplete dates, always assume first day of month if not specified.
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
switch (nNumericsCnt) // count of numbers in string
{ case0: // none if (nMonthPos) // only month (Jan)
{
pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
} else
{
res = false;
} break;
case1: // only one number
nCounter = 1; switch (nMonthPos) // where is the month
{ case0: // not found
{ // If input matched a date pattern, use the pattern // to determine if it is a day, month or year. The // pattern should have only one single value then, // 'D-', 'M-' or 'Y-'. If input did not match a // pattern assume the usual day of current month.
sal_uInt32 nDateOrder = (bFormatTurn ?
mpFormat->GetExactDateOrder() :
GetDatePatternOrder()); switch (nDateOrder)
{ case'Y':
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; case'M':
pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) ); break; case'D': default:
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); break;
} break;
} case1: // month at the beginning (Jan 01)
pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 ); switch (DateFmt)
{ case DateOrder::MDY: case DateOrder::YMD: if (sal_uInt16 day = ImplGetDay(0); day > 0 && day <= 32) // Why 32?
{
pCal->setValue(CalendarFieldIndex::DAY_OF_MONTH, day); break;
} // Parse 'june-2007' as June 1 2007
[[fallthrough]]; case DateOrder::DMY:
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; default:
res = false; break;
} break; case3: // month at the end (10 Jan)
pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 ); switch (DateFmt)
{ case DateOrder::DMY:
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) ); break; case DateOrder::YMD:
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; default:
res = false; break;
} break; default:
res = false; break;
} // switch (nMonthPos) break;
case2: // 2 numbers
nCounter = 2; switch (nMonthPos) // where is the month
{ case0: // not found
{
sal_uInt32 nExactDateOrder = (bFormatTurn ?
mpFormat->GetExactDateOrder() :
GetDatePatternOrder()); bool bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff); if (!bIsExact && bFormatTurn && IsAcceptedDatePattern( nNums[0]))
{ // If input does not match format but pattern, use pattern // instead, even if eEDF==NF_EVALDATEFORMAT_FORMAT_INTL. // For example, format has "Y-M-D" and pattern is "D.M.", // input with 2 numbers can't match format and 31.12. would // lead to 1931-12-01 (fdo#54344)
nExactDateOrder = GetDatePatternOrder();
bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
} bool bHadExact; if (bIsExact)
{ // 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;
}
SAL_WARN_IF( !bHadExact, "svl.numbers", "ImpSvNumberInputScan::GetDateRef: error in exact date order");
} else
{
bHadExact = false;
} // If input matched against a date acceptance pattern // do not attempt to mess around with guessing the // order, either it matches or it doesn't. if ((bFormatTurn || !bIsExact) && (!bHadExact || !pCal->isValid()))
{ if ( !bHadExact && nExactDateOrder )
{
pCal->setGregorianDateTime( aToday); // reset today
} switch (DateFmt)
{ case DateOrder::MDY:
{ // M D constauto month = ImplGetMonth(0);
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
pCal->setValue( CalendarFieldIndex::MONTH, month ); if ( !pCal->isValid() ) // 2nd try
{ // M Y
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
pCal->setValue( CalendarFieldIndex::MONTH, month );
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
} break;
} case DateOrder::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 DateOrder::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 = false; break;
}
}
} break; case1: // 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, std::abs(nMonth)-1 );
sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->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;
} case2: // month in the middle (10 Jan 94)
{
pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 ); const LongDateOrder eLDO = GetMiddleMonthLongDateOrder( bFormatTurn, pLoc, DateFmt); switch (eLDO)
{ case LongDateOrder::DMY:
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); break; case LongDateOrder::YMD:
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; default:
res = false; break;
} break;
} case3: // month at the end (94 10 Jan) if (pLoc->getLongDateOrder() != LongDateOrder::YDM)
res = false; else
{
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
} break; default:
res = 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
{ case0: // 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 < nNumericsCnt; j++ )
{ if ( nNums[j] == nTimePos - 2 )
{
nCounter = j; break; // for
}
}
} // ISO 8601 yyyy-mm-dd forced recognition
DateOrder eDF = (CanForceToIso8601( DateFmt) ? DateOrder::YMD : DateFmt); switch (eDF)
{ case DateOrder::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 DateOrder::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 DateOrder::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 = false; break;
} break;
} case1: // month at the beginning (Jan 01 01 8:23)
{
nCounter = 2; // 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, std::abs(nMonth)-1 );
sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->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;
} case2: // month in the middle (10 Jan 94 8:23)
{
nCounter = 2;
pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 ); const LongDateOrder eLDO = GetMiddleMonthLongDateOrder( bFormatTurn, pLoc, DateFmt); switch (eLDO)
{ case LongDateOrder::DMY:
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) ); break; case LongDateOrder::YMD:
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) ); break; default:
res = false; break;
} break;
} case3: // month at the end (94 10 Jan 8:23)
nCounter = 2; if (pLoc->getLongDateOrder() != LongDateOrder::YDM)
res = false; else
{
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
} break; default:
nCounter = 2;
res = false; break;
} // switch (nMonthPos) break;
} // switch (nNumericsCnt)
// 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.getLength() == 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, 0, true ) )
{
nMatchedAllStrings |= nMatchedStartString;
} else
{
nMatchedAllStrings = 0;
}
}
// Bail out early for just a sign. if (nSign && nPos == rString.getLength()) returntrue;
const sal_Int32 nStartBlanks = nPos; if ( GetDecSep(rString, nPos) ) // decimal separator in start string
{ if (SkipBlanks(rString, nPos))
nPos = nStartBlanks; // `. 2` not a decimal separator else
nDecPos = 1; // leading decimal separator
} elseif ( GetCurrency(rString, nPos) ) // currency (DM 1)?
{
eScannedType = SvNumFormatType::CURRENCY; // !!! it IS currency !!!
SkipBlanks(rString, nPos); if (nSign == 0) // no sign yet
{
nSign = GetSign(rString, nPos); if ( nSign ) // DM -1
{
SkipBlanks(rString, nPos);
}
} if ( GetDecSep(rString, nPos) ) // decimal separator follows currency
{ if (SkipBlanks(rString, nPos))
{
nPos = nStartBlanks; // `DM . 2` not a decimal separator
eScannedType = SvNumFormatType::UNDEFINED; // !!! it is NOT currency !!!
} else
nDecPos = 1; // leading decimal separator
}
} else
{ const sal_Int32 nMonthStart = nPos; short nTempMonth = GetMonth(rString, nPos); if (nTempMonth < 0)
{ // Short month and day names may be identical in some locales, e.g. // "mar" for "martes" or "marzo" in Spanish. // Do not let a month name immediately take precedence if a day // name was meant instead. Assume that both could be valid, until // encountered differently or the final evaluation in // IsNumberFormat() checks, but continue with weighing the month // name higher unless we have both day of week and month name here.
sal_Int32 nTempPos = nMonthStart;
nDayOfWeek = GetDayOfWeek( rString, nTempPos); if (nDayOfWeek < 0)
{
SkipChar( '.', rString, nTempPos ); // abbreviated
SkipString( mrCurrentLanguageData.GetLocaleData()->getLongDateDayOfWeekSep(), rString, nTempPos );
SkipBlanks( rString, nTempPos); short nTempTempMonth = GetMonth( rString, nTempPos); if (nTempTempMonth)
{ // Fall into the else branch below that handles both.
nTempMonth = 0;
nPos = nMonthStart;
nDayOfWeek = 0; // Do not set nDayOfWeek hereafter, anywhere.
}
}
} if ( nTempMonth ) // month (Jan 1)?
{ // Jan1 without separator is not a date, unless it is followed by a // separator and a (year) number. if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
{
eScannedType = SvNumFormatType::DATE; // !!! it IS a date !!!
nMonth = nTempMonth;
nMonthPos = 1; // month at the beginning if ( nMonth < 0 )
{
SkipChar( '.', rString, nPos ); // abbreviated
}
SkipBlanks(rString, nPos);
} else
{
nPos = nMonthStart; // rewind month
}
} else
{ int nTempDayOfWeek = GetDayOfWeek( rString, nPos ); if ( nTempDayOfWeek )
{ // day of week is just parsed away
eScannedType = SvNumFormatType::DATE; // !!! it IS a date !!! if ( nPos < rString.getLength() )
{ if ( nTempDayOfWeek < 0 )
{ // abbreviated if ( rString[ nPos ] == '.' )
{
++nPos;
}
} else
{ // full long name
SkipBlanks(rString, nPos);
SkipString( mrCurrentLanguageData.GetLocaleData()->getLongDateDayOfWeekSep(), rString, nPos );
}
SkipBlanks(rString, nPos);
nTempMonth = GetMonth(rString, nPos); if ( nTempMonth ) // month (Jan 1)?
{ // Jan1 without separator is not a date, unless it is followed by a // separator and a (year) number. if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
{
nMonth = nTempMonth;
nMonthPos = 1; // month at the beginning if ( nMonth < 0 )
{
SkipChar( '.', rString, nPos ); // abbreviated
}
SkipBlanks(rString, nPos);
} else
{
nPos = nMonthStart; // rewind month
}
}
} if (!nMonth)
{ // Determine and remember following date pattern, if any.
IsAcceptedDatePattern( 1);
}
}
} // Skip one trailing '-' or '/' character to recognize June-2007 if (nMonth && nPos + 1 == rString.getLength())
{
SkipChar('-', rString, nPos) || SkipChar('/', rString, nPos);
}
}
if (nPos < rString.getLength()) // not everything consumed
{ // Does input StartString equal StartString of format? // This time with sign detection! if ( !ScanStringNumFor( rString, nPos, 0 ) )
{ return MatchedReturn();
}
}
returntrue;
}
staticbool lcl_isBlanks( const OUString& rStr )
{ if (rStr.isEmpty()) returnfalse;
for (sal_Int32 i = rStr.getLength(); i-- > 0; )
{ if (rStr[i] != ' ') returnfalse;
} returntrue;
}
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, nStringPos ) )
{
nMatchedAllStrings |= nMatchedMidString;
} else
{
nMatchedAllStrings = 0;
}
}
const sal_Int32 nStartBlanks = nPos; constbool bBlanks = SkipBlanks(rString, nPos); if (GetDecSep(rString, nPos)) // decimal separator?
{ if (nDecPos == 1 || nDecPos == 3) // .12.4 or 1.E2.1
{ return MatchedReturn();
} elseif (nDecPos == 2) // . dup: 12.4.
{ bool bSignedYear = false; if (bDecSepInDateSeps || // . also date separator
SkipDatePatternSeparator( nStringPos, nPos, bSignedYear))
{ if ( eScannedType != SvNumFormatType::UNDEFINED &&
eScannedType != SvNumFormatType::DATE &&
eScannedType != SvNumFormatType::DATETIME) // already another type
{ return MatchedReturn();
} if (eScannedType == SvNumFormatType::UNDEFINED)
{
eScannedType = SvNumFormatType::DATE; // !!! it IS a date
}
SkipBlanks(rString, nPos);
} else
{ return MatchedReturn();
}
} elseif (bBlanks)
{ // `1 .2` or `1 . 2` not a decimal separator, reset
nPos = nStartBlanks;
} elseif (SkipBlanks(rString, nPos))
{ // `1. 2` not a decimal separator, reset
nPos = nStartBlanks;
} else
{
nDecPos = 2; // . in mid string
}
} elseif ( (eScannedType & SvNumFormatType::TIME) &&
GetTime100SecSep( rString, nPos ) )
{ // hundredth seconds separator if ( nDecPos )
{ return MatchedReturn();
}
nDecPos = 2; // . in mid string
// If this is exactly an ISO 8601 fractional seconds separator, bail // out early to not get confused by later checks for group separator or // other. if (bIso8601Tsep && nPos == rString.getLength() &&
eScannedType == SvNumFormatType::DATETIME && (rString == "." || rString == ",")) returntrue;
SkipBlanks(rString, nPos);
}
if (SkipChar('/', rString, nPos)) // fraction?
{ if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
eScannedType != SvNumFormatType::DATE) // except date
{ return MatchedReturn(); // => jan/31/1994
} elseif (eScannedType != SvNumFormatType::DATE && // analyzed no date until now
(eSetType == SvNumFormatType::FRACTION || // and preset was fraction
(nNumericsCnt == 3 && // or 3 numbers
(nStringPos == 3 || // and 4th string particle
(nStringPos == 4 && nSign)) && // or 5th if signed
lcl_isBlanks(sStrArray[nStringPos-2])))) // and not 23/11/1999 // that was not accepted as date yet, // nor anything else than blanks after integer.
{
SkipBlanks(rString, nPos); if (nPos == rString.getLength())
{
eScannedType = SvNumFormatType::FRACTION; // !!! it IS a fraction (so far) if (eSetType == SvNumFormatType::FRACTION &&
nNumericsCnt == 2 &&
(nStringPos == 1 || // for 4/5
(nStringPos == 2 && nSign))) // or signed -4/5
{ returntrue; // don't fall into date trap
}
}
} else
{
nPos--; // put '/' back
}
}
if (GetThousandSep(rString, nPos, nStringPos)) // 1,000
{ if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
eScannedType != SvNumFormatType::CURRENCY) // except currency
{ return MatchedReturn();
}
nThousand++;
}
const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData(); bool bSignedYear = false; bool bDate = SkipDatePatternSeparator( nStringPos, nPos, bSignedYear); // 12/31 31.12. 12/31/1999 31.12.1999 if (!bDate)
{ const OUString& rDate = mrCurrentLanguageData.GetDateSep();
SkipBlanks(rString, nPos);
bDate = SkipString( rDate, rString, nPos); // 10. 10- 10/
} if (!bDate && nStringPos == 1 && mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE))
{ // If a DMY format was given and a mid string starts with a literal // ". " dot+space and could contain a following month name and ends // with a space or LongDateMonthSeparator, like it's scanned in // `14". AUG "18`, then it may be a date as well. Regardless whether // defined such by the locale or not. // This *could* check for presence of ". "MMM or ". "MMMM in the actual // format code for further restriction to match only if present, but..
const sal_uInt32 nExactDateOrder = mpFormat->GetExactDateOrder(); // Exactly DMY. if (((nExactDateOrder & 0xff) == 'Y') && (((nExactDateOrder >> 8) & 0xff) == 'M')
&& (((nExactDateOrder >> 16) & 0xff) == 'D'))
{ const sal_Int32 nTmpPos = nPos; if (SkipChar('.', rString, nPos) && SkipBlanks(rString, nPos) && nPos + 2 < rString.getLength()
&& (rString.endsWith(" ") || rString.endsWith( pLoc->getLongDateMonthSep())))
bDate = true; else
nPos = nTmpPos;
}
} if (bDate || ((MayBeIso8601() || MayBeMonthDate()) && // 1999-12-31 31-Dec-1999
SkipChar( '-', rString, nPos)))
{ if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
eScannedType != SvNumFormatType::DATE) // except date
{ return MatchedReturn();
}
SkipBlanks(rString, nPos);
eScannedType = SvNumFormatType::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. elseif ( 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);
} if (bSignedYear)
{ if (mbEraCE != kDefaultEra) // signed year twice? return MatchedReturn();
mbEraCE = false; // BCE
}
}
const sal_Int32 nMonthStart = nPos; short nTempMonth = GetMonth(rString, nPos); // month in the middle (10 Jan 94) or at the end (94 10 Jan) if (nTempMonth)
{ if (nMonth != 0) // month dup
{ return MatchedReturn();
} if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
eScannedType != SvNumFormatType::DATE) // except date
{ return MatchedReturn();
} if (nMonthStart > 0 && nPos < rString.getLength()) // 10Jan or Jan94 without separator are not dates
{
eScannedType = SvNumFormatType::DATE; // !!! it IS a date
nMonth = nTempMonth; if (nCurNumCount <= 1)
nMonthPos = 2; // month in the middle else
nMonthPos = 3; // month at the end if ( nMonth < 0 )
{
SkipChar( '.', rString, nPos ); // abbreviated
}
SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
SkipBlanks(rString, nPos);
} else
{
nPos = nMonthStart; // rewind month
}
}
if ( SkipChar('E', rString, nPos) || // 10E, 10e, 10,Ee
SkipChar('e', rString, nPos) )
{ if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
{ return MatchedReturn();
} else
{
SkipBlanks(rString, nPos);
eScannedType = SvNumFormatType::SCIENTIFIC; // !!! it IS scientific if ( nThousand+2 == nNumericsCnt && nDecPos == 2 ) // special case 1.E2
{
nDecPos = 3; // 1,100.E2 1,100,100.E3
}
}
nESign = GetESign(rString, nPos); // signed exponent?
SkipBlanks(rString, nPos);
}
const OUString& rTime = pLoc->getTimeSep(); if ( SkipString(rTime, rString, nPos) ) // time separator?
{ if (nDecPos) // already . => maybe error
{ if (bDecSepInDateSeps) // . also date sep
{ if ( eScannedType != SvNumFormatType::DATE && // already another type than date
eScannedType != SvNumFormatType::DATETIME) // or date time
{ return MatchedReturn();
} if (eScannedType == SvNumFormatType::DATE)
{
nDecPos = 0; // reset for time transition
}
} else
{ return MatchedReturn();
}
} if ((eScannedType == SvNumFormatType::DATE || // already date type
eScannedType == SvNumFormatType::DATETIME) && // or date time
nNumericsCnt > 3) // and more than 3 numbers? (31.Dez.94 8:23)
{
SkipBlanks(rString, nPos);
eScannedType = SvNumFormatType::DATETIME; // !!! it IS date with time
} elseif ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
eScannedType != SvNumFormatType::TIME) // except time
{ return MatchedReturn();
} else
{
SkipBlanks(rString, nPos);
eScannedType = SvNumFormatType::TIME; // !!! it IS a time
} if ( !nTimePos )
{
nTimePos = nStringPos + 1;
}
}
if (nPos < rString.getLength())
{ switch (eScannedType)
{ case SvNumFormatType::DATE: if (nMonthPos == 1 && pLoc->getLongDateOrder() == LongDateOrder::MDY)
{ // #68232# recognize long date separators like ", " in "September 5, 1999" if (SkipString( pLoc->getLongDateDaySep(), rString, nPos ))
{
SkipBlanks( rString, nPos );
}
} elseif (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
{ if ( (nStringPos == 5 && rString[0] == 'T') ||
(nStringPos == 6 && rString[0] == 'T' && sStrArray[0] == "-"))
{ // ISO 8601 combined date and time, yyyy-mm-ddThh:mm or -yyyy-mm-ddThh:mm
++nPos;
bIso8601Tsep = true;
} elseif (nStringPos == 7 && rString[0] == ':')
{ // ISO 8601 combined date and time, the time part; we reach // here if the locale's separator is not ':' so it couldn't // be detected above in the time block. if (nNumericsCnt >= 5)
eScannedType = SvNumFormatType::DATETIME;
++nPos;
}
} break; case SvNumFormatType::DATETIME: if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
{ if (nStringPos == 9 && rString[0] == ':')
{ // ISO 8601 combined date and time, the time part continued.
++nPos;
}
} #if NF_RECOGNIZE_ISO8601_TIMEZONES elseif (nPos == 0 && rString.getLength() == 1 && nStringPos >= 9 && MayBeIso8601())
{ // ISO 8601 timezone offset switch (rString[ 0 ])
{ case'+': case'-': if (nStringPos == nStringsCnt - 2 ||
nStringPos == nStringsCnt - 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 == nStringsCnt - 2)
{
++nPos; // yyyy-mm-ddThh:mm[:ss]+xx:yy
} break;
}
} #endif break; default: break;
}
}
if (nPos < rString.getLength()) // not everything consumed?
{ if ( nMatchedAllStrings & ~nMatchedVirgin )
{
eScannedType = eOldScannedType;
} else
{ returnfalse;
}
}
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, 0xFFFF ) )
{
nMatchedAllStrings |= nMatchedEndString;
} else
{
nMatchedAllStrings = 0;
}
}
const sal_Int32 nStartBlanks = nPos; constbool bBlanks = SkipBlanks(rString, nPos); if (GetDecSep(rString, nPos)) // decimal separator?
{ if (nDecPos == 1 || nDecPos == 3) // .12.4 or 12.E4.
{ return MatchedReturn();
} elseif (nDecPos == 2) // . dup: 12.4.
{ bool bSignedYear = false; if (bDecSepInDateSeps || // . also date separator
SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear))
{ if ( eScannedType != SvNumFormatType::UNDEFINED &&
eScannedType != SvNumFormatType::DATE &&
eScannedType != SvNumFormatType::DATETIME) // already another type
{ return MatchedReturn();
} if (eScannedType == SvNumFormatType::UNDEFINED)
{
eScannedType = SvNumFormatType::DATE; // !!! it IS a date
}
SkipBlanks(rString, nPos);
} else
{ return MatchedReturn();
}
} elseif (bBlanks)
{ // not a decimal separator, reset
nPos = nStartBlanks;
} else
{
nDecPos = 3; // . in end string
SkipBlanks(rString, nPos);
}
}
bool bSignDetectedHere = false; if ( nSign == 0 && // conflict - not signed
eScannedType != SvNumFormatType::DATE) // and not date //!? catch time too?
{ // not signed yet
nSign = GetSign(rString, nPos); // 1- DM if (bNegCheck) // '(' as sign
{ return MatchedReturn();
} if (nSign)
{
bSignDetectedHere = true;
}
}
SkipBlanks(rString, nPos); if (bNegCheck && SkipChar(')', rString, nPos)) // skip ')' if appropriate
{
bNegCheck = false;
SkipBlanks(rString, nPos);
}
if ( GetCurrency(rString, nPos) ) // currency symbol?
{ if (eScannedType != SvNumFormatType::UNDEFINED) // currency dup
{ return MatchedReturn();
} else
{
SkipBlanks(rString, nPos);
eScannedType = SvNumFormatType::CURRENCY;
} // behind currency a '-' is allowed if (nSign == 0) // not signed yet
{
nSign = GetSign(rString, nPos); // DM -
SkipBlanks(rString, nPos); if (bNegCheck) // 3 DM (
{ return MatchedReturn();
}
} if ( bNegCheck && eScannedType == SvNumFormatType::CURRENCY &&
SkipChar(')', rString, nPos) )
{
bNegCheck = false; // ')' skipped
SkipBlanks(rString, nPos); // only if currency
}
}
if ( SkipChar('%', rString, nPos) ) // 1%
{ if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
{ return MatchedReturn();
}
SkipBlanks(rString, nPos);
eScannedType = SvNumFormatType::PERCENT;
}
/** *Recognizestypesofnumber,exponential,fraction,percent,currency,date,time. *Elsetext=>returnfalse
*/ bool ImpSvNumberInputScan::IsNumberFormatMain( const OUString& rString, // string to be analyzed const SvNumberformat* pFormat ) // maybe number format set to match against
{
Reset();
mpFormat = pFormat;
NumberStringDivision( rString ); // breakdown into strings and numbers if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS) // too many elements
{ returnfalse; // Njet, Nope, ...
} if (nNumericsCnt == 0) // no number in input
{ if ( nStringsCnt > 0 )
{ // Here we may change the original, we don't need it anymore. // This saves copies and ToUpper() in GetLogical() and is faster.
sStrArray[0] = comphelper::string::strip(sStrArray[0], ' ');
OUString& rStrArray = sStrArray[0];
nLogical = GetLogical( rStrArray ); if ( nLogical )
{
eScannedType = SvNumFormatType::LOGICAL; // !!! it's a BOOLEAN
nMatchedAllStrings &= ~nMatchedVirgin; returntrue;
} else
{ returnfalse; // simple text
}
} else
{ returnfalse; // simple text
}
}
sal_uInt16 i = 0; // mark any symbol
sal_uInt16 j = 0; // mark only numbers
switch ( nNumericsCnt )
{ case1 : // Exactly 1 number in input // nStringsCnt >= 1 if (GetNextNumber(i,j)) // i=1,0
{ // Number at start if (eSetType == SvNumFormatType::FRACTION) // Fraction 1 = 1/1
{ if (i >= nStringsCnt || // no end string nor decimal separator
mrCurrentLanguageData.IsDecimalSep( sStrArray[i]))
{
eScannedType = SvNumFormatType::FRACTION;
nMatchedAllStrings &= ~nMatchedVirgin; returntrue;
}
}
} else
{ // Analyze start string if (!ScanStartString( sStrArray[i] )) // i=0
{ returnfalse; // already an error
}
i++; // next symbol, i=1
}
GetNextNumber(i,j); // i=1,2 if (eSetType == SvNumFormatType::FRACTION) // Fraction -1 = -1/1
{ if (nSign && !bNegCheck && // Sign +, -
eScannedType == SvNumFormatType::UNDEFINED && // not date or currency
nDecPos == 0 && // no previous decimal separator
(i >= nStringsCnt || // no end string nor decimal separator
mrCurrentLanguageData.IsDecimalSep( sStrArray[i]))
)
{
eScannedType = SvNumFormatType::FRACTION;
nMatchedAllStrings &= ~nMatchedVirgin; returntrue;
}
} if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
{ returnfalse;
} break; case2 : // Exactly 2 numbers in input // nStringsCnt >= 3 if (!GetNextNumber(i,j)) // i=1,0
{ // Analyze start string if (!ScanStartString( sStrArray[i] )) returnfalse; // already an error
i++; // i=1
}
GetNextNumber(i,j); // i=1,2 if ( !ScanMidString( sStrArray[i], i, j ) )
{ returnfalse;
}
i++; // next symbol, i=2,3
GetNextNumber(i,j); // i=3,4 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
{ returnfalse;
} if (eSetType == SvNumFormatType::FRACTION) // -1,200. as fraction
{ if (!bNegCheck && // no sign '('
eScannedType == SvNumFormatType::UNDEFINED &&
(nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
)
{
eScannedType = SvNumFormatType::FRACTION;
nMatchedAllStrings &= ~nMatchedVirgin; returntrue;
}
} break; case3 : // Exactly 3 numbers in input // nStringsCnt >= 5 if (!GetNextNumber(i,j)) // i=1,0
{ // Analyze start string if (!ScanStartString( sStrArray[i] ))
{ returnfalse; // already an error
}
i++; // i=1 if (nDecPos == 1) // decimal separator at start => error
{ returnfalse;
}
}
GetNextNumber(i,j); // i=1,2 if ( !ScanMidString( sStrArray[i], i, j ) )
{ returnfalse;
}
i++; // i=2,3 if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at end
{ returnfalse;
}
GetNextNumber(i,j); // i=3,4 if ( !ScanMidString( sStrArray[i], i, j ) )
{ returnfalse;
}
i++; // i=4,5
GetNextNumber(i,j); // i=5,6 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
{ returnfalse;
} if (eSetType == SvNumFormatType::FRACTION) // -1,200,100. as fraction
{ if (!bNegCheck && // no sign '('
eScannedType == SvNumFormatType::UNDEFINED &&
(nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
)
{
eScannedType = SvNumFormatType::FRACTION;
nMatchedAllStrings &= ~nMatchedVirgin; returntrue;
}
} if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
{ returnfalse; // #36857# not a real fraction
} break; default: // More than 3 numbers in input // nStringsCnt >= 7 if (!GetNextNumber(i,j)) // i=1,0
{ // Analyze startstring if (!ScanStartString( sStrArray[i] )) returnfalse; // already an error
i++; // i=1 if (nDecPos == 1) // decimal separator at start => error returnfalse;
}
GetNextNumber(i,j); // i=1,2 if ( !ScanMidString( sStrArray[i], i, j ) )
{ returnfalse;
}
i++; // i=2,3
{
sal_uInt16 nThOld = 10; // just not 0 or 1 while (nThOld != nThousand && j < nNumericsCnt-1) // Execute at least one time // but leave one number.
{ // Loop over group separators
nThOld = nThousand; if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at end
{ returnfalse;
}
GetNextNumber(i,j); if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i, j ) )
{ returnfalse;
}
i++;
}
} if (eScannedType == SvNumFormatType::DATE || // long date or
eScannedType == SvNumFormatType::TIME || // long time or
eScannedType == SvNumFormatType::UNDEFINED) // long number
{ for (sal_uInt16 k = j; k < nNumericsCnt-1; k++)
{ if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at endd
{ returnfalse;
}
GetNextNumber(i,j); if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i, j ) )
{ returnfalse;
}
i++;
}
}
GetNextNumber(i,j); if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
{ returnfalse;
} if (eSetType == SvNumFormatType::FRACTION) // -1,200,100. as fraction
{ if (!bNegCheck && // no sign '('
eScannedType == SvNumFormatType::UNDEFINED &&
(nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
)
{
eScannedType = SvNumFormatType::FRACTION;
nMatchedAllStrings &= ~nMatchedVirgin; returntrue;
}
} if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
{ returnfalse; // #36857# not a real fraction
} break;
}
if (eScannedType == SvNumFormatType::UNDEFINED)
{
nMatchedAllStrings &= ~nMatchedVirgin; // did match including nMatchedUsedAsReturn bool bDidMatch = (nMatchedAllStrings != 0); if ( nMatchedAllStrings )
{ bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
nStringScanNumFor, nStringsCnt, nNumericsCnt ); if ( !bMatch )
{
nMatchedAllStrings = 0;
}
} if ( nMatchedAllStrings )
{ // A type DEFINED means that no category could be assigned to the // overall format because of mixed type subformats. Use the scan // matched subformat's type if any.
SvNumFormatType eForType = eSetType; if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor); if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
eScannedType = eForType; else
eScannedType = SvNumFormatType::NUMBER;
} elseif ( bDidMatch )
{ // Accept a plain fractional number like 123.45 as there may be a // decimal separator also present as literal like in a 0"."0 weirdo // format. if (nDecPos != 2 || nNumericsCnt != 2) returnfalse;
eScannedType = SvNumFormatType::NUMBER;
} else
{
eScannedType = SvNumFormatType::NUMBER; // everything else should have been recognized by now
}
} elseif ( eScannedType == SvNumFormatType::DATE )
{ // the very relaxed date input checks may interfere with a preset format
nMatchedAllStrings &= ~nMatchedVirgin; bool bWasReturn = ((nMatchedAllStrings & nMatchedUsedAsReturn) != 0); if ( nMatchedAllStrings )
{ bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
nStringScanNumFor, nStringsCnt, nNumericsCnt ); if ( !bMatch )
{
nMatchedAllStrings = 0;
}
} if ( nMatchedAllStrings )
{ // A type DEFINED means that no category could be assigned to the // overall format because of mixed type subformats. Do not override // the scanned type in this case. Otherwise in IsNumberFormat() the // first numeric particle would be accepted as number.
SvNumFormatType eForType = eSetType; if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor); if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
eScannedType = eForType;
} elseif ( bWasReturn )
{ returnfalse;
}
} else
{
nMatchedAllStrings = 0; // reset flag to no substrings matched
} returntrue;
}
/** *DoesrStringrepresentanumber(alsodate,timeetal)
*/ bool ImpSvNumberInputScan::IsNumberFormat( const OUString& rString, // string to be analyzed
SvNumFormatType& F_Type, // IN: old type, OUT: new type double& fOutNumber, // OUT: number if convertible const SvNumberformat* pFormat, // maybe a number format to match against const NativeNumberWrapper& rNatNum,
SvNumInputOptions eInputOptions )
{ bool res; // return value
sal_uInt16 k;
eSetType = F_Type; // old type set
if ( !rString.getLength() )
{
res = false;
} elseif (rString.getLength() > 308) // arbitrary
{
res = false;
} else
{ // NoMoreUpperNeeded, all comparisons on UpperCase
OUString aString = mrCurrentLanguageData.GetCharClass()->uppercase( rString ); // convert native number to ASCII if necessary
TransformInput(rNatNum, mrCurrentLanguageData, aString);
res = IsNumberFormatMain( aString, pFormat );
}
if (res)
{ // Accept signed date only for ISO date with at least four digits in // year to not have an input of -M-D-Y arbitrarily recognized. The // final order is only determined in GetDateRef(). // Also accept for Y/M/D date pattern match, i.e. if the first number // is year. // Accept only if the year immediately follows the sign character with // no space in between. if (nSign && (eScannedType == SvNumFormatType::DATE ||
eScannedType == SvNumFormatType::DATETIME) && mbEraCE == kDefaultEra &&
(IsDatePatternNumberOfType(0,'Y') || (MayBeIso8601() && sStrArray[nNums[0]].getLength() >= 4)))
{ const sal_Unicode c = sStrArray[0][sStrArray[0].getLength()-1]; if (c == '-' || c == '+')
{ // A '+' sign doesn't change the era. if (nSign < 0)
mbEraCE = false; // BCE
nSign = 0;
}
} if ( bNegCheck || // ')' not found for '('
(nSign && (eScannedType == SvNumFormatType::DATE ||
eScannedType == SvNumFormatType::DATETIME))) // signed date/datetime
{
res = false;
} else
{ // check count of partial number strings switch (eScannedType)
{ case SvNumFormatType::PERCENT: case SvNumFormatType::CURRENCY: case SvNumFormatType::NUMBER: if (nDecPos == 1) // .05
{ // Matched MidStrings function like group separators, but // there can't be an integer part numeric input, so // effectively 0 thousands groups. if ( nMatchedAllStrings )
{
nThousand = 0;
} elseif ( nNumericsCnt != 1 )
{
res = false;
}
} elseif (nDecPos == 2) // 1.05
{ // Matched MidStrings function like group separators, but // let a decimal separator override a literal separator // string; like 0"." with input 123.45 if ( nMatchedAllStrings )
{ if (nNumericsCnt == 2)
nThousand = 0; else
{ // Assume that if there was a decimal separator // matching also a literal string then it was the // last. We could find the last possible match to // support literals in fractions, but really..
nThousand = nNumericsCnt - 1;
}
} elseif ( nNumericsCnt != nThousand+2 )
{
res = false;
}
} else// 1,100 or 1,100.
{ // matched MidStrings function like group separators if ( nMatchedAllStrings )
{
nThousand = nNumericsCnt - 1;
} elseif ( nNumericsCnt != nThousand+1 )
{
res = false;
}
} break;
case SvNumFormatType::SCIENTIFIC: // 1.0e-2 if (nDecPos == 1) // .05
{ if (nNumericsCnt != 2)
{
res = false;
}
} elseif (nDecPos == 2) // 1.05
{ if (nNumericsCnt != nThousand+3)
{
res = false;
}
} else// 1,100 or 1,100.
{ if (nNumericsCnt != nThousand+2)
{
res = false;
}
} break;
case SvNumFormatType::DATE: if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt == 3)
{ // If both, short month name and day of week name were // detected, and also numbers for full date, assume that we // have a day of week instead of month name.
nMonth = 0;
nMonthPos = 0;
} if (nMonth)
{ // month name and numbers if (nNumericsCnt > 2)
{
res = false;
}
} else
{ if (nNumericsCnt > 3)
{
res = false;
} else
{ // Even if a date pattern was matched, for abbreviated // pattern like "D.M." an input of "D.M. #" was // accepted because # could had been a time. Here we do // not have a combined date/time input though and # // would be taken as Year in this example, which it is // not. The count of numbers in pattern must match the // count of numbers in input.
res = (GetDatePatternNumbers() == nNumericsCnt)
|| IsAcceptableIso8601() || nMatchedAllStrings;
}
} break;
case SvNumFormatType::TIME: if (nDecPos)
{ // hundredth seconds included if (nNumericsCnt > 4)
{
res = false;
}
} else
{ if (nNumericsCnt > 3)
{
res = false;
}
} break;
case SvNumFormatType::DATETIME: if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt >= 5)
{ // If both, abbreviated month name and day of week name // were detected, and also at least numbers for full date // plus time including minutes, assume that we have a day // of week instead of month name.
nMonth = 0;
nMonthPos = 0;
} if (nMonth)
{ // month name and numbers if (nDecPos)
{ // hundredth seconds included if (nNumericsCnt > 6)
{
res = false;
}
} else
{ if (nNumericsCnt > 5)
{
res = false;
}
}
} else
{ if (nDecPos)
{ // hundredth seconds included if (nNumericsCnt > 7)
{
res = false;
}
} else
{ if (nNumericsCnt > 6)
{
res = false;
}
} if (res)
{
res = IsAcceptedDatePattern( nNums[0]) || MayBeIso8601() || nMatchedAllStrings;
}
} break;
if (res)
{ // we finally have a number switch (eScannedType)
{ case SvNumFormatType::LOGICAL: if (nLogical == 1)
{
fOutNumber = 1.0; // True
} elseif (nLogical == -1)
{
fOutNumber = 0.0; // False
} else
{
res = false; // Oops
} break;
case SvNumFormatType::PERCENT: case SvNumFormatType::CURRENCY: case SvNumFormatType::NUMBER: case SvNumFormatType::SCIENTIFIC: case SvNumFormatType::DEFINED: // if no category detected handle as number if ( nDecPos == 1 ) // . at start
{
sResString.append("0.");
}
for ( k = 0; k <= nThousand; k++)
{
sResString.append(sStrArray[nNums[k]]); // integer part
} if ( nDecPos == 2 && k < nNumericsCnt ) // . somewhere
{
sResString.append('.');
sal_uInt16 nStop = (eScannedType == SvNumFormatType::SCIENTIFIC ?
nNumericsCnt-1 : nNumericsCnt); for ( ; k < nStop; k++)
{
sResString.append(sStrArray[nNums[k]]); // fractional part
}
}
case SvNumFormatType::TIME:
res = GetTimeRef(fOutNumber, 0, nNumericsCnt, eInputOptions); if ( nSign < 0 )
{
fOutNumber = -fOutNumber;
} break;
case SvNumFormatType::DATE:
res = GetDateRef( fOutNumber, k ); break;
case SvNumFormatType::DATETIME:
res = GetDateRef( fOutNumber, k ); if ( res )
{ double fTime;
res = GetTimeRef( fTime, k, nNumericsCnt - k, eInputOptions);
fOutNumber += fTime;
} break;
default:
SAL_WARN( "svl.numbers", "Some number recognized but what's it?" );
fOutNumber = 0.0; break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.