• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdesdk API Reference
  • KDE Home
  • Contact Us
 

lokalize

  • sources
  • kde-4.12
  • kdesdk
  • lokalize
  • src
  • tm
jobs.cpp
Go to the documentation of this file.
1 /* ****************************************************************************
2  This file is part of Lokalize
3 
4  Copyright (C) 2007-2012 by Nick Shaforostoff <shafff@ukr.net>
5 
6  This program is free software; you can redistribute it and/or
7  modify it under the terms of the GNU General Public License as
8  published by the Free Software Foundation; either version 2 of
9  the License or (at your option) version 3 or any later version
10  accepted by the membership of KDE e.V. (or its successor approved
11  by the membership of KDE e.V.), which shall act as a proxy
12  defined in Section 14 of version 3 of the license.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program. If not, see <http://www.gnu.org/licenses/>.
21 
22 **************************************************************************** */
23 
24 #undef KDE_NO_DEBUG_OUTPUT
25 
26 #include "jobs.h"
27 #include "catalog.h"
28 #include "project.h"
29 #include "diff.h"
30 #include "prefs_lokalize.h"
31 #include "version.h"
32 
33 #include "stemming.h"
34 
35 #include <kdebug.h>
36 #include <kstandarddirs.h>
37 #include <threadweaver/ThreadWeaver.h>
38 #include <threadweaver/Thread.h>
39 #include <QSqlDatabase>
40 #include <QSqlQuery>
41 #include <QSqlError>
42 #include <QStringBuilder>
43 
44 #include <QRegExp>
45 #include <QMap>
46 
47 #include <iostream>
48 
49 #include <math.h>
50 using namespace TM;
51 
52 #define TM_DELIMITER '\v'
53 #define TM_SEPARATOR '\b'
54 #define TM_NOTAPPROVED 0x04
55 
56 
57 static bool stop=false;
58 void TM::cancelAllJobs(){stop=true;}
59 
65 static void doSplit(QString& cleanEn,
66  QStringList& words,
67  QRegExp& rxClean1,
68  const QString& accel
69  )
70 {
71  static QRegExp rxSplit("\\W+|\\d+");
72 
73  if (!rxClean1.pattern().isEmpty())
74  cleanEn.replace(rxClean1," ");
75  cleanEn.remove(accel);
76 
77  words=cleanEn.toLower().split(rxSplit,QString::SkipEmptyParts);
78  if (words.size()>4)
79  {
80  int i=0;
81  for(;i<words.size();++i)
82  {
83  if (words.at(i).size()<4)
84  words.removeAt(i--);
85  else if (words.at(i).startsWith('t')&&words.at(i).size()==4)
86  {
87  if (words.at(i)=="then"
88  || words.at(i)=="than"
89  || words.at(i)=="that"
90  || words.at(i)=="this"
91  )
92  words.removeAt(i--);
93  }
94  }
95  }
96 
97 }
98 
99 
100 
101 static qlonglong getFileId(const QString& path,
102  QSqlDatabase& db)
103 {
104  QSqlQuery query1(db);
105  QString escapedPath=path;
106  escapedPath.replace('\'',"''");
107 
108  QString pathExpr="path='"+escapedPath+'\'';
109  if (path.isEmpty())
110  pathExpr="path ISNULL";
111  if (KDE_ISUNLIKELY(!query1.exec("SELECT id FROM files WHERE "
112  "path='"+escapedPath+'\'')))
113  kWarning(TM_AREA) <<"select db error: " <<query1.lastError().text();
114 
115  if (KDE_ISLIKELY(query1.next()))
116  {
117  //this is translation of en string that is already present in db
118 
119  qlonglong id=query1.value(0).toLongLong();
120  query1.clear();
121  return id;
122  }
123  query1.clear();
124 
125  //nope, this is new file
126  bool qpsql=(db.driverName()=="QPSQL");
127  QString sql="INSERT INTO files (path) VALUES (?)";
128  if (qpsql)
129  sql+=" RETURNING id";
130  query1.prepare(sql);
131 
132  query1.bindValue(0, path);
133  if (KDE_ISLIKELY(query1.exec()))
134  return qpsql ? (query1.next(), query1.value(0).toLongLong()) : query1.lastInsertId().toLongLong();
135  else
136  kWarning(TM_AREA) <<"insert db error: " <<query1.lastError().text();
137 
138  return -1;
139 }
140 
141 
142 
143 
144 static void addToIndex(qlonglong sourceId, QString sourceString,
145  QRegExp& rxClean1, const QString& accel, QSqlDatabase& db)
146 {
147  //kDebug(TM_AREA)<<sourceString;
148 
149  QStringList words;
150  doSplit(sourceString,words,rxClean1,accel);
151 
152  if (KDE_ISUNLIKELY( words.isEmpty() ))
153  return;
154 
155  QSqlQuery query1(db);
156 
157  QByteArray sourceIdStr=QByteArray::number(sourceId,36);
158 
159  bool isShort=words.size()<20;
160  int j=words.size();
161  while (--j>=0)
162  {
163  // insert word (if we do not have it)
164  if (KDE_ISUNLIKELY(!query1.exec("SELECT word, ids_short, ids_long FROM words WHERE "
165  "word='"+words.at(j)+'\'')))
166  kWarning(TM_AREA) <<"select error 3: " <<query1.lastError().text();
167 
168  //we _have_ it
169  bool weHaveIt=query1.next();
170 
171  if (weHaveIt)
172  {
173  //just add new id
174  QByteArray arr;
175  QString field;
176  if (isShort)
177  {
178  arr=query1.value(1).toByteArray();
179  field="ids_short";
180  }
181  else
182  {
183  arr=query1.value(2).toByteArray();
184  field="ids_long";
185  }
186  query1.clear();
187 
188  if (arr.contains(' '+sourceIdStr+' ')
189  || arr.startsWith(sourceIdStr+' ')
190  || arr.endsWith(' '+sourceIdStr)
191  || arr==sourceIdStr)
192  return;//this string is already indexed
193 
194  query1.prepare("UPDATE words "
195  "SET "%field%"=? "
196  "WHERE word='"%words.at(j)%'\'');
197 
198  if (!arr.isEmpty())
199  arr+=' ';
200  arr+=sourceIdStr;
201  query1.bindValue(0, arr);
202 
203  if (KDE_ISUNLIKELY(!query1.exec()))
204  kWarning(TM_AREA) <<"update error 4: " <<query1.lastError().text();
205 
206  }
207  else
208  {
209  query1.clear();
210  query1.prepare("INSERT INTO words (word, ids_short, ids_long) "
211  "VALUES (?, ?, ?)");
212  QByteArray idsShort;
213  QByteArray idsLong;
214  if (isShort)
215  idsShort=sourceIdStr;
216  else
217  idsLong=sourceIdStr;
218  query1.bindValue(0, words.at(j));
219  query1.bindValue(1, idsShort);
220  query1.bindValue(2, idsLong);
221  if (KDE_ISUNLIKELY(!query1.exec()))
222  kWarning(TM_AREA) <<"insert error 2: " <<query1.lastError().text() ;
223 
224  }
225  }
226 }
227 
232 static void removeFromIndex(qlonglong mainId, qlonglong sourceId, QString sourceString,
233  QRegExp& rxClean1, const QString& accel, QSqlDatabase& db)
234 {
235  kDebug(TM_AREA)<<"here for" <<sourceString;
236  QStringList words;
237  doSplit(sourceString,words,rxClean1,accel);
238 
239  if (KDE_ISUNLIKELY( words.isEmpty() ))
240  return;
241 
242  QSqlQuery query1(db);
243  QByteArray sourceIdStr=QByteArray::number(sourceId,36);
244 
245 //BEGIN check
246  //TM_NOTAPPROVED=4
247  if (KDE_ISUNLIKELY(!query1.exec("SELECT count(*) FROM main, target_strings WHERE "
248  "main.source="%QString::number(sourceId)%" AND "
249  "main.target=target_strings.id AND "
250  "target_strings.target NOTNULL AND "
251  "main.id!="%QString::number(mainId)%" AND "
252  "(main.bits&4)!=4")))
253  {
254  kWarning(TM_AREA) <<"select error 500: " <<query1.lastError().text();
255  return;
256  }
257 
258  bool exit=query1.next() && (query1.value(0).toLongLong()>0);
259  query1.clear();
260  if (exit)
261  return;
262 //END check
263 
264  bool isShort=words.size()<20;
265  int j=words.size();
266  while (--j>=0)
267  {
268  // remove from record for the word (if we do not have it)
269  if (KDE_ISUNLIKELY(!query1.exec("SELECT word, ids_short, ids_long FROM words WHERE "
270  "word='"%words.at(j)%'\'')))
271  {
272  kWarning(TM_AREA) <<"select error 3: " <<query1.lastError().text();
273  return;
274  }
275  if (!query1.next())
276  {
277  kWarning(TM_AREA)<<"exit here 1";
278  //we don't have record for the word, so nothing to remove
279  query1.clear();
280  return;
281  }
282 
283  QByteArray arr;
284  QString field;
285  if (isShort)
286  {
287  arr=query1.value(1).toByteArray();
288  field="ids_short";
289  }
290  else
291  {
292  arr=query1.value(2).toByteArray();
293  field="ids_long";
294  }
295  query1.clear();
296 
297  if (arr.contains(' '+sourceIdStr+' '))
298  arr.replace(' '+sourceIdStr+' '," ");
299  else if (arr.startsWith(sourceIdStr+' '))
300  arr.remove(0,sourceIdStr.size()+1);
301  else if (arr.endsWith(' '+sourceIdStr))
302  arr.chop(sourceIdStr.size()+1);
303  else if (arr==sourceIdStr)
304  arr.clear();
305 
306 
307  query1.prepare("UPDATE words "
308  "SET "%field%"=? "
309  "WHERE word='"%words.at(j)%'\'');
310 
311  query1.bindValue(0, arr);
312 
313  if (KDE_ISUNLIKELY(!query1.exec()))
314  kWarning(TM_AREA) <<"update error 504: " <<query1.lastError().text();
315 
316  }
317 }
318 
319 static bool doRemoveEntry(qlonglong mainId, QRegExp& rxClean1, const QString& accel, QSqlDatabase& db)
320 {
321  QSqlQuery query1(db);
322 
323  if (KDE_ISUNLIKELY(!query1.exec(QString("SELECT source_strings.id, source_strings.source FROM source_strings, main WHERE "
324  "source_strings.id=main.source AND main.id=%1").arg(mainId))))
325  return false;
326 
327  if (!query1.next())
328  return false;
329 
330  qlonglong sourceId=query1.value(0).toLongLong();
331  QString source_string=query1.value(1).toString();
332  query1.clear();
333 
334  if(!query1.exec(QString("SELECT count(*) FROM main WHERE source=%1").arg(sourceId))
335  || !query1.next())
336  return false;
337 
338  bool theOnly=query1.value(0).toInt()==1;
339  query1.clear();
340  if (theOnly)
341  {
342  removeFromIndex(mainId, sourceId, source_string, rxClean1, accel, db);
343  kWarning(TM_AREA)<<"ok delete?"<<query1.exec(QString("DELETE FROM source_strings WHERE id=%1").arg(sourceId));
344  }
345 
346  if (KDE_ISUNLIKELY(!query1.exec(QString("SELECT target FROM main WHERE "
347  "main.id=%1").arg(mainId))
348  || !query1.next()))
349  return false;
350 
351  qlonglong targetId=query1.value(0).toLongLong();
352  query1.clear();
353 
354  if (!query1.exec(QString("SELECT count(*) FROM main WHERE target=%1").arg(targetId))
355  ||! query1.next())
356  return false;
357  theOnly=query1.value(0).toInt()==1;
358  query1.clear();
359  if (theOnly)
360  query1.exec(QString("DELETE FROM target_strings WHERE id=%1").arg(targetId));
361 
362  return query1.exec(QString("DELETE FROM main WHERE id=%1").arg(mainId));
363 }
364 
365 static QString escape(QString str)
366 {
367  return str.replace('\'',"''");
368 }
369 
370 static bool doInsertEntry(CatalogString source,
371  CatalogString target,
372  const QString& ctxt, //TODO QStringList -- after XLIFF
373  bool approved,
374  qlonglong fileId,
375  QSqlDatabase& db,
376  QRegExp& rxClean1,//cleaning regexps for word index update
377  const QString& accel,
378  qlonglong priorId,
379  qlonglong& mainId
380  )
381 {
382  QTime a;a.start();
383 
384  mainId=-1;
385 
386  if (KDE_ISUNLIKELY( source.isEmpty() ))
387  {
388  kWarning(TM_AREA)<<"source empty";
389  return false;
390  }
391 
392  bool qpsql=(db.driverName()=="QPSQL");
393 
394  //we store non-entranslaed entries to make search over all source parts possible
395  bool untranslated=target.isEmpty();
396  bool shouldBeInIndex=!untranslated&&approved;
397 
398  //remove first occurrence of accel character so that search returns words containing accel mark
399  int sourceAccelPos=source.string.indexOf(accel);
400  if (sourceAccelPos!=-1)
401  source.string.remove(sourceAccelPos,accel.size());
402  int targetAccelPos=target.string.indexOf(accel);
403  if (targetAccelPos!=-1)
404  target.string.remove(targetAccelPos,accel.size());
405 
406  //check if we already have record with the same en string
407  QSqlQuery query1(db);
408  QString escapedCtxt =escape(ctxt);
409 
410  QByteArray sourceTags=source.tagsAsByteArray();
411  QByteArray targetTags=target.tagsAsByteArray();
412 
413 //BEGIN get sourceId
414  query1.prepare(QString("SELECT id FROM source_strings WHERE "
415  "source=? AND (source_accel%1) AND source_markup%2").arg
416  (sourceAccelPos!=-1?"=?":"=-1 OR source_accel ISNULL").arg
417  (sourceTags.isEmpty()?" ISNULL":"=?"));
418  int paranum=0;
419  query1.bindValue(paranum++,source.string);
420  if (sourceAccelPos!=-1)
421  query1.bindValue(paranum++,sourceAccelPos);
422  if (!sourceTags.isEmpty())
423  query1.bindValue(paranum++,sourceTags);
424  if (KDE_ISUNLIKELY(!query1.exec()))
425  {
426  kWarning(TM_AREA) <<"select db source_strings error: " <<query1.lastError().text();
427  return false;
428  }
429  qlonglong sourceId;
430  if (!query1.next())
431  {
432 //BEGIN insert source anew
433  kDebug(TM_AREA) <<"insert source anew";;
434 
435  QString sql="INSERT INTO source_strings (source, source_markup, source_accel) VALUES (?, ?, ?)";
436  if (qpsql)
437  sql+=" RETURNING id";
438 
439  query1.clear();
440  query1.prepare(sql);
441 
442  query1.bindValue(0, source.string);
443  query1.bindValue(1, sourceTags);
444  query1.bindValue(2, sourceAccelPos!=-1?QVariant(sourceAccelPos):QVariant());
445  if (KDE_ISUNLIKELY(!query1.exec()))
446  {
447  kWarning(TM_AREA) <<"select db source_strings error: " <<query1.lastError().text();
448  return false;
449  }
450  sourceId=qpsql ? (query1.next(), query1.value(0).toLongLong()) : query1.lastInsertId().toLongLong();
451  query1.clear();
452 
453  //update index
454  if (shouldBeInIndex)
455  addToIndex(sourceId,source.string,rxClean1,accel,db);
456 //END insert source anew
457  }
458  else
459  {
460  sourceId=query1.value(0).toLongLong();
461  //kDebug(TM_AREA)<<"SOURCE ALREADY PRESENT"<<source.string<<sourceId;
462  }
463  query1.clear();
464 //END get sourceId
465 
466 
467  if (KDE_ISUNLIKELY(!query1.exec(QString("SELECT id, target, bits FROM main WHERE "
468  "source=%1 AND file=%2 AND ctxt%3").arg(sourceId).arg(fileId).arg
469  (escapedCtxt.isEmpty()?" ISNULL":QString("='"+escapedCtxt+'\'')))))
470  {
471  kWarning(TM_AREA) <<"select db main error: " <<query1.lastError().text();
472  return false;
473  }
474 
475 //case:
476 // aaa-bbb
477 // aaa-""
478 // aaa-ccc
479 //bbb shouldn't be present in db
480 
481 
482  //update instead of adding record to main?
483  qlonglong bits=0;
484 //BEGIN target update
485  if (query1.next())
486  {
487  //kDebug(TM_AREA)<<target.string<<": update instead of adding record to main";
488  mainId=query1.value(0).toLongLong();
489  bits=query1.value(2).toLongLong();
490  bits=bits&(0xff-1);//clear obsolete bit
491  qlonglong targetId=query1.value(1).toLongLong();
492  query1.clear();
493 
494  //kWarning(TM_AREA)<<"8... "<<a.elapsed();
495 
496  bool dbApproved=!(bits&TM_NOTAPPROVED);
497  bool approvalChanged=dbApproved!=approved;
498  if (approvalChanged)
499  {
500  query1.prepare("UPDATE main "
501  "SET bits=?, change_date=CURRENT_DATE "
502  "WHERE id="+QString::number(mainId));
503 
504  query1.bindValue(0, bits^TM_NOTAPPROVED);
505  if (KDE_ISUNLIKELY(!query1.exec()))
506  {
507  kWarning(TM_AREA)<<"fail #9"<<query1.lastError().text();
508  return false;
509  }
510  }
511  query1.clear();
512 
513  //check if target in TM matches
514  if (KDE_ISUNLIKELY(!query1.exec("SELECT target, target_markup, target_accel FROM target_strings WHERE "
515  "id="%QString::number(targetId))))
516  {
517  kWarning(TM_AREA)<<"select db target_strings error: " <<query1.lastError().text();
518  return false;
519  }
520 
521  if (KDE_ISUNLIKELY(!query1.next()))
522  {
523  kWarning(TM_AREA)<<"linking to non-existing target should never happen";
524  return false;
525  }
526  QString dbTarget=query1.value(0).toString();
527  int accelPos=query1.value(2).isNull()?-1:query1.value(2).toInt();
528  bool matches=dbTarget==target.string && accelPos==targetAccelPos;
529  query1.clear();
530 
531  bool untransStatusChanged=((target.isEmpty() || dbTarget.isEmpty())&&!matches);
532  if (approvalChanged || untransStatusChanged) //only modify index if there were changes (this is not rescan)
533  {
534  if (shouldBeInIndex)
535  //this adds source to index if it's not already there
536  addToIndex(sourceId,source.string,rxClean1,accel,db);
537  else
538  //entry changed from indexable to non-indexable:
539  //remove source string from index if there are no other
540  //'good' entries using it
541  removeFromIndex(mainId,sourceId,source.string,rxClean1,accel,db);
542  }
543 
544  if (matches) //TODO XLIFF target_markup
545  {
546  if (!target.string.isEmpty())
547  kWarning(TM_AREA)<<"oops, it just matches!"<<source.string<<target.string;
548  return false;
549  }
550  // no, translation has changed: just update old target if it isn't used elsewhere
551  if (KDE_ISUNLIKELY(!query1.exec("SELECT count(*) FROM main WHERE "
552  "target="+QString::number(targetId))))
553  kWarning(TM_AREA) <<"select db target_strings error: " <<query1.lastError().text();
554 
555  if (query1.next() && query1.value(0).toLongLong()==1)
556  {
557  //TODO tnis may create duplicates, while no strings should be lost
558  query1.clear();
559 
560  query1.prepare("UPDATE target_strings "
561  "SET target=?, target_accel=?, target_markup=? "
562  "WHERE id="%QString::number(targetId));
563 
564  query1.bindValue(0, target.string.isEmpty()?QVariant():target.string);
565  query1.bindValue(1, targetAccelPos!=-1?QVariant(targetAccelPos):QVariant());
566  query1.bindValue(2, target.tagsAsByteArray());
567  bool ok=query1.exec();//note the RETURN!!!!
568  if (!ok)
569  kWarning(TM_AREA)<<"target update failed"<<query1.lastError().text();
570  else
571  ok=query1.exec("UPDATE main SET change_date=CURRENT_DATE WHERE target="%QString::number(targetId));
572  return ok;
573  }
574  //else -> there will be new record insertion and main table update below
575  }
576  //kDebug(TM_AREA)<<target.string<<": update instead of adding record to main NOT"<<query1.executedQuery();
577  query1.clear();
578 //END target update
579 
580 //BEGIN get targetId
581  query1.prepare(QString("SELECT id FROM target_strings WHERE "
582  "target=? AND (target_accel%1) AND target_markup%2").arg
583  (targetAccelPos!=-1?"=?":"=-1 OR target_accel ISNULL").arg
584  (targetTags.isEmpty()?" ISNULL":"=?"));
585  paranum=0;
586  query1.bindValue(paranum++,target.string);
587  if (targetAccelPos!=-1)
588  query1.bindValue(paranum++,targetAccelPos);
589  if (!targetTags.isEmpty())
590  query1.bindValue(paranum++,targetTags);
591  if (KDE_ISUNLIKELY(!query1.exec()))
592  {
593  kWarning(TM_AREA) <<"select db target_strings error: " <<query1.lastError().text();
594  return false;
595  }
596  qlonglong targetId;
597  if (!query1.next())
598  {
599  QString sql="INSERT INTO target_strings (target, target_markup, target_accel) VALUES (?, ?, ?)";
600  if (qpsql)
601  sql+=" RETURNING id";
602 
603  query1.clear();
604  query1.prepare(sql);
605 
606  query1.bindValue(0, target.string);
607  query1.bindValue(1, target.tagsAsByteArray());
608  query1.bindValue(2, targetAccelPos!=-1?QVariant(targetAccelPos):QVariant());
609  if (KDE_ISUNLIKELY(!query1.exec()))
610  {
611  kWarning(TM_AREA)<<"error inserting";
612  return false;
613  }
614  targetId=qpsql ? (query1.next(), query1.value(0).toLongLong()) : query1.lastInsertId().toLongLong();
615  }
616  else
617  {
618  //very unlikely, except for empty string case
619  targetId=query1.value(0).toLongLong();
620  }
621  query1.clear();
622 //END get targetId
623 
624  bool dbApproved=!(bits&TM_NOTAPPROVED);
625  if (dbApproved!=approved)
626  bits^=TM_NOTAPPROVED;
627 
628  if (mainId!=-1)
629  {
630  //just update main with new targetId
631  //(this is the case when target changed, but there were other users of the old one)
632 
633  //kWarning(TM_AREA) <<"YES! UPDATING!";
634  query1.prepare("UPDATE main "
635  "SET target=?, bits=?, change_date=CURRENT_DATE "
636  "WHERE id="%QString::number(mainId));
637 
638  query1.bindValue(0, targetId);
639  query1.bindValue(1, bits);
640  bool ok=query1.exec();
641  //kDebug(TM_AREA)<<"ok?"<<ok;
642  return ok;
643  }
644 
645  //for case when previous source additions were
646  //for entries that didn't deserve indexing
647  if (shouldBeInIndex)
648  //this adds source to index if it's not already there
649  addToIndex(sourceId,source.string,rxClean1,accel,db);
650 
651  QString sql="INSERT INTO main (source, target, file, ctxt, bits, prior) "
652  "VALUES (?, ?, ?, ?, ?, ?)";
653  if (qpsql)
654  sql+=" RETURNING id";
655 
656  query1.prepare(sql);
657 
658 // query1.prepare(QString("INSERT INTO main (source, target, file, ctxt, bits%1) "
659 // "VALUES (?, ?, ?, ?, ?%2)").arg((priorId!=-1)?", prior":"").arg((priorId!=-1)?", ?":""));
660 
661  query1.bindValue(0, sourceId);
662  query1.bindValue(1, targetId);
663  query1.bindValue(2, fileId);
664  query1.bindValue(3, ctxt.isEmpty()?QVariant():ctxt);
665  query1.bindValue(4, bits);
666  query1.bindValue(5, priorId!=-1?QVariant(priorId):QVariant());
667  bool ok=query1.exec();
668  mainId=qpsql ? (query1.next(), query1.value(0).toLongLong()) : query1.lastInsertId().toLongLong();
669  //kDebug(TM_AREA)<<"ok?"<<ok;
670  return ok;
671 }
672 
673 //TODO smth with its usage in places except opendbjob
674 static bool initSqliteDb(QSqlDatabase& db)
675 {
676  QSqlQuery queryMain(db);
677  //NOTE do this only if no japanese, chinese etc?
678  queryMain.exec("PRAGMA encoding = \"UTF-8\"");
679  queryMain.exec("CREATE TABLE IF NOT EXISTS source_strings ("
680  "id INTEGER PRIMARY KEY ON CONFLICT REPLACE, "// AUTOINCREMENT,"
681  "source TEXT, "
682  "source_markup BLOB, "//XLIFF markup info, see catalog/catalogstring.h catalog/xliff/*
683  "source_accel INTEGER "
684  ")");
685 
686  queryMain.exec("CREATE TABLE IF NOT EXISTS target_strings ("
687  "id INTEGER PRIMARY KEY ON CONFLICT REPLACE, "// AUTOINCREMENT,"
688  "target TEXT, "
689  "target_markup BLOB, "//XLIFF markup info, see catalog/catalogstring.h catalog/xliff/*
690  "target_accel INTEGER "
691  ")");
692 
693  queryMain.exec("CREATE TABLE IF NOT EXISTS main ("
694  "id INTEGER PRIMARY KEY ON CONFLICT REPLACE, "// AUTOINCREMENT,"
695  "source INTEGER, "
696  "target INTEGER, "
697  "file INTEGER, "// AUTOINCREMENT,"
698  "ctxt TEXT, "//context, after \v may be a plural form
699  "date DEFAULT CURRENT_DATE, "//creation date
700  "change_date DEFAULT CURRENT_DATE, "//last update date
701  //change_author
702  "bits NUMERIC DEFAULT 0, "
703  //bits&0x01 means entry obsolete (not present in file)
704  //bits&0x02 means entry is NOT equiv-trans (see XLIFF spec)
705  //bits&0x04 TM_NOTAPPROVED entry is NOT approved?
706 
707  //ALTER TABLE main ADD COLUMN prior INTEGER;
708  "prior INTEGER"// helps restoring full context!
709  //"reusability NUMERIC DEFAULT 0" //e.g. whether the translation is context-free, see XLIFF spec (equiv-trans)
710  //"hits NUMERIC DEFAULT 0"
711  ")");
712 
713  queryMain.exec("ALTER TABLE main ADD COLUMN prior INTEGER");
714  queryMain.exec("ALTER TABLE main ADD COLUMN change_date"); //DEFAULT CURRENT_DATE is not possible here
715 
716 
717  queryMain.exec("CREATE INDEX IF NOT EXISTS source_index ON source_strings ("
718  "source"
719  ")");
720 
721  queryMain.exec("CREATE INDEX IF NOT EXISTS target_index ON target_strings ("
722  "target"
723  ")");
724 
725  queryMain.exec("CREATE INDEX IF NOT EXISTS main_index ON main ("
726  "source, target, file"
727  ")");
728 
729  queryMain.exec("CREATE TABLE IF NOT EXISTS files ("
730  "id INTEGER PRIMARY KEY ON CONFLICT REPLACE, "
731  "path TEXT UNIQUE ON CONFLICT REPLACE, "
732  "date DEFAULT CURRENT_DATE " //last edit date when last scanned
733  ")");
734 
735 /* NOTE //"don't implement it till i'm sure it is actually useful"
736  //this is used to prevent readding translations that were removed by user
737  queryMain.exec("CREATE TABLE IF NOT EXISTS tm_removed ("
738  "id INTEGER PRIMARY KEY ON CONFLICT REPLACE, "
739  "english BLOB, "//qChecksum
740  "target BLOB, "
741  "ctxt TEXT, "//context; delimiter is \b
742  "date DEFAULT CURRENT_DATE, "
743  "hits NUMERIC DEFAULT 0"
744  ")");*/
745 
746 
747  //we create indexes manually, in a customized way
748 //OR: SELECT (tm_links.id) FROM tm_links,words WHERE tm_links.wordid==words.wordid AND (words.word) IN ("africa","abidjan");
749  queryMain.exec("CREATE TABLE IF NOT EXISTS words ("
750  "word TEXT UNIQUE ON CONFLICT REPLACE, "
751  "ids_short BLOB, " // actually, it's text,
752  "ids_long BLOB " // but it will never contain non-latin chars
753  ")");
754 
755  queryMain.exec("CREATE TABLE IF NOT EXISTS tm_config ("
756  "key INTEGER PRIMARY KEY ON CONFLICT REPLACE, "// AUTOINCREMENT,"
757  "value TEXT "
758  ")");
759 
760 
761  bool ok=queryMain.exec("select * from main limit 1");
762  return ok || !queryMain.lastError().text().contains("database disk image is malformed");
763 
764  //queryMain.exec("CREATE TEMP TRIGGER set_user_id_trigger AFTER UPDATE ON main FOR EACH ROW BEGIN UPDATE main SET change_author = 0 WHERE main.id=NEW.id; END;");
765  //CREATE TEMP TRIGGER set_user_id_trigger INSTEAD OF UPDATE ON main FOR EACH ROW BEGIN UPDATE main SET ctxt = 'test', source=NEW.source, target=NEW.target, WHERE main.id=NEW.id; END;
766 //config:
767  //accel
768  //markup
769 //(see a little below)
770 }
771 
772 //special SQL for PostgreSQL
773 static void initPgDb(QSqlDatabase& db)
774 {
775  QSqlQuery queryMain(db);
776  queryMain.exec("CREATE SEQUENCE source_id_serial");
777  queryMain.exec("CREATE TABLE source_strings ("
778  "id INTEGER PRIMARY KEY DEFAULT nextval('source_id_serial'), "
779  "source TEXT, "
780  "source_markup TEXT, "//XLIFF markup info, see catalog/catalogstring.h catalog/xliff/*
781  "source_accel INTEGER "
782  ")");
783 
784  queryMain.exec("CREATE SEQUENCE target_id_serial");
785  queryMain.exec("CREATE TABLE target_strings ("
786  "id INTEGER PRIMARY KEY DEFAULT nextval('target_id_serial'), "
787  "target TEXT, "
788  "target_markup TEXT, "//XLIFF markup info, see catalog/catalogstring.h catalog/xliff/*
789  "target_accel INTEGER "
790  ")");
791 
792  queryMain.exec("CREATE SEQUENCE main_id_serial");
793  queryMain.exec("CREATE TABLE main ("
794  "id INTEGER PRIMARY KEY DEFAULT nextval('main_id_serial'), "
795  "source INTEGER, "
796  "target INTEGER, "
797  "file INTEGER, "// AUTOINCREMENT,"
798  "ctxt TEXT, "//context, after \v may be a plural form
799  "date DATE DEFAULT CURRENT_DATE, "//last update date
800  "change_date DATE DEFAULT CURRENT_DATE, "//last update date
801  "change_author OID, "//last update date
802  "bits INTEGER DEFAULT 0, "
803  "prior INTEGER"// helps restoring full context!
804  ")");
805 
806  queryMain.exec("CREATE INDEX source_index ON source_strings ("
807  "source"
808  ")");
809 
810  queryMain.exec("CREATE INDEX target_index ON target_strings ("
811  "target"
812  ")");
813 
814  queryMain.exec("CREATE INDEX main_index ON main ("
815  "source, target, file"
816  ")");
817 
818  queryMain.exec("CREATE SEQUENCE file_id_serial");
819  queryMain.exec("CREATE TABLE files ("
820  "id INTEGER PRIMARY KEY DEFAULT nextval('file_id_serial'), "
821  "path TEXT UNIQUE, "
822  "date DATE DEFAULT CURRENT_DATE " //last edit date when last scanned
823  ")");
824 
825  //we create indexes manually, in a customized way
826 //OR: SELECT (tm_links.id) FROM tm_links,words WHERE tm_links.wordid==words.wordid AND (words.word) IN ("africa","abidjan");
827  queryMain.exec("CREATE TABLE words ("
828  "word TEXT UNIQUE, "
829  "ids_short BYTEA, " // actually, it's text,
830  "ids_long BYTEA " //` but it will never contain non-latin chars
831  ")");
832 
833  queryMain.exec("CREATE TABLE tm_config ("
834  "key INTEGER PRIMARY KEY, "// AUTOINCREMENT,"
835  "value TEXT "
836  ")");
837 //config:
838  //accel
839  //markup
840 //(see a little below)
841 
842  queryMain.exec("CREATE OR REPLACE FUNCTION set_user_id() RETURNS trigger AS $$"
843  "BEGIN"
844  " NEW.change_author = (SELECT usesysid FROM pg_user WHERE usename = CURRENT_USER);"
845  " RETURN NEW;"
846  "END"
847  "$$ LANGUAGE plpgsql;");
848 
849  //DROP TRIGGER set_user_id_trigger ON main;
850  queryMain.exec("CREATE TRIGGER set_user_id_trigger BEFORE INSERT OR UPDATE ON main FOR EACH ROW EXECUTE PROCEDURE set_user_id();");
851 }
852 
853 QMap<QString,TMConfig> tmConfigCache;
854 
855 static void setConfig(QSqlDatabase& db, const TMConfig& c)
856 {
857  qDebug()<<"setConfig"<<db.databaseName();
858  QSqlQuery query(db);
859  query.prepare("INSERT INTO tm_config (key, value) "
860  "VALUES (?, ?)");
861 
862  query.addBindValue(0);
863  query.addBindValue(c.markup);
864  //kDebug(TM_AREA)<<"setting tm db config:"<<query.exec();
865  qDebug()<<"setting tm db config 1:"<<query.exec();
866 
867  query.addBindValue(1);
868  query.addBindValue(c.accel);
869  qDebug()<<"setting tm db config 2:"<<query.exec();
870 
871  query.addBindValue(2);
872  query.addBindValue(c.sourceLangCode);
873  qDebug()<<"setting tm db config 3:"<<query.exec();
874 
875  query.addBindValue(3);
876  query.addBindValue(c.targetLangCode);
877  query.exec();
878 
879  tmConfigCache[db.databaseName()]=c;
880 }
881 
882 static TMConfig getConfig(QSqlDatabase& db, bool useCache=true) //int& emptyTargetId
883 {
884  if (useCache && tmConfigCache.contains(db.databaseName()))
885  {
886  //kDebug()<<"using config cache for"<<db.databaseName();
887  return tmConfigCache.value(db.databaseName());
888  }
889 
890  QSqlQuery query(db);
891  bool ok=query.exec("SELECT key, value FROM tm_config ORDER BY key ASC");
892  qDebug()<<"accessing tm db config"<<ok<<"use cache:"<<useCache;
893  Project& p=*(Project::instance());
894  bool f=query.next();
895  TMConfig c;
896  c.markup= f?query.value(1).toString():p.markup();
897  c.accel= query.next()?query.value(1).toString():p.accel();
898  c.sourceLangCode=query.next()?query.value(1).toString():p.sourceLangCode();
899  c.targetLangCode=query.next()?query.value(1).toString():p.targetLangCode();
900  query.clear();
901 
902  if (KDE_ISUNLIKELY( !f )) //tmConfigCache[db.databaseName()]=c;
903  setConfig(db,c);
904 
905  tmConfigCache.insert(db.databaseName(), c);
906  return c;
907 }
908 
909 
910 static void getStats(const QSqlDatabase& db,
911  int& pairsCount,
912  int& uniqueSourcesCount,
913  int& uniqueTranslationsCount
914  )
915 
916 {
917  QSqlQuery query(db);
918  if (!query.exec("SELECT count(id) FROM main")
919  || !query.next())
920  return;
921  pairsCount=query.value(0).toInt();
922  query.clear();
923 
924  if(!query.exec("SELECT count(*) FROM source_strings")
925  || !query.next())
926  return;
927  uniqueSourcesCount=query.value(0).toInt();
928  query.clear();
929 
930  if(!query.exec("SELECT count(*) FROM target_strings")
931  || !query.next())
932  return;
933  uniqueTranslationsCount=query.value(0).toInt();
934 
935  kDebug(TM_AREA)<<"getStats ok";
936  query.clear();
937 }
938 
939 OpenDBJob::OpenDBJob(const QString& name, DbType type, bool reconnect, const ConnectionParams& connParams, QObject* parent)
940  : ThreadWeaver::Job(parent)
941  , m_dbName(name)
942  , m_type(type)
943  , m_setParams(false)
944  , m_connectionSuccessful(false)
945  , m_reconnect(reconnect)
946  , m_connParams(connParams)
947 {
948  kDebug(TM_AREA)<<m_dbName;
949 }
950 
951 OpenDBJob::~OpenDBJob()
952 {
953  kDebug(TM_AREA)<<m_dbName;
954 }
955 
956 void OpenDBJob::run()
957 {
958  QTime a;a.start();
959  if (!QSqlDatabase::contains(m_dbName) || m_reconnect)
960  {
961  thread()->setPriority(QThread::IdlePriority);
962 
963  if (m_type==TM::Local)
964  {
965  QSqlDatabase db=QSqlDatabase::addDatabase("QSQLITE",m_dbName);
966  db.setDatabaseName(KStandardDirs::locateLocal("appdata", m_dbName % TM_DATABASE_EXTENSION));
967  m_connectionSuccessful=db.open();
968  if (KDE_ISUNLIKELY( !m_connectionSuccessful ))
969  {
970  QSqlDatabase::removeDatabase(m_dbName);
971  return;
972  }
973  if (!initSqliteDb(db)) //need to recreate db ;(
974  {
975  QString filename=db.databaseName();
976  db.close();
977  QSqlDatabase::removeDatabase(m_dbName);
978  QFile::remove(filename);
979 
980  db=QSqlDatabase::addDatabase("QSQLITE",m_dbName);
981  db.setDatabaseName(filename);
982  m_connectionSuccessful=db.open() && initSqliteDb(db);
983  if (!m_connectionSuccessful)
984  {
985  QSqlDatabase::removeDatabase(m_dbName);
986  return;
987  }
988  }
989  }
990  else
991  {
992  if (QSqlDatabase::contains(m_dbName))//reconnect is true
993  {
994  QSqlDatabase::database(m_dbName).close();
995  QSqlDatabase::removeDatabase(m_dbName);
996  }
997 
998  if (!m_connParams.isFilled())
999  {
1000  QFile rdb(KStandardDirs::locateLocal("appdata", m_dbName % REMOTETM_DATABASE_EXTENSION));
1001  if (!rdb.open(QIODevice::ReadOnly | QIODevice::Text))
1002  return;
1003 
1004  QTextStream rdbParams(&rdb);
1005 
1006  m_connParams.driver=rdbParams.readLine();
1007  m_connParams.host=rdbParams.readLine();
1008  m_connParams.db=rdbParams.readLine();
1009  m_connParams.user=rdbParams.readLine();
1010  m_connParams.passwd=rdbParams.readLine();
1011  }
1012 
1013  QSqlDatabase db=QSqlDatabase::addDatabase(m_connParams.driver,m_dbName);
1014  db.setHostName(m_connParams.host);
1015  db.setDatabaseName(m_connParams.db);
1016  db.setUserName(m_connParams.user);
1017  db.setPassword(m_connParams.passwd);
1018  m_connectionSuccessful=db.open();
1019  if (KDE_ISUNLIKELY( !m_connectionSuccessful ))
1020  {
1021  QSqlDatabase::removeDatabase(m_dbName);
1022  return;
1023  }
1024  m_connParams.user=db.userName();
1025  initPgDb(db);
1026  }
1027 
1028  }
1029  QSqlDatabase db=QSqlDatabase::database(m_dbName);
1030  //if (!m_markup.isEmpty()||!m_accel.isEmpty())
1031  if (m_setParams)
1032  setConfig(db,m_tmConfig);
1033  else
1034  m_tmConfig=getConfig(db);
1035  kWarning(TM_AREA) <<"db"<<m_dbName<<" opened "<<a.elapsed()<<m_tmConfig.targetLangCode;
1036 
1037  getStats(db,m_stat.pairsCount,m_stat.uniqueSourcesCount,m_stat.uniqueTranslationsCount);
1038 
1039  if (m_type==TM::Local)
1040  {
1041  db.close();
1042  db.open();
1043  }
1044 }
1045 
1046 
1047 CloseDBJob::CloseDBJob(const QString& name, QObject* parent)
1048  : ThreadWeaver::Job(parent)
1049  , m_dbName(name)
1050 {
1051  kWarning(TM_AREA)<<"here";
1052 }
1053 
1054 CloseDBJob::~CloseDBJob()
1055 {
1056  kWarning(TM_AREA)<<m_dbName;
1057 }
1058 
1059 void CloseDBJob::run ()
1060 {
1061  //kWarning(TM_AREA) <<"started";
1062 // thread()->setPriority(QThread::IdlePriority);
1063  QTime a;
1064  a.start();
1065 
1066 // QString dbFile=KStandardDirs::locateLocal("appdata", m_name+".db");
1067 
1068  QSqlDatabase::removeDatabase(m_dbName);
1069  kWarning(TM_AREA) <<"db closed "<<a.elapsed();
1070 }
1071 
1072 
1073 static QString makeAcceledString(QString source, const QString& accel, const QVariant& accelPos)
1074 {
1075  if (accelPos.isNull())
1076  return source;
1077  int accelPosInt=accelPos.toInt();
1078  if (accelPosInt!=-1)
1079  source.insert(accelPosInt, accel);
1080  return source;
1081 }
1082 
1083 
1084 SelectJob* TM::initSelectJob(Catalog* catalog, DocPosition pos, QString db, int opt)
1085 {
1086  SelectJob* job=new SelectJob(catalog->sourceWithTags(pos),
1087  catalog->context(pos.entry).first(),
1088  catalog->url().pathOrUrl(),
1089  pos,
1090  db.isEmpty()?Project::instance()->projectID():db);
1091  if (opt&Enqueue)
1092  {
1093  QObject::connect(job,SIGNAL(done(ThreadWeaver::Job*)),job,SLOT(deleteLater()));
1094  ThreadWeaver::Weaver::instance()->enqueue(job);
1095  }
1096  return job;
1097 }
1098 
1099 SelectJob::SelectJob(const CatalogString& source,
1100  const QString& ctxt,
1101  const QString& file,
1102  const DocPosition& pos,
1103  const QString& dbName,
1104  QObject* parent)
1105  : ThreadWeaver::Job(parent)
1106  , m_source(source)
1107  , m_ctxt(ctxt)
1108  , m_file(file)
1109  , m_dequeued(false)
1110  , m_pos(pos)
1111  , m_dbName(dbName)
1112 {
1113  kDebug(TM_AREA)<<dbName<<m_source.string;
1114 }
1115 
1116 SelectJob::~SelectJob()
1117 {
1118  //kDebug(TM_AREA)<<m_source.string;
1119 }
1120 
1121 void SelectJob::aboutToBeDequeued(ThreadWeaver::WeaverInterface*)
1122 {
1123  m_dequeued=true;
1124 }
1125 
1126 
1127 inline QMap<uint,qlonglong> invertMap(const QMap<qlonglong,uint>& source)
1128 {
1129  //uses the fact that map has its keys always sorted
1130  QMap<uint,qlonglong> sortingMap;
1131  for(QMap<qlonglong,uint>::const_iterator i=source.constBegin(); i!=source.constEnd(); ++i)
1132  {
1133  sortingMap.insertMulti(i.value(), i.key());
1134  }
1135  return sortingMap;
1136 }
1137 
1138 //returns true if seen translation with >85%
1139 bool SelectJob::doSelect(QSqlDatabase& db,
1140  QStringList& words,
1141  //QList<TMEntry>& entries,
1142  bool isShort)
1143 {
1144  bool qpsql=(db.driverName()=="QPSQL");
1145  QMap<qlonglong,uint> occurencies;
1146  QVector<qlonglong> idsForWord;
1147 
1148  QSqlQuery queryWords(db);
1149  //TODO ??? not sure. make another loop before to create QList< QList<qlonglong> > then reorder it by size
1150  static const QString queryC[]={QString("SELECT ids_long FROM words WHERE word='%1'"),
1151  QString("SELECT ids_short FROM words WHERE word='%1'")};
1152  QString queryString=queryC[isShort];
1153 
1154  //for each word...
1155  int o=words.size();
1156  while (--o>=0)
1157  {
1158  //if this is not the first word occurrence, just readd ids for it
1159  if (!( !idsForWord.isEmpty() && words.at(o)==words.at(o+1) ))
1160  {
1161  idsForWord.clear();
1162  queryWords.exec(queryString.arg(words.at(o)));
1163  if (KDE_ISUNLIKELY(!queryWords.exec(queryString.arg(words.at(o)))))
1164  kWarning(TM_AREA) <<"select error: " <<queryWords.lastError().text() << endl;
1165 
1166  if (queryWords.next())
1167  {
1168  QByteArray arr(queryWords.value(0).toByteArray());
1169  queryWords.clear();
1170 
1171  QList<QByteArray> ids(arr.split(' '));
1172  int p=ids.size();
1173  idsForWord.reserve(p);
1174  while (--p>=0)
1175  idsForWord.append(ids.at(p).toLongLong(/*bool ok*/0,36));
1176  }
1177  else
1178  {
1179  queryWords.clear();
1180  continue;
1181  }
1182  }
1183 
1184  //kWarning(TM_AREA) <<"SelectJob: idsForWord.size() "<<idsForWord.size()<<endl;
1185 
1186  //iterate over ids: this computes hit count for each id
1187  for(QVector<qlonglong>::const_iterator i=idsForWord.constBegin(); i!=idsForWord.constEnd(); i++)
1188  occurencies[*i]++; //0 is default value
1189  }
1190 
1191  //accels are removed
1192  TMConfig c=getConfig(db);
1193  QString tmp=c.markup;
1194  if (!c.markup.isEmpty())
1195  tmp+='|';
1196  QRegExp rxSplit('('%tmp%"\\W+|\\d+)+");
1197 
1198  QString sourceClean(m_source.string);
1199  sourceClean.remove(c.accel);
1200  //split m_english for use in wordDiff later--all words are needed so we cant use list we already have
1201  QStringList englishList(sourceClean.toLower().split(rxSplit,QString::SkipEmptyParts));
1202  static QRegExp delPart("<KBABELDEL>*</KBABELDEL>", Qt::CaseSensitive, QRegExp::Wildcard);
1203  static QRegExp addPart("<KBABELADD>*</KBABELADD>", Qt::CaseSensitive, QRegExp::Wildcard);
1204  delPart.setMinimal(true);
1205  addPart.setMinimal(true);
1206 
1207  //QList<uint> concordanceLevels=sortedUniqueValues(occurencies); //we start from entries with higher word-concordance level
1208  QMap<uint,qlonglong> concordanceLevelToIds=invertMap(occurencies);
1209  if (concordanceLevelToIds.isEmpty())
1210  return false;
1211  bool seen85=false;
1212  int limit=200;
1213  QMap<uint,qlonglong>::const_iterator clit=concordanceLevelToIds.constEnd();
1214  if (concordanceLevelToIds.size()) --clit;
1215  if (concordanceLevelToIds.size()) while (--limit>=0)
1216  {
1217  if (KDE_ISUNLIKELY( m_dequeued ))
1218  break;
1219 
1220  //for every concordance level
1221  qlonglong level=clit.key();
1222  QString joined;
1223  while(level==clit.key())
1224  {
1225  joined+=QString::number(clit.value())+',';
1226  if (clit==concordanceLevelToIds.constBegin() || --limit<0)
1227  break;
1228  --clit;
1229  }
1230  joined.chop(1);
1231 
1232  //get records containing current word
1233  QSqlQuery queryFetch("SELECT id, source, source_accel, source_markup FROM source_strings WHERE "
1234  "source_strings.id IN ("%joined%')',db);
1235  TMEntry e;
1236  while (queryFetch.next())
1237  {
1238  e.id=queryFetch.value(0).toLongLong();
1239  if (queryFetch.value(3).toByteArray().size())
1240  qDebug()<<"BA"<<queryFetch.value(3).toByteArray();
1241  e.source=CatalogString( makeAcceledString(queryFetch.value(1).toString(), c.accel, queryFetch.value(2)),
1242  queryFetch.value(3).toByteArray() );
1243  if (e.source.string.contains(TAGRANGE_IMAGE_SYMBOL))
1244  {
1245  if (!e.source.tags.size())
1246  kWarning(TM_AREA)<<queryFetch.value(3).toByteArray().size()<<queryFetch.value(3).toByteArray();
1247  }
1248  //e.target=queryFetch.value(2).toString();
1249  //QStringList e_ctxt=queryFetch.value(3).toString().split('\b',QString::SkipEmptyParts);
1250  //e.date=queryFetch.value(4).toString();
1251  e.markupExpr=c.markup;
1252  e.accelExpr=c.accel;
1253  e.dbName=db.connectionName();
1254 
1255 
1256 //BEGIN calc score
1257  QString str=e.source.string;
1258  str.remove(c.accel);
1259 
1260  QStringList englishSuggList(str.toLower().split(rxSplit,QString::SkipEmptyParts));
1261  if (englishSuggList.size()>10*englishList.size())
1262  continue;
1263  //sugg is 'old' --translator has to adapt its translation to 'new'--current
1264  QString result=wordDiff(englishSuggList,englishList);
1265  //kWarning(TM_AREA) <<"SelectJob: doin "<<j<<" "<<result;
1266 
1267  int pos=0;
1268  int delSubStrCount=0;
1269  int delLen=0;
1270  while ((pos=delPart.indexIn(result,pos))!=-1)
1271  {
1272  //kWarning(TM_AREA) <<"SelectJob: match del "<<delPart.cap(0);
1273  delLen+=delPart.matchedLength()-23;
1274  ++delSubStrCount;
1275  pos+=delPart.matchedLength();
1276  }
1277  pos=0;
1278  int addSubStrCount=0;
1279  int addLen=0;
1280  while ((pos=addPart.indexIn(result,pos))!=-1)
1281  {
1282  addLen+=addPart.matchedLength()-23;
1283  ++addSubStrCount;
1284  pos+=addPart.matchedLength();
1285  }
1286 
1287  //allLen - length of suggestion
1288  int allLen=result.size()-23*addSubStrCount-23*delSubStrCount;
1289  int commonLen=allLen-delLen-addLen;
1290  //now, allLen is the length of the string being translated
1291  allLen=m_source.string.size();
1292  bool possibleExactMatch=!(delLen+addLen);
1293  if (!possibleExactMatch)
1294  {
1295  //del is better than add
1296  if (addLen)
1297  {
1298  //kWarning(TM_AREA) <<"SelectJob: addLen:"<<addLen<<" "<<9500*(pow(float(commonLen)/float(allLen),0.20))<<" / "
1299  //<<pow(float(addLen*addSubStrCount),0.2)<<" "
1300  //<<endl;
1301 
1302  float score=9500*(pow(float(commonLen)/float(allLen),0.12f))//this was < 1 so we have increased it
1303  //this was > 1 so we have decreased it, and increased result:
1304  / exp(0.014*float(addLen)*log10(3.0f+addSubStrCount));
1305 
1306  if (delLen)
1307  {
1308  //kWarning(TM_AREA) <<"SelectJob: delLen:"<<delLen<<" / "
1309  //<<pow(float(delLen*delSubStrCount),0.1)<<" "
1310  //<<endl;
1311 
1312  float a=exp(0.008*float(delLen)*log10(3.0f+delSubStrCount));
1313 
1314  if (a!=0.0)
1315  score/=a;
1316  }
1317  e.score=(int)score;
1318 
1319  }
1320  else//==to adapt, only deletion is needed
1321  {
1322  //kWarning(TM_AREA) <<"SelectJob: b "<<int(pow(float(delLen*delSubStrCount),0.10));
1323  float score=9900*(pow(float(commonLen)/float(allLen),0.15f))
1324  / exp(0.008*float(delLen)*log10(3.0f+delSubStrCount));
1325  e.score=(int)score;
1326  }
1327  }
1328  else
1329  e.score=10000;
1330 
1331 //END calc score
1332  if (e.score<3500)
1333  continue;
1334  seen85=seen85 || e.score>8500;
1335  if (seen85 && e.score<6000)
1336  continue;
1337 
1338 //BEGIN fetch rest of the data
1339  QString change_author_str;
1340  QString authors_table_str;
1341  if (qpsql)
1342  {
1343  //change_author_str=", main.change_author ";
1344  change_author_str=", pg_user.usename ";
1345  authors_table_str=" JOIN pg_user ON (pg_user.usesysid=main.change_author) ";
1346  }
1347 
1348  QSqlQuery queryRest("SELECT main.id, main.date, main.ctxt, main.bits, "
1349  "target_strings.target, target_strings.target_accel, target_strings.target_markup, "
1350  "files.path, main.change_date " % change_author_str %
1351  "FROM main JOIN target_strings ON (target_strings.id=main.target) JOIN files ON (files.id=main.file) " % authors_table_str % "WHERE "
1352  "main.source="%QString::number(e.id)%" AND "
1353  "(main.bits&4)!=4 AND "
1354  "target_strings.target NOTNULL"
1355  ,db); //ORDER BY tm_main.id ?
1356  queryRest.exec();
1357  //qDebug()<<"main select error"<<queryRest.lastError().text();
1358  QMap<TMEntry,bool> sortedEntryList;//to eliminate same targets from different files
1359  while (queryRest.next())
1360  {
1361  e.id=queryRest.value(0).toLongLong();
1362  e.date=queryRest.value(1).toDate();
1363  e.ctxt=queryRest.value(2).toString();
1364  e.target=CatalogString( makeAcceledString(queryRest.value(4).toString(), c.accel, queryRest.value(5)),
1365  queryRest.value(6).toByteArray() );
1366 
1367  QStringList matchData=queryRest.value(2).toString().split(TM_DELIMITER,QString::KeepEmptyParts);//context|plural
1368  e.file=queryRest.value(7).toString();
1369  if (e.target.isEmpty())
1370  continue;
1371 
1372  e.obsolete=queryRest.value(3).toInt()&1;
1373 
1374  e.changeDate=queryRest.value(8).toDate();
1375  if (qpsql)
1376  e.changeAuthor=queryRest.value(9).toString();
1377 
1378 //BEGIN exact match score++
1379  if (possibleExactMatch) //"exact" match (case insensitive+w/o non-word characters!)
1380  {
1381  if (m_source.string==e.source.string)
1382  e.score=10000;
1383  else
1384  e.score=9900;
1385  }
1386  if (!m_ctxt.isEmpty()&&matchData.size()>0)//check not needed?
1387  {
1388  if (matchData.at(0)==m_ctxt)
1389  e.score+=33;
1390  }
1391  //kWarning(TM_AREA)<<"m_pos"<<QString::number(m_pos.form);
1392 // bool pluralMatches=false;
1393  if (matchData.size()>1)
1394  {
1395  int form=matchData.at(1).toInt();
1396 
1397  //pluralMatches=(form&&form==m_pos.form);
1398  if (form&&form==(int)m_pos.form)
1399  {
1400  //kWarning(TM_AREA)<<"this"<<matchData.at(1);
1401  e.score+=33;
1402  }
1403  }
1404  if (e.file==m_file)
1405  e.score+=33;
1406 //END exact match score++
1407  //kWarning(TM_AREA)<<"appending"<<e.target;
1408  sortedEntryList.insertMulti(e, false);
1409  }
1410  queryRest.clear();
1411  //eliminate same targets from different files
1412  QHash<QString,int> hash;
1413  int oldCount=m_entries.size();
1414  QMap<TMEntry,bool>::const_iterator it=sortedEntryList.constEnd();
1415  if (sortedEntryList.size()) while(true)
1416  {
1417  --it;
1418  const TMEntry& e=it.key();
1419  int& hits=hash[e.target.string];
1420  if (!hits) //0 was default value
1421  m_entries.append(e);
1422  hits++;
1423  if (it==sortedEntryList.constBegin())
1424  break;
1425  }
1426  for (int i=oldCount;i<m_entries.size();++i)
1427  m_entries[i].hits=hash.value(m_entries.at(i).target.string);
1428 //END fetch rest of the data
1429  }
1430  queryFetch.clear();
1431  if (clit==concordanceLevelToIds.constBegin())
1432  break;
1433  if (seen85) limit=qMin(limit, 100); //be more restrictive for the next concordance levels
1434  }
1435  return seen85;
1436 }
1437 
1438 void SelectJob::run ()
1439 {
1440  kDebug(TM_AREA)<<"started"<<m_dbName<<m_source.string;
1441  if (m_source.isEmpty() || stop) //sanity check
1442  return;
1443  //thread()->setPriority(QThread::IdlePriority);
1444  QTime a;a.start();
1445 
1446  QSqlDatabase db=QSqlDatabase::database(m_dbName);
1447 
1448  TMConfig c=getConfig(db);
1449  QRegExp rxClean1(c.markup);rxClean1.setMinimal(true);
1450 
1451  QString cleanSource=m_source.string;
1452  QStringList words;
1453  doSplit(cleanSource,words,rxClean1,c.accel);
1454  if (KDE_ISUNLIKELY( words.isEmpty() ))
1455  return;
1456  qSort(words);//to speed up if some words occur multiple times
1457 
1458  bool isShort=words.size()<20;
1459 
1460  if (!doSelect(db,words,isShort))
1461  doSelect(db,words,!isShort);
1462 
1463  //kWarning(TM_AREA) <<"SelectJob: done "<<a.elapsed()<<m_entries.size();
1464  qSort(m_entries.begin(), m_entries.end(), qGreater<TMEntry>());
1465  int limit=qMin(Settings::suggCount(),m_entries.size());
1466  int i=m_entries.size();
1467  while(--i>=limit)
1468  m_entries.removeLast();
1469 
1470  if (KDE_ISUNLIKELY( m_dequeued ))
1471  return;
1472 
1473  ++i;
1474  while(--i>=0)
1475  {
1476  m_entries[i].accelExpr=c.accel;
1477  m_entries[i].markupExpr=c.markup;
1478  m_entries[i].diff=userVisibleWordDiff(m_entries.at(i).source.string,
1479  m_source.string,
1480  m_entries.at(i).accelExpr,
1481  m_entries.at(i).markupExpr);
1482  }
1483 }
1484 
1485 
1486 
1487 
1488 
1489 
1490 ScanJob::ScanJob(const KUrl& url,
1491  const QString& dbName,
1492  QObject* parent)
1493  : ThreadWeaver::Job(parent)
1494  , m_url(url)
1495  , m_time(0)
1496  , m_size(0)
1497  , m_dbName(dbName)
1498 {
1499  kDebug(TM_AREA)<<m_dbName<<m_url.pathOrUrl();
1500 }
1501 
1502 ScanJob::~ScanJob()
1503 {
1504  kWarning(TM_AREA) <<m_url;
1505 }
1506 
1507 void ScanJob::run()
1508 {
1509  if (stop)
1510  return;
1511  kWarning(TM_AREA) <<"started"<<m_url.pathOrUrl()<<m_dbName;
1512  thread()->setPriority(QThread::IdlePriority);
1513  QTime a;a.start();
1514 
1515  m_added=0; //stats
1516  m_newVersions=0;//stats
1517  QSqlDatabase db=QSqlDatabase::database(m_dbName);
1518  //initSqliteDb(db);
1519  TMConfig c=getConfig(db,true);
1520  QRegExp rxClean1(c.markup);rxClean1.setMinimal(true);
1521 
1522  Catalog catalog(thread());
1523  if (KDE_ISLIKELY(catalog.loadFromUrl(m_url, KUrl(), &m_size)==0))
1524  {
1525  if (c.targetLangCode!=catalog.targetLangCode())
1526  {
1527  kWarning()<<"not indexing file because target languages don't match:"<<c.targetLangCode<<"in TM vs"<<catalog.targetLangCode()<<"in file";
1528  return;
1529  }
1530  qlonglong priorId=-1;
1531 
1532  QSqlQuery queryBegin("BEGIN",db);
1533  //kWarning(TM_AREA) <<"queryBegin error: " <<queryBegin.lastError().text();
1534 
1535  qlonglong fileId=getFileId(m_url.pathOrUrl(),db);
1536  //mark everything as obsolete
1537  queryBegin.exec(QString("UPDATE main SET bits=(bits|1) WHERE file=%1").arg(fileId));
1538  //kWarning(TM_AREA) <<"UPDATE error: " <<queryBegin.lastError().text();
1539 
1540  int numberOfEntries=catalog.numberOfEntries();
1541  DocPosition pos(0);
1542  for (;pos.entry<numberOfEntries;pos.entry++)
1543  {
1544  bool ok=true;
1545  if (catalog.isPlural(pos.entry))
1546  {
1547  DocPosition ppos=pos;
1548  for (ppos.form=0;ppos.form<catalog.numberOfPluralForms();++ppos.form)
1549  {
1550 /*
1551  QString target;
1552  if ( catalog.isApproved(i) && !catalog.isUntranslated(pos))
1553  target=catalog.target(pos);
1554 */
1555  ok=ok&&doInsertEntry(catalog.sourceWithTags(ppos),
1556  catalog.targetWithTags(ppos),
1557  catalog.context(ppos).first()+TM_DELIMITER+QString::number(ppos.form),
1558  catalog.isApproved(ppos),
1559  fileId,db,rxClean1,c.accel,priorId,priorId);
1560  }
1561  }
1562  else
1563  {
1564 /*
1565  QString target;
1566  if ( catalog.isApproved(i) && !catalog.isUntranslated(i))
1567  target=catalog.target(i);
1568 */
1569  ok=doInsertEntry(catalog.sourceWithTags(pos),
1570  catalog.targetWithTags(pos),
1571  catalog.context(pos).first(),
1572  catalog.isApproved(pos),
1573  fileId,db,rxClean1,c.accel,priorId,priorId);
1574  }
1575  if (KDE_ISLIKELY( ok ))
1576  ++m_added;
1577  }
1578  QSqlQuery queryEnd("END",db);
1579  kWarning(TM_AREA) <<"ScanJob: done "<<a.elapsed();
1580  }
1581  //kWarning(TM_AREA) <<"Done scanning "<<m_url.prettyUrl();
1582  m_time=a.elapsed();
1583 }
1584 
1585 
1586 RemoveJob::RemoveJob(const TMEntry& entry, QObject* parent)
1587  : ThreadWeaver::Job(parent)
1588  , m_entry(entry)
1589 {
1590  kWarning(TM_AREA)<<m_entry.file<<m_entry.source.string;
1591 }
1592 
1593 RemoveJob::~RemoveJob()
1594 {
1595  kWarning(TM_AREA)<<m_entry.file<<m_entry.source.string;
1596 }
1597 
1598 
1599 void RemoveJob::run ()
1600 {
1601  kDebug(TM_AREA)<<m_entry.dbName;
1602  QSqlDatabase db=QSqlDatabase::database(m_entry.dbName);
1603 
1604  //cleaning regexps for word index update
1605  TMConfig c=getConfig(db);
1606  QRegExp rxClean1(c.markup);rxClean1.setMinimal(true);
1607 
1608  kWarning(TM_AREA)<<doRemoveEntry(m_entry.id,rxClean1,c.accel,db);
1609 }
1610 
1611 
1612 UpdateJob::UpdateJob(const QString& filePath,
1613  const QString& ctxt,
1614  const CatalogString& english,
1615  const CatalogString& newTarget,
1616  //const DocPosition&,//for back tracking
1617  int form,
1618  bool approved,
1619  const QString& dbName,
1620  QObject* parent)
1621  : ThreadWeaver::Job(parent)
1622  , m_filePath(filePath)
1623  , m_ctxt(ctxt)
1624  , m_english(english)
1625  , m_newTarget(newTarget)
1626  , m_form(form)
1627  , m_approved(approved)
1628  , m_dbName(dbName)
1629 {
1630  kDebug(TM_AREA)<<m_english.string<<m_newTarget.string;
1631 }
1632 
1633 void UpdateJob::run ()
1634 {
1635  kDebug(TM_AREA)<<"run"<<m_english.string<<m_newTarget.string;
1636  QSqlDatabase db=QSqlDatabase::database(m_dbName);
1637 
1638  //cleaning regexps for word index update
1639  TMConfig c=getConfig(db);
1640  QRegExp rxClean1(c.markup);rxClean1.setMinimal(true);
1641 
1642 
1643  qlonglong fileId=getFileId(m_filePath,db);
1644 
1645  if (m_form!=-1)
1646  m_ctxt+=TM_DELIMITER%QString::number(m_form);
1647 
1648  QSqlQuery queryBegin("BEGIN",db);
1649  qlonglong priorId=-1;
1650  if (!doInsertEntry(m_english,m_newTarget,
1651  m_ctxt, //TODO QStringList -- after XLIFF
1652  m_approved, fileId,db,rxClean1,c.accel,priorId,priorId))
1653  kWarning(TM_AREA)<<"error updating db";
1654  QSqlQuery queryEnd("END",db);
1655 }
1656 
1657 
1658 
1659 //BEGIN TMX
1660 
1661 #include <QXmlDefaultHandler>
1662 #include <QXmlSimpleReader>
1663 
1667 class TmxParser : public QXmlDefaultHandler
1668 {
1669  enum State //localstate for getting chars into right place
1670  {
1671  null=0,
1672  seg,
1673  propContext,
1674  propFile,
1675  propPluralForm,
1676  propApproved
1677  };
1678 
1679  enum Lang
1680  {
1681  Source,
1682  Target,
1683  Null
1684  };
1685 
1686 public:
1687  TmxParser(const QString& dbName);
1688  ~TmxParser();
1689 
1690 private:
1691  bool startDocument();
1692  bool startElement(const QString&,const QString&,const QString&,const QXmlAttributes&);
1693  bool endElement(const QString&,const QString&,const QString&);
1694  bool characters(const QString&);
1695 
1696 private:
1697  QSqlDatabase db;
1698  QRegExp rxClean1;
1699  QString accel;
1700 
1701  int m_hits;
1702  CatalogString m_segment[3]; //Lang enum
1703  QList<InlineTag> m_inlineTags;
1704  QString m_context;
1705  QString m_pluralForm;
1706  QString m_filePath;
1707  QString m_approvedString;
1708 
1709  State m_state:8;
1710  Lang m_lang:8;
1711 
1712  ushort m_added;
1713 
1714 
1715  QMap<QString,qlonglong> m_fileIds;
1716  QString m_dbLangCode;
1717 };
1718 
1719 
1720 TmxParser::TmxParser(const QString& dbName)
1721  : m_dbLangCode(Project::instance()->langCode().toLower())
1722 {
1723  m_added=0; //stats
1724  db=QSqlDatabase::database(dbName);
1725 
1726  TMConfig c=getConfig(db);
1727  rxClean1.setPattern(c.markup);rxClean1.setMinimal(true);
1728  accel=c.accel;
1729 }
1730 
1731 bool TmxParser::startDocument()
1732 {
1733  //initSqliteDb(db);
1734  m_fileIds.clear();
1735 
1736  QSqlQuery queryBegin("BEGIN",db);
1737 
1738  m_state=null;
1739  m_lang=Null;
1740  return true;
1741 }
1742 
1743 
1744 TmxParser::~TmxParser()
1745 {
1746  QSqlQuery queryEnd("END",db);
1747 }
1748 
1749 
1750 bool TmxParser::startElement( const QString&, const QString&,
1751  const QString& qName,
1752  const QXmlAttributes& attr)
1753 {
1754  if (qName=="tu")
1755  {
1756  bool ok;
1757  m_hits=attr.value("usagecount").toInt(&ok);
1758  if (!ok)
1759  m_hits=-1;
1760 
1761  m_segment[Source].clear();
1762  m_segment[Target].clear();
1763  m_context.clear();
1764  m_pluralForm.clear();
1765  m_filePath.clear();
1766  m_approvedString.clear();
1767 
1768  }
1769  else if (qName=="tuv")
1770  {
1771  if (attr.value("xml:lang").toLower()=="en")
1772  m_lang=Source;
1773  else if (attr.value("xml:lang").toLower()==m_dbLangCode)
1774  m_lang=Target;
1775  else
1776  {
1777  kWarning(TM_AREA)<<"skipping lang"<<attr.value("xml:lang");
1778  m_lang=Null;
1779  }
1780  }
1781  else if (qName=="prop")
1782  {
1783  if (attr.value("type").toLower()=="x-context")
1784  m_state=propContext;
1785  else if (attr.value("type").toLower()=="x-file")
1786  m_state=propFile;
1787  else if (attr.value("type").toLower()=="x-pluralform")
1788  m_state=propPluralForm;
1789  else if (attr.value("type").toLower()=="x-approved")
1790  m_state=propApproved;
1791  else
1792  m_state=null;
1793  }
1794  else if (qName=="seg")
1795  {
1796  m_state=seg;
1797  }
1798  else if (m_state==seg && m_lang!=Null)
1799  {
1800  InlineTag::InlineElement t=InlineTag::getElementType(qName.toLatin1());
1801  if (t!=InlineTag::_unknown)
1802  {
1803  m_segment[m_lang].string+=QChar(TAGRANGE_IMAGE_SYMBOL);
1804  int pos=m_segment[m_lang].string.size();
1805  m_inlineTags.append(InlineTag(pos, pos, t, attr.value("id")));
1806  }
1807  }
1808  return true;
1809 }
1810 
1811 bool TmxParser::endElement(const QString&,const QString&,const QString& qName)
1812 {
1813  static const char* tmxFilename="tmx-import";
1814  static const char* no="no";
1815  if (qName=="tu")
1816  {
1817  if (m_filePath.isEmpty())
1818  m_filePath=tmxFilename;
1819  if (!m_fileIds.contains(m_filePath))
1820  m_fileIds.insert(m_filePath,getFileId(m_filePath,db));
1821  qlonglong fileId=m_fileIds.value(m_filePath);
1822 
1823  if (!m_pluralForm.isEmpty())
1824  m_context+=TM_DELIMITER+m_pluralForm;
1825 
1826  qlonglong priorId=-1;
1827  bool ok=doInsertEntry(m_segment[Source],
1828  m_segment[Target],
1829  m_context,
1830  m_approvedString!=no,
1831  fileId,db,rxClean1,accel,priorId,priorId);
1832  if (KDE_ISLIKELY( ok ))
1833  ++m_added;
1834  }
1835  else if (m_state==seg && m_lang!=Null)
1836  {
1837  InlineTag::InlineElement t=InlineTag::getElementType(qName.toLatin1());
1838  if (t!=InlineTag::_unknown)
1839  {
1840  InlineTag tag=m_inlineTags.takeLast();
1841  kWarning(TM_AREA)<<qName<<tag.getElementName();
1842 
1843  if (tag.isPaired())
1844  {
1845  tag.end=m_segment[m_lang].string.size();
1846  m_segment[m_lang].string+=QChar(TAGRANGE_IMAGE_SYMBOL);
1847  }
1848  m_segment[m_lang].tags.append(tag);
1849  }
1850  }
1851  m_state=null;
1852  return true;
1853 }
1854 
1855 
1856 
1857 bool TmxParser::characters ( const QString& ch )
1858 {
1859  if(m_state==seg && m_lang!=Null)
1860  m_segment[m_lang].string+=ch;
1861  else if(m_state==propFile)
1862  m_filePath+=ch;
1863  else if(m_state==propContext)
1864  m_context+=ch;
1865  else if(m_state==propPluralForm)
1866  m_pluralForm+=ch;
1867  else if(m_state==propApproved)
1868  m_approvedString+=ch;
1869 
1870  return true;
1871 }
1872 
1873 
1874 
1875 
1876 
1877 ImportTmxJob::ImportTmxJob(const QString& filename,//const KUrl& url,
1878  const QString& dbName,
1879  QObject* parent)
1880  : ThreadWeaver::Job(parent)
1881  , m_filename(filename)
1882  , m_dbName(dbName)
1883 {
1884 }
1885 
1886 ImportTmxJob::~ImportTmxJob()
1887 {
1888  kWarning(TM_AREA) <<"ImportTmxJob dtor ";
1889 }
1890 
1891 void ImportTmxJob::run()
1892 {
1893  thread()->setPriority(QThread::IdlePriority);
1894  QTime a;a.start();
1895 
1896  QFile file(m_filename);
1897  if (!file.open(QFile::ReadOnly | QFile::Text))
1898  return;
1899 
1900  TmxParser parser(m_dbName);
1901  QXmlSimpleReader reader;
1902  reader.setContentHandler(&parser);
1903 
1904  QXmlInputSource xmlInputSource(&file);
1905  if (!reader.parse(xmlInputSource))
1906  kWarning(TM_AREA) << "failed to load "<< m_filename;
1907 
1908  //kWarning(TM_AREA) <<"Done scanning "<<m_url.prettyUrl();
1909  m_time=a.elapsed();
1910 }
1911 
1912 
1913 
1914 
1915 #include <QXmlStreamWriter>
1916 
1917 ExportTmxJob::ExportTmxJob(const QString& filename,//const KUrl& url,
1918  const QString& dbName,
1919  QObject* parent)
1920  : ThreadWeaver::Job(parent)
1921  , m_filename(filename)
1922  , m_dbName(dbName)
1923 {
1924 }
1925 
1926 ExportTmxJob::~ExportTmxJob()
1927 {
1928  kWarning(TM_AREA) <<"ExportTmxJob dtor ";
1929 }
1930 
1931 void ExportTmxJob::run()
1932 {
1933  thread()->setPriority(QThread::IdlePriority);
1934  QTime a;a.start();
1935 
1936  QFile out(m_filename);
1937  if (!out.open(QFile::WriteOnly|QFile::Text))
1938  return;
1939 
1940  QXmlStreamWriter xmlOut(&out);
1941  xmlOut.setAutoFormatting(true);
1942  xmlOut.writeStartDocument("1.0");
1943 
1944 
1945 
1946  xmlOut.writeStartElement("tmx");
1947  xmlOut.writeAttribute("version","2.0");
1948 
1949  xmlOut.writeStartElement("header");
1950  xmlOut.writeAttribute("creationtool","lokalize");
1951  xmlOut.writeAttribute("creationtoolversion",LOKALIZE_VERSION);
1952  xmlOut.writeAttribute("segtype","paragraph");
1953  xmlOut.writeAttribute("o-encoding","UTF-8");
1954  xmlOut.writeEndElement();
1955 
1956  xmlOut.writeStartElement("body");
1957 
1958 
1959 
1960  QString dbLangCode=Project::instance()->langCode();
1961 
1962  QSqlDatabase db=QSqlDatabase::database(m_dbName);
1963  QSqlQuery query1(db);
1964 
1965  if (KDE_ISUNLIKELY(!query1.exec("SELECT main.id, main.ctxt, main.date, main.bits, "
1966  "source_strings.source, source_strings.source_accel, "
1967  "target_strings.target, target_strings.target_accel, "
1968  "files.path, main.change_date "
1969  "FROM main, source_strings, target_strings, files "
1970  "WHERE source_strings.id=main.source AND "
1971  "target_strings.id=main.target AND "
1972  "files.id=main.file")))
1973  kWarning(TM_AREA) <<"select error: " <<query1.lastError().text();
1974 
1975  TMConfig c=getConfig(db);
1976 
1977  while (query1.next())
1978  {
1979  QString source=makeAcceledString(query1.value(4).toString(),c.accel,query1.value(5));
1980  QString target=makeAcceledString(query1.value(6).toString(),c.accel,query1.value(7));
1981 
1982  xmlOut.writeStartElement("tu");
1983  xmlOut.writeAttribute("tuid",QString::number(query1.value(0).toLongLong()));
1984 
1985  xmlOut.writeStartElement("tuv");
1986  xmlOut.writeAttribute("xml:lang","en");
1987  xmlOut.writeStartElement("seg");
1988  xmlOut.writeCharacters(source);
1989  xmlOut.writeEndElement();
1990  xmlOut.writeEndElement();
1991 
1992  xmlOut.writeStartElement("tuv");
1993  xmlOut.writeAttribute("xml:lang",dbLangCode);
1994  xmlOut.writeAttribute("creationdate",QDate::fromString( query1.value(2).toString(), Qt::ISODate ).toString("yyyyMMdd"));
1995  xmlOut.writeAttribute("changedate",QDate::fromString( query1.value(9).toString(), Qt::ISODate ).toString("yyyyMMdd"));
1996  QString ctxt=query1.value(1).toString();
1997  if (!ctxt.isEmpty())
1998  {
1999  int pos=ctxt.indexOf(TM_DELIMITER);
2000  if (pos!=-1)
2001  {
2002  QString plural=ctxt;
2003  plural.remove(0,pos+1);
2004  ctxt.remove(pos, plural.size());
2005  xmlOut.writeStartElement("prop");
2006  xmlOut.writeAttribute("type","x-pluralform");
2007  xmlOut.writeCharacters(plural);
2008  xmlOut.writeEndElement();
2009  }
2010  if (!ctxt.isEmpty())
2011  {
2012  xmlOut.writeStartElement("prop");
2013  xmlOut.writeAttribute("type","x-context");
2014  xmlOut.writeCharacters(ctxt);
2015  xmlOut.writeEndElement();
2016  }
2017  }
2018  QString filePath=query1.value(8).toString();
2019  if (!filePath.isEmpty())
2020  {
2021  xmlOut.writeStartElement("prop");
2022  xmlOut.writeAttribute("type","x-file");
2023  xmlOut.writeCharacters(filePath);
2024  xmlOut.writeEndElement();
2025  }
2026  qlonglong bits=query1.value(8).toLongLong();
2027  if (bits&TM_NOTAPPROVED)
2028  if (!filePath.isEmpty())
2029  {
2030  xmlOut.writeStartElement("prop");
2031  xmlOut.writeAttribute("type","x-approved");
2032  xmlOut.writeCharacters("no");
2033  xmlOut.writeEndElement();
2034  }
2035  xmlOut.writeStartElement("seg");
2036  xmlOut.writeCharacters(target);
2037  xmlOut.writeEndElement();
2038  xmlOut.writeEndElement();
2039  xmlOut.writeEndElement();
2040 
2041 
2042 
2043  }
2044  query1.clear();
2045 
2046 
2047  xmlOut.writeEndDocument();
2048  out.close();
2049 
2050  kWarning(TM_AREA) <<"Done exporting "<<a.elapsed();
2051  m_time=a.elapsed();
2052 }
2053 
2054 
2055 //END TMX
2056 
2057 
2058 ExecQueryJob::ExecQueryJob(const QString& queryString, const QString& dbName, QObject* parent)
2059  : ThreadWeaver::Job(parent)
2060  , query(0)
2061  , m_dbName(dbName)
2062  , m_query(queryString)
2063 {
2064  kDebug(TM_AREA)<<dbName<<queryString;
2065 }
2066 
2067 ExecQueryJob::~ExecQueryJob()
2068 {
2069  delete query;
2070  kDebug(TM_AREA)<<"destroy";
2071 }
2072 
2073 void ExecQueryJob::run()
2074 {
2075  QSqlDatabase db=QSqlDatabase::database(m_dbName);
2076  kDebug(TM_AREA)<<"running"<<m_dbName<<"db.isOpen() ="<<db.isOpen();
2077  //temporarily:
2078  if (!db.isOpen())
2079  kWarning(TM_AREA)<<"db.open()="<<db.open();
2080  query=new QSqlQuery(m_query,db);
2081  query->exec();
2082  kDebug(TM_AREA)<<"done"<<query->lastError().text();
2083 }
2084 
2085 
2086 
2087 #include "jobs.moc"
Catalog::context
QStringList context(const DocPosition &pos) const
Definition: catalog.cpp:322
TM::DbType
DbType
Definition: jobs.h:43
TM_NOTAPPROVED
#define TM_NOTAPPROVED
Definition: jobs.cpp:54
TM::ImportTmxJob::m_dbName
QString m_dbName
Definition: jobs.h:324
TM::TMConfig::accel
QString accel
Definition: jobs.h:66
project.h
Catalog::url
const KUrl & url() const
Definition: catalog.h:189
TM::TMEntry::file
QString file
Definition: tmentry.h:41
Catalog::targetWithTags
CatalogString targetWithTags(const DocPosition &pos) const
Definition: catalog.cpp:211
TM::SelectJob::SelectJob
SelectJob(const CatalogString &source, const QString &ctxt, const QString &file, const DocPosition &, const QString &dbName, QObject *parent=0)
Definition: jobs.cpp:1099
ExecQueryJob::m_dbName
QString m_dbName
Definition: jobs.h:370
TM::TMEntry::dbName
QString dbName
Definition: tmentry.h:51
InlineTag::isPaired
static bool isPaired(InlineElement type)
Definition: catalogstring.h:105
ExecQueryJob::query
QSqlQuery * query
Definition: jobs.h:366
TM::ScanJob::run
void run()
Definition: jobs.cpp:1507
TM::CloseDBJob::CloseDBJob
CloseDBJob(const QString &dbName, QObject *parent=0)
Definition: jobs.cpp:1047
ExecQueryJob::ExecQueryJob
ExecQueryJob(const QString &queryString, const QString &dbName, QObject *parent=0)
Definition: jobs.cpp:2058
TM::TMEntry::markupExpr
QString markupExpr
Definition: tmentry.h:57
Catalog::targetLangCode
QString targetLangCode() const
Definition: catalog.cpp:474
TM::TMEntry::score
short score
Definition: tmentry.h:48
TM::ImportTmxJob::m_filename
QString m_filename
Definition: jobs.h:317
TM::OpenDBJob::~OpenDBJob
~OpenDBJob()
Definition: jobs.cpp:951
wordDiff
QString wordDiff(QStringList s1, QStringList s2)
This is low-level wrapper used for evaluating translation memory search results.
Definition: diff.cpp:342
id
static const QString id
Definition: glossary.cpp:49
TM::OpenDBJob::ConnectionParams::user
QString user
Definition: jobs.h:80
Catalog::numberOfPluralForms
int numberOfPluralForms() const
Definition: catalog.h:150
TM::ScanJob::m_size
int m_size
Definition: jobs.h:250
TM::cancelAllJobs
void cancelAllJobs()
Definition: jobs.cpp:58
TAGRANGE_IMAGE_SYMBOL
#define TAGRANGE_IMAGE_SYMBOL
Definition: catalogstring.h:33
TM::OpenDBJob::m_stat
DBStat m_stat
Definition: jobs.h:98
TM::Local
Definition: jobs.h:43
TM::OpenDBJob::m_dbName
QString m_dbName
Definition: jobs.h:95
TM::SelectJob::aboutToBeDequeued
void aboutToBeDequeued(ThreadWeaver::WeaverInterface *)
Definition: jobs.cpp:1121
userVisibleWordDiff
QString userVisibleWordDiff(const QString &str1ForMatching, const QString &str2ForMatching, const QString &accel, const QString &markup, int options)
Definition: diff.cpp:408
TM::ScanJob::m_dbName
QString m_dbName
Definition: jobs.h:252
Project::instance
static Project * instance()
Definition: project.cpp:67
TM::RemoveJob::RemoveJob
RemoveJob(const TMEntry &entry, QObject *parent=0)
Definition: jobs.cpp:1586
TM::RemoveJob::run
void run()
Definition: jobs.cpp:1599
TM::OpenDBJob::DBStat::uniqueTranslationsCount
int uniqueTranslationsCount
Definition: jobs.h:89
TM::OpenDBJob::m_connectionSuccessful
bool m_connectionSuccessful
Definition: jobs.h:104
getFileId
static qlonglong getFileId(const QString &path, QSqlDatabase &db)
Definition: jobs.cpp:101
TM::OpenDBJob::m_tmConfig
TMConfig m_tmConfig
Definition: jobs.h:101
TM::SelectJob::m_entries
QList< TMEntry > m_entries
Definition: jobs.h:163
ProjectBase::langCode
QString langCode() const
Get LangCode.
Definition: projectbase.h:65
TM::SelectJob
Definition: jobs.h:130
TM::OpenDBJob::OpenDBJob
OpenDBJob(const QString &dbName, DbType type=TM::Local, bool reconnect=false, const ConnectionParams &connParams=ConnectionParams(), QObject *parent=0)
Definition: jobs.cpp:939
addToIndex
static void addToIndex(qlonglong sourceId, QString sourceString, QRegExp &rxClean1, const QString &accel, QSqlDatabase &db)
Definition: jobs.cpp:144
ProjectBase::markup
QString markup() const
Get Markup.
Definition: projectbase.h:252
CatalogString::string
QString string
Definition: catalogstring.h:130
Catalog::loadFromUrl
int loadFromUrl(const KUrl &url, const KUrl &saidUrl=KUrl(), int *fileSize=0)
Definition: catalog.cpp:508
QObject
getConfig
static TMConfig getConfig(QSqlDatabase &db, bool useCache=true)
Definition: jobs.cpp:882
stop
static bool stop
Definition: jobs.cpp:57
ExecQueryJob::~ExecQueryJob
~ExecQueryJob()
Definition: jobs.cpp:2067
TM_AREA
#define TM_AREA
Definition: jobs.h:45
TM::ExportTmxJob::run
void run()
Definition: jobs.cpp:1931
InlineTag::getElementType
static InlineElement getElementType(const QByteArray &)
Definition: catalogstring.cpp:58
getStats
static void getStats(const QSqlDatabase &db, int &pairsCount, int &uniqueSourcesCount, int &uniqueTranslationsCount)
Definition: jobs.cpp:910
TM::ExportTmxJob::ExportTmxJob
ExportTmxJob(const QString &url, const QString &dbName, QObject *parent=0)
Definition: jobs.cpp:1917
DocPosition::entry
int entry
Definition: pos.h:48
TM::OpenDBJob::ConnectionParams::passwd
QString passwd
Definition: jobs.h:80
DocPosition
This struct represents a position in a catalog.
Definition: pos.h:38
TM::OpenDBJob::ConnectionParams::db
QString db
Definition: jobs.h:80
TM::ExportTmxJob::m_time
ushort m_time
Definition: jobs.h:346
TM::TMEntry
Definition: tmentry.h:35
CatalogString::isEmpty
bool isEmpty() const
Definition: catalogstring.h:144
ProjectBase::accel
QString accel() const
Get Accel.
Definition: projectbase.h:235
Project
Singleton object that represents project.
Definition: project.h:51
TM::OpenDBJob::DBStat::uniqueSourcesCount
int uniqueSourcesCount
Definition: jobs.h:89
TM::TMEntry::changeDate
QDate changeDate
Definition: tmentry.h:43
TM::ImportTmxJob::ImportTmxJob
ImportTmxJob(const QString &url, const QString &dbName, QObject *parent=0)
Definition: jobs.cpp:1877
TM::TMEntry::id
qlonglong id
Definition: tmentry.h:47
TM::CloseDBJob::~CloseDBJob
~CloseDBJob()
Definition: jobs.cpp:1054
InlineTag::InlineElement
InlineElement
Definition: catalogstring.h:49
TM::ScanJob::m_time
ushort m_time
Definition: jobs.h:246
catalog.h
TM::OpenDBJob::run
void run()
Definition: jobs.cpp:956
removeFromIndex
static void removeFromIndex(qlonglong mainId, qlonglong sourceId, QString sourceString, QRegExp &rxClean1, const QString &accel, QSqlDatabase &db)
remove source string from index if there are no other 'good' entries using it but the entry specified...
Definition: jobs.cpp:232
TM::ExportTmxJob::~ExportTmxJob
~ExportTmxJob()
Definition: jobs.cpp:1926
InlineTag::_unknown
Definition: catalogstring.h:51
DocPosition::form
char form
Definition: pos.h:50
LOKALIZE_VERSION
#define LOKALIZE_VERSION
Definition: version.h:11
TM::ExportTmxJob::m_filename
QString m_filename
Definition: jobs.h:343
escape
static QString escape(QString str)
Definition: jobs.cpp:365
TM::TMEntry::accelExpr
QString accelExpr
Definition: tmentry.h:56
TM::SelectJob::m_source
CatalogString m_source
Definition: jobs.h:155
Catalog::isPlural
bool isPlural(uint index) const
Definition: catalog.cpp:405
TM::TMEntry::source
CatalogString source
Definition: tmentry.h:37
Project::targetLangCode
Q_SCRIPTABLE QString targetLangCode() const
Definition: project.h:87
initPgDb
static void initPgDb(QSqlDatabase &db)
Definition: jobs.cpp:773
TM::initSelectJob
SelectJob * initSelectJob(Catalog *, DocPosition pos, QString db=QString(), int opt=Enqueue)
Definition: jobs.cpp:1084
diff.h
Settings::suggCount
static int suggCount()
Get SuggCount.
Definition: prefs_lokalize.h:313
TM::OpenDBJob::ConnectionParams::driver
QString driver
Definition: jobs.h:80
TM::OpenDBJob::m_setParams
bool m_setParams
Definition: jobs.h:102
TM::OpenDBJob::m_connParams
ConnectionParams m_connParams
Definition: jobs.h:106
TM::TMConfig::targetLangCode
QString targetLangCode
Definition: jobs.h:68
TM::ScanJob::ScanJob
ScanJob(const KUrl &url, const QString &dbName, QObject *parent=0)
Definition: jobs.cpp:1490
TM::UpdateJob::UpdateJob
UpdateJob(const QString &filePath, const QString &ctxt, const CatalogString &en, const CatalogString &newTarget, int form, bool approved, const QString &dbName, QObject *parent=0)
Definition: jobs.cpp:1612
doRemoveEntry
static bool doRemoveEntry(qlonglong mainId, QRegExp &rxClean1, const QString &accel, QSqlDatabase &db)
Definition: jobs.cpp:319
ExecQueryJob::run
void run()
Definition: jobs.cpp:2073
TM::TMConfig::sourceLangCode
QString sourceLangCode
Definition: jobs.h:67
TM::ScanJob::m_newVersions
ushort m_newVersions
Definition: jobs.h:248
stemming.h
TM::TMEntry::date
QDate date
Definition: tmentry.h:42
TM::TMEntry::target
CatalogString target
Definition: tmentry.h:38
TM::SelectJob::~SelectJob
~SelectJob()
Definition: jobs.cpp:1116
TM::TMConfig
Definition: jobs.h:63
TM::OpenDBJob::ConnectionParams
Definition: jobs.h:78
TM::RemoveJob::m_entry
TMEntry m_entry
Definition: jobs.h:184
TM::ImportTmxJob::m_time
ushort m_time
Definition: jobs.h:320
TM::Enqueue
Definition: jobs.h:168
tmConfigCache
QMap< QString, TMConfig > tmConfigCache
Definition: jobs.cpp:853
TM_DATABASE_EXTENSION
#define TM_DATABASE_EXTENSION
Definition: jobs.h:41
version.h
CatalogString
data structure used to pass info about inline elements a XLIFF tag is represented by a TAGRANGE_IMAGE...
Definition: catalogstring.h:128
jobs.h
TM::RemoveJob::~RemoveJob
~RemoveJob()
Definition: jobs.cpp:1593
TM::TMEntry::changeAuthor
QString changeAuthor
Definition: tmentry.h:44
REMOTETM_DATABASE_EXTENSION
#define REMOTETM_DATABASE_EXTENSION
Definition: jobs.h:42
TM::OpenDBJob::m_type
DbType m_type
Definition: jobs.h:96
prefs_lokalize.h
TM::TMConfig::markup
QString markup
Definition: jobs.h:65
TM::UpdateJob::run
void run()
Definition: jobs.cpp:1633
ProjectBase::projectID
QString projectID() const
Get ProjectID.
Definition: projectbase.h:31
CatalogString::tagsAsByteArray
QByteArray tagsAsByteArray() const
Definition: catalogstring.cpp:200
Catalog::sourceWithTags
CatalogString sourceWithTags(const DocPosition &pos) const
Definition: catalog.cpp:203
CatalogString::tags
QList< InlineTag > tags
Definition: catalogstring.h:131
TM::ImportTmxJob::run
void run()
Definition: jobs.cpp:1891
TM::SelectJob::m_dbName
QString m_dbName
Definition: jobs.h:165
Catalog::isApproved
bool isApproved(uint index) const
Definition: catalog.cpp:410
Catalog::numberOfEntries
int numberOfEntries() const
Definition: catalog.cpp:171
doInsertEntry
static bool doInsertEntry(CatalogString source, CatalogString target, const QString &ctxt, bool approved, qlonglong fileId, QSqlDatabase &db, QRegExp &rxClean1, const QString &accel, qlonglong priorId, qlonglong &mainId)
Definition: jobs.cpp:370
TM::ScanJob::m_url
KUrl m_url
Definition: jobs.h:243
TM_DELIMITER
#define TM_DELIMITER
Definition: jobs.cpp:52
initSqliteDb
static bool initSqliteDb(QSqlDatabase &db)
Definition: jobs.cpp:674
TM::OpenDBJob::ConnectionParams::host
QString host
Definition: jobs.h:80
Catalog
This class represents a catalog It uses CatalogStorage interface to work with catalogs in different f...
Definition: catalog.h:74
TM::OpenDBJob::DBStat::pairsCount
int pairsCount
Definition: jobs.h:89
ExecQueryJob::m_query
QString m_query
Definition: jobs.h:371
invertMap
QMap< uint, qlonglong > invertMap(const QMap< qlonglong, uint > &source)
Definition: jobs.cpp:1127
TM::OpenDBJob::ConnectionParams::isFilled
bool isFilled()
Definition: jobs.h:81
InlineTag::getElementName
static const char * getElementName(InlineElement type)
Definition: catalogstring.cpp:29
TM::ScanJob::m_added
ushort m_added
Definition: jobs.h:247
TM::TMEntry::obsolete
bool obsolete
Definition: tmentry.h:50
TM::SelectJob::run
void run()
Definition: jobs.cpp:1438
setConfig
static void setConfig(QSqlDatabase &db, const TMConfig &c)
Definition: jobs.cpp:855
InlineTag::end
int end
Definition: catalogstring.h:69
TM::ScanJob::~ScanJob
~ScanJob()
Definition: jobs.cpp:1502
TM::OpenDBJob::m_reconnect
bool m_reconnect
Definition: jobs.h:105
TM::TMEntry::ctxt
QString ctxt
Definition: tmentry.h:40
TM::CloseDBJob::run
void run()
Definition: jobs.cpp:1059
InlineTag
data structure used to pass info about inline elements a XLIFF tag is represented by a TAGRANGE_IMAGE...
Definition: catalogstring.h:44
doSplit
static void doSplit(QString &cleanEn, QStringList &words, QRegExp &rxClean1, const QString &accel)
splits string into words, removing any markup
Definition: jobs.cpp:65
TM::SelectJob::m_pos
DocPosition m_pos
Definition: jobs.h:162
TM::CloseDBJob::m_dbName
QString m_dbName
Definition: jobs.h:123
TM::ExportTmxJob::m_dbName
QString m_dbName
Definition: jobs.h:350
makeAcceledString
static QString makeAcceledString(QString source, const QString &accel, const QVariant &accelPos)
Definition: jobs.cpp:1073
TM::ImportTmxJob::~ImportTmxJob
~ImportTmxJob()
Definition: jobs.cpp:1886
Project::sourceLangCode
Q_SCRIPTABLE QString sourceLangCode() const
Definition: project.h:88
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:03:45 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

lokalize

Skip menu "lokalize"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdesdk API Reference

Skip menu "kdesdk API Reference"
  • kapptemplate
  • kcachegrind
  • kompare
  • lokalize
  • okteta
  • umbrello
  •   umbrello

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal