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