1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2013 Intel Corporation
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Tristan Van Berkom <tristanvb@openismus.com>
18  */
19 
20 #include "evolution-data-server-config.h"
21 
22 #include "e-alphabet-index-private.h"
23 
24 /* C++ standard library */
25 #include <string>
26 #include <memory>
27 
28 /* system headers */
29 #ifdef HAVE_CODESET
30 #include <langinfo.h>
31 #endif
32 #include <locale.h>
33 
34 /* ICU headers */
35 #include <unicode/alphaindex.h>
36 
37 using icu::AlphabeticIndex;
38 using icu::Locale;
39 using icu::UnicodeString;
40 
41 struct _EAlphabetIndex {
42 	AlphabeticIndex *priv;
43 };
44 
45 /* Create an AlphabetIndex for a given language code (normally
46  * language codes are 2 letter codes, eg. 'en' = English 'es' = Spanish.
47  */
48 EAlphabetIndex *
_e_alphabet_index_cxx_new_for_language(const gchar * language)49 _e_alphabet_index_cxx_new_for_language (const gchar *language)
50 {
51 	UErrorCode status = U_ZERO_ERROR;
52 	EAlphabetIndex *alphabet_index;
53 
54 	g_return_val_if_fail (language != NULL, NULL);
55 
56 	alphabet_index = g_slice_new (EAlphabetIndex);
57 	alphabet_index->priv = new AlphabeticIndex (Locale (language), status);
58 
59 	return alphabet_index;
60 }
61 
62 /* Frees an EAlphabetIndex and it's associated resources
63  */
64 void
_e_alphabet_index_cxx_free(EAlphabetIndex * alphabet_index)65 _e_alphabet_index_cxx_free (EAlphabetIndex *alphabet_index)
66 {
67 	if (alphabet_index) {
68 		delete alphabet_index->priv;
69 		g_slice_free (EAlphabetIndex, alphabet_index);
70 	}
71 }
72 
73 /* Fetch the given index where 'word' should sort
74  */
75 gint
_e_alphabet_index_cxx_get_index(EAlphabetIndex * alphabet_index,const gchar * word)76 _e_alphabet_index_cxx_get_index (EAlphabetIndex  *alphabet_index,
77 				 const gchar     *word)
78 {
79 	UErrorCode status = U_ZERO_ERROR;
80 	icu::UnicodeString string;
81 	gint index;
82 
83 	g_return_val_if_fail (alphabet_index != NULL, -1);
84 	g_return_val_if_fail (word != NULL, -1);
85 
86 	string = icu::UnicodeString::fromUTF8 (word);
87 	index = alphabet_index->priv->getBucketIndex (string, status);
88 
89 	return index;
90 }
91 
92 /* Fetch the list of labels in the alphabetic index.
93  *
94  * Returns an array of UTF-8 labels for each alphabetic
95  * index position 'n_labels' long, the returned array
96  * of strings can be freed with g_strfreev()
97  *
98  * The underflow, overflow and inflow parameters will be
99  * set to the appropriate indexes (reffers to indexes in the
100  * returned labels).
101  */
102 gchar **
_e_alphabet_index_cxx_get_labels(EAlphabetIndex * alphabet_index,gint * n_labels,gint * underflow,gint * inflow,gint * overflow)103 _e_alphabet_index_cxx_get_labels (EAlphabetIndex  *alphabet_index,
104 				  gint            *n_labels,
105 				  gint            *underflow,
106 				  gint            *inflow,
107 				  gint            *overflow)
108 {
109 	UErrorCode status = U_ZERO_ERROR;
110 	gchar **labels = NULL;
111 	gint count, i;
112 
113 	g_return_val_if_fail (alphabet_index != NULL, NULL);
114 	g_return_val_if_fail (n_labels != NULL, NULL);
115 	g_return_val_if_fail (underflow != NULL, NULL);
116 	g_return_val_if_fail (inflow != NULL, NULL);
117 	g_return_val_if_fail (overflow != NULL, NULL);
118 
119 	count = alphabet_index->priv->getBucketCount (status);
120 
121 	labels = g_new0 (gchar *, count + 1);
122 
123 	/* In case they are missing, they should be set to -1 */
124 	*underflow = *inflow = *overflow = -1;
125 
126 	/* Iterate over the AlphabeticIndex and collect UTF-8 versions
127 	 * of the bucket labels
128 	 */
129 	alphabet_index->priv->resetBucketIterator (status);
130 
131 	for (i = 0; alphabet_index->priv->nextBucket (status); i++) {
132 		UAlphabeticIndexLabelType label_type;
133 		icu::UnicodeString ustring;
134 		std::string string;
135 
136 		label_type = alphabet_index->priv->getBucketLabelType ();
137 
138 		switch (label_type) {
139 		case U_ALPHAINDEX_UNDERFLOW: *underflow = i; break;
140 		case U_ALPHAINDEX_INFLOW:    *inflow    = i; break;
141 		case U_ALPHAINDEX_OVERFLOW:  *overflow  = i; break;
142 		case U_ALPHAINDEX_NORMAL:  /* do nothing */  break;
143 		}
144 
145 		/* This is annoyingly heavy but not a function called
146 		 * very often, this could be improved by calling icu::UnicodeString::toUTF8()
147 		 * and implementing ICU's ByteSync class using glib's memory allocator.
148 		 */
149 		ustring   = alphabet_index->priv->getBucketLabel ();
150 		string    = ustring.toUTF8String (string);
151 		labels[i] = g_strdup (string.c_str());
152 	}
153 
154 	*n_labels = count;
155 
156 	return labels;
157 }
158