1 /*
2  * Smart Common Input Method
3  *
4  * Copyright (c) 2002-2005 James Su <suzhe@tsinghua.org.cn>
5  *
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA  02111-1307  USA
21  *
22  * $Id: scim_backend.cpp,v 1.38.2.1 2006/09/24 16:00:52 suzhe Exp $
23  *
24  */
25 
26 #define Uses_SCIM_FILTER_MANAGER
27 #define Uses_SCIM_BACKEND
28 #define Uses_SCIM_IMENGINE
29 #define Uses_SCIM_IMENGINE_MODULE
30 #define Uses_SCIM_CONFIG_PATH
31 #define Uses_STL_ALGORITHM
32 #include "scim_private.h"
33 #include "scim.h"
34 #include "scim_stl_map.h"
35 
36 namespace scim {
37 
38 #if SCIM_USE_STL_EXT_HASH_MAP
39 typedef __gnu_cxx::hash_map <String, IMEngineFactoryPointer, scim_hash_string >     IMEngineFactoryRepository;
40 #elif SCIM_USE_STL_HASH_MAP
41 typedef std::hash_map <String, IMEngineFactoryPointer, scim_hash_string >           IMEngineFactoryRepository;
42 #else
43 typedef std::map <String, IMEngineFactoryPointer>                                   IMEngineFactoryRepository;
44 #endif
45 
46 typedef std::vector <IMEngineFactoryPointer>                                        IMEngineFactoryPointerVector;
47 
48 class LocaleEqual
49 {
50     String m_lhs;
51 public:
LocaleEqual(const String & lhs)52     LocaleEqual (const String &lhs) : m_lhs (lhs) { }
53 
operator ()(const String & rhs) const54     bool operator () (const String &rhs) const {
55         if (m_lhs == rhs) return true;
56         if (scim_get_locale_language (m_lhs) == scim_get_locale_language (rhs) &&
57             scim_get_locale_encoding (m_lhs) == scim_get_locale_encoding (rhs) &&
58             m_lhs.find ('.') != String::npos && rhs.find ('.') != String::npos)
59             return true;
60         return false;
61     }
62 };
63 
64 class IMEngineFactoryPointerLess
65 {
66 public:
operator ()(const IMEngineFactoryPointer & lhs,const IMEngineFactoryPointer & rhs) const67     bool operator () (const IMEngineFactoryPointer &lhs, const IMEngineFactoryPointer &rhs) const {
68         return (lhs->get_language () < rhs->get_language ()) ||
69                (lhs->get_language () == rhs->get_language () && lhs->get_name () < rhs->get_name ());
70     }
71 };
72 
73 class BackEndBase::BackEndBaseImpl
74 {
75     IMEngineFactoryRepository    m_factory_repository;
76     String                       m_supported_unicode_locales;
77     ConfigPointer                m_config;
78 
79 public:
BackEndBaseImpl(const ConfigPointer & config)80     BackEndBaseImpl (const ConfigPointer &config)
81         : m_config (config)
82     {
83         String locales;
84 
85         // Set the default supported locales.
86         locales = scim_global_config_read (SCIM_GLOBAL_CONFIG_SUPPORTED_UNICODE_LOCALES, String ("en_US.UTF-8"));
87 
88         std::vector <String> locale_list;
89         std::vector <String> real_list;
90 
91         scim_split_string_list (locale_list, locales);
92 
93         for (std::vector <String>::iterator i = locale_list.begin (); i!= locale_list.end (); ++i) {
94             *i = scim_validate_locale (*i);
95             if (i->length () && scim_get_locale_encoding (*i) == "UTF-8" &&
96                 std::find_if (real_list.begin (), real_list.end (), LocaleEqual (*i)) == real_list.end ())
97                 real_list.push_back (*i);
98         }
99 
100         m_supported_unicode_locales = scim_combine_string_list (real_list);
101     }
102 
clear()103     void clear ()
104     {
105         m_factory_repository.clear ();
106     }
107 
get_all_locales() const108     String get_all_locales () const
109     {
110         String locale;
111 
112         std::vector <String> locale_list;
113         std::vector <String> real_list;
114 
115         IMEngineFactoryRepository::const_iterator it;
116 
117         for (it = m_factory_repository.begin (); it != m_factory_repository.end (); ++it) {
118             if (locale.length () == 0)
119                 locale += it->second->get_locales ();
120             else
121                 locale += (String (",") + it->second->get_locales ());
122         }
123 
124         if (m_supported_unicode_locales.length ())
125             locale += (String (",") + m_supported_unicode_locales);
126 
127         scim_split_string_list (locale_list, locale);
128 
129         for (std::vector <String>::iterator i = locale_list.begin (); i!= locale_list.end (); i++) {
130             locale = scim_validate_locale (*i);
131             if (locale.length () &&
132                 std::find_if (real_list.begin (), real_list.end (), LocaleEqual (locale)) == real_list.end ())
133                 real_list.push_back (locale);
134         }
135 
136         return scim_combine_string_list (real_list);
137     }
138 
get_factory(const String & uuid) const139     IMEngineFactoryPointer get_factory (const String &uuid) const
140     {
141         IMEngineFactoryRepository::const_iterator it = m_factory_repository.find (uuid);
142 
143         if (it != m_factory_repository.end ())
144             return it->second;
145 
146         return IMEngineFactoryPointer (0);
147     }
148 
get_factories_for_encoding(std::vector<IMEngineFactoryPointer> & factories,const String & encoding) const149     uint32 get_factories_for_encoding (std::vector<IMEngineFactoryPointer> &factories,
150                                        const String                        &encoding)  const
151     {
152         IMEngineFactoryRepository::const_iterator it;
153 
154         factories.clear ();
155 
156         for (it = m_factory_repository.begin (); it != m_factory_repository.end (); ++it) {
157             if ((encoding.length () == 0 || it->second->validate_encoding (encoding)))
158                 factories.push_back (it->second);
159         }
160 
161         sort_factories (factories);
162 
163         return factories.size ();
164     }
165 
get_factories_for_language(std::vector<IMEngineFactoryPointer> & factories,const String & language) const166     uint32 get_factories_for_language (std::vector<IMEngineFactoryPointer> &factories,
167                                        const String                        &language)  const
168     {
169         IMEngineFactoryRepository::const_iterator it;
170 
171         factories.clear ();
172 
173         for (it = m_factory_repository.begin (); it != m_factory_repository.end (); ++it) {
174             if ((language.length () == 0 || it->second->get_language () == language))
175                 factories.push_back (it->second);
176         }
177 
178         sort_factories (factories);
179 
180         return factories.size ();
181     }
182 
get_default_factory(const String & language,const String & encoding) const183     IMEngineFactoryPointer get_default_factory (const String &language, const String &encoding) const
184     {
185         if (!language.length ()) return IMEngineFactoryPointer ();
186 
187         IMEngineFactoryPointerVector factories;
188 
189         if (get_factories_for_encoding (factories, encoding) > 0) {
190             IMEngineFactoryPointer lang_first;
191             IMEngineFactoryPointerVector::iterator it;
192 
193             String def_uuid;
194 
195             def_uuid = m_config->read (String (SCIM_CONFIG_DEFAULT_IMENGINE_FACTORY) +
196                                        String ("/") + language,
197                                        String (""));
198 
199             // Match by Normalized language exactly.
200             for (it = factories.begin (); it != factories.end (); ++it) {
201                 if (scim_get_normalized_language ((*it)->get_language ()) == language && lang_first.null ())
202                     lang_first = *it;
203 
204                 if ((*it)->get_uuid () == def_uuid)
205                     return *it;
206             }
207 
208             if (!lang_first.null ()) return lang_first;
209 
210             // Match by short language name.
211             for (it = factories.begin (); it != factories.end (); ++it)
212                 if ((*it)->get_language () == language.substr (0,2))
213                     return *it;
214 
215             return factories [0];
216         }
217 
218         return IMEngineFactoryPointer ();
219     }
220 
set_default_factory(const String & language,const String & uuid)221     void set_default_factory (const String &language, const String &uuid)
222     {
223         if (!language.length () || !uuid.length ()) return;
224 
225         IMEngineFactoryPointerVector factories;
226         if (get_factories_for_encoding (factories, "") > 0) {
227             IMEngineFactoryPointerVector::iterator it;
228             for (it = factories.begin (); it != factories.end (); ++it) {
229                 if ((*it)->get_uuid () == uuid) {
230                     m_config->write (String (SCIM_CONFIG_DEFAULT_IMENGINE_FACTORY) + String ("/") + language, uuid);
231                     return;
232                 }
233             }
234         }
235     }
236 
get_next_factory(const String & language,const String & encoding,const String & cur_uuid) const237     IMEngineFactoryPointer get_next_factory (const String &language, const String &encoding, const String &cur_uuid) const
238     {
239         IMEngineFactoryPointerVector factories;
240 
241         if (get_factories_for_encoding (factories, encoding) > 0) {
242             IMEngineFactoryPointer lang_first;
243             IMEngineFactoryPointerVector::iterator it, itl;
244 
245             for (it = factories.begin (); it != factories.end (); ++it) {
246                 if ((language.length () == 0 || (*it)->get_language () == language) && lang_first.null ())
247                     lang_first = *it;
248 
249                 if ((*it)->get_uuid () == cur_uuid) {
250                     for (itl = it + 1; itl != factories.end (); ++itl) {
251                         if (language.length () == 0 || (*itl)->get_language () == language)
252                             return *itl;
253                     }
254                     if (!lang_first.null ()) return lang_first;
255 
256                     return factories [0];
257                 }
258             }
259         }
260 
261         return IMEngineFactoryPointer ();
262     }
263 
get_previous_factory(const String & language,const String & encoding,const String & cur_uuid) const264     IMEngineFactoryPointer get_previous_factory (const String &language, const String &encoding, const String &cur_uuid) const
265     {
266         IMEngineFactoryPointerVector factories;
267 
268         if (get_factories_for_encoding (factories, encoding) > 0) {
269             IMEngineFactoryPointer lang_last;
270             IMEngineFactoryPointerVector::iterator it, itl;
271 
272             for (it = factories.begin (); it != factories.end (); ++it) {
273                 if ((language.length () == 0 || (*it)->get_language () == language))
274                     lang_last = *it;
275             }
276 
277             for (it = factories.begin (); it != factories.end (); ++it) {
278                 if ((*it)->get_uuid () == cur_uuid) {
279                     for (itl = it; itl != factories.begin (); --itl) {
280                         if (language.length () == 0 || (*(itl-1))->get_language () == language)
281                             return *(itl-1);
282                     }
283 
284                     if (!lang_last.null ()) return lang_last;
285 
286                     return factories [factories.size () - 1];
287                 }
288             }
289         }
290 
291         return IMEngineFactoryPointer ();
292     }
293 
add_factory(const IMEngineFactoryPointer & factory)294     bool add_factory (const IMEngineFactoryPointer &factory)
295     {
296         if (!factory.null ()) {
297             String uuid = factory->get_uuid ();
298 
299             if (uuid.length () && m_factory_repository.find (uuid) == m_factory_repository.end ()) {
300                 m_factory_repository [uuid] = factory;
301                 return true;
302             }
303         }
304 
305         return false;
306     }
307 
308 private:
sort_factories(std::vector<IMEngineFactoryPointer> & factories) const309     void sort_factories (std::vector<IMEngineFactoryPointer> &factories) const
310     {
311         std::sort (factories.begin (), factories.end (), IMEngineFactoryPointerLess ());
312     }
313 };
314 
BackEndBase(const ConfigPointer & config)315 BackEndBase::BackEndBase (const ConfigPointer &config)
316     : m_impl (new BackEndBase::BackEndBaseImpl (config))
317 {
318 }
319 
~BackEndBase()320 BackEndBase::~BackEndBase ()
321 {
322     delete m_impl;
323 }
324 
325 String
get_all_locales() const326 BackEndBase::get_all_locales () const
327 {
328     return m_impl->get_all_locales ();
329 }
330 
331 IMEngineFactoryPointer
get_factory(const String & uuid) const332 BackEndBase::get_factory (const String &uuid) const
333 {
334     return m_impl->get_factory (uuid);
335 }
336 
337 uint32
get_factories_for_encoding(std::vector<IMEngineFactoryPointer> & factories,const String & encoding) const338 BackEndBase::get_factories_for_encoding (std::vector<IMEngineFactoryPointer> &factories, const String &encoding) const
339 {
340     return m_impl->get_factories_for_encoding (factories, encoding);
341 }
342 
343 uint32
get_factories_for_language(std::vector<IMEngineFactoryPointer> & factories,const String & language) const344 BackEndBase::get_factories_for_language (std::vector<IMEngineFactoryPointer> &factories, const String &language) const
345 {
346     return m_impl->get_factories_for_language (factories, language);
347 }
348 
349 IMEngineFactoryPointer
get_default_factory(const String & language,const String & encoding) const350 BackEndBase::get_default_factory (const String &language, const String &encoding) const
351 {
352     return m_impl->get_default_factory (language, encoding);
353 }
354 
355 void
set_default_factory(const String & language,const String & uuid)356 BackEndBase::set_default_factory (const String &language, const String &uuid)
357 {
358     m_impl->set_default_factory (language, uuid);
359 }
360 
361 IMEngineFactoryPointer
get_next_factory(const String & language,const String & encoding,const String & cur_uuid) const362 BackEndBase::get_next_factory (const String &language, const String &encoding, const String &cur_uuid) const
363 {
364     return m_impl->get_next_factory (language, encoding, cur_uuid);
365 }
366 
367 IMEngineFactoryPointer
get_previous_factory(const String & language,const String & encoding,const String & cur_uuid) const368 BackEndBase::get_previous_factory (const String &language, const String &encoding, const String &cur_uuid) const
369 {
370     return m_impl->get_previous_factory (language, encoding, cur_uuid);
371 }
372 
373 bool
add_factory(const IMEngineFactoryPointer & factory)374 BackEndBase::add_factory (const IMEngineFactoryPointer &factory)
375 {
376     return m_impl->add_factory (factory);
377 }
378 
379 void
clear()380 BackEndBase::clear ()
381 {
382     m_impl->clear ();
383 }
384 
385 // Implementation of CommonBackEnd.
386 struct CommonBackEnd::CommonBackEndImpl {
387     IMEngineModule      *m_engine_modules;
388     FilterManager       *m_filter_manager;
389 
CommonBackEndImplscim::CommonBackEnd::CommonBackEndImpl390     CommonBackEndImpl () : m_engine_modules (0), m_filter_manager (0) { }
391 };
392 
CommonBackEnd(const ConfigPointer & config,const std::vector<String> & modules)393 CommonBackEnd::CommonBackEnd (const ConfigPointer       &config,
394                               const std::vector<String> &modules)
395     : BackEndBase (config),
396       m_impl (new CommonBackEndImpl)
397 {
398     IMEngineFactoryPointer factory;
399     std::vector<String>    disabled_factories;
400     std::vector<String>    new_modules = modules;
401 
402     int all_factories_count = 0;
403     int module_factories_count = 0;
404 
405     if (config.null ()) return;
406 
407     // Get disabled factories list.
408     disabled_factories = scim_global_config_read (SCIM_GLOBAL_CONFIG_DISABLED_IMENGINE_FACTORIES, disabled_factories);
409 
410     // Put socket module to the end of list.
411     for (std::vector<String>::iterator it = new_modules.begin (); it != new_modules.end (); ++it) {
412         if (*it == "socket") {
413             new_modules.erase (it);
414             new_modules.push_back ("socket");
415             break;
416         }
417     }
418 
419     // Try to load all IMEngine modules
420     try {
421         m_impl->m_engine_modules = new IMEngineModule [new_modules.size ()];
422         m_impl->m_filter_manager = new FilterManager (config);
423     } catch (const std::exception & err) {
424         std::cerr << err.what () << "\n";
425         return;
426     }
427 
428     //load IMEngine modules
429     for (size_t i = 0; i < new_modules.size (); ++i) {
430         SCIM_DEBUG_BACKEND (1) << "Loading IMEngine module: " << new_modules [i] << " ...\n";
431 
432         module_factories_count = 0;
433 
434         if (m_impl->m_engine_modules [i].load (new_modules [i], config) &&
435             m_impl->m_engine_modules [i].valid ()) {
436             for (size_t j=0; j < m_impl->m_engine_modules [i].number_of_factories (); ++j) {
437 
438                 // Try to load a IMEngine Factory.
439                 try {
440                     factory = m_impl->m_engine_modules [i].create_factory (j);
441                 } catch (const std::exception & err) {
442                     std::cerr << err.what () << "\n";
443                     factory.reset ();
444                 }
445 
446                 if (!factory.null ()) {
447                     // Check if it's disabled.
448                     if (std::find (disabled_factories.begin (),
449                                    disabled_factories.end (),
450                                    factory->get_uuid ()) == disabled_factories.end ()) {
451 
452                         // Add it into disabled list to prevent from loading again.
453                         disabled_factories.push_back (factory->get_uuid ());
454 
455                         // Only load filter for none socket IMEngines.
456                         if (new_modules [i] != "socket")
457                             factory = m_impl->m_filter_manager->attach_filters_to_factory (factory);
458 
459                         add_factory (factory);
460 
461                         all_factories_count ++;
462                         module_factories_count ++;
463 
464                         SCIM_DEBUG_BACKEND (1) << "    Loading IMEngine Factory " << j << " : " << "OK\n";
465                     } else {
466                         SCIM_DEBUG_BACKEND (1) << "    Loading IMEngine Factory " << j << " : " << "Disabled\n";
467                         factory.reset ();
468                     }
469                 } else {
470                     SCIM_DEBUG_BACKEND (1) << "    Loading IMEngine Factory " << j << " : " << "Failed\n";
471                 }
472             }
473             if (module_factories_count) {
474                 SCIM_DEBUG_BACKEND (1) << new_modules [i] << " IMEngine module is successfully loaded.\n";
475             } else {
476                 SCIM_DEBUG_BACKEND (1) << "No Factory loaded from " << new_modules [i] << " IMEngine module!\n";
477                 m_impl->m_engine_modules [i].unload ();
478             }
479         } else {
480             SCIM_DEBUG_BACKEND (1) << "Failed to load " << new_modules [i] << " IMEngine module.\n";
481         }
482     }
483 
484     factory = new ComposeKeyFactory ();
485 
486     if (all_factories_count == 0 ||
487         std::find (disabled_factories.begin (),
488                    disabled_factories.end (),
489                    factory->get_uuid ()) == disabled_factories.end ()) {
490         factory = m_impl->m_filter_manager->attach_filters_to_factory (factory);
491         add_factory (factory);
492     }
493 }
494 
~CommonBackEnd()495 CommonBackEnd::~CommonBackEnd ()
496 {
497     clear ();
498 
499     delete [] m_impl->m_engine_modules;
500     delete m_impl->m_filter_manager;
501     delete m_impl;
502 }
503 
504 
505 } // namespace scim
506 
507 /*
508 vi:ts=4:nowrap:ai:expandtab
509 */
510