1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2005 Takuro Ashie <ashie@homa.ne.jp>
4 * Copyright (C) 2006 Juernjakob Harder <juernjakob.harder@gmail.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307 USA
20 *
21 * $Id: tomoe-char.c 1488 2007-06-16 01:16:23Z ikezoe $
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif /* HAVE_CONFIG_H */
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <glib.h>
31 #include <glib/gi18n-lib.h>
32
33 #include "tomoe-char.h"
34 #include "tomoe-dict.h"
35 #include "tomoe-xml-parser.h"
36 #include "glib-utils.h"
37
38 #define TOMOE_CHAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TOMOE_TYPE_CHAR, TomoeCharPrivate))
39
40 typedef struct _TomoeCharPrivate TomoeCharPrivate;
41 struct _TomoeCharPrivate
42 {
43 gchar *utf8;
44 gint n_strokes;
45 GList *readings;
46 GList *radicals;
47 TomoeWriting *writing;
48 gchar *variant;
49 GHashTable *meta_data;
50 };
51
52 enum
53 {
54 PROP_0,
55 PROP_UTF8,
56 PROP_N_STROKES,
57 PROP_WRITING,
58 PROP_VARIANT
59 };
60
61 G_DEFINE_TYPE (TomoeChar, tomoe_char, G_TYPE_OBJECT)
62
63 static void dispose (GObject *object);
64 static void set_property (GObject *object,
65 guint prop_id,
66 const GValue *value,
67 GParamSpec *pspec);
68 static void get_property (GObject *object,
69 guint prop_id,
70 GValue *value,
71 GParamSpec *pspec);
72
73 static void
tomoe_char_class_init(TomoeCharClass * klass)74 tomoe_char_class_init (TomoeCharClass *klass)
75 {
76 GObjectClass *gobject_class;
77 GParamSpec *spec;
78
79 gobject_class = G_OBJECT_CLASS (klass);
80
81 gobject_class->dispose = dispose;
82 gobject_class->set_property = set_property;
83 gobject_class->get_property = get_property;
84
85 spec = g_param_spec_string ("utf8",
86 N_("UTF8"),
87 N_("UTF8 encoding of the character."),
88 NULL,
89 G_PARAM_READABLE | G_PARAM_WRITABLE);
90 g_object_class_install_property (gobject_class, PROP_UTF8, spec);
91
92 spec = g_param_spec_int ("n_strokes",
93 N_("Number of strokes"),
94 N_("Number of strokes of the character."),
95 -1, G_MAXINT32, -1,
96 G_PARAM_READABLE | G_PARAM_WRITABLE);
97 g_object_class_install_property (gobject_class, PROP_N_STROKES, spec);
98
99 spec = g_param_spec_object ("writing",
100 N_("Writing"),
101 N_("Writing of the character."),
102 TOMOE_TYPE_WRITING,
103 G_PARAM_READABLE | G_PARAM_WRITABLE);
104 g_object_class_install_property (gobject_class, PROP_WRITING, spec);
105
106 spec = g_param_spec_string ("variant",
107 N_("Variant"),
108 N_("Variant of the character."),
109 NULL,
110 G_PARAM_READABLE | G_PARAM_WRITABLE);
111 g_object_class_install_property (gobject_class, PROP_VARIANT, spec);
112
113 g_type_class_add_private (gobject_class, sizeof (TomoeCharPrivate));
114 }
115
116 static void
tomoe_char_init(TomoeChar * chr)117 tomoe_char_init (TomoeChar *chr)
118 {
119 TomoeCharPrivate *priv = TOMOE_CHAR_GET_PRIVATE (chr);
120 priv->utf8 = NULL;
121 priv->n_strokes = -1;
122 priv->readings = NULL;
123 priv->radicals = NULL;
124 priv->writing = NULL;
125 priv->variant = NULL;
126 priv->meta_data = g_hash_table_new_full(g_str_hash, g_str_equal,
127 g_free, g_free);
128 }
129
130 /**
131 * tomoe_char_new:
132 *
133 * Create a new #TomoeChar.
134 *
135 * Return value: a new #TomoeChar
136 */
137 TomoeChar*
tomoe_char_new(void)138 tomoe_char_new (void)
139 {
140 return g_object_new(TOMOE_TYPE_CHAR, NULL);
141 }
142
143 TomoeChar*
tomoe_char_new_from_xml_data(const gchar * data,gssize len)144 tomoe_char_new_from_xml_data (const gchar *data, gssize len)
145 {
146 return _tomoe_xml_parser_parse_char_data (data, len);
147 }
148
149 static void
copy_meta_data(gpointer key,gpointer value,gpointer user_data)150 copy_meta_data (gpointer key, gpointer value, gpointer user_data)
151 {
152 TomoeChar *chr = user_data;
153 gchar *meta_key = key;
154 gchar *meta_value = value;
155
156 tomoe_char_register_meta_data (chr, meta_key, meta_value);
157 }
158
159 TomoeChar*
tomoe_char_dup(TomoeChar * chr)160 tomoe_char_dup (TomoeChar *chr)
161 {
162 TomoeChar *new_chr;
163 TomoeCharPrivate *priv;
164
165 new_chr = tomoe_char_new ();
166 priv = TOMOE_CHAR_GET_PRIVATE (chr);
167
168 tomoe_char_set_utf8 (new_chr, priv->utf8);
169 tomoe_char_set_n_strokes (new_chr, priv->n_strokes);
170
171 if (priv->writing) {
172 TomoeWriting *new_writing = tomoe_writing_dup (priv->writing);
173 tomoe_char_set_writing (new_chr, new_writing);
174 g_object_unref (new_writing);
175 }
176
177 if (priv->variant)
178 tomoe_char_set_variant (new_chr, priv->variant);
179
180 if (priv->readings) {
181 GList *node;
182 for (node = g_list_last(priv->readings); node; node = g_list_previous (node)) {
183 TomoeReading *new_reading = tomoe_reading_dup (TOMOE_READING(node->data));
184 tomoe_char_add_reading (new_chr, new_reading);
185 g_object_unref (new_reading);
186 }
187 }
188
189 if (priv->radicals) {
190 GList *node;
191 for (node = g_list_last(priv->radicals); node; node = g_list_previous (node)) {
192 tomoe_char_add_radical (new_chr, node->data);
193 }
194 }
195
196 if (priv->meta_data) {
197 tomoe_char_meta_data_foreach (chr, (GHFunc) copy_meta_data, new_chr);
198 }
199
200 return new_chr;
201 }
202
203 static void
dispose(GObject * object)204 dispose (GObject *object)
205 {
206 TomoeCharPrivate *priv = TOMOE_CHAR_GET_PRIVATE (object);
207
208 if (priv->utf8)
209 g_free (priv->utf8);
210 if (priv->readings) {
211 g_list_foreach (priv->readings, (GFunc)g_object_unref, NULL);
212 g_list_free (priv->readings);
213 }
214 if (priv->radicals) {
215 g_list_foreach (priv->radicals, (GFunc)g_free, NULL);
216 g_list_free (priv->radicals);
217 }
218 if (priv->writing)
219 g_object_unref (G_OBJECT (priv->writing));
220 if (priv->variant)
221 g_free (priv->variant);
222 if (priv->meta_data)
223 g_hash_table_destroy (priv->meta_data);
224
225 priv->utf8 = NULL;
226 priv->n_strokes = -1;
227 priv->readings = NULL;
228 priv->radicals = NULL;
229 priv->writing = NULL;
230 priv->variant = NULL;
231 priv->meta_data = NULL;
232
233 G_OBJECT_CLASS (tomoe_char_parent_class)->dispose (object);
234 }
235
236 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)237 set_property (GObject *object,
238 guint prop_id,
239 const GValue *value,
240 GParamSpec *pspec)
241 {
242 TomoeChar *chr;
243
244 chr = TOMOE_CHAR(object);
245 switch (prop_id) {
246 case PROP_UTF8:
247 tomoe_char_set_utf8 (chr, g_value_get_string (value));
248 break;
249 case PROP_N_STROKES:
250 tomoe_char_set_n_strokes (chr, g_value_get_int (value));
251 break;
252 case PROP_WRITING:
253 tomoe_char_set_writing (chr, g_value_get_object (value));
254 break;
255 case PROP_VARIANT:
256 tomoe_char_set_variant (chr, g_value_get_string (value));
257 break;
258 default:
259 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
260 break;
261 }
262 }
263
264 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)265 get_property (GObject *object,
266 guint prop_id,
267 GValue *value,
268 GParamSpec *pspec)
269 {
270 TomoeChar *chr;
271 TomoeCharPrivate *priv;
272
273 chr = TOMOE_CHAR (object);
274 priv = TOMOE_CHAR_GET_PRIVATE (chr);
275
276 switch (prop_id) {
277 case PROP_UTF8:
278 g_value_set_string (value, priv->utf8);
279 break;
280 case PROP_N_STROKES:
281 g_value_set_int (value, priv->n_strokes);
282 break;
283 case PROP_WRITING:
284 g_value_set_object (value, priv->writing);
285 break;
286 case PROP_VARIANT:
287 g_value_set_string (value, priv->variant);
288 break;
289 default:
290 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
291 break;
292 }
293 }
294
295 const gchar *
tomoe_char_get_utf8(TomoeChar * chr)296 tomoe_char_get_utf8 (TomoeChar *chr)
297 {
298 TomoeCharPrivate *priv;
299
300 g_return_val_if_fail (TOMOE_IS_CHAR (chr), NULL);
301
302 priv = TOMOE_CHAR_GET_PRIVATE (chr);
303 return priv->utf8;
304 }
305
306 void
tomoe_char_set_utf8(TomoeChar * chr,const gchar * utf8)307 tomoe_char_set_utf8 (TomoeChar *chr, const gchar *utf8)
308 {
309 TomoeCharPrivate *priv;
310
311 g_return_if_fail (TOMOE_IS_CHAR (chr));
312
313 priv = TOMOE_CHAR_GET_PRIVATE (chr);
314 if (priv->utf8)
315 g_free (priv->utf8);
316 priv->utf8 = utf8 ? g_strdup (utf8) : NULL;
317 }
318
319 gint
tomoe_char_get_n_strokes(TomoeChar * chr)320 tomoe_char_get_n_strokes (TomoeChar *chr)
321 {
322 TomoeCharPrivate *priv;
323
324 g_return_val_if_fail (TOMOE_IS_CHAR (chr), 0);
325
326 priv = TOMOE_CHAR_GET_PRIVATE (chr);
327 return priv->n_strokes;
328 }
329
330 void
tomoe_char_set_n_strokes(TomoeChar * chr,gint n_strokes)331 tomoe_char_set_n_strokes (TomoeChar *chr, gint n_strokes)
332 {
333 TomoeCharPrivate *priv;
334
335 g_return_if_fail (TOMOE_IS_CHAR (chr));
336
337 priv = TOMOE_CHAR_GET_PRIVATE (chr);
338 priv->n_strokes = n_strokes;
339 }
340
341 const GList *
tomoe_char_get_readings(TomoeChar * chr)342 tomoe_char_get_readings (TomoeChar* chr)
343 {
344 TomoeCharPrivate *priv;
345
346 g_return_val_if_fail (TOMOE_IS_CHAR (chr), NULL);
347
348 priv = TOMOE_CHAR_GET_PRIVATE (chr);
349 return priv->readings;
350 }
351
352 void
tomoe_char_add_reading(TomoeChar * chr,TomoeReading * reading)353 tomoe_char_add_reading (TomoeChar* chr, TomoeReading *reading)
354 {
355 TomoeCharPrivate *priv;
356
357 g_return_if_fail (TOMOE_IS_CHAR (chr));
358
359 priv = TOMOE_CHAR_GET_PRIVATE (chr);
360
361 priv->readings = g_list_prepend (priv->readings, g_object_ref (reading));
362 }
363
364 const GList *
tomoe_char_get_radicals(TomoeChar * chr)365 tomoe_char_get_radicals (TomoeChar* chr)
366 {
367 TomoeCharPrivate *priv;
368
369 g_return_val_if_fail (TOMOE_IS_CHAR (chr), NULL);
370
371 priv = TOMOE_CHAR_GET_PRIVATE (chr);
372 return priv->radicals;
373 }
374
375 void
tomoe_char_add_radical(TomoeChar * chr,const gchar * radical)376 tomoe_char_add_radical (TomoeChar* chr, const gchar *radical)
377 {
378 TomoeCharPrivate *priv;
379
380 g_return_if_fail (TOMOE_IS_CHAR (chr));
381 g_return_if_fail (radical && radical[0] != '\0');
382
383 priv = TOMOE_CHAR_GET_PRIVATE (chr);
384
385 priv->radicals = g_list_prepend (priv->radicals, g_strdup (radical));
386 }
387
388 TomoeWriting *
tomoe_char_get_writing(TomoeChar * chr)389 tomoe_char_get_writing (TomoeChar *chr)
390 {
391 TomoeCharPrivate *priv;
392
393 g_return_val_if_fail (TOMOE_IS_CHAR (chr), NULL);
394
395 priv = TOMOE_CHAR_GET_PRIVATE (chr);
396
397 return priv->writing;
398 }
399
400 void
tomoe_char_set_writing(TomoeChar * chr,TomoeWriting * writing)401 tomoe_char_set_writing (TomoeChar *chr, TomoeWriting *writing)
402 {
403 TomoeCharPrivate *priv;
404
405 g_return_if_fail (TOMOE_IS_CHAR (chr));
406
407 priv = TOMOE_CHAR_GET_PRIVATE (chr);
408
409 if (priv->writing)
410 g_object_unref (G_OBJECT (priv->writing));
411 priv->writing = g_object_ref (writing);
412 }
413
414 const gchar *
tomoe_char_get_variant(TomoeChar * chr)415 tomoe_char_get_variant (TomoeChar *chr)
416 {
417 TomoeCharPrivate *priv;
418
419 g_return_val_if_fail (TOMOE_IS_CHAR (chr), NULL);
420
421 priv = TOMOE_CHAR_GET_PRIVATE (chr);
422
423 return priv->variant;
424 }
425
426 void
tomoe_char_set_variant(TomoeChar * chr,const gchar * variant)427 tomoe_char_set_variant (TomoeChar *chr, const gchar *variant)
428 {
429 TomoeCharPrivate *priv;
430
431 g_return_if_fail (TOMOE_IS_CHAR (chr));
432
433 priv = TOMOE_CHAR_GET_PRIVATE (chr);
434
435 if (priv->variant)
436 g_free (priv->variant);
437 priv->variant = variant ? g_strdup (variant) : NULL;
438 }
439
440 void
tomoe_char_register_meta_data(TomoeChar * chr,const gchar * key,const gchar * value)441 tomoe_char_register_meta_data (TomoeChar *chr, const gchar *key,
442 const gchar *value)
443 {
444 TomoeCharPrivate *priv;
445 g_return_if_fail (chr);
446 g_return_if_fail (key);
447 g_return_if_fail (value);
448
449 priv = TOMOE_CHAR_GET_PRIVATE (chr);
450 g_hash_table_insert (priv->meta_data, g_strdup (key), g_strdup (value));
451 }
452
453 const gchar*
tomoe_char_get_meta_data(TomoeChar * chr,const gchar * key)454 tomoe_char_get_meta_data (TomoeChar* chr, const gchar *key)
455 {
456 TomoeCharPrivate *priv;
457 g_return_val_if_fail (chr, NULL);
458 g_return_val_if_fail (key, NULL);
459
460 priv = TOMOE_CHAR_GET_PRIVATE (chr);
461 return g_hash_table_lookup (priv->meta_data, key);
462 }
463
464 gboolean
tomoe_char_has_meta_data(TomoeChar * chr)465 tomoe_char_has_meta_data (TomoeChar *chr)
466 {
467 TomoeCharPrivate *priv;
468 g_return_val_if_fail (chr, FALSE);
469
470 priv = TOMOE_CHAR_GET_PRIVATE (chr);
471 return g_hash_table_size (priv->meta_data) > 0;
472 }
473
474 void
tomoe_char_meta_data_foreach(TomoeChar * chr,GHFunc func,gpointer user_data)475 tomoe_char_meta_data_foreach (TomoeChar* chr, GHFunc func, gpointer user_data)
476 {
477 TomoeCharPrivate *priv;
478 g_return_if_fail (chr);
479
480 priv = TOMOE_CHAR_GET_PRIVATE (chr);
481 g_hash_table_foreach (priv->meta_data, func, user_data);
482 }
483
484 /**
485 * tomoe_char_compare:
486 * @a: a TomoeChar object.
487 * @b: a TomoeChar object to compare with.
488 *
489 * Compare to TomoeChar objects with its own utf8 character.
490 *
491 * Return value: -1 a < b, 0 a= b, 1 a > b
492 */
493 gint
tomoe_char_compare(const TomoeChar * a,const TomoeChar * b)494 tomoe_char_compare (const TomoeChar *a, const TomoeChar *b)
495 {
496 TomoeCharPrivate *priv_a, *priv_b;
497
498 if (!a || !b) return 0;
499
500 priv_a = TOMOE_CHAR_GET_PRIVATE (a);
501 priv_b = TOMOE_CHAR_GET_PRIVATE (b);
502 if (!priv_a || !priv_b) return 0;
503
504 if (!priv_a->utf8 || !priv_b->utf8) return 0;
505 return strcmp (priv_a->utf8, priv_b->utf8);
506 }
507
508
509 static void
tomoe_char_to_xml_utf8(TomoeChar * chr,TomoeCharPrivate * priv,GString * output)510 tomoe_char_to_xml_utf8 (TomoeChar *chr, TomoeCharPrivate *priv,
511 GString *output)
512 {
513 gchar *xml;
514
515 if (!priv->utf8) return;
516
517 xml = g_markup_printf_escaped (" <utf8>%s</utf8>\n", priv->utf8);
518 g_string_append (output, xml);
519 g_free (xml);
520 }
521
522 static void
tomoe_char_to_xml_variant(TomoeChar * chr,TomoeCharPrivate * priv,GString * output)523 tomoe_char_to_xml_variant (TomoeChar *chr, TomoeCharPrivate *priv,
524 GString *output)
525 {
526 gchar *xml;
527
528 if (!priv->variant) return;
529
530 xml = g_markup_printf_escaped (" <variant>%s</variant>\n",
531 priv->variant);
532 g_string_append (output, xml);
533 g_free (xml);
534 }
535
536 static void
tomoe_char_to_xml_readings(TomoeChar * chr,TomoeCharPrivate * priv,GString * output)537 tomoe_char_to_xml_readings (TomoeChar *chr, TomoeCharPrivate *priv,
538 GString *output)
539 {
540 GList *node;
541
542 if (!priv->readings) return;
543
544 g_string_append (output, " <readings>\n");
545 for (node = g_list_last (priv->readings); node; node = g_list_previous (node)) {
546 TomoeReading *reading = node->data;
547 gchar *xml;
548
549 xml = tomoe_reading_to_xml (reading);
550 if (xml) {
551 g_string_append (output, xml);
552 g_free (xml);
553 }
554 }
555 g_string_append (output, " </readings>\n");
556 }
557
558 static void
tomoe_char_to_xml_radicals(TomoeChar * chr,TomoeCharPrivate * priv,GString * output)559 tomoe_char_to_xml_radicals (TomoeChar *chr, TomoeCharPrivate *priv,
560 GString *output)
561 {
562 GList *node;
563
564 if (!priv->radicals) return;
565
566 g_string_append (output, " <radicals>\n");
567 for (node = priv->radicals; node; node = g_list_next (node)) {
568 const gchar *radical = node->data;
569 gchar *xml;
570
571 xml = g_markup_printf_escaped (radical, -1);
572 g_string_append_printf (output, " <radical>%s</radical>\n", xml);
573 g_free (xml);
574 }
575 g_string_append (output, " </radicals>\n");
576 }
577
578 static void
tomoe_char_to_xml_writing(TomoeChar * chr,TomoeCharPrivate * priv,GString * output)579 tomoe_char_to_xml_writing (TomoeChar *chr, TomoeCharPrivate *priv,
580 GString *output)
581 {
582 gchar *xml;
583
584 if (!priv->writing) return;
585
586 xml = tomoe_writing_to_xml (priv->writing);
587
588 if (xml && xml[0] != '\0') {
589 g_string_append (output, xml);
590 g_free (xml);
591 }
592 }
593
594 static void
tomoe_char_to_xml_n_strokes(TomoeChar * chr,TomoeCharPrivate * priv,GString * output)595 tomoe_char_to_xml_n_strokes (TomoeChar *chr, TomoeCharPrivate *priv,
596 GString *output)
597 {
598 if (priv->n_strokes < 0) return;
599
600 g_string_append_printf (
601 output,
602 " <number-of-strokes>%d</number-of-strokes>\n",
603 priv->n_strokes);
604 }
605
606
607 static void
tomoe_char_to_xml_meta_datum(gpointer key,gpointer value,gpointer user_data)608 tomoe_char_to_xml_meta_datum (gpointer key, gpointer value, gpointer user_data)
609 {
610 GString *output = user_data;
611 gchar *meta_key = key;
612 gchar *meta_value = value;
613 gchar *result;
614
615 result = g_markup_printf_escaped (" <%s>%s</%s>\n",
616 meta_key, meta_value, meta_key);
617 g_string_append (output, result);
618 g_free (result);
619 }
620
621 static void
tomoe_char_to_xml_meta(TomoeChar * chr,TomoeCharPrivate * priv,GString * output)622 tomoe_char_to_xml_meta (TomoeChar *chr, TomoeCharPrivate *priv, GString *output)
623 {
624 if (!tomoe_char_has_meta_data (chr)) return;
625
626 g_string_append (output, " <meta>\n");
627 tomoe_char_meta_data_foreach (chr, tomoe_char_to_xml_meta_datum, output);
628 g_string_append (output, " </meta>\n");
629 }
630
631 gchar *
tomoe_char_to_xml(TomoeChar * chr)632 tomoe_char_to_xml (TomoeChar* chr)
633 {
634 TomoeCharPrivate *priv;
635 GString *output;
636
637 g_return_val_if_fail (TOMOE_IS_CHAR (chr), NULL);
638
639 priv = TOMOE_CHAR_GET_PRIVATE (chr);
640 output = g_string_new ("");
641
642 tomoe_char_to_xml_utf8 (chr, priv, output);
643 tomoe_char_to_xml_variant (chr, priv, output);
644 tomoe_char_to_xml_readings (chr, priv, output);
645 tomoe_char_to_xml_radicals (chr, priv, output);
646 tomoe_char_to_xml_n_strokes (chr, priv, output);
647 tomoe_char_to_xml_writing (chr, priv, output);
648 tomoe_char_to_xml_meta (chr, priv, output);
649
650 if (output->len > 0) {
651 g_string_prepend (output, " <character>\n");
652 g_string_append (output, " </character>\n");
653 }
654
655 return g_string_free (output, FALSE);
656 }
657
658
659 /*
660 vi:ts=4:nowrap:ai:expandtab
661 */
662