Tizen 2.1 base
[framework/uifw/ise-engine-anthy.git] / src / scim_anthy_style_file.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2005 Takuro Ashie
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This program 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 General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "scim_anthy_style_file.h"
21
22 using namespace scim_anthy;
23
24 const int MAX_LINE_LENGTH = 4096;
25
26 static String
27 escape (const String &str)
28 {
29     String dest = str;
30
31     for (unsigned int i = 0; i < dest.size (); i++) {
32         if (dest[i] == '#'  ||                   // for comment
33             dest[i] == '\\' ||                   // for backslash itself
34             dest[i] == '='  ||                   // for separatort
35             dest[i] == '['  || dest[i] == ']' || // for section
36             dest[i] == ','  ||                   // for array
37             dest[i] == ' '  || dest[i] == '\t')  // for space
38         {
39             dest.insert (i, "\\");
40             i++;
41         }
42     }
43
44     return dest;
45 }
46
47 static String
48 unescape (const String &str)
49 {
50     String dest = str;
51
52     for (unsigned int i = 0; i < dest.size (); i++) {
53         if (dest[i] == '\\') {
54             dest.erase (i, 1);
55             if (i < dest.size () && dest[i] == '\\')
56                 i++;
57         }
58     }
59
60     return dest;
61 }
62
63 StyleLine::StyleLine (StyleFile *style_file, String line)
64     : m_style_file (style_file),
65       m_line  (line),
66       m_type  (SCIM_ANTHY_STYLE_LINE_UNKNOWN)
67 {
68 }
69
70 StyleLine::StyleLine (StyleFile *style_file, String key, String value)
71     : m_style_file (style_file),
72       m_line  (escape (key) + String ("=")),
73       m_type  (SCIM_ANTHY_STYLE_LINE_KEY)
74 {
75     set_value (value);
76 }
77
78 StyleLine::StyleLine (StyleFile *style_file, String key,
79                       std::vector<String> &value)
80     : m_style_file (style_file),
81       m_line  (escape (key) + String("=")),
82       m_type  (SCIM_ANTHY_STYLE_LINE_KEY)
83 {
84     set_value_array (value);
85 }
86
87 StyleLine::~StyleLine ()
88 {
89 }
90
91 StyleLineType
92 StyleLine::get_type (void)
93 {
94     if (m_type != SCIM_ANTHY_STYLE_LINE_UNKNOWN)
95         return m_type;
96
97     unsigned int spos, epos;
98     for (spos = 0;
99          spos < m_line.length () && isspace (m_line[spos]);
100          spos++);
101     if (m_line.length() > 0) {
102         for (epos = m_line.length () - 1;
103              epos >= 0 && isspace (m_line[epos]);
104              epos--);
105     } else {
106         epos = 0;
107     }
108
109     if (m_line.length() == 0 || spos >= m_line.length()) {
110         m_type = SCIM_ANTHY_STYLE_LINE_SPACE;
111         return m_type;
112
113     } else if (m_line[spos] == '#') {
114         m_type = SCIM_ANTHY_STYLE_LINE_COMMENT;
115         return m_type;
116
117     } else if (m_line[spos] == '[' && m_line[epos] == ']') {
118         m_type = SCIM_ANTHY_STYLE_LINE_SECTION;
119         return m_type;
120     }
121
122     m_type = SCIM_ANTHY_STYLE_LINE_KEY;
123     return m_type;
124 }
125
126 bool
127 StyleLine::get_section (String &section)
128 {
129     if (get_type () != SCIM_ANTHY_STYLE_LINE_SECTION)
130         return false;
131
132     unsigned int spos, epos;
133     for (spos = 0;
134          spos < m_line.length () && isspace (m_line[spos]);
135          spos++);
136     for (epos = m_line.length () - 1;
137          epos >= 0 && isspace (m_line[epos]);
138          epos--);
139     spos++;
140
141     if (spos < epos)
142         section = m_line.substr (spos, epos - spos);
143     else
144         section = String ();
145
146     return true;
147 }
148
149 bool
150 StyleLine::get_key (String &key)
151 {
152     if (get_type () != SCIM_ANTHY_STYLE_LINE_KEY)
153         return false;
154
155     unsigned int spos, epos;
156     for (spos = 0;
157          spos < m_line.length () && isspace (m_line[spos]);
158          spos++);
159     bool found = false;
160     for (epos = spos;
161          epos < m_line.length ();
162          epos++)
163     {
164         if (m_line[epos] == '\\') {
165             epos++;
166             continue;
167         }
168         if (m_line[epos] == '=') {
169             found = true;
170             break;
171         }
172     }
173     for (--epos;
174          epos >= spos && isspace (m_line[epos]);
175          epos--);
176     if (!isspace(m_line[epos]))
177         epos++;
178
179     if (spos >= 0 && spos < epos && epos <= m_line.length ()) {
180         key = unescape (m_line.substr (spos, epos - spos));
181     } else
182         key = String ();
183
184     return true;
185 }
186
187 static int
188 get_value_position (String &str)
189 {
190     unsigned int spos;
191     for (spos = 0;
192          spos < str.length ();
193          spos++)
194     {
195         if (str[spos] == '\\') {
196             spos++;
197             continue;
198         }
199         if (str[spos] == '=') {
200             break;
201         }
202     }
203     if (spos >= str.length ())
204         return true;
205     else
206         spos++;
207     for (;
208          spos < str.length () && isspace(str[spos]);
209          spos++);
210
211     return spos;
212 }
213
214 bool
215 StyleLine::get_value (String &value)
216 {
217     if (get_type () != SCIM_ANTHY_STYLE_LINE_KEY)
218         return false;
219
220     unsigned int spos = get_value_position (m_line);
221     unsigned int epos = m_line.length ();
222
223     value = unescape (m_line.substr (spos, epos - spos));
224
225     return true;
226 }
227
228 void
229 StyleLine::set_value (String value)
230 {
231     String key;
232     get_key (key);
233     m_line = escape (key) + String ("=") + escape (value);
234 }
235
236 bool
237 StyleLine::get_value_array (std::vector<String> &value)
238 {
239     if (get_type () != SCIM_ANTHY_STYLE_LINE_KEY)
240         return false;
241
242     unsigned int spos = get_value_position (m_line);
243     unsigned int epos = m_line.length ();
244
245     unsigned int head_of_element = spos;
246     for (unsigned int i = spos; i <= epos; i++) {
247         if (i < epos && m_line[i] == '\\') {
248             i++;
249             continue;
250         }
251
252         if (i == epos || m_line[i] == ',') {
253             String str;
254             if (head_of_element == epos)
255                 str = String ();
256             else
257                 str = unescape (m_line.substr (head_of_element,
258                                                i - head_of_element));
259             value.push_back (str);
260             head_of_element = i + 1;
261         }
262     }
263
264     return true;
265 }
266
267 void
268 StyleLine::set_value_array (std::vector<String> &value)
269 {
270     String key;
271     get_key (key);
272
273     m_line = escape (key) + String ("=");
274     for (unsigned int i = 0; i < value.size (); i++) {
275         if (i != 0)
276             m_line += ",";
277         m_line += escape (value[i]);
278     }
279 }
280
281
282 StyleFile::StyleFile ()
283 {
284     setup_default_entries ();
285 }
286
287 StyleFile::~StyleFile ()
288 {
289 }
290
291 bool
292 StyleFile::load (const char *filename)
293 {
294     clear ();
295     setup_default_entries ();
296     m_filename = filename;
297
298     std::ifstream in_file (filename);
299     if (!in_file)
300         return false;
301
302     clear ();
303
304     m_sections.push_back (StyleLines ());
305     StyleLines *section = &m_sections[0];
306     unsigned int section_id = 0;
307
308     char buf[MAX_LINE_LENGTH];
309     do {
310         in_file.getline (buf, MAX_LINE_LENGTH);  
311         if (in_file.eof ())
312             break;
313
314         WideString dest;
315         m_iconv.convert (dest, buf);
316         StyleLine line (this, utf8_wcstombs (dest));
317         StyleLineType type = line.get_type ();
318
319         if (type == SCIM_ANTHY_STYLE_LINE_SECTION) {
320             m_sections.push_back (StyleLines ());
321             section = &m_sections.back();
322             section_id++;
323         }
324
325         section->push_back (line);
326
327         if (section_id == 0) {
328             String key;
329             line.get_key (key);
330             if (key == "FormatVersion") {
331                 line.get_value (m_format_version);
332
333             } else if (key == "Encoding") {
334                 line.get_value (m_encoding);
335                 bool success = m_iconv.set_encoding (m_encoding);
336                 if (!success)
337                     m_iconv.set_encoding ("UTF-8");
338
339             } else if (key == "Title") {
340                 line.get_value (m_title);
341
342             } else if (key == "Version") {
343                 line.get_value (m_version);
344             }
345         }
346     } while (!in_file.eof ());
347
348     in_file.close ();
349
350     m_filename = filename;
351
352     return true;
353 }
354
355 bool
356 StyleFile::save (const char *filename)
357 {
358     std::ofstream out_file (filename);
359     if (!out_file)
360         return false;
361
362     StyleSections::iterator it;
363     for (it = m_sections.begin (); it != m_sections.end (); it++) {
364         StyleLines::iterator lit;
365         for (lit = it->begin (); lit != it->end (); lit++) {
366             String line, dest;
367             lit->get_line (line);
368             m_iconv.convert (dest, utf8_mbstowcs (line));
369             out_file << dest.c_str () << std::endl;
370         }
371     }
372
373     out_file.close ();
374
375     m_filename = filename;
376
377     return true;
378 }
379
380 void
381 StyleFile::clear (void)
382 {
383     m_filename       = String ();
384     m_format_version = String ();
385     m_encoding       = String ();
386     m_title          = String ();
387     m_version        = String ();
388     m_sections.clear ();
389 }
390
391 String
392 StyleFile::get_encoding (void)
393 {
394     return m_encoding;
395 }
396
397 String
398 StyleFile::get_title (void)
399 {
400     return m_title;
401 }
402
403 String
404 StyleFile::get_file_name (void)
405 {
406     return m_filename;
407 }
408
409 bool
410 StyleFile::get_string (String &value, String section, String key)
411 {
412     StyleSections::iterator it;
413     for (it = m_sections.begin (); it != m_sections.end (); it++) {
414         if (it->size () <= 0)
415             continue;
416
417         String s, k;
418         (*it)[0].get_section (s);
419
420         if (s != section)
421             continue;
422
423         StyleLines::iterator lit;
424         for (lit = it->begin (); lit != it->end (); lit++) {
425             lit->get_key (k);
426             if (k == key) {
427                 lit->get_value (value);
428                 return true;
429             }
430         }
431     }
432
433     return false;
434 }
435
436 bool
437 StyleFile::get_string (WideString &value, String section, String key)
438 {
439     String str;
440     bool success = get_string (str, section, key);
441     if (!success)
442         return false;
443     value = utf8_mbstowcs (str);
444     return true;
445 }
446
447 bool
448 StyleFile::get_string_array (std::vector<String> &value,
449                              String section, String key)
450 {
451     StyleLines *lines = find_section (section);
452     if (!lines)
453         return false;
454
455     // find entry
456     StyleLines::iterator lit;
457     for (lit = lines->begin (); lit != lines->end (); lit++) {
458         String k;
459         lit->get_key (k);
460         if (k == key) {
461             lit->get_value_array (value);
462             return true;
463         }
464     }
465
466     return false;
467 }
468
469 bool
470 StyleFile::get_string_array (std::vector<WideString> &value,
471                              String section, String key)
472 {
473     std::vector<String> array;
474     bool success = get_string_array (array, section, key);
475     if (!success)
476         return false;
477
478     std::vector<String>::iterator it;
479     for (it = array.begin (); it != array.end (); it++)
480         value.push_back (utf8_mbstowcs (*it));
481     return true;
482 }
483
484 void
485 StyleFile::set_string (String section, String key, String value)
486 {
487     StyleLines *lines = find_section (section);
488     if (lines) {
489         // find entry
490         StyleLines::iterator lit, last = lines->begin () + 1;
491         for (lit = last; lit != lines->end (); lit++) {
492             StyleLineType type = lit->get_type ();
493             if (type != SCIM_ANTHY_STYLE_LINE_SPACE)
494                 last = lit + 1;
495
496             String k;
497             lit->get_key (k);
498
499             // replace existing entry
500             if (k.length () > 0 && k == key) {
501                 lit->set_value (value);
502                 return;
503             }
504         }
505
506         // append new entry if no mathced entry exists.
507         lines->insert (last, StyleLine (this, key, value));
508
509     } else {
510         StyleLines &newsec = append_new_section (section);
511
512         // append new entry
513         newsec.push_back (StyleLine (this, key, value));
514     }
515 }
516
517 void
518 StyleFile::set_string (String section, String key, WideString value)
519 {
520     set_string (section, key, utf8_wcstombs (value));
521 }
522
523 void
524 StyleFile::set_string_array (String section, String key,
525                              std::vector<String> &value)
526 {
527     StyleLines *lines = find_section (section);
528     if (lines) {
529         // find entry
530         StyleLines::iterator lit, last = lines->begin () + 1;
531         for (lit = last; lit != lines->end (); lit++) {
532             StyleLineType type = lit->get_type ();
533             if (type != SCIM_ANTHY_STYLE_LINE_SPACE)
534                 last = lit;
535
536             String k;
537             lit->get_key (k);
538
539             // replace existing entry
540             if (k.length () > 0 && k == key) {
541                 lit->set_value_array (value);
542                 return;
543             }
544         }
545
546         // append new entry if no mathced entry exists.
547         lines->insert (last + 1, StyleLine (this, key, value));
548
549     } else {
550         StyleLines &newsec = append_new_section (section);
551
552         // append new entry
553         newsec.push_back (StyleLine (this, key, value));
554     }
555 }
556
557 void
558 StyleFile::set_string_array (String section, String key,
559                              std::vector<WideString> &value)
560 {
561     std::vector<String> array;
562     std::vector<WideString>::iterator it;
563     for (it = value.begin (); it != value.end (); it++)
564         array.push_back (utf8_wcstombs (*it));
565     set_string_array (section, key, array);
566 }
567
568 bool
569 StyleFile::get_section_list (StyleSections &sections)
570 {
571     sections = m_sections;
572     return true;
573 }
574
575 bool
576 StyleFile::get_entry_list (StyleLines &lines, String section)
577 {
578     StyleSections::iterator it;
579     for (it = m_sections.begin (); it != m_sections.end (); it++) {
580         if (it->size () <= 0)
581             continue;
582
583         String s;
584         (*it)[0].get_section (s);
585         if (s == section) {
586             lines = (*it);
587             return true;
588         }
589     }
590
591     return false;
592 }
593
594 bool
595 StyleFile::get_key_list (std::vector<String> &keys, String section)
596 {
597     StyleLines *lines = find_section (section);
598     if (!lines)
599         return false;
600
601     StyleLines::iterator lit;
602     for (lit = lines->begin (); lit != lines->end (); lit++) {
603         if (lit->get_type () != SCIM_ANTHY_STYLE_LINE_KEY)
604             continue;
605
606         String key;
607         lit->get_key (key);
608         keys.push_back (key);
609     }
610     return true;
611 }
612
613 void
614 StyleFile::delete_key (String section, String key)
615 {
616     StyleLines *lines = find_section (section);
617     if (!lines)
618         return;
619
620     // find entry
621     StyleLines::iterator lit;
622     for (lit = lines->begin (); lit != lines->end (); lit++) {
623         String k;
624         lit->get_key (k);
625         if (k == key) {
626             lines->erase (lit);
627             return;
628         }
629     }
630 }
631
632 void
633 StyleFile::delete_section (String section)
634 {
635     StyleSections::iterator it;
636     for (it = m_sections.begin (); it != m_sections.end (); it++) {
637         if (it->size () <= 0)
638             continue;
639
640         StyleLines::iterator lit;
641         String s;
642         (*it)[0].get_section (s);
643         if (s == section) {
644             m_sections.erase (it);
645             return;
646         }
647     }
648 }
649
650 Key2KanaTable *
651 StyleFile::get_key2kana_table (String section)
652 {
653     Key2KanaTable *table = NULL;
654
655     std::vector<String> keys;
656     bool success = get_key_list (keys, section);
657     if (success) {
658         table = new Key2KanaTable (utf8_mbstowcs (get_title ()));
659         std::vector<String>::iterator it;
660         for (it = keys.begin (); it != keys.end (); it++) {
661             std::vector<String> array;
662             get_string_array (array, section, *it);
663             table->append_rule (*it, array);
664         }
665     }
666
667     return table;
668 }
669
670 void
671 StyleFile::setup_default_entries (void)
672 {
673     m_encoding = "UTF-8";
674     m_title    = "User defined";
675     m_iconv.set_encoding (m_encoding);
676     m_sections.push_back (StyleLines ());
677
678     m_sections.push_back (StyleLines ());
679     StyleLines &newsec = m_sections.back ();
680     String str = String ("Encoding") + String ("=") + escape (m_encoding);
681     newsec.push_back (StyleLine (this, str.c_str ()));
682     str = String ("Title") + String ("=") + escape (m_title);
683     newsec.push_back (StyleLine (this, str.c_str ()));
684 }
685
686 StyleLines *
687 StyleFile::find_section (const String  &section)
688 {
689     // find section
690     StyleSections::iterator it;
691     for (it = m_sections.begin (); it != m_sections.end (); it++) {
692         if (it->size () <= 0)
693             continue;
694
695         String s;
696         (*it)[0].get_section (s);
697
698         if (s == section)
699             return &(*it);
700     }
701
702     return NULL;
703 }
704
705 StyleLines &
706 StyleFile::append_new_section (const String &section)
707 {
708     // append space before new section
709     if (!m_sections.empty()) {
710         StyleLines &sec = m_sections.back ();
711         if (sec.empty() ||
712             sec.back().get_type() != SCIM_ANTHY_STYLE_LINE_SPACE)
713         {
714             sec.push_back (StyleLine (this, ""));
715         }
716     }
717
718     //
719     // append new section
720     //
721     m_sections.push_back (StyleLines ());
722     StyleLines &newsec = m_sections.back ();
723
724     // new section entry
725     String str = String ("[") + String (section) + String ("]");
726     newsec.push_back (StyleLine (this, str.c_str ()));
727
728     return newsec;
729 }