[evolution-data-server/openismus-phonenumber-work: 14/14] Permit smart comparison of phone numbers
- From: Mathias Hasselmann <hasselmm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/openismus-phonenumber-work: 14/14] Permit smart comparison of phone numbers
- Date: Thu, 6 Dec 2012 23:03:32 +0000 (UTC)
commit 9aaf5e7c4d2b993b321e73f09020ff8d0928d0d6
Author: Mathias Hasselmann <mathias openismus com>
Date: Thu Dec 6 23:53:10 2012 +0100
Permit smart comparison of phone numbers
Introduces new e-phone-util functions for comparing phone
numbers and uses them in e-book-backend-sexp.c to implement
fallback routines for "eqphone" and friends.
addressbook/libedata-book/e-book-backend-sexp.c | 75 +++++++++++-
.../libedataserver/libedataserver-sections.txt | 2 +
libedataserver/e-phone-utils.cpp | 83 ++++++++++++-
libedataserver/e-phone-utils.h | 46 +++++++
tests/libebook/data/vcards/custom-1.vcf | 2 +-
tests/libedataserver/e-phone-utils-test.c | 133 +++++++++++++++++++-
6 files changed, 330 insertions(+), 11 deletions(-)
---
diff --git a/addressbook/libedata-book/e-book-backend-sexp.c b/addressbook/libedata-book/e-book-backend-sexp.c
index ef7ae97..3fac3b0 100644
--- a/addressbook/libedata-book/e-book-backend-sexp.c
+++ b/addressbook/libedata-book/e-book-backend-sexp.c
@@ -18,10 +18,12 @@
* 02110-1301, USA.
*/
-#include <string.h>
-
#include "e-book-backend-sexp.h"
+#include <libedataserver/libedataserver.h>
+
+#include <string.h>
+
#define E_BOOK_BACKEND_SEXP_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_BOOK_BACKEND_SEXP, EBookBackendSExpPrivate))
@@ -744,6 +746,72 @@ func_beginswith (struct _ESExp *f,
}
static gboolean
+eqphone_helper (const gchar *ps1,
+ const gchar *ps2,
+ EPhoneNumberMatch required_match)
+{
+ const EPhoneNumberMatch actual_match =
+ e_phone_number_compare_strings (ps1, ps2, NULL);
+
+ return actual_match >= E_PHONE_NUMBER_MATCH_EXACT
+ && actual_match <= required_match;
+}
+
+static gboolean
+eqphone_exact_helper (const gchar *ps1,
+ const gchar *ps2)
+{
+ return eqphone_helper (ps1, ps2, E_PHONE_NUMBER_MATCH_EXACT);
+}
+
+static gboolean
+eqphone_national_helper (const gchar *ps1,
+ const gchar *ps2)
+{
+ return eqphone_helper (ps1, ps2, E_PHONE_NUMBER_MATCH_NATIONAL);
+}
+
+static gboolean
+eqphone_short_helper (const gchar *ps1,
+ const gchar *ps2)
+{
+ return eqphone_helper (ps1, ps2, E_PHONE_NUMBER_MATCH_SHORT);
+}
+
+static ESExpResult *
+func_eqphone (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ SearchContext *ctx = data;
+
+ return entry_compare (ctx, f, argc, argv, eqphone_exact_helper);
+}
+
+static ESExpResult *
+func_eqphone_national (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ SearchContext *ctx = data;
+
+ return entry_compare (ctx, f, argc, argv, eqphone_national_helper);
+}
+
+static ESExpResult *
+func_eqphone_short (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ SearchContext *ctx = data;
+
+ return entry_compare (ctx, f, argc, argv, eqphone_short_helper);
+}
+
+static gboolean
exists_helper (const gchar *ps1,
const gchar *ps2)
{
@@ -894,6 +962,9 @@ static struct {
{ "is", func_is, 0 },
{ "beginswith", func_beginswith, 0 },
{ "endswith", func_endswith, 0 },
+ { "eqphone", func_eqphone, 0 },
+ { "eqphone_national", func_eqphone_national, 0 },
+ { "eqphone_short", func_eqphone_short, 0 },
{ "exists", func_exists, 0 },
{ "exists_vcard", func_exists_vcard, 0 },
};
diff --git a/docs/reference/libedataserver/libedataserver-sections.txt b/docs/reference/libedataserver/libedataserver-sections.txt
index e6382e7..c395cd5 100644
--- a/docs/reference/libedataserver/libedataserver-sections.txt
+++ b/docs/reference/libedataserver/libedataserver-sections.txt
@@ -221,6 +221,8 @@ EPhoneNumberError
EPhoneNumberFormat
e_phone_number_from_string
e_phone_number_to_string
+e_phone_number_compare
+e_phone_number_compare_strings
e_phone_number_copy
e_phone_number_free
<SUBSECTION Standard>
diff --git a/libedataserver/e-phone-utils.cpp b/libedataserver/e-phone-utils.cpp
index 802739b..d2ad7b6 100644
--- a/libedataserver/e-phone-utils.cpp
+++ b/libedataserver/e-phone-utils.cpp
@@ -46,7 +46,7 @@ using i18n::phonenumbers::PhoneNumberUtil;
struct _EPhoneNumber {
#ifdef ENABLE_PHONENUMBER
- i18n::phonenumbers::PhoneNumber number;
+ i18n::phonenumbers::PhoneNumber phone_number;
#endif /* ENABLE_PHONENUMBER */
};
@@ -175,7 +175,7 @@ e_phone_number_from_string (const gchar *phone_number,
const PhoneNumberUtil::ErrorType err =
e_phone_number_util_get_instance ()->Parse (phone_number, country_code,
- &parsed_number->number);
+ &parsed_number->phone_number);
if (err != PhoneNumberUtil::NO_PARSING_ERROR) {
e_phone_number_set_error (error, e_phone_number_error_code (err));
@@ -204,7 +204,7 @@ e_phone_number_to_string (const EPhoneNumber *phone_number,
std::string formatted_number;
e_phone_number_util_get_instance ()->Format
- (phone_number->number,
+ (phone_number->phone_number,
static_cast<PhoneNumberUtil::PhoneNumberFormat> (format),
&formatted_number);
@@ -220,6 +220,83 @@ e_phone_number_to_string (const EPhoneNumber *phone_number,
return NULL;
}
+static EPhoneNumberMatch
+e_phone_number_match (PhoneNumberUtil::MatchType match_type)
+{
+ switch (match_type) {
+ case PhoneNumberUtil::NO_MATCH:
+ case PhoneNumberUtil::INVALID_NUMBER:
+ return E_PHONE_NUMBER_MATCH_NONE;
+ case PhoneNumberUtil::SHORT_NSN_MATCH:
+ return E_PHONE_NUMBER_MATCH_SHORT;
+ case PhoneNumberUtil::NSN_MATCH:
+ return E_PHONE_NUMBER_MATCH_NATIONAL;
+ case PhoneNumberUtil::EXACT_MATCH:
+ return E_PHONE_NUMBER_MATCH_EXACT;
+ }
+
+ g_return_val_if_reached (E_PHONE_NUMBER_MATCH_NONE);
+}
+
+EPhoneNumberMatch
+e_phone_number_compare (const EPhoneNumber *first_number,
+ const EPhoneNumber *second_number)
+{
+ g_return_val_if_fail (NULL != first_number, E_PHONE_NUMBER_MATCH_NONE);
+ g_return_val_if_fail (NULL != second_number, E_PHONE_NUMBER_MATCH_NONE);
+
+ EPhoneNumberMatch result = E_PHONE_NUMBER_MATCH_NONE;
+
+#ifdef ENABLE_PHONENUMBER
+
+ const PhoneNumberUtil::MatchType match_type =
+ e_phone_number_util_get_instance ()->
+ IsNumberMatch (first_number->phone_number,
+ second_number->phone_number);
+
+ g_warn_if_fail (match_type != PhoneNumberUtil::INVALID_NUMBER);
+ result = e_phone_number_match (match_type);
+
+#else /* ENABLE_PHONENUMBER */
+
+ e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+
+#endif /* ENABLE_PHONENUMBER */
+
+ return result;
+}
+
+EPhoneNumberMatch
+e_phone_number_compare_strings (const gchar *first_number,
+ const gchar *second_number,
+ GError **error)
+{
+ g_return_val_if_fail (NULL != first_number, E_PHONE_NUMBER_MATCH_NONE);
+ g_return_val_if_fail (NULL != second_number, E_PHONE_NUMBER_MATCH_NONE);
+
+ EPhoneNumberMatch result = E_PHONE_NUMBER_MATCH_NONE;
+
+#ifdef ENABLE_PHONENUMBER
+
+ const PhoneNumberUtil::MatchType match_type =
+ e_phone_number_util_get_instance ()->
+ IsNumberMatchWithTwoStrings (first_number, second_number);
+
+ if (match_type == PhoneNumberUtil::INVALID_NUMBER) {
+ e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_A_NUMBER);
+ } else {
+ result = e_phone_number_match (match_type);
+ }
+
+#else /* ENABLE_PHONENUMBER */
+
+ e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+
+#endif /* ENABLE_PHONENUMBER */
+
+ return result;
+}
+
EPhoneNumber *
e_phone_number_copy (const EPhoneNumber *phone_number)
{
diff --git a/libedataserver/e-phone-utils.h b/libedataserver/e-phone-utils.h
index 844c60d..1ee9f39 100644
--- a/libedataserver/e-phone-utils.h
+++ b/libedataserver/e-phone-utils.h
@@ -65,6 +65,22 @@ typedef enum {
} EPhoneNumberFormat;
/**
+ * EPhoneNumberMatch:
+ * @E_PHONE_NUMBER_MATCH_NONE: The phone numbers did not match.
+ * @E_PHONE_NUMBER_MATCH_EXACT: The phone numbers matched exactly.
+ * @E_PHONE_NUMBER_MATCH_NATIONAL: There was no country code for at least
+ * one of the numbers, but the national parts matched.
+ * @E_PHONE_NUMBER_MATCH_SHORT: There was no country code for at least
+ * one of the numbers, but one number might be part (suffix) of the other.
+ */
+typedef enum {
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_EXACT,
+ E_PHONE_NUMBER_MATCH_NATIONAL = 1024,
+ E_PHONE_NUMBER_MATCH_SHORT = 2048
+} EPhoneNumberMatch;
+
+/**
* EPhoneNumberError:
* @E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED: the library was built without phone
* number support
@@ -151,6 +167,36 @@ gchar * e_phone_number_to_string (const EPhoneNumber *phone_number,
EPhoneNumberFormat format);
/**
+ * e_phone_number_compare:
+ * @first_number: the first EPhoneNumber to compare
+ * @second_number: the second EPhoneNumber to compare
+ *
+ * Compares two phone numbers.
+ *
+ * Returns: The quality of matching for the two phone numbers.
+ *
+ * Since: 3.8
+ */
+EPhoneNumberMatch e_phone_number_compare (const EPhoneNumber *first_number,
+ const EPhoneNumber *second_number);
+
+/**
+ * e_phone_number_compare_strings:
+ * @first_number: the first EPhoneNumber to compare
+ * @second_number: the second EPhoneNumber to compare
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Compares two phone numbers.
+ *
+ * Returns: The quality of matching for the two phone numbers.
+ *
+ * Since: 3.8
+ */
+EPhoneNumberMatch e_phone_number_compare_strings (const gchar *first_number,
+ const gchar *second_number,
+ GError **error);
+
+/**
* e_phone_number_copy:
* @phone_number: the EPhoneNumber to copy
*
diff --git a/tests/libebook/data/vcards/custom-1.vcf b/tests/libebook/data/vcards/custom-1.vcf
index bc9a23c..6d7241b 100644
--- a/tests/libebook/data/vcards/custom-1.vcf
+++ b/tests/libebook/data/vcards/custom-1.vcf
@@ -1,5 +1,5 @@
BEGIN:VCARD
FN:Micheal Jackson
-TEL;HOME:+1234567
+TEL;HOME:+1-221-5423789
EMAIL;TYPE=home,work:micheal jackson com
END:VCARD
diff --git a/tests/libedataserver/e-phone-utils-test.c b/tests/libedataserver/e-phone-utils-test.c
index c085cba..32d9d17 100644
--- a/tests/libedataserver/e-phone-utils-test.c
+++ b/tests/libedataserver/e-phone-utils-test.c
@@ -25,6 +25,73 @@
#include <libedataserver/libedataserver.h>
+static const char *match_candidates[] = {
+ "not-a-number",
+ "+1-617-4663489", "617-4663489", "4663489",
+ "+1.408.845.5246", "4088455246", "8455246"
+};
+
+static const EPhoneNumberMatch expected_matches[] = {
+ /* not a number */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+
+ /* +1-617-4663489 */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_EXACT,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NATIONAL, /* XXX - Google, sure? */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+
+ /* +1.408.845.5246 */
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_EXACT,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_SHORT,
+
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_NATIONAL,
+ E_PHONE_NUMBER_MATCH_SHORT,
+
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_NONE,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_SHORT,
+ E_PHONE_NUMBER_MATCH_NATIONAL /* XXX - Google, sure? */
+};
+
static void
test_parse_and_format (gconstpointer data)
{
@@ -95,33 +162,89 @@ test_parse_bad_number (void)
g_clear_error (&error);
}
+static void
+test_compare_numbers (gconstpointer data)
+{
+ const size_t n = GPOINTER_TO_UINT (data);
+ const size_t i = n % G_N_ELEMENTS (match_candidates);
+ const size_t j = n / G_N_ELEMENTS (match_candidates);
+
+#ifdef ENABLE_PHONENUMBER
+ const gboolean error_expected = !(i && j) ;
+#else /* ENABLE_PHONENUMBER */
+ const gboolean error_expected = TRUE;
+#endif /* ENABLE_PHONENUMBER */
+
+ EPhoneNumberMatch actual_match;
+ GError *error = NULL;
+
+ actual_match = e_phone_number_compare_strings (match_candidates[i],
+ match_candidates[j],
+ &error);
+
+ g_assert_cmpuint (actual_match, ==, expected_matches[n]);
+
+ if (!error_expected) {
+ g_assert (error == NULL);
+ } else {
+ g_assert (error != NULL);
+ g_assert (error->domain == E_PHONE_NUMBER_ERROR);
+#ifdef ENABLE_PHONENUMBER
+ g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_A_NUMBER);
+#else /* ENABLE_PHONENUMBER */
+ g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+#endif /* ENABLE_PHONENUMBER */
+ g_assert (error->message != NULL);
+
+ g_clear_error (&error);
+ }
+}
+
gint
main (gint argc,
gchar **argv)
{
+ size_t i, j;
+
g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_data_func
- ("/e-phone-utils-test/ParseAndFormat/I164",
+ ("/e-phone-utils-test/parse-and-format/i164",
"+493011223344//+493011223344/+49 30 11223344/030 11223344/tel:+49-30-11223344",
test_parse_and_format);
g_test_add_data_func
- ("/e-phone-utils-test/ParseAndFormat/National",
+ ("/e-phone-utils-test/parse-and-format/national",
"(030) 22334-455/DE/+493022334455/+49 30 22334455/030 22334455/tel:+49-30-22334455",
test_parse_and_format);
g_test_add_data_func
- ("/e-phone-utils-test/ParseAndFormat/International",
+ ("/e-phone-utils-test/parse-and-format/international",
"+1 212 33445566//+121233445566/+1 21233445566/21233445566/tel:+1-21233445566",
test_parse_and_format);
g_test_add_data_func
- ("/e-phone-utils-test/ParseAndFormat/RFC3966",
+ ("/e-phone-utils-test/parse-and-format/rfc3966",
"tel:+358-71-44556677//+3587144556677/+358 71 44556677/071 44556677/tel:+358-71-44556677",
test_parse_and_format);
+
g_test_add_func
- ("/e-phone-utils-test/ParseAndFormat/BadNumber",
+ ("/e-phone-utils-test/parse-and-format/BadNumber",
test_parse_bad_number);
+ g_assert_cmpint (G_N_ELEMENTS (match_candidates) * G_N_ELEMENTS (match_candidates),
+ ==, G_N_ELEMENTS (expected_matches));
+
+ for (i = 0; i < G_N_ELEMENTS (match_candidates); ++i) {
+ for (j = 0; j < G_N_ELEMENTS (match_candidates); ++j) {
+ const size_t n = j * G_N_ELEMENTS (match_candidates) + i;
+ char *path = g_strdup_printf ("/e-phone-utils-test/compare/%s/%s",
+ match_candidates[i],
+ match_candidates[j]);
+
+ g_test_add_data_func (path, GUINT_TO_POINTER (n), test_compare_numbers);
+ g_free (path);
+ }
+ }
+
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]