[evolution-data-server/intel-work-3-12] EContact: Added support for phonetic names fields.



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]