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