predictivesearch/PcsAlgorithm/Algorithm2/src/CPcsAlgorithm2Helper.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 19 Aug 2010 09:41:07 +0300
branchRCL_3
changeset 58 d4f567ce2e7c
parent 21 b3431bff8c19
child 64 c1e8ba0c2b16
permissions -rw-r--r--
Revision: 201031 Kit: 201033

/*
* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Predictive Contact Search Algorithm 1 helper class
*
*/

// INCLUDES
#include <PbkGlobalSettingFactory.h>
#include "FindUtilChineseECE.h"
#include "CPcsAlgorithm2.h"
#include "CPcsAlgorithm2Helper.h"
#include "CPcsAlgorithm2Utils.h"
#include "CPcsDebug.h"
#include "CPcsCache.h"
#include "CPcsKeyMap.h"
#include "CPsData.h"
#include "CPsQuery.h"
#include "CPsQueryItem.h"
#include "CPsDataPluginInterface.h"
#include "CPcsPoolElement.h"


// ============================== MEMBER FUNCTIONS ============================

// ----------------------------------------------------------------------------
// CPcsAlgorithm2Helper::NewL
// Two Phase Construction
// ----------------------------------------------------------------------------
CPcsAlgorithm2Helper* CPcsAlgorithm2Helper::NewL(CPcsAlgorithm2* aAlgorithm)
    {
    PRINT ( _L("Enter CPcsAlgorithm2Helper::NewL") );

    CPcsAlgorithm2Helper* self = new (ELeave) CPcsAlgorithm2Helper();
    CleanupStack::PushL(self);
    self->ConstructL(aAlgorithm);
    CleanupStack::Pop(self);

    PRINT ( _L("End CPcsAlgorithm2Helper::NewL") );

    return self;
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2Helper::CPcsAlgorithm2Helper
// Two Phase Construction
// ----------------------------------------------------------------------------
CPcsAlgorithm2Helper::CPcsAlgorithm2Helper()
    {
    PRINT ( _L("Enter CPcsAlgorithm2Helper::CPcsAlgorithm2") );
    PRINT ( _L("End CPcsAlgorithm2Helper::CPcsAlgorithm2") );
    iMaxCount = 0;
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2Helper::ConstructL
// Two Phase Construction
// ----------------------------------------------------------------------------
void CPcsAlgorithm2Helper::ConstructL(CPcsAlgorithm2* aAlgorithm)
    {
    PRINT ( _L("Enter CPcsAlgorithm2Helper::ConstructL") );

    iAlgorithm = aAlgorithm;
    iKeyMap = iAlgorithm->GetKeyMap();

    iPbkSettings = PbkGlobalSettingFactory::CreatePersistentSettingL();
    iPbkSettings->ConnectL( MPbkGlobalSetting::EGeneralSettingCategory );
    iPbkSettings->RegisterObserverL( this );
    UpdateNameOrderL();
    
    PRINT ( _L("End CPcsAlgorithm2Helper::ConstructL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2Helper::~CPcsAlgorithm2Helper
// Destructor
// ----------------------------------------------------------------------------
CPcsAlgorithm2Helper::~CPcsAlgorithm2Helper()
    {
    PRINT ( _L("Enter CPcsAlgorithm2Helper::~CPcsAlgorithm2Helper") );
    iSearchResultsArr.ResetAndDestroy();
    PRINT ( _L("End CPcsAlgorithm2Helper::~CPcsAlgorithm2Helper") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2Helper::SearchSingleL
// Search function for input with both ITU-T and QWERTY mode
// ----------------------------------------------------------------------------
void CPcsAlgorithm2Helper::SearchSingleL(const CPsSettings& aSettings,
                                         CPsQuery& aPsQuery, 
                                         TBool aIsSearchInGroup,
                                         const RArray<TInt>& aContactsInGroup,
                                         RPointerArray<CPsData>& aSearchResults,
                                         RPointerArray<CPsPattern>& aSearchSeqs)
    {
    PRINT ( _L("Enter CPcsAlgorithm2Helper::SearchSingleL") );

    //__LATENCY_MARK ( _L("CPcsAlgorithm2Helper::SearchSingleL") );

    iMaxCount = aSettings.MaxResults();
    
    // Create filtering helper for the required sort type
    TSortType sortType = aSettings.GetSortType();
    CPcsAlgorithm2FilterHelper* filterHelper =
            CPcsAlgorithm2FilterHelper::NewL(sortType);
    CleanupStack::PushL( filterHelper );
    
    // Search from cache based on first character
    const CPsQueryItem& firstCharItem = aPsQuery.GetItemAtL(0);
    TInt cachePoolId = iKeyMap->PoolIdForCharacter( firstCharItem.Character(), firstCharItem.Mode() );

    // Reset the result set array for new search
    iSearchResultsArr.ResetAndDestroy();

    // Get the data stores
    RPointerArray<TDesC> dataStores;
    CleanupResetAndDestroyPushL( dataStores );
    aSettings.SearchUrisL(dataStores);

    // Get the required display fields from the client
    RArray<TInt> requiredDataFields;
    CleanupClosePushL( requiredDataFields );
    aSettings.DisplayFieldsL(requiredDataFields);

    // Perform search for each required data store
    RPointerArray<CPcsPoolElement> elements;
    CleanupClosePushL( elements );
    const TInt dataStoresCount = dataStores.Count();

    for (TInt dsIndex = 0; dsIndex < dataStoresCount; dsIndex++)
        {
        RPointerArray<CPsData>* temp = new (ELeave) RPointerArray<CPsData> ();
        CleanupStack::PushL( temp );
        iSearchResultsArr.AppendL( temp );
        CleanupStack::Pop( temp );

        // Get the contents for this data store
        TInt arrayIndex = iAlgorithm->GetCacheIndex(*(dataStores[dsIndex]));
        if (arrayIndex < 0)
            {
            continue;
            }
        CPcsCache* cache = iAlgorithm->GetCache(arrayIndex);
        cache->GetContactsForKeyL(cachePoolId, elements);

        // Get the supported data fields for this data store
        RArray<TInt> supportedDataFields;
        CleanupClosePushL( supportedDataFields );
        cache->GetDataFields(supportedDataFields);

        // Get the filtered data fields for this data store
        TUint8 filteredDataMatch = CPcsAlgorithm2Utils::FilterDataFieldsL(requiredDataFields,
                                                                          supportedDataFields);

        // Perform filtering
        FilterResultsSingleL(filterHelper,
                             elements,
                             aPsQuery,
                             filteredDataMatch,
                             aIsSearchInGroup,
                             aContactsInGroup);

        // If alphabetical sorting, get the results for this datastore
        if (sortType == EAlphabetical)
            {
            filterHelper->GetResults(*(iSearchResultsArr[dsIndex]));
            }

        elements.Reset();
        CleanupStack::PopAndDestroy( &supportedDataFields ); // Close
        }

    CleanupStack::PopAndDestroy( &elements );           // Close
    CleanupStack::PopAndDestroy( &requiredDataFields ); // Close
    CleanupStack::PopAndDestroy( &dataStores );         // ResetAndDestroy

    // If alphabetical sorting, merge the result sets of all datastores
    if (sortType == EAlphabetical)
        {
        // Form the complete searchResults array
        CPcsAlgorithm2Utils::FormCompleteSearchResultsL(iSearchResultsArr,
                                                        aSearchResults);
        }
    else
        {
        // Results are already sorted pattern based
        filterHelper->GetResults(aSearchResults);
        }

    // Get the sorted match sequence list
    filterHelper->GetPatternsL(aSearchSeqs);

    PRINT1 ( _L("Number of search results = %d"), aSearchResults.Count() );

    // Cleanup
    const TInt searchResultsArrCount = iSearchResultsArr.Count();
    for (TInt i = 0; i < searchResultsArrCount; i++)
        {
        iSearchResultsArr[i]->Reset();
        }
    iSearchResultsArr.ResetAndDestroy();

    CleanupStack::PopAndDestroy( filterHelper );

    //__LATENCY_MARKEND ( _L("CPcsAlgorithm2Helper::SearchSingleL") );

    PRINT ( _L("End CPcsAlgorithm2Helper::SearchSingleL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2Helper::SearchMatchSeqL
// Function to search matching sequences in the input text
// ----------------------------------------------------------------------------
void CPcsAlgorithm2Helper::SearchMatchSeqL(CPsQuery& aPsQuery,
                                           const TDesC& aData,
                                           RPointerArray<TDesC>& aMatchSet,
                                           RArray<TPsMatchLocation>& aMatchLocation )
    {
    PRINT ( _L("Enter CPcsAlgorithm2Helper::SearchMatchSeqL") );

    RArray<TInt> matchPos;
    CleanupClosePushL( matchPos );
    RArray<TInt> matchLen;
    CleanupClosePushL( matchLen );
    if ( iAlgorithm->FindUtilECE()->MatchRefineL( aData, aPsQuery, matchPos, matchLen, ETrue ) )
        {
        ASSERT( matchPos.Count() == matchLen.Count() );
        const TInt matchPosCount = matchPos.Count();
        for ( TInt i = 0 ; i < matchPosCount ; ++i )
            {
            TPsMatchLocation tempLocation;
    
            // check for directionality of the text
            TBool found(EFalse);
            TPtrC matchingPart = aData.Mid( matchPos[i], matchLen[i] );
            TBidiText::TDirectionality dir = TBidiText::TextDirectionality(matchingPart, &found);
    
            tempLocation.index = matchPos[i];
            tempLocation.length = matchLen[i];
            tempLocation.direction = dir;
    
            // Add the match location to the data structure array
            aMatchLocation.Append(tempLocation);
            
            // Add the matched sequence to set, not allowing duplicates
            CPcsAlgorithm2Utils::AppendMatchToSeqL( aMatchSet, matchingPart );
            }
        }
    CleanupStack::PopAndDestroy( &matchLen );
    CleanupStack::PopAndDestroy( &matchPos );

    PRINT ( _L("End CPcsAlgorithm2Helper::SearchMatchSeqL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2Helper::FilterResultsSingleL
// Subset search function
// ----------------------------------------------------------------------------
void CPcsAlgorithm2Helper::FilterResultsSingleL(CPcsAlgorithm2FilterHelper* aAlgorithmFilterHelper,
                                                RPointerArray<CPcsPoolElement>& aSearchSet,
                                                CPsQuery& aSearchQuery,
                                                TUint8 aFilteredDataMatch,
                                                TBool aIsSearchInGroup,
                                                const RArray<TInt>& aContactsInGroup)
    {
    PRINT ( _L("Enter CPcsAlgorithm2::FilterResultsSingleL") );

    TInt maxcount = 0;
    CFindUtilChineseECE* pFindUtilEce = iAlgorithm->FindUtilECE();

    // Assume that all the elements is aSearchSet are from the same database.
    // Get firstname index and lastname index for that database.
    // If both are found, then name fields are matched a bit differently compared
    // to rest of the fields.
    TInt fnIndex( KErrNotFound );
    TInt lnIndex( KErrNotFound );
    TBool fullNameSearch( EFalse );
    if ( aSearchSet.Count() )
        {
        TInt dbUriId = aSearchSet[0]->GetPsData()->UriId();
        CPcsCache* cache = iAlgorithm->GetCache( dbUriId );
        fnIndex = cache->GetFirstNameIndex();
        lnIndex = cache->GetLastNameIndex();

        // Ensure that firstname and lastname are among the fields to be searched
        TUint8 fnBitmask = 1 << fnIndex;
        TUint8 lnBitmask = 1 << lnIndex;
        if ( (aFilteredDataMatch & fnBitmask) && 
             (aFilteredDataMatch & lnBitmask) )
            {
            fullNameSearch = ETrue;
            // Remove firstname and lastname from the set of fields to search
            // so that they will not be searched twice.
            aFilteredDataMatch &= ~fnBitmask;
            aFilteredDataMatch &= ~lnBitmask;
            }
        }
    
    // Parse thru each search set elements and filter the results
    const TInt searchSetCount = aSearchSet.Count();
    for (TInt index = 0; index < searchSetCount; index++)
        {
        CPcsPoolElement* poolElement = static_cast<CPcsPoolElement*> (aSearchSet[index]);
        CPsData* psData = poolElement->GetPsData();
        psData->ClearDataMatches();

        // Skip the contact if we are doing a group search and contact doesn't belong to the group
        if ( aIsSearchInGroup && 
             aContactsInGroup.Find( psData->Id() ) == KErrNotFound )
            {
            continue;
            }
        
        // Buffer for matched character sequence. Currently, we don't return
        // accurate pattern but just first character from the matching point.
        // Current clients don't actually use this data for anything.
        TBuf<1> matchingData;
        
        TBool matched = EFalse;

        // Parse thru each data field and filter the results
        // -------------------------------------------------
        
        // Name fields are handled separately to enable searching with query like
        // "LastnameFirstname". Searching fullname by query string 
        // only for the pool elements matching with the firstname or lastname
        if ( fullNameSearch && ( poolElement->IsDataMatch(fnIndex) || 
            poolElement->IsDataMatch(lnIndex) ))
            {
            HBufC* fullName = CreateNameBufLC( *psData, fnIndex, lnIndex );

            // FindUtil can take care of matching queries like "Firstname", "Lastname", 
            // and "LastnameFirstname".
            matched = pFindUtilEce->MatchRefineL( *fullName, aSearchQuery );
            if (matched)
                {
                matchingData = fullName->Left(1);
                psData->SetDataMatch( fnIndex );
                psData->SetDataMatch( lnIndex );
                }
            
            CleanupStack::PopAndDestroy( fullName );
            }

        // Find from the rest of the fields if no match found so far. 
        // Name fields are already removed from aFilteredDataMatch if we did separate full name search.
        const TInt dataElementCount = psData->DataElementCount();
        for ( TInt dataIndex = 0; dataIndex < dataElementCount && !matched ; dataIndex++ )
            {
            // Filter off data fields not required in search
            TUint8 bitIndex = 1 << dataIndex;
            TUint8 filter = bitIndex & aFilteredDataMatch;
            if ( filter == 0x0 )
                {
                // Move to next data
                continue;
                }

            if ( poolElement->IsDataMatch(dataIndex) )
                {
                TPtrC fieldData( *psData->Data(dataIndex) );
                matched = pFindUtilEce->MatchRefineL( fieldData, aSearchQuery );
                if (matched)
                    {
                    matchingData = fieldData.Left(1);
                    psData->SetDataMatch( dataIndex );
                    }
                }
            }


        // Add to results if match is found
        if ( matched )
            {
            RPointerArray<TDesC> tempMatchSeq;
            CleanupClosePushL( tempMatchSeq );

            // Wrap matched character sequence to array.
            matchingData.UpperCase();
            tempMatchSeq.AppendL(&matchingData);
            
            // Add the result
            aAlgorithmFilterHelper->AddL( psData, tempMatchSeq );
            maxcount++;
            
            // Cleanup the match sequence array as 
            // they are stored in pattern details structure
            CleanupStack::PopAndDestroy( &tempMatchSeq ); // Close
            
            // TODO: Match seqs could be extracted from actual
            //       match locations by using the other overload of
            //       CFindUtilChineseECE::MatchRefineL().
            //       Currently, match seq data is not used by clients.
            }
        
          if ( iMaxCount != -1 && maxcount > iMaxCount )
              {
              return;
              }
        }

    PRINT ( _L("End CPcsAlgorithm2Helper::FilterResultsSingleL") );
    }

// ----------------------------------------------------------------------------
// CPcsAlgorithm2Helper::SortSearchSeqsL()
// 
// ----------------------------------------------------------------------------
void CPcsAlgorithm2Helper::SortSearchSeqsL(RPointerArray<TDesC>& aSearchSeqs)
    {
    // Sort the search seqs
    TLinearOrder<TDesC> rule( CPcsAlgorithm2Utils::MyCompareC );
    aSearchSeqs.Sort(rule);
    }

// ---------------------------------------------------------------------------
// CPcsAlgorithm2Helper::CreteNameBufLC
// Update name order according to Phonebook setting
// ---------------------------------------------------------------------------
HBufC* CPcsAlgorithm2Helper::CreateNameBufLC( const CPsData& aContactData, 
        TInt aFirstNameIndex, TInt aLastNameIndex ) const
    {
    const TDesC& firstName( *aContactData.Data(aFirstNameIndex) );
    const TDesC& lastName( *aContactData.Data(aLastNameIndex) );
    CFindUtilChineseECE* pFindUtilEce = iAlgorithm->FindUtilECE();
    HBufC* fullName = NULL;
    
    if ( pFindUtilEce->IsChineseWordIncluded( lastName ) || 
        pFindUtilEce->IsChineseWordIncluded( firstName ) )
        {
        fullName = HBufC::NewLC( lastName.Length() + firstName.Length()  + 1 );
        TPtr fullNamePtr = fullName->Des();
    
        // Form the full name according the Phonebook name order setting. Typically,
        // the order is always lastname-firstname in Chinese variants. However, at least
        // currently it is possible to change this from Contacts app if UI language has
        // been set to English.
        if ( iNameOrder == ELastnameFirstname )
            {
            fullNamePtr.Append( lastName );
            //Holds the first char of first name of a contact
            TBuf<10> firstChar;   
            firstChar.Zero();
            
            if(firstName.Length())
                {
                firstChar.Append(firstName[0]);   
                // There is no space between LastName and FirstName in Chinese Name
                // except that the firstChar of FirstName isn't Chinese character
                if ( !pFindUtilEce->IsChineseWordIncluded( firstChar ) )
                    {
                    fullNamePtr.Append( KSpace );
                    }
                fullNamePtr.Append( firstName ); 
                }
            }
        else
            {
            fullNamePtr.Append( firstName );
            //Holds the first char of last name of a contact
            TBuf<10> firstChar;   
            firstChar.Zero();
            
            if(lastName.Length())
                {
                firstChar.Append(lastName[0]);
                // There is no space between LastName and FirstName in Chinese Name
                // except that the firstChar of Lastname isn't Chinese character  
                if ( !pFindUtilEce->IsChineseWordIncluded( firstChar ) )
                    {
                    fullNamePtr.Append( KSpace );
                    }
                fullNamePtr.Append( lastName ); 
                }
            }
        }
    else
        {
        fullName = HBufC::NewLC( lastName.Length() + firstName.Length() + 1 );
        TPtr fullNamePtr = fullName->Des();
    
        // Form the full name according the Phonebook name order setting. Typically,
        // the order is always lastname-firstname in Chinese variants. However, at least
        // currently it is possible to change this from Contacts app if UI language has
        // been set to English.
        if ( iNameOrder == ELastnameFirstname )
            {
            fullNamePtr.Append( lastName );
            fullNamePtr.Append( KSpace );
            fullNamePtr.Append( firstName );
            }
        else
            {
            fullNamePtr.Append( firstName );
            fullNamePtr.Append( KSpace );
            fullNamePtr.Append( lastName );
            }
        }
    
    return fullName;
    }

// ---------------------------------------------------------------------------
// CPcsAlgorithm2Helper::UpdateNameOrderL
// Update name order according to Phonebook setting
// ---------------------------------------------------------------------------
void CPcsAlgorithm2Helper::UpdateNameOrderL()
    {
    /*
    * Phonebook name ordering flag, integer value, possible values:
    * 0: name order Lastname Firstname
    * 1: name order Firstname Lastname
    * 2: name order undefined
    */
    TInt nameOrderSetting;
    iPbkSettings->Get( MPbkGlobalSetting::ENameOrdering, nameOrderSetting );
    
    switch ( nameOrderSetting )
        {
        case 0:
            {
            iNameOrder = ELastnameFirstname;
            break;
            }
        case 1:
            {
            iNameOrder = EFirstnameLastname;
            break;
            }
        case 2:
        default:
            {
            // Decide name order based on UI language: lastname-firstname
            // for Chinese, firstname-lastname for the rest of languages.
            TLanguage uiLang = User::Language();
            if ( uiLang == ELangPrcChinese || 
                 uiLang == ELangHongKongChinese ||
                 uiLang == ELangTaiwanChinese )
                {
                iNameOrder = ELastnameFirstname;
                }
            else
                {
                iNameOrder = EFirstnameLastname;
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// CPcsAlgorithm2Helper::SettingChangedL
// From MPbkGlobalSettingObserver
// ---------------------------------------------------------------------------
void CPcsAlgorithm2Helper::SettingChangedL( MPbkGlobalSetting::TPbkGlobalSetting aKey )
    {
    if ( aKey == MPbkGlobalSetting::ENameOrdering )
        {
        UpdateNameOrderL();
        }
    }

// End of file