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