/src/aspell/modules/speller/default/speller_impl.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // This file is part of The New Aspell |
2 | | // Copyright (C) 2000-2001,2011 by Kevin Atkinson under the GNU LGPL |
3 | | // license version 2.0 or 2.1. You should have received a copy of the |
4 | | // LGPL license along with this library if you did not you can find it |
5 | | // at http://www.gnu.org/. |
6 | | |
7 | | #include <stdlib.h> |
8 | | #include <typeinfo> |
9 | | |
10 | | #include "clone_ptr-t.hpp" |
11 | | #include "config.hpp" |
12 | | #include "data.hpp" |
13 | | #include "data_id.hpp" |
14 | | #include "errors.hpp" |
15 | | #include "language.hpp" |
16 | | #include "speller_impl.hpp" |
17 | | #include "string_list.hpp" |
18 | | #include "suggest.hpp" |
19 | | #include "tokenizer.hpp" |
20 | | #include "convert.hpp" |
21 | | #include "stack_ptr.hpp" |
22 | | #include "istream_enumeration.hpp" |
23 | | |
24 | | //#include "iostream.hpp" |
25 | | |
26 | | #include "gettext.h" |
27 | | |
28 | | namespace aspeller { |
29 | | // |
30 | | // data_access functions |
31 | | // |
32 | | |
33 | 4.29k | const char * SpellerImpl::lang_name() const { |
34 | 4.29k | return lang_->name(); |
35 | 4.29k | } |
36 | | |
37 | | // |
38 | | // to lower |
39 | | // |
40 | | |
41 | | char * SpellerImpl::to_lower(char * str) |
42 | 0 | { |
43 | 0 | for (char * i = str; *i; ++i) |
44 | 0 | *i = lang_->to_lower(*i); |
45 | 0 | return str; |
46 | 0 | } |
47 | | |
48 | | ////////////////////////////////////////////////////////////////////// |
49 | | // |
50 | | // Spell check methods |
51 | | // |
52 | | |
53 | 0 | PosibErr<void> SpellerImpl::add_to_personal(MutableString word) { |
54 | 0 | if (!personal_) return no_err; |
55 | 0 | return personal_->add(word); |
56 | 0 | } |
57 | | |
58 | 0 | PosibErr<void> SpellerImpl::add_to_session(MutableString word) { |
59 | 0 | if (!session_) return no_err; |
60 | 0 | return session_->add(word); |
61 | 0 | } |
62 | | |
63 | 0 | PosibErr<void> SpellerImpl::clear_session() { |
64 | 0 | if (!session_) return no_err; |
65 | 0 | return session_->clear(); |
66 | 0 | } |
67 | | |
68 | | PosibErr<void> SpellerImpl::store_replacement(MutableString mis, |
69 | | MutableString cor) |
70 | 0 | { |
71 | 0 | return SpellerImpl::store_replacement(mis,cor,true); |
72 | 0 | } |
73 | | |
74 | | PosibErr<void> SpellerImpl::store_replacement(const String & mis, |
75 | | const String & cor, |
76 | | bool memory) |
77 | 0 | { |
78 | 0 | if (ignore_repl) return no_err; |
79 | 0 | if (!repl_) return no_err; |
80 | 0 | String::size_type pos; |
81 | 0 | StackPtr<StringEnumeration> sugels(intr_suggest_->suggest(mis.c_str()).elements()); |
82 | 0 | const char * first_word = sugels->next(); |
83 | 0 | CheckInfo w1, w2; |
84 | 0 | String cor1, cor2; |
85 | 0 | String buf; |
86 | 0 | bool correct = false; |
87 | 0 | pos = cor.find(' '); |
88 | 0 | if (pos == String::npos) { |
89 | 0 | cor1 = cor; |
90 | 0 | correct = check_affix(cor, w1, 0); |
91 | 0 | } else { |
92 | 0 | cor1 = (String)cor.substr(0,pos); |
93 | 0 | ++pos; |
94 | 0 | while (pos < cor.size() && cor[pos] == ' ') ++pos; |
95 | 0 | cor2 = (String)cor.substr(pos); |
96 | 0 | correct = check_affix(cor1, w1, 0) && check_affix(cor2, w2, 0); |
97 | 0 | } |
98 | 0 | if (correct) { |
99 | 0 | String cor_orignal_casing(cor1); |
100 | 0 | if (!cor2.empty()) { |
101 | 0 | cor_orignal_casing += cor[pos-1]; |
102 | 0 | cor_orignal_casing += cor2; |
103 | 0 | } |
104 | | // Don't try to add the empty string, causes all kinds of |
105 | | // problems. Can happen if the original replacement nothing but |
106 | | // whitespace. |
107 | 0 | if (cor_orignal_casing.empty()) |
108 | 0 | return no_err; |
109 | 0 | if (first_word == 0 || cor != first_word) { |
110 | 0 | lang().to_lower(buf, mis.str()); |
111 | 0 | repl_->add_repl(buf, cor_orignal_casing); |
112 | 0 | } |
113 | | |
114 | 0 | if (memory && prev_cor_repl_ == mis) |
115 | 0 | store_replacement(prev_mis_repl_, cor, false); |
116 | | |
117 | 0 | } else { //!correct |
118 | | |
119 | 0 | if (memory) { |
120 | 0 | if (prev_cor_repl_ != mis) |
121 | 0 | prev_mis_repl_ = mis; |
122 | 0 | prev_cor_repl_ = cor; |
123 | 0 | } |
124 | 0 | } |
125 | 0 | return no_err; |
126 | 0 | } |
127 | | |
128 | | // |
129 | | // simple functions |
130 | | // |
131 | | |
132 | | PosibErr<const WordList *> SpellerImpl::suggest(MutableString word) |
133 | 10.9k | { |
134 | 10.9k | return &suggest_->suggest(word); |
135 | 10.9k | } |
136 | | |
137 | | bool SpellerImpl::check_simple (ParmString w, WordEntry & w0) |
138 | 491k | { |
139 | 491k | w0.clear(); // FIXME: is this necessary? |
140 | 491k | const char * x = w; |
141 | 2.80M | while (*x != '\0' && (x-w) < static_cast<int>(ignore_count)) ++x; |
142 | 491k | if (*x == '\0') {w0.word = w; return true;} |
143 | 384k | WS::const_iterator i = check_ws.begin(); |
144 | 384k | WS::const_iterator end = check_ws.end(); |
145 | 1.51M | do { |
146 | 1.51M | if ((*i)->lookup(w, &s_cmp, w0)) return true; |
147 | 1.50M | ++i; |
148 | 1.50M | } while (i != end); |
149 | 377k | return false; |
150 | 384k | }; |
151 | | |
152 | | bool SpellerImpl::check_affix(ParmString word, CheckInfo & ci, GuessInfo * gi) |
153 | 491k | { |
154 | 491k | WordEntry w; |
155 | 491k | bool res = check_simple(word, w); |
156 | 491k | if (res) {ci.word = w.word; return true;} |
157 | 377k | if (affix_compress) { |
158 | 6.09k | res = lang_->affix()->affix_check(LookupInfo(this, LookupInfo::Word), word, ci, 0); |
159 | 6.09k | if (res) return true; |
160 | 6.09k | } |
161 | 377k | if (affix_info && gi) { |
162 | 230k | lang_->affix()->affix_check(LookupInfo(this, LookupInfo::Guess), word, ci, gi); |
163 | 230k | } |
164 | 377k | return false; |
165 | 377k | } |
166 | | |
167 | | inline bool SpellerImpl::check_single(char * word, /* it WILL modify word */ |
168 | | bool try_uppercase, |
169 | | CheckInfo & ci, GuessInfo * gi) |
170 | 491k | { |
171 | 491k | bool res = check_affix(word, ci, gi); |
172 | 491k | if (res) return true; |
173 | 376k | if (!try_uppercase) return false; |
174 | 721 | char t = *word; |
175 | 721 | *word = lang_->to_title(t); |
176 | 721 | res = check_affix(word, ci, gi); |
177 | 721 | *word = t; |
178 | 721 | if (res) return true; |
179 | 721 | return false; |
180 | 721 | } |
181 | | |
182 | | CheckInfo * SpellerImpl::check_runtogether(char * word, char * word_end, |
183 | | /* it WILL modify word */ |
184 | | bool try_uppercase, |
185 | | unsigned run_together_limit, |
186 | | CheckInfo * ci, CheckInfo * ci_end, |
187 | | GuessInfo * gi) |
188 | 219k | { |
189 | 219k | if (ci >= ci_end) return NULL; |
190 | 219k | clear_check_info(*ci); |
191 | 219k | bool res = check_single(word, try_uppercase, *ci, gi); |
192 | 219k | if (res) return ci; |
193 | 171k | if (run_together_limit <= 1) return NULL; |
194 | 17.3k | enum {Yes, No, Unknown} is_title = try_uppercase ? Yes : Unknown; |
195 | 17.3k | for (char * i = word + run_together_min_; |
196 | 279k | i <= word_end - run_together_min_; |
197 | 262k | ++i) |
198 | 271k | { |
199 | 271k | char t = *i; |
200 | 271k | *i = '\0'; |
201 | 271k | clear_check_info(*ci); |
202 | 271k | res = check_single(word, try_uppercase, *ci, gi); |
203 | 271k | if (!res) {*i = t; continue;} |
204 | 65.5k | if (is_title == Unknown) |
205 | 15.7k | is_title = lang_->case_pattern(word) == FirstUpper ? Yes : No; |
206 | 65.5k | *i = t; |
207 | 65.5k | CheckInfo * ci_last = check_runtogether(i, word_end, is_title == Yes, run_together_limit - 1, ci + 1, ci_end, 0); |
208 | 65.5k | if (ci_last) { |
209 | 9.00k | ci->compound = true; |
210 | 9.00k | ci->next = ci + 1; |
211 | 9.00k | return ci_last; |
212 | 9.00k | } |
213 | 65.5k | } |
214 | 8.32k | return NULL; |
215 | 17.3k | } |
216 | | |
217 | | PosibErr<bool> SpellerImpl::check(char * word, char * word_end, |
218 | | /* it WILL modify word */ |
219 | | bool try_uppercase, |
220 | | unsigned run_together_limit, |
221 | | CheckInfo * ci, CheckInfo * ci_end, |
222 | | GuessInfo * gi, CompoundInfo * cpi) |
223 | 115k | { |
224 | 115k | clear_check_info(*ci); |
225 | 115k | bool res = check_runtogether(word, word_end, try_uppercase, run_together_limit, ci, ci_end, gi); |
226 | 115k | if (res) return true; |
227 | | |
228 | 80.5k | CompoundWord cw = lang_->split_word(word, word_end - word, camel_case_); |
229 | 80.5k | if (cw.single()) return false; |
230 | 24.0k | bool ok = true; |
231 | 24.0k | CheckInfo * ci_prev = NULL; |
232 | 38.9k | do { |
233 | 38.9k | unsigned len = cw.word_len(); |
234 | | |
235 | 38.9k | char save = word[len]; |
236 | 38.9k | word[len] = '\0'; |
237 | 38.9k | CheckInfo * ci_last = check_runtogether(word, word + len, try_uppercase, run_together_limit, ci, ci_end, gi); |
238 | 38.9k | bool found = ci_last; |
239 | 38.9k | word[len] = save; |
240 | | |
241 | 38.9k | if (!found) { |
242 | 25.1k | if (cpi) { |
243 | 3.96k | ci_last = ci; |
244 | 3.96k | ok = false; |
245 | 3.96k | ci->word.str = word; |
246 | 3.96k | ci->word.len = len; |
247 | 3.96k | ci->incorrect = true; |
248 | 3.96k | cpi->incorrect_count++; |
249 | 3.96k | if (!cpi->first_incorrect) |
250 | 1.49k | cpi->first_incorrect = ci; |
251 | 21.1k | } else { |
252 | 21.1k | return false; |
253 | 21.1k | } |
254 | 25.1k | } |
255 | | |
256 | 17.8k | if (cpi) |
257 | 5.57k | cpi->count++; |
258 | | |
259 | 17.8k | if (ci_prev) { |
260 | 7.89k | ci_prev->compound = true; |
261 | 7.89k | ci_prev->next = ci; |
262 | 7.89k | } |
263 | | |
264 | 17.8k | ci_prev = ci_last; |
265 | 17.8k | ci = ci_last + 1; |
266 | 17.8k | if (ci >= ci_end) { |
267 | 428 | if (cpi) cpi->count = 0; |
268 | 428 | return false; |
269 | 428 | } |
270 | | |
271 | 17.3k | word = word + cw.rest_offset(); |
272 | 17.3k | cw = lang_->split_word(cw.rest, cw.rest_len(), camel_case_); |
273 | | |
274 | 17.3k | } while (!cw.empty()); |
275 | | |
276 | 2.44k | return ok; |
277 | | |
278 | | // for (;;) { |
279 | | // cw = lang_->split_word(cw.rest, cw.rest_len(), camel_case_); |
280 | | // if (cw.empty()) break; |
281 | | // if (ci + 1 >= ci_end) { |
282 | | // if (cpi) cpi->count = 0; |
283 | | // return false; |
284 | | // } |
285 | | // if (cpi) cpi->count++; |
286 | | // len = cw.word_len(); |
287 | | // save = word[len]; |
288 | | // word[len] = '\0'; |
289 | | // ci_last = check_runtogether(word, word + len, try_uppercase, run_together_limit, ci + 1, ci_end, gi); |
290 | | // word[len] = save; |
291 | | // ci->compound = true; |
292 | | // ci->next = ci + 1; |
293 | | // if (ci_last) { |
294 | | // ci = ci_last; |
295 | | // } else if (cpi) { |
296 | | // ok = false; |
297 | | // ci->word.str = word; |
298 | | // ci->word.len = len; |
299 | | // ci->incorrect = true; |
300 | | // cpi->incorrect_count++; |
301 | | // } else { |
302 | | // return false; |
303 | | // } |
304 | | // word = word + cw.rest_offset(); |
305 | | // } |
306 | 24.0k | } |
307 | | |
308 | | ////////////////////////////////////////////////////////////////////// |
309 | | // |
310 | | // Word list managment methods |
311 | | // |
312 | | |
313 | 0 | PosibErr<void> SpellerImpl::save_all_word_lists() { |
314 | 0 | SpellerDict * i = dicts_; |
315 | 0 | for (; i; i = i->next) { |
316 | 0 | if (i->save_on_saveall) |
317 | 0 | RET_ON_ERR(i->dict->synchronize()); |
318 | 0 | } |
319 | 0 | return no_err; |
320 | 0 | } |
321 | | |
322 | 0 | int SpellerImpl::num_wordlists() const { |
323 | 0 | return 0; //FIXME |
324 | 0 | } |
325 | | |
326 | 0 | SpellerImpl::WordLists SpellerImpl::wordlists() const { |
327 | 0 | return 0; //FIXME |
328 | | //return MakeEnumeration<DataSetCollection::Parms>(wls_->begin(), DataSetCollection::Parms(wls_->end())); |
329 | 0 | } |
330 | | |
331 | 0 | PosibErr<const WordList *> SpellerImpl::personal_word_list() const { |
332 | 0 | const WordList * wl = static_cast<const WordList *>(personal_); |
333 | 0 | if (!wl) return make_err(operation_not_supported_error, |
334 | 0 | _("The personal word list is unavailable.")); |
335 | 0 | return wl; |
336 | 0 | } |
337 | | |
338 | 0 | PosibErr<const WordList *> SpellerImpl::session_word_list() const { |
339 | 0 | const WordList * wl = static_cast<const WordList *>(session_); |
340 | 0 | if (!wl) return make_err(operation_not_supported_error, |
341 | 0 | _("The session word list is unavailable.")); |
342 | 0 | return wl; |
343 | 0 | } |
344 | | |
345 | 0 | PosibErr<const WordList *> SpellerImpl::main_word_list() const { |
346 | 0 | const WordList * wl = dynamic_cast<const WordList *>(main_); |
347 | 0 | if (!wl) return make_err(operation_not_supported_error, |
348 | 0 | _("The main word list is unavailable.")); |
349 | 0 | return wl; |
350 | 0 | } |
351 | | |
352 | | const SpellerDict * SpellerImpl::locate (const Dict::Id & id) const |
353 | 12.7k | { |
354 | 35.4k | for (const SpellerDict * i = dicts_; i; i = i->next) |
355 | 22.7k | if (i->dict->id() == id) return i; |
356 | 12.7k | return 0; |
357 | 12.7k | } |
358 | | |
359 | | PosibErr<void> SpellerImpl::add_dict(SpellerDict * wc) |
360 | 5.95k | { |
361 | 5.95k | Dict * w = wc->dict; |
362 | 5.95k | assert(locate(w->id()) == 0); |
363 | | |
364 | 5.95k | if (!lang_) { |
365 | 863 | lang_.copy(w->lang()); |
366 | 863 | config_->replace("lang", lang_name()); |
367 | 863 | config_->replace("language-tag", lang_name()); |
368 | 5.09k | } else { |
369 | 5.09k | if (strcmp(lang_->name(), w->lang()->name()) != 0) |
370 | 1 | return make_err(mismatched_language, lang_->name(), w->lang()->name()); |
371 | 5.09k | } |
372 | | |
373 | | // add to master list |
374 | 5.95k | wc->next = dicts_; |
375 | 5.95k | dicts_ = wc; |
376 | | |
377 | | // check if it has a special_id and act accordingly |
378 | 5.95k | switch (wc->special_id) { |
379 | 0 | case main_id: |
380 | 0 | assert(main_ == 0); |
381 | 0 | main_ = w; |
382 | 0 | break; |
383 | 854 | case personal_id: |
384 | 854 | assert(personal_ == 0); |
385 | 854 | personal_ = w; |
386 | 854 | break; |
387 | 854 | case session_id: |
388 | 854 | assert(session_ == 0); |
389 | 854 | session_ = w; |
390 | 854 | break; |
391 | 854 | case personal_repl_id: |
392 | 854 | assert(repl_ == 0); |
393 | 854 | repl_ = w; |
394 | 854 | break; |
395 | 3.39k | case none_id: |
396 | 3.39k | break; |
397 | 5.95k | } |
398 | | |
399 | 5.95k | return no_err; |
400 | 5.95k | } |
401 | | |
402 | | ////////////////////////////////////////////////////////////////////// |
403 | | // |
404 | | // Config Notifier |
405 | | // |
406 | | |
407 | | struct UpdateMember { |
408 | | const char * name; |
409 | | enum Type {String, Int, Bool, Add, Rem, RemAll}; |
410 | | Type type; |
411 | | union Fun { |
412 | | typedef PosibErr<void> (*WithStr )(SpellerImpl *, const char *); |
413 | | typedef PosibErr<void> (*WithInt )(SpellerImpl *, int); |
414 | | typedef PosibErr<void> (*WithBool)(SpellerImpl *, bool); |
415 | | WithStr with_str; |
416 | | WithInt with_int; |
417 | | WithBool with_bool; |
418 | 0 | Fun() {} |
419 | 2 | Fun(WithStr m) : with_str (m) {} |
420 | 6 | Fun(WithInt m) : with_int (m) {} |
421 | 10 | Fun(WithBool m) : with_bool(m) {} |
422 | | PosibErr<void> call(SpellerImpl * m, const char * val) const |
423 | 0 | {return (*with_str) (m,val);} |
424 | | PosibErr<void> call(SpellerImpl * m, int val) const |
425 | 0 | {return (*with_int) (m,val);} |
426 | | PosibErr<void> call(SpellerImpl * m, bool val) const |
427 | 0 | {return (*with_bool)(m,val);} |
428 | | } fun; |
429 | | typedef SpellerImpl::ConfigNotifier CN; |
430 | | }; |
431 | | |
432 | | template <typename T> |
433 | | PosibErr<void> callback(SpellerImpl * m, const KeyInfo * ki, T value, |
434 | | UpdateMember::Type t); |
435 | | |
436 | | class SpellerImpl::ConfigNotifier : public Notifier { |
437 | | private: |
438 | | SpellerImpl * speller_; |
439 | | public: |
440 | | ConfigNotifier(SpellerImpl * m) |
441 | 851 | : speller_(m) |
442 | 851 | {} |
443 | | |
444 | 0 | PosibErr<void> item_updated(const KeyInfo * ki, int value) { |
445 | 0 | return callback(speller_, ki, value, UpdateMember::Int); |
446 | 0 | } |
447 | 0 | PosibErr<void> item_updated(const KeyInfo * ki, bool value) { |
448 | 0 | return callback(speller_, ki, value, UpdateMember::Bool); |
449 | 0 | } |
450 | 0 | PosibErr<void> item_updated(const KeyInfo * ki, ParmStr value) { |
451 | 0 | return callback(speller_, ki, value, UpdateMember::String); |
452 | 0 | } |
453 | | |
454 | 0 | static PosibErr<void> ignore(SpellerImpl * m, int value) { |
455 | 0 | m->ignore_count = value; |
456 | 0 | return no_err; |
457 | 0 | } |
458 | 0 | static PosibErr<void> ignore_accents(SpellerImpl * m, bool value) { |
459 | 0 | return no_err; |
460 | 0 | } |
461 | 0 | static PosibErr<void> ignore_case(SpellerImpl * m, bool value) { |
462 | 0 | m->s_cmp.case_insensitive = value; |
463 | 0 | m->s_cmp_begin.case_insensitive = value; |
464 | 0 | m->s_cmp_middle.case_insensitive = value; |
465 | 0 | m->s_cmp_end.case_insensitive = value; |
466 | 0 | return no_err; |
467 | 0 | } |
468 | 0 | static PosibErr<void> ignore_repl(SpellerImpl * m, bool value) { |
469 | | |
470 | 0 | m->ignore_repl = value; |
471 | 0 | return no_err; |
472 | 0 | } |
473 | 0 | static PosibErr<void> save_repl(SpellerImpl * m, bool value) { |
474 | 0 | // FIXME |
475 | 0 | // m->save_on_saveall(DataSet::Id(&m->personal_repl()), value); |
476 | 0 | abort(); return no_err; |
477 | 0 | } |
478 | 0 | static PosibErr<void> sug_mode(SpellerImpl * m, const char * mode) { |
479 | 0 | RET_ON_ERR(m->suggest_->set_mode(mode)); |
480 | 0 | RET_ON_ERR(m->intr_suggest_->set_mode(mode)); |
481 | 0 | return no_err; |
482 | 0 | } |
483 | 0 | static PosibErr<void> run_together(SpellerImpl * m, bool value) { |
484 | 0 | m->unconditional_run_together_ = value; |
485 | 0 | m->run_together = m->unconditional_run_together_; |
486 | 0 | return no_err; |
487 | 0 | } |
488 | 0 | static PosibErr<void> run_together_limit(SpellerImpl * m, int value) { |
489 | 0 | if (value > 8) { |
490 | 0 | m->config()->replace("run-together-limit", "8"); |
491 | | // will loop back |
492 | 0 | } else { |
493 | 0 | m->run_together_limit_ = value; |
494 | 0 | } |
495 | 0 | return no_err; |
496 | 0 | } |
497 | 0 | static PosibErr<void> run_together_min(SpellerImpl * m, int value) { |
498 | 0 | m->run_together_min_ = value; |
499 | 0 | return no_err; |
500 | 0 | } |
501 | 0 | static PosibErr<void> camel_case(SpellerImpl * m, bool value) { |
502 | 0 | m->camel_case_ = value; |
503 | 0 | return no_err; |
504 | 0 | } |
505 | | }; |
506 | | |
507 | | static UpdateMember update_members[] = |
508 | | { |
509 | | {"ignore", UpdateMember::Int, UpdateMember::CN::ignore} |
510 | | ,{"ignore-accents",UpdateMember::Bool, UpdateMember::CN::ignore_accents} |
511 | | ,{"ignore-case", UpdateMember::Bool, UpdateMember::CN::ignore_case} |
512 | | ,{"ignore-repl", UpdateMember::Bool, UpdateMember::CN::ignore_repl} |
513 | | //,{"save-repl", UpdateMember::Bool, UpdateMember::CN::save_repl} |
514 | | ,{"sug-mode", UpdateMember::String, UpdateMember::CN::sug_mode} |
515 | | ,{"run-together", |
516 | | UpdateMember::Bool, |
517 | | UpdateMember::CN::run_together} |
518 | | ,{"run-together-limit", |
519 | | UpdateMember::Int, |
520 | | UpdateMember::CN::run_together_limit} |
521 | | ,{"run-together-min", |
522 | | UpdateMember::Int, |
523 | | UpdateMember::CN::run_together_min} |
524 | | ,{"camel-case", |
525 | | UpdateMember::Bool, |
526 | | UpdateMember::CN::camel_case} |
527 | | }; |
528 | | |
529 | | template <typename T> |
530 | | PosibErr<void> callback(SpellerImpl * m, const KeyInfo * ki, T value, |
531 | | UpdateMember::Type t) |
532 | 0 | { |
533 | 0 | const UpdateMember * i |
534 | 0 | = update_members; |
535 | 0 | const UpdateMember * end |
536 | 0 | = i + sizeof(update_members)/sizeof(UpdateMember); |
537 | 0 | while (i != end) { |
538 | 0 | if (strcmp(ki->name, i->name) == 0) { |
539 | 0 | if (i->type == t) { |
540 | 0 | RET_ON_ERR(i->fun.call(m, value)); |
541 | 0 | break; |
542 | 0 | } |
543 | 0 | } |
544 | 0 | ++i; |
545 | 0 | } |
546 | 0 | return no_err; |
547 | 0 | } Unexecuted instantiation: acommon::PosibErr<void> aspeller::callback<bool>(aspeller::SpellerImpl*, acommon::KeyInfo const*, bool, aspeller::UpdateMember::Type) Unexecuted instantiation: acommon::PosibErr<void> aspeller::callback<int>(aspeller::SpellerImpl*, acommon::KeyInfo const*, int, aspeller::UpdateMember::Type) Unexecuted instantiation: acommon::PosibErr<void> aspeller::callback<acommon::ParmString>(aspeller::SpellerImpl*, acommon::KeyInfo const*, acommon::ParmString, aspeller::UpdateMember::Type) |
548 | | |
549 | | ////////////////////////////////////////////////////////////////////// |
550 | | // |
551 | | // SpellerImpl inititization members |
552 | | // |
553 | | |
554 | | SpellerImpl::SpellerImpl() |
555 | 881 | : Speller(0) /* FIXME */, ignore_repl(true), |
556 | 881 | dicts_(0), personal_(0), session_(0), repl_(0), main_(0) |
557 | 881 | {} |
558 | | |
559 | | inline PosibErr<void> add_dicts(SpellerImpl * sp, DictList & d) |
560 | 865 | { |
561 | 4.25k | for (;!d.empty(); d.pop()) |
562 | 3.38k | { |
563 | 3.38k | if (!sp->locate(d.last()->id())) { |
564 | 3.38k | RET_ON_ERR(sp->add_dict(new SpellerDict(d.last()))); |
565 | 3.38k | } |
566 | 3.38k | } |
567 | 864 | return no_err; |
568 | 865 | } |
569 | | |
570 | 881 | PosibErr<void> SpellerImpl::setup(Config * c) { |
571 | 881 | assert (config_ == 0); |
572 | 881 | config_.reset(c); |
573 | | |
574 | 881 | ignore_repl = config_->retrieve_bool("ignore-repl"); |
575 | 881 | ignore_count = config_->retrieve_int("ignore"); |
576 | | |
577 | 881 | DictList to_add; |
578 | 881 | RET_ON_ERR(add_data_set(config_->retrieve("master-path"), *config_, &to_add, this)); |
579 | 863 | RET_ON_ERR(add_dicts(this, to_add)); |
580 | | |
581 | 863 | s_cmp.lang = lang_; |
582 | 863 | s_cmp.case_insensitive = config_->retrieve_bool("ignore-case"); |
583 | | |
584 | 863 | s_cmp_begin.lang = lang_; |
585 | 863 | s_cmp_begin.case_insensitive = s_cmp.case_insensitive; |
586 | 863 | s_cmp_begin.end = false; |
587 | | |
588 | 863 | s_cmp_middle.lang = lang_; |
589 | 863 | s_cmp_middle.case_insensitive = s_cmp.case_insensitive; |
590 | 863 | s_cmp_middle.begin = false; |
591 | 863 | s_cmp_middle.end = false; |
592 | | |
593 | 863 | s_cmp_end.lang = lang_; |
594 | 863 | s_cmp_end.case_insensitive = s_cmp.case_insensitive; |
595 | 863 | s_cmp_end.begin = false; |
596 | | |
597 | 863 | StringList extra_dicts; |
598 | 863 | config_->retrieve_list("extra-dicts", &extra_dicts); |
599 | 863 | StringListEnumeration els = extra_dicts.elements_obj(); |
600 | 863 | const char * dict_name; |
601 | 864 | while ( (dict_name = els.next()) != 0) { |
602 | 3 | RET_ON_ERR(add_data_set(dict_name,*config_, &to_add, this)); |
603 | 2 | RET_ON_ERR(add_dicts(this, to_add)); |
604 | 2 | } |
605 | | |
606 | 861 | bool use_other_dicts = config_->retrieve_bool("use-other-dicts"); |
607 | | |
608 | 861 | if (use_other_dicts && !personal_) |
609 | 854 | { |
610 | 854 | Dictionary * temp; |
611 | 854 | temp = new_default_writable_dict(*config_); |
612 | 854 | PosibErrBase pe = temp->load(config_->retrieve("personal-path"),*config_); |
613 | 854 | if (pe.has_err(cant_read_file)) |
614 | 854 | temp->set_check_lang(lang_name(), *config_); |
615 | 0 | else if (pe.has_err()) |
616 | 0 | return pe; |
617 | 854 | RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, personal_id))); |
618 | 854 | } |
619 | | |
620 | 861 | if (use_other_dicts && !session_) |
621 | 854 | { |
622 | 854 | Dictionary * temp; |
623 | 854 | temp = new_default_writable_dict(*config_); |
624 | 854 | temp->set_check_lang(lang_name(), *config_); |
625 | 854 | RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, session_id))); |
626 | 854 | } |
627 | | |
628 | 861 | if (use_other_dicts && !repl_) |
629 | 854 | { |
630 | 854 | ReplacementDict * temp = new_default_replacement_dict(*config_); |
631 | 854 | PosibErrBase pe = temp->load(config_->retrieve("repl-path"),*config_); |
632 | 854 | if (pe.has_err(cant_read_file)) |
633 | 854 | temp->set_check_lang(lang_name(), *config_); |
634 | 0 | else if (pe.has_err()) |
635 | 0 | return pe; |
636 | 854 | RET_ON_ERR(add_dict(new SpellerDict(temp, *config_, personal_repl_id))); |
637 | 854 | } |
638 | | |
639 | 861 | StringList wordlist_files; |
640 | 861 | config_->retrieve_list("wordlists", &wordlist_files); |
641 | 861 | if (!wordlist_files.empty()) { |
642 | 7 | Dictionary * dict = session_; |
643 | 7 | if (!dict) { |
644 | 7 | dict = new_default_writable_dict(*config_); |
645 | 7 | dict->set_check_lang(lang_name(), *config_); |
646 | 7 | RET_ON_ERR(add_dict(new SpellerDict(dict, *config_))); |
647 | 7 | } |
648 | 7 | const char * fn; |
649 | 7 | StringListEnumeration els = wordlist_files.elements_obj(); |
650 | 14 | while ( (fn = els.next()) != 0) { |
651 | 7 | FStream f; |
652 | 7 | RET_ON_ERR(f.open(fn, "r")); |
653 | 7 | IstreamEnumeration els(f); |
654 | 7 | WordListIterator wl_itr(&els, lang_, 0); |
655 | 7 | wl_itr.init_plain(*config_); |
656 | 7 | for (;;) { |
657 | 7 | PosibErr<bool> pe = wl_itr.adv(); |
658 | 7 | if (pe.has_err()) |
659 | 0 | return pe.with_file(fn); |
660 | 7 | if (!pe.data) break; |
661 | 0 | PosibErr<void> pev = dict->add(wl_itr->word); |
662 | 0 | if (pev.has_err()) |
663 | 0 | return pev.with_file(fn); |
664 | 0 | } |
665 | 7 | } |
666 | 7 | } |
667 | | |
668 | 861 | const char * sys_enc = lang_->charmap(); |
669 | 861 | ConfigConvKey user_enc = config_->retrieve_value("encoding"); |
670 | 861 | if (user_enc.val == "none") { |
671 | 737 | config_->replace("encoding", sys_enc); |
672 | 737 | user_enc = sys_enc; |
673 | 737 | } |
674 | | |
675 | 861 | PosibErr<Convert *> conv; |
676 | 861 | conv = new_convert(*c, user_enc, sys_enc, NormFrom); |
677 | 861 | if (conv.has_err()) return conv; |
678 | 851 | to_internal_.reset(conv); |
679 | 851 | conv = new_convert(*c, sys_enc, user_enc, NormTo); |
680 | 851 | if (conv.has_err()) return conv; |
681 | 851 | from_internal_.reset(conv); |
682 | | |
683 | 851 | unconditional_run_together_ = config_->retrieve_bool("run-together"); |
684 | 851 | run_together = unconditional_run_together_; |
685 | | |
686 | 851 | run_together_limit_ = config_->retrieve_int("run-together-limit"); |
687 | 851 | if (run_together_limit_ > 8) { |
688 | 0 | config_->replace("run-together-limit", "8"); |
689 | 0 | run_together_limit_ = 8; |
690 | 0 | } |
691 | 851 | run_together_min_ = config_->retrieve_int("run-together-min"); |
692 | | |
693 | 851 | camel_case_ = config_->retrieve_bool("camel-case"); |
694 | | |
695 | 851 | config_->add_notifier(new ConfigNotifier(this)); |
696 | | |
697 | 851 | config_->set_attached(true); |
698 | | |
699 | 851 | affix_info = lang_->affix(); |
700 | | |
701 | | // |
702 | | // setup word set lists |
703 | | // |
704 | | |
705 | 851 | typedef Vector<SpellerDict *> AllWS; AllWS all_ws; |
706 | 6.72k | for (SpellerDict * i = dicts_; i; i = i->next) { |
707 | 5.87k | if (i->dict->basic_type == Dict::basic_dict || |
708 | 5.87k | i->dict->basic_type == Dict::replacement_dict) { |
709 | 4.20k | all_ws.push_back(i); |
710 | 4.20k | } |
711 | 5.87k | } |
712 | | |
713 | 851 | const std::type_info * ti = 0; |
714 | 6.75k | while (!all_ws.empty()) |
715 | 5.90k | { |
716 | 5.90k | AllWS::iterator i0 = all_ws.end(); |
717 | 5.90k | int max = -2; |
718 | 5.90k | AllWS::iterator i = all_ws.begin(); |
719 | 22.6k | for (; i != all_ws.end(); ++i) |
720 | 16.7k | { |
721 | 16.7k | const Dictionary * ws = (*i)->dict; |
722 | 16.7k | if (ti && *ti != typeid(*ws)) continue; |
723 | 10.0k | if ((int)ws->size() > max) {max = ws->size(); i0 = i;} |
724 | 10.0k | } |
725 | | |
726 | 5.90k | if (i0 == all_ws.end()) {ti = 0; continue;} |
727 | | |
728 | 4.20k | SpellerDict * cur = *i0; |
729 | | |
730 | 4.20k | all_ws.erase(i0); |
731 | | |
732 | 4.20k | ti = &typeid(*cur->dict); |
733 | | |
734 | 4.20k | if (cur->use_to_check) { |
735 | 3.35k | check_ws.push_back(cur->dict); |
736 | 3.35k | if (cur->dict->affix_compressed) affix_ws.push_back(cur->dict); |
737 | 3.35k | } |
738 | 4.20k | if (cur->use_to_suggest) { |
739 | 4.20k | suggest_ws.push_back(cur->dict); |
740 | 4.20k | if (cur->dict->affix_compressed) suggest_affix_ws.push_back(cur->dict); |
741 | 4.20k | } |
742 | 4.20k | } |
743 | 851 | fast_scan = suggest_ws.front()->fast_scan; |
744 | 851 | fast_lookup = suggest_ws.front()->fast_lookup; |
745 | 851 | have_soundslike = lang_->have_soundslike(); |
746 | 851 | have_repl = lang_->have_repl(); |
747 | 851 | invisible_soundslike = suggest_ws.front()->invisible_soundslike; |
748 | 851 | soundslike_root_only = suggest_ws.front()->soundslike_root_only; |
749 | 851 | affix_compress = !affix_ws.empty(); |
750 | | |
751 | | // |
752 | | // Setup suggest |
753 | | // |
754 | | |
755 | 851 | PosibErr<Suggest *> pe; |
756 | 851 | pe = new_default_suggest(this); |
757 | 851 | if (pe.has_err()) return pe; |
758 | 847 | suggest_.reset(pe.data); |
759 | 847 | pe = new_default_suggest(this); |
760 | 847 | if (pe.has_err()) return pe; |
761 | 847 | intr_suggest_.reset(pe.data); |
762 | | |
763 | 847 | return no_err; |
764 | 847 | } |
765 | | |
766 | | ////////////////////////////////////////////////////////////////////// |
767 | | // |
768 | | // SpellerImpl destrution members |
769 | | // |
770 | | |
771 | 881 | SpellerImpl::~SpellerImpl() { |
772 | 6.83k | while (dicts_) { |
773 | 5.95k | SpellerDict * next = dicts_->next; |
774 | 5.95k | delete dicts_; |
775 | 5.95k | dicts_ = next; |
776 | 5.95k | } |
777 | 881 | } |
778 | | |
779 | | ////////////////////////////////////////////////////////////////////// |
780 | | // |
781 | | // SpellerImple setup tokenizer method |
782 | | // |
783 | | |
784 | | void SpellerImpl::setup_tokenizer(Tokenizer * tok) |
785 | 832 | { |
786 | 213k | for (int i = 0; i != 256; ++i) |
787 | 212k | { |
788 | 212k | tok->char_type_[i].word = lang_->is_alpha(i); |
789 | 212k | tok->char_type_[i].begin = lang_->special(i).begin; |
790 | 212k | tok->char_type_[i].middle = lang_->special(i).middle; |
791 | 212k | tok->char_type_[i].end = lang_->special(i).end; |
792 | 212k | } |
793 | 832 | tok->conv_ = to_internal_; |
794 | 832 | } |
795 | | |
796 | | |
797 | | ////////////////////////////////////////////////////////////////////// |
798 | | // |
799 | | // |
800 | | // |
801 | | |
802 | | SpellerDict::SpellerDict(Dict * d) |
803 | 3.38k | : dict(d), special_id(none_id), next(0) |
804 | 3.38k | { |
805 | 3.38k | switch (dict->basic_type) { |
806 | 1.69k | case Dict::basic_dict: |
807 | 1.69k | use_to_check = true; |
808 | 1.69k | use_to_suggest = true; |
809 | 1.69k | break; |
810 | 0 | case Dict::replacement_dict: |
811 | 0 | use_to_check = false; |
812 | 0 | use_to_suggest = true; |
813 | 1.69k | case Dict::multi_dict: |
814 | 1.69k | break; |
815 | 0 | default: |
816 | 0 | abort(); |
817 | 3.38k | } |
818 | 3.38k | save_on_saveall = false; |
819 | 3.38k | } |
820 | | |
821 | | SpellerDict::SpellerDict(Dict * w, const Config & c, SpecialId id) |
822 | 2.56k | : next(0) |
823 | 2.56k | { |
824 | 2.56k | dict = w; |
825 | 2.56k | special_id = id; |
826 | 2.56k | switch (id) { |
827 | 0 | case main_id: |
828 | 0 | if (dict->basic_type == Dict::basic_dict) { |
829 | |
|
830 | 0 | use_to_check = true; |
831 | 0 | use_to_suggest = true; |
832 | 0 | save_on_saveall = false; |
833 | |
|
834 | 0 | } else if (dict->basic_type == Dict::replacement_dict) { |
835 | | |
836 | 0 | use_to_check = false; |
837 | 0 | use_to_suggest = false; |
838 | 0 | save_on_saveall = false; |
839 | | |
840 | 0 | } else { |
841 | | |
842 | 0 | abort(); |
843 | | |
844 | 0 | } |
845 | 0 | break; |
846 | 854 | case personal_id: |
847 | 854 | use_to_check = true; |
848 | 854 | use_to_suggest = true; |
849 | 854 | save_on_saveall = true; |
850 | 854 | break; |
851 | 854 | case session_id: |
852 | 854 | use_to_check = true; |
853 | 854 | use_to_suggest = true; |
854 | 854 | save_on_saveall = false; |
855 | 854 | break; |
856 | 854 | case personal_repl_id: |
857 | 854 | use_to_check = false; |
858 | 854 | use_to_suggest = true; |
859 | 854 | save_on_saveall = c.retrieve_bool("save-repl"); |
860 | 854 | break; |
861 | 7 | case none_id: |
862 | 7 | break; |
863 | 2.56k | } |
864 | 2.56k | } |
865 | | |
866 | | extern "C" |
867 | | Speller * libaspell_speller_default_LTX_new_speller_class(SpellerLtHandle) |
868 | 881 | { |
869 | 881 | return new SpellerImpl(); |
870 | 881 | } |
871 | | } |
872 | | |