Added ICU chain component - which used to be part of Pazpar2.
[yaz-moved-to-github.git] / src / icu_I18N.c
1 /* $Id: icu_I18N.c,v 1.1 2007-10-22 12:21:39 adam Exp $
2    Copyright (c) 2006-2007, Index Data.
3
4    This file is part of Pazpar2.
5
6    Pazpar2 is free software; you can redistribute it and/or modify it under
7    the terms of the GNU General Public License as published by the Free
8    Software Foundation; either version 2, or (at your option) any later
9    version.
10
11    Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
12    WARRANTY; without even the implied warranty of MERCHANTABILITY or
13    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14    for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with Pazpar2; see the file LICENSE.  If not, write to the
18    Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19    02111-1307, USA.
20 */
21
22 #if HAVE_CONFIG_H
23 #include "cconfig.h"
24 #endif
25
26 #define USE_TIMING 0
27 #if USE_TIMING
28 #include <yaz/timing.h>
29 #endif
30
31
32 #ifdef HAVE_ICU
33 #include <yaz/icu_I18N.h>
34
35 #include <yaz/log.h>
36
37 #include <string.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40
41 #include <unicode/ustring.h>  /* some more string fcns*/
42 #include <unicode/uchar.h>    /* char names           */
43
44
45 //#include <unicode/ustdio.h>
46 //#include <unicode/utypes.h>   /* Basic ICU data types */
47 #include <unicode/ucol.h> 
48 //#include <unicode/ucnv.h>     /* C   Converter API    */
49 //#include <unicode/uloc.h>
50 //#include <unicode/ubrk.h>
51 /* #include <unicode/unistr.h> */
52
53
54
55
56 int icu_check_status (UErrorCode status)
57 {
58     if(U_FAILURE(status)){
59         yaz_log(YLOG_WARN, 
60                 "ICU: %d %s\n", status, u_errorName(status));
61         return 0;   
62     }
63     return 1;
64     
65 }
66
67
68
69 struct icu_buf_utf16 * icu_buf_utf16_create(size_t capacity)
70 {
71     struct icu_buf_utf16 * buf16 
72         = (struct icu_buf_utf16 *) malloc(sizeof(struct icu_buf_utf16));
73
74     buf16->utf16 = 0;
75     buf16->utf16_len = 0;
76     buf16->utf16_cap = 0;
77
78     if (capacity > 0){
79         buf16->utf16 = (UChar *) malloc(sizeof(UChar) * capacity);
80         buf16->utf16[0] = (UChar) 0;
81         buf16->utf16_cap = capacity;
82     }
83     return buf16;
84 };
85
86 struct icu_buf_utf16 * icu_buf_utf16_resize(struct icu_buf_utf16 * buf16,
87                                             size_t capacity)
88 {
89     if (buf16){
90         if (capacity >  0){
91             if (0 == buf16->utf16)
92                 buf16->utf16 = (UChar *) malloc(sizeof(UChar) * capacity);
93             else
94                 buf16->utf16 
95                     = (UChar *) realloc(buf16->utf16, sizeof(UChar) * capacity);
96             buf16->utf16[0] = (UChar) 0;
97             buf16->utf16_len = 0;
98             buf16->utf16_cap = capacity;
99         } 
100         else { 
101             if (buf16->utf16)
102                 free(buf16->utf16);
103             buf16->utf16 = 0;
104             buf16->utf16_len = 0;
105             buf16->utf16_cap = 0;
106         }
107     }
108
109     return buf16;
110 };
111
112
113 struct icu_buf_utf16 * icu_buf_utf16_copy(struct icu_buf_utf16 * dest16,
114                                           struct icu_buf_utf16 * src16)
115 {
116     if(!dest16 || !src16
117        || dest16 == src16)
118         return 0;
119
120     if (dest16->utf16_cap < src16->utf16_len)
121         icu_buf_utf16_resize(dest16, src16->utf16_len * 2);
122
123     u_strncpy(dest16->utf16, src16->utf16, src16->utf16_len);
124     dest16->utf16_len = src16->utf16_len;
125
126     return dest16;
127 };
128
129
130 void icu_buf_utf16_destroy(struct icu_buf_utf16 * buf16)
131 {
132     if (buf16){
133         if (buf16->utf16)
134             free(buf16->utf16);
135         free(buf16);
136     }
137 };
138
139
140
141
142
143
144 struct icu_buf_utf8 * icu_buf_utf8_create(size_t capacity)
145 {
146     struct icu_buf_utf8 * buf8 
147         = (struct icu_buf_utf8 *) malloc(sizeof(struct icu_buf_utf8));
148
149     buf8->utf8 = 0;
150     buf8->utf8_len = 0;
151     buf8->utf8_cap = 0;
152
153     if (capacity > 0){
154         buf8->utf8 = (uint8_t *) malloc(sizeof(uint8_t) * capacity);
155         buf8->utf8[0] = (uint8_t) 0;
156         buf8->utf8_cap = capacity;
157     }
158     return buf8;
159 };
160
161
162
163 struct icu_buf_utf8 * icu_buf_utf8_resize(struct icu_buf_utf8 * buf8,
164                                           size_t capacity)
165 {
166     if (buf8){
167         if (capacity >  0){
168             if (0 == buf8->utf8)
169                 buf8->utf8 = (uint8_t *) malloc(sizeof(uint8_t) * capacity);
170             else
171                 buf8->utf8 
172                     = (uint8_t *) realloc(buf8->utf8, 
173                                           sizeof(uint8_t) * capacity);
174             buf8->utf8_cap = capacity;
175         } 
176         else { 
177             if (buf8->utf8)
178                 free(buf8->utf8);
179             buf8->utf8 = 0;
180             buf8->utf8_len = 0;
181             buf8->utf8_cap = 0;
182         }
183     }
184
185     return buf8;
186 };
187
188
189 struct icu_buf_utf8 * icu_buf_utf8_copy(struct icu_buf_utf8 * dest8,
190                                           struct icu_buf_utf8 * src8)
191 {
192     if(!dest8 || !src8
193        || dest8 == src8)
194         return 0;
195     
196
197     if (dest8->utf8_cap < src8->utf8_len)
198         icu_buf_utf8_resize(dest8, src8->utf8_len * 2);
199
200     strncpy((char*) dest8->utf8, (char*) src8->utf8, src8->utf8_len);
201
202     return dest8;
203 };
204
205
206 const char *icu_buf_utf8_to_cstr(struct icu_buf_utf8 *src8)
207 {
208     if (!src8 || src8->utf8_len == 0)
209         return "";
210     if (src8->utf8_len == src8->utf8_cap)
211         src8 = icu_buf_utf8_resize(src8, src8->utf8_len * 2 + 1);
212     src8->utf8[src8->utf8_len] = '\0';
213     return (const char *) src8->utf8;
214 }
215
216
217 void icu_buf_utf8_destroy(struct icu_buf_utf8 * buf8)
218 {
219     if (buf8){
220         if (buf8->utf8)
221             free(buf8->utf8);
222         free(buf8);
223     }
224 };
225
226
227
228 UErrorCode icu_utf16_from_utf8(struct icu_buf_utf16 * dest16,
229                                struct icu_buf_utf8 * src8,
230                                UErrorCode * status)
231 {
232     int32_t utf16_len = 0;
233   
234     u_strFromUTF8(dest16->utf16, dest16->utf16_cap,
235                   &utf16_len,
236                   (const char *) src8->utf8, src8->utf8_len, status);
237   
238     // check for buffer overflow, resize and retry
239     if (*status == U_BUFFER_OVERFLOW_ERROR
240         //|| dest16->utf16_len > dest16->utf16_cap
241         ){
242         icu_buf_utf16_resize(dest16, utf16_len * 2);
243         *status = U_ZERO_ERROR;
244         u_strFromUTF8(dest16->utf16, dest16->utf16_cap,
245                       &utf16_len,
246                       (const char *) src8->utf8, src8->utf8_len, status);
247     }
248
249     //if (*status != U_BUFFER_OVERFLOW_ERROR
250     if (U_SUCCESS(*status)  
251         && utf16_len <= dest16->utf16_cap)
252         dest16->utf16_len = utf16_len;
253     else {
254         dest16->utf16[0] = (UChar) 0;
255         dest16->utf16_len = 0;
256     }
257   
258     return *status;
259 };
260
261  
262
263 UErrorCode icu_utf16_from_utf8_cstr(struct icu_buf_utf16 * dest16,
264                                     const char * src8cstr,
265                                     UErrorCode * status)
266 {
267     size_t src8cstr_len = 0;
268     int32_t utf16_len = 0;
269
270     src8cstr_len = strlen(src8cstr);
271   
272     u_strFromUTF8(dest16->utf16, dest16->utf16_cap,
273                   &utf16_len,
274                   src8cstr, src8cstr_len, status);
275   
276     // check for buffer overflow, resize and retry
277     if (*status == U_BUFFER_OVERFLOW_ERROR
278         //|| dest16->utf16_len > dest16->utf16_cap
279         ){
280         icu_buf_utf16_resize(dest16, utf16_len * 2);
281         *status = U_ZERO_ERROR;
282         u_strFromUTF8(dest16->utf16, dest16->utf16_cap,
283                       &utf16_len,
284                       src8cstr, src8cstr_len, status);
285     }
286
287     //  if (*status != U_BUFFER_OVERFLOW_ERROR
288     if (U_SUCCESS(*status)  
289         && utf16_len <= dest16->utf16_cap)
290         dest16->utf16_len = utf16_len;
291     else {
292         dest16->utf16[0] = (UChar) 0;
293         dest16->utf16_len = 0;
294     }
295   
296     return *status;
297 };
298
299
300
301
302 UErrorCode icu_utf16_to_utf8(struct icu_buf_utf8 * dest8,
303                              struct icu_buf_utf16 * src16,
304                              UErrorCode * status)
305 {
306     int32_t utf8_len = 0;
307   
308     u_strToUTF8((char *) dest8->utf8, dest8->utf8_cap,
309                 &utf8_len,
310                 src16->utf16, src16->utf16_len, status);
311   
312     // check for buffer overflow, resize and retry
313     if (*status == U_BUFFER_OVERFLOW_ERROR
314         //|| dest8->utf8_len > dest8->utf8_cap
315         ){
316         icu_buf_utf8_resize(dest8, utf8_len * 2);
317         *status = U_ZERO_ERROR;
318         u_strToUTF8((char *) dest8->utf8, dest8->utf8_cap,
319                     &utf8_len,
320                     src16->utf16, src16->utf16_len, status);
321
322     }
323
324     //if (*status != U_BUFFER_OVERFLOW_ERROR
325     if (U_SUCCESS(*status)  
326         && utf8_len <= dest8->utf8_cap)
327         dest8->utf8_len = utf8_len;
328     else {
329         dest8->utf8[0] = (uint8_t) 0;
330         dest8->utf8_len = 0;
331     }
332   
333     return *status;
334 };
335
336
337
338 struct icu_casemap * icu_casemap_create(const char *locale, char action,
339                                         UErrorCode *status)
340 {    
341     struct icu_casemap * casemap
342         = (struct icu_casemap *) malloc(sizeof(struct icu_casemap));
343     strcpy(casemap->locale, locale);
344     casemap->action = action;
345
346     switch(casemap->action) {    
347     case 'l':   
348         break;
349     case 'u':   
350         break;
351     case 't':  
352         break;
353     case 'f':  
354         break;
355     default:
356         icu_casemap_destroy(casemap);
357         return 0;
358     }
359
360     return casemap;
361 };
362
363 void icu_casemap_destroy(struct icu_casemap * casemap)
364 {
365     if (casemap) 
366         free(casemap);
367 };
368
369
370 int icu_casemap_casemap(struct icu_casemap * casemap,
371                         struct icu_buf_utf16 * dest16,
372                         struct icu_buf_utf16 * src16,
373                         UErrorCode *status)
374 {
375     if(!casemap)
376         return 0;
377     
378     return icu_utf16_casemap(dest16, src16,
379                              casemap->locale, casemap->action, status);
380 };
381
382
383 int icu_utf16_casemap(struct icu_buf_utf16 * dest16,
384                       struct icu_buf_utf16 * src16,
385                       const char *locale, char action,
386                       UErrorCode *status)
387 {
388     int32_t dest16_len = 0;
389     
390     switch(action) {    
391     case 'l':    
392         dest16_len = u_strToLower(dest16->utf16, dest16->utf16_cap,
393                                   src16->utf16, src16->utf16_len, 
394                                   locale, status);
395         break;
396     case 'u':    
397         dest16_len = u_strToUpper(dest16->utf16, dest16->utf16_cap,
398                                   src16->utf16, src16->utf16_len, 
399                                   locale, status);
400         break;
401     case 't':    
402         dest16_len = u_strToTitle(dest16->utf16, dest16->utf16_cap,
403                                   src16->utf16, src16->utf16_len,
404                                   0, locale, status);
405         break;
406     case 'f':    
407         dest16_len = u_strFoldCase(dest16->utf16, dest16->utf16_cap,
408                                    src16->utf16, src16->utf16_len,
409                                    U_FOLD_CASE_DEFAULT, status);
410         break;
411         
412     default:
413         return U_UNSUPPORTED_ERROR;
414         break;
415     }
416
417     // check for buffer overflow, resize and retry
418     if (*status == U_BUFFER_OVERFLOW_ERROR
419         && dest16 != src16        // do not resize if in-place conversion 
420         //|| dest16_len > dest16->utf16_cap
421         ){
422         icu_buf_utf16_resize(dest16, dest16_len * 2);
423         *status = U_ZERO_ERROR;
424
425     
426         switch(action) {    
427         case 'l':    
428             dest16_len = u_strToLower(dest16->utf16, dest16->utf16_cap,
429                                       src16->utf16, src16->utf16_len, 
430                                       locale, status);
431             break;
432         case 'u':    
433             dest16_len = u_strToUpper(dest16->utf16, dest16->utf16_cap,
434                                       src16->utf16, src16->utf16_len, 
435                                       locale, status);
436             break;
437         case 't':    
438             dest16_len = u_strToTitle(dest16->utf16, dest16->utf16_cap,
439                                       src16->utf16, src16->utf16_len,
440                                       0, locale, status);
441             break;
442         case 'f':    
443             dest16_len = u_strFoldCase(dest16->utf16, dest16->utf16_cap,
444                                        src16->utf16, src16->utf16_len,
445                                        U_FOLD_CASE_DEFAULT, status);
446             break;
447         
448         default:
449             return U_UNSUPPORTED_ERROR;
450             break;
451         }
452     }
453     
454     if (U_SUCCESS(*status)
455         && dest16_len <= dest16->utf16_cap)
456         dest16->utf16_len = dest16_len;
457     else {
458         dest16->utf16[0] = (UChar) 0;
459         dest16->utf16_len = 0;
460     }
461   
462     return *status;
463 };
464
465
466
467 UErrorCode icu_sortkey8_from_utf16(UCollator *coll,
468                                    struct icu_buf_utf8 * dest8, 
469                                    struct icu_buf_utf16 * src16,
470                                    UErrorCode * status)
471
472   
473     int32_t sortkey_len = 0;
474
475     sortkey_len = ucol_getSortKey(coll, src16->utf16, src16->utf16_len,
476                                   dest8->utf8, dest8->utf8_cap);
477
478     // check for buffer overflow, resize and retry
479     if (sortkey_len > dest8->utf8_cap) {
480         icu_buf_utf8_resize(dest8, sortkey_len * 2);
481         sortkey_len = ucol_getSortKey(coll, src16->utf16, src16->utf16_len,
482                                       dest8->utf8, dest8->utf8_cap);
483     }
484
485     if (U_SUCCESS(*status)
486         && sortkey_len > 0)
487         dest8->utf8_len = sortkey_len;
488     else {
489         dest8->utf8[0] = (UChar) 0;
490         dest8->utf8_len = 0;
491     }
492
493     return sortkey_len;
494 };
495
496
497
498 struct icu_tokenizer * icu_tokenizer_create(const char *locale, char action,
499                                             UErrorCode *status)
500 {
501     struct icu_tokenizer * tokenizer
502         = (struct icu_tokenizer *) malloc(sizeof(struct icu_tokenizer));
503
504     strcpy(tokenizer->locale, locale);
505     tokenizer->action = action;
506     tokenizer->bi = 0;
507     tokenizer->buf16 = 0;
508     tokenizer->token_count = 0;
509     tokenizer->token_id = 0;
510     tokenizer->token_start = 0;
511     tokenizer->token_end = 0;
512
513
514     switch(tokenizer->action) {    
515     case 'l':
516         tokenizer->bi
517             = ubrk_open(UBRK_LINE, tokenizer->locale,
518                         0, 0, status);
519         break;
520     case 's':
521         tokenizer->bi
522             = ubrk_open(UBRK_SENTENCE, tokenizer->locale,
523                         0, 0, status);
524         break;
525     case 'w':
526         tokenizer->bi 
527             = ubrk_open(UBRK_WORD, tokenizer->locale,
528                         0, 0, status);
529         break;
530     case 'c':
531         tokenizer->bi 
532             = ubrk_open(UBRK_CHARACTER, tokenizer->locale,
533                         0, 0, status);
534         break;
535     case 't':
536         tokenizer->bi 
537             = ubrk_open(UBRK_TITLE, tokenizer->locale,
538                         0, 0, status);
539         break;
540     default:
541         *status = U_UNSUPPORTED_ERROR;
542         return 0;
543         break;
544     }
545     
546     // ICU error stuff is a very  funny business
547     if (U_SUCCESS(*status))
548         return tokenizer;
549
550     // freeing if failed
551     icu_tokenizer_destroy(tokenizer);
552     return 0;
553 };
554
555 void icu_tokenizer_destroy(struct icu_tokenizer * tokenizer)
556 {
557     if (tokenizer) {
558         if (tokenizer->bi)
559             ubrk_close(tokenizer->bi);
560         free(tokenizer);
561     }
562 };
563
564 int icu_tokenizer_attach(struct icu_tokenizer * tokenizer, 
565                          struct icu_buf_utf16 * src16, 
566                          UErrorCode *status)
567 {
568     if (!tokenizer || !tokenizer->bi || !src16)
569         return 0;
570
571
572     tokenizer->buf16 = src16;
573     tokenizer->token_count = 0;
574     tokenizer->token_id = 0;
575     tokenizer->token_start = 0;
576     tokenizer->token_end = 0;
577
578     ubrk_setText(tokenizer->bi, src16->utf16, src16->utf16_len, status);
579     
580  
581     if (U_FAILURE(*status))
582         return 0;
583
584     return 1;
585 };
586
587 int32_t icu_tokenizer_next_token(struct icu_tokenizer * tokenizer, 
588                          struct icu_buf_utf16 * tkn16, 
589                          UErrorCode *status)
590 {
591     int32_t tkn_start = 0;
592     int32_t tkn_end = 0;
593     int32_t tkn_len = 0;
594     
595
596     if (!tokenizer || !tokenizer->bi
597         || !tokenizer->buf16 || !tokenizer->buf16->utf16_len)
598         return 0;
599
600     // never change tokenizer->buf16 and keep always invariant
601     // 0 <= tokenizer->token_start 
602     //   <= tokenizer->token_end 
603     //   <= tokenizer->buf16->utf16_len
604     // returns length of token
605
606     if (0 == tokenizer->token_end) // first call
607         tkn_start = ubrk_first(tokenizer->bi);
608     else //successive calls
609         tkn_start = tokenizer->token_end;
610
611     // get next position
612     tkn_end = ubrk_next(tokenizer->bi);
613
614     // repairing invariant at end of ubrk, which is UBRK_DONE = -1 
615     if (UBRK_DONE == tkn_end)
616         tkn_end = tokenizer->buf16->utf16_len;
617
618     // copy out if everything is well
619     if(U_FAILURE(*status))
620         return 0;        
621     
622     // everything OK, now update internal state
623     tkn_len = tkn_end - tkn_start;
624
625     if (0 < tkn_len){
626         tokenizer->token_count++;
627         tokenizer->token_id++;
628     } else {
629         tokenizer->token_id = 0;    
630     }
631     tokenizer->token_start = tkn_start;
632     tokenizer->token_end = tkn_end;
633     
634
635     // copying into token buffer if it exists 
636     if (tkn16){
637         if (tkn16->utf16_cap < tkn_len)
638             icu_buf_utf16_resize(tkn16, (size_t) tkn_len * 2);
639
640         u_strncpy(tkn16->utf16, &(tokenizer->buf16->utf16)[tkn_start], 
641                   tkn_len);
642
643         tkn16->utf16_len = tkn_len;
644     }
645
646     return tkn_len;
647 }
648
649
650 int32_t icu_tokenizer_token_id(struct icu_tokenizer * tokenizer)
651 {
652     return tokenizer->token_id;
653 };
654
655 int32_t icu_tokenizer_token_start(struct icu_tokenizer * tokenizer)
656 {
657     return tokenizer->token_start;
658 };
659
660 int32_t icu_tokenizer_token_end(struct icu_tokenizer * tokenizer)
661 {
662     return tokenizer->token_end;
663 };
664
665 int32_t icu_tokenizer_token_length(struct icu_tokenizer * tokenizer)
666 {
667     return (tokenizer->token_end - tokenizer->token_start);
668 };
669
670 int32_t icu_tokenizer_token_count(struct icu_tokenizer * tokenizer)
671 {
672     return tokenizer->token_count;
673 };
674
675
676
677 struct icu_normalizer * icu_normalizer_create(const char *rules, char action,
678                                               UErrorCode *status)
679 {
680
681     struct icu_normalizer * normalizer
682         = (struct icu_normalizer *) malloc(sizeof(struct icu_normalizer));
683
684     normalizer->action = action;
685     normalizer->trans = 0;
686     normalizer->rules16 =  icu_buf_utf16_create(0);
687     icu_utf16_from_utf8_cstr(normalizer->rules16, rules, status);
688      
689     switch(normalizer->action) {    
690     case 'f':
691         normalizer->trans
692             = utrans_openU(normalizer->rules16->utf16, 
693                            normalizer->rules16->utf16_len,
694                            UTRANS_FORWARD,
695                            0, 0, 
696                            normalizer->parse_error, status);
697         // yaz_log(YLOG_LOG, "utrans_open %p", normalizer->trans);
698         break;
699     case 'r':
700         normalizer->trans
701             = utrans_openU(normalizer->rules16->utf16,
702                            normalizer->rules16->utf16_len,
703                            UTRANS_REVERSE ,
704                            0, 0,
705                            normalizer->parse_error, status);
706         // yaz_log(YLOG_LOG, "utrans_open %p", normalizer->trans);
707         break;
708     default:
709         *status = U_UNSUPPORTED_ERROR;
710         return 0;
711         break;
712     }
713     
714     if (U_SUCCESS(*status))
715         return normalizer;
716
717     // freeing if failed
718     icu_normalizer_destroy(normalizer);
719     return 0;
720 };
721
722
723 void icu_normalizer_destroy(struct icu_normalizer * normalizer){
724     if (normalizer) {
725         if (normalizer->rules16) 
726             icu_buf_utf16_destroy(normalizer->rules16);
727         if (normalizer->trans)
728         {
729             // yaz_log(YLOG_LOG, "utrans_close %p", normalizer->trans);
730             utrans_close(normalizer->trans);
731         }
732         free(normalizer);
733     }
734 };
735
736
737
738 int icu_normalizer_normalize(struct icu_normalizer * normalizer,
739                              struct icu_buf_utf16 * dest16,
740                              struct icu_buf_utf16 * src16,
741                              UErrorCode *status)
742 {
743     if (!normalizer || !normalizer->trans || !src16 || !dest16)
744         return 0;
745
746     if (!icu_buf_utf16_copy(dest16, src16))
747         return 0;
748
749     utrans_transUChars (normalizer->trans, 
750                         dest16->utf16, &(dest16->utf16_len),
751                         dest16->utf16_cap,
752                         0, &(src16->utf16_len), status);
753
754     if (U_FAILURE(*status)){
755         dest16->utf16[0] = (UChar) 0;
756         dest16->utf16_len = 0;
757     }
758     
759     return dest16->utf16_len;
760 }
761
762
763
764
765 struct icu_chain_step * icu_chain_step_create(struct icu_chain * chain,
766                                               enum icu_chain_step_type type,
767                                               const uint8_t * rule,
768                                               struct icu_buf_utf16 * buf16,
769                                               UErrorCode *status)
770 {
771     struct icu_chain_step * step = 0;
772     
773     if(!chain || !type || !rule)
774         return 0;
775
776     step = (struct icu_chain_step *) malloc(sizeof(struct icu_chain_step));
777
778     step->type = type;
779
780     step->buf16 = buf16;
781
782     // create auxilary objects
783     switch(step->type) {
784     case ICU_chain_step_type_display:
785         break;
786     case ICU_chain_step_type_index:
787         break;
788     case ICU_chain_step_type_sortkey:
789         break;
790     case ICU_chain_step_type_casemap:
791         step->u.casemap = icu_casemap_create((char *) chain->locale, 
792                                              (char) rule[0], status);
793         break;
794     case ICU_chain_step_type_normalize:
795         step->u.normalizer = icu_normalizer_create((char *) rule, 'f', status);
796         break;
797     case ICU_chain_step_type_tokenize:
798         step->u.tokenizer = icu_tokenizer_create((char *) chain->locale, 
799                                                  (char) rule[0], status);
800         break;
801     default:
802         break;
803     }
804
805     return step;
806 };
807
808
809 void icu_chain_step_destroy(struct icu_chain_step * step){
810     
811     if (!step)
812         return;
813
814     icu_chain_step_destroy(step->previous);
815
816     switch(step->type) {
817     case ICU_chain_step_type_display:
818         break;
819     case ICU_chain_step_type_index:
820         break;
821     case ICU_chain_step_type_sortkey:
822         break;
823     case ICU_chain_step_type_casemap:
824         icu_casemap_destroy(step->u.casemap);
825         icu_buf_utf16_destroy(step->buf16);
826         break;
827     case ICU_chain_step_type_normalize:
828         icu_normalizer_destroy(step->u.normalizer);
829         icu_buf_utf16_destroy(step->buf16);
830         break;
831     case ICU_chain_step_type_tokenize:
832         icu_tokenizer_destroy(step->u.tokenizer);
833         icu_buf_utf16_destroy(step->buf16);
834         break;
835     default:
836         break;
837     }
838     free(step);
839 };
840
841
842
843 struct icu_chain * icu_chain_create(const uint8_t * identifier,
844                                     const uint8_t * locale)
845 {
846
847     struct icu_chain * chain 
848         = (struct icu_chain *) malloc(sizeof(struct icu_chain));
849
850     strncpy((char *) chain->identifier, (const char *) identifier, 128);
851     chain->identifier[128 - 1] = '\0';
852     strncpy((char *) chain->locale, (const char *) locale, 16);    
853     chain->locale[16 - 1] = '\0';
854
855     chain->token_count = 0;
856
857     chain->display8 = icu_buf_utf8_create(0);
858     chain->norm8 = icu_buf_utf8_create(0);
859     chain->sort8 = icu_buf_utf8_create(0);
860
861     chain->src16 = icu_buf_utf16_create(0);
862
863     chain->steps = 0;
864
865     return chain;
866 };
867
868
869 void icu_chain_destroy(struct icu_chain * chain)
870 {
871     if (chain){
872         icu_buf_utf8_destroy(chain->display8);
873         icu_buf_utf8_destroy(chain->norm8);
874         icu_buf_utf8_destroy(chain->sort8);
875         
876         icu_buf_utf16_destroy(chain->src16);
877     
878         icu_chain_step_destroy(chain->steps);
879         free(chain);
880     }
881 };
882
883
884
885 struct icu_chain * icu_chain_xml_config(xmlNode *xml_node, 
886                                         UErrorCode * status){
887
888     xmlNode *node = 0;
889     struct icu_chain * chain = 0;
890    
891     if (!xml_node 
892         ||xml_node->type != XML_ELEMENT_NODE 
893         || strcmp((const char *) xml_node->name, "icu_chain"))
894
895         return 0;
896     
897     xmlChar *xml_id = xmlGetProp(xml_node, (xmlChar *) "id");
898     xmlChar *xml_locale = xmlGetProp(xml_node, (xmlChar *) "locale");
899
900     if (!xml_id || !strlen((const char *) xml_id) 
901         || !xml_locale || !strlen((const char *) xml_locale))
902         return 0;
903
904     chain = icu_chain_create((const uint8_t *) xml_id, 
905                              (const uint8_t *) xml_locale);
906     
907     xmlFree(xml_id);
908     xmlFree(xml_locale);
909     if (!chain)
910         return 0;
911         
912     for (node = xml_node->children; node; node = node->next)
913     {
914         if (node->type != XML_ELEMENT_NODE)
915             continue;
916
917         xmlChar *xml_rule = xmlGetProp(node, (xmlChar *) "rule");
918         struct icu_chain_step * step = 0;
919
920         if (!strcmp((const char *) node->name, 
921                     (const char *) "casemap")){
922             step = icu_chain_insert_step(chain, ICU_chain_step_type_casemap, 
923                                          (const uint8_t *) xml_rule, status);
924         }
925         else if (!strcmp((const char *) node->name,
926                          (const char *) "normalize")){
927             step = icu_chain_insert_step(chain, ICU_chain_step_type_normalize, 
928                                          (const uint8_t *) xml_rule, status);
929         }
930         else if (!strcmp((const char *) node->name,
931                          (const char *) "tokenize")){
932             step = icu_chain_insert_step(chain, ICU_chain_step_type_tokenize, 
933                                          (const uint8_t *) xml_rule, status);
934         }
935         else if (!strcmp((const char *) node->name,
936                          (const char *) "display")){
937             step = icu_chain_insert_step(chain, ICU_chain_step_type_display, 
938                                          (const uint8_t *) "", status);
939         }
940         else if (!strcmp((const char *) node->name,
941                          (const char *) "index")){
942             step = icu_chain_insert_step(chain, ICU_chain_step_type_index, 
943                                          (const uint8_t *) "", status);
944         }
945         else if (!strcmp((const char *) node->name,
946                          (const char *) "sortkey")){
947             step = icu_chain_insert_step(chain, ICU_chain_step_type_sortkey, 
948                                          (const uint8_t *) "", status);
949         }
950
951         xmlFree(xml_rule);
952         if (!step || U_FAILURE(*status)){
953             icu_chain_destroy(chain);
954             return 0;
955         }
956         
957
958     }
959
960     return chain;
961 };
962
963
964
965 struct icu_chain_step * icu_chain_insert_step(struct icu_chain * chain,
966                                               enum icu_chain_step_type type,
967                                               const uint8_t * rule,
968                                               UErrorCode *status)
969 {    
970     struct icu_chain_step * step = 0;
971     struct icu_buf_utf16 * src16 = 0;
972     struct icu_buf_utf16 * buf16 = 0;
973
974     if (!chain || !type || !rule)
975         return 0;
976
977     // assign utf16 src buffers as needed 
978     if (chain->steps && chain->steps->buf16)
979         src16 = chain->steps->buf16;
980     else if (chain->src16)
981         src16 = chain->src16;
982     else
983         return 0;
984
985     
986     // create utf16 destination buffers as needed, or
987     switch(type) {
988     case ICU_chain_step_type_display:
989         buf16 = src16;
990         break;
991     case ICU_chain_step_type_index:
992         buf16 = src16;
993         break;
994     case ICU_chain_step_type_sortkey:
995         buf16 = src16;
996         break;
997     case ICU_chain_step_type_casemap:
998         buf16 = icu_buf_utf16_create(0);
999         break;
1000     case ICU_chain_step_type_normalize:
1001         buf16 = icu_buf_utf16_create(0);
1002         break;
1003     case ICU_chain_step_type_tokenize:
1004         buf16 = icu_buf_utf16_create(0);
1005         break;
1006     default:
1007         break;
1008     }
1009
1010     // create actual chain step with this buffer
1011     step = icu_chain_step_create(chain, type, rule, buf16, status);
1012
1013     step->previous = chain->steps;
1014     chain->steps = step;
1015
1016     return step;
1017 };
1018
1019
1020 int icu_chain_step_next_token(struct icu_chain * chain,
1021                               struct icu_chain_step * step,
1022                               UErrorCode *status)
1023 {
1024     struct icu_buf_utf16 * src16 = 0;
1025     
1026     //printf("icu_chain_step_next_token %d\n", (int) step);
1027
1028     if (!chain || !chain->src16 || !step || !step->more_tokens)
1029         return 0;
1030
1031     // assign utf16 src buffers as neeed, advance in previous steps
1032     // tokens until non-zero token met, and setting stop condition
1033     if (step->previous){
1034         src16 = step->previous->buf16;
1035         if (step->need_new_token)
1036             //while (step->more_tokens &&  !src16->utf16_len)
1037                 step->more_tokens 
1038                     = icu_chain_step_next_token(chain, step->previous, status);
1039     }
1040     else { // first step can only work once on chain->src16 input buffer
1041         src16 = chain->src16;
1042         step->more_tokens = 1;
1043     }
1044
1045     // stop if nothing to process 
1046     // i.e new token source was not properly assigned
1047     if (!step->more_tokens || !src16) // || !src16->utf16_len 
1048         return 0;
1049
1050     //printf("icu_chain_step_next_token %d working\n", (int) step);
1051
1052
1053     // perform the work, eventually put this steps output in 
1054     // step->buf16 or the chains UTF8 output buffers 
1055     switch(step->type) {
1056     case ICU_chain_step_type_display:
1057         icu_utf16_to_utf8(chain->display8, src16, status);
1058         break;
1059     case ICU_chain_step_type_index:
1060         icu_utf16_to_utf8(chain->norm8, src16, status);
1061         break;
1062     case ICU_chain_step_type_sortkey:
1063         icu_utf16_to_utf8(chain->sort8, src16, status);
1064         break;
1065     case ICU_chain_step_type_casemap:
1066         icu_casemap_casemap(step->u.casemap,
1067                             step->buf16, src16, status);
1068         break;
1069     case ICU_chain_step_type_normalize:
1070         icu_normalizer_normalize(step->u.normalizer,
1071                                  step->buf16, src16, status);
1072         break;
1073     case ICU_chain_step_type_tokenize:
1074         // attach to new src16 token only first time during splitting
1075         if (step->need_new_token){
1076             icu_tokenizer_attach(step->u.tokenizer, src16, status);
1077             step->need_new_token = 0;
1078         }
1079         // splitting one src16 token into multiple buf16 tokens
1080         step->more_tokens
1081             = icu_tokenizer_next_token(step->u.tokenizer,
1082                                        step->buf16, status);
1083         // make sure to get new previous token if this one had been used up
1084         if (step->previous && !step->more_tokens){
1085             if (icu_chain_step_next_token(chain, step->previous, status)){
1086                 icu_tokenizer_attach(step->u.tokenizer, src16, status);
1087                 step->need_new_token = 0;   
1088                 step->more_tokens
1089                     = icu_tokenizer_next_token(step->u.tokenizer,
1090                                                step->buf16, status);
1091             }
1092         }
1093         if (0 == step->more_tokens)
1094             return 0;
1095         break;
1096     default:
1097         return 0;
1098         break;
1099     }
1100
1101
1102
1103     // stop further token processing if last step and 
1104     // new tokens are needed from previous (non-existing) step
1105     if (!step->previous && step->need_new_token)
1106         step->more_tokens = 0;
1107
1108     //printf("%d %d %d\n", 
1109     //       step->more_tokens, src16->utf16_len, step->buf16->utf16_len);
1110
1111
1112     if (U_FAILURE(*status))
1113         return 0;
1114
1115     return 1;
1116 };
1117
1118
1119
1120 int icu_chain_assign_cstr(struct icu_chain * chain,
1121                           const char * src8cstr, 
1122                           UErrorCode *status)
1123 {
1124     struct icu_chain_step * stp = 0; 
1125
1126     if (!chain || !src8cstr)
1127         return 0;
1128
1129     stp = chain->steps;
1130     
1131     // clear token count
1132     chain->token_count = 0;
1133
1134     // clear all steps stop states
1135
1136     while (stp){
1137         stp->more_tokens = 1;
1138         stp->need_new_token = 1;
1139         stp = stp->previous;
1140     }
1141     
1142     // finally convert UTF8 to UTF16 string
1143     icu_utf16_from_utf8_cstr(chain->src16, src8cstr, status);
1144             
1145     if (U_FAILURE(*status))
1146         return 0;
1147
1148     return 1;
1149 };
1150
1151
1152
1153 int icu_chain_next_token(struct icu_chain * chain,
1154                          UErrorCode *status)
1155 {
1156     int success = 0;
1157     
1158     if (!chain || !chain->steps)
1159         return 0;
1160
1161     success = icu_chain_step_next_token(chain, chain->steps, status);
1162     
1163     if (success){
1164         chain->token_count++;
1165         return chain->token_count;
1166     }
1167
1168     return 0;
1169 };
1170
1171 int icu_chain_get_token_count(struct icu_chain * chain)
1172 {
1173     if (!chain)
1174         return 0;
1175     
1176     return chain->token_count;
1177 };
1178
1179
1180
1181 const char * icu_chain_get_display(struct icu_chain * chain)
1182 {
1183     if (chain->display8)
1184         return icu_buf_utf8_to_cstr(chain->display8);
1185     
1186     return 0;
1187 };
1188
1189 const char * icu_chain_get_norm(struct icu_chain * chain)
1190 {
1191     if (chain->norm8)
1192         return icu_buf_utf8_to_cstr(chain->norm8);
1193     
1194     return 0;
1195 };
1196
1197 const char * icu_chain_get_sort(struct icu_chain * chain)
1198 {
1199     if (chain->sort8)
1200         return icu_buf_utf8_to_cstr(chain->sort8);
1201     
1202     return 0;
1203 };
1204
1205
1206
1207
1208 #endif // HAVE_ICU    
1209
1210
1211
1212
1213 /*
1214  * Local variables:
1215  * c-basic-offset: 4
1216  * indent-tabs-mode: nil
1217  * End:
1218  * vim: shiftwidth=4 tabstop=8 expandtab
1219  */