Merge branch 'master' into google
[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 \author Jakub Jaszczynski <j.j.jaszczynski@gmail.com>
24 */
25
26 #include "xdxfplugin.h"
27 #include <QDebug>
28 #include "../../../includes/Notify.h"
29
30 XdxfPlugin::XdxfPlugin(QObject *parent) : CommonDictInterface(parent),
31                     _langFrom(tr("")), _langTo(tr("")),_name(tr("")),
32                     _type(tr("xdxf")), _infoNote(tr("")) {
33     _wordsCount = -1;
34     _settings = new Settings();
35     _dictDialog = new XdxfDictDialog(this);
36     cachingDialog = new XdxfCachingDialog();
37
38     connect(cachingDialog, SIGNAL(cancelCaching()),
39             this, SLOT(stop()));
40
41     connect(this, SIGNAL(updateCachingProgress(int,int)),
42             cachingDialog, SLOT(updateCachingProgress(int,int)));
43
44     _settings->setValue("type","xdxf");
45
46     stopped = false;
47
48     _icon = QIcon(":/icons/xdxf.png");
49     initAccents();
50 }
51
52
53
54 XdxfPlugin::~XdxfPlugin() {
55     delete _settings;
56 }
57
58
59 QString XdxfPlugin::langFrom() const {   
60     return _langFrom;
61 }
62
63 QString XdxfPlugin::langTo() const {
64     return  _langTo;
65 }
66
67 QString XdxfPlugin::name() const {
68     return  _name;
69 }
70
71 QString XdxfPlugin::type() const {
72     return _type;
73 }
74
75 QString XdxfPlugin::infoNote() const {
76     return  _infoNote;
77 }
78
79 QList<Translation*> XdxfPlugin::searchWordList(QString word, int limit) {
80     if(word.indexOf("*")==-1 && word.indexOf("?")==-1 && word.indexOf("_")==-1
81        && word.indexOf("%")==-1)
82         word+="*";
83     if(isCached())
84         return searchWordListCache(word,limit);
85     return searchWordListFile(word, limit);
86 }
87
88 QList<Translation*> XdxfPlugin::searchWordListCache(QString word, int limit) {
89
90     QSet<Translation*> translations;
91     QString cacheFilePath = _settings->value("cache_path");
92         db.setDatabaseName(cacheFilePath);
93         if(!db.open()) {
94             qDebug() << "Database error" << db.lastError().text() << endl;
95             Q_EMIT notify(Notify::Warning, QString("Cache database cannot be "
96                     "opened for %1 dictionary. Searching in xdxf file. "
97                     "You may want to recache.").arg(name()));
98             return searchWordListFile(word, limit);
99         }
100
101         stopped = false;
102         word = word.toLower();
103         word = word.replace("*", "%");
104         word = word.replace("?", "_");
105
106         QSqlQuery cur(db);
107         if(limit !=0)
108             cur.prepare("select word from dict where word like ? or normalized like ? limit ?");
109         else
110             cur.prepare("select word from dict where word like ? or normalized like ?");
111         cur.addBindValue(word);
112         cur.addBindValue(word);
113         if(limit !=0)
114             cur.addBindValue(limit);
115         cur.exec();
116         bool in = false;
117         while(cur.next()){
118             in = true;
119             bool ok=true;
120             Translation *tran;
121             foreach(tran,translations) {
122                 if(tran->key().toLower()==cur.value(0).toString().toLower())
123                         ok=false;
124             }
125             if(ok)  /*add key word to list*/
126                 translations.insert(new TranslationXdxf(
127                         cur.value(0).toString().toLower(),
128                         _infoNote, this));
129         }
130         db.close();
131     return translations.toList();
132 }
133
134 QList<Translation*> XdxfPlugin::searchWordListFile(QString word, int limit) {
135     QTime time;
136     time.start();
137     QSet<Translation*> translations;
138     QFile dictionaryFile(path);
139
140     word = word.toLower();
141     //word = removeAccents(word);
142
143     stopped = false;
144     QRegExp regWord(word);
145     regWord.setCaseSensitivity(Qt::CaseInsensitive);
146     regWord.setPatternSyntax(QRegExp::Wildcard);
147     if(!dictionaryFile.open(QFile::ReadOnly | QFile::Text)) {
148         qDebug()<<"Error: could not open file";
149         Q_EMIT notify(Notify::Warning,
150                 QString("Xdxf file cannot be read for %1").arg(name()));
151         return translations.toList();
152     }
153
154     QXmlStreamReader reader(&dictionaryFile);
155     /*search words list*/
156     QString a;
157
158     int i=0;
159     while(!reader.atEnd() && !stopped){
160         reader.readNextStartElement();
161         if(reader.name()=="ar") {
162             while(reader.name()!="k" && !reader.atEnd())
163                 reader.readNextStartElement();
164             if(!reader.atEnd())
165                 a = reader.readElementText();
166             if((regWord.exactMatch(a) || regWord.exactMatch(removeAccents(a))) &&
167                     (i<limit || limit==0)) {
168                 bool ok=true;
169                 Translation *tran;
170                 foreach(tran,translations) {
171                     if(tran->key().toLower()==a.toLower())
172                         ok=false;  /*if key word is in the dictionary more that one */
173                 }
174                 if(ok)  /*add key word to list*/
175                     translations<<(new TranslationXdxf(a.toLower(),
176                                 _infoNote,this));
177                 i++;
178                 if(i>=limit && limit!=0)
179                     break;
180             }
181         }
182         this->thread()->yieldCurrentThread();
183     }
184     stopped=false;
185     dictionaryFile.close();
186     qDebug() << time.elapsed();
187     return translations.toList();
188 }
189
190 QString XdxfPlugin::search(QString key) {
191 //    if(_settings->value("cached") == "true")
192     if(isCached())
193         return searchCache(key);
194     return searchFile(key);
195 }
196
197 QString XdxfPlugin::searchCache(QString key) {
198     QString result("");
199     QString cacheFilePath = _settings->value("cache_path");
200     db.setDatabaseName(cacheFilePath);
201     key = key.toLower();
202
203     if(!db.open()) {
204         qDebug() << "Database error" << db.lastError().text() << endl;
205         Q_EMIT notify(Notify::Warning, QString("Cache database cannot be "
206                 "opened for %1 dictionary. Searching in xdxf file. "
207                 "You may want to recache.").arg(name()));
208         return searchFile(key);
209     }
210
211     QSqlQuery cur(db);
212     cur.prepare("select translation from dict where word like ?");
213     cur.addBindValue(key);
214     cur.exec();
215     while(cur.next())
216         result += cur.value(0).toString();
217
218     db.close();
219
220     return result;
221
222 }
223
224 QString XdxfPlugin::searchFile(QString key) {
225     key = key.toLower();
226     QFile dictionaryFile(path);
227     QString resultString("");
228     if(!dictionaryFile.open(QFile::ReadOnly | QFile::Text)) {
229         Q_EMIT notify(Notify::Warning,
230                 QString("Xdxf file cannot be read for %1").arg(name()));
231         qDebug()<<"Error: could not open file";
232         return "";
233     }
234     QXmlStreamReader reader(&dictionaryFile);
235     QString a;
236
237     bool match =false;
238     stopped = false;
239     while (!reader.atEnd()&& !stopped) {
240         reader.readNext();
241         if(reader.tokenType() == QXmlStreamReader::StartElement) {
242             if(reader.name()=="k") {
243                 a = reader.readElementText();
244                 if(a.toLower()==key.toLower())
245                     match = true;
246             }
247         }
248         if(match) {
249             QString temp("");
250             while(reader.name()!="ar" && !reader.atEnd()) {
251                 if(reader.name()!="" && reader.name()!="k") {
252                     if(reader.tokenType()==QXmlStreamReader::EndElement)
253                         temp+=tr("</");
254                     if(reader.tokenType()==QXmlStreamReader::StartElement)
255                         temp+=tr("<");
256                     temp+=reader.name().toString();
257                     if(reader.name().toString()=="c" &&
258                             reader.tokenType()==QXmlStreamReader::StartElement)
259                        temp= temp + tr(" c=\"") + reader.attributes().
260                                value(tr("c")).toString() + tr("\"");
261                     temp+=tr(">");
262                 }
263                 temp+= reader.text().toString().replace("<","&lt;").
264                         replace(">","&gt;");
265                 reader.readNext();
266             }
267             if(temp.at(0)==QChar('\n'))
268                 temp.remove(0,1);
269             resultString+=tr("<key>") + a +tr("</key>");
270             resultString+=tr("<t>") + temp + tr("</t>");
271             match=false;
272         }
273         this->thread()->yieldCurrentThread();
274     }
275     stopped=false;
276     dictionaryFile.close();
277
278     return resultString;
279 }
280
281 void XdxfPlugin::stop() {
282     stopped=true;
283 }
284
285 DictDialog* XdxfPlugin::dictDialog() {
286      return _dictDialog;
287 }
288
289 void XdxfPlugin::setPath(QString path){
290     this->path=path;
291     _settings->setValue("path",path);
292     //getDictionaryInfo();
293 }
294
295 CommonDictInterface* XdxfPlugin::getNew(const Settings *settings) const {
296     XdxfPlugin *plugin = new XdxfPlugin();
297     if(settings){
298         plugin->setPath(settings->value("path"));
299         QStringList list = settings->keys();
300
301         plugin->db_name = plugin->_settings->value("type")
302                          + plugin->_settings->value("path");
303         plugin->db = QSqlDatabase::addDatabase("QSQLITE", plugin->db_name);
304
305         if(settings->value("cached").isEmpty() &&
306            settings->value("generateCache") == "true") {
307             plugin->makeCache("");
308         }
309         foreach(QString key, list)
310            if(key != "generateCache")
311                 plugin->settings()->setValue(key, settings->value(key));
312         delete settings;
313     }
314     plugin->getDictionaryInfo();
315     return  plugin;
316 }
317
318 bool XdxfPlugin::isAvailable() const {
319     return true;
320 }
321
322
323
324 void XdxfPlugin::setHash(uint _hash) {
325     this->_hash=_hash;
326 }
327
328
329
330 uint XdxfPlugin::hash() const {
331    return _hash;
332 }
333
334
335
336 Settings* XdxfPlugin::settings() {
337     return _settings;
338 }
339
340
341
342 bool XdxfPlugin::isCached() {
343     if(_settings->value("cached") == "true")
344         return true;
345     return false;
346 }
347
348
349
350 void XdxfPlugin::setSettings(Settings *settings) {
351
352     QString oldPath = _settings->value("path");
353     if(oldPath != settings->value("path")) {
354         setPath(settings->value("path"));
355     }
356
357    foreach(QString key, settings->keys())
358        if(key != "generateCache")
359            _settings->setValue(key, settings->value(key));
360
361     if((_settings->value("cached") == "false" ||
362             _settings->value("cached").isEmpty()) &&
363             settings->value("generateCache") == "true") {
364         makeCache("");
365     }
366     else if (settings->value("generateCache") != "true") {
367        _settings->setValue("cached", "false");
368     }
369
370
371     delete settings;
372
373     Q_EMIT settingsChanged();
374 }
375
376
377
378 void XdxfPlugin::getDictionaryInfo() {
379     QFile dictionaryFile(path);
380     if(!dictionaryFile.open(QFile::ReadOnly | QFile::Text)) {
381        Q_EMIT notify(Notify::Warning,
382                QString("Xdxf file cannot be read dictionary"));
383         qDebug()<<"Error: could not open file";
384         return;
385     }
386
387     QXmlStreamReader reader(&dictionaryFile);
388     reader.readNextStartElement();
389     if(reader.name()=="xdxf") {
390       if(reader.attributes().hasAttribute("lang_from"))
391         _langFrom = reader.attributes().value("lang_from").toString();
392       if(reader.attributes().hasAttribute("lang_to"))
393         _langTo = reader.attributes().value("lang_to").toString();
394     }
395     reader.readNextStartElement();
396     if(reader.name()=="full_name")
397         _name=reader.readElementText();
398     reader.readNextStartElement();
399     if(reader.name()=="description")
400         _infoNote=reader.readElementText();
401
402     QString format = "png";
403     QString initialPath = QDir::currentPath() + tr("/xdxf.") + format;
404
405     _infoNote="path=\""+initialPath+"\"> \n" + _name + " [" + _langFrom + "-" + _langTo + "] (" + _type + ")";
406     dictionaryFile.close();
407 }
408
409
410
411 QIcon* XdxfPlugin::icon() {
412     return &_icon;
413 }
414
415 int XdxfPlugin::countWords() {
416     if(_wordsCount > 0)
417         return _wordsCount;
418
419     QFile dictionaryFile(path);
420     if(!dictionaryFile.open(QFile::ReadOnly | QFile::Text)) {
421         Q_EMIT notify(Notify::Warning,
422                 QString("Xdxf file cannot be read for %1 dictionary")
423                 .arg(name()));
424         qDebug()<<"Error: could not open file";
425         return -1;
426     }
427
428     dictionaryFile.seek(0);
429
430     long wordsCount = 0;
431
432     QString line;
433     while(!dictionaryFile.atEnd()) {
434         line = dictionaryFile.readLine();
435         if(line.contains("<k>")) {
436             wordsCount++;
437         }
438     }
439     _wordsCount = wordsCount;
440     dictionaryFile.close();
441     return wordsCount;
442 }
443
444 bool XdxfPlugin::makeCache(QString dir) {
445     cachingDialog->setVisible(true);
446     QCoreApplication::processEvents();
447     stopped = false;
448     QFileInfo dictFileN(_settings->value("path"));
449     QString cachePathN;
450     cachePathN = QDir::homePath() + "/.mdictionary/"
451                  + dictFileN.completeBaseName() + ".cache";
452
453     QFile dictionaryFile(dictFileN.filePath());
454
455
456     if (!dictionaryFile.open(QFile::ReadOnly | QFile::Text)) {
457         Q_EMIT updateCachingProgress(100, 0);
458         Q_EMIT notify(Notify::Warning,
459                 QString("Xdxf file cannot be read for %1 dictionary")
460                 .arg(name()));
461         return 0;
462     }
463     QXmlStreamReader reader(&dictionaryFile);
464
465
466     db.setDatabaseName(cachePathN);
467     if(!db.open()) {
468         qDebug() << "Database error" << db.lastError().text() << endl;
469         Q_EMIT updateCachingProgress(100, 0);
470         Q_EMIT notify(Notify::Warning, QString("Cache database cannot be "
471                 "opened for %1 dictionary. Searching in xdxf file. "
472                 "You may want to recache.").arg(name()));
473         return false;
474     }
475     QCoreApplication::processEvents();
476     QSqlQuery cur(db);
477     cur.exec("PRAGMA synchronous = 0");
478     cur.exec("drop table dict");
479     QCoreApplication::processEvents();
480     cur.exec("create table dict(word text, normalized text ,translation text)");
481     int counter = 0;
482     cur.exec("BEGIN;");
483
484     QString a;
485     bool match = false;
486     QTime timer;
487     timer.start();
488     countWords();
489
490     int lastProg = -1;
491
492     QString stripAcc = settings()->value("strip_accents");
493     settings()->setValue("strip_accents", "true");
494
495
496     counter=0;
497     while (!reader.atEnd() && !stopped) {
498
499         QCoreApplication::processEvents();
500         reader.readNext();
501
502         if(reader.tokenType() == QXmlStreamReader::StartElement) {
503             if(reader.name()=="k"){
504                 a = reader.readElementText();
505                 match = true;
506             }
507         }
508         if(match) {
509             QString temp("");
510             while(reader.name()!="ar" && !reader.atEnd()) {
511                 if(reader.name()!="" && reader.name()!="k") {
512                     if(reader.tokenType()==QXmlStreamReader::EndElement)
513                         temp+=tr("</");
514                     if(reader.tokenType()==QXmlStreamReader::StartElement)
515                         temp+=tr("<");
516                     temp+=reader.name().toString();
517                     if(reader.name().toString()=="c" && reader.tokenType()==QXmlStreamReader::StartElement)
518                        temp= temp + tr(" c=\"") + reader.attributes().value(tr("c")).toString() + tr("\"");
519                     temp+=tr(">");
520                 }
521                 temp+= reader.text().toString().replace("<","&lt;").replace(">","&gt;");;
522                 reader.readNext();
523             }
524             if(temp.at(0)==QChar('\n'))
525                 temp.remove(0,1);
526             temp=tr("<key>") + a + tr("</key>") + tr("<t>") + temp+ tr("</t>");
527             match=false;
528             cur.prepare("insert into dict values(?,?,?)");
529             cur.addBindValue(a);
530             cur.addBindValue(removeAccents(a));
531             cur.addBindValue(temp);
532             cur.exec();
533             counter++;
534             int prog = counter*100/_wordsCount;
535             if(prog % 5 == 0 && lastProg != prog) {
536                 Q_EMIT updateCachingProgress(prog,
537                                              timer.restart());
538                 lastProg = prog;
539             }
540         }
541     }
542
543     cur.exec("END;");
544     cur.exec("select count(*) from dict");
545     settings()->setValue("strip_accents", stripAcc);
546
547     countWords();
548     cachingDialog->setVisible(false);
549
550     if(!cur.next() || countWords() != cur.value(0).toInt())
551     {
552         Q_EMIT updateCachingProgress(100, timer.restart());
553         Q_EMIT notify(Notify::Warning,
554                 QString("Database caching error, please try againg."));
555         db.close();
556         return false;
557     }
558     _settings->setValue("cache_path", cachePathN);
559     _settings->setValue("cached", "true");
560
561
562     db.close();
563     return true;
564 }
565
566 Q_EXPORT_PLUGIN2(xdxf, XdxfPlugin)