Updated to version 79b7bde of tinygettext
[supertux.git] / external / tinygettext / src / dictionary_manager.cpp
1 //  tinygettext - A gettext replacement that works directly on .po files
2 //  Copyright (C) 2006 Ingo Ruhnke <grumbel@gmx.de>
3 //
4 //  This program is free software; you can redistribute it and/or
5 //  modify it under the terms of the GNU General Public License
6 //  as published by the Free Software Foundation; either version 2
7 //  of the License, or (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; if not, write to the Free Software
16 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17
18 #include "tinygettext/dictionary_manager.hpp"
19
20 #include <memory>
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <fstream>
25 #include <algorithm>
26
27 #include "tinygettext/log_stream.hpp"
28 #include "tinygettext/po_parser.hpp"
29 #include "tinygettext/unix_file_system.hpp"
30
31 namespace tinygettext {
32
33 static bool has_suffix(const std::string& lhs, const std::string rhs)
34 {
35   if (lhs.length() < rhs.length())
36     return false;
37   else
38     return lhs.compare(lhs.length() - rhs.length(), rhs.length(), rhs) == 0;
39 }
40
41 DictionaryManager::DictionaryManager(const std::string& charset_) :
42   dictionaries(),
43   search_path(),
44   charset(charset_),
45   use_fuzzy(true),
46   current_language(),
47   current_dict(0),
48   empty_dict(),
49   filesystem(new UnixFileSystem)
50 {
51 }
52
53 DictionaryManager::~DictionaryManager()
54 {
55   for(Dictionaries::iterator i = dictionaries.begin(); i != dictionaries.end(); ++i)
56   {
57     delete i->second;
58   }
59 }
60
61 void
62 DictionaryManager::clear_cache()
63 {
64   for(Dictionaries::iterator i = dictionaries.begin(); i != dictionaries.end(); ++i)
65   {
66     delete i->second;
67   }
68   dictionaries.clear();
69
70   current_dict = 0;
71 }
72
73 Dictionary&
74 DictionaryManager::get_dictionary()
75 {
76   if (current_dict)
77   {
78     return *current_dict; 
79   }
80   else
81   {
82     if (current_language)
83     {
84       current_dict = &get_dictionary(current_language);
85       return *current_dict;
86     }
87     else
88     {
89       return empty_dict;
90     }
91   }
92 }
93
94 Dictionary&
95 DictionaryManager::get_dictionary(const Language& language)
96 {
97   //log_debug << "Dictionary for language \"" << spec << "\" requested" << std::endl;
98   //log_debug << "...normalized as \"" << lang << "\"" << std::endl;
99   assert(language);
100
101   Dictionaries::iterator i = dictionaries.find(language); 
102   if (i != dictionaries.end())
103   {
104     return *i->second;
105   }
106   else // Dictionary for languages lang isn't loaded, so we load it
107   {
108     //log_debug << "get_dictionary: " << lang << std::endl;
109     Dictionary* dict = new Dictionary(charset);
110
111     dictionaries[language] = dict;
112
113     for (SearchPath::reverse_iterator p = search_path.rbegin(); p != search_path.rend(); ++p)
114     {
115       std::vector<std::string> files = filesystem->open_directory(*p);
116
117       std::string best_filename;
118       int best_score = 0;
119
120       for(std::vector<std::string>::iterator filename = files.begin(); filename != files.end(); filename++)
121       {
122         // check if filename matches requested language
123         if (has_suffix(*filename, ".po"))
124         { // ignore anything that isn't a .po file
125           Language po_language = Language::from_env(filename->substr(0, filename->size()-3));
126
127           if (!po_language)
128           {
129             log_warning << *filename << ": warning: ignoring, unknown language" << std::endl;
130           }
131           else
132           {
133             int score = Language::match(language, po_language);
134                           
135             if (score > best_score)
136             {
137               best_score = score;
138               best_filename = *filename;
139             }
140           }
141         }
142       }
143               
144       if (!best_filename.empty())
145       {
146         std::string pofile = *p + "/" + best_filename;
147         try 
148         {
149           std::unique_ptr<std::istream> in = filesystem->open_file(pofile);
150           if (!in.get())
151           {
152             log_error << "error: failure opening: " << pofile << std::endl;
153           }
154           else
155           {
156             POParser::parse(pofile, *in, *dict);
157           }
158         }
159         catch(std::exception& e) 
160         {
161           log_error << "error: failure parsing: " << pofile << std::endl;
162           log_error << e.what() << "" << std::endl;
163         }
164       }
165     }
166
167     return *dict;
168   }
169 }
170
171 std::set<Language>
172 DictionaryManager::get_languages()
173 {
174   std::set<Language> languages;
175
176   for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
177   {
178     std::vector<std::string> files = filesystem->open_directory(*p);
179
180     for(std::vector<std::string>::iterator file = files.begin(); file != files.end(); ++file)
181     {
182       if (has_suffix(*file, ".po")) 
183       {
184         languages.insert(Language::from_env(file->substr(0, file->size()-3)));
185       }
186     }
187   }
188   return languages;
189 }
190
191 void
192 DictionaryManager::set_language(const Language& language)
193 {
194   if (current_language != language)
195   {
196     current_language = language;
197     current_dict     = 0;
198   }
199 }
200
201 Language
202 DictionaryManager::get_language() const
203 {
204   return current_language;
205 }
206
207 void
208 DictionaryManager::set_charset(const std::string& charset_)
209 {
210   clear_cache(); // changing charset invalidates cache
211   charset = charset_;
212 }
213
214 void
215 DictionaryManager::set_use_fuzzy(bool t)
216 {
217   clear_cache();
218   use_fuzzy = t;
219 }
220
221 bool
222 DictionaryManager::get_use_fuzzy() const
223 {
224   return use_fuzzy;
225 }
226
227 void
228 DictionaryManager::add_directory(const std::string& pathname)
229 {
230   clear_cache(); // adding directories invalidates cache
231   search_path.push_back(pathname);
232 }
233
234 void
235 DictionaryManager::set_filesystem(std::unique_ptr<FileSystem> filesystem_)
236 {
237   filesystem = std::move(filesystem_);
238 }
239
240 } // namespace tinygettext
241
242 /* EOF */