Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  fmsrcimp.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <svx/fmtools.hxx>
#include <svx/fmsrccfg.hxx>
#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <tools/wldcrd.hxx>
#include <vcl/svapp.hxx>
#include <unotools/textsearch.hxx>
#include <com/sun/star/awt/XTextComponent.hpp>
#include <com/sun/star/awt/XListBox.hpp>
#include <com/sun/star/awt/XCheckBox.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/util/SearchAlgorithms2.hpp>
#include <com/sun/star/util/SearchFlags.hpp>
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/i18n/CollatorOptions.hpp>

#include <com/sun/star/sdb/XColumn.hpp>
#include <com/sun/star/sdbc/XConnection.hpp>
#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>

#include <fmprop.hxx>
#include <svx/fmsrcimp.hxx>

#include <comphelper/types.hxx>
#include <unotools/syslocale.hxx>
#include <i18nutil/searchopt.hxx>

#define EQUAL_BOOKMARKS(a, b) a == b

#define IFACECAST(c)          static_cast<const Reference< XInterface >&>(c)

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star::beans;
using namespace ::svxform;


// = FmRecordCountListener

//  SMART_UNO_IMPLEMENTATION(FmRecordCountListener, UsrObject);


FmRecordCountListener::FmRecordCountListener(const Reference< css::sdbc::XResultSet > &&nbsp;dbcCursor)
{

    m_xListening.set(dbcCursor, UNO_QUERY);
    if (!m_xListening.is())
        return;

    if (::comphelper::getBOOL(m_xListening->getPropertyValue(FM_PROP_ROWCOUNTFINAL)))
    {
        m_xListening = nullptr;
        // there's nothing to do as the record count is already known
        return;
    }

    m_xListening->addPropertyChangeListener(FM_PROP_ROWCOUNT, static_cast<css::beans::XPropertyChangeListener*>(this));
}


void FmRecordCountListener::SetPropChangeHandler(const Link<sal_Int32,void>& lnk)
{
    m_lnkWhoWantsToKnow = lnk;

    if (m_xListening.is())
        NotifyCurrentCount();
}


FmRecordCountListener::~FmRecordCountListener()
{

}


void FmRecordCountListener::DisConnect()
{
    if(m_xListening.is())
        m_xListening->removePropertyChangeListener(FM_PROP_ROWCOUNT, static_cast<css::beans::XPropertyChangeListener*>(this));
    m_xListening = nullptr;
}


void SAL_CALL FmRecordCountListener::disposing(const css::lang::EventObject& /*Source*/)
{
    DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::disposing should never have been called without a propset !");
    DisConnect();
}


void FmRecordCountListener::NotifyCurrentCount()
{
    if (m_lnkWhoWantsToKnow.IsSet())
    {
        DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::NotifyCurrentCount : I have no propset ... !?");
        sal_Int32 theCount = ::comphelper::getINT32(m_xListening->getPropertyValue(FM_PROP_ROWCOUNT));
        m_lnkWhoWantsToKnow.Call(theCount);
    }
}


void FmRecordCountListener::propertyChange(const  css::beans::PropertyChangeEvent&&nbsp;/*evt*/)
{
    NotifyCurrentCount();
}


// FmSearchEngine - local classes

SimpleTextWrapper::SimpleTextWrapper(const Reference< css::awt::XTextComponent > & _xText)
    :ControlTextWrapper(_xText)
    ,m_xText(_xText)
{
    DBG_ASSERT(m_xText.is(), "FmSearchEngine::SimpleTextWrapper::SimpleTextWrapper : invalid argument !");
}


OUString SimpleTextWrapper::getCurrentText() const
{
    return m_xText->getText();
}


ListBoxWrapper::ListBoxWrapper(const Reference< css::awt::XListBox > & _xBox)
    :ControlTextWrapper(_xBox)
    ,m_xBox(_xBox)
{
    DBG_ASSERT(m_xBox.is(), "FmSearchEngine::ListBoxWrapper::ListBoxWrapper : invalid argument !");
}


OUString ListBoxWrapper::getCurrentText() const
{
    return m_xBox->getSelectedItem();
}


CheckBoxWrapper::CheckBoxWrapper(const Reference< css::awt::XCheckBox > & _xBox)
    :ControlTextWrapper(_xBox)
    ,m_xBox(_xBox)
{
    DBG_ASSERT(m_xBox.is(), "FmSearchEngine::CheckBoxWrapper::CheckBoxWrapper : invalid argument !");
}


OUString CheckBoxWrapper::getCurrentText() const
{
    switch (static_cast<TriState>(m_xBox->getState()))
    {
        case TRISTATE_FALSE: return u"0"_ustr;
        case TRISTATE_TRUE: return u"1"_ustr;
        defaultbreak;
    }
    return OUString();
}


// = FmSearchEngine

bool FmSearchEngine::MoveCursor()
{
    bool bSuccess = true;
    try
    {
        if (m_bForward)
            if (m_xSearchCursor.isLast())
                m_xSearchCursor.first();
            else
                m_xSearchCursor.next();
        else
            if (m_xSearchCursor.isFirst())
            {
                rtl::Reference<FmRecordCountListener> prclListener = new FmRecordCountListener(m_xSearchCursor);
                prclListener->SetPropChangeHandler(LINK(this, FmSearchEngine, OnNewRecordCount));

                m_xSearchCursor.last();

                prclListener->DisConnect();
            }
            else
                m_xSearchCursor.previous();
    }
    catch(Exception const&)
    {
        TOOLS_WARN_EXCEPTION( "svx""FmSearchEngine::MoveCursor");
        bSuccess = false;
    }
    catch(...)
    {
        TOOLS_WARN_EXCEPTION( "svx""FmSearchEngine::MoveCursor : caught an unknown Exception !");
        bSuccess = false;
    }

    return bSuccess;
}


bool FmSearchEngine::MoveField(sal_Int32& nPos, FieldCollection::iterator& iter, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator&&nbsp;iterEnd)
{
    bool bSuccess(true);
    if (m_bForward)
    {
        ++iter;
        ++nPos;
        if (iter == iterEnd)
        {
            bSuccess = MoveCursor();
            iter = iterBegin;
            nPos = 0;
        }
    } else
    {
        if (iter == iterBegin)
        {
            bSuccess = MoveCursor();
            iter = iterEnd;
            nPos = iter-iterBegin;
        }
        --iter;
        --nPos;
    }
    return bSuccess;
}


void FmSearchEngine::BuildAndInsertFieldInfo(const Reference< css::container::XIndexAccess > & xAllFields, sal_Int32 nField)
{
    DBG_ASSERT( xAllFields.is() && ( nField >= 0 ) && ( nField < xAllFields->getCount() ),
        "FmSearchEngine::BuildAndInsertFieldInfo: invalid field descriptor!" );

    // the field itself
    Reference< XInterface > xCurrentField;
    xAllFields->getByIndex(nField) >>= xCurrentField;

    // From this I now know that it supports the DatabaseRecord service (I hope).
    // For the FormatKey and the type I need the PropertySet.
    Reference< css::beans::XPropertySet >  xProperties(xCurrentField, UNO_QUERY_THROW);

    // build the FieldInfo for that
    FieldInfo fiCurrent;
    fiCurrent.xContents.set(xCurrentField, UNO_QUERY);

    // and memorize
    m_arrUsedFields.insert(m_arrUsedFields.end(), fiCurrent);

}

OUString FmSearchEngine::FormatField(sal_Int32 nWhich)
{
    DBG_ASSERT(o3tl::make_unsigned(nWhich) < m_aControlTexts.size(), "FmSearchEngine::FormatField(sal_Int32) : invalid position !");
    DBG_ASSERT(m_aControlTexts[nWhich], "FmSearchEngine::FormatField(sal_Int32) : invalid object in array !");
    DBG_ASSERT(m_aControlTexts[nWhich]->getControl().is(), "FmSearchEngine::FormatField : invalid control !");

    if (m_nCurrentFieldIndex != -1)
    {
        DBG_ASSERT((nWhich == 0) || (nWhich == m_nCurrentFieldIndex), "FmSearchEngine::FormatField : parameter nWhich is invalid");
        // analogous situation as below
        nWhich = m_nCurrentFieldIndex;
    }

    DBG_ASSERT((nWhich >= 0) && (o3tl::make_unsigned(nWhich) < m_aControlTexts.size()),
        "FmSearchEngine::FormatField : invalid argument nWhich !");
    return m_aControlTexts[m_nCurrentFieldIndex == -1 ? nWhich : m_nCurrentFieldIndex]->getCurrentText();
}


FmSearchEngine::SearchResult FmSearchEngine::SearchSpecial(bool _bSearchForNull, sal_Int32& nFieldPos,
    FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
{
    // memorize the start position
    Any aStartMark;
    try { aStartMark = m_xSearchCursor.getBookmark(); }
    catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
    FieldCollection::const_iterator iterInitialField = iterFieldLoop;


    bool bFound(false);
    bool bMovedAround(false);
    do
    {
        Application::Reschedule( true );

        // the content to be compared currently
        iterFieldLoop->xContents->getString();  // needed for wasNull
        bFound = _bSearchForNull == bool(iterFieldLoop->xContents->wasNull());
        if (bFound)
            break;

        // next field (implicitly next record, if necessary)
        if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
        {   // When moving to the next field, something went wrong...
            // Continuing is not possible, since the next time exactly the same
            // will definitely go wrong again, thus abort.
            // Before, however, so that the search continues at the current position:
            try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
            catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
            m_iterPreviousLocField = iterFieldLoop;
            // and leave
            return SearchResult::Error;
        }

        Any aCurrentBookmark;
        try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
        catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }

        bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);

        if (nFieldPos == 0)
            // that is, I've moved to a new record
            PropagateProgress(bMovedAround);
                // if we moved to the starting position we don't have to propagate an 'overflow' message
                // FS - 07.12.99 - 68530

        // cancel requested?
        if (CancelRequested())
            return SearchResult::Cancelled;

    } while (!bMovedAround);

    return bFound ? SearchResult::Found : SearchResult::NotFound;
}


FmSearchEngine::SearchResult FmSearchEngine::SearchWildcard(std::u16string_view strExpression, sal_Int32& nFieldPos,
    FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
{
    // memorize the start position
    Any aStartMark;
    try { aStartMark = m_xSearchCursor.getBookmark(); }
    catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
    FieldCollection::const_iterator iterInitialField = iterFieldLoop;

    WildCard aSearchExpression(strExpression);


    bool bFound(false);
    bool bMovedAround(false);
    do
    {
        Application::Reschedule( true );

        // the content to be compared currently
        OUString sCurrentCheck;
        if (m_bFormatter)
            sCurrentCheck = FormatField(nFieldPos);
        else
            sCurrentCheck = iterFieldLoop->xContents->getString();

        if (!GetCaseSensitive())
            // norm the string
            sCurrentCheck = m_aCharacterClassficator.lowercase(sCurrentCheck);

        // now the test is easy...
        bFound = aSearchExpression.Matches(sCurrentCheck);

        if (bFound)
            break;

        // next field (implicitly next record, if necessary)
        if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
        {   // When moving to the next field, something went wrong...
            // Continuing is not possible, since the next time exactly the same
            // will definitely go wrong again, thus abort.
            // Before, however, so that the search continues at the current position:
            try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
            catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
            m_iterPreviousLocField = iterFieldLoop;
            // and leave
            return SearchResult::Error;
        }

        Any aCurrentBookmark;
        try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
        catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }

        bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);

        if (nFieldPos == 0)
            // that is, I've moved to a new record
            PropagateProgress(bMovedAround);
                // if we moved to the starting position we don't have to propagate an 'overflow' message
                // FS - 07.12.99 - 68530

        //  cancel requested?
        if (CancelRequested())
            return SearchResult::Cancelled;

    } while (!bMovedAround);

    return bFound ? SearchResult::Found : SearchResult::NotFound;
}


FmSearchEngine::SearchResult FmSearchEngine::SearchRegularApprox(const OUString&&nbsp;strExpression, sal_Int32& nFieldPos,
    FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
{
    DBG_ASSERT(m_bLevenshtein || m_bRegular,
        "FmSearchEngine::SearchRegularApprox : invalid search mode!");
    DBG_ASSERT(!m_bLevenshtein || !m_bRegular,
        "FmSearchEngine::SearchRegularApprox : cannot search for regular expressions and similarities at the same time!");

    // memorize start position
    Any aStartMark;
    try { aStartMark = m_xSearchCursor.getBookmark(); }
    catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
    FieldCollection::const_iterator iterInitialField = iterFieldLoop;

    // collect parameters
    i18nutil::SearchOptions2 aParam;
    aParam.AlgorithmType2 = m_bRegular ? SearchAlgorithms2::REGEXP : SearchAlgorithms2::APPROXIMATE;
    aParam.searchFlag = 0;
    aParam.transliterateFlags = GetTransliterationFlags();
    if ( !GetTransliteration() )
    {   // if transliteration is not enabled, the only flags which matter are IGNORE_CASE and IGNORE_WIDTH
        aParam.transliterateFlags &= TransliterationFlags::IGNORE_CASE | TransliterationFlags::IGNORE_WIDTH;
    }
    if (m_bLevenshtein)
    {
        if (m_bLevRelaxed)
            aParam.searchFlag |= SearchFlags::LEV_RELAXED;
        aParam.changedChars = m_nLevOther;
        aParam.deletedChars = m_nLevShorter;
        aParam.insertedChars = m_nLevLonger;
    }
    aParam.searchString = strExpression;
    aParam.Locale = SvtSysLocale().GetLanguageTag().getLocale();
    ::utl::TextSearch aLocalEngine( aParam);


    bool bFound = false;
    bool bMovedAround(false);
    do
    {
        Application::Reschedule( true );

        // the content to be compared currently
        OUString sCurrentCheck;
        if (m_bFormatter)
            sCurrentCheck = FormatField(nFieldPos);
        else
            sCurrentCheck = iterFieldLoop->xContents->getString();

        // (don't care about case here, this is done by the TextSearch object, 'cause we passed our case parameter to it)

        sal_Int32 nStart = 0, nEnd = sCurrentCheck.getLength();
        bFound = aLocalEngine.SearchForward(sCurrentCheck, &nStart, &nEnd);
            // it says 'forward' here, but that only refers to the search within
            // sCurrentCheck, so it has nothing to do with the direction of my
            // record migration (MoveField takes care of that)

        // check if the position is correct
        if (bFound)
        {
            switch (m_nPosition)
            {
                case MATCHING_WHOLETEXT :
                    if (nEnd != sCurrentCheck.getLength())
                    {
                        bFound = false;
                        break;
                    }
                    [[fallthrough]];
                case MATCHING_BEGINNING :
                    if (nStart != 0)
                        bFound = false;
                    break;
                case MATCHING_END :
                    if (nEnd != sCurrentCheck.getLength())
                        bFound = false;
                    break;
            }
        }

        if (bFound) // still?
            break;

        // next field (implicitly next record, if necessary)
        if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
        {   // When moving to the next field, something went wrong...
            // Continuing is not possible, since the next time exactly the same
            // will definitely go wrong again, thus abort (without error
            // notification, I expect it to be displayed in the Move).
            // Before, however, so that the search continues at the current position:
            try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
            catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
            m_iterPreviousLocField = iterFieldLoop;
            // and leave
            return SearchResult::Error;
        }

        Any aCurrentBookmark;
        try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
        catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
        bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);

        if (nFieldPos == 0)
            // that is, I've moved to a new record
            PropagateProgress(bMovedAround);
                // if we moved to the starting position we don't have to propagate an 'overflow' message
                // FS - 07.12.99 - 68530

        // cancel requested?
        if (CancelRequested())
            return SearchResult::Cancelled;

    } while (!bMovedAround);

    return bFound ? SearchResult::Found : SearchResult::NotFound;
}


FmSearchEngine::FmSearchEngine(const Reference< XComponentContext >& _rxContext,
        const Reference< XResultSet > & xCursor, std::u16string_view sVisibleFields,
        const InterfaceArray& arrFields)
    :m_xSearchCursor(xCursor)
    ,m_aCharacterClassficator( _rxContext, SvtSysLocale().GetLanguageTag() )
    ,m_aStringCompare( _rxContext )
    ,m_nCurrentFieldIndex(-2)   // -1 already has a meaning, so I take -2 for 'invalid'
    ,m_xOriginalIterator(xCursor)
    ,m_xClonedIterator(m_xOriginalIterator, true)
    ,m_eSearchForType(SearchFor::String)
    ,m_srResult(SearchResult::Found)
    ,m_bCancelAsynchRequest(false)
    ,m_bSearchingCurrently(false)
    ,m_bFormatter(true)     // this must be consistent with m_xSearchCursor, which is generally == m_xOriginalIterator
    ,m_bForward(false)
    ,m_bWildcard(false)
    ,m_bRegular(false)
    ,m_bLevenshtein(false)
    ,m_bTransliteration(false)
    ,m_bLevRelaxed(false)
    ,m_nLevOther(0)
    ,m_nLevShorter(0)
    ,m_nLevLonger(0)
    ,m_nPosition(MATCHING_ANYWHERE)
    ,m_nTransliterationFlags(TransliterationFlags::NONE)
{

    fillControlTexts(arrFields);
    Init(sVisibleFields);
}


void FmSearchEngine::SetIgnoreWidthCJK(bool bSet)
{
    if (bSet)
        m_nTransliterationFlags |= TransliterationFlags::IGNORE_WIDTH;
    else
        m_nTransliterationFlags &= ~TransliterationFlags::IGNORE_WIDTH;
}


bool FmSearchEngine::GetIgnoreWidthCJK() const
{
    return bool(m_nTransliterationFlags & TransliterationFlags::IGNORE_WIDTH);
}


void FmSearchEngine::SetCaseSensitive(bool bSet)
{
    if (bSet)
        m_nTransliterationFlags &= ~TransliterationFlags::IGNORE_CASE;
    else
        m_nTransliterationFlags |= TransliterationFlags::IGNORE_CASE;
}


bool FmSearchEngine::GetCaseSensitive() const
{
    return !(m_nTransliterationFlags & TransliterationFlags::IGNORE_CASE);
}


void FmSearchEngine::fillControlTexts(const InterfaceArray& arrFields)
{
    m_aControlTexts.clear();
    Reference< XInterface >  xCurrent;
    for (const auto & rField : arrFields)
    {
        xCurrent = rField;
        DBG_ASSERT(xCurrent.is(), "FmSearchEngine::fillControlTexts : invalid field interface !");
        // check which type of control this is
        Reference< css::awt::XTextComponent >  xAsText(xCurrent, UNO_QUERY);
        if (xAsText.is())
        {
            m_aControlTexts.emplace_back(new SimpleTextWrapper(xAsText));
            continue;
        }

        Reference< css::awt::XListBox >  xAsListBox(xCurrent, UNO_QUERY);
        if (xAsListBox.is())
        {
            m_aControlTexts.emplace_back(new ListBoxWrapper(xAsListBox));
            continue;
        }

        Reference< css::awt::XCheckBox >  xAsCheckBox(xCurrent, UNO_QUERY);
        DBG_ASSERT(xAsCheckBox.is(), "FmSearchEngine::fillControlTexts : invalid field interface (no supported type) !");
            // we don't have any more options ...
        m_aControlTexts.emplace_back(new CheckBoxWrapper(xAsCheckBox));
    }
}


void FmSearchEngine::Init(std::u16string_view sVisibleFields)
{
    // analyze the fields
    // additionally, create the mapping: because the list of used columns can be shorter than the list
    // of columns of the cursor, we need a mapping: "used column number n" -> "cursor column m"
    m_arrFieldMapping.clear();

    // important: The case of the columns does not need to be exact - for instance:
    // - a user created a form which works on a table, for which the driver returns a column name "COLUMN"
    // - the driver itself works case-insensitive with column names
    // - a control in the form is bound to "column" - not the different case
    // In such a scenario, the form and the field would work okay, but we here need to case for the different case
    // explicitly
    // #i8755#

    // so first of all, check if the database handles identifiers case sensitive
    Reference< XConnection > xConn;
    Reference< XDatabaseMetaData > xMeta;
    Reference< XPropertySet > xCursorProps( IFACECAST( m_xSearchCursor ), UNO_QUERY );
    if ( xCursorProps.is() )
    {
        try
        {
            xCursorProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ) >>= xConn;
        }
        catchconst Exception& ) { /* silent this - will be asserted below */ }
    }
    if ( xConn.is() )
        xMeta = xConn->getMetaData();
    OSL_ENSURE( xMeta.is(), "FmSearchEngine::Init: very strange cursor (could not derive connection meta data from it)!" );

    bool bCaseSensitiveIdentifiers = true;  // assume case sensitivity
    if ( xMeta.is() )
        bCaseSensitiveIdentifiers = xMeta->supportsMixedCaseQuotedIdentifiers();

    // now that we have this information, we need a collator which is able to case (in)sensitivity compare strings
    m_aStringCompare.loadDefaultCollator( SvtSysLocale().GetLanguageTag().getLocale(),
        bCaseSensitiveIdentifiers ? 0 : css::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );

    try
    {
        // the cursor can give me a record (as PropertySet), which supports the DatabaseRecord service
        Reference< css::sdbcx::XColumnsSupplier >  xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
        DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::Init : invalid cursor (no columns supplier) !");
        Reference< css::container::XNameAccess >       xAllFieldNames = xSupplyCols->getColumns();
        const Sequence< OUString > seqFieldNames = xAllFieldNames->getElementNames();

        OUString sCurrentField;
        sal_Int32 nIndex = 0;
        do
        {
            sCurrentField = o3tl::getToken(sVisibleFields, 0, ';' , nIndex);

            // search in the field collection
            sal_Int32 nFoundIndex = -1;
            auto pFieldName = std::find_if(seqFieldNames.begin(), seqFieldNames.end(),
                [this, &sCurrentField](const OUString& rFieldName) {
                    return 0 == m_aStringCompare.compareString( rFieldName, sCurrentField ); });
            if (pFieldName != seqFieldNames.end())
                nFoundIndex = static_cast<sal_Int32>(std::distance(seqFieldNames.begin(), pFieldName));
            DBG_ASSERT(nFoundIndex != -1, "FmSearchEngine::Init : Invalid field name were given !");
            m_arrFieldMapping.push_back(nFoundIndex);
        }
        while ( nIndex >= 0 );
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("svx.form""");
    }

}


void FmSearchEngine::SetFormatterUsing(bool bSet)
{
    if (m_bFormatter == bSet)
        return;
    m_bFormatter = bSet;

    // I did not use a formatter, but TextComponents -> the SearchIterator needs to be adjusted
    try
    {
        if (m_bFormatter)
        {
            DBG_ASSERT(m_xSearchCursor == m_xClonedIterator, "FmSearchEngine::SetFormatterUsing : inconsistent state !");
            m_xSearchCursor = m_xOriginalIterator;
            m_xSearchCursor.moveToBookmark(m_xClonedIterator.getBookmark());
                // so that I continue with the new iterator at the actual place where I previously stopped
        }
        else
        {
            DBG_ASSERT(m_xSearchCursor == m_xOriginalIterator, "FmSearchEngine::SetFormatterUsing : inconsistent state !");
            m_xSearchCursor = m_xClonedIterator;
            m_xSearchCursor.moveToBookmark(m_xOriginalIterator.getBookmark());
        }
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }

    // I have to re-bind the fields, because the text exchange might take
    // place over these fields and the underlying cursor has changed
    RebuildUsedFields(m_nCurrentFieldIndex, true);
}


void FmSearchEngine::PropagateProgress(bool _bDontPropagateOverflow)
{
    if (!m_aProgressHandler.IsSet())
        return;

    FmSearchProgress aProgress;
    try
    {
        aProgress.aSearchState = FmSearchProgress::State::Progress;
        aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
        if (m_bForward)
            aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isFirst();
        else
            aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isLast();
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }

    m_aProgressHandler.Call(&aProgress);
}


void FmSearchEngine::SearchNextImpl()
{
    DBG_ASSERT(!(m_bWildcard && m_bRegular) && !(m_bRegular && m_bLevenshtein) && !(m_bLevenshtein && m_bWildcard),
        "FmSearchEngine::SearchNextImpl : search parameters are mutually exclusive!");

    DBG_ASSERT(m_xSearchCursor.is(), "FmSearchEngine::SearchNextImpl : have invalid iterator!");

    // the parameters of the search
    OUString strSearchExpression(m_strSearchExpression); // I need non-const
    if (!GetCaseSensitive())
        // norm the string
        strSearchExpression = m_aCharacterClassficator.lowercase(strSearchExpression);

    if (!m_bRegular && !m_bLevenshtein)
    {   // 'normal' search I run through WildCards in any case, but must before adjust the OUString depending on the mode

        if (!m_bWildcard)
        {   // since in all other cases * and ? in the search string are of course
            // also allowed, but should not count as WildCards, I need to normalize
            OUString aTmp(strSearchExpression);
            aTmp = aTmp.replaceAll("*""\\*");
            aTmp = aTmp.replaceAll("?""\\?");
            strSearchExpression = aTmp;

            switch (m_nPosition)
            {
                case MATCHING_ANYWHERE :
                    strSearchExpression = "*" + strSearchExpression + "*";
                    break;
                case MATCHING_BEGINNING :
                    strSearchExpression += "*";
                    break;
                case MATCHING_END :
                    strSearchExpression = "*" + strSearchExpression;
                    break;
                case MATCHING_WHOLETEXT :
                    break;
                default :
                    OSL_FAIL("FmSearchEngine::SearchNextImpl() : the methods listbox may contain only 4 entries ...");
            }
        }
    }

    // for work on field list
    FieldCollection::iterator iterBegin = m_arrUsedFields.begin();
    FieldCollection::iterator iterEnd = m_arrUsedFields.end();
    FieldCollection::iterator iterFieldCheck;

    sal_Int32 nFieldPos;

    if (m_aPreviousLocBookmark.hasValue())
    {
        DBG_ASSERT(EQUAL_BOOKMARKS(m_aPreviousLocBookmark, m_xSearchCursor.getBookmark()),
            "FmSearchEngine::SearchNextImpl : invalid position!");
        iterFieldCheck = m_iterPreviousLocField;
        // continue in the field after (or before) the last discovery
        nFieldPos = iterFieldCheck - iterBegin;
        MoveField(nFieldPos, iterFieldCheck, iterBegin, iterEnd);
    }
    else
    {
        if (m_bForward)
            iterFieldCheck = iterBegin;
        else
        {
            iterFieldCheck = iterEnd;
            --iterFieldCheck;
        }
        nFieldPos = iterFieldCheck - iterBegin;
    }

    PropagateProgress(true);
    SearchResult srResult;
    if (m_eSearchForType != SearchFor::String)
        srResult = SearchSpecial(m_eSearchForType == SearchFor::Null, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
    else if (!m_bRegular && !m_bLevenshtein)
        srResult = SearchWildcard(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
    else
        srResult = SearchRegularApprox(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);

    m_srResult = srResult;

    if (SearchResult::Error == m_srResult)
        return;

    // found?
    if (SearchResult::Found == m_srResult)
    {
        // memorize the position
        try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
        catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
        m_iterPreviousLocField = iterFieldCheck;
    }
    else
        // invalidate the "last discovery"
        InvalidatePreviousLoc();
}


void FmSearchEngine::OnSearchTerminated()
{
    if (!m_aProgressHandler.IsSet())
        return;

    FmSearchProgress aProgress;
    try
    {
        switch (m_srResult)
        {
            case SearchResult::Error :
                aProgress.aSearchState = FmSearchProgress::State::Error;
                break;
            case SearchResult::Found :
                aProgress.aSearchState = FmSearchProgress::State::Successful;
                aProgress.aBookmark = m_aPreviousLocBookmark;
                aProgress.nFieldIndex = m_iterPreviousLocField - m_arrUsedFields.begin();
                break;
            case SearchResult::NotFound :
                aProgress.aSearchState = FmSearchProgress::State::NothingFound;
                aProgress.aBookmark = m_xSearchCursor.getBookmark();
                break;
            case SearchResult::Cancelled :
                aProgress.aSearchState = FmSearchProgress::State::Canceled;
                aProgress.aBookmark = m_xSearchCursor.getBookmark();
                break;
        }
        aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }

    // by definition, the link must be thread-safe (I just require that),
    // so that I do not have to worry about such things here
    m_aProgressHandler.Call(&aProgress);

    m_bSearchingCurrently = false;
}


IMPL_LINK(FmSearchEngine, OnNewRecordCount, sal_Int32, theCounter, void)
{
    if (!m_aProgressHandler.IsSet())
        return;

    FmSearchProgress aProgress;
    aProgress.nCurrentRecord = theCounter;
    aProgress.aSearchState = FmSearchProgress::State::ProgressCounting;
    m_aProgressHandler.Call(&aProgress);
}


bool FmSearchEngine::CancelRequested()
{
    bool bReturn = m_bCancelAsynchRequest;
    return bReturn;
}


void FmSearchEngine::CancelSearch()
{
    m_bCancelAsynchRequest = true;
}


void FmSearchEngine::SwitchToContext(const Reference< css::sdbc::XResultSet > & xCursor, std::u16string_view sVisibleFields, const InterfaceArray& arrFields,
    sal_Int32 nFieldIndex)
{
    DBG_ASSERT(!m_bSearchingCurrently, "FmSearchEngine::SwitchToContext : please do not call while I'm searching !");
    if (m_bSearchingCurrently)
        return;

    m_xSearchCursor = xCursor;
    m_xOriginalIterator = xCursor;
    m_xClonedIterator = CursorWrapper(m_xOriginalIterator, true);

    fillControlTexts(arrFields);

    Init(sVisibleFields);
    RebuildUsedFields(nFieldIndex, true);
}


void FmSearchEngine::ImplStartNextSearch()
{
    m_bCancelAsynchRequest = false;
    m_bSearchingCurrently = true;

    SearchNextImpl();
    OnSearchTerminated();
}


void FmSearchEngine::SearchNext(const OUString& strExpression)
{
    m_strSearchExpression = strExpression;
    m_eSearchForType = SearchFor::String;
    ImplStartNextSearch();
}


void FmSearchEngine::SearchNextSpecial(bool _bSearchForNull)
{
    m_eSearchForType = _bSearchForNull ? SearchFor::Null : SearchFor::NotNull;
    ImplStartNextSearch();
}


void FmSearchEngine::StartOver(const OUString& strExpression)
{
    try
    {
        if (m_bForward)
            m_xSearchCursor.first();
        else
            m_xSearchCursor.last();
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
        return;
    }

    InvalidatePreviousLoc();
    SearchNext(strExpression);
}


void FmSearchEngine::StartOverSpecial(bool _bSearchForNull)
{
    try
    {
        if (m_bForward)
            m_xSearchCursor.first();
        else
            m_xSearchCursor.last();
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
        return;
    }

    InvalidatePreviousLoc();
    SearchNextSpecial(_bSearchForNull);
}


void FmSearchEngine::InvalidatePreviousLoc()
{
    m_aPreviousLocBookmark.clear();
    m_iterPreviousLocField = m_arrUsedFields.end();
}


void FmSearchEngine::RebuildUsedFields(sal_Int32 nFieldIndex, bool bForce)
{
    if (!bForce && (nFieldIndex == m_nCurrentFieldIndex))
        return;
    // (since I allow no change of the iterator from the outside, the same css::sdbcx::Index
    // also always means the same column, so I have nothing to do)

    DBG_ASSERT((nFieldIndex == -1) ||
               ((nFieldIndex >= 0) &&
                (o3tl::make_unsigned(nFieldIndex) < m_arrFieldMapping.size())),
            "FmSearchEngine::RebuildUsedFields : nFieldIndex is invalid!");
    // collect all fields I need to search through
    m_arrUsedFields.clear();
    if (nFieldIndex == -1)
    {
        Reference< css::container::XIndexAccess >  xFields;
        for (sal_Int32 i : m_arrFieldMapping)
        {
            Reference< css::sdbcx::XColumnsSupplier >  xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
            DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
            xFields.set(xSupplyCols->getColumns(), UNO_QUERY);
            BuildAndInsertFieldInfo(xFields, i);
        }
    }
    else
    {
        Reference< css::container::XIndexAccess >  xFields;
        Reference< css::sdbcx::XColumnsSupplier >  xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
        DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
        xFields.set (xSupplyCols->getColumns(), UNO_QUERY);
        BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[static_cast< size_t >(nFieldIndex)]);
    }

    m_nCurrentFieldIndex = nFieldIndex;
    // and of course I start the next search in a virgin state again
    InvalidatePreviousLoc();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=96 H=94 G=94

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge