Line data Source code
1 : /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 : /*
3 : * lt-extlang-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-extlang.h"
21 : #include "lt-extlang-private.h"
22 : #include "lt-mem.h"
23 : #include "lt-messages.h"
24 : #include "lt-trie.h"
25 : #include "lt-utils.h"
26 : #include "lt-xml.h"
27 : #include "lt-extlang-db.h"
28 :
29 :
30 : /**
31 : * SECTION:lt-extlang-db
32 : * @Short_Description: An interface to access Extlang Database
33 : * @Title: Database - Extlang
34 : *
35 : * This class provides an interface to access Extlang database. which has been
36 : * registered as ISO 639 code.
37 : */
38 : struct _lt_extlang_db_t {
39 : lt_mem_t parent;
40 : lt_xml_t *xml;
41 : lt_trie_t *extlang_entries;
42 : };
43 :
44 : /*< private >*/
45 : static lt_bool_t
46 4 : lt_extlang_db_parse(lt_extlang_db_t *extlangdb,
47 : lt_error_t **error)
48 : {
49 4 : lt_bool_t retval = TRUE;
50 4 : xmlDocPtr doc = NULL;
51 4 : xmlXPathContextPtr xctxt = NULL;
52 4 : xmlXPathObjectPtr xobj = NULL;
53 4 : lt_error_t *err = NULL;
54 : int i, n;
55 :
56 4 : lt_return_val_if_fail (extlangdb != NULL, FALSE);
57 :
58 4 : doc = lt_xml_get_subtag_registry(extlangdb->xml);
59 4 : xctxt = xmlXPathNewContext(doc);
60 4 : if (!xctxt) {
61 0 : lt_error_set(&err, LT_ERR_OOM,
62 : "Unable to create an instance of xmlXPathContextPtr.");
63 0 : goto bail;
64 : }
65 4 : xobj = xmlXPathEvalExpression((const xmlChar *)"/registry/extlang", xctxt);
66 4 : if (!xobj) {
67 0 : lt_error_set(&err, LT_ERR_FAIL_ON_XML,
68 : "No valid elements for %s",
69 : doc->name);
70 0 : goto bail;
71 : }
72 4 : n = xmlXPathNodeSetGetLength(xobj->nodesetval);
73 :
74 912 : for (i = 0; i < n; i++) {
75 908 : xmlNodePtr ent = xmlXPathNodeSetItem(xobj->nodesetval, i);
76 : xmlNodePtr cnode;
77 908 : xmlChar *subtag = NULL, *desc = NULL, *macrolang = NULL, *preferred = NULL, *prefix = NULL;
78 908 : lt_extlang_t *le = NULL;
79 : char *s;
80 :
81 908 : 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 908 : cnode = ent->children;
87 12604 : while (cnode != NULL) {
88 10788 : if (xmlStrcmp(cnode->name, (const xmlChar *)"subtag") == 0) {
89 908 : if (subtag) {
90 0 : lt_warning("Duplicate subtag element in extlang: previous value was '%s'",
91 : subtag);
92 : } else {
93 908 : subtag = xmlNodeGetContent(cnode);
94 : }
95 18852 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"added") == 0 ||
96 8972 : xmlStrcmp(cnode->name, (const xmlChar *)"text") == 0) {
97 : /* ignore it */
98 3124 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"description") == 0) {
99 : /* wonder if many descriptions helps something. or is it a bug? */
100 960 : if (!desc)
101 908 : desc = xmlNodeGetContent(cnode);
102 2164 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"macrolanguage") == 0) {
103 348 : if (macrolang) {
104 0 : lt_warning("Duplicate macrolanguage element in extlang: previous value was '%s'",
105 : macrolang);
106 : } else {
107 348 : macrolang = xmlNodeGetContent(cnode);
108 : }
109 1816 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"preferred-value") == 0) {
110 908 : if (preferred) {
111 0 : lt_warning("Duplicate preferred-value element in extlang: previous value was '%s'",
112 : preferred);
113 : } else {
114 908 : preferred = xmlNodeGetContent(cnode);
115 : }
116 908 : } else if (xmlStrcmp(cnode->name, (const xmlChar *)"prefix") == 0) {
117 908 : if (prefix) {
118 0 : lt_warning("Duplicate prefix element in extlang: previous value was '%s'",
119 : prefix);
120 : } else {
121 908 : prefix = xmlNodeGetContent(cnode);
122 : }
123 : } else {
124 0 : lt_warning("Unknown node under /registry/extlang: %s", cnode->name);
125 : }
126 10788 : cnode = cnode->next;
127 : }
128 908 : if (!subtag) {
129 0 : lt_warning("No subtag node: description = '%s', macrolanguage = '%s', preferred-value = '%s', prefix = '%s'",
130 : desc, macrolang, preferred, prefix);
131 0 : goto bail1;
132 : }
133 908 : if (!desc) {
134 0 : lt_warning("No description node: subtag = '%s', macrolanguage = '%s', preferred-value = '%s', prefix = '%s'",
135 : subtag, macrolang, preferred, prefix);
136 0 : goto bail1;
137 : }
138 908 : le = lt_extlang_create();
139 908 : if (!le) {
140 0 : lt_error_set(&err, LT_ERR_OOM,
141 : "Unable to create an instance of lt_extlang_t.");
142 0 : goto bail1;
143 : }
144 908 : lt_extlang_set_tag(le, (const char *)subtag);
145 908 : lt_extlang_set_name(le, (const char *)desc);
146 908 : if (macrolang)
147 348 : lt_extlang_set_macro_language(le, (const char *)macrolang);
148 908 : if (preferred)
149 908 : lt_extlang_set_preferred_tag(le, (const char *)preferred);
150 908 : if (prefix)
151 908 : lt_extlang_add_prefix(le, (const char *)prefix);
152 :
153 908 : s = strdup(lt_extlang_get_tag(le));
154 908 : lt_trie_replace(extlangdb->extlang_entries,
155 908 : lt_strlower(s),
156 908 : lt_extlang_ref(le),
157 : (lt_destroy_func_t)lt_extlang_unref);
158 908 : free(s);
159 : bail1:
160 908 : if (subtag)
161 908 : xmlFree(subtag);
162 908 : if (desc)
163 908 : xmlFree(desc);
164 908 : if (macrolang)
165 348 : xmlFree(macrolang);
166 908 : if (preferred)
167 908 : xmlFree(preferred);
168 908 : if (prefix)
169 908 : xmlFree(prefix);
170 908 : lt_extlang_unref(le);
171 : }
172 : bail:
173 4 : if (lt_error_is_set(err, LT_ERR_ANY)) {
174 0 : if (error)
175 0 : *error = lt_error_ref(err);
176 : else
177 0 : lt_error_print(err, LT_ERR_ANY);
178 0 : lt_error_unref(err);
179 0 : retval = FALSE;
180 : }
181 :
182 4 : if (xobj)
183 4 : xmlXPathFreeObject(xobj);
184 4 : if (xctxt)
185 4 : xmlXPathFreeContext(xctxt);
186 :
187 4 : return retval;
188 : }
189 :
190 : /*< public >*/
191 : /**
192 : * lt_extlang_db_new:
193 : *
194 : * Create a new instance of a #lt_extlang_db_t.
195 : *
196 : * Returns: (transfer full): a new instance of #lt_extlang_db_t.
197 : */
198 : lt_extlang_db_t *
199 4 : lt_extlang_db_new(void)
200 : {
201 4 : lt_extlang_db_t *retval = lt_mem_alloc_object(sizeof (lt_extlang_db_t));
202 :
203 4 : if (retval) {
204 4 : lt_error_t *err = NULL;
205 : lt_extlang_t *le;
206 :
207 4 : retval->extlang_entries = lt_trie_new();
208 4 : lt_mem_add_ref(&retval->parent, retval->extlang_entries,
209 : (lt_destroy_func_t)lt_trie_unref);
210 :
211 4 : le = lt_extlang_create();
212 4 : lt_extlang_set_tag(le, "*");
213 4 : lt_extlang_set_name(le, "Wildcard entry");
214 4 : lt_trie_replace(retval->extlang_entries,
215 : lt_extlang_get_tag(le),
216 : le,
217 : (lt_destroy_func_t)lt_extlang_unref);
218 4 : le = lt_extlang_create();
219 4 : lt_extlang_set_tag(le, "");
220 4 : lt_extlang_set_name(le, "Empty entry");
221 4 : lt_trie_replace(retval->extlang_entries,
222 : lt_extlang_get_tag(le),
223 : le,
224 : (lt_destroy_func_t)lt_extlang_unref);
225 :
226 4 : retval->xml = lt_xml_new();
227 4 : if (!retval->xml) {
228 0 : lt_extlang_db_unref(retval);
229 0 : retval = NULL;
230 : goto bail;
231 : }
232 4 : lt_mem_add_ref(&retval->parent, retval->xml,
233 : (lt_destroy_func_t)lt_xml_unref);
234 4 : lt_extlang_db_parse(retval, &err);
235 4 : if (err) {
236 0 : lt_error_print(err, LT_ERR_ANY);
237 0 : lt_extlang_db_unref(retval);
238 0 : retval = NULL;
239 0 : lt_error_unref(err);
240 : }
241 : }
242 : bail:
243 :
244 4 : return retval;
245 : }
246 :
247 : /**
248 : * lt_extlang_db_ref:
249 : * @extlangdb: a #lt_extlang_db_t.
250 : *
251 : * Increases the reference count of @extlangdb.
252 : *
253 : * Returns: (transfer none): the same @extlangdb object.
254 : */
255 : lt_extlang_db_t *
256 4 : lt_extlang_db_ref(lt_extlang_db_t *extlangdb)
257 : {
258 4 : lt_return_val_if_fail (extlangdb != NULL, NULL);
259 :
260 4 : return lt_mem_ref(&extlangdb->parent);
261 : }
262 :
263 : /**
264 : * lt_extlang_db_unref:
265 : * @extlangdb: a #lt_extlang_db_t.
266 : *
267 : * Decreases the reference count of @extlangdb. when its reference count
268 : * drops to 0, the object is finalized (i.e. its memory is freed).
269 : */
270 : void
271 8 : lt_extlang_db_unref(lt_extlang_db_t *extlangdb)
272 : {
273 8 : if (extlangdb)
274 8 : lt_mem_unref(&extlangdb->parent);
275 8 : }
276 :
277 : /**
278 : * lt_extlang_db_lookup:
279 : * @extlangdb: a #lt_extlang_db_t.
280 : * @subtag: a subtag name to lookup.
281 : *
282 : * Lookup @lt_extlang_t if @subtag is valid and registered into the database.
283 : *
284 : * Returns: (transfer full): a #lt_extlang_t that meets with @subtag.
285 : * otherwise %NULL.
286 : */
287 : lt_extlang_t *
288 4 : lt_extlang_db_lookup(lt_extlang_db_t *extlangdb,
289 : const char *subtag)
290 : {
291 : lt_extlang_t *retval;
292 : char *s;
293 :
294 4 : lt_return_val_if_fail (extlangdb != NULL, NULL);
295 4 : lt_return_val_if_fail (subtag != NULL, NULL);
296 :
297 4 : s = strdup(subtag);
298 4 : retval = lt_trie_lookup(extlangdb->extlang_entries,
299 4 : lt_strlower(s));
300 4 : free(s);
301 4 : if (retval)
302 0 : return lt_extlang_ref(retval);
303 :
304 4 : return NULL;
305 : }
|