|
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 } |