[evolution-data-server/intel-work-3-12] EBookSqlite: Added new phone lookup operator.



commit b52e2d3a0030d6467f372ee246b6848c49412281
Author: Mateusz Polrola <mateuszx potrola intel com>
Date:   Fri Dec 12 12:50:09 2014 +0000

    EBookSqlite: Added new phone lookup operator.
    
    New phone lookup operator uses custom phone matching function, that does
    not relies on currently set locale for given address book.

 addressbook/libebook-contacts/e-book-query.c       |   13 +
 addressbook/libebook-contacts/e-book-query.h       |    1 +
 .../libebook-contacts/e-phone-number-private.cpp   |   13 +
 .../libebook-contacts/e-phone-number-private.h     |    2 +
 addressbook/libebook-contacts/e-phone-number.c     |  318 ++++++++++++++++++++
 addressbook/libebook-contacts/e-phone-number.h     |    6 +
 addressbook/libedata-book/e-book-backend-sexp.c    |   33 ++
 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 518fb31..2baf04a 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 1c74f4a..5928541 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 f41e75b..ff359ae 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 &region,
diff --git a/addressbook/libebook-contacts/e-phone-number-private.h 
b/addressbook/libebook-contacts/e-phone-number-private.h
index ac5b701..700d8b8 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 c167d86..427deec 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 5fef307..c493c95 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 6b3f9bd..c1dfb0f 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 89ef584..8d435bf 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 */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]