resolved fdo#41166 match month and day name word instead of substring

(cherry picked from commit 8e71f81f47c20320c4de7a7aadb7d524f0d8ea76)
(cherry picked from commit 3885b5d4b00ebb31adabc36c507abd642c03d0d4)

Change-Id: I897dbcee47de574d91ba3e3b40a39a35b779fef8
Reviewed-on: https://gerrit.libreoffice.org/9387
Reviewed-by: Kohei Yoshida <libreoffice@kohei.us>
Tested-by: Kohei Yoshida <libreoffice@kohei.us>
diff --git a/svl/source/numbers/zforfind.cxx b/svl/source/numbers/zforfind.cxx
index 840af0c..9c8bb1c 100644
--- a/svl/source/numbers/zforfind.cxx
+++ b/svl/source/numbers/zforfind.cxx
@@ -438,6 +438,52 @@ bool ImpSvNumberInputScan::StringPtrContainsImpl( const OUString& rWhat,


/**
 * Whether rString contains word rWhat at nPos
 */
bool ImpSvNumberInputScan::StringContainsWord( const OUString& rWhat,
                                               const OUString& rString, sal_Int32 nPos )
{
    if (rWhat.isEmpty() || rString.getLength() < nPos + rWhat.getLength())
        return false;

    if (StringPtrContainsImpl( rWhat, rString.getStr(), nPos))
    {
        nPos += rWhat.getLength();
        if (nPos == rString.getLength())
            return true;    // word at end of string

        /* TODO: we COULD invoke bells and whistles word break iterator to find
         * the next boundary, but really ... this is called for date input, so
         * how many languages do not separate the day and month names in some
         * form? */

        // Check simple ASCII first before invoking i18n or anything else.
        if (rtl::isAsciiAlphanumeric( rString[nPos] ))
            return false;   // Alpha or numeric is not word gap.

        sal_Int32 nIndex = nPos;
        const sal_uInt32 c = rString.iterateCodePoints( &nIndex);
        if (nPos+1 < nIndex)
            return true;    // Surrogate, assume these to be new words.
        (void)c;

        const sal_Int32 nType = pFormatter->GetCharClass()->getCharacterType( rString, nPos);
        using namespace ::com::sun::star::i18n;

        if ((nType & (KCharacterType::UPPER | KCharacterType::LOWER | KCharacterType::DIGIT)) != 0)
            return false;   // Alpha or numeric is not word gap.

        if ((nType & (KCharacterType::LETTER)) != 0)
            return true;    // Letter other than alpha is new word. (Is it?)

        return true;        // Catch all remaining as gap until we know better.
    }

    return false;
}


/**
 * Skips the supplied char
 */
inline bool ImpSvNumberInputScan::SkipChar( sal_Unicode c, const OUString& rString,
@@ -575,7 +621,7 @@ short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
        // if we stopped at the first match.
        for ( sal_Int16 i = 0; i < nMonths; i++ )
        {
            if ( bScanGenitiveMonths && StringContains( pUpperGenitiveMonthText[i], rString, nPos ) )
            if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveMonthText[i], rString, nPos ) )
            {   // genitive full names first
                const int nMonthLen = pUpperGenitiveMonthText[i].getLength();
                if (nMonthLen > nMatchLen)
@@ -584,7 +630,7 @@ short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
                    res = i + 1;
                }
            }
            else if ( bScanGenitiveMonths && StringContains( pUpperGenitiveAbbrevMonthText[i], rString, nPos ) )
            else if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveAbbrevMonthText[i], rString, nPos ) )
            {   // genitive abbreviated
                const int nMonthLen = pUpperGenitiveAbbrevMonthText[i].getLength();
                if (nMonthLen > nMatchLen)
@@ -593,7 +639,7 @@ short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
                    res = sal::static_int_cast< short >(-(i+1)); // negative
                }
            }
            else if ( bScanPartitiveMonths && StringContains( pUpperPartitiveMonthText[i], rString, nPos ) )
            else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveMonthText[i], rString, nPos ) )
            {   // partitive full names
                const int nMonthLen = pUpperPartitiveMonthText[i].getLength();
                if (nMonthLen > nMatchLen)
@@ -602,7 +648,7 @@ short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
                    res = i+1;
                }
            }
            else if ( bScanPartitiveMonths && StringContains( pUpperPartitiveAbbrevMonthText[i], rString, nPos ) )
            else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveAbbrevMonthText[i], rString, nPos ) )
            {   // partitive abbreviated
                const int nMonthLen = pUpperPartitiveAbbrevMonthText[i].getLength();
                if (nMonthLen > nMatchLen)
@@ -611,7 +657,7 @@ short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
                    res = sal::static_int_cast< short >(-(i+1)); // negative
                }
            }
            else if ( StringContains( pUpperMonthText[i], rString, nPos ) )
            else if ( StringContainsWord( pUpperMonthText[i], rString, nPos ) )
            {   // noun full names
                const int nMonthLen = pUpperMonthText[i].getLength();
                if (nMonthLen > nMatchLen)
@@ -620,7 +666,7 @@ short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
                    res = i+1;
                }
            }
            else if ( StringContains( pUpperAbbrevMonthText[i], rString, nPos ) )
            else if ( StringContainsWord( pUpperAbbrevMonthText[i], rString, nPos ) )
            {   // noun abbreviated
                const int nMonthLen = pUpperAbbrevMonthText[i].getLength();
                if (nMonthLen > nMatchLen)
@@ -630,7 +676,7 @@ short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
                }
            }
            else if ( i == 8 && pUpperAbbrevMonthText[i] == aSeptCorrect &&
                    StringContains( aSepShortened, rString, nPos ) )
                    StringContainsWord( aSepShortened, rString, nPos ) )
            {   // #102136# SEPT/SEP
                const int nMonthLen = aSepShortened.getLength();
                if (nMonthLen > nMatchLen)
@@ -665,13 +711,13 @@ int ImpSvNumberInputScan::GetDayOfWeek( const OUString& rString, sal_Int32& nPos
        sal_Int16 nDays = pFormatter->GetCalendar()->getNumberOfDaysInWeek();
        for ( sal_Int16 i = 0; i < nDays; i++ )
        {
            if ( StringContains( pUpperDayText[i], rString, nPos ) )
            if ( StringContainsWord( pUpperDayText[i], rString, nPos ) )
            {   // full names first
                nPos = nPos + pUpperDayText[i].getLength();
                res = i + 1;
                break;  // for
            }
            if ( StringContains( pUpperAbbrevDayText[i], rString, nPos ) )
            if ( StringContainsWord( pUpperAbbrevDayText[i], rString, nPos ) )
            {   // abbreviated
                nPos = nPos + pUpperAbbrevDayText[i].getLength();
                res = -(i + 1); // negative
diff --git a/svl/source/numbers/zforfind.hxx b/svl/source/numbers/zforfind.hxx
index 6e24040..002db24 100644
--- a/svl/source/numbers/zforfind.hxx
+++ b/svl/source/numbers/zforfind.hxx
@@ -210,6 +210,13 @@ private:
    void NumberStringDivision( const OUString& rString );


    /** Whether rString contains word (!) rWhat at nPos.
        rWhat will not be matched if it is a substring of a word.
     */
    bool StringContainsWord( const OUString& rWhat,
                             const OUString& rString,
                             sal_Int32 nPos );

    // optimized substring versions

    // Whether rString contains rWhat at nPos