--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/contacts/symbian/plugin/src/filtering/cntfilterdetail.cpp Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,810 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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$
+**
+****************************************************************************/
+
+#include <centralrepository.h>
+#include <cntfldst.h>
+
+#include "cntfilterdetail.h"
+#include "cntfilterdetaildisplaylabel.h" //todo rename class to follow naming pattern CntFilterDetailDisplayLabel
+#include "cntsqlsearch.h"
+#include "cntsymbianengine.h"
+#include "cnttransformphonenumber.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);
+
+CntFilterDetail::CntFilterDetail(CContactDatabase& contactDatabase,CntSymbianSrvConnection &cntServer,CntDbInfo& dbInfo)
+ : m_contactdatabase(contactDatabase),
+ m_srvConnection(cntServer),
+ m_dbInfo(dbInfo),
+ m_emulateBestMatching(false)
+{
+}
+
+CntFilterDetail::~CntFilterDetail()
+{
+}
+
+
+QList<QContactLocalId> CntFilterDetail::contacts(
+ const QContactFilter &filter,
+ const QList<QContactSortOrder> &sortOrders,
+ bool &filterSupportedflag,
+ QContactManager::Error* error)
+{
+ Q_UNUSED(filterSupportedflag);
+ //Check if any invalid filter is passed
+ 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)) {
+ //Handle phonenumber ...
+ createMatchPhoneNumberQuery(filter,sqlQuery,error);
+ if (*error == QContactManager::NoError) {
+ //fetch the contacts
+ idList = m_srvConnection.searchContacts(sqlQuery,error);
+ }
+ }
+
+ else if (detailFilter.matchFlags() == QContactFilter::MatchKeypadCollation) {
+ //predictive search filter
+ idList = HandlePredictiveSearchFilter(filter,error);
+ }
+
+ // handle other cases
+ else {
+ createSelectQuery(filter,sqlQuery,error);
+ QString sortQuery = m_dbInfo.getSortQuery(sortOrders, sqlQuery, error);
+
+ 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()) {
+ result = true;
+ }
+ return result;
+}
+
+void CntFilterDetail::createSelectQuery(const QContactFilter& filter,
+ 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";
+ }
+ 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;
+ }
+}
+
+/*!
+ * Updates match flags for columns.
+ */
+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;
+ }
+ }
+}
+
+void CntFilterDetail::getTableNameWhereClause(const QContactDetailFilter& detailfilter,
+ QString& tableName,
+ QString& sqlWhereClause ,
+ QContactManager::Error* error) const
+{
+ //Get the table name and the column name
+ QString columnName;
+ bool isSubType;
+
+ m_dbInfo.getDbTableAndColumnName(detailfilter.detailDefinitionName(), detailfilter.detailFieldName(), tableName, columnName, isSubType);
+
+ // return if tableName is empty
+ if (tableName.isEmpty()) {
+ *error = QContactManager::NotSupportedError;
+ return;
+ }
+
+ //check columnName
+ if (columnName.isEmpty()) {
+ *error = QContactManager::NotSupportedError;
+ return;
+ }
+ else if (isSubType) {
+ sqlWhereClause += columnName;
+ sqlWhereClause += " NOT NULL ";
+ }
+ 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)
+{
+ QString sqlQuery;
+
+ if (filter.type() == QContactFilter::ContactDetailFilter) {
+ const QContactDetailFilter detailFilter(filter);
+ 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 {
+ return QList<QContactLocalId>();
+ }
+ }
+ else {
+ return QList<QContactLocalId>();
+ }
+}
+
+/*
+ * 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.
+ *
+ * Note that due to the way numbers are stored in the database, it is recommended
+ * that at least 7 match digits are specified even when matching a number
+ * containing fewer digits. Failure to follow this guideline may (depending on the
+ * database contents) mean that the function will not return the expected Contact
+ * IDs.
+ */
+void CntFilterDetail::createMatchPhoneNumberQuery(
+ const QContactFilter& filter,
+ QString& sqlQuery,
+ QContactManager::Error* error)
+
+{
+ if (!filterSupported(filter) ) {
+ *error = QContactManager::NotSupportedError;
+ return;
+ }
+
+ QContactDetailFilter detailFilter(filter);
+ QString number((detailFilter.value()).toString());
+ TPtrC numberPtr(reinterpret_cast<const TUint16*>(number.utf16()));
+
+ 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) {
+ numLowerDigits = KLowerSevenDigits;
+ numUpperDigits = matchLengthFromRight - KLowerSevenDigits;
+ }
+ else if (numLowerDigits == 0) {
+ // best match phonenumbers
+ numLowerDigits = KLowerSevenDigits;
+ }
+
+ TMatch phoneDigits = createPaddedPhoneDigits(
+ numberPtr, numLowerDigits, numUpperDigits, error);
+
+ 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 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...
+ TMatch phoneNumber = createPhoneMatchNumber(
+ numberPtr, numLowerDigits, numUpperDigits, error);
+ QString fieldToMatch = QString(" LIKE '%1").arg(phoneNumber.iUpperDigits) + "%'" ;
+ whereClause += " AND extra_value" + fieldToMatch;
+ sqlQuery = "SELECT contact_id FROM comm_addr" + whereClause;
+ }
+
+ // refine search
+ if (bestMatchingEnabled()) {
+ QList<QContactLocalId> list = m_srvConnection.searchContacts(sqlQuery,error);
+ QList<QContactLocalId> bestMatchingIds;
+ if (*error == QContactManager::NoError) {
+ TRAP_IGNORE(
+ bestMatchingIds = getBestMatchPhoneNumbersL(number, list, error);
+ )
+ if (bestMatchingIds.count()>0) {
+ // recreate query
+ QString selectQuery = " SELECT contact_id FROM comm_addr WHERE contact_id in (";
+ QString ids = QString("%1").arg(bestMatchingIds.at(0));
+ for(int i=1; i<bestMatchingIds.count(); ++i) {
+ ids += QString(" ,%1").arg(bestMatchingIds.at(i));
+ }
+ selectQuery += ids + ')';
+ sqlQuery = selectQuery;
+ }
+ else {
+ // empty list
+ QString selectQuery = " SELECT contact_id FROM comm_addr WHERE contact_id in (null)";
+ sqlQuery = selectQuery;
+ }
+ }
+ }
+ }
+}
+
+#ifdef PBK_UNIT_TEST
+void CntFilterDetail::emulateBestMatching()
+{
+ m_emulateBestMatching = true;
+}
+#endif
+
+/*
+ * Best matching number if matchLengthFromRight set to 0
+ */
+bool CntFilterDetail::bestMatchingEnabled()
+{
+#ifdef PBK_UNIT_TEST
+ if (m_emulateBestMatching) {
+ return true;
+ }
+#endif
+ bool result = false;
+ TInt matchLengthFromRight(KDefaultMatchLength);
+ TRAP_IGNORE(getMatchLengthL(matchLengthFromRight));
+ if (matchLengthFromRight == 0) {
+ result = true;
+ }
+ return result;
+}
+
+/*
+ * Get the match length setting. Digits to be used in matching (counted from
+ * right).
+ */
+bool CntFilterDetail::getMatchLengthL(TInt& matchLength)
+{
+#ifdef PBK_UNIT_TEST
+ if (m_emulateBestMatching) {
+ matchLength = 0;
+ return true;
+ }
+#endif
+ //Get number of digits used to match
+ bool result = false;
+ CRepository* repository = CRepository::NewL(KCRUidTelConfiguration);
+ 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;
+}
+
+/*
+ * 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.
+ */
+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;
+}
+
+QList<QContactLocalId> CntFilterDetail::getBestMatchPhoneNumbersL(
+ const QString number,
+ const QList<QContactLocalId>& idList,
+ QContactManager::Error* error)
+
+{
+ TPtrC numberPtr(reinterpret_cast<const TUint16*>(number.utf16()));
+ RBuf matchNumber;
+ matchNumber.CleanupClosePushL();
+ matchNumber.CreateL(numberPtr);
+
+ QList<QContactLocalId> bestMatchingIds;
+ for (int i=0; i<idList.count(); i++) {
+ QContact contact = m_dbInfo.engine()->contact(idList.at(i), QContactFetchHint(), error);
+ QList<QContactPhoneNumber> details = contact.details<QContactPhoneNumber>();
+ CntTransformContactData* transformPhoneNumber = new CntTransformPhoneNumber();
+
+ bool matchFound(false);
+ for (int j = 0;j < details.count(); j++) {
+ QList<CContactItemField *> fields = transformPhoneNumber->transformDetailL(details.at(j));
+ for (int k = 0;k < details.count() && !matchFound; k++) {
+ CContactTextField* storage = fields.at(k)->TextStorage();
+ RBuf phoneNumber;
+ phoneNumber.CleanupClosePushL();
+ phoneNumber.CreateL(storage->Text());
+ if (TMatch::validateBestMatchingRulesL(phoneNumber,matchNumber)) {
+ matchFound = true;
+ }
+ // phoneNumber
+ CleanupStack::PopAndDestroy();
+ }
+ if (matchFound) {
+ bestMatchingIds.append(idList.at(i));
+ break;
+ }
+ }
+ delete transformPhoneNumber;
+ }
+ // matchNumber
+ CleanupStack::PopAndDestroy();
+ return bestMatchingIds;
+}
+
+//CntFilterDetail::TMatch constructor.
+CntFilterDetail::TMatch::TMatch()
+ :
+ iLowerSevenDigits(0),
+ iUpperDigits(0),
+ iNumLowerDigits(0),
+ iNumUpperDigits(0)
+{
+}
+
+/*
+ * 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;
+ }
+
+ 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;
+}
+
+// Removes non-digit chars except plus form the beginning
+// Checks if number matches to one of defined types
+//
+TInt CntFilterDetail::TMatch::formatAndCheckNumberType(TDes& number)
+ {
+ _LIT( KOneZeroPattern, "0*" );
+ _LIT( KTwoZerosPattern, "00*" );
+ _LIT( KPlusPattern, "+*" );
+ const TChar KPlus = TChar('+');
+ const TChar KZero = TChar('0');
+ const TChar KAsterisk = TChar('*');
+ const TChar KHash = TChar('#');
+
+ for( TInt pos = 0; pos < number.Length(); ++pos ) {
+ TChar chr = number[pos];
+ if ( !chr.IsDigit() && !( pos == 0 && chr == KPlus )
+ && !( chr == KAsterisk ) && !( chr == KHash ) ) {
+ number.Delete( pos, 1 );
+ --pos;
+ }
+ }
+
+ TInt format;
+
+ if (!number.Match(KTwoZerosPattern) && number.Length() > 2 && number[2] != KZero) {
+ format = ETwoZeros;
+ }
+ else if (!number.Match(KOneZeroPattern)&& number.Length() > 1 && number[1] != KZero) {
+ format = EOneZero;
+ }
+ else if (!number.Match(KPlusPattern) && number.Length() > 1 && number[1] != KZero) {
+ format = EPlus;
+ }
+ else if (number.Length() > 0 && number[0] != KZero && ( ( TChar ) number[0] ).IsDigit()) {
+ format = EDigit;
+ }
+ else {
+ format = EUnknown;
+ }
+
+ return format;
+ }
+
+TBool CntFilterDetail::TMatch::validateBestMatchingRulesL(const TDesC& phoneNumber, const TDesC& matchNumber)
+ {
+ RBuf numberA;
+ numberA.CleanupClosePushL();
+ numberA.CreateL(matchNumber);
+ TNumberType numberAType = (TNumberType) TMatch::formatAndCheckNumberType(numberA);
+
+ RBuf numberB;
+ numberB.CleanupClosePushL();
+ numberB.CreateL(phoneNumber);
+ TNumberType numberBType = (TNumberType) TMatch::formatAndCheckNumberType(numberB);
+
+ TBool match = (!numberB.Compare(numberA) ||
+ TMatch::checkBestMatchingRules(numberA, numberAType, numberB, numberBType) ||
+ TMatch::checkBestMatchingRules(numberB, numberBType, numberA, numberAType));
+
+ // cleanup
+ CleanupStack::PopAndDestroy(2);
+ return match;
+ }
+
+TBool CntFilterDetail::TMatch::checkBestMatchingRules(
+ const TDesC& numberA, TNumberType numberAType,
+ const TDesC& numberB, TNumberType numberBType )
+ {
+ TBool result = EFalse;
+
+ // Rules for matching not identical numbers
+ // Rules details are presented in Best_Number_Matching_Algorithm_Description.doc
+
+ // rule International-International 1
+ if (!result && numberAType == EPlus && numberBType == ETwoZeros) {
+ TPtrC numberAPtr = numberA.Right(numberA.Length() - 1);
+ TPtrC numberBPtr = numberB.Right(numberB.Length() - 2);
+ result = !(numberAPtr.Compare(numberBPtr));
+ if (result) {
+ return result;
+ }
+ }
+
+ // rule International-International 2
+ if (numberAType == EPlus && numberBType == EDigit) {
+ TPtrC numberAPtr = numberA.Right( numberA.Length() - 1 );
+ if (numberAPtr.Length() < numberB.Length()) {
+ TPtrC numberBPtr = numberB.Right( numberAPtr.Length() );
+ result = !(numberAPtr.Compare(numberBPtr));
+ if (result) {
+ return result;
+ }
+ }
+ }
+
+ // rule International-International 3
+ if (numberAType == ETwoZeros && numberBType == EDigit) {
+ TPtrC numberAPtr = numberA.Right(numberA.Length() - 2);
+ if (numberAPtr.Length() < numberB.Length()) {
+ TPtrC numberBPtr = numberB.Right(numberAPtr.Length());
+ result = !(numberAPtr.Compare(numberBPtr));
+ if (result) {
+ return result;
+ }
+ }
+ }
+
+ // rule International-Operator 1
+ if (numberAType == EOneZero && numberBType == EPlus
+ || numberAType == EDigit && numberBType == EPlus) {
+ TPtrC numberAPtr;
+ if (numberAType == EOneZero) {
+ numberAPtr.Set(numberA.Right(numberA.Length() - 1));
+ }
+ else {
+ numberAPtr.Set(numberA);
+ }
+ if (numberAPtr.Length() < numberB.Length() - 1) {
+ TPtrC numberBPtr = numberB.Right(numberAPtr.Length());
+ result = !(numberAPtr.Compare(numberBPtr));
+ if (result) {
+ return result;
+ }
+ }
+ }
+
+ // rule International-Operator 2
+ if (numberAType == EOneZero && numberBType == ETwoZeros
+ || numberAType == EDigit && numberBType == ETwoZeros) {
+ TPtrC numberAPtr;
+ if (numberAType == EOneZero) {
+ numberAPtr.Set(numberA.Right(numberA.Length() - 1));
+ }
+ else {
+ numberAPtr.Set(numberA);
+ }
+ if (numberAPtr.Length() < numberB.Length() - 2) {
+ TPtrC numberBPtr = numberB.Right(numberAPtr.Length());
+ result = !(numberAPtr.Compare(numberBPtr));
+ if (result) {
+ return result;
+ }
+ }
+ }
+
+ // rule International-Operator 3
+ if (numberAType == EOneZero && numberBType == EDigit
+ || numberAType == EDigit && numberBType == EDigit) {
+ TPtrC numberAPtr;
+ if (numberAType == EOneZero) {
+ numberAPtr.Set(numberA.Right( numberA.Length() - 1));
+ }
+ else {
+ numberAPtr.Set(numberA);
+ }
+ if (numberAPtr.Length() < numberB.Length()) {
+ TPtrC numberBPtr = numberB.Right(numberAPtr.Length());
+ result = !(numberAPtr.Compare(numberBPtr));
+ if (result) {
+ return result;
+ }
+ }
+ }
+
+ // rule Operator-Operator 1
+ if (numberAType == EOneZero && numberBType == EDigit) {
+ TPtrC numberAPtr = numberA.Right(numberA.Length() - 1);
+ result = !(numberAPtr.Compare(numberB));
+ if (result) {
+ return result;
+ }
+ }
+
+ // rule North America Numbering Plan 1
+ if (numberAType == EDigit && numberBType == EPlus) {
+ TPtrC numberBPtr = numberB.Right(numberB.Length() - 1);
+ result = !(numberA.Compare(numberBPtr));
+ if (result) {
+ return result;
+ }
+ }
+
+ // More exceptional acceptance rules can be added here
+ // Keep rules updated in the document Best_Number_Matching_Algorithm_Description.doc
+
+ return result;
+ }