diff options
author | Mateusz Polrola <mateuszx.potrola@intel.com> | 2014-12-12 12:50:09 +0000 |
---|---|---|
committer | Mateusz Polrola <mateusz.polrola@gmail.com> | 2015-01-15 08:39:09 +0100 |
commit | b52e2d3a0030d6467f372ee246b6848c49412281 (patch) | |
tree | 116324b6c25c633d76f6cb2788ccb4537736ba47 | |
parent | a02c510d95457ec04d82773284a2e4e6714536ec (diff) | |
download | evolution-data-server-intel-work-3-12.tar.gz |
EBookSqlite: Added new phone lookup operator.intel-work-3-12
New phone lookup operator uses custom phone matching function, that does
not relies on currently set locale for given address book.
-rw-r--r-- | addressbook/libebook-contacts/e-book-query.c | 13 | ||||
-rw-r--r-- | addressbook/libebook-contacts/e-book-query.h | 1 | ||||
-rw-r--r-- | addressbook/libebook-contacts/e-phone-number-private.cpp | 13 | ||||
-rw-r--r-- | addressbook/libebook-contacts/e-phone-number-private.h | 2 | ||||
-rw-r--r-- | addressbook/libebook-contacts/e-phone-number.c | 318 | ||||
-rw-r--r-- | addressbook/libebook-contacts/e-phone-number.h | 6 | ||||
-rw-r--r-- | addressbook/libedata-book/e-book-backend-sexp.c | 33 | ||||
-rw-r--r-- | addressbook/libedata-book/e-book-sqlite.c | 143 |
8 files changed, 526 insertions, 3 deletions
diff --git a/addressbook/libebook-contacts/e-book-query.c b/addressbook/libebook-contacts/e-book-query.c index 518fb31d0..2baf04a3e 100644 --- a/addressbook/libebook-contacts/e-book-query.c +++ b/addressbook/libebook-contacts/e-book-query.c @@ -652,6 +652,15 @@ func_eqphone_short (struct _ESExp *f, } static ESExpResult * +func_eqphone_is (struct _ESExp *f, + gint argc, + struct _ESExpResult **argv, + gpointer data) +{ + return func_field_test (E_BOOK_QUERY_PHONE_NUMBER_IS, f, argc, argv, data); +} + +static ESExpResult * func_regex_normal (struct _ESExp *f, gint argc, struct _ESExpResult **argv, @@ -777,6 +786,7 @@ static const struct { { "eqphone", func_eqphone, 0 }, { "eqphone_national", func_eqphone_national, 0 }, { "eqphone_short", func_eqphone_short, 0 }, + { "eqphone_is", func_eqphone_is, 0 }, { "regex_translit", func_regex_translit, 0 }, { "regex_normal", func_regex_normal, 0 }, { "regex_raw", func_regex_raw, 0 }, @@ -860,6 +870,8 @@ field_test_name (EBookQueryTest field_test) return "eqphone_national"; case E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER: return "eqphone_short"; + case E_BOOK_QUERY_PHONE_NUMBER_IS: + return "eqphone_is"; case E_BOOK_QUERY_REGEX_NORMAL: return "regex_normal"; case E_BOOK_QUERY_REGEX_RAW: @@ -888,6 +900,7 @@ is_phone_test (EBookQueryTest field_test) case E_BOOK_QUERY_EQUALS_PHONE_NUMBER: case E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER: case E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER: + case E_BOOK_QUERY_PHONE_NUMBER_IS: return TRUE; case E_BOOK_QUERY_IS: diff --git a/addressbook/libebook-contacts/e-book-query.h b/addressbook/libebook-contacts/e-book-query.h index 1c74f4ad8..592854100 100644 --- a/addressbook/libebook-contacts/e-book-query.h +++ b/addressbook/libebook-contacts/e-book-query.h @@ -77,6 +77,7 @@ typedef enum { E_BOOK_QUERY_EQUALS_PHONE_NUMBER, E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER, E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER, + E_BOOK_QUERY_PHONE_NUMBER_IS, E_BOOK_QUERY_REGEX_NORMAL, E_BOOK_QUERY_REGEX_RAW, diff --git a/addressbook/libebook-contacts/e-phone-number-private.cpp b/addressbook/libebook-contacts/e-phone-number-private.cpp index f41e75b9f..ff359ae87 100644 --- a/addressbook/libebook-contacts/e-phone-number-private.cpp +++ b/addressbook/libebook-contacts/e-phone-number-private.cpp @@ -139,6 +139,19 @@ _e_phone_number_cxx_get_default_region () return g_strdup (_e_phone_number_cxx_make_region_code (NULL).c_str ()); } +gchar * +_e_phone_number_cxx_normalize (const gchar *phone_number) +{ + std::string number = phone_number; + std::size_t pos = number.find_first_of('+'); + if (pos != std::string::npos) { + number.replace (pos, 1, "00"); + } + + e_phone_number_util_get_instance ()->NormalizeDigitsOnly (&number); + return g_strdup (number.c_str()); +} + static bool _e_phone_number_cxx_parse (const std::string &phone_number, const std::string ®ion, diff --git a/addressbook/libebook-contacts/e-phone-number-private.h b/addressbook/libebook-contacts/e-phone-number-private.h index ac5b70171..700d8b8d5 100644 --- a/addressbook/libebook-contacts/e-phone-number-private.h +++ b/addressbook/libebook-contacts/e-phone-number-private.h @@ -54,6 +54,8 @@ E_PHONE_NUMBER_LOCAL gint _e_phone_number_cxx_get_country_code_for_region (const gchar *region_code); E_PHONE_NUMBER_LOCAL gchar * _e_phone_number_cxx_get_default_region (void); +E_PHONE_NUMBER_LOCAL gchar * _e_phone_number_cxx_normalize (const gchar *phone_number); + E_PHONE_NUMBER_LOCAL EPhoneNumber * _e_phone_number_cxx_from_string (const gchar *phone_number, const gchar *region_code, GError **error); diff --git a/addressbook/libebook-contacts/e-phone-number.c b/addressbook/libebook-contacts/e-phone-number.c index c167d86c3..427deeca3 100644 --- a/addressbook/libebook-contacts/e-phone-number.c +++ b/addressbook/libebook-contacts/e-phone-number.c @@ -157,6 +157,33 @@ e_phone_number_get_default_region (GError **error) } /** + * e_phone_number_normalize: + * @phone_number: phone number string + * @error: (out): a #GError to set an error, if any + * + * Normalizes provided phone number string. + * + * Returns: (transfer full): a newly allocated string containing the + * normalized phone number. + * + * Since: 3.12 + */ +gchar * +e_phone_number_normalize (const char *phone_number, GError **error) +{ +#ifdef ENABLE_PHONENUMBER + + return _e_phone_number_cxx_normalize (phone_number); + +#else /* ENABLE_PHONENUMBER */ + + _e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED); + return NULL; + +#endif /* ENABLE_PHONENUMBER */ +} + +/** * e_phone_number_from_string: * @phone_number: the phone number to parse * @region_code: (allow-none): a two-letter country code, or %NULL @@ -422,3 +449,294 @@ e_phone_number_free (EPhoneNumber *phone_number) #endif /* ENABLE_PHONENUMBER */ } + +/** + * match_exit_code: + * @phone: phone number string + * @max: number of characters to be checked (indexed from 0 or -1 if whole string should be checked) + * @up_to (out): number of matched characters (indexed from 0) + * + * Checks if given phone string begins with valid exit code. + * Tries to match exit codes from longest to shortest ones. + * + * @Returns: TRUE in case that string begins with valid exit code, false otherwise. + * + * Since: 3.12 + **/ +static gboolean +match_exit_code(const char* phone, int max, int* up_to) +{ + if (max == 4 || max == -1) + { + *up_to = 4; + if (strncmp (phone, "00414", 5) == 0) return TRUE; + if (strncmp (phone, "00468", 5) == 0) return TRUE; + if (strncmp (phone, "00456", 5) == 0) return TRUE; + if (strncmp (phone, "00444", 5) == 0) return TRUE; + } + + if (max == 3 || max == -1) + { + *up_to = 3; + if (strncmp (phone, "0011", 4) == 0) return TRUE; + if (strncmp (phone, "0014", 4) == 0) return TRUE; + if (strncmp (phone, "0015", 4) == 0) return TRUE; + if (strncmp (phone, "0021", 4) == 0) return TRUE; + if (strncmp (phone, "0023", 4) == 0) return TRUE; + if (strncmp (phone, "0031", 4) == 0) return TRUE; + if (strncmp (phone, "1230", 4) == 0) return TRUE; + if (strncmp (phone, "1200", 4) == 0) return TRUE; + if (strncmp (phone, "1220", 4) == 0) return TRUE; + if (strncmp (phone, "1810", 4) == 0) return TRUE; + if (strncmp (phone, "1690", 4) == 0) return TRUE; + if (strncmp (phone, "1710", 4) == 0) return TRUE; + } + + if (max == 2 || max == -1) + { + *up_to = 2; + if (strncmp (phone, "011", 3) == 0) return TRUE; + if (strncmp (phone, "001", 3) == 0) return TRUE; + if (strncmp (phone, "006", 3) == 0) return TRUE; + if (strncmp (phone, "007", 3) == 0) return TRUE; + if (strncmp (phone, "008", 3) == 0) return TRUE; + if (strncmp (phone, "119", 3) == 0) return TRUE; + if (strncmp (phone, "005", 3) == 0) return TRUE; + if (strncmp (phone, "007", 3) == 0) return TRUE; + if (strncmp (phone, "009", 3) == 0) return TRUE; + if (strncmp (phone, "990", 3) == 0) return TRUE; + if (strncmp (phone, "994", 3) == 0) return TRUE; + if (strncmp (phone, "999", 3) == 0) return TRUE; + if (strncmp (phone, "013", 3) == 0) return TRUE; + if (strncmp (phone, "014", 3) == 0) return TRUE; + if (strncmp (phone, "018", 3) == 0) return TRUE; + if (strncmp (phone, "010", 3) == 0) return TRUE; + if (strncmp (phone, "009", 3) == 0) return TRUE; + if (strncmp (phone, "002", 3) == 0) return TRUE; + } + + if (max == 1 || max == -1) + { + *up_to = 1; + if (strncmp (phone, "00", 2) == 0) return TRUE; + if (strncmp (phone, "99", 2) == 0) return TRUE; + } + + if (max == 0 || max == -1) + { + *up_to = 0; + if (strncmp (phone, "+", 1) == 0) return TRUE; + } + + return FALSE; +} + +/** + * match_exit_code_reverse: + * @phone: phone number string + * @max: number of characters to be checked (indexed from 0 or -1 if whole string should be checked) + * @up_to (out): number of matched characters (indexed from 0) + * + * Checks if given phone string begins with valid exit code. + * Tries to match exit codes from shortest to longest ones. + * + * @Returns: TRUE in case that string begins with valid exit code, false otherwise. + * + * Since: 3.12 + **/ +static gboolean +match_exit_code_reverse(const char* phone, int max, int* up_to) +{ + if (max == 0 || max == -1) + { + *up_to = 0; + if (strncmp (phone, "+", 1) == 0) return TRUE; + } + + if (max == 1 || max == -1) + { + *up_to = 1; + if (strncmp (phone, "00", 2) == 0) return TRUE; + if (strncmp (phone, "99", 2) == 0) return TRUE; + } + + if (max == 2 || max == -1) + { + *up_to = 2; + if (strncmp (phone, "011", 3) == 0) return TRUE; + if (strncmp (phone, "001", 3) == 0) return TRUE; + if (strncmp (phone, "006", 3) == 0) return TRUE; + if (strncmp (phone, "007", 3) == 0) return TRUE; + if (strncmp (phone, "008", 3) == 0) return TRUE; + if (strncmp (phone, "119", 3) == 0) return TRUE; + if (strncmp (phone, "005", 3) == 0) return TRUE; + if (strncmp (phone, "007", 3) == 0) return TRUE; + if (strncmp (phone, "009", 3) == 0) return TRUE; + if (strncmp (phone, "990", 3) == 0) return TRUE; + if (strncmp (phone, "994", 3) == 0) return TRUE; + if (strncmp (phone, "999", 3) == 0) return TRUE; + if (strncmp (phone, "013", 3) == 0) return TRUE; + if (strncmp (phone, "014", 3) == 0) return TRUE; + if (strncmp (phone, "018", 3) == 0) return TRUE; + if (strncmp (phone, "010", 3) == 0) return TRUE; + if (strncmp (phone, "009", 3) == 0) return TRUE; + if (strncmp (phone, "002", 3) == 0) return TRUE; + } + + if (max == 3 || max == -1) + { + *up_to = 3; + if (strncmp (phone, "0011", 4) == 0) return TRUE; + if (strncmp (phone, "0014", 4) == 0) return TRUE; + if (strncmp (phone, "0015", 4) == 0) return TRUE; + if (strncmp (phone, "0021", 4) == 0) return TRUE; + if (strncmp (phone, "0023", 4) == 0) return TRUE; + if (strncmp (phone, "0031", 4) == 0) return TRUE; + if (strncmp (phone, "1230", 4) == 0) return TRUE; + if (strncmp (phone, "1200", 4) == 0) return TRUE; + if (strncmp (phone, "1220", 4) == 0) return TRUE; + if (strncmp (phone, "1810", 4) == 0) return TRUE; + if (strncmp (phone, "1690", 4) == 0) return TRUE; + if (strncmp (phone, "1710", 4) == 0) return TRUE; + } + + if (max == 4 || max == -1) + { + *up_to = 4; + if (strncmp (phone, "00414", 5) == 0) return TRUE; + if (strncmp (phone, "00468", 5) == 0) return TRUE; + if (strncmp (phone, "00456", 5) == 0) return TRUE; + if (strncmp (phone, "00444", 5) == 0) return TRUE; + } + + return FALSE; +} + +/** + * is_exit_code_and_cc: + * @phone: phone number string + * @up_to: number of characters to be checked (indexed from 0) + * + * Checks if given phone string begins with valid exit code and country code. + * After sucessfully matching exit code, any further 1-3 digits + * will be recognized as valid country code. + * + * @Returns: TRUE in case that string begins with valid exit code + * and country code, false otherwise. + * + * Since: 3.12 + **/ +static gboolean +is_exit_code_and_cc(const char* phone, int up_to) +{ + int matched_len = 0; + if (match_exit_code(phone, -1, &matched_len)) + { + if (up_to - matched_len -1 >= 0 && + up_to - matched_len - 1 < 3) + return TRUE; + } + if (match_exit_code_reverse(phone, -1, &matched_len)) + { + if (up_to - matched_len -1 >= 0 && + up_to - matched_len - 1 < 3) + return TRUE; + } + + return FALSE; +} + +/** + * is_trunk: + * @phone: phone number string + * @up_to: number of characters to be checked (indexed from 0) + * + * Checks if given phone string begins with trunk code. + * + * @Returns: TRUE in case that string begins with trunk code. + * + * Since: 3.12 + **/ +static gboolean +is_trunk(const char* phone, int up_to) +{ + if (up_to > 1) return FALSE; + if (phone[0] == '0' && up_to == 0) return TRUE; + if (phone[0] == '0' && phone[1] == '1' && up_to == 1) return TRUE; //Mexico trunk code + if (phone[0] == '0' && phone[1] == '6' && up_to == 1) return TRUE; //Hungary trunk code + if (phone[0] == '8' && phone[1] == '0' && up_to == 1) return TRUE; //Belarus trunk code + + + if (phone[0] == '1' && up_to == 0) return TRUE; //Jamaica trunk code + if (phone[0] == '8' && up_to == 0) return TRUE; //Kazakhstan,Lituana trunk code + + + return FALSE; +} + +/** + * e_phone_number_equal: + * @phone1: first phone number string + * @phone2: second phone number string + * + * Compares two phone number strings. + * + * @Returns: TRUE in case that phone number matches. false otherwise. + * + * Since: 3.12 + **/ +gboolean +e_phone_number_equal (const gchar *phone1, + const gchar *phone2) +{ + int a = 0; + int b = 0; + int matched = 0; + if (!phone1 || !phone2) { + return FALSE; + } + + a = strlen (phone1) - 1; + b = strlen (phone2) - 1; + + /* + * Check (in reverse order) number of matching digits + */ + while (a >= 0 && b >= 0) { + if (phone1[a] != phone2[b]) { + break; + } + a--; + b--; + matched++; + } + + //if both phone numbers matched completly, they are equal + if (a < 0 && b < 0) { + return TRUE; + } + + //if one of numbers matched completly, + //and total number of matched digits is greater than 7, + //assume also that numbers are equal + if (matched >= 7 && (a < 0 || b < 0)) { + return TRUE; + } + + /* + * Check if first phone number begins with trunk code + * and second phone number begins exit code + country code + */ + if (is_trunk(phone1, a) && is_exit_code_and_cc(phone2, b)) { + return TRUE; + } + + /* + * Check opposite condition + */ + if (is_trunk(phone2, b) && is_exit_code_and_cc(phone1, a)) { + return TRUE; + } + + return FALSE; +} diff --git a/addressbook/libebook-contacts/e-phone-number.h b/addressbook/libebook-contacts/e-phone-number.h index 5fef30773..c493c959a 100644 --- a/addressbook/libebook-contacts/e-phone-number.h +++ b/addressbook/libebook-contacts/e-phone-number.h @@ -206,6 +206,12 @@ GType e_phone_number_get_type (void); GQuark e_phone_number_error_quark (void); gboolean e_phone_number_is_supported (void) G_GNUC_CONST; +gchar * e_phone_number_normalize (const gchar *phone_number, + GError **error); + +gboolean e_phone_number_equal (const gchar *first_number, + const gchar *second_number); + gint e_phone_number_get_country_code_for_region (const gchar *region_code, GError **error); diff --git a/addressbook/libedata-book/e-book-backend-sexp.c b/addressbook/libedata-book/e-book-backend-sexp.c index 6b3f9bd99..c1dfb0fdc 100644 --- a/addressbook/libedata-book/e-book-backend-sexp.c +++ b/addressbook/libedata-book/e-book-backend-sexp.c @@ -860,6 +860,38 @@ func_eqphone_short (struct _ESExp *f, } static gboolean +eqphone_is_helper (const gchar *ps1, + const gchar *ps2, + const gchar *region) +{ + gchar *p1, *p2; + gboolean res = FALSE; + GError *error = NULL; + + p1 = e_phone_number_normalize(ps1, &error); + p2 = e_phone_number_normalize(ps2, &error); + + res = e_phone_number_equal (p1, p2); + + g_free (p1); + g_free (p2); + + return res; +} + +static ESExpResult * +func_eqphone_is (struct _ESExp *f, + gint argc, + struct _ESExpResult **argv, + gpointer data) +{ + SearchContext *ctx = data; + + return entry_compare (ctx, f, argc, argv, eqphone_is_helper); +} + + +static gboolean regex_helper (const gchar *ps1, const gchar *ps2, const gchar *region, @@ -1255,6 +1287,7 @@ static struct { { "eqphone", func_eqphone, 0 }, { "eqphone_national", func_eqphone_national, 0 }, { "eqphone_short", func_eqphone_short, 0 }, + { "eqphone_is", func_eqphone_is, 0 }, { "regex_normal", func_regex_normal, 0 }, { "regex_translit", func_regex_normal, 0 }, { "regex_raw", func_regex_raw, 0 }, diff --git a/addressbook/libedata-book/e-book-sqlite.c b/addressbook/libedata-book/e-book-sqlite.c index 89ef58468..8d435bff9 100644 --- a/addressbook/libedata-book/e-book-sqlite.c +++ b/addressbook/libedata-book/e-book-sqlite.c @@ -277,6 +277,7 @@ ebsql_init_debug (void) #define EBSQL_FUNC_EQPHONE_EXACT "eqphone_exact" #define EBSQL_FUNC_EQPHONE_NATIONAL "eqphone_national" #define EBSQL_FUNC_EQPHONE_SHORT "eqphone_short" +#define EBSQL_FUNC_EQPHONE_IS "eqphone_is" /* Fallback collations are generated as with a prefix and an EContactField name */ #define EBSQL_COLLATE_PREFIX "ebsql_" @@ -290,6 +291,8 @@ ebsql_init_debug (void) #define EBSQL_SUFFIX_PHONE "phone" #define EBSQL_SUFFIX_COUNTRY "country" #define EBSQL_SUFFIX_TRANSLIT "translit" +#define EBSQL_SUFFIX_REVERSE_PHONE "phone_reverse" +#define EBSQL_SUFFIX_NORMALIZED_PHONE "phone_normalized" /* Track EBookIndexType's in a bit mask */ #define INDEX_FLAG(type) (1 << E_BOOK_INDEX_##type) @@ -687,6 +690,14 @@ summary_field_list_columns (SummaryField *field, info = column_info_new (field, folderid, EBSQL_SUFFIX_PHONE, "TEXT", NULL, "PINDEX"); columns = g_slist_prepend (columns, info); + /* One indexed column for storing reversed phone number */ + info = column_info_new (field, folderid, EBSQL_SUFFIX_REVERSE_PHONE, "TEXT", NULL, "RINDEX"); + columns = g_slist_prepend (columns, info); + + /* One string column for storing normalized phone number */ + info = column_info_new (field, folderid, EBSQL_SUFFIX_NORMALIZED_PHONE, "TEXT", NULL, NULL); + columns = g_slist_prepend (columns, info); + /* One integer column for storing the country code */ info = column_info_new (field, folderid, EBSQL_SUFFIX_COUNTRY, "INTEGER", "DEFAULT 0", NULL); columns = g_slist_prepend (columns, info); @@ -1502,6 +1513,32 @@ ebsql_eqphone_short (sqlite3_context *context, ebsql_eqphone (context, argc, argv, E_PHONE_NUMBER_MATCH_SHORT); } +/* Alternative locale invariant phone number match function: EBSQL_FUNC_EQPHONE_IS */ +static void +ebsql_eqphone_is (sqlite3_context *context, + gint argc, + sqlite3_value **argv) +{ + const gchar *phone1 = NULL; + const gchar *phone2 = NULL; + gboolean res = FALSE; + + phone1 = (const gchar *) sqlite3_value_text (argv[0]); + phone2 = (const gchar *) sqlite3_value_text (argv[1]); + + if (!phone1 || !phone2) { + sqlite3_result_error (context, "No phone number provided", -1); + return; + } + + res = e_phone_number_equal (phone1, phone2); + + if (res) + sqlite3_result_int (context, 1); + else + sqlite3_result_int (context, 0); +} + /* Implementation of EBSQL_FUNC_FETCH_VCARD (fallback for shallow addressbooks) */ static void ebsql_fetch_vcard (sqlite3_context *context, @@ -1543,6 +1580,7 @@ static EbSqlCustomFuncTab ebsql_custom_functions[] = { { EBSQL_FUNC_EQPHONE_EXACT, ebsql_eqphone_exact, 2 }, /* eqphone_exact (search_input, column_data) */ { EBSQL_FUNC_EQPHONE_NATIONAL, ebsql_eqphone_national, 2 }, /* eqphone_national (search_input, column_data) */ { EBSQL_FUNC_EQPHONE_SHORT, ebsql_eqphone_short, 2 }, /* eqphone_national (search_input, column_data) */ + { EBSQL_FUNC_EQPHONE_IS, ebsql_eqphone_is, 2 }, /* eqphone_is (search_input, column_data) */ }; /****************************************************** @@ -2162,6 +2200,14 @@ ebsql_introspect_summary (EBookSqlite *ebsql, computed = INDEX_FLAG (PHONE); freeme = g_strndup (col, p - col); col = freeme; + } else if ((p = strstr (col, "_" EBSQL_SUFFIX_REVERSE_PHONE)) != NULL) { + computed = INDEX_FLAG (PHONE); + freeme = g_strndup (col, p - col); + col = freeme; + } else if ((p = strstr (col, "_" EBSQL_SUFFIX_NORMALIZED_PHONE)) != NULL) { + computed = INDEX_FLAG (PHONE); + freeme = g_strndup (col, p - col); + col = freeme; } else if ((p = strstr (col, "_" EBSQL_SUFFIX_SORT_KEY)) != NULL) { computed = INDEX_FLAG (SORT_KEY); freeme = g_strndup (col, p - col); @@ -3035,6 +3081,27 @@ remove_leading_zeros (gchar *number) return trimmed; } +static gchar * +normalize_phone (const gchar *normal) +{ + GError *error = NULL; + gchar *normalized = normal ? e_phone_number_normalize (normal, &error) : NULL; + return normalized; +} + +static gchar * +reverse_phone (const gchar *normal) +{ + gchar *normalized = normalize_phone (normal); + gchar *reverse = normalized ? g_utf8_strreverse (normalized, -1) : NULL; + gchar *reverse_cut = reverse ? g_strndup (reverse, 5) : NULL; + + g_free (normalized); + g_free (reverse); + + return reverse_cut; +} + typedef struct { gint country_code; gchar *national; @@ -3303,6 +3370,8 @@ ebsql_prepare_multi_insert (EBookSqlite *ebsql, if ((field->index & INDEX_FLAG (PHONE)) != 0) { g_string_append (string, ", value_" EBSQL_SUFFIX_PHONE); g_string_append (string, ", value_" EBSQL_SUFFIX_COUNTRY); + g_string_append (string, ", value_" EBSQL_SUFFIX_REVERSE_PHONE); + g_string_append (string, ", value_" EBSQL_SUFFIX_NORMALIZED_PHONE); } if ((field->index & INDEX_FLAG (TRANSLIT)) != 0) @@ -3316,6 +3385,8 @@ ebsql_prepare_multi_insert (EBookSqlite *ebsql, if ((field->index & INDEX_FLAG (PHONE)) != 0) { g_string_append (string, ", :value_" EBSQL_SUFFIX_PHONE); g_string_append (string, ", :value_" EBSQL_SUFFIX_COUNTRY); + g_string_append (string, ", :value_" EBSQL_SUFFIX_REVERSE_PHONE); + g_string_append (string, ", :value_" EBSQL_SUFFIX_NORMALIZED_PHONE); } if ((field->index & INDEX_FLAG (TRANSLIT)) != 0) @@ -3370,8 +3441,17 @@ ebsql_run_multi_insert_one (EBookSqlite *ebsql, /* :value_country */ if (ret == SQLITE_OK) - sqlite3_bind_int (stmt, param_idx++, country_code); + ret = sqlite3_bind_int (stmt, param_idx++, country_code); + + /* :value reverse phone */ + str = reverse_phone (normal); + if (ret == SQLITE_OK) + ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free); + /* :value normalized phone */ + str = normalize_phone (normal); + if (ret == SQLITE_OK) + sqlite3_bind_text (stmt, param_idx++, str, -1, g_free); } if (ret == SQLITE_OK && (field->index & INDEX_FLAG (TRANSLIT)) != 0) { @@ -3482,6 +3562,15 @@ ebsql_prepare_insert (EBookSqlite *ebsql, g_string_append (string, ", "); g_string_append (string, field->dbname); g_string_append (string, "_" EBSQL_SUFFIX_COUNTRY); + + g_string_append (string, ", "); + g_string_append (string, field->dbname); + g_string_append (string, "_" EBSQL_SUFFIX_REVERSE_PHONE); + + g_string_append (string, ", "); + g_string_append (string, field->dbname); + g_string_append (string, "_" EBSQL_SUFFIX_NORMALIZED_PHONE); + } if ((field->index & INDEX_FLAG (TRANSLIT)) != 0) { @@ -3523,6 +3612,8 @@ ebsql_prepare_insert (EBookSqlite *ebsql, if ((field->index & INDEX_FLAG (PHONE)) != 0) { g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_PHONE, field->dbname); g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_COUNTRY, field->dbname); + g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_REVERSE_PHONE, field->dbname); + g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_NORMALIZED_PHONE, field->dbname); } if ((field->index & INDEX_FLAG (TRANSLIT)) != 0) @@ -3679,7 +3770,15 @@ ebsql_run_insert (EBookSqlite *ebsql, ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free); if (ret == SQLITE_OK) - sqlite3_bind_int (stmt, param_idx++, country_code); + ret = sqlite3_bind_int (stmt, param_idx++, country_code); + + str = reverse_phone (normal); + if (ret == SQLITE_OK) + ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free); + + str = normalize_phone (normal); + if (ret == SQLITE_OK) + sqlite3_bind_text (stmt, param_idx++, str, -1, g_free); } if (ret == SQLITE_OK && @@ -3870,6 +3969,7 @@ enum { (query) == E_BOOK_QUERY_EQUALS_PHONE_NUMBER ? "eqphone" : \ (query) == E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER ? "eqphone-national" : \ (query) == E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER ? "eqphone-short" : \ + (query) == E_BOOK_QUERY_PHONE_NUMBER_IS ? "eqphone-is" : \ (query) == E_BOOK_QUERY_REGEX_NORMAL ? "regex-normal" : \ (query) == E_BOOK_QUERY_REGEX_RAW ? "regex-raw" : \ (query) == E_BOOK_QUERY_REGEX_TRANSLIT ? "regex-translit" : \ @@ -3886,7 +3986,8 @@ enum { #define IS_QUERY_PHONE(query) \ ((query) == E_BOOK_QUERY_EQUALS_PHONE_NUMBER || \ (query) == E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER || \ - (query) == E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER) + (query) == E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER || \ + (query) == E_BOOK_QUERY_PHONE_NUMBER_IS) typedef struct { guint query; /* EBookQueryTest (extended) */ @@ -4232,6 +4333,7 @@ static const struct { { "eqphone", FALSE, E_BOOK_QUERY_EQUALS_PHONE_NUMBER }, { "eqphone_national", FALSE, E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER }, { "eqphone_short", FALSE, E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER }, + { "eqphone_is", FALSE, E_BOOK_QUERY_PHONE_NUMBER_IS }, { "regex_normal", FALSE, E_BOOK_QUERY_REGEX_NORMAL }, { "regex_raw", FALSE, E_BOOK_QUERY_REGEX_RAW }, { "regex_translit", FALSE, E_BOOK_QUERY_REGEX_TRANSLIT }, @@ -4653,6 +4755,7 @@ query_preflight_check (PreflightContext *context, case E_BOOK_QUERY_EQUALS_PHONE_NUMBER: case E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER: case E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER: + case E_BOOK_QUERY_PHONE_NUMBER_IS: /* Phone number queries are supported so long as they are in the summary, * libphonenumber is available, and the phone number string is a valid one @@ -5163,6 +5266,39 @@ field_test_query_eqphone_short (EBookSqlite *ebsql, } static void +field_test_query_eqphone_is (EBookSqlite *ebsql, + GString *string, + QueryFieldTest *test) +{ + SummaryField *field = test->field; + QueryPhoneTest *phone_test = (QueryPhoneTest *) test; + + gchar * normalized = normalize_phone (phone_test->value); + gchar * reversed = reverse_phone (phone_test->value); + + if ((field->index & INDEX_FLAG (PHONE)) != 0) { + /* Only a compound expression if there is a country code */ + g_string_append_c (string, '('); + + /* Generate: phone = %Q */ + ebsql_string_append_column (string, field, EBSQL_SUFFIX_REVERSE_PHONE); + ebsql_string_append_printf (string, " = %Q", reversed); + + g_string_append (string, " AND "); + g_string_append (string, EBSQL_FUNC_EQPHONE_IS " ("); + ebsql_string_append_column (string, field, EBSQL_SUFFIX_NORMALIZED_PHONE); + ebsql_string_append_printf (string, ", %Q))", normalized); + } else { + g_string_append (string, EBSQL_FUNC_EQPHONE_IS " ("); + ebsql_string_append_column (string, field, EBSQL_SUFFIX_NORMALIZED_PHONE); + ebsql_string_append_printf (string, ", %Q)", normalized); + } + + g_free (normalized); + g_free (reversed); +} + +static void field_test_query_regex_normal (EBookSqlite *ebsql, GString *string, QueryFieldTest *test) @@ -5327,6 +5463,7 @@ static const GenerateFieldTest field_test_func_table[] = { field_test_query_eqphone, /* E_BOOK_QUERY_EQUALS_PHONE_NUMBER */ field_test_query_eqphone_national, /* E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER */ field_test_query_eqphone_short, /* E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER */ + field_test_query_eqphone_is, /* E_BOOK_QUERY_EQUALS_PHONE_NUMBER_IS */ field_test_query_regex_normal, /* E_BOOK_QUERY_REGEX_NORMAL */ NULL /* Requires fallback */, /* E_BOOK_QUERY_REGEX_RAW */ field_test_query_regex_translit, /* E_BOOK_QUERY_REGEX_TRANSLIT */ |