plugins/contacts/symbian/plugin/src/filtering/cntfilterdetail.cpp
changeset 0 876b1a06bc25
child 5 603d3f8b6302
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtCore module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <centralrepository.h>
       
    43 #include <cntfldst.h>
       
    44 
       
    45 #include "cntfilterdetail.h"
       
    46 #include "cntfilterdetaildisplaylabel.h" //todo rename class to follow naming pattern CntFilterDetailDisplayLabel
       
    47 #include "cntsqlsearch.h"
       
    48 #include "cntsymbianengine.h"
       
    49 #include "cnttransformphonenumber.h"
       
    50 
       
    51 // Telephony Configuration API
       
    52 // Keys under this category are used in defining telephony configuration.
       
    53 const TUid KCRUidTelConfiguration = {0x102828B8};
       
    54 // Amount of digits to be used in contact matching.
       
    55 // This allows a customer to variate the amount of digits to be matched.
       
    56 const TUint32 KTelMatchDigits                               = 0x00000001;
       
    57 // Default match length
       
    58 const TInt KDefaultMatchLength(7);
       
    59 
       
    60 CntFilterDetail::CntFilterDetail(CContactDatabase& contactDatabase,CntSymbianSrvConnection &cntServer,CntDbInfo& dbInfo) 
       
    61                                         : m_contactdatabase(contactDatabase),
       
    62                                           m_srvConnection(cntServer),
       
    63                                           m_dbInfo(dbInfo),
       
    64                                           m_emulateBestMatching(false)
       
    65 {
       
    66 }
       
    67 
       
    68 CntFilterDetail::~CntFilterDetail()
       
    69 {
       
    70 }
       
    71 
       
    72 
       
    73 QList<QContactLocalId> CntFilterDetail::contacts(
       
    74         const QContactFilter &filter,
       
    75         const QList<QContactSortOrder> &sortOrders,
       
    76         bool &filterSupportedflag,
       
    77         QContactManager::Error* error)
       
    78 {
       
    79     Q_UNUSED(filterSupportedflag);
       
    80     //Check if any invalid filter is passed 
       
    81     if (!filterSupported(filter) ) {
       
    82         *error =  QContactManager::NotSupportedError;
       
    83         return QList<QContactLocalId>();
       
    84     }
       
    85     QList<QContactLocalId> idList;
       
    86     QContactDetailFilter detailFilter(filter);
       
    87     QString sqlQuery;
       
    88     //Check for phonenumber. Special handling needed
       
    89     if ( (detailFilter.detailDefinitionName() == QContactPhoneNumber::DefinitionName ) &&
       
    90             (detailFilter.detailFieldName() != QContactPhoneNumber::FieldSubTypes)) {
       
    91         //Handle phonenumber ...
       
    92         createMatchPhoneNumberQuery(filter,sqlQuery,error);
       
    93         if (*error == QContactManager::NoError) {
       
    94             //fetch the contacts
       
    95             idList =  m_srvConnection.searchContacts(sqlQuery,error);
       
    96         }
       
    97     }
       
    98 
       
    99     else if (detailFilter.matchFlags() == QContactFilter::MatchKeypadCollation) {
       
   100         //predictive search filter
       
   101         idList = HandlePredictiveSearchFilter(filter,error);
       
   102     }
       
   103             
       
   104     // handle other cases
       
   105     else {
       
   106         createSelectQuery(filter,sqlQuery,error);
       
   107         QString sortQuery = m_dbInfo.getSortQuery(sortOrders, sqlQuery, error);
       
   108         
       
   109         if (*error == QContactManager::NoError) {
       
   110             //fetch the contacts
       
   111             idList =  m_srvConnection.searchContacts(sortQuery, error);
       
   112         }
       
   113     }
       
   114     return idList;
       
   115 }
       
   116 
       
   117 bool CntFilterDetail::filterSupported(const QContactFilter& filter) 
       
   118 {
       
   119     bool result = false;
       
   120     if (QContactFilter::ContactDetailFilter == filter.type()) {
       
   121         result = true;
       
   122     }
       
   123     return result;
       
   124 }
       
   125 
       
   126 void CntFilterDetail::createSelectQuery(const QContactFilter& filter,
       
   127                                         QString& sqlQuery,
       
   128                                         QContactManager::Error* error)
       
   129 
       
   130 {
       
   131     if (!filterSupported(filter)) {
       
   132       *error = QContactManager::NotSupportedError;
       
   133       return;
       
   134     }
       
   135     QContactDetailFilter detailFilter(filter);
       
   136     //display label
       
   137     if (detailFilter.detailDefinitionName() == QContactDisplayLabel::DefinitionName) {
       
   138       CntFilterDetailDisplayLabel displayLabelFilter;
       
   139       displayLabelFilter.createSelectQuery(filter, sqlQuery, error);
       
   140     }
       
   141     //type
       
   142     else if (detailFilter.detailDefinitionName() == QContactType::DefinitionName) {
       
   143        if (detailFilter.value().toString() == QContactType::TypeContact)
       
   144            sqlQuery = "SELECT contact_id FROM contact WHERE (type_flags>>24)=0";
       
   145        else if (detailFilter.value().toString() == QContactType::TypeGroup)
       
   146            sqlQuery = "SELECT contact_id FROM contact WHERE (type_flags>>24)=3";
       
   147     }
       
   148     else if (detailFilter.detailDefinitionName() == QContactGuid::DefinitionName) {
       
   149        if (detailFilter.detailFieldName() == QContactGuid::FieldGuid) {
       
   150            QStringList fullGuidValue = detailFilter.value().toString().split('-');
       
   151            if (fullGuidValue.count() == 3) {
       
   152                QString localGuidValue = fullGuidValue.at(1);
       
   153                sqlQuery = "SELECT contact_id FROM contact WHERE guid_string = '" + localGuidValue + '\'';
       
   154            }
       
   155        }
       
   156     }
       
   157     //everything else
       
   158     else {   
       
   159        QString tableName;
       
   160        QString sqlWhereClause;
       
   161        getTableNameWhereClause(detailFilter,tableName,sqlWhereClause,error);
       
   162        //Create the sql query
       
   163        sqlQuery += "SELECT DISTINCT contact_id FROM " + tableName + " WHERE " + sqlWhereClause;
       
   164     }
       
   165 }
       
   166 
       
   167 /*!
       
   168  * Updates match flags for columns.
       
   169  */
       
   170 void CntFilterDetail::updateForMatchFlag(const QContactDetailFilter& filter,
       
   171                                          QString& fieldToUpdate ,
       
   172                                          QContactManager::Error* error) const
       
   173 {
       
   174     // Modify the filed depending on the query
       
   175     switch (filter.matchFlags()) {
       
   176         case QContactFilter::MatchExactly: {
       
   177             // Pattern for MatchExactly:
       
   178             // " ='xyz'"
       
   179             fieldToUpdate = " ='"
       
   180                            + filter.value().toString() + '\'';
       
   181             *error = QContactManager::NoError;
       
   182             break;
       
   183         }
       
   184         case QContactFilter::MatchContains: {
       
   185             // Pattern for MatchContains:
       
   186             // " LIKE '%xyz%'"
       
   187             fieldToUpdate = " LIKE '%" + filter.value().toString() + "%'" ;
       
   188             *error = QContactManager::NoError;
       
   189             break;
       
   190         }
       
   191         case QContactFilter::MatchStartsWith: {
       
   192             // Pattern for MatchStartsWith:
       
   193             // " LIKE 'xyz%'"
       
   194             fieldToUpdate = " LIKE '" +  filter.value().toString() + "%'"  ;
       
   195             *error = QContactManager::NoError;
       
   196             break;
       
   197         }
       
   198         case QContactFilter::MatchEndsWith: {
       
   199             // Pattern for MatchEndsWith:
       
   200             // " LIKE '%xyz'"
       
   201             fieldToUpdate = " LIKE '%" + filter.value().toString() + '\'' ;
       
   202             *error = QContactManager::NoError;
       
   203             break;
       
   204         }
       
   205         case QContactFilter::MatchFixedString: {
       
   206             *error = QContactManager::NotSupportedError;
       
   207             break;
       
   208         }
       
   209         case QContactFilter::MatchCaseSensitive: {
       
   210             *error = QContactManager::NotSupportedError;
       
   211             break;
       
   212         }
       
   213         default: {
       
   214             *error = QContactManager::NotSupportedError;
       
   215             break;
       
   216         }
       
   217     }
       
   218 }
       
   219 
       
   220 void CntFilterDetail::getTableNameWhereClause(const QContactDetailFilter& detailfilter,
       
   221                                               QString& tableName,
       
   222                                               QString& sqlWhereClause ,
       
   223                                               QContactManager::Error* error) const
       
   224 {
       
   225     //Get the table name and the column name
       
   226     QString columnName;
       
   227     bool isSubType;
       
   228 
       
   229     m_dbInfo.getDbTableAndColumnName(detailfilter.detailDefinitionName(), detailfilter.detailFieldName(), tableName, columnName, isSubType);
       
   230 
       
   231     // return if tableName is empty
       
   232     if (tableName.isEmpty()) {
       
   233         *error = QContactManager::NotSupportedError;
       
   234         return;
       
   235     }
       
   236 
       
   237     //check columnName
       
   238     if (columnName.isEmpty()) {
       
   239         *error = QContactManager::NotSupportedError;
       
   240         return;
       
   241     }
       
   242     else if (isSubType) {
       
   243         sqlWhereClause += columnName;
       
   244         sqlWhereClause += " NOT NULL ";
       
   245     }
       
   246     else {
       
   247         sqlWhereClause += ' ' + columnName + ' ';
       
   248         QString fieldToUpdate;
       
   249         //Update the value depending on the match flag
       
   250         updateForMatchFlag(detailfilter,fieldToUpdate,error);
       
   251         sqlWhereClause +=  fieldToUpdate;
       
   252     }
       
   253 }
       
   254 
       
   255 QList<QContactLocalId>  CntFilterDetail::HandlePredictiveSearchFilter(const QContactFilter& filter,
       
   256                                                                       QContactManager::Error* error)
       
   257 {
       
   258     QString sqlQuery;
       
   259     
       
   260     if (filter.type() == QContactFilter::ContactDetailFilter) {
       
   261        const QContactDetailFilter detailFilter(filter);
       
   262        if (detailFilter.matchFlags() == QContactFilter::MatchKeypadCollation) {
       
   263            CntSqlSearch sqlSearch;
       
   264            //convert string to numeric format
       
   265             QString pattern = detailFilter.value().toString();
       
   266             sqlQuery = sqlSearch.CreatePredictiveSearch(pattern);
       
   267             return  m_srvConnection.searchContacts(sqlQuery, error);  
       
   268        }
       
   269        else {
       
   270            return QList<QContactLocalId>();
       
   271        }
       
   272     }
       
   273     else {
       
   274         return QList<QContactLocalId>();
       
   275     }
       
   276 }
       
   277 
       
   278 /*
       
   279  * Creates an sql query to fetch contact item IDs for all the contact items
       
   280  * which may contain the specified telephone number in a telephone, fax
       
   281  * or SMS type field.
       
   282  *
       
   283  * The comparison method used is not exact.  The number is compared starting from
       
   284  * the right side of the number and the method returns an array of candidate
       
   285  * matches.  Punctuation (e.g. spaces) and other alphabetic characters are ignored
       
   286  * when comparing.
       
   287  * 
       
   288  * Note that due to the way numbers are stored in the database, it is recommended
       
   289  * that at least 7 match digits are specified even when matching a number
       
   290  * containing fewer digits.  Failure to follow this guideline may (depending on the
       
   291  * database contents) mean that the function will not return the expected Contact
       
   292  * IDs.
       
   293  */
       
   294 void CntFilterDetail::createMatchPhoneNumberQuery(
       
   295                                       const QContactFilter& filter,
       
   296                                       QString& sqlQuery,
       
   297                                       QContactManager::Error* error)
       
   298 
       
   299 {
       
   300     if (!filterSupported(filter) ) {
       
   301       *error = QContactManager::NotSupportedError;
       
   302       return;
       
   303     }
       
   304           
       
   305     QContactDetailFilter detailFilter(filter);
       
   306     QString number((detailFilter.value()).toString());
       
   307     TPtrC numberPtr(reinterpret_cast<const TUint16*>(number.utf16()));
       
   308     
       
   309     TInt matchLengthFromRight(KDefaultMatchLength);
       
   310     // no need to propagate error, we can use the default match length
       
   311     TRAP_IGNORE(getMatchLengthL(matchLengthFromRight));
       
   312     
       
   313     TInt numLowerDigits = matchLengthFromRight;
       
   314     TInt numUpperDigits = 0;
       
   315     
       
   316     if (numLowerDigits > KLowerSevenDigits) {
       
   317         numLowerDigits = KLowerSevenDigits;
       
   318         numUpperDigits = matchLengthFromRight - KLowerSevenDigits;
       
   319     }
       
   320     else if (numLowerDigits == 0) {
       
   321         // best match phonenumbers
       
   322         numLowerDigits = KLowerSevenDigits;
       
   323     }
       
   324     
       
   325     TMatch phoneDigits = createPaddedPhoneDigits(
       
   326                           numberPtr, numLowerDigits, numUpperDigits, error);
       
   327     
       
   328     if (*error == QContactManager::NoError) {
       
   329         // select fields for contacts that match phone lookup
       
   330         //  SELECT contact_id FROM comm_addr
       
   331         //      WHERE value = [value string] AND type = [type value];
       
   332         //
       
   333         QString type =  QString(" type = %1").arg(CntDbInfo::EPhoneNumber);
       
   334         QString value =  QString(" value = %1").arg(phoneDigits.iLowerSevenDigits);
       
   335         QString whereClause = " WHERE" + value + " AND" + type;
       
   336         if (matchLengthFromRight <= KLowerSevenDigits) {
       
   337             // Matching 7 or less digits...
       
   338             sqlQuery = "SELECT contact_id FROM comm_addr" + whereClause;
       
   339         }
       
   340         else {
       
   341             // Checking the upper digits...
       
   342             TMatch phoneNumber = createPhoneMatchNumber(
       
   343                                   numberPtr, numLowerDigits, numUpperDigits, error);
       
   344             QString fieldToMatch = QString(" LIKE '%1").arg(phoneNumber.iUpperDigits) + "%'"  ;
       
   345             whereClause += " AND extra_value" + fieldToMatch;
       
   346             sqlQuery = "SELECT contact_id FROM comm_addr" + whereClause;
       
   347         }
       
   348       
       
   349         // refine search
       
   350         if (bestMatchingEnabled()) {
       
   351             QList<QContactLocalId> list =  m_srvConnection.searchContacts(sqlQuery,error);
       
   352             QList<QContactLocalId> bestMatchingIds;
       
   353             if (*error == QContactManager::NoError) {
       
   354                 TRAP_IGNORE(
       
   355                         bestMatchingIds = getBestMatchPhoneNumbersL(number, list, error);
       
   356                 )
       
   357                 if (bestMatchingIds.count()>0) {
       
   358                     // recreate query
       
   359                     QString selectQuery = " SELECT contact_id FROM comm_addr WHERE contact_id in (";
       
   360                     QString ids = QString("%1").arg(bestMatchingIds.at(0));
       
   361                     for(int i=1; i<bestMatchingIds.count(); ++i) {
       
   362                         ids += QString(" ,%1").arg(bestMatchingIds.at(i));
       
   363                     }
       
   364                     selectQuery += ids + ')';
       
   365                     sqlQuery = selectQuery;
       
   366                 }
       
   367                 else {
       
   368                     // empty list
       
   369                     QString selectQuery = " SELECT contact_id FROM comm_addr WHERE contact_id in (null)";
       
   370                     sqlQuery = selectQuery;
       
   371                 }
       
   372             }
       
   373         }
       
   374     }
       
   375 }
       
   376 
       
   377 #ifdef PBK_UNIT_TEST
       
   378 void CntFilterDetail::emulateBestMatching()
       
   379 {
       
   380     m_emulateBestMatching = true;
       
   381 }
       
   382 #endif
       
   383 
       
   384 /*
       
   385  * Best matching number if matchLengthFromRight set to 0
       
   386  */
       
   387 bool CntFilterDetail::bestMatchingEnabled() 
       
   388 {
       
   389 #ifdef PBK_UNIT_TEST
       
   390     if (m_emulateBestMatching) {
       
   391         return true;
       
   392     }
       
   393 #endif
       
   394     bool result = false;
       
   395     TInt matchLengthFromRight(KDefaultMatchLength);
       
   396     TRAP_IGNORE(getMatchLengthL(matchLengthFromRight));
       
   397     if (matchLengthFromRight == 0) {
       
   398         result = true;
       
   399     }
       
   400     return result;
       
   401 }
       
   402 
       
   403 /*
       
   404  * Get the match length setting. Digits to be used in matching (counted from
       
   405  * right).
       
   406  */
       
   407 bool CntFilterDetail::getMatchLengthL(TInt& matchLength)
       
   408 {
       
   409 #ifdef PBK_UNIT_TEST
       
   410     if (m_emulateBestMatching) {
       
   411         matchLength = 0;
       
   412         return true;
       
   413     }
       
   414 #endif
       
   415     //Get number of digits used to match
       
   416     bool result = false;
       
   417     CRepository* repository = CRepository::NewL(KCRUidTelConfiguration);
       
   418     TInt err = repository->Get(KTelMatchDigits, matchLength);
       
   419     delete repository;
       
   420     result = (err == KErrNone);
       
   421     return result;
       
   422 }
       
   423 
       
   424 /*
       
   425  * Convert the supplied string to a matchable phone number.
       
   426  *
       
   427  * \param text Descriptor containing phone number.
       
   428  * \param lowerMatchlength Number of least significant phone digits to use.
       
   429  * \param upperMatchLength Number of most significant phone digits to use.
       
   430  * \param error Qt error code.
       
   431  * \return The hash code(s) to use when matching the supplied phone number.
       
   432  */
       
   433 CntFilterDetail::TMatch CntFilterDetail::createPaddedPhoneDigits(
       
   434                                             const TDesC& number, 
       
   435                                             const TInt numLowerDigits, 
       
   436                                             const TInt numUpperDigits,
       
   437                                             QContactManager::Error* error)
       
   438 {
       
   439     TMatch phoneNumber = createPhoneMatchNumber(
       
   440                                             number, numLowerDigits, numUpperDigits, error);
       
   441     if (*error == QContactManager::NoError) {
       
   442         if (phoneNumber.iNumLowerDigits + phoneNumber.iUpperDigits == 0) {
       
   443             // No digits, do nothing
       
   444         }
       
   445         else if (phoneNumber.iNumLowerDigits < KLowerSevenDigits) {
       
   446             // Only the lower-digits hash is used, pad out the number to
       
   447             // KLowerSevenDigits.
       
   448             TInt pad = KLowerSevenDigits - phoneNumber.iNumLowerDigits;
       
   449             phoneNumber.iLowerSevenDigits = TMatch::padOutPhoneMatchNumber(phoneNumber.iLowerSevenDigits,pad);
       
   450         }
       
   451         else if (phoneNumber.iNumUpperDigits < (KMaxPhoneMatchLength - KLowerSevenDigits) ) {
       
   452             // The lower-digits hash is full, pad out the upper hash if less than 15
       
   453             // digits total.
       
   454             TInt pad = KMaxPhoneMatchLength - KLowerSevenDigits - phoneNumber.iNumUpperDigits;
       
   455             phoneNumber.iUpperDigits = TMatch::padOutPhoneMatchNumber(phoneNumber.iUpperDigits,pad);
       
   456         }
       
   457     }
       
   458     return phoneNumber;
       
   459 }
       
   460 
       
   461 /*
       
   462  * Returns the hash code(s) to use when matching the supplied phone number.  If the
       
   463  * number supplied has more actual phone digits (i.e. not including spaces) than
       
   464  * KLowerSevenDigits, a second hash is generated to hold the remaining most
       
   465  * significant phone digits. Removes the non-digit characters.
       
   466 
       
   467  * \param text Descriptor containing contacts phone number field.
       
   468  * \param lowerMatchlength Number of least significant phone digits to use.
       
   469  * \param upperMatchLength Number of most significant phone digits to use.
       
   470  * \param error Qt error code.
       
   471  * \return The hash code(s) to use when matching the supplied phone number.
       
   472  */
       
   473 CntFilterDetail::TMatch CntFilterDetail::createPhoneMatchNumber(
       
   474                                             const TDesC& text, 
       
   475                                             TInt lowerMatchLength, 
       
   476                                             TInt upperMatchLength,
       
   477                                             QContactManager::Error* error)
       
   478 {
       
   479     const TInt KBufLength = KCntMaxTextFieldLength+1;
       
   480     TBuf<KBufLength> buf;
       
   481     
       
   482     if (text.Length() <= KBufLength) {
       
   483         buf = text;
       
   484     }
       
   485     else {
       
   486         buf = text.Right(KBufLength);
       
   487     }
       
   488     TMatch::stripOutNonDigitChars(buf);
       
   489     
       
   490     TMatch phoneNumber;
       
   491     if (buf.Length() == 0) {
       
   492         *error = QContactManager::BadArgumentError;
       
   493         return phoneNumber;
       
   494     }
       
   495     
       
   496     // Generate a hash for the upper digits only if the phone number string is
       
   497     // large enough and more than 7 digits are to be matched.
       
   498     TInt phoneNumberlength = buf.Length();
       
   499     if ((phoneNumberlength > KLowerSevenDigits) && (upperMatchLength > 0)) {
       
   500         TPtrC upperPart = buf.Left(phoneNumberlength - KLowerSevenDigits);
       
   501         phoneNumber.iUpperDigits = TMatch::createHash(upperPart,
       
   502             upperMatchLength, phoneNumber.iNumUpperDigits);
       
   503     }
       
   504     // Generate a hash of the lower digits.
       
   505     phoneNumber.iLowerSevenDigits = TMatch::createHash(buf, 
       
   506             lowerMatchLength, phoneNumber.iNumLowerDigits);
       
   507     
       
   508     return phoneNumber;
       
   509 }
       
   510 
       
   511 QList<QContactLocalId> CntFilterDetail::getBestMatchPhoneNumbersL(
       
   512                                       const QString number,
       
   513                                       const QList<QContactLocalId>& idList,
       
   514                                       QContactManager::Error* error)
       
   515 
       
   516 {
       
   517     TPtrC numberPtr(reinterpret_cast<const TUint16*>(number.utf16()));
       
   518     RBuf matchNumber;
       
   519     matchNumber.CleanupClosePushL();
       
   520     matchNumber.CreateL(numberPtr);
       
   521   
       
   522     QList<QContactLocalId> bestMatchingIds;
       
   523     for (int i=0; i<idList.count(); i++) {
       
   524         QContact contact = m_dbInfo.engine()->contact(idList.at(i), QContactFetchHint(), error);
       
   525         QList<QContactPhoneNumber> details = contact.details<QContactPhoneNumber>();
       
   526         CntTransformContactData* transformPhoneNumber = new CntTransformPhoneNumber();
       
   527         
       
   528         bool matchFound(false);
       
   529         for (int j = 0;j < details.count(); j++) {
       
   530             QList<CContactItemField *> fields = transformPhoneNumber->transformDetailL(details.at(j));
       
   531             for (int k = 0;k < details.count() && !matchFound; k++) {
       
   532                 CContactTextField* storage = fields.at(k)->TextStorage();
       
   533                 RBuf phoneNumber;
       
   534                 phoneNumber.CleanupClosePushL();
       
   535                 phoneNumber.CreateL(storage->Text());
       
   536                 if (TMatch::validateBestMatchingRulesL(phoneNumber,matchNumber)) {
       
   537                     matchFound = true;
       
   538                 }
       
   539                 // phoneNumber
       
   540                 CleanupStack::PopAndDestroy();
       
   541             }
       
   542             if (matchFound) {
       
   543                 bestMatchingIds.append(idList.at(i));
       
   544                 break;
       
   545             }
       
   546         }
       
   547         delete transformPhoneNumber;
       
   548     }
       
   549     // matchNumber
       
   550     CleanupStack::PopAndDestroy();
       
   551     return bestMatchingIds;
       
   552 }
       
   553 
       
   554 //CntFilterDetail::TMatch constructor.
       
   555 CntFilterDetail::TMatch::TMatch()
       
   556     :
       
   557     iLowerSevenDigits(0),
       
   558     iUpperDigits(0),
       
   559     iNumLowerDigits(0),
       
   560     iNumUpperDigits(0)
       
   561 {
       
   562 }
       
   563 
       
   564 /*
       
   565  * Generates a hash value by reversing the matchLength least significant digits,
       
   566  * ignoring non-digits and zeroes at the end of the number.  Returns error if no phone
       
   567  * digits are supplied.
       
   568 
       
   569  * \param phoneNumberString A descriptor containing a phone number.
       
   570  * \param matchLength The number of digits from the right of the phone number to use.
       
   571  * \param numPhoneDigits The number of digits found in the phone number string.
       
   572  * \param error Qt error code.*
       
   573  * \return An integer representation of the phone number string in reverse.
       
   574  */
       
   575 TInt32 CntFilterDetail::TMatch::createHash(
       
   576                                     const TDesC& phoneNumberString, 
       
   577                                     TInt matchLength, 
       
   578                                     TInt& numPhoneDigits)
       
   579 {
       
   580     TInt phoneNumberLength = phoneNumberString.Length();
       
   581     TInt startIndex = 0;
       
   582     if (phoneNumberLength > matchLength) {
       
   583         startIndex = phoneNumberLength - matchLength;
       
   584     }
       
   585     
       
   586     numPhoneDigits = 0;
       
   587     TUint reversedDigits = 0;
       
   588     TInt mult = 1;
       
   589     
       
   590     for (TInt chrIndex = startIndex; (numPhoneDigits < matchLength) && (chrIndex < phoneNumberLength); chrIndex++) {
       
   591         TChar chr = phoneNumberString[chrIndex];
       
   592         if (chr.IsDigit()) {
       
   593             reversedDigits += (chr.GetNumericValue()) * mult;
       
   594             mult = mult * 10;
       
   595             ++numPhoneDigits;
       
   596         }
       
   597     }
       
   598     return reversedDigits ;
       
   599 }
       
   600 
       
   601 void CntFilterDetail::TMatch::stripOutNonDigitChars(TDes& text)
       
   602 {
       
   603     for (TInt chrPos = 0; chrPos < text.Length(); ++chrPos) {
       
   604         TChar chr = text[chrPos];
       
   605         if (!chr.IsDigit()) {
       
   606             text.Delete(chrPos, 1);
       
   607             --chrPos;
       
   608         }
       
   609     }
       
   610 }
       
   611 
       
   612 TInt32 CntFilterDetail::TMatch::padOutPhoneMatchNumber(TInt32& phoneNumber,
       
   613                                                        TInt padOutLength)
       
   614 {
       
   615     TInt32 result(phoneNumber);
       
   616     const TInt KMult(10);
       
   617     for (TInt i = 0; i < padOutLength; ++i) {
       
   618         result *= KMult;
       
   619     }
       
   620     phoneNumber = result;
       
   621     return result;
       
   622 }
       
   623 
       
   624 // Removes non-digit chars except plus form the beginning
       
   625 // Checks if number matches to one of defined types
       
   626 //
       
   627 TInt CntFilterDetail::TMatch::formatAndCheckNumberType(TDes& number)
       
   628     {
       
   629     _LIT( KOneZeroPattern, "0*" );
       
   630     _LIT( KTwoZerosPattern, "00*" );
       
   631     _LIT( KPlusPattern, "+*" );
       
   632     const TChar KPlus = TChar('+');
       
   633     const TChar KZero = TChar('0');
       
   634     const TChar KAsterisk = TChar('*');
       
   635     const TChar KHash = TChar('#');
       
   636     
       
   637     for( TInt pos = 0; pos < number.Length(); ++pos ) {
       
   638         TChar chr = number[pos];
       
   639         if ( !chr.IsDigit() && !( pos == 0 && chr == KPlus  )
       
   640                 && !( chr == KAsterisk ) && !( chr == KHash ) ) {
       
   641             number.Delete( pos, 1 );
       
   642             --pos;
       
   643         }
       
   644     }
       
   645     
       
   646     TInt format;
       
   647     
       
   648     if (!number.Match(KTwoZerosPattern) && number.Length() > 2 && number[2] != KZero) {
       
   649         format = ETwoZeros;
       
   650     }
       
   651     else if (!number.Match(KOneZeroPattern)&& number.Length() > 1 && number[1] != KZero) {
       
   652         format = EOneZero;
       
   653     }
       
   654     else if (!number.Match(KPlusPattern) && number.Length() > 1 && number[1] != KZero) {
       
   655         format = EPlus;
       
   656     }
       
   657     else if (number.Length() > 0 && number[0] != KZero && ( ( TChar ) number[0] ).IsDigit()) {
       
   658         format = EDigit;
       
   659     }
       
   660     else {
       
   661         format = EUnknown;
       
   662     }
       
   663 
       
   664     return format;
       
   665     }
       
   666 
       
   667 TBool CntFilterDetail::TMatch::validateBestMatchingRulesL(const TDesC& phoneNumber, const TDesC& matchNumber)
       
   668     {
       
   669     RBuf numberA;
       
   670     numberA.CleanupClosePushL();
       
   671     numberA.CreateL(matchNumber);
       
   672     TNumberType numberAType = (TNumberType) TMatch::formatAndCheckNumberType(numberA);
       
   673     
       
   674     RBuf numberB;
       
   675     numberB.CleanupClosePushL();
       
   676     numberB.CreateL(phoneNumber);
       
   677     TNumberType numberBType = (TNumberType) TMatch::formatAndCheckNumberType(numberB);
       
   678 
       
   679     TBool match = (!numberB.Compare(numberA) ||
       
   680                     TMatch::checkBestMatchingRules(numberA, numberAType, numberB, numberBType) ||
       
   681                     TMatch::checkBestMatchingRules(numberB, numberBType, numberA, numberAType));
       
   682     
       
   683     // cleanup
       
   684     CleanupStack::PopAndDestroy(2);
       
   685     return match;
       
   686     }
       
   687 
       
   688 TBool CntFilterDetail::TMatch::checkBestMatchingRules(
       
   689         const TDesC& numberA, TNumberType numberAType,
       
   690         const TDesC& numberB, TNumberType numberBType  )
       
   691     {
       
   692     TBool result = EFalse;
       
   693     
       
   694     // Rules for matching not identical numbers
       
   695     // Rules details are presented in Best_Number_Matching_Algorithm_Description.doc
       
   696     
       
   697     // rule International-International 1
       
   698     if (!result && numberAType == EPlus && numberBType == ETwoZeros) {
       
   699         TPtrC numberAPtr = numberA.Right(numberA.Length() - 1);
       
   700         TPtrC numberBPtr = numberB.Right(numberB.Length() - 2);
       
   701         result = !(numberAPtr.Compare(numberBPtr));
       
   702         if (result) {
       
   703             return result;
       
   704         }
       
   705     }
       
   706 
       
   707     // rule International-International 2
       
   708     if (numberAType == EPlus && numberBType == EDigit) {
       
   709         TPtrC numberAPtr = numberA.Right( numberA.Length() - 1 );
       
   710         if (numberAPtr.Length() < numberB.Length()) {
       
   711             TPtrC numberBPtr = numberB.Right( numberAPtr.Length() );
       
   712             result = !(numberAPtr.Compare(numberBPtr));
       
   713             if (result) {
       
   714                 return result;
       
   715             }
       
   716         }
       
   717     }
       
   718 
       
   719     // rule International-International 3
       
   720     if (numberAType == ETwoZeros && numberBType == EDigit) {
       
   721         TPtrC numberAPtr = numberA.Right(numberA.Length() - 2);
       
   722         if (numberAPtr.Length() < numberB.Length()) {
       
   723             TPtrC numberBPtr = numberB.Right(numberAPtr.Length());
       
   724             result = !(numberAPtr.Compare(numberBPtr));
       
   725             if (result) {
       
   726                 return result;
       
   727             }
       
   728         }
       
   729     }
       
   730 
       
   731     // rule International-Operator 1
       
   732     if (numberAType == EOneZero && numberBType == EPlus
       
   733             || numberAType == EDigit && numberBType == EPlus) {
       
   734         TPtrC numberAPtr;
       
   735         if (numberAType == EOneZero) {
       
   736             numberAPtr.Set(numberA.Right(numberA.Length() - 1));
       
   737         }
       
   738         else {
       
   739             numberAPtr.Set(numberA);
       
   740         }
       
   741         if (numberAPtr.Length() < numberB.Length() - 1) {
       
   742             TPtrC numberBPtr = numberB.Right(numberAPtr.Length());
       
   743             result = !(numberAPtr.Compare(numberBPtr));
       
   744             if (result) {
       
   745                 return result;
       
   746             }
       
   747         }
       
   748     }
       
   749 
       
   750     // rule International-Operator 2
       
   751     if (numberAType == EOneZero && numberBType == ETwoZeros
       
   752             || numberAType == EDigit && numberBType == ETwoZeros) {
       
   753         TPtrC numberAPtr;
       
   754         if (numberAType == EOneZero) {
       
   755             numberAPtr.Set(numberA.Right(numberA.Length() - 1));
       
   756         }
       
   757         else {
       
   758             numberAPtr.Set(numberA);
       
   759         }
       
   760         if (numberAPtr.Length() < numberB.Length() - 2) {
       
   761             TPtrC numberBPtr = numberB.Right(numberAPtr.Length());
       
   762             result = !(numberAPtr.Compare(numberBPtr));
       
   763             if (result) {
       
   764                 return result;
       
   765             }
       
   766         }
       
   767     }
       
   768 
       
   769     // rule International-Operator 3
       
   770     if (numberAType == EOneZero && numberBType == EDigit
       
   771             || numberAType == EDigit && numberBType == EDigit) {
       
   772         TPtrC numberAPtr;
       
   773         if (numberAType == EOneZero) {
       
   774             numberAPtr.Set(numberA.Right( numberA.Length() - 1));
       
   775         }
       
   776         else {
       
   777             numberAPtr.Set(numberA);
       
   778         }
       
   779         if (numberAPtr.Length() < numberB.Length()) {
       
   780             TPtrC numberBPtr = numberB.Right(numberAPtr.Length());
       
   781             result = !(numberAPtr.Compare(numberBPtr));
       
   782             if (result) {
       
   783                 return result;
       
   784             }
       
   785         }
       
   786     }
       
   787 
       
   788     // rule Operator-Operator 1
       
   789     if (numberAType == EOneZero && numberBType == EDigit) {
       
   790         TPtrC numberAPtr = numberA.Right(numberA.Length() - 1);
       
   791         result = !(numberAPtr.Compare(numberB));
       
   792         if (result) {
       
   793             return result;
       
   794         }
       
   795     }
       
   796     
       
   797     // rule North America Numbering Plan 1
       
   798     if (numberAType == EDigit && numberBType == EPlus) {
       
   799         TPtrC numberBPtr = numberB.Right(numberB.Length() - 1);
       
   800         result = !(numberA.Compare(numberBPtr));
       
   801         if (result) {
       
   802             return result;
       
   803         }
       
   804     }
       
   805 
       
   806     // More exceptional acceptance rules can be added here
       
   807     // Keep rules updated in the document Best_Number_Matching_Algorithm_Description.doc
       
   808 
       
   809     return result;
       
   810     }