phonebookengines/VirtualPhonebook/VPbkEng/src/CVPbkPhoneNumberMatchStrategy.cpp
author andy simpson <andrews@symbian.org>
Thu, 02 Sep 2010 15:35:50 +0100
branchRCL_3
changeset 64 c1e8ba0c2b16
parent 58 d4f567ce2e7c
parent 63 f4a778e096c2
permissions -rw-r--r--
Merge after bad RCL_3 drop reverted

/*
* Copyright (c) 2005-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:  A high level class for matching phone numbers from stores.
*
*/


// INCLUDES
#include <CVPbkPhoneNumberMatchStrategy.h>

#include <CVPbkContactManager.h>
#include <CVPbkContactLinkArray.h>
#include <MVPbkContactOperation.h>
#include <MVPbkContactStoreList.h>
#include <MVPbkContactStore.h>
#include <MVPbkContactStoreProperties.h>
#include <MVPbkContactLink.h>
#include <MVPbkStoreContact.h>
#include <MVPbkSingleContactOperationObserver.h>
#include <RLocalizedResourceFile.h>
#include <VPbkDataCaging.hrh>
#include <VPbkEng.rsg>
#include <VPbkFieldTypeSelectors.rsg>
#include <CVPbkFieldTypeSelector.h>
#include <CVPbkFieldFilter.h>
#include <barsread.h>
#include <MVPbkContactFieldTextData.h>
#include <CVPbkContactStoreUriArray.h>
#include <centralrepository.h>
#include <VPbkStoreUriLiterals.h>

#include "CVPbkPhoneNumberSequentialMatchStrategy.h"
#include "CVPbkPhoneNumberParallelMatchStrategy.h"
#include "CVPbkETelCntConverter.h"

#include <cntdb.h>
#include <ecom/ecom.h>

#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <cntphonenumparser.h>
#endif
// CONSTANTS
// Unnamed namespace for local definitions
namespace {
// --------------------------------------------------------------------------
// Phonebook Central Repository UIDs
// Copied from sf\app\contacts\phonebookui\Phonebook2\inc\Phonebook2InternalCRKeys.h
// --------------------------------------------------------------------------
//
const TUint32 KCRUidPhonebookStoreConfiguration             = 0x1020727f;
const TUint32 KPhonebookCurrentConfigurationPartialKey      = 0x00000100;
const TUint32 KPhonebookCurrentConfigurationMask            = 0xffffff00;

const TInt KInitialStoreUriSize = 22; // length of KVPbkDefaultCntDbURI
const TInt KMagicNumber = -1;
} // namespace

NONSHARABLE_CLASS(CVPbkPhoneNumberMatchStrategyImpl) :
        public CActive,
        public MVPbkContactFindObserver,
        public MVPbkSingleContactOperationObserver
    {
    public: // Construction
        static CVPbkPhoneNumberMatchStrategyImpl* NewL(
                CVPbkPhoneNumberMatchStrategy& aParent,
                const CVPbkPhoneNumberMatchStrategy::TConfig& aConfig,
                CVPbkContactManager& aContactManager,
                MVPbkContactFindObserver& aObserver);
        ~CVPbkPhoneNumberMatchStrategyImpl();

    public: // Interface
        void MatchL(const TDesC& aPhoneNumber);
        TInt MaxMatchDigits() const;
        TArray<MVPbkContactStore*> StoresToMatch() const;
        TBool IsSimStore( const MVPbkContactStore& aStore );
        
    private: // From CActive
        void RunL();
        void DoCancel();
        TInt RunError(TInt aError);

    private: // From MVPbkContactFindObserver
        void FindCompleteL(MVPbkContactLinkArray* aResults);
        void FindFailed(TInt aError);

    private: // From MVPbkSingleContactOperationObserver
        void VPbkSingleContactOperationComplete(
                MVPbkContactOperationBase& aOperation,
                MVPbkStoreContact* aContact);
        void VPbkSingleContactOperationFailed(
                MVPbkContactOperationBase& aOperation,
                TInt aError);
   
    private:
        /// Phone number types
        enum TNumberType { ENotInitialized, EUnknown, EDigit, EPlus, EOneZero, ETwoZeros };
        
    private: // Implementation
        CVPbkPhoneNumberMatchStrategyImpl(CVPbkPhoneNumberMatchStrategy& aParent);
        void ConstructL(
                const CVPbkPhoneNumberMatchStrategy::TConfig& aConfig,
                CVPbkContactManager& aContactManager,
                MVPbkContactFindObserver& aObserver);

        /**
         * Searches for a store that has given URI from the list of
         * stores that the contact manager has.
         * @param aUriPtr URI of the store to search for.
         * @return The store with aUriPtr, or NULL if store was not found.
         */
        MVPbkContactStore* FindStoreL(
                const TVPbkContactStoreUriPtr& aUriPtr);

        /**
         * Issues new request to be handled in RunL.
         */
        void IssueRequest();

        MVPbkContactLink* IsValidResultLC(MVPbkStoreContact* aContact);

        /**
         * Creates name tokens array
         * @param aContact which is checking, RPointerArray reference
         */
        void CreateNameTokensArrayL( MVPbkStoreContact* aContact, RPointerArray <HBufC>& aNameTokensArray );
        
        /**
         * Check if contact already exists in results array
         * @param aContact which is checking
         * @return True if contact exist or if array was empty.
         */
        TBool CheckContactDuplicationL( MVPbkStoreContact* aContact );
        
        /**
         * Gets contact's field value
         * @param aContact to get field's value from
         * @param aFieldType: EVPbkVersitNameN for Last Name 
         * EVPbkVersitNameFN for First Name
         * @return Pointer descriptor with field's value.
         */
        TPtrC NameFieldValueL( MVPbkStoreContact* aContact, TVPbkFieldTypeName aFieldType );
        
        TBool ValidateBestMatchingRulesL( const TDesC& aNumber );
        TBool CheckBestMatchingRules( const TDesC& aNumberA, TNumberType aNumberAType,
                const TDesC& aNumberB, TNumberType aNumberBType  );
        TInt FormatAndCheckNumberType( TDes& aNumber );
        
        /**
         * Reads current store configuration from central repositiry
         * @return Array of stores or NULL if error during reading form cenrep.
        */
        CVPbkContactStoreUriArray* GetCurrentStoreConfigurationL();
        
        /**
         * If there is in the results at least one contact
         * from currently used stores in Phonebook2 it removes from results 
         * contacts from other stores
        */
        void RefineDuplicatedNumbersL();
        
        /**
         * Load number parser plugin.
        */
        void LoadNumberParserPluginL();
        
    private: // Data
        CVPbkPhoneNumberMatchStrategy& iParent;
        /// Ref: The contact manager instance to be used for searching.
        CVPbkContactManager* iContactManager;
        /// Ref: Observer for the searching process.
        MVPbkContactFindObserver* iObserver;
        /// Own: The find operation that is currently ongoing.
        MVPbkContactOperationBase* iOperation;
        /// Maximum number of matched digits.
        TInt iMaxMatchDigits;
        /// Flags to configure matching process, @see TVPbkPhoneNumberMatchFlags
        TUint32 iMatchFlags;

        /// Own: Array of stores that are used in matching.
        RPointerArray<MVPbkContactStore> iStoresToMatch;
        /// Own: Phone number that is being matched.
        HBufC* iPhoneNumber;
        /// Own: Intermediate results of the matching process.
        CVPbkContactLinkArray* iWorkingResults;
        /// Own: Final results of the matching process.
        CVPbkContactLinkArray* iResults;
        /// iWorkingResults index of current contact retrieval
        TInt iCurrentContact;

        /// Own: Currently retrieved contact
        MVPbkStoreContact* iStoreContact;
        /// Own: Field type selector
        CVPbkFieldTypeSelector* iFieldTypeSelector;
        /// Own: A filtered and sorted collection of Virtual Phonebook contact fields.
        CVPbkFieldFilter* iFieldFilter;

        /// Active object states
        enum TState { EMatch, ERemoveDuplicates, ERefineSearch, EComplete };
        /// Active object current state
        TState iState;
        
        /// Own: First Name field type selector
        CVPbkFieldTypeSelector* iFirstNameSelector;
        /// Own: Last Name field type selector
        CVPbkFieldTypeSelector* iLastNameSelector;
        /// Own: Array of tokens gotten from first and last name fields of first matched contact
        RPointerArray <HBufC> iNameTokensArray;
        /// Own: Array of tokens gotten from first and last name fields of contact
        RPointerArray <HBufC> iTempNameTokensArray;
        /// Indicates if all contact in iResult are the same
        TBool iDoubledContacts;
        /// type of iPhoneNumber
        TNumberType iPhoneNumberType;
        // Own: parser
        CContactPhoneNumberParser* iParser;
    };

CVPbkPhoneNumberMatchStrategyImpl::CVPbkPhoneNumberMatchStrategyImpl(
        CVPbkPhoneNumberMatchStrategy& aParent) :
    CActive(CActive::EPriorityIdle),
    iParent(aParent)
    {
    CActiveScheduler::Add(this);
    }

inline void CVPbkPhoneNumberMatchStrategyImpl::ConstructL(
        const CVPbkPhoneNumberMatchStrategy::TConfig& aConfig,
        CVPbkContactManager& aContactManager,
        MVPbkContactFindObserver& aObserver)
    {
    iContactManager = &aContactManager;
    iObserver = &aObserver;

    iMaxMatchDigits = aConfig.iMaxMatchDigits;
    iMatchFlags = aConfig.iMatchFlags;

    const TInt uriCount = aConfig.iUriPriorities.Count();
    for (TInt i = 0; i < uriCount; ++i)
        {
        MVPbkContactStore* store = FindStoreL(aConfig.iUriPriorities[i]);
        if (store)
            {
            iStoresToMatch.AppendL(store);
            }
        }

    VPbkEngUtils::RLocalizedResourceFile resFile;
    resFile.OpenLC(iContactManager->FsSession(),
                   KVPbkRomFileDrive,
                   KDC_RESOURCE_FILES_DIR,
                   KVPbkFieldTypeSelectorsResFileName);
    HBufC8* selectorBuf = resFile.AllocReadLC(R_VPBK_PHONE_NUMBER_SELECTOR);
    TResourceReader resReader;
    resReader.SetBuffer(selectorBuf);

    iFieldTypeSelector = CVPbkFieldTypeSelector::NewL(resReader, iContactManager->FieldTypes());

    CleanupStack::PopAndDestroy( selectorBuf );
    
    if ( iMatchFlags & CVPbkPhoneNumberMatchStrategy::EVPbkDuplicatedContactsMatchFlag )
    	{ 
	    HBufC8* firstNameSelectorBuf = resFile.AllocReadLC( R_VPBK_FIRST_NAME_SELECTOR );
	    resReader.SetBuffer( firstNameSelectorBuf );
	    iFirstNameSelector = CVPbkFieldTypeSelector::NewL( resReader, iContactManager->FieldTypes() );
	    CleanupStack::PopAndDestroy( firstNameSelectorBuf );
	
	    HBufC8* lastNameSelectorBuf = resFile.AllocReadLC( R_VPBK_LAST_NAME_SELECTOR );
	    resReader.SetBuffer( lastNameSelectorBuf );
	    iLastNameSelector = CVPbkFieldTypeSelector::NewL( resReader, iContactManager->FieldTypes() );
	    CleanupStack::PopAndDestroy( lastNameSelectorBuf );
    	}

    CleanupStack::PopAndDestroy( &resFile );
    
    LoadNumberParserPluginL();
    }

CVPbkPhoneNumberMatchStrategyImpl* CVPbkPhoneNumberMatchStrategyImpl::NewL(
        CVPbkPhoneNumberMatchStrategy& aParent,
        const CVPbkPhoneNumberMatchStrategy::TConfig& aConfig,
        CVPbkContactManager& aContactManager,
        MVPbkContactFindObserver& aObserver)
    {
    CVPbkPhoneNumberMatchStrategyImpl* self =
            new(ELeave) CVPbkPhoneNumberMatchStrategyImpl(aParent);
    CleanupStack::PushL(self);
    self->ConstructL(aConfig, aContactManager, aObserver);
    CleanupStack::Pop(self);
    return self;
    }

CVPbkPhoneNumberMatchStrategyImpl::~CVPbkPhoneNumberMatchStrategyImpl()
    {
    Cancel();
    
    delete iFieldFilter;
    delete iFieldTypeSelector;
    delete iWorkingResults;
    delete iResults;
    delete iStoreContact;
    delete iPhoneNumber;
    delete iOperation;
    delete iFirstNameSelector;
    delete iLastNameSelector;
    iNameTokensArray.ResetAndDestroy();
    iTempNameTokensArray.ResetAndDestroy();
    iStoresToMatch.Close();
    delete iParser;
    REComSession::FinalClose();
    }

void CVPbkPhoneNumberMatchStrategyImpl::MatchL(const TDesC& aPhoneNumber)
    {
    HBufC* phoneNumber = aPhoneNumber.AllocL();
    delete iPhoneNumber;
    iPhoneNumber = phoneNumber;
    iPhoneNumberType = ENotInitialized;
    
    if ( iWorkingResults )
        {
        iWorkingResults->ResetAndDestroy();
        }                
    if ( iResults )
        {
        iResults->ResetAndDestroy();
        }                
    iCurrentContact = KMagicNumber;
    
    if ( iMatchFlags & CVPbkPhoneNumberMatchStrategy::EVPbkDuplicatedContactsMatchFlag )
    	{
    	iDoubledContacts = ETrue;
    	}
    else
    	{
    	iDoubledContacts = EFalse;
    	}
    iState = EMatch;
    IssueRequest();
    }


void CVPbkPhoneNumberMatchStrategyImpl::IssueRequest()
    {
    TRequestStatus* status = &iStatus;
    User::RequestComplete(status, KErrNone);
    SetActive();
    }

void CVPbkPhoneNumberMatchStrategyImpl::RunL()
    {
    switch (iState)
        {
        case EMatch:
            {
            MVPbkContactOperation* operation = iParent.CreateFindOperationLC(*iPhoneNumber);
            if (operation)
                {
                operation->StartL();
                CleanupStack::Pop(); // operation
                delete iOperation;
                iOperation = operation;
                }
            else
                {
                // all needed operations are done if any
                iState = ERemoveDuplicates;
                IssueRequest();
                }
            break;
            }
        case ERemoveDuplicates:
            {
            const TInt count = iWorkingResults ? iWorkingResults->Count() : 0;
            CVPbkContactLinkArray* results = CVPbkContactLinkArray::NewLC();
            for (TInt i = 0; i < count; ++i)
                {
                MVPbkContactLink* link = iWorkingResults->At(i).CloneLC();
                if (results->Find(*link) == KErrNotFound)
                    {
                    results->AppendL(link);
                    CleanupStack::Pop(); // link
                    }
                else
                    {
                    CleanupStack::PopAndDestroy(); // link
                    }
                }
            CleanupStack::Pop(results);
            delete iWorkingResults;
            iWorkingResults = results;
            iState = ERefineSearch;
            iCurrentContact = 0;
            IssueRequest();
            break;
            }
        case ERefineSearch:
            {
            if (!iResults)
                {
                iResults = CVPbkContactLinkArray::NewL();
                }
            
            MVPbkContactLink* result = IsValidResultLC( iStoreContact );
            if ( result )
                {
                iResults->AppendL( result );
                CleanupStack::Pop(); // MVPbkContactLink
                
                if ( iDoubledContacts )
                    {
                    iDoubledContacts = CheckContactDuplicationL( iStoreContact );
                    }
                }

            delete iStoreContact;
            iStoreContact = NULL;
                
            const TInt count = iWorkingResults ? iWorkingResults->Count() : 0;
            if (iCurrentContact < count &&                 
                !( iResults->Count() > 0 && 
                        (iMatchFlags & CVPbkPhoneNumberMatchStrategy::EVPbkStopOnFirstMatchFlag)))
                {
                const MVPbkContactLink& link = iWorkingResults->At(iCurrentContact);
                delete iOperation;
                iOperation = NULL;
                iOperation = iContactManager->RetrieveContactL(link, *this);
                ++iCurrentContact;
                }
            else
                {
                iState = EComplete;
                IssueRequest();
                }
            break;
            }
        case EComplete:
            {
            iNameTokensArray.ResetAndDestroy();
            iTempNameTokensArray.ResetAndDestroy();
            if ( iDoubledContacts && iResults->Count() )
                {
                CVPbkContactLinkArray* results = CVPbkContactLinkArray::NewLC();
                MVPbkContactLink* link = iResults->At(0).CloneLC(); // first element
                results->AppendL( link );
                CleanupStack::Pop( 2, results ); // results, link
                delete iResults;
                iResults = results;
                }

            if ( iResults->Count() > 1 )
                {
                RefineDuplicatedNumbersL();
                }

            CVPbkContactLinkArray* results = iResults;
            iResults = NULL;            
            iObserver->FindCompleteL( results );
            break;
            }
        default:
            {
            // This case should not be possible
            User::Leave( KErrArgument );
            break;
            }
        }
    }

void CVPbkPhoneNumberMatchStrategyImpl::DoCancel()
    {
    }

TInt CVPbkPhoneNumberMatchStrategyImpl::RunError(TInt aError)
    {
    iObserver->FindFailed(aError);
    return KErrNone;
    }

inline MVPbkContactStore* CVPbkPhoneNumberMatchStrategyImpl::FindStoreL(
        const TVPbkContactStoreUriPtr& aUriPtr)
    {
    const TInt storeCount = iContactManager->ContactStoresL().Count();
    for (TInt i = 0; i < storeCount; ++i)
        {
        MVPbkContactStore& store = iContactManager->ContactStoresL().At(i);
        if (store.StoreProperties().Uri().Compare(
                    aUriPtr,
                    TVPbkContactStoreUriPtr::EContactStoreUriAllComponents) == 0)
            {
            return &store;
            }
        }
    return NULL;
    }

void CVPbkPhoneNumberMatchStrategyImpl::FindCompleteL(MVPbkContactLinkArray* aResults)
    {
    // This function called by operation which is created in 
    // iParent.CreateFindOperationLC
    if (aResults)
        {
        CleanupDeletePushL( aResults ); // Take ownership
        
        if (!iWorkingResults)
            {
            iWorkingResults = CVPbkContactLinkArray::NewL();
            }
        const TInt count = aResults->Count();
        for (TInt i = 0; i < count; ++i)
            {
            MVPbkContactLink* link = aResults->At(i).CloneLC();
            iWorkingResults->AppendL(link);
            CleanupStack::Pop(); // link
            }

        CleanupStack::PopAndDestroy(); // aResults
        }
    IssueRequest();
    }

void CVPbkPhoneNumberMatchStrategyImpl::FindFailed(TInt aError)
    {
    iObserver->FindFailed(aError);
    }

void CVPbkPhoneNumberMatchStrategyImpl::VPbkSingleContactOperationComplete(
        MVPbkContactOperationBase& aOperation,
        MVPbkStoreContact* aContact)
    {
    if (iOperation == &aOperation)
        {
        delete iOperation;
        iOperation = NULL;

        iStoreContact = aContact;
        IssueRequest();
        }
    }

void CVPbkPhoneNumberMatchStrategyImpl::VPbkSingleContactOperationFailed(
        MVPbkContactOperationBase& aOperation,
        TInt aError)
    {
    if (iOperation == &aOperation)
        {
        delete iOperation;
        iOperation = NULL;

        iObserver->FindFailed(aError);
        }
    }

TInt CVPbkPhoneNumberMatchStrategyImpl::MaxMatchDigits() const
    {
    return iMaxMatchDigits;
    }

TArray<MVPbkContactStore*> CVPbkPhoneNumberMatchStrategyImpl::StoresToMatch() const
    {
    return iStoresToMatch.Array();
    }

MVPbkContactLink* CVPbkPhoneNumberMatchStrategyImpl::IsValidResultLC(
        MVPbkStoreContact* aContact)
    {
    MVPbkContactLink* result = NULL;

    if (!(iMatchFlags & CVPbkPhoneNumberMatchStrategy::EVPbkExactMatchFlag)
            && !(iMatchFlags & CVPbkPhoneNumberMatchStrategy::EVPbkBestMatchingFlag))
        {
        // If exact match is not needed we will accept the contact as valid
        // if it is not NULL
        if ( aContact )
            {
            result = aContact->CreateLinkLC();
            }        
        }
    else if (aContact)
        {
        TPtrC matchedNumber = iPhoneNumber->Des().
                Right(Min(iPhoneNumber->Length(), iMaxMatchDigits));

        CVPbkFieldFilter::TConfig config(aContact->Fields(), iFieldTypeSelector);
        if (!iFieldFilter)
            {
            iFieldFilter = CVPbkFieldFilter::NewL(config);
            }
        else
            {
            iFieldFilter->ResetL(config);
            }
        const TInt count = iFieldFilter->FieldCount();
        for (TInt i = 0; i < count; ++i)
            {
            MVPbkContactFieldData& data = iFieldFilter->FieldAt(i).FieldData();
            if (data.DataType() == EVPbkFieldStorageTypeText)
                {
                const TDesC& dataText = MVPbkContactFieldTextData::Cast(data).Text();
                if (iMatchFlags & CVPbkPhoneNumberMatchStrategy::EVPbkExactMatchFlag)
                    {
                    TPtrC dataTextPtr = dataText.Right(Min(dataText.Length(), iMaxMatchDigits));
                    if (dataTextPtr == matchedNumber)
                        {
                        result = iFieldFilter->FieldAt(i).CreateLinkLC();
                        break;
                        }
                    }
                else if (iMatchFlags & CVPbkPhoneNumberMatchStrategy::EVPbkBestMatchingFlag
                            && ValidateBestMatchingRulesL(dataText))
                    {
                    result = aContact->CreateLinkLC();
                    break;
                    }
                }
            }
        }
    return result;
    }

TBool CVPbkPhoneNumberMatchStrategyImpl::CheckContactDuplicationL(
        MVPbkStoreContact* aContact )
    {

    if ( !aContact )
        {
        return ETrue;
        }
    
    if ( aContact && iCurrentContact == 1 )
        {
        CreateNameTokensArrayL( aContact, iNameTokensArray );
        return ETrue;
        }
    else 
        {
        iTempNameTokensArray.ResetAndDestroy();
        CreateNameTokensArrayL( aContact, iTempNameTokensArray );
        TInt count = iNameTokensArray.Count();
        if ( iNameTokensArray.Count() != iTempNameTokensArray.Count() )
        	{
        	return EFalse;
        	}
        else
        	{
        	TInt result = 0;
        	for ( TInt i = 0; i < count; i++ )
        		{
        		HBufC* token = iNameTokensArray[i];
        		TInt tempCount = iTempNameTokensArray.Count();
            	for ( TInt j = 0; j < tempCount; j++ )
            		{
            		result = token->Compare( *( iTempNameTokensArray[j] ) );
                	if ( !result )
                		{
                		HBufC* removedToken = iTempNameTokensArray[j];
                		iTempNameTokensArray.Remove( j );
                		delete removedToken;
                		break;
                		}
            		}
            	if ( result )
            		{
            		return EFalse;
            		}
        		}
        	}
        return ETrue;
        }
    }

void CVPbkPhoneNumberMatchStrategyImpl::CreateNameTokensArrayL( MVPbkStoreContact* aContact,
										RPointerArray <HBufC>& aNameTokensArray )
	{
    TPtrC firstName = NameFieldValueL( aContact, EVPbkVersitNameFN );
    TLex lexer;
    lexer.Assign( firstName );
    
    TPtrC ptr;
    TInt len = 0;
    while( ETrue )
        {
        ptr.Set( lexer.NextToken() );
        
        len = ptr.Length();
        if ( len )
            {
            HBufC* token = ptr.AllocLC();
            aNameTokensArray.AppendL( token );
            CleanupStack::Pop( token );
            }
        else
            {
            break;
            }
        } 

    TPtrC lastName = NameFieldValueL( aContact, EVPbkVersitNameN );

    lexer.Assign( lastName );
    
    len = 0;
    while( ETrue )
        {
        ptr.Set( lexer.NextToken() );
        
        len = ptr.Length();
        if ( len )
            {
            HBufC* token = ptr.AllocLC();
            aNameTokensArray.AppendL( token );
            CleanupStack::Pop( token );
            }
        else
            {
            break;
            }
        }
	}

TPtrC CVPbkPhoneNumberMatchStrategyImpl::NameFieldValueL( 
        MVPbkStoreContact* aContact, TVPbkFieldTypeName aFieldType )
    {
    if ( aContact )
        {
        CVPbkFieldFilter::TConfig config( aContact->Fields() );
        if ( aFieldType == EVPbkVersitNameFN )
            {
            config.iFieldSelector = iFirstNameSelector;
            }
        else if ( aFieldType == EVPbkVersitNameN )
            {
            config.iFieldSelector = iLastNameSelector;
            }
        else
            {
            return KNullDesC();
            }
        
        if (!iFieldFilter)
            {
            iFieldFilter = CVPbkFieldFilter::NewL(config);
            }
        else
            {
            iFieldFilter->ResetL(config);
            }
        const TInt count = iFieldFilter->FieldCount();
        for (TInt i = 0; i < count; ++i)
            {
            MVPbkContactFieldData& data = iFieldFilter->FieldAt(i).FieldData();
            if (data.DataType() == EVPbkFieldStorageTypeText)
                {
                return  MVPbkContactFieldTextData::Cast(data).Text();
                }
            }
        return KNullDesC();
        }
    else
        {
        return KNullDesC();
        }
    }

// Removes non-digit chars except plus form the beginning
// Checks if number matches to one of defined types
//
TInt CVPbkPhoneNumberMatchStrategyImpl::FormatAndCheckNumberType( TDes& aNumber )
    {
    _LIT( KOneZeroPattern, "0*" );
    _LIT( KTwoZerosPattern, "00*" );
    _LIT( KPlusPattern, "+*" );
    _LIT( KPlusString, "+" );
    const TChar KPlus = TChar('+');
    const TChar KZero = TChar('0');
    const TChar KAsterisk = TChar('*');
    const TChar KHash = TChar('#');
    
    HBufC* numberBuf = HBufC::NewL( aNumber.Length() );
    TPtr number = numberBuf->Des();
    if ( iParser )
        {
        iParser->ExtractRawNumber( aNumber, number );
        }
    TInt pos = aNumber.Find( number );
    
    if ( pos > 0 && aNumber[pos-1] == KPlus )
        {
        number.Insert( 0, KPlusString );
        }
    
    if ( number.Length() > 0)
        {
        aNumber.Copy( number );
        }
    delete numberBuf;
    
	TInt format;
	
    if ( !aNumber.Match( KTwoZerosPattern ) && aNumber.Length() > 2 && aNumber[2] != KZero )
        {
        format = ETwoZeros;
        }
    else if ( !aNumber.Match( KOneZeroPattern )&& aNumber.Length() > 1 && aNumber[1] != KZero )
        {
        format = EOneZero;
        }
    else if ( !aNumber.Match( KPlusPattern ) && aNumber.Length() > 1 && aNumber[1] != KZero )
        {
        format = EPlus;
        }
    else if ( aNumber.Length() > 0 && aNumber[0] != KZero && ( ( TChar ) aNumber[0] ).IsDigit() )
        {
        format = EDigit;
        }
	else
		{
        format = EUnknown;
	    }

    return format;
    }

TBool CVPbkPhoneNumberMatchStrategyImpl::ValidateBestMatchingRulesL( const TDesC& aNumber )
    {
    if ( iPhoneNumberType == ENotInitialized )
        {
        TPtr16 phoneNumber = iPhoneNumber->Des();
        iPhoneNumberType = ( TNumberType ) FormatAndCheckNumberType( phoneNumber );
        }
    
    HBufC* number = aNumber.AllocLC();
    TPtr16 phoneNumber = number->Des();
    TNumberType numberType = ( TNumberType ) FormatAndCheckNumberType( phoneNumber );

    TBool match = ( !phoneNumber.Compare( *iPhoneNumber ) ||
                  CheckBestMatchingRules( *iPhoneNumber, iPhoneNumberType, *number, numberType  ) ||
                  CheckBestMatchingRules( *number, numberType, *iPhoneNumber, iPhoneNumberType  ) );

    CleanupStack::PopAndDestroy( number );

    return match;
    }

TBool CVPbkPhoneNumberMatchStrategyImpl::CheckBestMatchingRules(
        const TDesC& aNumberA, TNumberType aNumberAType,
        const TDesC& aNumberB, TNumberType aNumberBType  )
    {
    TBool result = EFalse;
    
    // Rules for matching not identical numbers
    // Rules details are presented in Best_Number_Matching_Algorithm_Description.doc
    
    // rule International-International 1
    if ( !result && aNumberAType == EPlus && aNumberBType == ETwoZeros )
        {
        TPtrC numberA = aNumberA.Right( aNumberA.Length() - 1 );
        TPtrC numberB = aNumberB.Right( aNumberB.Length() - 2 );
        result = !( numberA.Compare( numberB ) );
        if ( result )
            {
            return result;
            }
        }

    // rule International-International 2
    if ( aNumberAType == EPlus && aNumberBType == EDigit )
        {
        TPtrC numberA = aNumberA.Right( aNumberA.Length() - 1 );
        if ( numberA.Length() < aNumberB.Length() )
            {
            TPtrC numberB = aNumberB.Right( numberA.Length() );
            result = !( numberA.Compare( numberB ) );
            if ( result )
                {
                return result;
                }
            }
        }

    // rule International-International 3
    if ( aNumberAType == ETwoZeros && aNumberBType == EDigit )
        {
        TPtrC numberA = aNumberA.Right( aNumberA.Length() - 2 );
        if ( numberA.Length() < aNumberB.Length() )
            {
            TPtrC numberB = aNumberB.Right( numberA.Length() );
            result = !( numberA.Compare( numberB ) );
            if ( result )
                {
                return result;
                }
            }
        }

    // rule International-Operator 1
    if ( aNumberAType == EOneZero && aNumberBType == EPlus
            || aNumberAType == EDigit && aNumberBType == EPlus )
        {
        TPtrC numberA;
        if ( aNumberAType == EOneZero )
            {
            numberA.Set( aNumberA.Right( aNumberA.Length() - 1 ) );
            }
        else
            {
            numberA.Set( aNumberA );
            }
        if ( numberA.Length() < aNumberB.Length() - 1 )
            {
            TPtrC numberB = aNumberB.Right( numberA.Length() );
            result = !( numberA.Compare( numberB ) );
            if ( result )
                {
                return result;
                }
            }
        }

    // rule International-Operator 2
    if ( aNumberAType == EOneZero && aNumberBType == ETwoZeros
            || aNumberAType == EDigit && aNumberBType == ETwoZeros )
        {
        TPtrC numberA;
        if ( aNumberAType == EOneZero )
            {
            numberA.Set( aNumberA.Right( aNumberA.Length() - 1 ) );
            }
        else
            {
            numberA.Set( aNumberA );
            }
        if ( numberA.Length() < aNumberB.Length() - 2 )
            {
            TPtrC numberB = aNumberB.Right( numberA.Length() );
            result = !( numberA.Compare( numberB ) );
            if ( result )
                {
                return result;
                }
            }
        }

    // rule International-Operator 3
    if ( aNumberAType == EOneZero && aNumberBType == EDigit
            || aNumberAType == EDigit && aNumberBType == EDigit )
        {
        TPtrC numberA;
        if ( aNumberAType == EOneZero )
            {
            numberA.Set( aNumberA.Right( aNumberA.Length() - 1 ) );
            }
        else
            {
            numberA.Set( aNumberA );
            }
        if ( numberA.Length() < aNumberB.Length() )
            {
            TPtrC numberB = aNumberB.Right( numberA.Length() );
            result = !( numberA.Compare( numberB ) );
            if ( result )
                {
                return result;
                }
            }
        }

    // rule Operator-Operator 1
    if ( aNumberAType == EOneZero && aNumberBType == EDigit )
        {
        TPtrC numberA = aNumberA.Right( aNumberA.Length() - 1 );
        result = !( numberA.Compare( aNumberB ) );
            {
            if ( result )
                {
                return result;
                }
            }
        }
    
    // rule North America Numbering Plan 1
    if ( aNumberAType == EDigit && aNumberBType == EPlus )
        {
        TPtrC numberB = aNumberB.Right( aNumberB.Length() - 1 );
        result = !( aNumberA.Compare( numberB ) );
            {
            if ( result )
                {
                return result;
                }
            }
        }

    // More exceptional acceptance rules can be added here
	// Keep rules updated in the document Best_Number_Matching_Algorithm_Description.doc

    return result;
    }

CVPbkContactStoreUriArray* CVPbkPhoneNumberMatchStrategyImpl::GetCurrentStoreConfigurationL()
    {
    CRepository* repository = CRepository::NewL( TUid::Uid( KCRUidPhonebookStoreConfiguration ) );
    CleanupStack::PushL( repository );
    CVPbkContactStoreUriArray* result = CVPbkContactStoreUriArray::NewLC();

    RArray<TUint32> configurationKeys;
    CleanupClosePushL( configurationKeys );

    repository->FindL( KPhonebookCurrentConfigurationPartialKey,
            KPhonebookCurrentConfigurationMask, configurationKeys );

    HBufC* buffer = HBufC::NewLC( KInitialStoreUriSize );
    const TInt keyCount = configurationKeys.Count();
    TInt ret = KErrNone;
    for ( TInt i = 0; i < keyCount; ++i )
        {
        TPtr ptr = buffer->Des();
        ptr.Zero();
        TInt actualSize = 0;
        ret = repository->Get( configurationKeys[i], ptr, actualSize );
        if ( ret == KErrOverflow )
            {
            CleanupStack::PopAndDestroy(); // buffer
            buffer = HBufC::NewLC( actualSize );
            ptr.Set( buffer->Des() );
            ret = repository->Get( configurationKeys[i], ptr );
            }
        
        if ( ret != KErrNone )
            {
            break;
            }
        
        if( !result->IsIncluded( TVPbkContactStoreUriPtr( ptr ) ) )  // Only append if the uri is not yet included
            {
            result->AppendL( ptr );
            }
        }
    CleanupStack::PopAndDestroy( buffer );
    CleanupStack::PopAndDestroy( &configurationKeys );
    CleanupStack::Pop( result );
    CleanupStack::PopAndDestroy( repository );
    
    if ( ret != KErrNone )
        {
        delete result;
        result = NULL;
        }
    return result;
    }

void CVPbkPhoneNumberMatchStrategyImpl::RefineDuplicatedNumbersL()
    {
    CVPbkContactStoreUriArray* stores = GetCurrentStoreConfigurationL();
    if ( !stores )
        {
        return;
        }
    CleanupStack::PushL( stores );
    
    TInt storesCount = stores->Count();
    if ( storesCount )
        {
        TInt linksCount = iResults->Count();
        // check if there is in the results at least one contact
        // from currently used stores
        TBool isFromUsedStore = EFalse;
        for ( TInt i = 0; i < linksCount; i++ )
            {
            const MVPbkContactStoreProperties& linkStoreProp = 
                iResults->At( i ).ContactStore().StoreProperties();

            for ( TInt j = 0; j < storesCount; j++ )
                {
                if ( !linkStoreProp.Uri().UriDes().Compare( ( *stores )[j].UriDes() ) )
                    {
                    isFromUsedStore = ETrue;
                    break;
                    }
                }
            if ( isFromUsedStore )
                {
                break;
                }
            }
        // remove from results contacts from not used stores
        if ( isFromUsedStore )
            {
            for ( TInt i = 0; i < linksCount; i++ )
                {
                TBool remove = ETrue;
                const MVPbkContactStoreProperties& linkStoreProp = 
                    iResults->At( i ).ContactStore().StoreProperties();
                
                for ( TInt j = 0; j < storesCount; j++ )
                    {
                    if ( !linkStoreProp.Uri().UriDes().Compare( ( *stores )[j].UriDes() ) )
                        {
                        remove = EFalse;
                        break;
                        }
                    }
                if ( remove )
                    {
                    iResults->Delete( i );
                    linksCount--;
                    i--;
                    }
                }
            }
        }
    CleanupStack::PopAndDestroy( stores );
    }

TBool CVPbkPhoneNumberMatchStrategyImpl::IsSimStore( const MVPbkContactStore& aStore )
    {
    TVPbkContactStoreUriPtr uriPtr = aStore.StoreProperties().Uri();
    if ( !uriPtr.UriDes().Compare( KVPbkSimGlobalAdnURI )
            || !uriPtr.UriDes().Compare( KVPbkSimGlobalFdnURI )
            || !uriPtr.UriDes().Compare( KVPbkSimGlobalSdnURI )
            || !uriPtr.UriDes().Compare( KVPbkSimGlobalOwnNumberURI ) )
        {
        return ETrue;
        }
    return EFalse;
    }

void CVPbkPhoneNumberMatchStrategyImpl::LoadNumberParserPluginL()
    {    
    RImplInfoPtrArray   implInfoArray;
    CleanupResetAndDestroyPushL( implInfoArray );
    REComSession::ListImplementationsL( KUidEcomCntPhoneNumberParserInterface, 
                                        implInfoArray );
    // Load the first implementation found for KUidEcomCntPhoneNumberParserInterface 
    const TInt count = implInfoArray.Count();
    __ASSERT_ALWAYS( count > 0, User::Leave( KErrNotFound ) );
    const TUid firstImplementationFound = implInfoArray[0]->ImplementationUid();
    iParser = reinterpret_cast<CContactPhoneNumberParser*> 
        ( CContactEcomPhoneNumberParser::NewL( firstImplementationFound ) );
    CleanupStack::PopAndDestroy( &implInfoArray );
    }

CVPbkPhoneNumberMatchStrategy::CVPbkPhoneNumberMatchStrategy()
    {
    }

EXPORT_C CVPbkPhoneNumberMatchStrategy* CVPbkPhoneNumberMatchStrategy::NewL(
        const TConfig& aConfig,
        CVPbkContactManager& aContactManager,
        MVPbkContactFindObserver& aObserver)
    {
    if (aConfig.iMatchMode == EVPbkSequentialMatch)
        {
        return CVPbkPhoneNumberSequentialMatchStrategy::NewL(aConfig, aContactManager, aObserver);
        }
    else
        {
        return CVPbkPhoneNumberParallelMatchStrategy::NewL(aConfig, aContactManager, aObserver);
        }
   }

CVPbkPhoneNumberMatchStrategy::~CVPbkPhoneNumberMatchStrategy()
    {
    delete iImpl;
    }

EXPORT_C void CVPbkPhoneNumberMatchStrategy::MatchL(const TDesC& aPhoneNumber)
    {
    InitMatchingL();

    iImpl->MatchL(aPhoneNumber);
    }

void CVPbkPhoneNumberMatchStrategy::BaseConstructL(
        const TConfig& aConfig,
        CVPbkContactManager& aContactManager,
        MVPbkContactFindObserver& aObserver)
    {
    iImpl = CVPbkPhoneNumberMatchStrategyImpl::NewL(
            *this, aConfig, aContactManager, aObserver);
    }

MVPbkContactFindObserver& CVPbkPhoneNumberMatchStrategy::FindObserver() const
    {
    return *iImpl;
    }

TInt CVPbkPhoneNumberMatchStrategy::MaxMatchDigits() const
    {
    return iImpl->MaxMatchDigits();
    }

TArray<MVPbkContactStore*> CVPbkPhoneNumberMatchStrategy::StoresToMatch() const
    {
    return iImpl->StoresToMatch();
    }

TBool CVPbkPhoneNumberMatchStrategy::IsSimStore( const MVPbkContactStore& aStore )
    {
    return iImpl->IsSimStore( aStore );
    }
// End of File