Added removing accents
[mdictionary] / trunk / src / plugins / xdxf / src / xdxfplugin.cpp
1 /*******************************************************************************
2
3     This file is part of mDictionary.
4
5     mDictionary 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 3 of the License, or
8     (at your option) any later version.
9
10     mDictionary 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 mDictionary.  If not, see <http://www.gnu.org/licenses/>.
17
18     Copyright 2010 Comarch S.A.
19
20 *******************************************************************************/
21
22 /*! \file xdxfplugin.cpp
23 */
24
25 #include "xdxfplugin.h"
26 #include <QDebug>
27 #include "../../../includes/Notify.h"
28
29 XdxfPlugin::XdxfPlugin(QObject *parent) : CommonDictInterface(parent),
30                     _langFrom(tr("")), _langTo(tr("")),_name(tr("")),
31                     _type(tr("xdxf")), _infoNote(tr("")) {
32     _wordsCount = -1;
33     _settings = new Settings();
34     _dictDialog = new XdxfDictDialog(this);
35     cachingDialog = new XdxfCachingDialog();
36
37     connect(cachingDialog, SIGNAL(cancelCaching()),
38             this, SLOT(stop()));
39
40     connect(this, SIGNAL(updateCachingProgress(int,int)),
41             cachingDialog, SLOT(updateCachingProgress(int,int)));
42
43     _settings->setValue("type","xdxf");
44
45     stopped = false;
46
47     _icon = QIcon(":/icons/xdxf.png");
48     initAccents();
49 }
50
51
52
53 XdxfPlugin::~XdxfPlugin()
54 {
55 //  QString connection(db.connectionName());
56 //   db.close();
57 //  QSqlDatabase::removeDatabase(connection);
58
59     delete _settings;
60 }
61
62 void XdxfPlugin::initAccents() {
63     letters['a'] = QRegExp("[ÀàÁáÂÂâÃãÄäÅåæÆĀāĂ㥹]", Qt::CaseInsensitive);
64     letters['c'] = QRegExp("[ÇçÈçŒĆćĈĉĊċČč]", Qt::CaseInsensitive);
65     letters['d'] = QRegExp("[ÐĐđĎď]", Qt::CaseInsensitive);
66     letters['e'] = QRegExp("[ÈéèÉÊêËëĒēĔĕĖėĘęĚěē]", Qt::CaseInsensitive);
67     letters['f'] = QRegExp("[ſ]", Qt::CaseInsensitive);
68     letters['g'] = QRegExp("[ĠġĢģĜĝĞğ]", Qt::CaseInsensitive);
69     letters['h'] = QRegExp("[ħĤĥĦ]", Qt::CaseInsensitive);
70     letters['i'] = QRegExp("[ÌìÍíÎîÏïİijĨĩĪīĬĭĮį]", Qt::CaseInsensitive);
71     letters['j'] = QRegExp("[Ĵĵ]", Qt::CaseInsensitive);
72     letters['k'] = QRegExp("[Ķķĸ]", Qt::CaseInsensitive);
73     letters['l'] = QRegExp("[ŀŁłĹĺĻļĽľĿ]", Qt::CaseInsensitive);
74     letters['n'] = QRegExp("[ÑñŃńŅņŇňʼnŊŋ]", Qt::CaseInsensitive);
75     letters['o'] = QRegExp("[ÒòÓóÔÕõÖöØøŐőœŌōŎŏ]", Qt::CaseInsensitive);
76     letters['r'] = QRegExp("[ŕŖŖŗŘř]", Qt::CaseInsensitive);
77     letters['s'] = QRegExp("[ߊšŚśŜŝŞş]", Qt::CaseInsensitive);
78     letters['t'] = QRegExp("[ŢţŤťŦŧ]", Qt::CaseInsensitive);
79     letters['u'] = QRegExp("[ÙùÚúÛûÜüŰűŲųŨũŪūŬŮů]", Qt::CaseInsensitive);
80     letters['w'] = QRegExp("[Ŵŵ]", Qt::CaseInsensitive);
81     letters['y'] = QRegExp("[ÿÝýŶŷŸ]", Qt::CaseInsensitive);
82     letters['z'] = QRegExp("[ŹźŻżŽž]", Qt::CaseInsensitive);
83     noLetter = QRegExp("[^a-z ]", Qt::CaseInsensitive);
84 }
85
86 QString XdxfPlugin::langFrom() const {   
87     return _langFrom;
88 }
89
90 QString XdxfPlugin::langTo() const {
91     return  _langTo;
92 }
93
94 QString XdxfPlugin::name() const {
95     return  _name;
96 }
97
98 QString XdxfPlugin::type() const {
99 //    return _settings->value("type");
100     return _type;
101 }
102
103 QString XdxfPlugin::infoNote() const {
104     return  _infoNote;
105 }
106
107 QList<Translation*> XdxfPlugin::searchWordList(QString word, int limit) {
108     if(word.indexOf("*")==-1 && word.indexOf("?")==-1 && word.indexOf("_")==-1
109        && word.indexOf("%")==-1)
110         word+="*";
111     if(isCached())
112         return searchWordListCache(word,limit);
113     return searchWordListFile(word, limit);
114 }
115
116 QList<Translation*> XdxfPlugin::searchWordListCache(QString word, int limit) {
117
118     QSet<Translation*> translations;
119     QString cacheFilePath = _settings->value("cache_path");
120         db.setDatabaseName(cacheFilePath);
121         if(!db.open()) {
122             qDebug() << "Database error" << db.lastError().text() << endl;
123             Q_EMIT notify(Notify::Warning, QString("Cache database cannot be "
124                     "opened for %1 dictionary. Searching in xdxf file. "
125                     "You may want to recache.").arg(name()));
126             return searchWordListFile(word, limit);
127         }
128
129         stopped = false;
130         word = word.toLower();
131         word = word.replace("*", "%");
132         word = word.replace("?", "_");
133
134         QSqlQuery cur(db);
135         if(limit !=0)
136             cur.prepare("select word from dict where word like ? limit ?");
137         else
138             cur.prepare("select word from dict where word like ?");
139         cur.addBindValue(word);
140         if(limit !=0)
141             cur.addBindValue(limit);
142         cur.exec();
143         bool in = false;
144         while(cur.next()){
145             in = true;
146             bool ok=true;
147             Translation *tran;
148             foreach(tran,translations) {
149                 if(tran->key().toLower()==cur.value(0).toString().toLower())
150                         ok=false;
151             }
152             if(ok)  /*add key word to list*/
153                 translations.insert(new TranslationXdxf(
154                         cur.value(0).toString().toLower(),
155                         _infoNote, this));
156         }
157         if(!in) {
158
159             QSqlQuery cur(db);
160             if(limit !=0)
161                 cur.prepare("select word from dict where normalized like ? limit ?");
162             else
163                 cur.prepare("select word from dict where normalized like ?");
164             cur.addBindValue(word);
165             if(limit !=0)
166                 cur.addBindValue(limit);
167             cur.exec();
168             while(cur.next()){
169                 bool ok=true;
170                 Translation *tran;
171                 foreach(tran,translations) {
172                     if(tran->key().toLower()==cur.value(0).toString().toLower())
173                             ok=false;
174                 }
175                 if(ok)  /*add key word to list*/
176                     translations.insert(new TranslationXdxf(
177                             cur.value(0).toString().toLower(),
178                             _infoNote, this));
179             }
180         }
181         db.close();
182     return translations.toList();
183 }
184
185 QList<Translation*> XdxfPlugin::searchWordListFile(QString word, int limit) {
186     QTime time;
187     time.start();
188     QSet<Translation*> translations;
189     QFile dictionaryFile(path);
190
191     word = word.toLower();
192     //word = removeAccents(word);
193
194     stopped = false;
195     QRegExp regWord(word);
196     regWord.setCaseSensitivity(Qt::CaseInsensitive);
197     regWord.setPatternSyntax(QRegExp::Wildcard);
198     if(!dictionaryFile.open(QFile::ReadOnly | QFile::Text)) {
199         qDebug()<<"Error: could not open file";
200         Q_EMIT notify(Notify::Warning,
201                 QString("Xdxf file cannot be read for %1").arg(name()));
202         return translations.toList();
203     }
204
205     QXmlStreamReader reader(&dictionaryFile);
206     /*search words list*/
207     QString a;
208
209     int i=0;
210     while(!reader.atEnd() && !stopped){
211         reader.readNextStartElement();
212         if(reader.name()=="ar") {
213             while(reader.name()!="k" && !reader.atEnd())
214                 reader.readNextStartElement();
215             if(!reader.atEnd())
216                 a = reader.readElementText();
217             if((regWord.exactMatch(a) || regWord.exactMatch(removeAccents(a))) &&
218                     (i<limit || limit==0)) {
219                 bool ok=true;
220                 Translation *tran;
221                 foreach(tran,translations) {
222                     if(tran->key().toLower()==a.toLower())
223                         ok=false;  /*if key word is in the dictionary more that one */
224                 }
225                 if(ok)  /*add key word to list*/
226                     translations<<(new TranslationXdxf(a.toLower(),
227                                 _infoNote,this));
228                 i++;
229                 if(i>=limit && limit!=0)
230                     break;
231             }
232         }
233         this->thread()->yieldCurrentThread();
234     }
235     stopped=false;
236     dictionaryFile.close();
237     qDebug() << time.elapsed();
238     return translations.toList();
239 }
240
241 QString XdxfPlugin::search(QString key) {
242 //    if(_settings->value("cached") == "true")
243     if(isCached())
244         return searchCache(key);
245     return searchFile(key);
246 }
247
248 QString XdxfPlugin::searchCache(QString key) {
249     QString result("");
250     QString cacheFilePath = _settings->value("cache_path");
251     db.setDatabaseName(cacheFilePath);
252     key = key.toLower();
253
254     if(!db.open()) {
255         qDebug() << "Database error" << db.lastError().text() << endl;
256         Q_EMIT notify(Notify::Warning, QString("Cache database cannot be "
257                 "opened for %1 dictionary. Searching in xdxf file. "
258                 "You may want to recache.").arg(name()));
259         return searchFile(key);
260     }
261
262     QSqlQuery cur(db);
263     cur.prepare("select translation from dict where word like ?");
264     cur.addBindValue(key);
265     cur.exec();
266     while(cur.next())
267         result += cur.value(0).toString();
268
269     db.close();
270
271     return result;
272
273 }
274
275 QString XdxfPlugin::searchFile(QString key) {
276     key = key.toLower();
277     QFile dictionaryFile(path);
278     QString resultString("");
279     if(!dictionaryFile.open(QFile::ReadOnly | QFile::Text)) {
280         Q_EMIT notify(Notify::Warning,
281                 QString("Xdxf file cannot be read for %1").arg(name()));
282         qDebug()<<"Error: could not open file";
283         return "";
284     }
285     QXmlStreamReader reader(&dictionaryFile);
286     QString a;
287
288     bool match =false;
289     stopped = false;
290     while (!reader.atEnd()&& !stopped) {
291         reader.readNext();
292         if(reader.tokenType() == QXmlStreamReader::StartElement) {
293             if(reader.name()=="k") {
294                 a = reader.readElementText();
295                 if(a.toLower()==key.toLower())
296                     match = true;
297             }
298         }
299         if(match) {
300             QString temp("");
301             while(reader.name()!="ar" && !reader.atEnd()) {
302                 if(reader.name()!="" && reader.name()!="k") {
303                     if(reader.tokenType()==QXmlStreamReader::EndElement)
304                         temp+=tr("</");
305                     if(reader.tokenType()==QXmlStreamReader::StartElement)
306                         temp+=tr("<");
307                     temp+=reader.name().toString();
308                     if(reader.name().toString()=="c" &&
309                             reader.tokenType()==QXmlStreamReader::StartElement)
310                        temp= temp + tr(" c=\"") + reader.attributes().
311                                value(tr("c")).toString() + tr("\"");
312                     temp+=tr(">");
313                 }
314                 temp+= reader.text().toString().replace("<","&lt;").
315                         replace(">","&gt;");
316                 reader.readNext();
317             }
318             if(temp.at(0)==QChar('\n'))
319                 temp.remove(0,1);
320             resultString+=tr("<key>") + a +tr("</key>");
321             resultString+=tr("<t>") + temp + tr("</t>");
322             match=false;
323         }
324         this->thread()->yieldCurrentThread();
325     }
326     stopped=false;
327     dictionaryFile.close();
328
329     return resultString;
330 }
331
332 void XdxfPlugin::stop() {
333     stopped=true;
334 }
335
336 DictDialog* XdxfPlugin::dictDialog() {
337      return _dictDialog;
338 }
339
340 void XdxfPlugin::setPath(QString path){
341     this->path=path;
342     _settings->setValue("path",path);
343     //getDictionaryInfo();
344 }
345
346 CommonDictInterface* XdxfPlugin::getNew(const Settings *settings) const {
347     XdxfPlugin *plugin = new XdxfPlugin();
348     static int a=0;
349     if(settings){
350         plugin->setPath(settings->value("path"));
351         QStringList list = settings->keys();
352         foreach(QString key, list)
353             plugin->settings()->setValue(key, settings->value(key));
354
355         a=a+1;
356         plugin->db_name = plugin->_settings->value("type")
357                          + plugin->_settings->value("path");
358         plugin->db = QSqlDatabase::addDatabase("QSQLITE", plugin->db_name);
359
360         if(settings->value("cached").isEmpty() &&
361            settings->value("generateCache") == "true") {
362             plugin->makeCache("");
363         }
364         delete settings;
365     }
366     plugin->getDictionaryInfo();
367     return  plugin;
368 }
369
370 bool XdxfPlugin::isAvailable() const {
371     return true;
372 }
373
374 void XdxfPlugin::setHash(uint _hash) {
375     this->_hash=_hash;
376 }
377
378 uint XdxfPlugin::hash() const {
379    return _hash;
380 }
381
382 Settings* XdxfPlugin::settings() {
383     return _settings;
384 }
385
386 bool XdxfPlugin::isCached() {
387     if(_settings->value("cached") == "true")
388         return true;
389     return false;
390 }
391
392 void XdxfPlugin::setSettings(Settings *settings) {
393
394     QString oldPath = _settings->value("path");
395     if(oldPath != settings->value("path")) {
396         setPath(settings->value("path"));
397     }
398
399     if((_settings->value("cached") == "false" ||
400         _settings->value("cached").isEmpty()) &&
401        settings->value("generateCache") == "true") {
402         makeCache("");
403     }
404     else {
405        _settings->setValue("cached", "false");
406     }
407     delete settings;
408
409     emit settingsChanged();
410 }
411
412 void XdxfPlugin::getDictionaryInfo() {
413     QFile dictionaryFile(path);
414     if(!dictionaryFile.open(QFile::ReadOnly | QFile::Text)) {
415        Q_EMIT notify(Notify::Warning,
416                QString("Xdxf file cannot be read dictionary"));
417         qDebug()<<"Error: could not open file";
418         return;
419     }
420
421     QXmlStreamReader reader(&dictionaryFile);
422     reader.readNextStartElement();
423     if(reader.name()=="xdxf") {
424       if(reader.attributes().hasAttribute("lang_from"))
425         _langFrom = reader.attributes().value("lang_from").toString();
426       if(reader.attributes().hasAttribute("lang_to"))
427         _langTo = reader.attributes().value("lang_to").toString();
428     }
429     reader.readNextStartElement();
430     if(reader.name()=="full_name")
431         _name=reader.readElementText();
432     reader.readNextStartElement();
433     if(reader.name()=="description")
434         _infoNote=reader.readElementText();
435
436     QString format = "png";
437     QString initialPath = QDir::currentPath() + tr("/xdxf.") + format;
438
439     _infoNote="path=\""+initialPath+"\">"+"\n" + _name + " [" + _langFrom +
440             "-" + _langTo + "] "+ "(" + _type + ")";
441
442     dictionaryFile.close();
443 }
444
445
446
447 QString XdxfPlugin::removeAccents(QString string) {
448     string = string.toLower();
449     QString normalized = string.normalized(QString::NormalizationForm_KD);
450     foreach(QChar let, letters.keys())
451         normalized.replace(letters[let], QString(let));
452
453     normalized.replace(noLetter, "");
454     //qDebug() << "NORMALIZED: " << normalized;
455     return normalized;
456     return string;
457 }
458
459
460
461 QIcon* XdxfPlugin::icon() {
462     return &_icon;
463 }
464
465 int XdxfPlugin::countWords() {
466     if(_wordsCount > 0)
467         return _wordsCount;
468
469     QFile dictionaryFile(path);
470     if(!dictionaryFile.open(QFile::ReadOnly | QFile::Text)) {
471         Q_EMIT notify(Notify::Warning,
472                 QString("Xdxf file cannot be read for %1 dictionary")
473                 .arg(name()));
474         qDebug()<<"Error: could not open file";
475         return -1;
476     }
477
478     dictionaryFile.seek(0);
479
480     long wordsCount = 0;
481
482     QString line;
483     while(!dictionaryFile.atEnd()) {
484         line = dictionaryFile.readLine();
485         if(line.contains("<k>")) {
486             wordsCount++;
487         }
488     }
489     _wordsCount = wordsCount;
490     dictionaryFile.close();
491     return wordsCount;
492 }
493
494 bool XdxfPlugin::makeCache(QString dir) {
495     cachingDialog->setVisible(true);
496     QCoreApplication::processEvents();
497     stopped = false;
498     QFileInfo dictFileN(_settings->value("path"));
499     QString cachePathN;
500     cachePathN = QDir::homePath() + "/.mdictionary/"
501                  + dictFileN.completeBaseName() + ".cache";
502
503     QFile dictionaryFile(dictFileN.filePath());
504
505
506     if (!dictionaryFile.open(QFile::ReadOnly | QFile::Text)) {
507         Q_EMIT updateCachingProgress(100, 0);
508         Q_EMIT notify(Notify::Warning,
509                 QString("Xdxf file cannot be read for %1 dictionary")
510                 .arg(name()));
511         return 0;
512     }
513     QXmlStreamReader reader(&dictionaryFile);
514
515     db.setDatabaseName(cachePathN);
516     if(!db.open()) {
517         qDebug() << "Database error" << db.lastError().text() << endl;
518         Q_EMIT updateCachingProgress(100, 0);
519         Q_EMIT notify(Notify::Warning, QString("Cache database cannot be "
520                 "opened for %1 dictionary. Searching in xdxf file. "
521                 "You may want to recache.").arg(name()));
522         return false;
523     }
524     QCoreApplication::processEvents();
525     QSqlQuery cur(db);
526     cur.exec("PRAGMA synchronous = 0");
527     cur.exec("drop table dict");
528     QCoreApplication::processEvents();
529     cur.exec("create table dict(word text, normalized text ,translation text)");
530     int counter = 0;
531     cur.exec("BEGIN;");
532
533     QString a;
534     bool match = false;
535     QTime timer;
536     timer.start();
537     countWords();
538
539     int lastProg = -1;
540
541
542     counter=0;
543     while (!reader.atEnd() && !stopped) {
544
545         QCoreApplication::processEvents();
546         reader.readNext();
547
548         if(reader.tokenType() == QXmlStreamReader::StartElement) {
549             if(reader.name()=="k"){
550                 a = reader.readElementText();
551                 match = true;
552             }
553         }
554         if(match) {
555             QString temp("");
556             while(reader.name()!="ar" && !reader.atEnd()) {
557                 if(reader.name()!="" && reader.name()!="k") {
558                     if(reader.tokenType()==QXmlStreamReader::EndElement)
559                         temp+=tr("</");
560                     if(reader.tokenType()==QXmlStreamReader::StartElement)
561                         temp+=tr("<");
562                     temp+=reader.name().toString();
563                     if(reader.name().toString()=="c" && reader.tokenType()==QXmlStreamReader::StartElement)
564                        temp= temp + tr(" c=\"") + reader.attributes().value(tr("c")).toString() + tr("\"");
565                     temp+=tr(">");
566                 }
567                 temp+= reader.text().toString().replace("<","&lt;").replace(">","&gt;");;
568                 reader.readNext();
569             }
570             if(temp.at(0)==QChar('\n'))
571                 temp.remove(0,1);
572             temp=tr("<key>") + a + tr("</key>") + tr("<t>") + temp+ tr("</t>");
573             match=false;
574             cur.prepare("insert into dict values(?,?,?)");
575             cur.addBindValue(a);
576             cur.addBindValue(removeAccents(a));
577             cur.addBindValue(temp);
578             cur.exec();
579             counter++;
580             int prog = counter*100/_wordsCount;
581             if(prog % 5 == 0 && lastProg != prog) {
582                 qDebug() << prog;
583                 Q_EMIT updateCachingProgress(prog,
584                                              timer.restart());
585                 lastProg = prog;
586             }
587         }
588     }
589
590     cur.exec("END;");
591     cur.exec("select count(*) from dict");
592
593     countWords();
594     cachingDialog->setVisible(false);
595
596     if(!cur.next() || countWords() != cur.value(0).toInt())
597     {
598         Q_EMIT updateCachingProgress(100, timer.restart());
599         Q_EMIT notify(Notify::Warning,
600                 QString("Database caching error, please try againg."));
601         db.close();
602         return false;
603     }
604     _settings->setValue("cache_path", cachePathN);
605     _settings->setValue("cached", "true");
606
607
608     db.close();
609     return true;
610 }
611
612 Q_EXPORT_PLUGIN2(xdxf, XdxfPlugin)