1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2006 Kouhei Sutou <kou@cozmixng.org>
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this program; if not, write to the
17  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  *  Boston, MA  02111-1307  USA
19  *
20  *  $Id: tomoe-dict-est.c 1493 2007-06-16 10:59:43Z ikezoe $
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif /* HAVE_CONFIG_H */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <glib.h>
31 #include <glib/gi18n-lib.h>
32 #include <gmodule.h>
33 
34 #include <estraier.h>
35 
36 #include <tomoe-module-impl.h>
37 #include <tomoe-dict.h>
38 #include <tomoe-candidate.h>
39 #include <tomoe-xml-parser.h>
40 #include <glib-utils.h>
41 
42 #define TOMOE_TYPE_DICT_EST            tomoe_type_dict_est
43 #define TOMOE_DICT_EST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TOMOE_TYPE_DICT_EST, TomoeDictEst))
44 #define TOMOE_DICT_EST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TOMOE_TYPE_DICT_EST, TomoeDictEstClass))
45 #define TOMOE_IS_DICT_EST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TOMOE_TYPE_DICT_EST))
46 #define TOMOE_IS_DICT_EST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TOMOE_TYPE_DICT_EST))
47 #define TOMOE_DICT_EST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), TOMOE_TYPE_DICT_EST, TomoeDictEstClass))
48 
49 enum {
50     PROP_0,
51     PROP_NAME,
52     PROP_DATABASE,
53     PROP_EDITABLE
54 };
55 
56 typedef struct _TomoeDictEst TomoeDictEst;
57 typedef struct _TomoeDictEstClass TomoeDictEstClass;
58 struct _TomoeDictEst
59 {
60     TomoeDict            object;
61     gchar               *name;
62     gchar               *database;
63 
64     gboolean             editable;
65 
66     ESTDB               *db;
67 
68     GHashTable          *cache;
69 };
70 
71 struct _TomoeDictEstClass
72 {
73     TomoeDictClass parent_class;
74 };
75 
76 typedef struct _TomoeDictSearchContext {
77     TomoeQuery *query;
78     GList *results;
79 } TomoeDictSearchContext;
80 
81 static GType tomoe_type_dict_est = 0;
82 static TomoeDictClass *parent_class;
83 
84 static GObject     *constructor               (GType                  type,
85                                                guint                  n_props,
86                                                GObjectConstructParam *props);
87 static void         dispose                   (GObject       *object);
88 static void         set_property              (GObject       *object,
89                                                guint         prop_id,
90                                                const GValue  *value,
91                                                GParamSpec    *pspec);
92 static void         get_property              (GObject       *object,
93                                                guint          prop_id,
94                                                GValue        *value,
95                                                GParamSpec    *pspec);
96 static const gchar *get_name                  (TomoeDict     *dict);
97 static gboolean     register_char             (TomoeDict     *dict,
98                                                TomoeChar     *chr);
99 static gboolean     unregister_char           (TomoeDict     *dict,
100                                                const gchar   *utf8);
101 static TomoeChar   *get_char                  (TomoeDict     *dict,
102                                                const gchar   *utf8);
103 static GList       *search                    (TomoeDict     *dict,
104                                                TomoeQuery    *query);
105 static gboolean     flush                     (TomoeDict     *dict);
106 static gboolean     is_editable               (TomoeDict     *dict);
107 static gboolean     is_available              (TomoeDict     *dict);
108 static gchar       *get_available_private_utf8 (TomoeDict    *dict);
109 static gboolean     tomoe_dict_est_open       (TomoeDictEst  *dict);
110 static gboolean     tomoe_dict_est_close      (TomoeDictEst  *dict);
111 
112 static void
class_init(TomoeDictEstClass * klass)113 class_init (TomoeDictEstClass *klass)
114 {
115     GObjectClass *gobject_class;
116     TomoeDictClass *dict_class;
117 
118     parent_class = g_type_class_peek_parent (klass);
119 
120     gobject_class = G_OBJECT_CLASS (klass);
121 
122     gobject_class->constructor  = constructor;
123     gobject_class->dispose      = dispose;
124     gobject_class->set_property = set_property;
125     gobject_class->get_property = get_property;
126 
127     dict_class = TOMOE_DICT_CLASS (klass);
128     dict_class->get_name        = get_name;
129     dict_class->register_char   = register_char;
130     dict_class->unregister_char = unregister_char;
131     dict_class->get_char        = get_char;
132     dict_class->search          = search;
133     dict_class->flush           = flush;
134     dict_class->is_editable     = is_editable;
135     dict_class->is_available    = is_available;
136     dict_class->get_available_private_utf8 = get_available_private_utf8;
137 
138     g_object_class_install_property (
139         gobject_class,
140         PROP_NAME,
141         g_param_spec_string (
142             "name",
143             "Name",
144             "The name of the dictionary",
145             NULL,
146             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
147     g_object_class_install_property (
148         gobject_class,
149         PROP_DATABASE,
150         g_param_spec_string (
151             "database",
152             "Database",
153             "The database name of Hyper Estraier",
154             NULL,
155             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
156     g_object_class_install_property(
157         gobject_class,
158         PROP_EDITABLE,
159         g_param_spec_boolean(
160             "editable",
161             "Editable",
162             "Editable flag",
163             FALSE,
164             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
165 }
166 
167 static void
init(TomoeDictEst * dict)168 init (TomoeDictEst *dict)
169 {
170     dict->name          = NULL;
171     dict->database      = NULL;
172     dict->db            = NULL;
173     dict->editable      = FALSE;
174     dict->cache         = g_hash_table_new_full(g_str_hash, g_str_equal,
175                                                 g_free, g_object_unref);
176 }
177 
178 static void
register_type(GTypeModule * type_module)179 register_type (GTypeModule *type_module)
180 {
181     static const GTypeInfo info =
182         {
183             sizeof (TomoeDictEstClass),
184             (GBaseInitFunc) NULL,
185             (GBaseFinalizeFunc) NULL,
186             (GClassInitFunc) class_init,
187             NULL,           /* class_finalize */
188             NULL,           /* class_data */
189             sizeof (TomoeDictEst),
190             0,
191             (GInstanceInitFunc) init,
192         };
193 
194     tomoe_type_dict_est = g_type_module_register_type (type_module,
195                                                        TOMOE_TYPE_DICT,
196                                                        "TomoeDictEst",
197                                                        &info, 0);
198 }
199 
200 G_MODULE_EXPORT GList *
TOMOE_MODULE_IMPL_INIT(GTypeModule * type_module)201 TOMOE_MODULE_IMPL_INIT (GTypeModule *type_module)
202 {
203     GList *registered_types = NULL;
204 
205     register_type (type_module);
206     if (tomoe_type_dict_est)
207         registered_types =
208             g_list_prepend (registered_types,
209                             (gchar *) g_type_name (tomoe_type_dict_est));
210 
211     return registered_types;
212 }
213 
214 G_MODULE_EXPORT void
TOMOE_MODULE_IMPL_EXIT(void)215 TOMOE_MODULE_IMPL_EXIT (void)
216 {
217 }
218 
219 G_MODULE_EXPORT GObject *
TOMOE_MODULE_IMPL_INSTANTIATE(const gchar * first_property,va_list var_args)220 TOMOE_MODULE_IMPL_INSTANTIATE (const gchar *first_property, va_list var_args)
221 {
222     return g_object_new_valist (TOMOE_TYPE_DICT_EST, first_property, var_args);
223 }
224 
225 G_MODULE_EXPORT gchar *
TOMOE_MODULE_IMPL_GET_LOG_DOMAIN(void)226 TOMOE_MODULE_IMPL_GET_LOG_DOMAIN (void)
227 {
228     return g_strdup (G_LOG_DOMAIN);
229 }
230 
231 static GObject *
constructor(GType type,guint n_props,GObjectConstructParam * props)232 constructor (GType type, guint n_props,
233              GObjectConstructParam *props)
234 {
235     GObject *object;
236     GObjectClass *klass = G_OBJECT_CLASS (parent_class);
237     TomoeDictEst *dict;
238 
239     object = klass->constructor (type, n_props, props);
240     dict = TOMOE_DICT_EST (object);
241 
242     tomoe_dict_est_open (dict);
243 
244     return object;
245 }
246 
247 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)248 set_property (GObject *object,
249               guint prop_id,
250               const GValue *value,
251               GParamSpec *pspec)
252 {
253     TomoeDictEst *dict = TOMOE_DICT_EST (object);
254 
255     switch (prop_id) {
256       case PROP_NAME:
257         dict->name = g_value_dup_string (value);
258         break;
259       case PROP_DATABASE:
260         dict->database = g_value_dup_string (value);
261         break;
262       case PROP_EDITABLE:
263         dict->editable = g_value_get_boolean (value);
264         break;
265       default:
266         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
267         break;
268     }
269 }
270 
271 
272 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)273 get_property (GObject *object,
274               guint prop_id,
275               GValue *value,
276               GParamSpec *pspec)
277 {
278     TomoeDictEst *dict = TOMOE_DICT_EST (object);
279 
280     switch (prop_id) {
281       case PROP_NAME:
282         g_value_set_string (value, dict->name);
283         break;
284       case PROP_DATABASE:
285         g_value_set_string (value, dict->database);
286         break;
287       case PROP_EDITABLE:
288         g_value_set_boolean (value, dict->editable);
289         break;
290       default:
291         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
292         break;
293     }
294 }
295 
296 static void
dispose(GObject * object)297 dispose (GObject *object)
298 {
299     TomoeDictEst *dict;
300 
301     dict = TOMOE_DICT_EST (object);
302 
303     tomoe_dict_est_close (dict);
304 
305     if (dict->name)
306         g_free (dict->name);
307     if (dict->database)
308         g_free (dict->database);
309     if (dict->cache)
310         g_hash_table_destroy (dict->cache);
311 
312     dict->name          = NULL;
313     dict->database      = NULL;
314     dict->cache         = NULL;
315 
316     G_OBJECT_CLASS (parent_class)->dispose (object);
317 }
318 
319 static const gchar*
get_name(TomoeDict * _dict)320 get_name (TomoeDict *_dict)
321 {
322     TomoeDictEst *dict = TOMOE_DICT_EST (_dict);
323     g_return_val_if_fail (TOMOE_IS_DICT_EST (dict), NULL);
324     return dict->name;
325 }
326 
327 static gboolean
register_char(TomoeDict * _dict,TomoeChar * chr)328 register_char (TomoeDict *_dict, TomoeChar *chr)
329 {
330     TomoeDictEst *dict = TOMOE_DICT_EST (_dict);
331     gboolean success = FALSE;
332     const gchar *original_value;
333     gchar *value;
334     gint n_strokes;
335     const GList *readings, *node;
336     ESTDOC *doc;
337 
338     g_return_val_if_fail (TOMOE_IS_DICT_EST (dict), success);
339     g_return_val_if_fail (chr, success);
340 
341     doc = est_doc_new ();
342 
343     original_value = tomoe_char_get_utf8 (chr);
344     if (original_value) {
345         value = g_strdup_printf ("font:%s", original_value);
346         est_doc_add_attr (doc, "@uri", value);
347         g_free (value);
348 
349         est_doc_add_attr (doc, "utf8", original_value);
350 
351         value = g_strdup_printf ("%d", g_utf8_get_char (original_value));
352         est_doc_add_attr (doc, "code-point", value);
353         g_free (value);
354     }
355 
356     n_strokes = tomoe_char_get_n_strokes (chr);
357     if (n_strokes < 0) {
358         TomoeWriting *writing;
359         writing = tomoe_char_get_writing (chr);
360         if (writing)
361             n_strokes = tomoe_writing_get_n_strokes (writing);
362     }
363 
364     if (n_strokes >= 0) {
365         value = g_strdup_printf ("%d", n_strokes);
366         est_doc_add_attr (doc, "n-strokes", value);
367         g_free (value);
368     }
369 
370     original_value = tomoe_char_get_variant (chr);
371     if (original_value) {
372         est_doc_add_attr (doc, "variant", original_value);
373     }
374 
375     readings = tomoe_char_get_readings (chr);
376     if (readings) {
377         GString *on_readings = NULL, *kun_readings = NULL, *all_readings = NULL;
378         all_readings = g_string_new (NULL);
379         for (node = readings; node; node = g_list_next (node)) {
380             TomoeReading *reading = TOMOE_READING (node->data);
381             const gchar *read_str = tomoe_reading_get_reading (reading);
382             switch (tomoe_reading_get_reading_type (reading)) {
383               case TOMOE_READING_JA_ON:
384                 if (on_readings)
385                     on_readings = g_string_append_c (on_readings, ' ');
386                 else
387                     on_readings = g_string_new (NULL);
388                 on_readings = g_string_append (on_readings, read_str);
389                 break;
390               case TOMOE_READING_JA_KUN:
391                 if (kun_readings)
392                     kun_readings = g_string_append_c (kun_readings, ' ');
393                 else
394                     kun_readings = g_string_new (NULL);
395                 kun_readings = g_string_append (kun_readings, read_str);
396                 break;
397               case TOMOE_READING_UNKNOWN:
398               case TOMOE_READING_INVALID:
399                 all_readings = g_string_append_c (all_readings, ' ');
400                 all_readings = g_string_append (all_readings, read_str);
401               default:
402                 break;
403             }
404         }
405 
406         if (on_readings) {
407             est_doc_add_attr (doc, "ja_on_readings", on_readings->str);
408             all_readings = g_string_append_c (all_readings, ' ');
409             all_readings = g_string_append (all_readings, on_readings->str);
410             g_string_free (on_readings, TRUE);
411         }
412         all_readings = g_string_append_c (all_readings, ' ');
413         if (kun_readings) {
414             est_doc_add_attr (doc, "ja_kun_readings", kun_readings->str);
415             all_readings = g_string_append_c (all_readings, ' ');
416             all_readings = g_string_append (all_readings, kun_readings->str);
417             g_string_free (kun_readings, TRUE);
418         }
419         est_doc_add_attr (doc, "all_readings", all_readings->str);
420         g_string_free (all_readings, TRUE);
421     }
422 
423     value = tomoe_char_to_xml (chr);
424     est_doc_add_hidden_text (doc, value);
425     g_free (value);
426 
427     success = est_db_put_doc (dict->db, doc, ESTPDCLEAN);
428     if (!success) {
429         g_warning ("put error: %s\n", est_err_msg (est_db_error (dict->db)));
430     }
431 
432     est_doc_delete (doc);
433 
434     return success;
435 }
436 
437 static gboolean
unregister_char(TomoeDict * _dict,const gchar * utf8)438 unregister_char (TomoeDict *_dict, const gchar *utf8)
439 {
440     TomoeDictEst *dict = TOMOE_DICT_EST (_dict);
441     gboolean success = FALSE;
442     ESTCOND *cond;
443     int i, *results, n_results;
444     gchar *expr;
445 
446     g_return_val_if_fail (TOMOE_IS_DICT_EST (dict), success);
447     g_return_val_if_fail (utf8 && *utf8 != '\0', success);
448 
449     cond = est_cond_new ();
450     expr = g_strdup_printf ("utf8 STREQ %s", utf8);
451     est_cond_add_attr (cond, expr);
452     g_free (expr);
453 
454     results = est_db_search (dict->db, cond, &n_results, NULL);
455 
456     for (i = 0; i < n_results; i++) {
457         gint id;
458         const gchar *utf8;
459 
460         id = results[i];
461 
462         utf8 = est_db_get_doc_attr (dict->db, id, "utf8");
463         if (utf8)
464             g_hash_table_remove (dict->cache, utf8);
465 
466         success = est_db_out_doc (dict->db, id, ESTODCLEAN);
467         if (!success) {
468             g_warning ("out error: %s\n",
469                        est_err_msg (est_db_error (dict->db)));
470             break;
471         }
472     }
473     g_free (results);
474     est_cond_delete (cond);
475 
476     return success;
477 }
478 
479 static TomoeChar *
retrieve_char_by_id(TomoeDictEst * dict,int id)480 retrieve_char_by_id (TomoeDictEst *dict, int id)
481 {
482     TomoeChar *chr = NULL;
483     ESTDOC *doc;
484     const gchar *utf8;
485 
486     utf8 = est_db_get_doc_attr (dict->db, id, "utf8");
487     if (!utf8)
488         return NULL;
489 
490     chr = g_hash_table_lookup (dict->cache, utf8);
491     if (chr)
492         return chr;
493 
494     doc = est_db_get_doc (dict->db, id, 0);
495     if (!doc)
496         return NULL;
497 
498     chr = tomoe_char_new_from_xml_data (est_doc_hidden_texts (doc), -1);
499     if (chr)
500         g_hash_table_insert (dict->cache, g_strdup (utf8), chr);
501 
502     est_doc_delete (doc);
503 
504     return chr;
505 }
506 
507 static TomoeChar *
get_char(TomoeDict * _dict,const gchar * utf8)508 get_char (TomoeDict *_dict, const gchar *utf8)
509 {
510     TomoeDictEst *dict = TOMOE_DICT_EST (_dict);
511     TomoeChar *chr = NULL;
512     ESTCOND *cond;
513     int i, *results, n_results;
514     gchar *expr;
515 
516     g_return_val_if_fail (TOMOE_IS_DICT_EST (dict), chr);
517     g_return_val_if_fail (utf8 && *utf8 != '\0', chr);
518 
519     cond = est_cond_new ();
520     expr = g_strdup_printf ("utf8 STREQ %s", utf8);
521     est_cond_add_attr (cond, expr);
522     g_free (expr);
523 
524     results = est_db_search (dict->db, cond, &n_results, NULL);
525     for (i = 0; i < n_results; i++) {
526         chr = retrieve_char_by_id (dict, results[i]);
527         if (chr) break;
528     }
529     g_free (results);
530     est_cond_delete (cond);
531 
532     if (chr)
533         g_object_ref (chr);
534 
535     return chr;
536 }
537 
538 static void
append_search_cond_utf8(TomoeDictEst * dict,ESTCOND * cond,GString * phrase,const gchar * utf8)539 append_search_cond_utf8 (TomoeDictEst *dict, ESTCOND *cond, GString *phrase,
540                          const gchar *utf8)
541 {
542     if (utf8) {
543         gchar *escaped_utf8;
544 
545         escaped_utf8 = g_markup_escape_text (utf8, -1);
546         g_string_append_printf (phrase, " <utf8>%s</utf8>", escaped_utf8);
547         g_free (escaped_utf8);
548     }
549 }
550 
551 static void
append_search_cond_variant(TomoeDictEst * dict,ESTCOND * cond,GString * phrase,const gchar * variant)552 append_search_cond_variant (TomoeDictEst *dict, ESTCOND *cond, GString *phrase,
553                             const gchar *variant)
554 {
555     if (variant) {
556         gchar *escaped_variant;
557 
558         escaped_variant = g_markup_escape_text (variant, -1);
559         g_string_append_printf (phrase,
560                                 " <variant>%s</variant>", escaped_variant);
561         g_free (escaped_variant);
562     }
563 }
564 
565 static void
append_search_cond_n_strokes(TomoeDictEst * dict,ESTCOND * cond,GString * phrase,gint min_n_strokes,gint max_n_strokes)566 append_search_cond_n_strokes (TomoeDictEst *dict, ESTCOND *cond,
567                               GString *phrase,
568                               gint min_n_strokes,
569                               gint max_n_strokes)
570 {
571     gchar *attr;
572 
573     if (min_n_strokes >= 0) {
574         attr = g_strdup_printf ("n-strokes NUMGE %d", min_n_strokes);
575         est_cond_add_attr (cond, attr);
576         g_free (attr);
577     }
578 
579     if (max_n_strokes >= 0) {
580         attr = g_strdup_printf ("n-strokes NUMLE %d", max_n_strokes);
581         est_cond_add_attr (cond, attr);
582         g_free (attr);
583     }
584 }
585 
586 static void
append_search_cond_readings(TomoeDictEst * dict,ESTCOND * cond,GString * phrase,const GList * readings)587 append_search_cond_readings (TomoeDictEst *dict, ESTCOND *cond, GString *phrase,
588                              const GList *readings)
589 {
590     GList *node;
591     for (node = (GList *)readings; node; node = g_list_next (node)) {
592         TomoeReading *reading = TOMOE_READING (node->data);
593         const gchar *read_str = tomoe_reading_get_reading (reading);
594         gchar *expr;
595 
596         switch (tomoe_reading_get_reading_type (reading)) {
597 #if 0
598           case TOMOE_READING_JA_ON:
599             expr = g_strdup_printf ("ja_on_readings STRINC %s", read_str);
600             est_cond_add_attr (cond, expr);
601             g_free (expr);
602             expr = g_strdup_printf ("all_readings STRINC %s", read_str);
603             est_cond_add_attr (cond, expr);
604             g_free (expr);
605             break;
606           case TOMOE_READING_JA_KUN:
607             expr = g_strdup_printf ("ja_kun_readings STRINC %s", read_str);
608             est_cond_add_attr (cond, expr);
609             g_free (expr);
610             expr = g_strdup_printf ("all_readings STRINC %s", read_str);
611             est_cond_add_attr (cond, expr);
612             g_free (expr);
613             break;
614 #endif
615           case TOMOE_READING_JA_ON:
616           case TOMOE_READING_JA_KUN:
617           case TOMOE_READING_INVALID:
618           case TOMOE_READING_UNKNOWN:
619           default:
620             expr = g_strdup_printf ("all_readings STRRX (\\s|^)%s", read_str);
621             est_cond_add_attr (cond, expr);
622             g_free (expr);
623             break;
624         }
625     }
626 }
627 
628 static void
append_search_cond_radicals(TomoeDictEst * dict,ESTCOND * cond,GString * phrase,const GList * radicals)629 append_search_cond_radicals (TomoeDictEst *dict, ESTCOND *cond, GString *phrase,
630                              const GList *radicals)
631 {
632     GList *node;
633     for (node = (GList *)radicals; node; node = g_list_next (node)) {
634         const gchar *radical = node->data;
635         gchar *escaped_radical;
636 
637         escaped_radical = g_markup_escape_text (radical, -1);
638         g_string_append_printf (phrase,
639                                 " <radical>%s</radical>",
640                                 escaped_radical);
641         g_free (escaped_radical);
642     }
643 }
644 
645 
646 static GList *
search(TomoeDict * _dict,TomoeQuery * query)647 search (TomoeDict *_dict, TomoeQuery *query)
648 {
649     TomoeDictEst *dict = TOMOE_DICT_EST (_dict);
650     GList *candidates = NULL;
651     ESTCOND *cond;
652     GString *phrase;
653     int i, *results, n_results;
654 
655     g_return_val_if_fail (TOMOE_IS_DICT_EST (dict), candidates);
656 
657     cond = est_cond_new ();
658     est_cond_set_order (cond, "utf8 STRA");
659 
660     phrase = g_string_new ("");
661 
662     if (tomoe_query_is_empty (query)) {
663         g_string_assign (phrase, "[UVSET]");
664     } else {
665         append_search_cond_utf8 (dict, cond, phrase,
666                                  tomoe_query_get_utf8 (query));
667         append_search_cond_variant (dict, cond, phrase,
668                                     tomoe_query_get_variant (query));
669         append_search_cond_n_strokes (dict, cond, phrase,
670                                       tomoe_query_get_min_n_strokes (query),
671                                       tomoe_query_get_max_n_strokes (query));
672         append_search_cond_readings (dict, cond, phrase,
673                                      tomoe_query_get_readings (query));
674         append_search_cond_radicals (dict, cond, phrase,
675                                      tomoe_query_get_radicals (query));
676     }
677 
678     if (phrase->len > 0)
679         est_cond_set_phrase (cond, phrase->str);
680     g_string_free (phrase, TRUE);
681 
682     results = est_db_search (dict->db, cond, &n_results, NULL);
683     for (i = 0; i < n_results; i++) {
684         TomoeChar *chr;
685 
686         chr = retrieve_char_by_id (dict, results[i]);
687         if (chr)
688             candidates = g_list_prepend (candidates,
689                                          tomoe_candidate_new (chr));
690     }
691     g_free (results);
692     est_cond_delete (cond);
693 
694     return candidates;
695 }
696 
697 static gboolean
flush(TomoeDict * _dict)698 flush (TomoeDict *_dict)
699 {
700     TomoeDictEst *dict = TOMOE_DICT_EST (_dict);
701 
702     return est_db_sync (dict->db);
703 }
704 
705 static gboolean
is_editable(TomoeDict * _dict)706 is_editable (TomoeDict *_dict)
707 {
708     TomoeDictEst *dict = TOMOE_DICT_EST (_dict);
709 
710     g_return_val_if_fail (TOMOE_IS_DICT_EST (dict), FALSE);
711 
712     return dict->editable;
713 }
714 
715 static gboolean
is_available(TomoeDict * _dict)716 is_available (TomoeDict *_dict)
717 {
718     TomoeDictEst *dict = TOMOE_DICT_EST (_dict);
719 
720     g_return_val_if_fail (TOMOE_IS_DICT_EST (dict), FALSE);
721 
722     return dict->db != NULL;
723 }
724 
725 static gchar *
get_available_private_utf8(TomoeDict * _dict)726 get_available_private_utf8 (TomoeDict *_dict)
727 {
728     TomoeDictEst *dict = TOMOE_DICT_EST (_dict);
729     ESTCOND *cond;
730     gchar *expr;
731     int i, *results, n_results;
732     gunichar result_ucs = TOMOE_CHAR_PRIVATE_USE_AREA_START;
733 
734     g_return_val_if_fail (TOMOE_IS_DICT_EST (dict), FALSE);
735 
736     cond = est_cond_new ();
737     est_cond_set_order (cond, "utf8 STRD");
738     est_cond_set_max (cond, 1);
739 
740     expr = g_strdup_printf ("code-point NUMGE %d",
741                             TOMOE_CHAR_PRIVATE_USE_AREA_START);
742     est_cond_add_attr (cond, expr);
743     g_free (expr);
744 
745     expr = g_strdup_printf ("code-point NUMLE %d",
746                             TOMOE_CHAR_PRIVATE_USE_AREA_END);
747     est_cond_add_attr (cond, expr);
748     g_free (expr);
749 
750     results = est_db_search (dict->db, cond, &n_results, NULL);
751     for (i = 0; i < n_results; i++) {
752         TomoeChar *chr;
753         gunichar ucs;
754 
755         chr = retrieve_char_by_id (dict, results[i]);
756         ucs = g_utf8_get_char (tomoe_char_get_utf8 (chr));
757         if (ucs >= TOMOE_CHAR_PRIVATE_USE_AREA_START) {
758             if (ucs >= TOMOE_CHAR_PRIVATE_USE_AREA_END) {
759                 result_ucs = 0;
760             } else {
761                 result_ucs = ucs + 1;
762             }
763         }
764     }
765     g_free (results);
766     est_cond_delete (cond);
767 
768     if (result_ucs >= TOMOE_CHAR_PRIVATE_USE_AREA_START) {
769         gint result_len;
770         gchar *result;
771 
772         result_len = g_unichar_to_utf8 (result_ucs, NULL);
773         result = g_new (gchar, result_len + 1);
774         g_unichar_to_utf8 (result_ucs, result);
775         result[result_len] = '\0';
776         return result;
777     } else {
778         return NULL;
779     }
780 }
781 
782 static gboolean
tomoe_dict_est_open(TomoeDictEst * dict)783 tomoe_dict_est_open (TomoeDictEst *dict)
784 {
785     gboolean success = TRUE;
786     int option, ecode;
787 
788     option = dict->editable ? ESTDBWRITER | ESTDBCREAT : ESTDBREADER;
789     dict->db = est_db_open (dict->database, option, &ecode);
790 
791     if (!dict->db) {
792         g_warning ("open error: %s\n", est_err_msg (ecode));
793         success = FALSE;
794     }
795 
796     return success;
797 }
798 
799 static gboolean
tomoe_dict_est_close(TomoeDictEst * dict)800 tomoe_dict_est_close (TomoeDictEst *dict)
801 {
802     gboolean success;
803     int ecode;
804 
805     success = est_db_close (dict->db, &ecode);
806     if (!success) {
807         g_warning ("close error: %s\n", est_err_msg (ecode));
808     }
809 
810     dict->db = NULL;
811 
812     return success;
813 }
814 
815 /*
816 vi:ts=4:nowrap:ai:expandtab
817 */
818