[evolution-data-server/intel-work-3-12] EContact: Added support for phonetic names fields.
- From: Mateusz Polrola <mpolrola src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/intel-work-3-12] EContact: Added support for phonetic names fields.
- Date: Tue, 14 Oct 2014 12:56:16 +0000 (UTC)
commit 92cca2205f9c5ddee4acc460a93f6481416ea30b
Author: Mateusz Polrola <mateusz polrola gmail com>
Date: Thu Oct 9 12:23:55 2014 +0200
EContact: Added support for phonetic names fields.
VCards can contain fields with phonetic version of contact family and
given names.
It should be possible to use them as sorting keys for database queries
as for some languages like e.g. Japanese it is not possible to generate
correct sorting order even using ICU library, as the same Kanji
characters can have different pronunciation e.g.:
* 淳子 (Junko)
* 淳子 (Atsuko)
* 淳子 (Kiyoko)
* 淳子 (Akiko)
More information can be found here:
http://www.localizingjapan.com/blog/2011/02/13/sorting-in-japanese-%E2%80%94-an-unsolved-problem/
VCards supports additional fields that can contain phonetic version of
names allowing for correct sorting order in aboive cases.
As these fields are not standardized different VCard providers can use
different fields to store phonetic information e.g.
Android phones, Google Contacts and iCloud is using
"X-PHONETIC-FIRST-NAME" and "X-PHONETIC-LAST-NAME" fields,
BlackBerry Z10 uses "X-FAMILYNAME-SOUND" and "X-GIVENNAME-SOUND",
also according to
http://www.mcpc-jp.org/news/pdf/TR-015vCard_Guideline_V1.00.pdf
"X-YOMI-FNAME" and "X-YOMI-LNAME" can be used.
This patch adds phonetic family and given name attributes to
EContact. Values of these attributes can be parsed from all VCard fields
mentioned above. Additionally when VCard does not contain any of these
fields, synthetic ones will be created using contact family and given
name
respectively - in that way these fields can be safely used as sort
key in database queries.
EContact when converted back to vCard will store phonetic names using
"X-PHONETIC-FIRST-NAME" and "X-PHONETIC-LAST-NAME", if source VCard
was using different fields for them, they will also be preset in
resulting VCard.
addressbook/libebook-contacts/e-contact.c | 174 ++++++++++++++++++++++++++
addressbook/libebook-contacts/e-contact.h | 6 +
addressbook/libebook-contacts/e-vcard.h | 7 +
tests/libebook-contacts/test-vcard-parsing.c | 86 +++++++++++++
4 files changed, 273 insertions(+), 0 deletions(-)
---
diff --git a/addressbook/libebook-contacts/e-contact.c b/addressbook/libebook-contacts/e-contact.c
index a2a5ac4..016487b 100644
--- a/addressbook/libebook-contacts/e-contact.c
+++ b/addressbook/libebook-contacts/e-contact.c
@@ -110,6 +110,10 @@ static gpointer date_getter (EContact *contact, EVCardAttribute *attr);
static void date_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
static gpointer cert_getter (EContact *contact, EVCardAttribute *attr);
static void cert_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
+static gpointer phonetic_given_name_getter (EContact *contact, EVCardAttribute *attr);
+static void phonetic_given_name_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
+static gpointer phonetic_family_name_getter (EContact *contact, EVCardAttribute *attr);
+static void phonetic_family_name_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
#define STRING_FIELD(id,vc,n,pn,ro) { E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro) }
#define BOOLEAN_FIELD(id,vc,n,pn,ro) { E_CONTACT_FIELD_TYPE_BOOLEAN, (id), (vc), (n), (pn), (ro) }
@@ -146,6 +150,15 @@ static const EContactFieldInfo field_info[] = {
LIST_ELEM_STR_FIELD (E_CONTACT_FAMILY_NAME, EVC_N, "family_name", N_("Family Name"), FALSE, 0),
STRING_FIELD (E_CONTACT_NICKNAME, EVC_NICKNAME, "nickname", N_("Nickname"), FALSE),
+ /* Phonetic name fields */
+ /* If vcard does not contains this fields they will be set to
+ * E_CONTACT_GIVEN_NAME and E_CONTACT_FAMILY_NAME respectively,
+ * this will ensure that each contact will have value assigned
+ * to these fields so they can be used as sort key in database
+ * queries */
+ GETSET_FIELD (E_CONTACT_GIVEN_NAME_PHONETIC, EVC_X_PHONETIC_FIRST_NAME,
"phonetic_given_name", N_("Phonetic Given Name"), FALSE, phonetic_given_name_getter,
phonetic_given_name_setter),
+ GETSET_FIELD (E_CONTACT_FAMILY_NAME_PHONETIC, EVC_X_PHONETIC_LAST_NAME,
"phonetic_family_name", N_("Phonetic Family Name"), FALSE, phonetic_family_name_getter,
phonetic_family_name_setter),
+
/* Email fields */
MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_1, EVC_EMAIL, "email_1", N_("Email 1"),
FALSE, 0),
MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_2, EVC_EMAIL, "email_2", N_("Email 2"),
FALSE, 1),
@@ -854,6 +867,167 @@ cert_setter (EContact *contact,
e_vcard_attribute_add_value_decoded (attr, cert->data, cert->length);
}
+static gpointer
+phonetic_given_name_getter (EContact *contact,
+ EVCardAttribute *attr)
+{
+ GList *p = NULL;
+ gboolean add_attribute = FALSE;
+
+ /* If contact does not contain EVC_X_PHONETIC_FIRST_NAME,
+ * check other vcard fields that can be used to store phonetic given name.
+ * Mark also that EVC_X_PHONETIC_FIRST_NAME attribute should be added.
+ */
+ if (!attr) {
+ add_attribute = TRUE;
+ attr = e_vcard_get_attribute (E_VCARD (contact), EVC_X_YOMI_FNAME);
+ }
+
+ if (!attr) {
+ attr = e_vcard_get_attribute (E_VCARD (contact), EVC_X_GIVENNAME_SOUND);
+ }
+
+ if (attr) {
+ p = e_vcard_attribute_get_values (attr);
+ if (!p || !p->data || !*((const gchar *) p->data)) {
+ p = NULL;
+ }
+ }
+
+ if (!p) {
+ /* VCard does not contains any field with phonetic given name
+ * Generate it using contact given name */
+ EContactName *name;
+ gchar *new_phonetic_given_name = NULL;
+
+ name = e_contact_get (contact, E_CONTACT_NAME);
+
+ /* Use name if available */
+ if (name) {
+ if (name->given && *name->given) {
+ new_phonetic_given_name = g_strdup (name->given);
+ }
+
+ e_contact_name_free (name);
+ }
+
+ /* Add the EVC_X_PHONETIC_FIRST_NAME attribute to contact */
+ if (new_phonetic_given_name) {
+ add_attribute = FALSE;
+ attr = e_vcard_attribute_new (NULL, EVC_X_PHONETIC_FIRST_NAME);
+ e_vcard_append_attribute_with_value (E_VCARD (contact), attr,
new_phonetic_given_name);
+
+ g_free (new_phonetic_given_name);
+ }
+ }
+
+ if (attr) {
+ if (add_attribute) {
+ p = e_vcard_attribute_get_values (attr);
+ attr = e_vcard_attribute_new (NULL, EVC_X_PHONETIC_FIRST_NAME);
+ e_vcard_append_attribute_with_value (E_VCARD (contact), attr, p && p->data ? p->data
: (gpointer) "");
+ }
+
+ p = e_vcard_attribute_get_values (attr);
+
+ return p && p->data ? p->data : (gpointer) "";
+ } else {
+ return NULL;
+ }
+}
+
+static void
+phonetic_given_name_setter (EContact *contact,
+ EVCardAttribute *attr,
+ gpointer data)
+{
+ /* Default implementation */
+ const gchar *phonetic_given_name = data;
+ e_vcard_attribute_add_value (attr,
+ phonetic_given_name ? phonetic_given_name : "");
+}
+
+static gpointer
+phonetic_family_name_getter (EContact *contact,
+ EVCardAttribute *attr)
+{
+ GList *p = NULL;
+ gboolean add_attribute = FALSE;
+
+ /* If contact does not contain EVC_X_PHONETIC_LAST_NAME,
+ * check other vcard fields that can be used to store phonetic family name.
+ * Mark also that EVC_X_PHONETIC_LAST_NAME attribute should be added.
+ */
+ if (!attr) {
+ add_attribute = TRUE;
+ attr = e_vcard_get_attribute (E_VCARD (contact), EVC_X_YOMI_LNAME);
+ }
+
+ if (!attr) {
+ attr = e_vcard_get_attribute (E_VCARD (contact), EVC_X_FAMILYNAME_SOUND);
+ }
+
+ if (attr) {
+ p = e_vcard_attribute_get_values (attr);
+ if (!p || !p->data || !*((const gchar *) p->data)) {
+ p = NULL;
+ }
+ }
+
+ if (!p) {
+ /* VCard does not contains any field with phonetic family name
+ * Generate it using contact family name
+ * */
+ EContactName *name;
+ gchar *new_phonetic_family_name = NULL;
+
+ name = e_contact_get (contact, E_CONTACT_NAME);
+
+ /* Use name if available */
+ if (name) {
+ if (name->family && *name->family) {
+ new_phonetic_family_name = g_strdup (name->family);
+ }
+
+ e_contact_name_free (name);
+ }
+
+ /* Add the EVC_X_PHONETIC_LAST_NAME attribute to contact */
+ if (new_phonetic_family_name) {
+ add_attribute = FALSE;
+ attr = e_vcard_attribute_new (NULL, EVC_X_PHONETIC_LAST_NAME);
+ e_vcard_append_attribute_with_value (E_VCARD (contact), attr,
new_phonetic_family_name);
+
+ g_free (new_phonetic_family_name);
+ }
+ }
+
+ if (attr) {
+ if (add_attribute) {
+ p = e_vcard_attribute_get_values (attr);
+ attr = e_vcard_attribute_new (NULL, EVC_X_PHONETIC_LAST_NAME);
+ e_vcard_append_attribute_with_value (E_VCARD (contact), attr, p && p->data ? p->data
: (gpointer) "");
+ }
+
+ p = e_vcard_attribute_get_values (attr);
+
+ return p && p->data ? p->data : (gpointer) "";
+ } else {
+ return NULL;
+ }
+}
+
+static void
+phonetic_family_name_setter (EContact *contact,
+ EVCardAttribute *attr,
+ gpointer data)
+{
+ /* Default implementation */
+ const gchar *phonetic_family_name = data;
+ e_vcard_attribute_add_value (attr,
+ phonetic_family_name ? phonetic_family_name : "");
+}
+
static EVCardAttribute *
e_contact_find_attribute_with_types (EContact *contact,
const gchar *attr_name,
diff --git a/addressbook/libebook-contacts/e-contact.h b/addressbook/libebook-contacts/e-contact.h
index dbd9778..ac704a7 100644
--- a/addressbook/libebook-contacts/e-contact.h
+++ b/addressbook/libebook-contacts/e-contact.h
@@ -76,6 +76,10 @@ typedef enum {
E_CONTACT_FAMILY_NAME, /* synthetic string field */
E_CONTACT_NICKNAME, /* string field */
+ /* Phonetic name fields */
+ E_CONTACT_GIVEN_NAME_PHONETIC, /* synthetic string field */
+ E_CONTACT_FAMILY_NAME_PHONETIC, /* synthetic string field */
+
/* Email fields */
E_CONTACT_EMAIL_1, /* synthetic string field */
E_CONTACT_EMAIL_2, /* synthetic string field */
@@ -271,6 +275,8 @@ typedef struct {
gchar *additional;
gchar *prefixes;
gchar *suffixes;
+ gchar *family_phonetic;
+ gchar *given_phonetic;
} EContactName;
/**
diff --git a/addressbook/libebook-contacts/e-vcard.h b/addressbook/libebook-contacts/e-vcard.h
index 0578297..4cd5437 100644
--- a/addressbook/libebook-contacts/e-vcard.h
+++ b/addressbook/libebook-contacts/e-vcard.h
@@ -166,6 +166,13 @@ G_BEGIN_DECLS
**/
#define EVC_CL_UID "X-EVOLUTION-CONTACT-LIST-UID"
+#define EVC_X_PHONETIC_FIRST_NAME "X-PHONETIC-FIRST-NAME"
+#define EVC_X_PHONETIC_LAST_NAME "X-PHONETIC-LAST-NAME"
+#define EVC_X_YOMI_FNAME "X-YOMI-FNAME"
+#define EVC_X_YOMI_LNAME "X-YOMI-LNAME"
+#define EVC_X_FAMILYNAME_SOUND "X-FAMILYNAME-SOUND"
+#define EVC_X_GIVENNAME_SOUND "X-GIVENNAME-SOUND"
+
#ifndef EDS_DISABLE_DEPRECATED
#define EVC_X_DEST_EMAIL "X-EVOLUTION-DEST-EMAIL"
#define EVC_X_DEST_NAME "X-EVOLUTION-DEST-NAME"
diff --git a/tests/libebook-contacts/test-vcard-parsing.c b/tests/libebook-contacts/test-vcard-parsing.c
index 7a997ab..e3c333f 100644
--- a/tests/libebook-contacts/test-vcard-parsing.c
+++ b/tests/libebook-contacts/test-vcard-parsing.c
@@ -377,6 +377,37 @@ test_vcard_quoted_printable (void)
g_assert (test_vcard_qp_3_0_saving (expected_text));
}
+static gboolean
+test_vcard_with_phonetic (const gchar* vcard_str, const gchar* expected_family_name, const gchar*
expected_given_name)
+{
+ EContact *c;
+ const gchar* family_name;
+ const gchar* given_name;
+
+ c = e_contact_new_from_vcard (vcard_str);
+ g_return_val_if_fail (e_vcard_is_parsed (E_VCARD (c)) == FALSE, FALSE);
+ family_name = (const gchar*) e_contact_get_const (c, E_CONTACT_FAMILY_NAME_PHONETIC);
+ given_name = (const gchar*) e_contact_get_const (c, E_CONTACT_GIVEN_NAME_PHONETIC);
+
+ if (family_name == NULL || expected_family_name == NULL) {
+ g_return_val_if_fail (family_name == expected_family_name, FALSE);
+ }
+ else {
+ g_return_val_if_fail (g_strcmp0(family_name, expected_family_name) == 0, FALSE);
+ }
+
+ if (given_name == NULL || expected_given_name == NULL) {
+ g_return_val_if_fail (given_name == expected_given_name, FALSE);
+ }
+ else {
+ g_return_val_if_fail (g_strcmp0(given_name, expected_given_name) == 0, FALSE);
+ }
+
+ g_object_unref (c);
+
+ return TRUE;
+}
+
static const gchar *test_vcard_no_uid_str =
"BEGIN:VCARD\r\n"
"VERSION:3.0\r\n"
@@ -394,6 +425,38 @@ static const gchar *test_vcard_with_uid_str =
"N:zyx;mix;;;\r\n"
"END:VCARD";
+static const gchar *test_vcard_with_x_phonetic =
+ "BEGIN:VCARD\r\n"
+ "VERSION:3.0\r\n"
+ "FN:zyx mix\r\n"
+ "N:zyx;mix;;;\r\n"
+ "X-PHONETIC-FIRST-NAME:xim\r\n"
+ "X-PHONETIC-LAST-NAME:xyz\r\n"
+ "END:VCARD";
+
+static const gchar *test_vcard_with_x_sound =
+ "BEGIN:VCARD\r\n"
+ "VERSION:3.0\r\n"
+ "FN:zyx mix\r\n"
+ "N:zyx;mix;;;\r\n"
+ "X-GIVENNAME-SOUND:xim\r\n"
+ "X-FAMILYNAME-SOUND:xyz\r\n"
+ "END:VCARD";
+
+static const gchar *test_vcard_with_x_yomi =
+ "BEGIN:VCARD\r\n"
+ "VERSION:3.0\r\n"
+ "FN:zyx mix\r\n"
+ "N:zyx;mix;;;\r\n"
+ "X-YOMI-FNAME:xim\r\n"
+ "X-YOMI-LNAME:xyz\r\n"
+ "END:VCARD";
+
+static const gchar *test_vcard_without_name =
+ "BEGIN:VCARD\r\n"
+ "VERSION:3.0\r\n"
+ "END:VCARD";
+
static void
test_vcard_with_uid (void)
{
@@ -418,6 +481,26 @@ test_contact_without_uid (void)
g_assert (test_econtact (test_vcard_no_uid_str));
}
+static void
+test_contact_with_x_phonetic (void)
+{
+ g_assert (test_vcard_with_phonetic (test_vcard_with_x_phonetic, "xyz", "xim"));
+ g_assert (test_vcard_with_phonetic (test_vcard_with_x_sound, "xyz", "xim"));
+ g_assert (test_vcard_with_phonetic (test_vcard_with_x_yomi, "xyz", "xim"));
+}
+
+static void
+test_contact_without_phonetic (void)
+{
+ g_assert (test_vcard_with_phonetic (test_vcard_no_uid_str, "zyx", "mix"));
+}
+
+static void
+test_contact_without_name (void)
+{
+ g_assert (test_vcard_with_phonetic (test_vcard_without_name, NULL, NULL));
+}
+
gint
main (gint argc,
gchar **argv)
@@ -430,6 +513,9 @@ main (gint argc,
g_test_add_func ("/Parsing/VCard/WithUID", test_contact_with_uid);
g_test_add_func ("/Parsing/VCard/WithoutUID", test_contact_without_uid);
g_test_add_func ("/Parsing/VCard/QuotedPrintable", test_vcard_quoted_printable);
+ g_test_add_func ("/Parsing/VCard/WithoutPhonetics", test_contact_without_phonetic);
+ g_test_add_func ("/Parsing/VCard/WithXPhonetics", test_contact_with_x_phonetic);
+ g_test_add_func ("/Parsing/VCard/WithoutName", test_contact_without_name);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]