qtcontactsmobility/plugins/contacts/symbian/src/filtering/cntfilterdetail.cpp
changeset 37 fd64c38c277d
parent 27 de1630741fbe
child 40 b46a585f6909
--- a/qtcontactsmobility/plugins/contacts/symbian/src/filtering/cntfilterdetail.cpp	Fri May 14 15:42:23 2010 +0300
+++ b/qtcontactsmobility/plugins/contacts/symbian/src/filtering/cntfilterdetail.cpp	Thu May 27 12:45:19 2010 +0300
@@ -60,12 +60,10 @@
                                           m_srvConnection(cntServer),
                                           m_dbInfo(dbInfo)
 {
-
 }
 
 CntFilterDetail::~CntFilterDetail()
 {
-    
 }
 
 
@@ -77,174 +75,149 @@
 {
     Q_UNUSED(filterSupportedflag);
     //Check if any invalid filter is passed 
-    if(!filterSupported(filter) )
-        {
+    if (!filterSupported(filter) ) {
         *error =  QContactManager::NotSupportedError;
         return QList<QContactLocalId>();
-        }
+    }
     QList<QContactLocalId> idList;
     QContactDetailFilter detailFilter(filter);
     QString sqlQuery;
     //Check for phonenumber. Special handling needed
-    if( (detailFilter.detailDefinitionName() == QContactPhoneNumber::DefinitionName ) &&
-            (detailFilter.detailFieldName() != QContactPhoneNumber::FieldSubTypes))
-    {
+    if ( (detailFilter.detailDefinitionName() == QContactPhoneNumber::DefinitionName ) &&
+            (detailFilter.detailFieldName() != QContactPhoneNumber::FieldSubTypes)) {
         //Handle phonenumber ...
-        idList = HandlePhonenumberDetailFilter(filter);
+        createMatchPhoneNumberQuery(filter,sqlQuery,error);
+        if (*error == QContactManager::NoError) {
+            //fetch the contacts
+            idList =  m_srvConnection.searchContacts(sqlQuery, error);
+        }
         
     }
-    else if (detailFilter.matchFlags() == QContactFilter::MatchKeypadCollation) 
-    {
+    else if (detailFilter.matchFlags() == QContactFilter::MatchKeypadCollation) {
         //predictive search filter
         idList = HandlePredictiveSearchFilter(filter,error);
     }
             
     // handle other cases
-    else 
-    {
+    else {
         createSelectQuery(filter,sqlQuery,error);
         QString sortQuery = m_dbInfo.getSortQuery(sortOrders, sqlQuery, error);
         
-        if(*error == QContactManager::NoError)
-            {
+        if (*error == QContactManager::NoError) {
             //fetch the contacts
             idList =  m_srvConnection.searchContacts(sortQuery, error);
-            }
+        }
     }
-    
     return idList;
-
-    
-    
 }
 
-
 bool CntFilterDetail::filterSupported(const QContactFilter& filter) 
 {
     bool result = false;
-    if(QContactFilter::ContactDetailFilter == filter.type())
-        {
+    if (QContactFilter::ContactDetailFilter == filter.type()) {
         result = true;
-        }
- 
+    }
     return result;
 }
 
 void CntFilterDetail::createSelectQuery(const QContactFilter& filter,
-                              QString& sqlQuery,
-                              QContactManager::Error* error)
+                                        QString& sqlQuery,
+                                        QContactManager::Error* error)
 
 {
-      if(!filterSupported(filter) )
-      {
-          *error = QContactManager::NotSupportedError;
-          return;
-      }
-      QContactDetailFilter detailFilter(filter);
-      //display label
-      if (detailFilter.detailDefinitionName() == QContactDisplayLabel::DefinitionName)
-       {
-          CntFilterDetailDisplayLabel displayLabelFilter;
-          displayLabelFilter.createSelectQuery(filter, 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";
+    if (!filterSupported(filter)) {
+      *error = QContactManager::NotSupportedError;
+      return;
+    }
+    QContactDetailFilter detailFilter(filter);
+    //display label
+    if (detailFilter.detailDefinitionName() == QContactDisplayLabel::DefinitionName) {
+      CntFilterDetailDisplayLabel displayLabelFilter;
+      displayLabelFilter.createSelectQuery(filter, 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";
+    }
+    else if (detailFilter.detailDefinitionName() == QContactGuid::DefinitionName) {
+       if (detailFilter.detailFieldName() == QContactGuid::FieldGuid) {
+           QStringList fullGuidValue = detailFilter.value().toString().split('-');
+           if (fullGuidValue.count() == 3) {
+               QString localGuidValue = fullGuidValue.at(1);
+               sqlQuery = "SELECT contact_id FROM contact WHERE guid_string = '" + localGuidValue + '\'';
+           }
        }
-       else if(detailFilter.detailDefinitionName() == QContactGuid::DefinitionName)
-       {
-           if(detailFilter.detailFieldName() == QContactGuid::FieldGuid)
-           {
-               QStringList fullGuidValue = detailFilter.value().toString().split('-');
-               if (fullGuidValue.count() == 3) {
-                   QString localGuidValue = fullGuidValue.at(1);
-                   sqlQuery = "SELECT contact_id FROM contact WHERE guid_string = '" + localGuidValue + '\'';
-                }
-            }
-       }
-       //everything else
-       else
-       {   
-           QString tableName;
-           QString sqlWhereClause;
-           getTableNameWhereClause(detailFilter,tableName,sqlWhereClause,error);
-           //Create the sql query
-           sqlQuery += "SELECT DISTINCT contact_id FROM " + tableName + " WHERE " + sqlWhereClause;
-       }
+    }
+    //everything else
+    else {   
+       QString tableName;
+       QString sqlWhereClause;
+       getTableNameWhereClause(detailFilter,tableName,sqlWhereClause,error);
+       //Create the sql query
+       sqlQuery += "SELECT DISTINCT contact_id FROM " + tableName + " WHERE " + sqlWhereClause;
+    }
 }
 
-
 /*!
- * Updates match flage for columns.
+ * Updates match flags for columns.
  */
-void CntFilterDetail::updateForMatchFlag( const QContactDetailFilter& filter,
-                                          QString& fieldToUpdate ,
-                                          QContactManager::Error* error) const
+void CntFilterDetail::updateForMatchFlag(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 = " ='"
-                               + filter.value().toString() + '\'';
-                *error = QContactManager::NoError;
-                break;
-                }
-            case QContactFilter::MatchContains:
-                {
-                // Pattern for MatchContains:
-                // " LIKE '%xyz%'"
-                fieldToUpdate = " LIKE '%" + filter.value().toString() + "%'" ;
-                *error = QContactManager::NoError;
-                break;
-                }
-            case QContactFilter::MatchStartsWith:
-                {
-                // Pattern for MatchStartsWith:
-                // " LIKE 'xyz%'"
-                fieldToUpdate = " LIKE '" +  filter.value().toString() + "%'"  ;
-                *error = QContactManager::NoError;
-                break;
-                }
-            case QContactFilter::MatchEndsWith:
-                {
-                // Pattern for MatchEndsWith:
-                // " LIKE '%xyz'"
-                fieldToUpdate = " LIKE '%" + filter.value().toString() + '\'' ;
-                *error = QContactManager::NoError;
-                break;
-                }
-            case QContactFilter::MatchFixedString:
-                {
-                *error = QContactManager::NotSupportedError;
-                break;
-                }
-            case QContactFilter::MatchCaseSensitive:
-                {
-                *error = QContactManager::NotSupportedError;
-                break;
-                }
-            default:
-                {
-                *error = QContactManager::NotSupportedError;
-                break;
-                }
+    switch (filter.matchFlags()) {
+        case QContactFilter::MatchExactly: {
+            // Pattern for MatchExactly:
+            // " ='xyz'"
+            fieldToUpdate = " ='"
+                           + filter.value().toString() + '\'';
+            *error = QContactManager::NoError;
+            break;
+        }
+        case QContactFilter::MatchContains: {
+            // Pattern for MatchContains:
+            // " LIKE '%xyz%'"
+            fieldToUpdate = " LIKE '%" + filter.value().toString() + "%'" ;
+            *error = QContactManager::NoError;
+            break;
         }
-        
+        case QContactFilter::MatchStartsWith: {
+            // Pattern for MatchStartsWith:
+            // " LIKE 'xyz%'"
+            fieldToUpdate = " LIKE '" +  filter.value().toString() + "%'"  ;
+            *error = QContactManager::NoError;
+            break;
+        }
+        case QContactFilter::MatchEndsWith: {
+            // Pattern for MatchEndsWith:
+            // " LIKE '%xyz'"
+            fieldToUpdate = " LIKE '%" + filter.value().toString() + '\'' ;
+            *error = QContactManager::NoError;
+            break;
+        }
+        case QContactFilter::MatchFixedString: {
+            *error = QContactManager::NotSupportedError;
+            break;
+        }
+        case QContactFilter::MatchCaseSensitive: {
+            *error = QContactManager::NotSupportedError;
+            break;
+        }
+        default: {
+            *error = QContactManager::NotSupportedError;
+            break;
+        }
+    }
 }
 
-void CntFilterDetail::getTableNameWhereClause( const QContactDetailFilter& detailfilter,
-                                               QString& tableName,
-                                               QString& sqlWhereClause ,
-                                               QContactManager::Error* error) const
+void CntFilterDetail::getTableNameWhereClause(const QContactDetailFilter& detailfilter,
+                                              QString& tableName,
+                                              QString& sqlWhereClause ,
+                                              QContactManager::Error* error) const
 {
     //Get the table name and the column name
     QString columnName;
@@ -253,119 +226,282 @@
     m_dbInfo.getDbTableAndColumnName(detailfilter.detailDefinitionName(), detailfilter.detailFieldName(), tableName, columnName, isSubType);
 
     // return if tableName is empty
-    if(tableName.isEmpty())
-        {
+    if (tableName.isEmpty()) {
         *error = QContactManager::NotSupportedError;
         return;
-        }
+    }
 
     //check columnName
-    if(columnName.isEmpty())
-        {
+    if (columnName.isEmpty()) {
         *error = QContactManager::NotSupportedError;
         return;
-        }
-    else if (isSubType) 
-        {
+    }
+    else if (isSubType) {
         sqlWhereClause += columnName;
         sqlWhereClause += " NOT NULL ";
-        }
-    else 
-        {
+    }
+    else {
         sqlWhereClause += ' ' + columnName + ' ';
         QString fieldToUpdate;
         //Update the value depending on the match flag
         updateForMatchFlag(detailfilter,fieldToUpdate,error);
         sqlWhereClause +=  fieldToUpdate;
-        }
+    }
 }
 
-QList<QContactLocalId>  CntFilterDetail::HandlePredictiveSearchFilter(const QContactFilter& filter,QContactManager::Error* error)
-    {
-    
+QList<QContactLocalId>  CntFilterDetail::HandlePredictiveSearchFilter(const QContactFilter& filter,
+                                                                      QContactManager::Error* error)
+{
     QString sqlQuery;
     
-    if(filter.type() == QContactFilter::ContactDetailFilter){
+    if (filter.type() == QContactFilter::ContactDetailFilter) {
        const QContactDetailFilter detailFilter(filter);
-       if(  detailFilter.matchFlags() == QContactFilter::MatchKeypadCollation ){
-       CntSqlSearch sqlSearch;
-          //convert string to numeric format
+       if (detailFilter.matchFlags() == QContactFilter::MatchKeypadCollation) {
+           CntSqlSearch sqlSearch;
+           //convert string to numeric format
             QString pattern = detailFilter.value().toString();
             sqlQuery = sqlSearch.CreatePredictiveSearch(pattern);
             return  m_srvConnection.searchContacts(sqlQuery, error);  
-           }
-       else
-           {
+       }
+       else {
            return QList<QContactLocalId>();
-           }
-        }
-    else
-        {
+       }
+    }
+    else {
         return QList<QContactLocalId>();
-        }
     }
+}
 
-QList<QContactLocalId> CntFilterDetail::HandlePhonenumberDetailFilter(const QContactFilter& filter)
-    {
-    QList<QContactLocalId> matches;
-    QContactDetailFilter detailFilter(filter);
-    // Phone numbers need separate handling
+/*
+ * Creates an sql query to fetch contact item IDs for all the contact items
+ * which may contain the specified telephone number in a telephone, fax
+ * or SMS type field.
+ *
+ * The comparison method used is not exact.  The number is compared starting from
+ * the right side of the number and the method returns an array of candidate
+ * matches.  Punctuation (e.g. spaces) and other alphabetic characters are ignored
+ * when comparing.
+ */
+void CntFilterDetail::createMatchPhoneNumberQuery(
+                                      const QContactFilter& filter,
+                                      QString& sqlQuery,
+                                      QContactManager::Error* error)
 
-    QString number((detailFilter.value()).toString());
-    TPtrC commPtr(reinterpret_cast<const TUint16*>(number.utf16()));
+{
+  if (!filterSupported(filter) ) {
+      *error = QContactManager::NotSupportedError;
+      return;
+  }
+  
+  QContactDetailFilter detailFilter(filter);
+  QString number((detailFilter.value()).toString());
+  TPtrC numberPtr(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));
-    int actualLength = number.length();
+  TInt matchLengthFromRight(KDefaultMatchLength);
+  // no need to propagate error, we can use the default match length
+  TRAP_IGNORE(getMatchLengthL(matchLengthFromRight));
+  
+  TInt numLowerDigits = matchLengthFromRight;
+  TInt numUpperDigits = 0;
+
+  if (numLowerDigits > KLowerSevenDigits) {
+      // New style matching.
+      numLowerDigits = KLowerSevenDigits;
+      numUpperDigits = matchLengthFromRight - KLowerSevenDigits;
+  }
+
+  TMatch phoneDigits = createPaddedPhoneDigits(
+                          numberPtr, numLowerDigits, numUpperDigits, error);
 
-    //call 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;
-
-    }
+  if (*error == QContactManager::NoError) {
+      // select fields for contacts that match phone lookup
+      //  SELECT contact_id FROM comm_addr
+      //      WHERE value = [value string] AND type = [type value];
+      //
+      QString type =  QString(" type = %1").arg(CntDbInfo::EPhoneNumber);
+      QString value =  QString(" value = %1").arg(phoneDigits.iLowerSevenDigits);
+      QString extraValue =  QString(" extra_value = %1").arg(phoneDigits.iUpperDigits);
+      QString whereClause = " WHERE" + value + " AND" + type;
+      if (matchLengthFromRight <= KLowerSevenDigits) {
+          // Matching 7 or less digits...
+          sqlQuery = "SELECT contact_id FROM comm_addr" + whereClause;
+      }
+      else {
+          // Checking the upper digits...
+          whereClause += " AND" + extraValue;
+          sqlQuery = "SELECT contact_id FROM comm_addr" + whereClause;
+      }
+  }
+}
 /*
  * Get the match length setting. Digits to be used in matching (counted from
  * right).
  */
-void CntFilterDetail::getMatchLengthL(TInt& matchLength)
+bool CntFilterDetail::getMatchLengthL(TInt& matchLength)
 {
     //Get number of digits used to match
+    bool result = false;
     CRepository* repository = CRepository::NewL(KCRUidTelConfiguration);
-    CleanupStack::PushL(repository);
-    User::LeaveIfError(repository->Get(KTelMatchDigits, matchLength));
-    CleanupStack::PopAndDestroy(repository);
+    TInt err = repository->Get(KTelMatchDigits, matchLength);
+    delete repository;
+    
+    result = (err == KErrNone);
+    return result;
+}
+
+/*
+ * Convert the supplied string to a matchable phone number.
+ *
+ * \param text Descriptor containing phone number.
+ * \param lowerMatchlength Number of least significant phone digits to use.
+ * \param upperMatchLength Number of most significant phone digits to use.
+ * \param error Qt error code.
+ * \return The hash code(s) to use when matching the supplied phone number.
+ */
+CntFilterDetail::TMatch CntFilterDetail::createPaddedPhoneDigits(
+                                            const TDesC& number, 
+                                            const TInt numLowerDigits, 
+                                            const TInt numUpperDigits,
+                                            QContactManager::Error* error)
+{
+    TMatch phoneNumber = createPhoneMatchNumber(
+                                            number, numLowerDigits, numUpperDigits, error);
+    if (*error == QContactManager::NoError) {
+        if (phoneNumber.iNumLowerDigits + phoneNumber.iUpperDigits == 0) {
+            // No digits, do nothing
+        }
+        else if (phoneNumber.iNumLowerDigits < KLowerSevenDigits) {
+            // Only the lower-digits hash is used, pad out the number to
+            // KLowerSevenDigits.
+            TInt pad = KLowerSevenDigits - phoneNumber.iNumLowerDigits;
+            phoneNumber.iLowerSevenDigits = TMatch::padOutPhoneMatchNumber(phoneNumber.iLowerSevenDigits,pad);
+        }
+        else if (phoneNumber.iNumUpperDigits < (KMaxPhoneMatchLength - KLowerSevenDigits) ) {
+            // The lower-digits hash is full, pad out the upper hash if less than 15
+            // digits total.
+            TInt pad = KMaxPhoneMatchLength - KLowerSevenDigits - phoneNumber.iNumUpperDigits;
+            phoneNumber.iUpperDigits = TMatch::padOutPhoneMatchNumber(phoneNumber.iUpperDigits,pad);
+        }
+    }
+    return phoneNumber;
 }
 
 /*
- * 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.
+ * Returns the hash code(s) to use when matching the supplied phone number.  If the
+ * number supplied has more actual phone digits (i.e. not including spaces) than
+ * KLowerSevenDigits, a second hash is generated to hold the remaining most
+ * significant phone digits. Removes the non-digit characters.
+
+ * \param text Descriptor containing contacts phone number field.
+ * \param lowerMatchlength Number of least significant phone digits to use.
+ * \param upperMatchLength Number of most significant phone digits to use.
+ * \param error Qt error code.
+ * \return The hash code(s) to use when matching the supplied phone number.
  */
-TInt CntFilterDetail::searchPhoneNumbers(
-        CContactIdArray*& idArray,
-        const TDesC& phoneNumber,
-        const TInt matchLength)
+CntFilterDetail::TMatch CntFilterDetail::createPhoneMatchNumber(
+                                            const TDesC& text, 
+                                            TInt lowerMatchLength, 
+                                            TInt upperMatchLength,
+                                            QContactManager::Error* error)
+{
+    const TInt KBufLength = KCntMaxTextFieldLength+1;
+    TBuf<KBufLength> buf;
+    
+    if (text.Length() <= KBufLength) {
+        buf = text;
+    }
+    else {
+        buf = text.Right(KBufLength);
+    }
+    TMatch::stripOutNonDigitChars(buf);
+    
+    TMatch phoneNumber;
+    if (buf.Length() == 0) {
+        *error = QContactManager::BadArgumentError;
+        return phoneNumber;
+    }
+    
+    // Generate a hash for the upper digits only if the phone number string is
+    // large enough and more than 7 digits are to be matched.
+    TInt phoneNumberlength = buf.Length();
+    if ((phoneNumberlength > KLowerSevenDigits) && (upperMatchLength > 0)) {
+        TPtrC upperPart = buf.Left(phoneNumberlength - KLowerSevenDigits);
+        phoneNumber.iUpperDigits = TMatch::createHash(upperPart,
+            upperMatchLength, phoneNumber.iNumUpperDigits);
+    }
+    // Generate a hash of the lower digits.
+    phoneNumber.iLowerSevenDigits = TMatch::createHash(buf, 
+            lowerMatchLength, phoneNumber.iNumLowerDigits);
+    
+    return phoneNumber;
+}
+
+//CntFilterDetail::TMatch constructor.
+CntFilterDetail::TMatch::TMatch()
+    :
+    iLowerSevenDigits(0),
+    iUpperDigits(0),
+    iNumLowerDigits(0),
+    iNumUpperDigits(0)
 {
-    CContactIdArray* idArrayTmp(0);
-    TRAPD( err, idArrayTmp = m_contactdatabase.MatchPhoneNumberL(phoneNumber, matchLength));
-    if(err == KErrNone){
-       idArray = idArrayTmp;
+}
+
+/*
+ * Generates a hash value by reversing the matchLength least significant digits,
+ * ignoring non-digits and zeroes at the end of the number.  Returns error if no phone
+ * digits are supplied.
+
+ * \param phoneNumberString A descriptor containing a phone number.
+ * \param matchLength The number of digits from the right of the phone number to use.
+ * \param numPhoneDigits The number of digits found in the phone number string.
+ * \param error Qt error code.*
+ * \return An integer representation of the phone number string in reverse.
+ */
+TInt32 CntFilterDetail::TMatch::createHash(
+                                    const TDesC& phoneNumberString, 
+                                    TInt matchLength, 
+                                    TInt& numPhoneDigits)
+{
+    TInt phoneNumberLength = phoneNumberString.Length();
+    TInt startIndex = 0;
+    if (phoneNumberLength > matchLength) {
+        startIndex = phoneNumberLength - matchLength;
     }
-    return 0;
+    
+    numPhoneDigits = 0;
+    TUint reversedDigits = 0;
+    TInt mult = 1;
+    
+    for (TInt chrIndex = startIndex; (numPhoneDigits < matchLength) && (chrIndex < phoneNumberLength); chrIndex++) {
+        TChar chr = phoneNumberString[chrIndex];
+        if (chr.IsDigit()) {
+            reversedDigits += (chr.GetNumericValue()) * mult;
+            mult = mult * 10;
+            ++numPhoneDigits;
+        }
+    }
+    return reversedDigits ;
 }
+
+void CntFilterDetail::TMatch::stripOutNonDigitChars(TDes& text)
+{
+    for (TInt chrPos = 0; chrPos < text.Length(); ++chrPos) {
+        TChar chr = text[chrPos];
+        if (!chr.IsDigit()) {
+            text.Delete(chrPos, 1);
+            --chrPos;
+        }
+    }
+}
+
+TInt32 CntFilterDetail::TMatch::padOutPhoneMatchNumber(TInt32& phoneNumber,
+                                                       TInt padOutLength)
+{
+    TInt32 result(phoneNumber);
+    const TInt KMult(10);
+    for (TInt i = 0; i < padOutLength; ++i) {
+        result *= KMult;
+    }
+    phoneNumber = result;
+    return result;
+}