qtmobility/plugins/contacts/symbian/src/filtering/cntsymbianfiltersqlhelper.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 16 Apr 2010 15:51:22 +0300
changeset 1 2b40d63a9c3d
permissions -rw-r--r--
Revision: 201011 Kit: 201015

/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
//system includes
#include <e32base.h>
#include <s32mem.h>
#include <centralrepository.h>

#include <QDebug>

//user includes
#include "cntsymbianfiltersqlhelper.h"
#include "qcontactdetailfilter.h"
#include "cnttransformcontact.h"
#include "cntdisplaylabel.h"
#include "cntdisplaylabelsqlfilter.h"
#include "cntsqlsearch.h"

// Telephony Configuration API
// Keys under this category are used in defining telephony configuration.
const TUid KCRUidTelConfiguration = {0x102828B8};
// Amount of digits to be used in contact matching.
// This allows a customer to variate the amount of digits to be matched.
const TUint32 KTelMatchDigits                               = 0x00000001;
// Default match length
const TInt KDefaultMatchLength(7);
//Class documentation go here:
/*!
    \class CntSymbianFilterSqlHelper
    \brief Helper class for converting filter to sql queries
*/

 Q_DEFINE_LATIN1_LITERAL(CntSymbianFilterSqlHelper::SingleQuote,"'")  ;
 Q_DEFINE_LATIN1_LITERAL(CntSymbianFilterSqlHelper::PercentSign,"%") ;
 Q_DEFINE_LATIN1_LITERAL(CntSymbianFilterSqlHelper::Space," ") ;
 Q_DEFINE_LATIN1_LITERAL(CntSymbianFilterSqlHelper::EqualTo,"=") ;
 Q_DEFINE_LATIN1_LITERAL(CntSymbianFilterSqlHelper::SqlLike," LIKE ") ;
 Q_DEFINE_LATIN1_LITERAL(CntSymbianFilterSqlHelper::SqlNotNull," NOT NULL ") ;
 Q_DEFINE_LATIN1_LITERAL(CntSymbianFilterSqlHelper::SqlWhere," WHERE ") ;
 Q_DEFINE_LATIN1_LITERAL(CntSymbianFilterSqlHelper::SqlOr," OR ") ;
 Q_DEFINE_LATIN1_LITERAL(CntSymbianFilterSqlHelper::contactsTable," contact ") ;
 Q_DEFINE_LATIN1_LITERAL(CntSymbianFilterSqlHelper::commAddrTable," comm_addr ") ;
 

/*!
 * The constructor
 */
CntSymbianFilterSqlHelper::CntSymbianFilterSqlHelper(CContactDatabase& contactDatabase)
                                               : m_contactDatabase(contactDatabase),
                                                 isPhoneNumberSearchforDetailFilter(false)
{
   m_srvConnection = new CntSymbianSrvConnection();
   m_sqlSearch = new CntSqlSearch();

   contactsTableIdColumNameMapping.insert(KUidContactFieldGivenName.iUid,"first_name" );
   contactsTableIdColumNameMapping.insert(KUidContactFieldGivenNamePronunciation.iUid,"firstname_prn" );
   contactsTableIdColumNameMapping.insert(KUidContactFieldFamilyName.iUid,"last_name" );
   contactsTableIdColumNameMapping.insert(KUidContactFieldFamilyNamePronunciation.iUid,"lastname_prn" );
   contactsTableIdColumNameMapping.insert(KUidContactFieldCompanyName.iUid,"company_name" );
   contactsTableIdColumNameMapping.insert(KUidContactFieldCompanyNamePronunciation.iUid,"companyname_prn" );

   //commAddrTableIdColumNameMapping.insert(KUidContactFieldIMPP.iUid,ESipAddress );
   commAddrTableIdColumNameMapping.insert(KUidContactFieldSIPID.iUid,ESipAddress );
   commAddrTableIdColumNameMapping.insert(KUidContactFieldEMail.iUid,EEmailAddress );

}

/*!
 * Destructor
 */
CntSymbianFilterSqlHelper::~CntSymbianFilterSqlHelper()

{
    delete m_srvConnection;
    delete m_sqlSearch;
    contactsTableIdColumNameMapping.clear();
    commAddrTableIdColumNameMapping.clear();
}

/*!
 * Fetch search results from the database.
 *
 * \a filter The simple/complex QContactFilter passed .
 * \a error On return, contains the possible error.
 * \return the list of matched contact ids
 */
QList<QContactLocalId> CntSymbianFilterSqlHelper::searchContacts(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders,
                                                                  QContactManager::Error& error)
{
    isPhoneNumberSearchforDetailFilter = false;
    QList<QContactLocalId> idList;
    bool isPredSearch;
    idList =  HandlePredictiveSearchFilter(filter,isPredSearch, error);
    if(isPredSearch)
            return idList;
   if(filterSupportLevel(filter)){
        
        // Create sql query from the filters
        QString sqlQuery;
        createSqlQuery(filter, sqlQuery, error);

        if( error != QContactManager::NoError) {
            return QList<QContactLocalId>();
        }
        // Query the database
        // If isPhoneNumberSearchforDetailFilter flag is set, we use the existing cntmodel
        // else call searchContacts
        if(isPhoneNumberSearchforDetailFilter)
        {
            // cast the filter into detail filte
            const QContactDetailFilter detailFilter(filter);
            idList = HandlePhonenumberDetailFilter(detailFilter);
        }
        else
        {
            //append the sort order to the query
            appendSortOrderQuery(sqlQuery, sortOrders);
            
            //fetch the contacts
            idList =  m_srvConnection->searchContacts(sqlQuery, error);
        }

    }
    else
        {
        error = QContactManager::NotSupportedError;
        }
    return idList;
    
        
}

QList<QContactLocalId>  CntSymbianFilterSqlHelper::HandlePredictiveSearchFilter(const QContactFilter& filter, bool &isPredSearch,
        QContactManager::Error& error)
    {
    isPredSearch = false;
    QString sqlQuery;
    if(filter.type() == QContactFilter::ContactDetailFilter){
       const QContactDetailFilter detailFilter(filter);
       if(  detailFilter.matchFlags() == QContactFilter::MatchKeypadCollation ){
          //convert string to numeric format
            QString pattern = detailFilter.value().toString();
            sqlQuery = m_sqlSearch->CreatePredictiveSearch(pattern);
            isPredSearch = true;
        
            return  m_srvConnection->searchContacts(sqlQuery, error);  
           }
       else
           {
           return QList<QContactLocalId>();
           }
        }
    else
        {
        return QList<QContactLocalId>();
        }
    }

/*!
 * Append the sort order to the sql query
 *
 * \a sqlQuery to add the sort order to
 * \a sortOrders to be added
 */
void CntSymbianFilterSqlHelper::appendSortOrderQuery(QString& sqlQuery, const QList<QContactSortOrder>& sortOrders)
{
    QString column;
    CntDisplayLabel displayLabel;
    
    bool first(true);
    
    for(int i = 0; i < sortOrders.count(); i++)
    {
        columnName(column, sortOrders.at(i).detailDefinitionName(), sortOrders.at(i).detailFieldName());
      
        if(!column.isEmpty())
        {
            if(first)
            {
                sqlQuery += " ORDER BY";
                first = false;        
            }
          
            else
            {
                sqlQuery += ",";
            }
            
            //use the display label if the name is null, ignore case
            sqlQuery += " CASE WHEN " + column + " ISNULL THEN \'"+ displayLabel.unNamned().toLower() + "\' ELSE lower(" + column + ") END";
            
            if(sortOrders.at(i).direction() == Qt::AscendingOrder)
            {
                sqlQuery += " ASC";
            }
                
            else if(sortOrders.at(i).direction() == Qt::DescendingOrder)
            {
                sqlQuery += " DESC";
            }
        }
    }
}

/*!
 * Retrieve a column name
 *
 * \a columnName to be saved the column name if found
 * \a detailDefinitionName of the detail to fetch column name for
 * \a detailFieldName of the detail to fetch column name for
 */
void CntSymbianFilterSqlHelper::columnName( QString &columnName, const QString &detailDefinitionName, const QString & detailFieldName)
{
    columnName = "";

    //Name detail
    if(detailDefinitionName == QContactName::DefinitionName)
    {
        if(detailFieldName == QContactName::FieldFirst)
        {
            columnName = "first_name";
        }
        
        else if(detailFieldName == QContactName::FieldLast)
        {
            columnName = "last_name";
        }
    }
    
    //Organization
    else if(detailDefinitionName == QContactOrganization::DefinitionName)
    {
        if(detailFieldName == QContactOrganization::FieldName)
        {
            columnName = "company_name";
        }
    }
}

/*!
 * converts complex filter into simple filters
 *
 * \a filter The simple/complex QContactFilter passed .
 * \a sqlQuery The sql query that would be formed
 * \a error On return, contains the possible error.
 */
void CntSymbianFilterSqlHelper::createSqlQuery(const QContactFilter& filter,
                                                 QString& sqlQuery,
                                                 QContactManager::Error& error)
{
    //Check if it is a single filter
    bool IsOneLevelFilter  = isSingleFilter(filter);
    if(IsOneLevelFilter) {
        //Single Filter, get the sql query here
        updateSqlQueryForSingleFilter(filter,sqlQuery,error);
    } else {
        // We have multiple filters. Combine these to form correct query
        // Not supported yet
        error = QContactManager::NotSupportedError;
    }
}

/*!
 * Checks if the given filter is a single filter or combination of filters
 *
 * \a filter The QContactFilter to be used.
 * \return True if the filters is single filter
 */

bool CntSymbianFilterSqlHelper::isSingleFilter(const QContactFilter& singlefilter) const
{

    bool returnValue = false;
    switch (singlefilter.type()) {
        case QContactFilter::ContactDetailFilter:
        case QContactFilter::InvalidFilter :
        case QContactFilter::ContactDetailRangeFilter:
        case QContactFilter::ChangeLogFilter:
        case QContactFilter::DefaultFilter:
                        //All are single filters, return True
                        returnValue = true;
                        break;
        case QContactFilter::ActionFilter:
        case QContactFilter::IntersectionFilter:
        case QContactFilter::UnionFilter:

                        //All these are multiple filters
                        returnValue = false;
                        break;
        default:
                        returnValue = false;
                        break;
    };
    return returnValue;
}

/*!
 * Updates the input sql query for single filter
 *
 * \a filter The QContactFilter to be used.
 * \a sqlQuery The sql query that would be updated
 * \a error On return, contains the possible error
 */
void  CntSymbianFilterSqlHelper::updateSqlQueryForSingleFilter( const QContactFilter& filter,
                                                                QString& sqlQuery,
                                                                QContactManager::Error& error)
{
    switch (filter.type()) {
           case QContactFilter::InvalidFilter :
           {
               // Not supported yet
               error = QContactManager::NotSupportedError;
               break;
           }
           case QContactFilter::ContactDetailFilter:
           {
               const QContactDetailFilter detailFilter(filter);
               
               //display label
               if (detailFilter.detailDefinitionName() == QContactDisplayLabel::DefinitionName)
               {
                   CntDisplayLabelSqlFilter displayLabelFilter;
                   displayLabelFilter.createSqlQuery(detailFilter,sqlQuery,error);
               }
               
               //type
               else if(detailFilter.detailDefinitionName() == QContactType::DefinitionName)
               {
                   if(detailFilter.value().toString() == QContactType::TypeContact)
                       sqlQuery = "SELECT contact_id FROM contact WHERE (type_flags>>24)=0";
                   else if(detailFilter.value().toString() == QContactType::TypeGroup)
                       sqlQuery = "SELECT contact_id FROM contact WHERE (type_flags>>24)=3";
               }
               
               //everything else
               else
               {
                   updateSqlQueryForDetailFilter(filter,sqlQuery,error);
               }
               
               break;
           }
           case QContactFilter::ContactDetailRangeFilter:
                       // Not supported yet
                       error = QContactManager::NotSupportedError;
                       break;

           case QContactFilter::ChangeLogFilter:
                       // Not supported yet
                       error = QContactManager::NotSupportedError;
                       break;
           case QContactFilter::DefaultFilter:
                       sqlQuery = "SELECT DISTINCT contact_id FROM contact WHERE (type_flags>>24)=0 OR (type_flags>>24)=3"; 
                       error = QContactManager::NoError;
                       break;
           case QContactFilter::ActionFilter:
           case QContactFilter::IntersectionFilter:
           case QContactFilter::UnionFilter:
                       //All these are multiple filters
                       // Not supported yet
                       error = QContactManager::NotSupportedError;
                       break;
           default:
                       //Some Unknow filter value
                       // Not supported
                       error = QContactManager::NotSupportedError;
                       break;
    };
    if( error != QContactManager::NoError)
        {
        sqlQuery = "";
        }
}

/*!
 * Updates the input sql query for detail filter
 *
 * \a filter The QContactFilter to be used.
 * \a sqlQuery The sql query that would be updated
 * \a error On return, contains the possible error
 */
void CntSymbianFilterSqlHelper::updateSqlQueryForDetailFilter(const QContactFilter& filter,
                                                              QString& sqlQuery,
                                                              QContactManager::Error& error)
{


    // cast the filter into detail filter
    const QContactDetailFilter detailFilter(filter);

    QString sqlWhereClause =  Space  + " WHERE ";

    //Get the table name and the column name
    bool isSubType;
    QString columnName;
    QString tableName;

    //Check for phonenumber. Special handling needed
    if(detailFilter.detailDefinitionName() == QContactPhoneNumber::DefinitionName){
        isPhoneNumberSearchforDetailFilter = true;
        error = QContactManager::NoError;
        return;
    }

    getSqlDbTableAndColumnNameforDetailFilter(detailFilter,isSubType,tableName,columnName);

    //return if tableName is empty
    if(tableName == "" ){
        error = QContactManager::NotSupportedError;
        return;
    }

    //check columnName
    if(columnName == "") {
        error = QContactManager::NotSupportedError;
        return;
    }
    else if(isSubType) {
        sqlWhereClause += columnName;
        sqlWhereClause += " NOT NULL ";
    }
    else {

        sqlWhereClause += Space + columnName + Space ;
        QString fieldToUpdate;
        //Update the value depending on the match flag
        updateFieldForDeatilFilterMatchFlag(detailFilter,fieldToUpdate,error);
        sqlWhereClause +=  fieldToUpdate;
    }


    //Create the sql query
    sqlQuery += "SELECT DISTINCT contact_id FROM " + Space + tableName + Space + sqlWhereClause;


}

/*!
 * Converts filed id to column name of the database table.
 * QContactManager::contacts function.
 *
 * \a fieldId field id representing the detail field name
 * \a sqlDbTableColumnName On return,contains the column name in the database
 */
void CntSymbianFilterSqlHelper::updateFieldForDeatilFilterMatchFlag(
                                                    const QContactDetailFilter& filter,
                                                    QString& fieldToUpdate ,
                                                    QContactManager::Error& error) const
{
    // Modify the filed depending on the query
    switch(filter.matchFlags())
        {
            case QContactFilter::MatchExactly:
                {
                // Pattern for MatchExactly:
                // " ='xyz'"
                fieldToUpdate = Space + EqualTo + SingleQuote
                               + filter.value().toString() + SingleQuote;
                error = QContactManager::NoError;
                break;
                }
            case QContactFilter::MatchContains:
                {
                // Pattern for MatchContains:
                // " LIKE '%xyz%'"
                fieldToUpdate = Space + SqlLike + Space + SingleQuote + PercentSign
                                + filter.value().toString() + PercentSign + SingleQuote ;
                error = QContactManager::NoError;
                break;
                }
            case QContactFilter::MatchStartsWith:
                {
                // Pattern for MatchStartsWith:
                // " LIKE 'xyz%'"
                fieldToUpdate = Space + SqlLike + Space + SingleQuote
                               +  filter.value().toString() + PercentSign + SingleQuote  ;
                error = QContactManager::NoError;
                break;
                }
            case QContactFilter::MatchEndsWith:
                {
                // Pattern for MatchEndsWith:
                // " LIKE '%xyz'"
                fieldToUpdate = Space + SqlLike + Space + SingleQuote + PercentSign
                              + filter.value().toString() + SingleQuote ;
                error = QContactManager::NoError;
                break;
                }
            case QContactFilter::MatchFixedString:
                {
                error = QContactManager::NotSupportedError;
                break;
                }
            case QContactFilter::MatchCaseSensitive:
                {
                error = QContactManager::NotSupportedError;
                break;
                }
            default:
                {
                error = QContactManager::NotSupportedError;
                break;
                }
        }
}

/*!
 * Converts filed id to column name of the database table.
 * QContactManager::contacts function.
 *
 * \a fieldId field id representing the detail field name
 * \a sqlDbTableColumnName On return,contains the column name in the database
 */
void CntSymbianFilterSqlHelper::getSqlDbTableAndColumnNameforDetailFilter(
                                                    const QContactDetailFilter& detailFilter ,
                                                    bool& isSubType,
                                                    QString& tableName,
                                                    QString& columnName )
{

    //Get the field id for the detail field name
    CntTransformContact transformContact;
    quint32 fieldId  = transformContact.GetIdForDetailL(detailFilter, isSubType);

    //check contacts table
    columnName = "";
    tableName = "";

    if (contactsTableIdColumNameMapping.contains(fieldId)){
         columnName = contactsTableIdColumNameMapping.value(fieldId);
         tableName = "contact";
     }

    if( ("" == columnName)  || ("" == tableName)){
        //Search comm Addr table
        if (commAddrTableIdColumNameMapping.contains(fieldId)){
                // communication address table has slightly differnt format, so we make the column name as
                //  "type = <type> and value "
                int typeval = commAddrTableIdColumNameMapping.value(fieldId) ;
                columnName = Space + "TYPE = ";
                columnName.append('0'+ typeval)
                + typeval + Space;
                columnName += " and value " ;
                tableName = "comm_addr";
             }

    }
}

QList<QContactLocalId> CntSymbianFilterSqlHelper::HandlePhonenumberDetailFilter(const QContactDetailFilter detailFilter)
    {
    QList<QContactLocalId> matches;

    if(detailFilter.detailDefinitionName() == QContactPhoneNumber::DefinitionName){

    // Phone numbers need separate handling
            if ((filterSupportLevel(detailFilter) == CntAbstractContactFilter::Supported
                 || filterSupportLevel(detailFilter) == CntAbstractContactFilter::SupportedPreFilterOnly))
            {
                QString number((detailFilter.value()).toString());
                TPtrC commPtr(reinterpret_cast<const TUint16*>(number.utf16()));

                TInt matchLength(KDefaultMatchLength);
                // no need to propagate error, we can use the default match length
                TRAP_IGNORE(getMatchLengthL(matchLength));

                //cal the search
                CContactIdArray* idArray(0);
                TInt err = searchPhoneNumbers(idArray, commPtr, matchLength);
                if( idArray && (err == KErrNone)){
                    // copy the matching contact ids
                    for(int i(0); i < idArray->Count(); i++) {
                        matches.append(QContactLocalId((*idArray)[i]));
                    }
                    delete idArray;
                }
                else{
                    //CntSymbianTransformError::transformError(err, error);
                    }
            }


    }
    return matches;

    }
/*!
 * The contact database version implementation for
 * QContactManager::filterSupported function. The possible return values are
 * Supported, NotSupported and SupportedPreFilterOnly. Supported means that
 * the filtering is implemented directly by the underlying database.
 * NotSupported means that CntSymbianFilterDbms::contacts will return an
 * error. And SupportedPreFilterOnly means that the filter is not supported,
 * but the CntSymbianFilterDbms::contacts will act like the filter was
 * supported. This means that the client must filter the pre-filtered set of
 * contacts to see if there are false positives included. Note that in some
 * cases the pre-filtering is not very effective.
 *
 * \a filter The QContactFilter to be checked.
 * \a return Supported in case the filter is supported. NotSupported in case
 * the filter is not supported. returns
 *
 */
CntAbstractContactFilter::FilterSupport CntSymbianFilterSqlHelper::filterSupportLevel(const QContactFilter& filter)
{
    CntAbstractContactFilter::FilterSupport filterSupported(CntAbstractContactFilter::NotSupported);
    switch (filter.type()) {
            case QContactFilter::DefaultFilter: //default filter == no filter
                {
                filterSupported = CntAbstractContactFilter::Supported;
                break;
                }
            case QContactFilter::ContactDetailFilter:
            	{
                const QContactDetailFilter &detailFilter = static_cast<const QContactDetailFilter &>(filter);
                filterSupported = checkIfDetailFilterSupported(detailFilter);
                break;
                }
            case QContactFilter::InvalidFilter :
            case QContactFilter::ContactDetailRangeFilter:
            case QContactFilter::ChangeLogFilter:
            case QContactFilter::ActionFilter:
            case QContactFilter::IntersectionFilter:
            case QContactFilter::UnionFilter:
            case QContactFilter::RelationshipFilter: 
            default:
                    filterSupported = CntAbstractContactFilter::NotSupported;
                    break;
        }
    return filterSupported;
}
CntAbstractContactFilter::FilterSupport CntSymbianFilterSqlHelper::checkIfDetailFilterSupported
                                                                    (const QContactDetailFilter& detailFilter) const
{

        CntAbstractContactFilter::FilterSupport filterSupported(CntAbstractContactFilter::NotSupported);
        //Get the match flags
        QContactFilter::MatchFlags matchFlags = detailFilter.matchFlags();
        // Phone numbers
        if (detailFilter.detailDefinitionName() == QContactPhoneNumber::DefinitionName) {
            if (matchFlags == QContactFilter::MatchEndsWith){
                filterSupported = CntAbstractContactFilter::Supported;
            }
            else if (matchFlags == QContactFilter::MatchExactly){
                filterSupported = CntAbstractContactFilter::SupportedPreFilterOnly;
            }
        }
        // Names , Email,Sip address
        else if ( detailFilter.detailDefinitionName() == QContactName::DefinitionName ||
                  detailFilter.detailDefinitionName() == QContactEmailAddress::DefinitionName ||
                  detailFilter.detailDefinitionName() == QContactOnlineAccount::DefinitionName ||
                  detailFilter.detailDefinitionName() == QContactDisplayLabel::DefinitionName ||
                  detailFilter.detailDefinitionName() == QContactType::DefinitionName){
               if (  (matchFlags == QContactFilter::MatchContains)|| (matchFlags == QContactFilter::MatchStartsWith)||
                     (matchFlags == QContactFilter::MatchEndsWith)|| (matchFlags == QContactFilter::MatchExactly)){
                filterSupported = CntAbstractContactFilter::Supported;
               }
               if(matchFlags == QContactFilter::MatchKeypadCollation)
                      filterSupported = CntAbstractContactFilter::Supported;
        }
        return filterSupported;

}
/*
 * Get the match length setting. Digits to be used in matching (counted from
 * right).
 */
void CntSymbianFilterSqlHelper::getMatchLengthL(TInt& matchLength)
{
    //Get number of digits used to match
    CRepository* repository = CRepository::NewL(KCRUidTelConfiguration);
    CleanupStack::PushL(repository);
    User::LeaveIfError(repository->Get(KTelMatchDigits, matchLength));
    CleanupStack::PopAndDestroy(repository);
}

/*
 * Find contacts based on a phone number.
 * \a idArray On return contains the ids of the contacts that match the phonenumber.
 * \a phoneNumber The phone number to match
 * \a matchLength Match length; digits from right.
 */
TInt CntSymbianFilterSqlHelper::searchPhoneNumbers(
        CContactIdArray*& idArray,
        const TDesC& phoneNumber,
        const TInt matchLength)
{
    CContactIdArray* idArrayTmp(0);
    TRAPD( err, idArrayTmp = m_contactDatabase.MatchPhoneNumberL(phoneNumber, matchLength));
    if(err == KErrNone){
       idArray = idArrayTmp;
    }
    return 0;
}