Only use yaz_icu_iter_t if ICU is enabled for YAZ
[pazpar2-moved-to-github.git] / src / charsets.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2010 Index Data
3
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 /** \file charsets.c
21     \brief Pazpar2 Character set facilities
22 */
23
24 #if HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <yaz/xmalloc.h>
29 #include <yaz/wrbuf.h>
30 #include <yaz/log.h>
31 #include <yaz/yaz-version.h>
32 #include <ctype.h>
33 #include <assert.h>
34 #include <string.h>
35
36 #include "charsets.h"
37 #include "normalize7bit.h"
38
39 #if YAZ_HAVE_ICU
40 #include <yaz/icu.h>
41 #endif
42
43 /* charset handle */
44 struct pp2_charset_s {
45     const char *(*token_next_handler)(pp2_relevance_token_t prt);
46     const char *(*get_sort_handler)(pp2_relevance_token_t prt);
47     int ref_count;
48 #if YAZ_HAVE_ICU
49     struct icu_chain * icu_chn;
50     UErrorCode icu_sts;
51 #endif
52 };
53
54 static const char *pp2_relevance_token_a_to_z(pp2_relevance_token_t prt);
55 static const char *pp2_get_sort_ascii(pp2_relevance_token_t prt);
56
57 #if YAZ_HAVE_ICU
58 static const char *pp2_relevance_token_icu(pp2_relevance_token_t prt);
59 static const char *pp2_get_sort_icu(pp2_relevance_token_t prt);
60 #endif
61
62 /* tokenzier handle */
63 struct pp2_relevance_token_s {
64     const char *cp;     /* unnormalized buffer we're tokenizing */
65     const char *last_cp;  /* pointer to last token we're dealing with */
66     pp2_charset_t pct;  /* our main charset handle (type+config) */
67     WRBUF norm_str;     /* normized string we return (temporarily) */
68     WRBUF sort_str;     /* sort string we return (temporarily) */
69 #if YAZ_HAVE_ICU
70     yaz_icu_iter_t iter;
71 #endif
72 };
73
74
75 pp2_charset_t pp2_charset_create_xml(xmlNode *xml_node)
76 {
77 #if YAZ_HAVE_ICU
78     UErrorCode status = U_ZERO_ERROR;
79     struct icu_chain *chain = 0;
80     if (xml_node)
81         xml_node = xml_node->children;
82     while (xml_node && xml_node->type != XML_ELEMENT_NODE)
83         xml_node = xml_node->next;
84     chain = icu_chain_xml_config(xml_node, 1, &status);
85     if (!chain || U_FAILURE(status)){
86         //xmlDocPtr icu_doc = 0;
87         //xmlChar *xmlstr = 0;
88                 //int size = 0;
89                 //xmlDocDumpMemory(icu_doc, size);
90         
91         yaz_log(YLOG_FATAL, "Could not parse ICU chain config:\n"
92                 "<%s>\n ... \n</%s>",
93                 xml_node->name, xml_node->name);
94         return 0;
95     }
96     return pp2_charset_create(chain);
97 #else // YAZ_HAVE_ICU
98     yaz_log(YLOG_FATAL, "Error: ICU support requested with element:\n"
99             "<%s>\n ... \n</%s>",
100             xml_node->name, xml_node->name);
101     yaz_log(YLOG_FATAL, 
102             "But no ICU support is compiled into the YAZ library.");
103     return 0;
104 #endif // YAZ_HAVE_ICU
105 }
106
107 void pp2_charset_incref(pp2_charset_t pct)
108 {
109     (pct->ref_count)++;
110 }
111
112 pp2_charset_t pp2_charset_create(struct icu_chain * icu_chn)
113 {
114     pp2_charset_t pct = xmalloc(sizeof(*pct));
115
116     pct->token_next_handler = pp2_relevance_token_a_to_z;
117     pct->get_sort_handler  = pp2_get_sort_ascii;
118     pct->ref_count = 1;
119 #if YAZ_HAVE_ICU
120     pct->icu_chn = 0;
121     if (icu_chn)
122     {
123         pct->icu_chn = icu_chn;
124         pct->icu_sts = U_ZERO_ERROR;
125         pct->token_next_handler = pp2_relevance_token_icu;
126         pct->get_sort_handler = pp2_get_sort_icu;
127     }
128 #endif // YAZ_HAVE_ICU
129     return pct;
130 }
131
132 void pp2_charset_destroy(pp2_charset_t pct)
133 {
134     if (pct)
135     {
136         assert(pct->ref_count >= 1);
137         --(pct->ref_count);
138         if (pct->ref_count == 0)
139         {
140 #if YAZ_HAVE_ICU
141             icu_chain_destroy(pct->icu_chn);
142 #endif
143             xfree(pct);
144         }
145     }
146 }
147
148 pp2_relevance_token_t pp2_relevance_tokenize(pp2_charset_t pct)
149 {
150     pp2_relevance_token_t prt = xmalloc(sizeof(*prt));
151
152     assert(pct);
153
154     prt->norm_str = wrbuf_alloc();
155     prt->sort_str = wrbuf_alloc();
156     prt->cp = 0;
157     prt->last_cp = 0;
158     prt->pct = pct;
159
160 #if YAZ_HAVE_ICU
161     prt->iter = 0;
162     if (pct->icu_chn)
163         prt->iter = icu_iter_create(pct->icu_chn);
164 #endif
165     return prt;
166 }
167
168 void pp2_relevance_first(pp2_relevance_token_t prt,
169                          const char *buf,
170                          int skip_article)
171
172     if (skip_article)
173     {
174         const char *p = buf;
175         char firstword[64];
176         char *pout = firstword;
177         char articles[] = "the den der die des an a "; // must end in space
178         
179         while (*p && !isalnum(*(unsigned char *)p))
180             p++;
181         for (; *p && *p != ' ' && pout - firstword < (sizeof(firstword)-2); p++)
182             *pout++ = tolower(*(unsigned char *)p);
183         *pout++ = ' ';
184         *pout++ = '\0';
185         if (strstr(articles, firstword))
186             buf = p;
187     }
188
189     wrbuf_rewind(prt->norm_str);
190     wrbuf_rewind(prt->sort_str);
191     prt->cp = buf;
192     prt->last_cp = 0;
193
194 #if YAZ_HAVE_ICU
195     if (prt->iter)
196     {
197         icu_iter_first(prt->iter, buf);
198     }
199 #endif // YAZ_HAVE_ICU
200 }
201
202 void pp2_relevance_token_destroy(pp2_relevance_token_t prt)
203 {
204     assert(prt);
205 #if YAZ_HAVE_ICU
206     if (prt->iter)
207         icu_iter_destroy(prt->iter);
208 #endif
209     if(prt->norm_str) 
210         wrbuf_destroy(prt->norm_str);
211     if(prt->sort_str) 
212         wrbuf_destroy(prt->sort_str);
213     xfree(prt);
214 }
215
216 const char *pp2_relevance_token_next(pp2_relevance_token_t prt)
217 {
218     assert(prt);
219     return (prt->pct->token_next_handler)(prt);
220 }
221
222 const char *pp2_get_sort(pp2_relevance_token_t prt)
223 {
224     return prt->pct->get_sort_handler(prt);
225 }
226
227 #define raw_char(c) (((c) >= 'a' && (c) <= 'z') ? (c) : -1)
228 /* original tokenizer with our tokenize interface, but we
229    add +1 to ensure no '\0' are in our string (except for EOF)
230 */
231 static const char *pp2_relevance_token_a_to_z(pp2_relevance_token_t prt)
232 {
233     const char *cp = prt->cp;
234     int c;
235
236     /* skip white space */
237     while (*cp && (c = raw_char(tolower(*(const unsigned char *)cp))) < 0)
238         cp++;
239     if (*cp == '\0')
240     {
241         prt->cp = cp;
242         prt->last_cp = 0;
243         return 0;
244     }
245     /* now read the term itself */
246
247     prt->last_cp = cp;
248     wrbuf_rewind(prt->norm_str);
249     while (*cp && (c = raw_char(tolower(*cp))) >= 0)
250     {
251         wrbuf_putc(prt->norm_str, c);
252         cp++;
253     }
254     prt->cp = cp;
255     return wrbuf_cstr(prt->norm_str);
256 }
257
258 static const char *pp2_get_sort_ascii(pp2_relevance_token_t prt)
259 {
260     if (prt->last_cp == 0)
261         return 0;
262     else
263     {
264         char *tmp = xstrdup(prt->last_cp);
265         char *result = 0;
266         result = normalize7bit_mergekey(tmp);
267         
268         wrbuf_rewind(prt->sort_str);
269         wrbuf_puts(prt->sort_str, result);
270         xfree(tmp);
271         return wrbuf_cstr(prt->sort_str);
272     }
273 }
274
275
276 #if YAZ_HAVE_ICU
277 static const char *pp2_relevance_token_icu(pp2_relevance_token_t prt)
278 {
279     if (icu_iter_next(prt->iter))
280     {
281         return icu_iter_get_norm(prt->iter);
282     }
283     return 0;
284 }
285
286 static const char *pp2_get_sort_icu(pp2_relevance_token_t prt)
287 {
288     return icu_iter_get_sortkey(prt->iter);
289 }
290
291 #endif // YAZ_HAVE_ICU
292
293
294 /*
295  * Local variables:
296  * c-basic-offset: 4
297  * c-file-style: "Stroustrup"
298  * indent-tabs-mode: nil
299  * End:
300  * vim: shiftwidth=4 tabstop=8 expandtab
301  */
302