[evolution-data-server] libebook: Add phone number utilities



commit 89e444369e5f2916c5ebbdaad34af1d2b92168f4
Author: Mathias Hasselmann <mathias openismus com>
Date:   Wed Dec 5 00:47:07 2012 +0100

    libebook: Add phone number utilities
    
    This functionality covers parsing, formatting and comparing
    phone numbers.
    
    Note that this commit introduces an optional depedency on C++.
    
    See: https://bugzilla.gnome.org/show_bug.cgi?id=689622

 addressbook/libebook/Makefile.am                   |   13 +-
 addressbook/libebook/e-phone-number-private.cpp    |  212 ++++++++++++++
 addressbook/libebook/e-phone-number-private.h      |   77 ++++++
 addressbook/libebook/e-phone-number.c              |  283 +++++++++++++++++++
 addressbook/libebook/e-phone-number.h              |  188 +++++++++++++
 addressbook/libebook/libebook.h                    |    1 +
 configure.ac                                       |   13 +
 docs/reference/addressbook/libebook/Makefile.am    |    5 +
 .../addressbook/libebook/libebook-docs.sgml        |    1 +
 .../addressbook/libebook/libebook-sections.txt     |   20 ++
 m4/evo_phonenumber.m4                              |   69 +++++
 tests/libebook/Makefile.am                         |    3 +
 tests/libebook/test-ebook-phone-number.c           |  289 ++++++++++++++++++++
 13 files changed, 1172 insertions(+), 2 deletions(-)
---
diff --git a/addressbook/libebook/Makefile.am b/addressbook/libebook/Makefile.am
index 5e29354..b015dd3 100644
--- a/addressbook/libebook/Makefile.am
+++ b/addressbook/libebook/Makefile.am
@@ -48,8 +48,10 @@ libebook_1_2_la_SOURCES =				\
 	e-contact.c					\
 	e-destination.c					\
 	e-name-western.c				\
-	e-name-western-tables.h                         \
-	e-source-backend-summary-setup.c			\
+	e-name-western-tables.h				\
+	e-phone-number.c				\
+	e-phone-number-private.h			\
+	e-source-backend-summary-setup.c		\
 	e-vcard.c					\
 	e-error.h
 
@@ -65,6 +67,12 @@ libebook_1_2_la_LDFLAGS = 							\
 	$(CODE_COVERAGE_LDFLAGS) \
 	$(NULL)
 
+if ENABLE_PHONENUMBER
+libebook_1_2_la_SOURCES += e-phone-number-private.cpp
+libebook_1_2_la_CPPFLAGS += $(PHONENUMBER_INCLUDES)
+libebook_1_2_la_LIBADD += $(PHONENUMBER_LIBS)
+endif # ENABLE_PHONENUMBER
+
 libebookincludedir = $(privincludedir)/libebook
 
 libebookinclude_HEADERS =				\
@@ -80,6 +88,7 @@ libebookinclude_HEADERS =				\
 	e-contact.h					\
 	e-destination.h					\
 	e-name-western.h				\
+	e-phone-number.h				\
 	e-source-backend-summary-setup.h		\
 	e-vcard.h
 
diff --git a/addressbook/libebook/e-phone-number-private.cpp b/addressbook/libebook/e-phone-number-private.cpp
new file mode 100644
index 0000000..99caa7f
--- /dev/null
+++ b/addressbook/libebook/e-phone-number-private.cpp
@@ -0,0 +1,212 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012,2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias openismus com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef ENABLE_PHONENUMBER
+#error Phone number support must be enabled for this file
+#endif /* ENABLE_PHONENUMBER */
+
+#include "e-phone-number-private.h"
+
+/* C++ standard library */
+#include <string>
+
+/* system headers */
+#include <langinfo.h>
+
+/* libphonenumber */
+#include <phonenumbers/logger.h>
+#include <phonenumbers/phonenumberutil.h>
+
+using i18n::phonenumbers::PhoneNumberUtil;
+
+struct _EPhoneNumber {
+	i18n::phonenumbers::PhoneNumber phone_number;
+};
+
+G_DEFINE_BOXED_TYPE (EPhoneNumber,
+                     e_phone_number,
+                     e_phone_number_copy,
+                     e_phone_number_free)
+
+static PhoneNumberUtil *
+e_phone_number_util_get_instance (void)
+{
+	static PhoneNumberUtil *instance = NULL;
+
+	if (g_once_init_enter (&instance)) {
+		/* FIXME: Ideally PhoneNumberUtil would not be a singleton,
+		 * so that we could safely tweak it's attributes without
+		 * influencing other users of the library. */
+		PhoneNumberUtil *new_instance = PhoneNumberUtil::GetInstance ();
+
+		/* Disable all logging: libphonenumber is pretty verbose. */
+		new_instance->SetLogger (new i18n::phonenumbers::NullLogger);
+		g_once_init_leave (&instance, new_instance);
+	}
+
+	return instance;
+}
+
+static EPhoneNumberError
+e_phone_number_error_code (PhoneNumberUtil::ErrorType error)
+{
+	switch (error) {
+	case PhoneNumberUtil::NO_PARSING_ERROR:
+		g_return_val_if_reached (E_PHONE_NUMBER_ERROR_UNKNOWN);
+	case PhoneNumberUtil::INVALID_COUNTRY_CODE_ERROR:
+		return E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE;
+	case PhoneNumberUtil::NOT_A_NUMBER:
+		return E_PHONE_NUMBER_ERROR_NOT_A_NUMBER;
+	case PhoneNumberUtil::TOO_SHORT_AFTER_IDD:
+		return E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD;
+	case PhoneNumberUtil::TOO_SHORT_NSN:
+		return E_PHONE_NUMBER_ERROR_TOO_SHORT;
+	case PhoneNumberUtil::TOO_LONG_NSN:
+		return E_PHONE_NUMBER_ERROR_TOO_LONG;
+	}
+
+	/* Please file a bug that we can add a proper error code. */
+	g_return_val_if_reached (E_PHONE_NUMBER_ERROR_UNKNOWN);
+}
+
+EPhoneNumber *
+_e_phone_number_cxx_from_string (const gchar *phone_number,
+                                 const gchar *country_code,
+                                 GError **error)
+{
+	g_return_val_if_fail (NULL != phone_number, NULL);
+
+	if (country_code == NULL) {
+#if HAVE__NL_ADDRESS_COUNTRY_AB2
+		country_code = nl_langinfo (_NL_ADDRESS_COUNTRY_AB2);
+#else /* HAVE__NL_ADDRESS_COUNTRY_AB2 */
+#error Cannot resolve default 2-letter country code. Find a replacement for _NL_ADDRESS_COUNTRY_AB2 or implement code to parse the locale name.
+#endif /* HAVE__NL_ADDRESS_COUNTRY_AB2 */
+	}
+
+	std::auto_ptr<EPhoneNumber> parsed_number(new EPhoneNumber);
+
+	const PhoneNumberUtil::ErrorType err =
+		e_phone_number_util_get_instance ()->Parse (phone_number, country_code,
+		                                            &parsed_number->phone_number);
+
+	if (err != PhoneNumberUtil::NO_PARSING_ERROR) {
+		_e_phone_number_set_error (error, e_phone_number_error_code (err));
+		return NULL;
+	}
+
+	return parsed_number.release();
+}
+
+gchar *
+_e_phone_number_cxx_to_string (const EPhoneNumber *phone_number,
+                               EPhoneNumberFormat format)
+{
+	g_return_val_if_fail (NULL != phone_number, NULL);
+
+	std::string formatted_number;
+
+	e_phone_number_util_get_instance ()->Format
+		(phone_number->phone_number,
+		 static_cast<PhoneNumberUtil::PhoneNumberFormat> (format),
+		 &formatted_number);
+
+	if (!formatted_number.empty ())
+		return g_strdup (formatted_number.c_str ());
+
+	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_cxx_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);
+
+	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);
+	return e_phone_number_match (match_type);
+}
+
+EPhoneNumberMatch
+_e_phone_number_cxx_compare_strings (const gchar *first_number,
+                                     const gchar *second_number,
+                                     GError **error)
+{
+	EPhoneNumberMatch result = E_PHONE_NUMBER_MATCH_NONE;
+
+	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);
+
+	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);
+	}
+
+	return result;
+}
+
+EPhoneNumber *
+_e_phone_number_cxx_copy (const EPhoneNumber *phone_number)
+{
+	if (phone_number)
+		return new EPhoneNumber (*phone_number);
+
+	return NULL;
+}
+
+void
+_e_phone_number_cxx_free (EPhoneNumber *phone_number)
+{
+	delete phone_number;
+}
diff --git a/addressbook/libebook/e-phone-number-private.h b/addressbook/libebook/e-phone-number-private.h
new file mode 100644
index 0000000..091f18e
--- /dev/null
+++ b/addressbook/libebook/e-phone-number-private.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012,2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias openismus com>
+ */
+
+/* NOTE: Keeping API documentation in this header file because gtkdoc-mkdb
+ * explicitly only scans .h and .c files, but ignores .cpp files. */
+
+/**
+ * SECTION: e-phone-utils
+ * @include: libedataserver/libedataserver.h
+ * @short_description: Phone number support
+ *
+ * This modules provides utility functions for parsing and formatting
+ * phone numbers. Under the hood it uses Google's libphonenumber.
+ **/
+
+#if !defined (__LIBEBOOK_H_INSIDE__) && !defined (LIBEBOOK_COMPILATION)
+#error "Only <libebook/libebook.h> should be included directly."
+#endif
+
+#ifndef E_PHONE_NUMBER_PRIVATE_H
+#define E_PHONE_NUMBER_PRIVATE_H
+
+#include "e-phone-number.h"
+
+G_BEGIN_DECLS
+
+#if __GNUC__ >= 4
+#define E_PHONE_NUMBER_LOCAL __attribute__ ((visibility ("hidden")))
+#else
+#define E_PHONE_NUMBER_LOCAL
+#endif
+
+/* defined and used in e-phone-number.c, but also used by e-phone-number-private.cpp */
+
+E_PHONE_NUMBER_LOCAL void		_e_phone_number_set_error		(GError **error,
+										 EPhoneNumberError code);
+
+#ifdef ENABLE_PHONENUMBER
+
+/* defined in e-phone-number-private.cpp, and used by by e-phone-number.c */
+
+E_PHONE_NUMBER_LOCAL EPhoneNumber *	_e_phone_number_cxx_from_string		(const gchar *phone_number,
+										 const gchar *country_code,
+										 GError **error);
+E_PHONE_NUMBER_LOCAL gchar *		_e_phone_number_cxx_to_string		(const EPhoneNumber *phone_number,
+										 EPhoneNumberFormat format);
+E_PHONE_NUMBER_LOCAL EPhoneNumberMatch	_e_phone_number_cxx_compare		(const EPhoneNumber *first_number,
+										 const EPhoneNumber *second_number);
+E_PHONE_NUMBER_LOCAL EPhoneNumberMatch	_e_phone_number_cxx_compare_strings	(const gchar *first_number,
+										 const gchar *second_number,
+										 GError **error);
+E_PHONE_NUMBER_LOCAL EPhoneNumber *	_e_phone_number_cxx_copy		(const EPhoneNumber *phone_number);
+E_PHONE_NUMBER_LOCAL void		_e_phone_number_cxx_free		(EPhoneNumber *phone_number);
+
+#endif /* ENABLE_PHONENUMBER */
+
+G_END_DECLS
+
+#endif /* E_PHONE_NUMBER_PRIVATE_H */
diff --git a/addressbook/libebook/e-phone-number.c b/addressbook/libebook/e-phone-number.c
new file mode 100644
index 0000000..a24272d
--- /dev/null
+++ b/addressbook/libebook/e-phone-number.c
@@ -0,0 +1,283 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012,2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias openismus com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-phone-number.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-phone-number-private.h"
+
+#ifndef ENABLE_PHONENUMBER
+
+/* With phonenumber support enabled the boxed type must be defined in
+ * the C++ code because we cannot compute the size of C++ types here. */
+G_DEFINE_BOXED_TYPE (EPhoneNumber,
+                     e_phone_number,
+                     e_phone_number_copy,
+                     e_phone_number_free)
+
+#endif /* ENABLE_PHONENUMBER */
+
+GQuark
+e_phone_number_error_quark (void)
+{
+	static GQuark q = 0;
+
+	if (q == 0)
+		q = g_quark_from_static_string ("e-phone-number-error-quark");
+
+	return q;
+}
+
+static const gchar *
+e_phone_number_error_to_string (EPhoneNumberError code)
+{
+	switch (code) {
+	case E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED:
+		return _("The library was built without phone number support.");
+	case E_PHONE_NUMBER_ERROR_UNKNOWN:
+		return _("The phone number parser reported an yet unkown error code.");
+	case E_PHONE_NUMBER_ERROR_NOT_A_NUMBER:
+		return _("Not a phone number");
+	case E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE:
+		return _("Invalid country code");
+	case E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD:
+		return _("Remaining text after the country code is to short for a phone number");
+	case E_PHONE_NUMBER_ERROR_TOO_SHORT:
+		return _("Text is too short for a phone number");
+	case E_PHONE_NUMBER_ERROR_TOO_LONG:
+		return _("Text is too long for a phone number");
+	}
+
+	return _("Unknown error");
+}
+
+void
+_e_phone_number_set_error (GError **error,
+                           EPhoneNumberError code)
+{
+	const gchar *message = e_phone_number_error_to_string (code);
+	g_set_error_literal (error, E_PHONE_NUMBER_ERROR, code, message);
+}
+
+/**
+ * e_phone_number_is_supported:
+ *
+ * Checks if phone number support is available. It is recommended to call this
+ * function before using any of the phone-utils functions to ensure that the
+ * required functionality is available, and to pick alternative mechnisms if
+ * needed.
+ *
+ * Returns: %TRUE if phone number support is available.
+ **/
+gboolean
+e_phone_number_is_supported (void)
+{
+#ifdef ENABLE_PHONENUMBER
+
+	return TRUE;
+
+#else /* ENABLE_PHONENUMBER */
+
+	return FALSE;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_from_string:
+ * @phone_number: the phone number to parse
+ * @country_code: (allow-none): a 2-letter country code, or %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Parses the string passed in @phone_number. Note that no validation is
+ * performed whether the recognized phone number is valid for a particular
+ * region.
+ *
+ * The 2-letter country code passed in @country_code only is used if the
+ * @phone_number is not written in international format. The applications's
+ * currently locale is consulted if %NULL gets passed for @country_code.
+ * If the number is guaranteed to start with a '+' followed by the country
+ * calling code, then "ZZ" can be passed here.
+ *
+ * Returns: (transfer full): a new EPhoneNumber instance on success,
+ * or %NULL on error. Call e_phone_number_free() to release this instance.
+ *
+ * Since: 3.8
+ **/
+EPhoneNumber *
+e_phone_number_from_string (const gchar *phone_number,
+                            const gchar *country_code,
+                            GError **error)
+{
+#ifdef ENABLE_PHONENUMBER
+
+	return _e_phone_number_cxx_from_string (phone_number, country_code, error);
+
+#else /* ENABLE_PHONENUMBER */
+
+	_e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+	return NULL;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_to_string:
+ * @phone_number: the phone number to format
+ * @format: the phone number format to apply
+ *
+ * Describes the @phone_number according to the rules applying to @format.
+ *
+ * Returns: (transfer full): A formatted string for @phone_number.
+ *
+ * Since: 3.8
+ **/
+gchar *
+e_phone_number_to_string (const EPhoneNumber *phone_number,
+                          EPhoneNumberFormat format)
+{
+#ifdef ENABLE_PHONENUMBER
+
+	return _e_phone_number_cxx_to_string (phone_number, format);
+
+#else /* ENABLE_PHONENUMBER */
+
+	g_warning ("%s: The library was built without phone number support.", G_STRFUNC);
+	return NULL;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * 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)
+{
+#ifdef ENABLE_PHONENUMBER
+
+	return _e_phone_number_cxx_compare (first_number, second_number);
+
+#else /* ENABLE_PHONENUMBER */
+
+	/* NOTE: This calls for a dedicated return value, but I sense broken
+	 * client code that only checks for E_PHONE_NUMBER_MATCH_NONE and then
+	 * treats the "not-implemented" return value as a match */
+	g_warning ("%s: The library was built without phone number support.", G_STRFUNC);
+	return E_PHONE_NUMBER_MATCH_NONE;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * 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)
+{
+#ifdef ENABLE_PHONENUMBER
+
+	return _e_phone_number_cxx_compare_strings (first_number, second_number, error);
+
+#else /* ENABLE_PHONENUMBER */
+
+	_e_phone_number_set_error (error, E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+	return E_PHONE_NUMBER_MATCH_NONE;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_copy:
+ * @phone_number: the EPhoneNumber to copy
+ *
+ * Makes a copy of @phone_number.
+ *
+ * Returns: (transfer full): A newly allocated EPhoneNumber instance.
+ * Call e_phone_number_free() to release this instance.
+ *
+ * Since: 3.8
+ **/
+EPhoneNumber *
+e_phone_number_copy (const EPhoneNumber *phone_number)
+{
+#ifdef ENABLE_PHONENUMBER
+
+	return _e_phone_number_cxx_copy (phone_number);
+
+#else /* ENABLE_PHONENUMBER */
+
+	/* Without phonenumber support there are no instances.
+	 * Any non-NULL value is a programming error in this setup. */
+	g_warn_if_fail (phone_number == NULL);
+	return NULL;
+
+#endif /* ENABLE_PHONENUMBER */
+}
+
+/**
+ * e_phone_number_free:
+ * @phone_number: the EPhoneNumber to free
+ *
+ * Released the memory occupied by @phone_number.
+ *
+ * Since: 3.8
+ **/
+void
+e_phone_number_free (EPhoneNumber *phone_number)
+{
+#ifdef ENABLE_PHONENUMBER
+
+	_e_phone_number_cxx_free (phone_number);
+
+#else /* ENABLE_PHONENUMBER */
+
+	/* Without phonenumber support there are no instances.
+	 * Any non-NULL value is a programming error in this setup. */
+	g_warn_if_fail (phone_number == NULL);
+
+#endif /* ENABLE_PHONENUMBER */
+}
diff --git a/addressbook/libebook/e-phone-number.h b/addressbook/libebook/e-phone-number.h
new file mode 100644
index 0000000..2e33835
--- /dev/null
+++ b/addressbook/libebook/e-phone-number.h
@@ -0,0 +1,188 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012,2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias openismus com>
+ */
+
+/**
+ * SECTION: e-phone-number
+ * @include: libedataserver/libedataserver.h
+ * @short_description: Phone number support
+ *
+ * This modules provides utility functions for parsing and formatting
+ * phone numbers. Under the hood it uses Google's libphonenumber.
+ **/
+
+#if !defined (__LIBEBOOK_H_INSIDE__) && !defined (LIBEBOOK_COMPILATION)
+#error "Only <libebook/libebook.h> should be included directly."
+#endif
+
+#ifndef E_PHONE_NUMBER_H
+#define E_PHONE_NUMBER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_PHONE_NUMBER (e_phone_number_get_type ())
+#define E_PHONE_NUMBER_ERROR (e_phone_number_error_quark ())
+
+/**
+ * EPhoneNumberFormat:
+ * @E_PHONE_NUMBER_FORMAT_E164: format according E.164: "+493055667788".
+ * @E_PHONE_NUMBER_FORMAT_INTERNATIONAL: a formatted phone number always
+ * starting with the country calling code: "+49 30 55667788".
+ * @E_PHONE_NUMBER_FORMAT_NATIONAL: a formatted phone number in national
+ * scope, that is without country code: "(030) 55667788".
+ * @E_PHONE_NUMBER_FORMAT_RFC3966: a tel: URL according to RFC 3966:
+ * "tel:+49-30-55667788".
+ *
+ * The supported formatting rules for phone numbers.
+ **/
+typedef enum {
+	E_PHONE_NUMBER_FORMAT_E164,
+	E_PHONE_NUMBER_FORMAT_INTERNATIONAL,
+	E_PHONE_NUMBER_FORMAT_NATIONAL,
+	E_PHONE_NUMBER_FORMAT_RFC3966
+} 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.
+ *
+ * The quality of a phone number match.
+
+ * Let's consider the phone number "+1-221-5423789", then comparing with
+ * "+1.221.542.3789" we have get E_PHONE_NUMBER_MATCH_EXACT because country
+ * code, region code and local number are matching. Comparing with "2215423789"
+ * will result in E_PHONE_NUMBER_MATCH_NATIONAL because the country code is
+ * missing, but the national portion is matching. Finally comparing with
+ * "5423789" gives E_PHONE_NUMBER_MATCH_SHORT. For more detail have a look at
+ * the following table:
+ *
+ * <informaltable border="1" align="center">
+ *  <colgroup>
+ *   <col width="20%" />
+ *   <col width="20%" />
+ *   <col width="20%" />
+ *   <col width="20%" />
+ *   <col width="20%" />
+ *  </colgroup>
+ *  <tbody>
+ *   <tr>
+ *    <th></th>
+ *    <th align="center">+1-617-5423789</th>
+ *    <th align="center">+1-221-5423789</th>
+ *    <th align="center">221-5423789</th>
+ *    <th align="center">5423789</th>
+ *   </tr><tr>
+ *    <th align="right">+1-617-5423789</th>
+ *    <td align="center">exact</td>
+ *    <td align="center">none</td>
+ *    <td align="center">none</td>
+ *    <td align="center">short</td>
+ *   </tr><tr>
+ *    <th align="right">+1-221-5423789</th>
+ *    <td align="center">none</td>
+ *    <td align="center">exact</td>
+ *    <td align="center">national</td>
+ *    <td align="center">short</td>
+ *   </tr><tr>
+ *    <th align="right">221-5423789</th>
+ *    <td align="center">none</td>
+ *    <td align="center">national</td>
+ *    <td align="center">national</td>
+ *    <td align="center">short</td>
+ *   </tr><tr>
+ *    <th align="right">5423789</th>
+ *    <td align="center">short</td>
+ *    <td align="center">short</td>
+ *    <td align="center">short</td>
+ *    <td align="center">short</td>
+ *   </tr>
+ *  </tbody>
+ * </informaltable>
+ */
+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
+ * @E_PHONE_NUMBER_ERROR_UNKNOWN: the phone number parser reported an yet
+ * unkown error code.
+ * @E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE: the supplied phone number has an
+ * invalid country code.
+ * @E_PHONE_NUMBER_ERROR_NOT_A_NUMBER: the supplied text is not a phone number.
+ * @E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD: the remaining text after the
+ * country code is to short for a phone number.
+ * @E_PHONE_NUMBER_ERROR_TOO_SHORT: the text is too short for a phone number.
+ * @E_PHONE_NUMBER_ERROR_TOO_LONG: the text is too long for a phone number.
+ *
+ * Numeric description of a phone number related error.
+ **/
+typedef enum {
+	E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED,
+	E_PHONE_NUMBER_ERROR_UNKNOWN,
+	E_PHONE_NUMBER_ERROR_NOT_A_NUMBER,
+	E_PHONE_NUMBER_ERROR_INVALID_COUNTRY_CODE,
+	E_PHONE_NUMBER_ERROR_TOO_SHORT_AFTER_IDD,
+	E_PHONE_NUMBER_ERROR_TOO_SHORT,
+	E_PHONE_NUMBER_ERROR_TOO_LONG
+} EPhoneNumberError;
+
+/**
+ * EPhoneNumber:
+ * This opaque type describes a parsed phone number. It can be copied using
+ * e_phone_number_copy(). To release it call e_phone_number_free().
+ */
+typedef struct _EPhoneNumber EPhoneNumber;
+
+GType			e_phone_number_get_type		(void);
+GQuark			e_phone_number_error_quark	(void);
+
+gboolean		e_phone_number_is_supported	(void) G_GNUC_CONST;
+
+EPhoneNumber *		e_phone_number_from_string	(const gchar *phone_number,
+							 const gchar *country_code,
+							 GError **error);
+gchar *			e_phone_number_to_string	(const EPhoneNumber *phone_number,
+							 EPhoneNumberFormat format);
+
+EPhoneNumberMatch	e_phone_number_compare		(const EPhoneNumber *first_number,
+							 const EPhoneNumber *second_number);
+EPhoneNumberMatch	e_phone_number_compare_strings	(const gchar *first_number,
+							 const gchar *second_number,
+							 GError **error);
+
+EPhoneNumber *		e_phone_number_copy		(const EPhoneNumber *phone_number);
+void			e_phone_number_free		(EPhoneNumber *phone_number);
+
+G_END_DECLS
+
+#endif /* E_PHONE_NUMBER_H */
diff --git a/addressbook/libebook/libebook.h b/addressbook/libebook/libebook.h
index b1ddffc..f8eafb0 100644
--- a/addressbook/libebook/libebook.h
+++ b/addressbook/libebook/libebook.h
@@ -34,6 +34,7 @@
 #include <libebook/e-contact.h>
 #include <libebook/e-destination.h>
 #include <libebook/e-name-western.h>
+#include <libebook/e-phone-number.h>
 #include <libebook/e-source-backend-summary-setup.h>
 #include <libebook/e-vcard.h>
 
diff --git a/configure.ac b/configure.ac
index 1128e30..68b495b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -272,6 +272,18 @@ localedir='$(prefix)/$(DATADIRNAME)/locale'
 AC_SUBST(localedir)
 
 dnl ******************************
+dnl libphonenumber support
+dnl ******************************
+
+dnl The EVO_PHONENUMBER_SUPPORT macro calls AC_PROG_CXX if libphonenumber
+dnl support got requested. Therefore this macro must be expanded before
+dnl the libtool macros. Feel free to move back to the other optional
+dnl dependencies if you know how to fix the autoconf issue, or if you
+dnl concluded that C++ actually is pretty awesome and should be a hard
+dnl dependency.
+EVO_PHONENUMBER_SUPPORT
+
+dnl ******************************
 dnl Initialize libtool
 dnl ******************************
 LT_PREREQ(2.2)
@@ -1672,6 +1684,7 @@ echo "
 	Kerberos 5:		$msg_krb5
 	SMIME support:		$msg_smime
 	IPv6 support:		$msg_ipv6
+	Phone number support:	$msg_phonenumber
 	Dot Locking:		$msg_dot
 	File Locking:		$msg_file
 	Large files:		$enable_largefile
diff --git a/docs/reference/addressbook/libebook/Makefile.am b/docs/reference/addressbook/libebook/Makefile.am
index 1d96640..7c1fb1f 100644
--- a/docs/reference/addressbook/libebook/Makefile.am
+++ b/docs/reference/addressbook/libebook/Makefile.am
@@ -4,6 +4,11 @@ DOC_MODULE = libebook
 # The top-level SGML file.
 DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.sgml
 
+# Extra options to supply to gtkdoc-mkdb
+# We must enable the SGML mode so that we can have HTML tables
+# in the documentation (e.g. to illustrate phone number matching).
+MKDB_OPTIONS = --sgml-mode --output-format=xml --name-space=e
+
 # Extra options to supply to gtkdoc-scan
 SCAN_OPTIONS = --deprecated-guards="EDS_DISABLE_DEPRECATED"
 
diff --git a/docs/reference/addressbook/libebook/libebook-docs.sgml b/docs/reference/addressbook/libebook/libebook-docs.sgml
index 0c2fc79..7144490 100644
--- a/docs/reference/addressbook/libebook/libebook-docs.sgml
+++ b/docs/reference/addressbook/libebook/libebook-docs.sgml
@@ -20,6 +20,7 @@
     <xi:include href="xml/e-address-western.xml"/>
     <xi:include href="xml/e-name-western.xml"/>
     <xi:include href="xml/e-source-backend-summary-setup.xml"/>
+    <xi:include href="xml/e-phone-number.xml"/>
   </chapter>
 
   <chapter>
diff --git a/docs/reference/addressbook/libebook/libebook-sections.txt b/docs/reference/addressbook/libebook/libebook-sections.txt
index 2bb7125..75d842e 100644
--- a/docs/reference/addressbook/libebook/libebook-sections.txt
+++ b/docs/reference/addressbook/libebook/libebook-sections.txt
@@ -553,3 +553,23 @@ e_source_backend_summary_setup_get_type
 <SUBSECTION Private>
 ESourceBackendSummarySetupPrivate
 </SECTION>
+
+<SECTION>
+<FILE>e-phone-number</FILE>
+EPhoneNumber
+EPhoneNumberError
+EPhoneNumberFormat
+EPhoneNumberMatch
+e_phone_number_is_supported
+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>
+E_PHONE_NUMBER_ERROR
+E_TYPE_PHONE_NUMBER
+e_phone_number_error_quark
+e_phone_number_get_type
+</SECTION>
diff --git a/m4/evo_phonenumber.m4 b/m4/evo_phonenumber.m4
new file mode 100644
index 0000000..ba0e00b
--- /dev/null
+++ b/m4/evo_phonenumber.m4
@@ -0,0 +1,69 @@
+dnl EVO_PHONENUMBER_SUPPORT([default])
+dnl Check for Google's libphonenumber. Adds a --with-phonenumber option
+dnl to explicitly enable and disable phonenumber support, but also for
+dnl pointing to libphonenumber's install prefix.
+AC_DEFUN([EVO_PHONENUMBER_SUPPORT],[
+	AC_MSG_CHECKING([whether to enable phonenumber support])
+
+	evo_phonenumber_prefix=
+	msg_phonenumber=no
+
+	PHONENUMBER_INCLUDES=
+	PHONENUMBER_LIBS=
+
+	AC_ARG_WITH([phonenumber],
+		[AS_HELP_STRING([--with-phonenumber@<:@=PREFIX@:>@],
+		                [use libphonenumber at PREFIX])],
+		[evo_phonenumber_prefix=$withval],
+		[with_phonenumber=m4_default([$1],[check])])
+
+	AC_MSG_RESULT([$with_phonenumber])
+
+	AS_VAR_IF([with_phonenumber], [no],, [
+		AC_REQUIRE([AC_PROG_CXX])
+		AC_LANG_PUSH(C++)
+
+		PHONENUMBER_LIBS="-lphonenumber -lboost_thread"
+
+		AS_VAR_IF([evo_phonenumber_prefix],,,[
+			PHONENUMBER_INCLUDES="-I$evo_phonenumber_prefix/include"
+			PHONENUMBER_LIBS="-L$evo_phonenumber_prefix/lib $PHONENUMBER_LIBS"
+		])
+
+		evo_cxxflags_saved="$CXXFLAGS"
+		CXXFLAGS="$CXXFLAGS $PHONENUMBER_INCLUDES"
+
+		evo_libs_saved="$LIBS"
+		LIBS="$LIBS $PHONENUMBER_LIBS"
+
+		AC_MSG_CHECKING([if libphonenumber is usable])
+		AC_LINK_IFELSE(
+			[AC_LANG_PROGRAM(
+				[[#include <phonenumbers/phonenumberutil.h>]],
+				[[i18n::phonenumbers::PhoneNumberUtil::GetInstance();]])],
+			[with_phonenumber=yes],
+			[AS_VAR_IF([with_phonenumber], [check], [with_phonenumber=no], [
+				AC_MSG_ERROR([libphonenumber cannot be used. Use --with-phonenumber to specify the library prefix.])])
+			])
+
+		CXXFLAGS="$evo_cxxflags_saved"
+		LDFLAGS="$evo_ldflags_saved"
+		LIBS="$evo_libs_saved"
+
+		AS_VAR_IF([evo_phonenumber_prefix],,
+		          [msg_phonenumber=$with_phonenumber],
+		          [msg_phonenumber=$evo_phonenumber_prefix])
+
+		AC_MSG_RESULT([$with_phonenumber])
+		AC_LANG_POP(C++)
+	])
+
+	AM_CONDITIONAL([ENABLE_PHONENUMBER],
+	               [test "x$with_phonenumber" != "xno"])
+
+	AS_VAR_IF([with_phonenumber], [yes],
+	          [AC_DEFINE([ENABLE_PHONENUMBER], 1, [Enable phonenumber parsing])])
+
+	AC_SUBST([PHONENUMBER_INCLUDES])
+	AC_SUBST([PHONENUMBER_LIBS])
+])
diff --git a/tests/libebook/Makefile.am b/tests/libebook/Makefile.am
index 7fff918..7b365bf 100644
--- a/tests/libebook/Makefile.am
+++ b/tests/libebook/Makefile.am
@@ -51,6 +51,7 @@ DEPRECATED_TESTS =				\
 
 # Should be kept ordered approximately from least to most difficult/complex
 TESTS = \
+	test-ebook-phone-number \
 	test-ebook-add-contact \
 	test-ebook-get-contact \
 	test-ebook-commit-contact \
@@ -134,6 +135,8 @@ test_ebook_remove_contact_by_id_LDADD=$(TEST_LIBS)
 test_ebook_remove_contact_by_id_CPPFLAGS=$(TEST_CPPFLAGS)
 test_ebook_remove_contacts_LDADD=$(TEST_LIBS)
 test_ebook_remove_contacts_CPPFLAGS=$(TEST_CPPFLAGS)
+test_ebook_phone_number_LDADD=$(TEST_LIBS)
+test_ebook_phone_number_CPPFLAGS=$(TEST_CPPFLAGS)
 #test_ebook_stress_factory__fifo_LDADD=$(TEST_LIBS)
 test_ebook_stress_factory__fifo_CPPFLAGS=$(TEST_CPPFLAGS)
 #test_ebook_stress_factory__serial_LDADD=$(TEST_LIBS)
diff --git a/tests/libebook/test-ebook-phone-number.c b/tests/libebook/test-ebook-phone-number.c
new file mode 100644
index 0000000..9d9e740
--- /dev/null
+++ b/tests/libebook/test-ebook-phone-number.c
@@ -0,0 +1,289 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012,2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Mathias Hasselmann <mathias openismus com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libebook/libebook.h>
+
+static const char *match_candidates[] = {
+	"not-a-number",
+	"+1-617-4663489", "617-4663489", "4663489",
+	"+1.408.845.5246", "4088455246", "8455246",
+	"+1-857-4663489"
+};
+
+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,
+	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,
+
+	/* 617-4663489 */
+	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,
+
+	/* 4663489 */
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_SHORT,
+	E_PHONE_NUMBER_MATCH_SHORT,
+	E_PHONE_NUMBER_MATCH_NATIONAL, /* XXX - Google, really? I'd expect a full match here. */
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_SHORT,
+
+	/* +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,
+
+	/* 4088455246 */
+	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,
+
+	/* 8455246 */
+	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, really?  I'd expect a full match here. */
+	E_PHONE_NUMBER_MATCH_NONE,
+
+	/* +1-857-4663489 */
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_SHORT,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_NONE,
+	E_PHONE_NUMBER_MATCH_EXACT
+};
+
+static void
+test_parse_and_format (gconstpointer data)
+{
+	GError *error = NULL;
+	EPhoneNumber *parsed;
+	gchar **params;
+
+	params = g_strsplit (data, "/", G_MAXINT);
+	g_assert_cmpint (g_strv_length (params), ==, 6);
+
+	parsed = e_phone_number_from_string (params[0], params[1], &error);
+
+#ifdef ENABLE_PHONENUMBER
+
+	{
+		gchar **test_numbers;
+		gint i;
+
+		test_numbers = params + 2;
+
+		g_assert (parsed != NULL);
+		g_assert (error == NULL);
+
+		for (i = 0; test_numbers[i]; ++i) {
+			gchar *formatted;
+
+			formatted = e_phone_number_to_string (parsed, i);
+			g_assert (formatted != NULL);
+			g_assert_cmpstr (formatted, ==, test_numbers[i]);
+			g_free (formatted);
+		}
+
+		e_phone_number_free (parsed);
+	}
+
+#else /* ENABLE_PHONENUMBER */
+
+	g_assert (parsed == NULL);
+	g_assert (error != NULL);
+	g_assert (error->domain == E_PHONE_NUMBER_ERROR);
+	g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
+	g_assert (error->message != NULL);
+
+#endif /* ENABLE_PHONENUMBER */
+
+	g_clear_error (&error);
+	g_strfreev (params);
+}
+
+static void
+test_parse_bad_number (void)
+{
+	GError *error = NULL;
+	EPhoneNumber *parsed;
+
+	parsed = e_phone_number_from_string ("+1-NOT-A-NUMBER", "US", &error);
+
+	g_assert (parsed == NULL);
+	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);
+}
+
+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);
+
+#ifdef ENABLE_PHONENUMBER
+	g_assert_cmpuint (actual_match, ==, expected_matches[n]);
+#else /* ENABLE_PHONENUMBER */
+	g_assert_cmpuint (actual_match, ==, E_PHONE_NUMBER_MATCH_NONE);
+#endif /* ENABLE_PHONENUMBER */
+
+	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);
+	}
+}
+
+static void
+test_supported (void)
+{
+#ifdef ENABLE_PHONENUMBER
+	g_assert (e_phone_number_is_supported ());
+#else /* ENABLE_PHONENUMBER */
+	g_assert (!e_phone_number_is_supported ());
+#endif /* ENABLE_PHONENUMBER */
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+	size_t i, j;
+
+	g_type_init ();
+
+	g_test_init (&argc, &argv, NULL);
+
+	g_test_add_func
+		("/ebook-phone-number/supported",
+		 test_supported);
+
+	g_test_add_data_func
+		("/ebook-phone-number/parse-and-format/i164",
+		 "+493011223344//+493011223344/+49 30 11223344/030 11223344/tel:+49-30-11223344",
+		 test_parse_and_format);
+	g_test_add_data_func
+		("/ebook-phone-number/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
+		("/ebook-phone-number/parse-and-format/international",
+		 "+1 212 33445566//+121233445566/+1 21233445566/21233445566/tel:+1-21233445566",
+		 test_parse_and_format);
+	g_test_add_data_func
+		("/ebook-phone-number/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
+		("/ebook-phone-number/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 + i * G_N_ELEMENTS (match_candidates);
+			char *path = g_strdup_printf ("/ebook-phone-number/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]