[evolution-data-server/intel-work-3-12: 2/4] EBookSqlite: Added support for transliterated queries
- From: Mateusz Polrola <mpolrola src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/intel-work-3-12: 2/4] EBookSqlite: Added support for transliterated queries
- Date: Fri, 30 May 2014 12:06:19 +0000 (UTC)
commit 0baa84b5b6d351af20ffbcc0e9d15523b7de712b
Author: Mateusz Polrola <mateusz polrola gmail com>
Date: Fri May 30 12:44:35 2014 +0200
EBookSqlite: Added support for transliterated queries
Cherry picked from commits in openismus-work-3-8:
Added ETransliterator wrapper object. 0471dc
EBookQuery: Added transliteration queries. c99eba
EBookBackendSexp: Support new transliterated query types. 569d94
test-client-custom-summary.c: Added tests for transliteration queries.
ebe6d9
test-client-custom-summary.c: Added test for transliterated queries
c7be1c
test-client-cursor-operations.c: Added tests for using
transliteratedqueries f0a2d1
Tests: Added 3 new contacts and a few more query tests. 87e1d1
EBookSqlite: Not handle transliterated queries gracefully 7b6d9a
Added E_BOOK_INDEX_TRANSLIT index type 19f482
EBookSqlite: Full support for transliterated queries e59b57
Added transliterated indexes to the custom book in the test case 6b857b
.../libebook-contacts/e-book-contacts-types.h | 3 +-
addressbook/libebook-contacts/e-book-query.c | 52 +++++
addressbook/libebook-contacts/e-book-query.h | 9 +
addressbook/libedata-book/e-book-backend-sexp.c | 139 +++++++++++
addressbook/libedata-book/e-book-sqlite.c | 242 +++++++++++++++++++-
libedataserver/Makefile.am | 2 +
libedataserver/e-collator.c | 2 +-
libedataserver/e-transliterator-private.cpp | 20 +-
libedataserver/e-transliterator-private.h | 12 +-
libedataserver/e-transliterator.c | 155 +++++++++++++
libedataserver/e-transliterator.h | 55 +++++
libedataserver/libedataserver.h | 1 +
.../client/test-book-client-cursor-operations.c | 41 ++++
.../client/test-book-client-custom-summary.c | 103 ++++++++-
tests/libebook/data/vcards/custom-13.vcf | 6 +-
tests/libebook/data/vcards/custom-14.vcf | 6 +-
tests/libebook/data/vcards/custom-15.vcf | 6 +-
tests/libebook/data/vcards/custom-16.vcf | 6 +
tests/libebook/data/vcards/sorted-4.vcf | 1 +
tests/libebook/data/vcards/sorted-5.vcf | 2 +-
tests/libebook/data/vcards/sorted-9.vcf | 2 +-
21 files changed, 825 insertions(+), 40 deletions(-)
---
diff --git a/addressbook/libebook-contacts/e-book-contacts-types.h
b/addressbook/libebook-contacts/e-book-contacts-types.h
index 7ee3b79..3c0f3f7 100644
--- a/addressbook/libebook-contacts/e-book-contacts-types.h
+++ b/addressbook/libebook-contacts/e-book-contacts-types.h
@@ -146,7 +146,8 @@ typedef enum {
E_BOOK_INDEX_PREFIX = 0,
E_BOOK_INDEX_SUFFIX,
E_BOOK_INDEX_PHONE,
- E_BOOK_INDEX_SORT_KEY
+ E_BOOK_INDEX_SORT_KEY,
+ E_BOOK_INDEX_TRANSLIT
} EBookIndexType;
/**
diff --git a/addressbook/libebook-contacts/e-book-query.c b/addressbook/libebook-contacts/e-book-query.c
index 419255a..39b7c20 100644
--- a/addressbook/libebook-contacts/e-book-query.c
+++ b/addressbook/libebook-contacts/e-book-query.c
@@ -670,6 +670,42 @@ func_regex_raw (struct _ESExp *f,
}
static ESExpResult *
+func_translit_contains (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ return func_field_test (E_BOOK_QUERY_TRANSLIT_CONTAINS, f, argc, argv, data);
+}
+
+static ESExpResult *
+func_translit_is (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ return func_field_test (E_BOOK_QUERY_TRANSLIT_IS, f, argc, argv, data);
+}
+
+static ESExpResult *
+func_translit_beginswith (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ return func_field_test (E_BOOK_QUERY_TRANSLIT_BEGINS_WITH, f, argc, argv, data);
+}
+
+static ESExpResult *
+func_translit_endswith (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ return func_field_test (E_BOOK_QUERY_TRANSLIT_ENDS_WITH, f, argc, argv, data);
+}
+
+static ESExpResult *
func_exists (struct _ESExp *f,
gint argc,
struct _ESExpResult **argv,
@@ -734,6 +770,10 @@ static const struct {
{ "eqphone_short", func_eqphone_short, 0 },
{ "regex_normal", func_regex_normal, 0 },
{ "regex_raw", func_regex_raw, 0 },
+ { "translit_is", func_translit_is, 0 },
+ { "translit_contains", func_translit_contains, 0 },
+ { "translit_beginswith", func_translit_beginswith, 0 },
+ { "translit_endswith", func_translit_endswith, 0 },
{ "exists", func_exists, 0 },
{ "exists_vcard", func_exists_vcard, 0 }
};
@@ -814,6 +854,14 @@ field_test_name (EBookQueryTest field_test)
return "regex_normal";
case E_BOOK_QUERY_REGEX_RAW:
return "regex_raw";
+ case E_BOOK_QUERY_TRANSLIT_IS:
+ return "translit_is";
+ case E_BOOK_QUERY_TRANSLIT_CONTAINS:
+ return "translit_contains";
+ case E_BOOK_QUERY_TRANSLIT_BEGINS_WITH:
+ return "translit_beginswith";
+ case E_BOOK_QUERY_TRANSLIT_ENDS_WITH:
+ return "translit_endswith";
case E_BOOK_QUERY_LAST:
g_return_val_if_reached (NULL);
}
@@ -836,6 +884,10 @@ is_phone_test (EBookQueryTest field_test)
case E_BOOK_QUERY_ENDS_WITH:
case E_BOOK_QUERY_REGEX_NORMAL:
case E_BOOK_QUERY_REGEX_RAW:
+ case E_BOOK_QUERY_TRANSLIT_IS:
+ case E_BOOK_QUERY_TRANSLIT_CONTAINS:
+ case E_BOOK_QUERY_TRANSLIT_BEGINS_WITH:
+ case E_BOOK_QUERY_TRANSLIT_ENDS_WITH:
case E_BOOK_QUERY_LAST:
break;
}
diff --git a/addressbook/libebook-contacts/e-book-query.h b/addressbook/libebook-contacts/e-book-query.h
index d0aa787..25c1e6b 100644
--- a/addressbook/libebook-contacts/e-book-query.h
+++ b/addressbook/libebook-contacts/e-book-query.h
@@ -58,6 +58,10 @@ typedef struct _EBookQuery EBookQuery;
* @E_BOOK_QUERY_REGEX_RAW: A regular expression query against raw contact data, this is usually slower than
* a %E_BOOK_QUERY_REGEX_NORMAL as it implies that #EVCards must be parsed in order to get the raw data
* for comparison.
+ * @E_BOOK_QUERY_TRANSLIT_IS: Like %E_BOOK_QUERY_IS, but transliterates text to Latin script before the
comparison
+ * @E_BOOK_QUERY_TRANSLIT_CONTAINS: Like %E_BOOK_QUERY_CONTAINS, but transliterates text to Latin script
before the comparison
+ * @E_BOOK_QUERY_TRANSLIT_BEGINS_WITH: Like %E_BOOK_QUERY_BEGINS_WITH, but transliterates text to Latin
script before the comparison
+ * @E_BOOK_QUERY_TRANSLIT_ENDS_WITH: Like %E_BOOK_QUERY_ENDS_WITH, but transliterates text to Latin script
before the comparison
* @E_BOOK_QUERY_LAST: End marker for the #EBookQueryTest enumeration, not a valid query test.
*
* The kind of test a query created by e_book_query_field_test() shall perform.
@@ -77,6 +81,11 @@ typedef enum {
E_BOOK_QUERY_REGEX_NORMAL,
E_BOOK_QUERY_REGEX_RAW,
+ E_BOOK_QUERY_TRANSLIT_IS,
+ E_BOOK_QUERY_TRANSLIT_CONTAINS,
+ E_BOOK_QUERY_TRANSLIT_BEGINS_WITH,
+ E_BOOK_QUERY_TRANSLIT_ENDS_WITH,
+
/*
Consider these "coming soon".
diff --git a/addressbook/libedata-book/e-book-backend-sexp.c b/addressbook/libedata-book/e-book-backend-sexp.c
index e0bf16c..842b2ab 100644
--- a/addressbook/libedata-book/e-book-backend-sexp.c
+++ b/addressbook/libedata-book/e-book-backend-sexp.c
@@ -1073,6 +1073,141 @@ func_exists_vcard (struct _ESExp *f,
return r;
}
+/* first space between words is treated as wildcard character;
+ * we are looking for s2 in s1, so s2 will be breaked into words
+ */
+static gboolean
+translit_contains_helper (const gchar *s1,
+ const gchar *s2,
+ const gchar *region)
+{
+ ETransliterator *transliterator;
+ gchar *t1, *t2;
+ gboolean res = FALSE;
+
+ transliterator = e_transliterator_new ("Any-Latin");
+ t1 = e_transliterator_transliterate (transliterator, s1);
+ t2 = e_transliterator_transliterate (transliterator, s2);
+ e_transliterator_unref (transliterator);
+
+ res = contains_helper (t1, t2, region);
+
+ g_free (t1);
+ g_free (t2);
+
+ return res;
+}
+
+static ESExpResult *
+func_translit_contains (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ SearchContext *ctx = data;
+
+ return entry_compare (ctx, f, argc, argv, translit_contains_helper);
+}
+
+static gboolean
+translit_is_helper (const gchar *ps1,
+ const gchar *ps2,
+ const gchar *region)
+{
+ ETransliterator *transliterator;
+ gchar *t1, *t2;
+ gboolean res = FALSE;
+
+ transliterator = e_transliterator_new ("Any-Latin");
+ t1 = e_transliterator_transliterate (transliterator, ps1);
+ t2 = e_transliterator_transliterate (transliterator, ps2);
+ e_transliterator_unref (transliterator);
+
+ res = is_helper (t1, t2, region);
+
+ g_free (t1);
+ g_free (t2);
+
+ return res;
+}
+
+static ESExpResult *
+func_translit_is (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ SearchContext *ctx = data;
+
+ return entry_compare (ctx, f, argc, argv, translit_is_helper);
+}
+
+static gboolean
+translit_endswith_helper (const gchar *ps1,
+ const gchar *ps2,
+ const gchar *region)
+{
+ ETransliterator *transliterator;
+ gchar *t1, *t2;
+ gboolean res = FALSE;
+
+ transliterator = e_transliterator_new ("Any-Latin");
+ t1 = e_transliterator_transliterate (transliterator, ps1);
+ t2 = e_transliterator_transliterate (transliterator, ps2);
+ e_transliterator_unref (transliterator);
+
+ res = endswith_helper (t1, t2, region);
+
+ g_free (t1);
+ g_free (t2);
+
+ return res;
+}
+
+static ESExpResult *
+func_translit_endswith (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ SearchContext *ctx = data;
+
+ return entry_compare (ctx, f, argc, argv, translit_endswith_helper);
+}
+
+static gboolean
+translit_beginswith_helper (const gchar *ps1,
+ const gchar *ps2,
+ const gchar *region)
+{
+ ETransliterator *transliterator;
+ gchar *t1, *t2;
+ gboolean res = FALSE;
+
+ transliterator = e_transliterator_new ("Any-Latin");
+ t1 = e_transliterator_transliterate (transliterator, ps1);
+ t2 = e_transliterator_transliterate (transliterator, ps2);
+ e_transliterator_unref (transliterator);
+
+ res = beginswith_helper (t1, t2, region);
+
+ g_free (t1);
+ g_free (t2);
+
+ return res;
+}
+
+static ESExpResult *
+func_translit_beginswith (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ SearchContext *ctx = data;
+
+ return entry_compare (ctx, f, argc, argv, translit_beginswith_helper);
+}
+
static void
book_backend_sexp_finalize (GObject *object)
{
@@ -1124,6 +1259,10 @@ static struct {
{ "regex_raw", func_regex_raw, 0 },
{ "exists", func_exists, 0 },
{ "exists_vcard", func_exists_vcard, 0 },
+ { "translit_contains", func_translit_contains, 0 },
+ { "translit_is", func_translit_is, 0 },
+ { "translit_beginswith", func_translit_beginswith, 0 },
+ { "translit_endswith", func_translit_endswith, 0 },
};
/**
diff --git a/addressbook/libedata-book/e-book-sqlite.c b/addressbook/libedata-book/e-book-sqlite.c
index 044d9b1..4881bcb 100644
--- a/addressbook/libedata-book/e-book-sqlite.c
+++ b/addressbook/libedata-book/e-book-sqlite.c
@@ -289,6 +289,7 @@ ebsql_init_debug (void)
#define EBSQL_SUFFIX_SORT_KEY "localized"
#define EBSQL_SUFFIX_PHONE "phone"
#define EBSQL_SUFFIX_COUNTRY "country"
+#define EBSQL_SUFFIX_TRANSLIT "translit"
/* Track EBookIndexType's in a bit mask */
#define INDEX_FLAG(type) (1 << E_BOOK_INDEX_##type)
@@ -359,6 +360,7 @@ struct _EBookSqlitePrivate {
GCancellable *cancel; /* User passed GCancellable, we abort an operation if cancelled */
ECollator *collator; /* The ECollator to create sort keys for any sortable fields */
+ ETransliterator *transliterator; /* For transliterated queries */
/* SQLite resources */
sqlite3 *db;
@@ -679,6 +681,12 @@ summary_field_list_columns (SummaryField *field,
columns = g_slist_prepend (columns, info);
}
+ /* Transliterated value column */
+ if (field->type != G_TYPE_BOOLEAN && (field->index & INDEX_FLAG (TRANSLIT)) != 0) {
+ info = column_info_new (field, folderid, EBSQL_SUFFIX_TRANSLIT, "TEXT", NULL, "TINDEX");
+ columns = g_slist_prepend (columns, info);
+ }
+
return g_slist_reverse (columns);
}
@@ -2051,6 +2059,8 @@ format_multivalues (EBookSqlite *ebsql)
g_string_append (string, ";suffix");
if ((ebsql->priv->summary_fields[i].index & INDEX_FLAG (PHONE)) != 0)
g_string_append (string, ";phone");
+ if ((ebsql->priv->summary_fields[i].index & INDEX_FLAG (TRANSLIT)) != 0)
+ g_string_append (string, ";translit");
}
}
@@ -2145,6 +2155,10 @@ ebsql_introspect_summary (EBookSqlite *ebsql,
computed = INDEX_FLAG (SORT_KEY);
freeme = g_strndup (col, p - col);
col = freeme;
+ } else if ((p = strstr (col, "_" EBSQL_SUFFIX_TRANSLIT)) != NULL) {
+ computed = INDEX_FLAG (TRANSLIT);
+ freeme = g_strndup (col, p - col);
+ col = freeme;
}
/* First check exception fields */
@@ -2228,6 +2242,8 @@ ebsql_introspect_summary (EBookSqlite *ebsql,
iter->index |= INDEX_FLAG (SUFFIX);
} else if (strcmp (params[j], "phone") == 0) {
iter->index |= INDEX_FLAG (PHONE);
+ } else if (strcmp (params[j], "translit") == 0) {
+ iter->index |= INDEX_FLAG (TRANSLIT);
}
}
}
@@ -3257,6 +3273,9 @@ ebsql_prepare_multi_insert (EBookSqlite *ebsql,
g_string_append (string, ", value_" EBSQL_SUFFIX_COUNTRY);
}
+ if ((field->index & INDEX_FLAG (TRANSLIT)) != 0)
+ g_string_append (string, ", value_" EBSQL_SUFFIX_TRANSLIT);
+
g_string_append (string, ") VALUES (:uid, :value");
if ((field->index & INDEX_FLAG (SUFFIX)) != 0)
@@ -3267,6 +3286,9 @@ ebsql_prepare_multi_insert (EBookSqlite *ebsql,
g_string_append (string, ", :value_" EBSQL_SUFFIX_COUNTRY);
}
+ if ((field->index & INDEX_FLAG (TRANSLIT)) != 0)
+ g_string_append (string, ", :value_" EBSQL_SUFFIX_TRANSLIT);
+
g_string_append_c (string, ')');
stmt = ebsql_prepare_statement (ebsql, string->str, error);
@@ -3284,7 +3306,7 @@ ebsql_run_multi_insert_one (EBookSqlite *ebsql,
GError **error)
{
gchar *normal = e_util_utf8_normalize (value);
- gchar *str;
+ gchar *tmp, *str;
gint ret, param_idx = 1;
/* :uid */
@@ -3319,6 +3341,20 @@ ebsql_run_multi_insert_one (EBookSqlite *ebsql,
}
+ if (ret == SQLITE_OK && (field->index & INDEX_FLAG (TRANSLIT)) != 0) {
+
+ if (value) {
+ tmp = e_transliterator_transliterate (ebsql->priv->transliterator, value);
+ str = e_util_utf8_normalize (tmp);
+ g_free (tmp);
+ } else {
+ str = NULL;
+ }
+
+ /* :value_translit */
+ ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
+ }
+
/* Run the statement */
return ebsql_complete_statement (ebsql, stmt, ret, error);
}
@@ -3414,6 +3450,12 @@ ebsql_prepare_insert (EBookSqlite *ebsql,
g_string_append (string, field->dbname);
g_string_append (string, "_" EBSQL_SUFFIX_COUNTRY);
}
+
+ if ((field->index & INDEX_FLAG (TRANSLIT)) != 0) {
+ g_string_append (string, ", ");
+ g_string_append (string, field->dbname);
+ g_string_append (string, "_" EBSQL_SUFFIX_TRANSLIT);
+ }
}
}
g_string_append (string, ", vcard, bdata)");
@@ -3450,6 +3492,9 @@ ebsql_prepare_insert (EBookSqlite *ebsql,
g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_COUNTRY, field->dbname);
}
+ if ((field->index & INDEX_FLAG (TRANSLIT)) != 0)
+ g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_TRANSLIT,
field->dbname);
+
} else if (field->type != E_TYPE_CONTACT_ATTR_LIST)
g_warn_if_reached ();
}
@@ -3603,6 +3648,20 @@ ebsql_run_insert (EBookSqlite *ebsql,
sqlite3_bind_int (stmt, param_idx++, country_code);
}
+ if (ret == SQLITE_OK &&
+ (field->index & INDEX_FLAG (TRANSLIT)) != 0) {
+
+ if (val) {
+ gchar *tmp = e_transliterator_transliterate
(ebsql->priv->transliterator, val);
+ str = e_util_utf8_normalize (tmp);
+ g_free (tmp);
+ } else {
+ str = NULL;
+ }
+
+ ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
+ }
+
g_free (val);
} else if (field->type == G_TYPE_BOOLEAN) {
gboolean val;
@@ -3778,7 +3837,11 @@ enum {
(query) == E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER ? "eqphone-national" : \
(query) == E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER ? "eqphone-short" : \
(query) == E_BOOK_QUERY_REGEX_NORMAL ? "regex-normal" : \
- (query) == E_BOOK_QUERY_REGEX_NORMAL ? "regex-raw" : "(unknown)")
+ (query) == E_BOOK_QUERY_REGEX_RAW ? "regex-raw" : \
+ (query) == E_BOOK_QUERY_TRANSLIT_IS ? "translit-is" : \
+ (query) == E_BOOK_QUERY_TRANSLIT_CONTAINS ? "translit-contains" : \
+ (query) == E_BOOK_QUERY_TRANSLIT_BEGINS_WITH ? "translit-begins-with" : \
+ (query) == E_BOOK_QUERY_TRANSLIT_ENDS_WITH ? "translit-ends-with" : "unknown")
#define EBSQL_FIELD_ID_STR(field_id) \
((field_id) == E_CONTACT_FIELD_LAST ? "x-evolution-any-field" : \
@@ -4127,7 +4190,6 @@ static const struct {
{ "and", TRUE, BOOK_QUERY_SUB_AND },
{ "or", TRUE, BOOK_QUERY_SUB_OR },
{ "not", TRUE, BOOK_QUERY_SUB_NOT },
-
{ "contains", FALSE, E_BOOK_QUERY_CONTAINS },
{ "is", FALSE, E_BOOK_QUERY_IS },
{ "beginswith", FALSE, E_BOOK_QUERY_BEGINS_WITH },
@@ -4137,6 +4199,10 @@ static const struct {
{ "eqphone_short", FALSE, E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER },
{ "regex_normal", FALSE, E_BOOK_QUERY_REGEX_NORMAL },
{ "regex_raw", FALSE, E_BOOK_QUERY_REGEX_RAW },
+ { "translit_is", FALSE, E_BOOK_QUERY_TRANSLIT_IS },
+ { "translit_contains", FALSE, E_BOOK_QUERY_TRANSLIT_CONTAINS },
+ { "translit_beginswith", FALSE, E_BOOK_QUERY_TRANSLIT_BEGINS_WITH },
+ { "translit_endswith", FALSE, E_BOOK_QUERY_TRANSLIT_ENDS_WITH },
{ "exists", FALSE, BOOK_QUERY_EXISTS },
};
@@ -4603,6 +4669,24 @@ query_preflight_check (PreflightContext *context,
}
}
break;
+
+ case E_BOOK_QUERY_TRANSLIT_IS:
+ case E_BOOK_QUERY_TRANSLIT_CONTAINS:
+ case E_BOOK_QUERY_TRANSLIT_BEGINS_WITH:
+ case E_BOOK_QUERY_TRANSLIT_ENDS_WITH:
+
+ /* These only only searchable in the summary with the E_BOOK_INDEX_TRANSLIT index */
+ if (test->field == NULL ||
+ (test->field->index & INDEX_FLAG (TRANSLIT)) == 0) {
+
+ context->status = MAX (context->status, PREFLIGHT_NOT_SUMMARIZED);
+ EBSQL_NOTE (PREFLIGHT,
+ g_printerr ("PREFLIGHT CHECK: "
+ "Query `%s' needs fallback search, new status: %s\n",
+ EBSQL_QUERY_TYPE_STR (field_test),
+ EBSQL_STATUS_STR (context->status)));
+ }
+ break;
}
if (test->field &&
@@ -4640,6 +4724,7 @@ query_preflight_substitute_full_name (PreflightContext *context,
SummaryField *family_name, *given_name, *nickname;
QueryElement *element;
QueryFieldTest *test;
+ gboolean need_translit = FALSE;
element = g_ptr_array_index (context->constraints, i);
@@ -4650,10 +4735,30 @@ query_preflight_substitute_full_name (PreflightContext *context,
if (test->field_id != E_CONTACT_FULL_NAME)
continue;
+ /* If it's transliterated, we won't do this */
+ if (element->query >= E_BOOK_QUERY_TRANSLIT_IS &&
+ element->query <= E_BOOK_QUERY_TRANSLIT_ENDS_WITH)
+ need_translit = TRUE;
+
family_name = summary_field_get (ebsql, E_CONTACT_FAMILY_NAME);
given_name = summary_field_get (ebsql, E_CONTACT_GIVEN_NAME);
nickname = summary_field_get (ebsql, E_CONTACT_NICKNAME);
+ /* Only OR them in if they are also indexed for transliteration */
+ if (need_translit) {
+ if (family_name &&
+ (family_name->index & INDEX_FLAG (TRANSLIT)) == 0)
+ family_name = NULL;
+
+ if (given_name &&
+ (given_name->index & INDEX_FLAG (TRANSLIT)) == 0)
+ given_name = NULL;
+
+ if (nickname &&
+ (nickname->index & INDEX_FLAG (TRANSLIT)) == 0)
+ nickname = NULL;
+ }
+
/* If any of these are in the summary, then we'll construct
* a grouped OR statment for this E_CONTACT_FULL_NAME test */
if (family_name || given_name || nickname) {
@@ -4748,7 +4853,8 @@ typedef void (* GenerateFieldTest) (EBookSqlite *ebsql,
* with %Q or %q
*/
static gchar *
-ebsql_normalize_for_like (QueryFieldTest *test,
+ebsql_normalize_for_like (EBookSqlite *ebsql,
+ QueryFieldTest *test,
gboolean reverse_string,
gboolean *escape_needed)
{
@@ -4765,6 +4871,13 @@ ebsql_normalize_for_like (QueryFieldTest *test,
if (test->field_id == E_CONTACT_UID ||
test->field_id == E_CONTACT_REV) {
normal = test->value;
+ } else if (test->query >= E_BOOK_QUERY_TRANSLIT_CONTAINS &&
+ test->query <= E_BOOK_QUERY_TRANSLIT_ENDS_WITH) {
+ gchar *tmp = e_transliterator_transliterate (ebsql->priv->transliterator, test->value);
+ freeme = e_util_utf8_normalize (tmp);
+ g_free (tmp);
+
+ normal = freeme;
} else {
freeme = e_util_utf8_normalize (test->value);
normal = freeme;
@@ -4834,7 +4947,7 @@ field_test_query_contains (EBookSqlite *ebsql,
gboolean need_escape;
gchar *escaped;
- escaped = ebsql_normalize_for_like (test, FALSE, &need_escape);
+ escaped = ebsql_normalize_for_like (ebsql, test, FALSE, &need_escape);
g_string_append_c (string, '(');
@@ -4862,7 +4975,7 @@ field_test_query_begins_with (EBookSqlite *ebsql,
gboolean need_escape;
gchar *escaped;
- escaped = ebsql_normalize_for_like (test, FALSE, &need_escape);
+ escaped = ebsql_normalize_for_like (ebsql, test, FALSE, &need_escape);
g_string_append_c (string, '(');
ebsql_string_append_column (string, field, NULL);
@@ -4891,7 +5004,7 @@ field_test_query_ends_with (EBookSqlite *ebsql,
if ((field->index & INDEX_FLAG (SUFFIX)) != 0) {
- escaped = ebsql_normalize_for_like (test, TRUE, &need_escape);
+ escaped = ebsql_normalize_for_like (ebsql, test, TRUE, &need_escape);
g_string_append_c (string, '(');
ebsql_string_append_column (string, field, EBSQL_SUFFIX_REVERSE);
@@ -4904,7 +5017,7 @@ field_test_query_ends_with (EBookSqlite *ebsql,
} else {
- escaped = ebsql_normalize_for_like (test, FALSE, &need_escape);
+ escaped = ebsql_normalize_for_like (ebsql, test, FALSE, &need_escape);
g_string_append_c (string, '(');
ebsql_string_append_column (string, field, NULL);
@@ -5035,9 +5148,109 @@ field_test_query_regex_normal (EBookSqlite *ebsql,
}
static void
+field_test_query_translit_is (EBookSqlite *ebsql,
+ GString *string,
+ QueryFieldTest *test)
+{
+ SummaryField *field = test->field;
+ gchar *normal, *translit;
+
+ translit = e_transliterator_transliterate (ebsql->priv->transliterator, test->value);
+ normal = e_util_utf8_normalize (translit);
+
+ ebsql_string_append_column (string, field, EBSQL_SUFFIX_TRANSLIT);
+ ebsql_string_append_printf (string, " = %Q", normal);
+
+ g_free (translit);
+ g_free (normal);
+}
+
+static void
+field_test_query_translit_contains (EBookSqlite *ebsql,
+ GString *string,
+ QueryFieldTest *test)
+{
+ SummaryField *field = test->field;
+ gboolean need_escape;
+ gchar *escaped;
+
+ escaped = ebsql_normalize_for_like (ebsql, test, FALSE, &need_escape);
+
+ g_string_append_c (string, '(');
+
+ ebsql_string_append_column (string, field, EBSQL_SUFFIX_TRANSLIT);
+ g_string_append (string, " IS NOT NULL AND ");
+ ebsql_string_append_column (string, field, EBSQL_SUFFIX_TRANSLIT);
+ g_string_append (string, " LIKE '%");
+ g_string_append (string, escaped);
+ g_string_append (string, "%'");
+
+ if (need_escape)
+ g_string_append (string, EBSQL_ESCAPE_SEQUENCE);
+
+ g_string_append_c (string, ')');
+
+ g_free (escaped);
+}
+
+static void
+field_test_query_translit_begins_with (EBookSqlite *ebsql,
+ GString *string,
+ QueryFieldTest *test)
+{
+ SummaryField *field = test->field;
+ gboolean need_escape;
+ gchar *escaped;
+
+ escaped = ebsql_normalize_for_like (ebsql, test, FALSE, &need_escape);
+
+ g_string_append_c (string, '(');
+ ebsql_string_append_column (string, field, EBSQL_SUFFIX_TRANSLIT);
+ g_string_append (string, " IS NOT NULL AND ");
+
+ ebsql_string_append_column (string, field, EBSQL_SUFFIX_TRANSLIT);
+ g_string_append (string, " LIKE \'");
+ g_string_append (string, escaped);
+ g_string_append (string, "%\'");
+
+ if (need_escape)
+ g_string_append (string, EBSQL_ESCAPE_SEQUENCE);
+ g_string_append_c (string, ')');
+
+ g_free (escaped);
+}
+
+static void
+field_test_query_translit_ends_with (EBookSqlite *ebsql,
+ GString *string,
+ QueryFieldTest *test)
+{
+ SummaryField *field = test->field;
+ gboolean need_escape;
+ gchar *escaped;
+
+ escaped = ebsql_normalize_for_like (ebsql, test, FALSE, &need_escape);
+ g_string_append_c (string, '(');
+
+ ebsql_string_append_column (string, field, EBSQL_SUFFIX_TRANSLIT);
+ g_string_append (string, " IS NOT NULL AND ");
+
+ ebsql_string_append_column (string, field, EBSQL_SUFFIX_TRANSLIT);
+ g_string_append (string, " LIKE \'%");
+ g_string_append (string, escaped);
+ g_string_append (string, "\'");
+
+ if (need_escape)
+ g_string_append (string, EBSQL_ESCAPE_SEQUENCE);
+
+ g_string_append_c (string, ')');
+ g_free (escaped);
+}
+
+static void
field_test_query_exists (EBookSqlite *ebsql,
- GString *string,
- QueryFieldTest *test)
+ GString *string,
+ QueryFieldTest *test)
{
SummaryField *field = test->field;
@@ -5059,6 +5272,10 @@ static const GenerateFieldTest field_test_func_table[] = {
field_test_query_eqphone_short, /* E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER */
field_test_query_regex_normal, /* E_BOOK_QUERY_REGEX_NORMAL */
NULL /* Requires fallback */, /* E_BOOK_QUERY_REGEX_RAW */
+ field_test_query_translit_is, /* E_BOOK_QUERY_TRANSLIT_IS */
+ field_test_query_translit_contains,/* E_BOOK_QUERY_TRANSLIT_CONTAINS */
+ field_test_query_translit_begins_with,/* E_BOOK_QUERY_TRANSLIT_BEGINS_WITH */
+ field_test_query_translit_ends_with,/* E_BOOK_QUERY_TRANSLIT_ENDS_WITH */
field_test_query_exists, /* BOOK_QUERY_EXISTS */
};
@@ -5924,6 +6141,9 @@ e_book_sqlite_finalize (GObject *object)
if (priv->collator)
e_collator_unref (priv->collator);
+ if (ebsql->priv->transliterator)
+ e_transliterator_unref (ebsql->priv->transliterator);
+
g_mutex_clear (&priv->lock);
g_mutex_clear (&priv->updates_lock);
@@ -5968,6 +6188,8 @@ e_book_sqlite_init (EBookSqlite *ebsql)
g_mutex_init (&ebsql->priv->lock);
g_mutex_init (&ebsql->priv->updates_lock);
+
+ ebsql->priv->transliterator = e_transliterator_new ("Any-Latin");
}
/**********************************************************
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index 0391f83..9a607da 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -92,6 +92,7 @@ libedataserver_1_2_la_SOURCES = \
e-debug-log.c \
e-time-utils.c \
e-transliterator-private.h \
+ e-transliterator.c \
e-uid.c \
e-url.c \
e-data-server-util.c \
@@ -126,6 +127,7 @@ libedataserverinclude_HEADERS = \
e-categories.h \
e-client.h \
e-collator.h \
+ e-transliterator.h \
e-credentials.h \
e-flag.h \
e-gdbus-templates.h \
diff --git a/libedataserver/e-collator.c b/libedataserver/e-collator.c
index fee720b..116cfb1 100644
--- a/libedataserver/e-collator.c
+++ b/libedataserver/e-collator.c
@@ -70,7 +70,7 @@ struct _ECollator
gint inflow;
gint overflow;
- ETransliterator *transliterator;
+ ECxxTransliterator *transliterator;
};
/*****************************************************
diff --git a/libedataserver/e-transliterator-private.cpp b/libedataserver/e-transliterator-private.cpp
index f777a0f..153538d 100644
--- a/libedataserver/e-transliterator-private.cpp
+++ b/libedataserver/e-transliterator-private.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2013 Intel Corporation
*
- * This library is free software; you can redistribute it and/or modify it
+g * This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
@@ -36,43 +36,43 @@
using icu::Transliterator;
-struct _ETransliterator {
+struct _ECxxTransliterator {
Transliterator *priv;
};
/* Create an Transliterator for the source and target
* language stripts
*/
-ETransliterator *
+ECxxTransliterator *
_e_transliterator_cxx_new (const gchar *transliterator_id)
{
UErrorCode status = U_ZERO_ERROR;
- ETransliterator *transliterator;
+ ECxxTransliterator *transliterator;
g_return_val_if_fail (transliterator_id != NULL, NULL);
- transliterator = g_slice_new (ETransliterator);
+ transliterator = g_slice_new (ECxxTransliterator);
transliterator->priv = Transliterator::createInstance (transliterator_id, UTRANS_FORWARD, status);
return transliterator;
}
-/* Frees an ETransliterator and it's associated resources
+/* Frees an ECxxTransliterator and it's associated resources
*/
void
-_e_transliterator_cxx_free (ETransliterator *transliterator)
+_e_transliterator_cxx_free (ECxxTransliterator *transliterator)
{
if (transliterator) {
delete transliterator->priv;
- g_slice_free (ETransliterator, transliterator);
+ g_slice_free (ECxxTransliterator, transliterator);
}
}
/* Transliterates 'str' and returns the new allocated result
*/
gchar *
-_e_transliterator_cxx_transliterate (ETransliterator *transliterator,
- const gchar *str)
+_e_transliterator_cxx_transliterate (ECxxTransliterator *transliterator,
+ const gchar *str)
{
UnicodeString transform;
std::string sourceUTF8;
diff --git a/libedataserver/e-transliterator-private.h b/libedataserver/e-transliterator-private.h
index e977811..30a2012 100644
--- a/libedataserver/e-transliterator-private.h
+++ b/libedataserver/e-transliterator-private.h
@@ -35,19 +35,19 @@ G_BEGIN_DECLS
#endif
/**
- * ETransliterator:
+ * ECxxTransliterator:
*
* A private opaque type describing an alphabetic index
*
* Since: 3.12
**/
-typedef struct _ETransliterator ETransliterator;
+typedef struct _ECxxTransliterator ECxxTransliterator;
/* defined in e-transliterator-private.cpp, and used by by e-collator.c */
-E_TRANSLITERATOR_LOCAL ETransliterator *_e_transliterator_cxx_new (const gchar
*transliterator_id);
-E_TRANSLITERATOR_LOCAL void _e_transliterator_cxx_free (ETransliterator
*transliterator);
-E_TRANSLITERATOR_LOCAL gchar *_e_transliterator_cxx_transliterate (ETransliterator
*transliterator,
- const gchar *str);
+E_TRANSLITERATOR_LOCAL ECxxTransliterator *_e_transliterator_cxx_new (const gchar
*transliterator_id);
+E_TRANSLITERATOR_LOCAL void _e_transliterator_cxx_free (ECxxTransliterator
*transliterator);
+E_TRANSLITERATOR_LOCAL gchar *_e_transliterator_cxx_transliterate (ECxxTransliterator
*transliterator,
+ const gchar *str);
G_END_DECLS
diff --git a/libedataserver/e-transliterator.c b/libedataserver/e-transliterator.c
new file mode 100644
index 0000000..26665a3
--- /dev/null
+++ b/libedataserver/e-transliterator.c
@@ -0,0 +1,155 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 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: Tristan Van Berkom <tristanvb openismus com>
+ */
+
+/**
+ * SECTION: e-transliterator
+ * @include: libedataserver/libedataserver.h
+ * @short_description: Collation services for locale sensitive sorting
+ *
+ * The #ETransliterator is a wrapper object around ICU collation services and
+ * provides features to sort words in locale specific ways. The transliterator
+ * also provides some API for determining features of the active alphabet
+ * in the user's locale, and which words should be sorted under which
+ * letter in the user's alphabet.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "e-transliterator.h"
+#include "e-transliterator-private.h"
+
+G_DEFINE_BOXED_TYPE (ETransliterator,
+ e_transliterator,
+ e_transliterator_ref,
+ e_transliterator_unref)
+
+struct _ETransliterator
+{
+ ECxxTransliterator *transliterator;
+
+ volatile gint ref_count;
+};
+
+/*****************************************************
+ * API *
+ *****************************************************/
+
+/**
+ * e_transliterator_new:
+ * @id: The id of the transliterator to create.
+ *
+ * Creates a new #ETransliterator for the given @id,
+ * IDs are defined by ICU libraries and can be of the
+ * form "Any-Latin", "Han-Latin" etc.
+ *
+ * Transliterator services exist for all script types
+ * to convert into Latin, however the same is not true
+ * for other scripts (i.e. you cannot transliterate
+ * Greek into Chinese or Japanese).
+ *
+ * For more details on ICU transliteration services,
+ * visit this link:
+ * http://userguide.icu-project.org/transforms/general
+ *
+ * Returns: (transfer full): A newly created #ETransliterator.
+ *
+ * Since: 3.12
+ */
+ETransliterator *
+e_transliterator_new (const gchar *id)
+{
+ ETransliterator *transliterator;
+
+ transliterator = g_slice_new0 (ETransliterator);
+ transliterator->transliterator = _e_transliterator_cxx_new (id);
+ transliterator->ref_count = 1;
+
+ return transliterator;
+}
+
+/**
+ * e_transliterator_ref:
+ * @transliterator: An #ETransliterator
+ *
+ * Increases the reference count of @transliterator.
+ *
+ * Returns: (transfer full): @transliterator
+ *
+ * Since: 3.12
+ */
+ETransliterator *
+e_transliterator_ref (ETransliterator *transliterator)
+{
+ g_return_val_if_fail (transliterator != NULL, NULL);
+
+ g_atomic_int_inc (&transliterator->ref_count);
+
+ return transliterator;
+}
+
+/**
+ * e_transliterator_unref:
+ * @transliterator: An #ETransliterator
+ *
+ * Decreases the reference count of @transliterator.
+ * If the reference count reaches 0 then the transliterator is freed
+ *
+ * Since: 3.12
+ */
+void
+e_transliterator_unref (ETransliterator *transliterator)
+{
+ g_return_if_fail (transliterator != NULL);
+
+ if (g_atomic_int_dec_and_test (&transliterator->ref_count)) {
+
+ if (transliterator->transliterator)
+ _e_transliterator_cxx_free (transliterator->transliterator);
+
+ g_slice_free (ETransliterator, transliterator);
+ }
+}
+
+/**
+ * e_transliterator_transliterate:
+ * @transliterator: An #ETransliterator
+ * @str: The string to transliterate
+ *
+ * Transliterates @str according to the transliteration service
+ * chosen and passed to e_transliterator_new().
+ *
+ * Returns: (transfer full): The newly created transliteration of @str
+ *
+ * Since: 3.12
+ */
+gchar *
+e_transliterator_transliterate (ETransliterator *transliterator,
+ const gchar *str)
+{
+ g_return_val_if_fail (transliterator != NULL, NULL);
+
+ return _e_transliterator_cxx_transliterate (transliterator->transliterator, str);
+}
diff --git a/libedataserver/e-transliterator.h b/libedataserver/e-transliterator.h
new file mode 100644
index 0000000..9ed33fe
--- /dev/null
+++ b/libedataserver/e-transliterator.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 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: Tristan Van Berkom <tristanvb openismus com>
+ */
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#include <glib.h>
+#include <libedataserver/e-source-enumtypes.h>
+#include <libedataserver/e-data-server-util.h>
+
+#ifndef E_TRANSLITERATOR_H
+#define E_TRANSLITERATOR_H
+
+#define E_TYPE_TRANSLITERATOR (e_transliterator_get_type ())
+
+G_BEGIN_DECLS
+
+/**
+ * ETransliterator:
+ *
+ * An opaque object used for string transliterations.
+ *
+ * Since: 3.12
+ */
+typedef struct _ETransliterator ETransliterator;
+
+GType e_transliterator_get_type (void);
+ETransliterator *e_transliterator_new (const gchar *id);
+ETransliterator *e_transliterator_ref (ETransliterator *transliterator);
+void e_transliterator_unref (ETransliterator *transliterator);
+gchar *e_transliterator_transliterate (ETransliterator *transliterator,
+ const gchar *str);
+
+
+G_END_DECLS
+
+#endif /* E_TRANSLITERATOR_H */
diff --git a/libedataserver/libedataserver.h b/libedataserver/libedataserver.h
index f33f7c6..4298313 100644
--- a/libedataserver/libedataserver.h
+++ b/libedataserver/libedataserver.h
@@ -24,6 +24,7 @@
#include <libedataserver/e-categories.h>
#include <libedataserver/e-client.h>
#include <libedataserver/e-collator.h>
+#include <libedataserver/e-transliterator.h>
#include <libedataserver/e-credentials.h>
#include <libedataserver/e-data-server-util.h>
#include <libedataserver/e-debug-log.h>
diff --git a/tests/libebook/client/test-book-client-cursor-operations.c
b/tests/libebook/client/test-book-client-cursor-operations.c
index dabd043..bdd5713 100644
--- a/tests/libebook/client/test-book-client-cursor-operations.c
+++ b/tests/libebook/client/test-book-client-cursor-operations.c
@@ -2444,6 +2444,47 @@ main (gint argc,
cursor_closure_position (closure, 13, 6, TRUE);
cursor_closure_add (closure, "/EBookClientCursor/SearchExpression/EffectsPosition%s",
base_params[i].base_path);
+ /* Supports transliterated queries */
+ closure = cursor_closure_new (&base_params[i], "POSIX");
+ cursor_closure_position (closure, 20, 0, TRUE);
+
+ cursor_closure_step (closure,
+ E_BOOK_CURSOR_STEP_MOVE | E_BOOK_CURSOR_STEP_FETCH,
+ E_BOOK_CURSOR_ORIGIN_CURRENT,
+ 10, /* Count */
+ 10, /* Expected results */
+ 11, 2, 6, 3, 8, 1, 5, 4, 7, 15);
+ cursor_closure_position (closure, 20, 10, TRUE);
+ cursor_closure_set_sexp (closure,
+ e_book_query_field_test (
+ E_CONTACT_EMAIL,
+ E_BOOK_QUERY_TRANSLIT_BEGINS_WITH,
+ "几"));
+
+ /* In POSIX Locale, the 10th contact out of 20 becomes the 2nd out of 3 contacts
+ * which start with the transliterated '几' sound (which transliterates to 'ji').
+ *
+ * Our three matching contacts are:
+ * sorted-9: jim morrison com
+ * sorted-4: 警察 brown org (transliterates to "jing cha brown org")
+ * sorted-5: 救命 brown com (transliterates to "jiu ming brown com")
+ *
+ * All of these emails begin with 'ji'
+ */
+ cursor_closure_position (closure, 3, 2, TRUE);
+
+ cursor_closure_set_sexp (closure,
+ e_book_query_field_test (
+ E_CONTACT_EMAIL,
+ E_BOOK_QUERY_TRANSLIT_BEGINS_WITH,
+ "ji"));
+
+ /* The '几' sound transliterates to 'ji', so we should have the same results & position.
+ */
+ cursor_closure_position (closure, 3, 2, TRUE);
+ cursor_closure_add (closure, "/EBookClientCursor/SearchExpression/Translit/Email/Prefix/ji%s",
+ base_params[i].base_path);
+
/****************************************************
* Address Book Modifications *
****************************************************/
diff --git a/tests/libebook/client/test-book-client-custom-summary.c
b/tests/libebook/client/test-book-client-custom-summary.c
index ec3f59c..4940369 100644
--- a/tests/libebook/client/test-book-client-custom-summary.c
+++ b/tests/libebook/client/test-book-client-custom-summary.c
@@ -46,12 +46,15 @@ setup_custom_book (ESource *scratch,
e_source_backend_summary_setup_set_indexed_fields (
setup,
E_CONTACT_EMAIL, E_BOOK_INDEX_PREFIX,
+ E_CONTACT_EMAIL, E_BOOK_INDEX_TRANSLIT,
E_CONTACT_TEL, E_BOOK_INDEX_SUFFIX,
E_CONTACT_TEL, E_BOOK_INDEX_PHONE,
E_CONTACT_FULL_NAME, E_BOOK_INDEX_PREFIX,
E_CONTACT_FULL_NAME, E_BOOK_INDEX_SUFFIX,
+ E_CONTACT_FULL_NAME, E_BOOK_INDEX_TRANSLIT,
E_CONTACT_FAMILY_NAME, E_BOOK_INDEX_PREFIX,
E_CONTACT_FAMILY_NAME, E_BOOK_INDEX_SUFFIX,
+ E_CONTACT_FAMILY_NAME, E_BOOK_INDEX_TRANSLIT,
0);
}
@@ -79,7 +82,7 @@ static GOptionEntry entries[] = {
# define CHECK_UNSUPPORTED_ERROR(data) (((ClientTestData *)(data))->phone_number_query != FALSE)
#endif
-#define N_CONTACTS 15
+#define N_CONTACTS 16
typedef struct {
ETestServerClosure parent;
@@ -1186,6 +1189,104 @@ main (gint argc,
suites[i].direct,
suites[i].custom,
FALSE);
+
+ /*********************************************
+ * TRANSLITERATION QUERIES FOLLOW *
+ *********************************************/
+
+ /* We have one contact that is "Jim Morrison" and
+ * another which is "警察 懂吗" (which transliterates
+ * to "Jing cha Dong ma", which means "Police do you understand").
+ *
+ * This test tries to fetch 2 contacts beginning with '几' which
+ * transliterates to 'jǐ'.
+ *
+ * So here we assert that when searching for contacts which begin
+ * with a transliterated "几" match both "Jim Morrison" and
+ * "警察 懂吗"
+ */
+ add_client_test (
+ suites[i].prefix,
+ "/Transliterated/Prefix/FullName/几",
+ suites[i].func,
+ e_book_query_field_test (
+ E_CONTACT_FULL_NAME,
+ E_BOOK_QUERY_TRANSLIT_BEGINS_WITH,
+ "几"),
+ 2,
+ suites[i].direct,
+ suites[i].custom,
+ FALSE);
+
+ /* Same results as above, only the query input is provided in Latin script */
+ add_client_test (
+ suites[i].prefix,
+ "/Transliterated/Prefix/FullName/Ji",
+ suites[i].func,
+ e_book_query_field_test (
+ E_CONTACT_FULL_NAME,
+ E_BOOK_QUERY_TRANSLIT_BEGINS_WITH,
+ "Ji"),
+ 2,
+ suites[i].direct,
+ suites[i].custom,
+ FALSE);
+
+ /* Check if anything contains "Ma", this should
+ * only find one match for "警察 懂吗" */
+ add_client_test (
+ suites[i].prefix,
+ "/Transliterated/Contains/FullName/Ma",
+ suites[i].func,
+ e_book_query_field_test (
+ E_CONTACT_FULL_NAME,
+ E_BOOK_QUERY_TRANSLIT_CONTAINS,
+ "Ma"),
+ 1,
+ suites[i].direct,
+ suites[i].custom,
+ FALSE);
+
+
+ add_client_test (
+ suites[i].prefix,
+ "/Transliterated/Contains/FullName/Ma",
+ suites[i].func,
+ e_book_query_field_test (
+ E_CONTACT_FULL_NAME,
+ E_BOOK_QUERY_TRANSLIT_CONTAINS,
+ "Ma"),
+ 1,
+ suites[i].direct,
+ suites[i].custom,
+ FALSE);
+
+ add_client_test (
+ suites[i].prefix,
+ "/Transliterated/Suffix/FullName/Cha",
+ suites[i].func,
+ e_book_query_field_test (
+ E_CONTACT_FULL_NAME,
+ E_BOOK_QUERY_TRANSLIT_ENDS_WITH,
+ "Cha"),
+ 1,
+ suites[i].direct,
+ suites[i].custom,
+ FALSE);
+
+ add_client_test (
+ suites[i].prefix,
+ "/Transliterated/Prefix/Email/几",
+ suites[i].func,
+ e_book_query_field_test (
+ E_CONTACT_EMAIL,
+ E_BOOK_QUERY_TRANSLIT_BEGINS_WITH,
+ "几"),
+ 2,
+ suites[i].direct,
+ suites[i].custom,
+ FALSE);
+
}
ret = e_test_server_utils_run ();
diff --git a/tests/libebook/data/vcards/custom-13.vcf b/tests/libebook/data/vcards/custom-13.vcf
index 878def9..f13426d 100644
--- a/tests/libebook/data/vcards/custom-13.vcf
+++ b/tests/libebook/data/vcards/custom-13.vcf
@@ -1,6 +1,6 @@
BEGIN:VCARD
UID:custom-13
-N:Murphey;Eddie
-TEL;HOME:408 765-5050
-EMAIL;TYPE=home,work:eddie murphey usa
+N:警察;懂吗
+TEL;HOME:1.800 555 PONY
+EMAIL;TYPE=home,work:警察 china net
END:VCARD
diff --git a/tests/libebook/data/vcards/custom-14.vcf b/tests/libebook/data/vcards/custom-14.vcf
index d064757..5b549e1 100644
--- a/tests/libebook/data/vcards/custom-14.vcf
+++ b/tests/libebook/data/vcards/custom-14.vcf
@@ -1,6 +1,6 @@
BEGIN:VCARD
UID:custom-14
-N:Turner;Tina
-TEL;HOME:+1 408 765-5050
-EMAIL;TYPE=home,work:tina turner net
+N:Murphey;Eddie
+TEL;HOME:408 765-5050
+EMAIL;TYPE=home,work:eddie murphey usa
END:VCARD
diff --git a/tests/libebook/data/vcards/custom-15.vcf b/tests/libebook/data/vcards/custom-15.vcf
index 4e72cf3..3e59b7e 100644
--- a/tests/libebook/data/vcards/custom-15.vcf
+++ b/tests/libebook/data/vcards/custom-15.vcf
@@ -1,6 +1,6 @@
BEGIN:VCARD
UID:custom-15
-N:Crawley;Alister
-TEL;HOME:+49 408 765-5050
-EMAIL;TYPE=home,work:777 starry sect
+N:Turner;Tina
+TEL;HOME:+1 408 765-5050
+EMAIL;TYPE=home,work:tina turner net
END:VCARD
diff --git a/tests/libebook/data/vcards/custom-16.vcf b/tests/libebook/data/vcards/custom-16.vcf
new file mode 100644
index 0000000..75efcb3
--- /dev/null
+++ b/tests/libebook/data/vcards/custom-16.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+UID:custom-16
+N:Crawley;Alister
+TEL;HOME:+49 408 765-5050
+EMAIL;TYPE=home,work:777 starry sect
+END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-4.vcf b/tests/libebook/data/vcards/sorted-4.vcf
index 61a0c56..6e1b581 100644
--- a/tests/libebook/data/vcards/sorted-4.vcf
+++ b/tests/libebook/data/vcards/sorted-4.vcf
@@ -1,6 +1,7 @@
BEGIN:VCARD
UID:sorted-4
N:bat;First Name
+EMAIL:警察 brown org
TEL;TYPE=work,pref:12 245999
EMAIL:big bobby brown org
END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-5.vcf b/tests/libebook/data/vcards/sorted-5.vcf
index 8a8993f..a3cec4f 100644
--- a/tests/libebook/data/vcards/sorted-5.vcf
+++ b/tests/libebook/data/vcards/sorted-5.vcf
@@ -2,5 +2,5 @@ BEGIN:VCARD
UID:sorted-5
N:bäd;First Name
TEL;HOME:049-2459-4393
-EMAIL;TYPE=home,work:james brown com
+EMAIL;TYPE=home,work:救命 brown com
END:VCARD
diff --git a/tests/libebook/data/vcards/sorted-9.vcf b/tests/libebook/data/vcards/sorted-9.vcf
index 9a825d6..a5ed36b 100644
--- a/tests/libebook/data/vcards/sorted-9.vcf
+++ b/tests/libebook/data/vcards/sorted-9.vcf
@@ -2,5 +2,5 @@ BEGIN:VCARD
UID:sorted-9
N:côté;First Name
TEL;HOME:514-845-8436
-EMAIL;TYPE=home,work:pink pony com
+EMAIL;TYPE=home,work:jim morrison com
END:VCARD
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]