Line data Source code
1 : /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 : /*
3 : * lt-lang-db.c
4 : * Copyright (C) 2011-2012 Akira TAGOH
5 : *
6 : * Authors:
7 : * Akira TAGOH <akira@tagoh.org>
8 : *
9 : * You may distribute under the terms of either the GNU
10 : * Lesser General Public License or the Mozilla Public
11 : * License, as specified in the README file.
12 : */
13 : #ifdef HAVE_CONFIG_H
14 : #include "config.h"
15 : #endif
16 :
17 : #include <string.h>
18 : #include <libxml/xpath.h>
19 : #include "lt-error.h"
20 : #include "lt-mem.h"
21 : #include "lt-messages.h"
22 : #include "lt-trie.h"
23 : #include "lt-utils.h"
24 : #include "lt-xml.h"
25 : #include "lt-lang-private.h"
26 : #include "lt-lang-db.h"
27 :
28 :
29 : /**
30 : * SECTION: lt-lang-db
31 : * @Short_Description: An interface to access Language Database
32 : * @Title: Database - Language
33 : *
34 : * This class provides an interface to access Language database. which has been
35 : * registered as ISO 639 code.
36 : */
37 : struct _lt_lang_db_t {
38 : lt_mem_t parent;
39 : lt_xml_t *xml;
40 : lt_trie_t *lang_entries;
41 : };
42 :
43 : /*< private >*/
44 : static lt_bool_t
45 4 : lt_lang_db_parse(lt_lang_db_t *langdb,
46 : lt_error_t **error)
47 : {
48 4 : lt_bool_t retval = TRUE;
49 4 : xmlDocPtr doc = NULL;
50 4 : xmlXPathContextPtr xctxt = NULL;
51 4 : xmlXPathObjectPtr xobj = NULL;
52 4 : lt_error_t *err = NULL;
53 : int i, n;
54 :
55 4 : lt_return_val_if_fail (langdb != NULL, FALSE);
56 :
57 4 : doc = lt_xml_get_subtag_registry(langdb->xml);
58 4 : xctxt = xmlXPathNewContext(doc);
59 4 : if (!xctxt) {
60 0 : lt_error_set(&err, LT_ERR_OOM,
61 : "Unable to create an instance of xmlXPathContextPtr.");
62 0 : goto bail;
63 : }
64 4 : xobj = xmlXPathEvalExpression((const xmlChar *)"/registry/language", xctxt);
65 4 : if (!xobj) {
66 0 : lt_error_set(&err, LT_ERR_FAIL_ON_XML,
67 : "No valid elements for %s",
68 : doc->name);
69 0 : goto bail;
70 : }
71 4 : n = xmlXPathNodeSetGetLength(xobj->nodesetval);
72 :
73 31808 : for (i = 0; i < n; i++) {
74 31804 : xmlNodePtr ent = xmlXPathNodeSetItem(xobj->nodesetval, i);
75 : xmlNodePtr cnode;
76 31804 : xmlChar *subtag = NULL, *desc = NULL, *scope = NULL, *macrolang = NULL;
77 31804 : xmlChar *preferred = NULL, *suppress = NULL;
78 31804 : lt_lang_t *le = NULL;
79 : char *s;
80 :
81 31804 : if (!ent) {
82 0 : lt_error_set(&err, LT_ERR_FAIL_ON_XML,
83 : "Unable to obtain the xml node via XPath.");
84 0 : goto bail;
85 : }
86 31804 : cnode = ent->children;
87 295204 : while (cnode != NULL) {
88 231596 : if (xmlStrcmp(cnode->name, (const xmlChar *)"subtag") == 0) {
89 31804 : if (subtag) {
90 0 : lt_warning("Duplicate subtag element in language: previous value was '%s'",
91 : subtag);
92 : } else {
93 31804 : subtag = xmlNodeGetContent(cnode);
94 : }
95 367780 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"added") == 0 ||
96 335736 : xmlStrcmp(cnode->name, (const xmlChar *)"deprecated") == 0 ||
97 203796 : xmlStrcmp(cnode->name, (const xmlChar *)"text") == 0 ||
98 36048 : xmlStrcmp(cnode->name, (const xmlChar *)"comments") == 0) {
99 : /* ignore it */
100 35908 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"description") == 0) {
101 : /* wonder if many descriptions helps something. or is it a bug? */
102 32760 : if (!desc)
103 31804 : desc = xmlNodeGetContent(cnode);
104 3148 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"scope") == 0) {
105 732 : if (scope) {
106 0 : lt_warning("Duplicate scope element in language: previous value was '%s'",
107 : scope);
108 : } else {
109 732 : scope = xmlNodeGetContent(cnode);
110 : }
111 2416 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"macrolanguage") == 0) {
112 1772 : if (macrolang) {
113 0 : lt_warning("Duplicate macrolanguage element in language: previous value was '%s'",
114 : macrolang);
115 : } else {
116 1772 : macrolang = xmlNodeGetContent(cnode);
117 : }
118 644 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"preferred-value") == 0) {
119 108 : if (preferred) {
120 0 : lt_warning("Duplicate preferred-value element in language: previous value was '%s'",
121 : preferred);
122 : } else {
123 108 : preferred = xmlNodeGetContent(cnode);
124 : }
125 536 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"suppress-script") == 0) {
126 536 : if (suppress) {
127 0 : lt_warning("Duplicate suppress-script element in language: previous value was '%s'",
128 : suppress);
129 : } else {
130 536 : suppress = xmlNodeGetContent(cnode);
131 : }
132 : } else {
133 0 : lt_warning("Unknown node under /registry/language: %s", cnode->name);
134 : }
135 231596 : cnode = cnode->next;
136 : }
137 31804 : if (!subtag) {
138 0 : lt_warning("No subtag node: description = '%s', scope = '%s', macrolanguage = '%s'",
139 : desc, scope, macrolang);
140 0 : goto bail1;
141 : }
142 31804 : if (!desc) {
143 0 : lt_warning("No description node: subtag = '%s', scope = '%s', macrolanguage = '%s'",
144 : subtag, scope, macrolang);
145 0 : goto bail1;
146 : }
147 31804 : le = lt_lang_create();
148 31804 : if (!le) {
149 0 : lt_error_set(&err, LT_ERR_OOM,
150 : "Unable to create an instance of lt_lang_t.");
151 0 : goto bail1;
152 : }
153 31804 : lt_lang_set_tag(le, (const char *)subtag);
154 31804 : lt_lang_set_name(le, (const char *)desc);
155 31804 : if (scope)
156 732 : lt_lang_set_scope(le, (const char *)scope);
157 31804 : if (macrolang)
158 1772 : lt_lang_set_macro_language(le, (const char *)macrolang);
159 31804 : if (preferred)
160 108 : lt_lang_set_preferred_tag(le, (const char *)preferred);
161 31804 : if (suppress)
162 536 : lt_lang_set_suppress_script(le, (const char *)suppress);
163 :
164 31804 : s = strdup(lt_lang_get_tag(le));
165 31804 : lt_trie_replace(langdb->lang_entries,
166 31804 : lt_strlower(s),
167 31804 : lt_lang_ref(le),
168 : (lt_destroy_func_t)lt_lang_unref);
169 31804 : free(s);
170 : bail1:
171 31804 : if (subtag)
172 31804 : xmlFree(subtag);
173 31804 : if (desc)
174 31804 : xmlFree(desc);
175 31804 : if (scope)
176 732 : xmlFree(scope);
177 31804 : if (macrolang)
178 1772 : xmlFree(macrolang);
179 31804 : if (preferred)
180 108 : xmlFree(preferred);
181 31804 : if (suppress)
182 536 : xmlFree(suppress);
183 31804 : lt_lang_unref(le);
184 : }
185 : bail:
186 4 : if (lt_error_is_set(err, LT_ERR_ANY)) {
187 0 : if (error)
188 0 : *error = lt_error_ref(err);
189 : else
190 0 : lt_error_print(err, LT_ERR_ANY);
191 0 : lt_error_unref(err);
192 0 : retval = FALSE;
193 : }
194 :
195 4 : if (xobj)
196 4 : xmlXPathFreeObject(xobj);
197 4 : if (xctxt)
198 4 : xmlXPathFreeContext(xctxt);
199 :
200 4 : return retval;
201 : }
202 :
203 : /*< public >*/
204 : /**
205 : * lt_lang_db_new:
206 : *
207 : * Create a new instance of a #lt_lang_db_t.
208 : *
209 : * Returns: (transfer full): a new instance of #lt_lang_db_t.
210 : */
211 : lt_lang_db_t *
212 4 : lt_lang_db_new(void)
213 : {
214 4 : lt_lang_db_t *retval = lt_mem_alloc_object(sizeof (lt_lang_db_t));
215 :
216 4 : if (retval) {
217 4 : lt_error_t *err = NULL;
218 : lt_lang_t *le;
219 :
220 4 : retval->lang_entries = lt_trie_new();
221 4 : lt_mem_add_ref(&retval->parent, retval->lang_entries,
222 : (lt_destroy_func_t)lt_trie_unref);
223 :
224 4 : le = lt_lang_create();
225 4 : lt_lang_set_tag(le, "*");
226 4 : lt_lang_set_name(le, "Wildcard entry");
227 4 : lt_trie_replace(retval->lang_entries,
228 : lt_lang_get_tag(le),
229 : le,
230 : (lt_destroy_func_t)lt_lang_unref);
231 :
232 4 : retval->xml = lt_xml_new();
233 4 : if (!retval->xml) {
234 0 : lt_lang_db_unref(retval);
235 0 : retval = NULL;
236 : goto bail;
237 : }
238 4 : lt_mem_add_ref(&retval->parent, retval->xml,
239 : (lt_destroy_func_t)lt_xml_unref);
240 :
241 4 : lt_lang_db_parse(retval, &err);
242 4 : if (lt_error_is_set(err, LT_ERR_ANY)) {
243 0 : lt_error_print(err, LT_ERR_ANY);
244 0 : lt_lang_db_unref(retval);
245 0 : retval = NULL;
246 0 : lt_error_unref(err);
247 : }
248 : }
249 : bail:
250 :
251 4 : return retval;
252 : }
253 :
254 : /**
255 : * lt_lang_db_ref:
256 : * @langdb: a #lt_lang_db_t.
257 : *
258 : * Increases the reference count of @langdb.
259 : *
260 : * Returns: (transfer none): the same @langdb object.
261 : */
262 : lt_lang_db_t *
263 20 : lt_lang_db_ref(lt_lang_db_t *langdb)
264 : {
265 20 : lt_return_val_if_fail (langdb != NULL, NULL);
266 :
267 20 : return lt_mem_ref(&langdb->parent);
268 : }
269 :
270 : /**
271 : * lt_lang_db_unref:
272 : * @langdb: a #lt_lang_db_t.
273 : *
274 : * Decreases the reference count of @langdb. when its reference count
275 : * drops to 0, the object is finalized (i.e. its memory is freed).
276 : */
277 : void
278 24 : lt_lang_db_unref(lt_lang_db_t *langdb)
279 : {
280 24 : if (langdb)
281 24 : lt_mem_unref(&langdb->parent);
282 24 : }
283 :
284 : /**
285 : * lt_lang_db_lookup:
286 : * @langdb: a #lt_lang_db_t.
287 : * @subtag: a subtag name to lookup.
288 : *
289 : * Lookup @lt_lang_t if @subtag is valid and registered into the database.
290 : *
291 : * Returns: (transfer full): a #lt_lang_t that meets with @subtag.
292 : * otherwise %NULL.
293 : */
294 : lt_lang_t *
295 20 : lt_lang_db_lookup(lt_lang_db_t *langdb,
296 : const char *subtag)
297 : {
298 : lt_lang_t *retval;
299 : char *s;
300 :
301 20 : lt_return_val_if_fail (langdb != NULL, NULL);
302 20 : lt_return_val_if_fail (subtag != NULL, NULL);
303 :
304 20 : s = strdup(subtag);
305 20 : retval = lt_trie_lookup(langdb->lang_entries, lt_strlower(s));
306 20 : free(s);
307 20 : if (retval)
308 20 : return lt_lang_ref(retval);
309 :
310 0 : return NULL;
311 : }
|