24 #undef KDE_NO_DEBUG_OUTPUT
36 #include <kstandarddirs.h>
37 #include <threadweaver/ThreadWeaver.h>
38 #include <threadweaver/Thread.h>
39 #include <QSqlDatabase>
42 #include <QStringBuilder>
52 #define TM_DELIMITER '\v'
53 #define TM_SEPARATOR '\b'
54 #define TM_NOTAPPROVED 0x04
71 static QRegExp rxSplit(
"\\W+|\\d+");
73 if (!rxClean1.pattern().isEmpty())
74 cleanEn.replace(rxClean1,
" ");
75 cleanEn.remove(accel);
77 words=cleanEn.toLower().split(rxSplit,QString::SkipEmptyParts);
81 for(;i<words.size();++i)
83 if (words.at(i).size()<4)
85 else if (words.at(i).startsWith(
't')&&words.at(i).size()==4)
87 if (words.at(i)==
"then"
88 || words.at(i)==
"than"
89 || words.at(i)==
"that"
90 || words.at(i)==
"this"
104 QSqlQuery query1(db);
105 QString escapedPath=path;
106 escapedPath.replace(
'\'',
"''");
108 QString pathExpr=
"path='"+escapedPath+
'\'';
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();
115 if (KDE_ISLIKELY(query1.next()))
119 qlonglong
id=query1.value(0).toLongLong();
126 bool qpsql=(db.driverName()==
"QPSQL");
127 QString sql=
"INSERT INTO files (path) VALUES (?)";
129 sql+=
" RETURNING id";
132 query1.bindValue(0, path);
133 if (KDE_ISLIKELY(query1.exec()))
134 return qpsql ? (query1.next(), query1.value(0).toLongLong()) : query1.lastInsertId().toLongLong();
136 kWarning(
TM_AREA) <<
"insert db error: " <<query1.lastError().text();
144 static void addToIndex(qlonglong sourceId, QString sourceString,
145 QRegExp& rxClean1,
const QString& accel, QSqlDatabase& db)
150 doSplit(sourceString,words,rxClean1,accel);
152 if (KDE_ISUNLIKELY( words.isEmpty() ))
155 QSqlQuery query1(db);
157 QByteArray sourceIdStr=QByteArray::number(sourceId,36);
159 bool isShort=words.size()<20;
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();
169 bool weHaveIt=query1.next();
178 arr=query1.value(1).toByteArray();
183 arr=query1.value(2).toByteArray();
188 if (arr.contains(
' '+sourceIdStr+
' ')
189 || arr.startsWith(sourceIdStr+
' ')
190 || arr.endsWith(
' '+sourceIdStr)
194 query1.prepare(
"UPDATE words "
196 "WHERE word='"%words.at(j)%
'\'');
201 query1.bindValue(0, arr);
203 if (KDE_ISUNLIKELY(!query1.exec()))
204 kWarning(
TM_AREA) <<
"update error 4: " <<query1.lastError().text();
210 query1.prepare(
"INSERT INTO words (word, ids_short, ids_long) "
215 idsShort=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() ;
232 static void removeFromIndex(qlonglong mainId, qlonglong sourceId, QString sourceString,
233 QRegExp& rxClean1,
const QString& accel, QSqlDatabase& db)
235 kDebug(
TM_AREA)<<
"here for" <<sourceString;
237 doSplit(sourceString,words,rxClean1,accel);
239 if (KDE_ISUNLIKELY( words.isEmpty() ))
242 QSqlQuery query1(db);
243 QByteArray sourceIdStr=QByteArray::number(sourceId,36);
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")))
254 kWarning(
TM_AREA) <<
"select error 500: " <<query1.lastError().text();
258 bool exit=query1.next() && (query1.value(0).toLongLong()>0);
264 bool isShort=words.size()<20;
269 if (KDE_ISUNLIKELY(!query1.exec(
"SELECT word, ids_short, ids_long FROM words WHERE "
270 "word='"%words.at(j)%
'\'')))
272 kWarning(
TM_AREA) <<
"select error 3: " <<query1.lastError().text();
277 kWarning(
TM_AREA)<<
"exit here 1";
287 arr=query1.value(1).toByteArray();
292 arr=query1.value(2).toByteArray();
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)
307 query1.prepare(
"UPDATE words "
309 "WHERE word='"%words.at(j)%
'\'');
311 query1.bindValue(0, arr);
313 if (KDE_ISUNLIKELY(!query1.exec()))
314 kWarning(
TM_AREA) <<
"update error 504: " <<query1.lastError().text();
319 static bool doRemoveEntry(qlonglong mainId, QRegExp& rxClean1,
const QString& accel, QSqlDatabase& db)
321 QSqlQuery query1(db);
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))))
330 qlonglong sourceId=query1.value(0).toLongLong();
331 QString source_string=query1.value(1).toString();
334 if(!query1.exec(QString(
"SELECT count(*) FROM main WHERE source=%1").arg(sourceId))
338 bool theOnly=query1.value(0).toInt()==1;
343 kWarning(
TM_AREA)<<
"ok delete?"<<query1.exec(QString(
"DELETE FROM source_strings WHERE id=%1").arg(sourceId));
346 if (KDE_ISUNLIKELY(!query1.exec(QString(
"SELECT target FROM main WHERE "
347 "main.id=%1").arg(mainId))
351 qlonglong targetId=query1.value(0).toLongLong();
354 if (!query1.exec(QString(
"SELECT count(*) FROM main WHERE target=%1").arg(targetId))
357 theOnly=query1.value(0).toInt()==1;
360 query1.exec(QString(
"DELETE FROM target_strings WHERE id=%1").arg(targetId));
362 return query1.exec(QString(
"DELETE FROM main WHERE id=%1").arg(mainId));
367 return str.replace(
'\'',
"''");
377 const QString& accel,
386 if (KDE_ISUNLIKELY( source.
isEmpty() ))
388 kWarning(
TM_AREA)<<
"source empty";
392 bool qpsql=(db.driverName()==
"QPSQL");
395 bool untranslated=target.
isEmpty();
396 bool shouldBeInIndex=!untranslated&&approved;
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());
407 QSqlQuery query1(db);
408 QString escapedCtxt =
escape(ctxt);
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":
"=?"));
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()))
426 kWarning(
TM_AREA) <<
"select db source_strings error: " <<query1.lastError().text();
433 kDebug(
TM_AREA) <<
"insert source anew";;
435 QString sql=
"INSERT INTO source_strings (source, source_markup, source_accel) VALUES (?, ?, ?)";
437 sql+=
" RETURNING id";
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()))
447 kWarning(
TM_AREA) <<
"select db source_strings error: " <<query1.lastError().text();
450 sourceId=qpsql ? (query1.next(), query1.value(0).toLongLong()) : query1.lastInsertId().toLongLong();
460 sourceId=query1.value(0).toLongLong();
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+
'\'')))))
471 kWarning(
TM_AREA) <<
"select db main error: " <<query1.lastError().text();
488 mainId=query1.value(0).toLongLong();
489 bits=query1.value(2).toLongLong();
491 qlonglong targetId=query1.value(1).toLongLong();
497 bool approvalChanged=dbApproved!=approved;
500 query1.prepare(
"UPDATE main "
501 "SET bits=?, change_date=CURRENT_DATE "
502 "WHERE id="+QString::number(mainId));
505 if (KDE_ISUNLIKELY(!query1.exec()))
507 kWarning(
TM_AREA)<<
"fail #9"<<query1.lastError().text();
514 if (KDE_ISUNLIKELY(!query1.exec(
"SELECT target, target_markup, target_accel FROM target_strings WHERE "
515 "id="%QString::number(targetId))))
517 kWarning(
TM_AREA)<<
"select db target_strings error: " <<query1.lastError().text();
521 if (KDE_ISUNLIKELY(!query1.next()))
523 kWarning(
TM_AREA)<<
"linking to non-existing target should never happen";
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;
531 bool untransStatusChanged=((target.
isEmpty() || dbTarget.isEmpty())&&!matches);
532 if (approvalChanged || untransStatusChanged)
546 if (!target.
string.isEmpty())
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();
555 if (query1.next() && query1.value(0).toLongLong()==1)
560 query1.prepare(
"UPDATE target_strings "
561 "SET target=?, target_accel=?, target_markup=? "
562 "WHERE id="%QString::number(targetId));
564 query1.bindValue(0, target.
string.isEmpty()?QVariant():target.
string);
565 query1.bindValue(1, targetAccelPos!=-1?QVariant(targetAccelPos):QVariant());
567 bool ok=query1.exec();
569 kWarning(
TM_AREA)<<
"target update failed"<<query1.lastError().text();
571 ok=query1.exec(
"UPDATE main SET change_date=CURRENT_DATE WHERE target="%QString::number(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":
"=?"));
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()))
593 kWarning(
TM_AREA) <<
"select db target_strings error: " <<query1.lastError().text();
599 QString sql=
"INSERT INTO target_strings (target, target_markup, target_accel) VALUES (?, ?, ?)";
601 sql+=
" RETURNING id";
606 query1.bindValue(0, target.
string);
608 query1.bindValue(2, targetAccelPos!=-1?QVariant(targetAccelPos):QVariant());
609 if (KDE_ISUNLIKELY(!query1.exec()))
611 kWarning(
TM_AREA)<<
"error inserting";
614 targetId=qpsql ? (query1.next(), query1.value(0).toLongLong()) : query1.lastInsertId().toLongLong();
619 targetId=query1.value(0).toLongLong();
625 if (dbApproved!=approved)
634 query1.prepare(
"UPDATE main "
635 "SET target=?, bits=?, change_date=CURRENT_DATE "
636 "WHERE id="%QString::number(mainId));
638 query1.bindValue(0, targetId);
639 query1.bindValue(1, bits);
640 bool ok=query1.exec();
651 QString sql=
"INSERT INTO main (source, target, file, ctxt, bits, prior) "
652 "VALUES (?, ?, ?, ?, ?, ?)";
654 sql+=
" RETURNING id";
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();
676 QSqlQuery queryMain(db);
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, "
682 "source_markup BLOB, "
683 "source_accel INTEGER "
686 queryMain.exec(
"CREATE TABLE IF NOT EXISTS target_strings ("
687 "id INTEGER PRIMARY KEY ON CONFLICT REPLACE, "
689 "target_markup BLOB, "
690 "target_accel INTEGER "
693 queryMain.exec(
"CREATE TABLE IF NOT EXISTS main ("
694 "id INTEGER PRIMARY KEY ON CONFLICT REPLACE, "
699 "date DEFAULT CURRENT_DATE, "
700 "change_date DEFAULT CURRENT_DATE, "
702 "bits NUMERIC DEFAULT 0, "
713 queryMain.exec(
"ALTER TABLE main ADD COLUMN prior INTEGER");
714 queryMain.exec(
"ALTER TABLE main ADD COLUMN change_date");
717 queryMain.exec(
"CREATE INDEX IF NOT EXISTS source_index ON source_strings ("
721 queryMain.exec(
"CREATE INDEX IF NOT EXISTS target_index ON target_strings ("
725 queryMain.exec(
"CREATE INDEX IF NOT EXISTS main_index ON main ("
726 "source, target, file"
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 "
749 queryMain.exec(
"CREATE TABLE IF NOT EXISTS words ("
750 "word TEXT UNIQUE ON CONFLICT REPLACE, "
755 queryMain.exec(
"CREATE TABLE IF NOT EXISTS tm_config ("
756 "key INTEGER PRIMARY KEY ON CONFLICT REPLACE, "
761 bool ok=queryMain.exec(
"select * from main limit 1");
762 return ok || !queryMain.lastError().text().contains(
"database disk image is malformed");
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'), "
780 "source_markup TEXT, "
781 "source_accel INTEGER "
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'), "
788 "target_markup TEXT, "
789 "target_accel INTEGER "
792 queryMain.exec(
"CREATE SEQUENCE main_id_serial");
793 queryMain.exec(
"CREATE TABLE main ("
794 "id INTEGER PRIMARY KEY DEFAULT nextval('main_id_serial'), "
799 "date DATE DEFAULT CURRENT_DATE, "
800 "change_date DATE DEFAULT CURRENT_DATE, "
801 "change_author OID, "
802 "bits INTEGER DEFAULT 0, "
806 queryMain.exec(
"CREATE INDEX source_index ON source_strings ("
810 queryMain.exec(
"CREATE INDEX target_index ON target_strings ("
814 queryMain.exec(
"CREATE INDEX main_index ON main ("
815 "source, target, file"
818 queryMain.exec(
"CREATE SEQUENCE file_id_serial");
819 queryMain.exec(
"CREATE TABLE files ("
820 "id INTEGER PRIMARY KEY DEFAULT nextval('file_id_serial'), "
822 "date DATE DEFAULT CURRENT_DATE "
827 queryMain.exec(
"CREATE TABLE words ("
833 queryMain.exec(
"CREATE TABLE tm_config ("
834 "key INTEGER PRIMARY KEY, "
842 queryMain.exec(
"CREATE OR REPLACE FUNCTION set_user_id() RETURNS trigger AS $$"
844 " NEW.change_author = (SELECT usesysid FROM pg_user WHERE usename = CURRENT_USER);"
847 "$$ LANGUAGE plpgsql;");
850 queryMain.exec(
"CREATE TRIGGER set_user_id_trigger BEFORE INSERT OR UPDATE ON main FOR EACH ROW EXECUTE PROCEDURE set_user_id();");
857 qDebug()<<
"setConfig"<<db.databaseName();
859 query.prepare(
"INSERT INTO tm_config (key, value) "
862 query.addBindValue(0);
863 query.addBindValue(c.
markup);
865 qDebug()<<
"setting tm db config 1:"<<query.exec();
867 query.addBindValue(1);
868 query.addBindValue(c.
accel);
869 qDebug()<<
"setting tm db config 2:"<<query.exec();
871 query.addBindValue(2);
873 qDebug()<<
"setting tm db config 3:"<<query.exec();
875 query.addBindValue(3);
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;
897 c.
accel= query.next()?query.value(1).toString():p.
accel();
902 if (KDE_ISUNLIKELY( !f ))
912 int& uniqueSourcesCount,
913 int& uniqueTranslationsCount
918 if (!query.exec(
"SELECT count(id) FROM main")
921 pairsCount=query.value(0).toInt();
924 if(!query.exec(
"SELECT count(*) FROM source_strings")
927 uniqueSourcesCount=query.value(0).toInt();
930 if(!query.exec(
"SELECT count(*) FROM target_strings")
933 uniqueTranslationsCount=query.value(0).toInt();
935 kDebug(
TM_AREA)<<
"getStats ok";
940 : ThreadWeaver::Job(parent)
944 , m_connectionSuccessful(false)
945 , m_reconnect(reconnect)
946 , m_connParams(connParams)
961 thread()->setPriority(QThread::IdlePriority);
965 QSqlDatabase db=QSqlDatabase::addDatabase(
"QSQLITE",
m_dbName);
970 QSqlDatabase::removeDatabase(
m_dbName);
975 QString filename=db.databaseName();
977 QSqlDatabase::removeDatabase(
m_dbName);
978 QFile::remove(filename);
980 db=QSqlDatabase::addDatabase(
"QSQLITE",
m_dbName);
981 db.setDatabaseName(filename);
985 QSqlDatabase::removeDatabase(
m_dbName);
992 if (QSqlDatabase::contains(
m_dbName))
994 QSqlDatabase::database(
m_dbName).close();
995 QSqlDatabase::removeDatabase(
m_dbName);
1001 if (!rdb.open(QIODevice::ReadOnly | QIODevice::Text))
1004 QTextStream rdbParams(&rdb);
1021 QSqlDatabase::removeDatabase(
m_dbName);
1029 QSqlDatabase db=QSqlDatabase::database(
m_dbName);
1048 : ThreadWeaver::Job(parent)
1068 QSqlDatabase::removeDatabase(
m_dbName);
1069 kWarning(
TM_AREA) <<
"db closed "<<a.elapsed();
1075 if (accelPos.isNull())
1077 int accelPosInt=accelPos.toInt();
1078 if (accelPosInt!=-1)
1079 source.insert(accelPosInt, accel);
1088 catalog->
url().pathOrUrl(),
1093 QObject::connect(job,SIGNAL(done(ThreadWeaver::Job*)),job,SLOT(deleteLater()));
1094 ThreadWeaver::Weaver::instance()->enqueue(job);
1100 const QString& ctxt,
1101 const QString& file,
1103 const QString& dbName,
1105 : ThreadWeaver::Job(parent)
1127 inline QMap<uint,qlonglong>
invertMap(
const QMap<qlonglong,uint>& source)
1130 QMap<uint,qlonglong> sortingMap;
1131 for(QMap<qlonglong,uint>::const_iterator i=source.constBegin(); i!=source.constEnd(); ++i)
1133 sortingMap.insertMulti(i.value(), i.key());
1139 bool SelectJob::doSelect(QSqlDatabase& db,
1144 bool qpsql=(db.driverName()==
"QPSQL");
1145 QMap<qlonglong,uint> occurencies;
1146 QVector<qlonglong> idsForWord;
1148 QSqlQuery queryWords(db);
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];
1159 if (!( !idsForWord.isEmpty() && words.at(o)==words.at(o+1) ))
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;
1166 if (queryWords.next())
1168 QByteArray arr(queryWords.value(0).toByteArray());
1171 QList<QByteArray> ids(arr.split(
' '));
1173 idsForWord.reserve(p);
1175 idsForWord.append(ids.at(p).toLongLong(0,36));
1187 for(QVector<qlonglong>::const_iterator i=idsForWord.constBegin(); i!=idsForWord.constEnd(); i++)
1196 QRegExp rxSplit(
'('%tmp%
"\\W+|\\d+)+");
1199 sourceClean.remove(c.
accel);
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);
1208 QMap<uint,qlonglong> concordanceLevelToIds=
invertMap(occurencies);
1209 if (concordanceLevelToIds.isEmpty())
1213 QMap<uint,qlonglong>::const_iterator clit=concordanceLevelToIds.constEnd();
1214 if (concordanceLevelToIds.size()) --clit;
1215 if (concordanceLevelToIds.size())
while (--limit>=0)
1217 if (KDE_ISUNLIKELY( m_dequeued ))
1221 qlonglong level=clit.key();
1223 while(level==clit.key())
1225 joined+=QString::number(clit.value())+
',';
1226 if (clit==concordanceLevelToIds.constBegin() || --limit<0)
1233 QSqlQuery queryFetch(
"SELECT id, source, source_accel, source_markup FROM source_strings WHERE "
1234 "source_strings.id IN ("%joined%
')',db);
1236 while (queryFetch.next())
1238 e.
id=queryFetch.value(0).toLongLong();
1239 if (queryFetch.value(3).toByteArray().size())
1240 qDebug()<<
"BA"<<queryFetch.value(3).toByteArray();
1242 queryFetch.value(3).toByteArray() );
1246 kWarning(
TM_AREA)<<queryFetch.value(3).toByteArray().size()<<queryFetch.value(3).toByteArray();
1253 e.
dbName=db.connectionName();
1258 str.remove(c.
accel);
1260 QStringList englishSuggList(str.toLower().split(rxSplit,QString::SkipEmptyParts));
1261 if (englishSuggList.size()>10*englishList.size())
1264 QString result=
wordDiff(englishSuggList,englishList);
1268 int delSubStrCount=0;
1270 while ((pos=delPart.indexIn(result,pos))!=-1)
1273 delLen+=delPart.matchedLength()-23;
1275 pos+=delPart.matchedLength();
1278 int addSubStrCount=0;
1280 while ((pos=addPart.indexIn(result,pos))!=-1)
1282 addLen+=addPart.matchedLength()-23;
1284 pos+=addPart.matchedLength();
1288 int allLen=result.size()-23*addSubStrCount-23*delSubStrCount;
1289 int commonLen=allLen-delLen-addLen;
1292 bool possibleExactMatch=!(delLen+addLen);
1293 if (!possibleExactMatch)
1302 float score=9500*(pow(
float(commonLen)/
float(allLen),0.12f))
1304 / exp(0.014*
float(addLen)*log10(3.0f+addSubStrCount));
1312 float a=exp(0.008*
float(delLen)*log10(3.0f+delSubStrCount));
1323 float score=9900*(pow(
float(commonLen)/
float(allLen),0.15f))
1324 / exp(0.008*
float(delLen)*log10(3.0f+delSubStrCount));
1334 seen85=seen85 || e.
score>8500;
1335 if (seen85 && e.
score<6000)
1339 QString change_author_str;
1340 QString authors_table_str;
1344 change_author_str=
", pg_user.usename ";
1345 authors_table_str=
" JOIN pg_user ON (pg_user.usesysid=main.change_author) ";
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"
1358 QMap<TMEntry,bool> sortedEntryList;
1359 while (queryRest.next())
1361 e.
id=queryRest.value(0).toLongLong();
1362 e.
date=queryRest.value(1).toDate();
1363 e.
ctxt=queryRest.value(2).toString();
1365 queryRest.value(6).toByteArray() );
1367 QStringList matchData=queryRest.value(2).toString().split(
TM_DELIMITER,QString::KeepEmptyParts);
1368 e.
file=queryRest.value(7).toString();
1372 e.
obsolete=queryRest.value(3).toInt()&1;
1379 if (possibleExactMatch)
1386 if (!m_ctxt.isEmpty()&&matchData.size()>0)
1388 if (matchData.at(0)==m_ctxt)
1393 if (matchData.size()>1)
1395 int form=matchData.at(1).toInt();
1408 sortedEntryList.insertMulti(e,
false);
1412 QHash<QString,int> hash;
1414 QMap<TMEntry,bool>::const_iterator it=sortedEntryList.constEnd();
1415 if (sortedEntryList.size())
while(
true)
1423 if (it==sortedEntryList.constBegin())
1426 for (
int i=oldCount;i<
m_entries.size();++i)
1431 if (clit==concordanceLevelToIds.constBegin())
1433 if (seen85) limit=qMin(limit, 100);
1446 QSqlDatabase db=QSqlDatabase::database(
m_dbName);
1449 QRegExp rxClean1(c.
markup);rxClean1.setMinimal(
true);
1454 if (KDE_ISUNLIKELY( words.isEmpty() ))
1458 bool isShort=words.size()<20;
1460 if (!doSelect(db,words,isShort))
1461 doSelect(db,words,!isShort);
1470 if (KDE_ISUNLIKELY( m_dequeued ))
1491 const QString& dbName,
1493 : ThreadWeaver::Job(parent)
1512 thread()->setPriority(QThread::IdlePriority);
1517 QSqlDatabase db=QSqlDatabase::database(
m_dbName);
1520 QRegExp rxClean1(c.
markup);rxClean1.setMinimal(
true);
1527 kWarning()<<
"not indexing file because target languages don't match:"<<c.
targetLangCode<<
"in TM vs"<<catalog.
targetLangCode()<<
"in file";
1530 qlonglong priorId=-1;
1532 QSqlQuery queryBegin(
"BEGIN",db);
1537 queryBegin.exec(QString(
"UPDATE main SET bits=(bits|1) WHERE file=%1").arg(fileId));
1559 fileId,db,rxClean1,c.
accel,priorId,priorId);
1573 fileId,db,rxClean1,c.
accel,priorId,priorId);
1575 if (KDE_ISLIKELY( ok ))
1578 QSqlQuery queryEnd(
"END",db);
1579 kWarning(
TM_AREA) <<
"ScanJob: done "<<a.elapsed();
1587 : ThreadWeaver::Job(parent)
1606 QRegExp rxClean1(c.
markup);rxClean1.setMinimal(
true);
1613 const QString& ctxt,
1619 const QString& dbName,
1621 : ThreadWeaver::Job(parent)
1622 , m_filePath(filePath)
1624 , m_english(english)
1625 , m_newTarget(newTarget)
1627 , m_approved(approved)
1636 QSqlDatabase db=QSqlDatabase::database(m_dbName);
1640 QRegExp rxClean1(c.
markup);rxClean1.setMinimal(
true);
1643 qlonglong fileId=
getFileId(m_filePath,db);
1648 QSqlQuery queryBegin(
"BEGIN",db);
1649 qlonglong priorId=-1;
1652 m_approved, fileId,db,rxClean1,c.
accel,priorId,priorId))
1653 kWarning(
TM_AREA)<<
"error updating db";
1654 QSqlQuery queryEnd(
"END",db);
1661 #include <QXmlDefaultHandler>
1662 #include <QXmlSimpleReader>
1667 class TmxParser :
public QXmlDefaultHandler
1687 TmxParser(
const QString& dbName);
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&);
1703 QList<InlineTag> m_inlineTags;
1705 QString m_pluralForm;
1707 QString m_approvedString;
1715 QMap<QString,qlonglong> m_fileIds;
1716 QString m_dbLangCode;
1720 TmxParser::TmxParser(
const QString& dbName)
1721 : m_dbLangCode(
Project::instance()->langCode().toLower())
1724 db=QSqlDatabase::database(dbName);
1727 rxClean1.setPattern(c.
markup);rxClean1.setMinimal(
true);
1731 bool TmxParser::startDocument()
1736 QSqlQuery queryBegin(
"BEGIN",db);
1744 TmxParser::~TmxParser()
1746 QSqlQuery queryEnd(
"END",db);
1750 bool TmxParser::startElement(
const QString&,
const QString&,
1751 const QString& qName,
1752 const QXmlAttributes& attr)
1757 m_hits=attr.value(
"usagecount").toInt(&ok);
1761 m_segment[Source].clear();
1762 m_segment[Target].clear();
1764 m_pluralForm.clear();
1766 m_approvedString.clear();
1769 else if (qName==
"tuv")
1771 if (attr.value(
"xml:lang").toLower()==
"en")
1773 else if (attr.value(
"xml:lang").toLower()==m_dbLangCode)
1777 kWarning(
TM_AREA)<<
"skipping lang"<<attr.value(
"xml:lang");
1781 else if (qName==
"prop")
1783 if (attr.value(
"type").toLower()==
"x-context")
1784 m_state=propContext;
1785 else if (attr.value(
"type").toLower()==
"x-file")
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;
1794 else if (qName==
"seg")
1798 else if (m_state==seg && m_lang!=Null)
1804 int pos=m_segment[m_lang].string.size();
1805 m_inlineTags.append(
InlineTag(pos, pos, t, attr.value(
"id")));
1811 bool TmxParser::endElement(
const QString&,
const QString&,
const QString& qName)
1813 static const char* tmxFilename=
"tmx-import";
1814 static const char* no=
"no";
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);
1823 if (!m_pluralForm.isEmpty())
1826 qlonglong priorId=-1;
1830 m_approvedString!=no,
1831 fileId,db,rxClean1,accel,priorId,priorId);
1832 if (KDE_ISLIKELY( ok ))
1835 else if (m_state==seg && m_lang!=Null)
1845 tag.
end=m_segment[m_lang].string.size();
1848 m_segment[m_lang].tags.append(tag);
1857 bool TmxParser::characters (
const QString& ch )
1859 if(m_state==seg && m_lang!=Null)
1860 m_segment[m_lang].string+=ch;
1861 else if(m_state==propFile)
1863 else if(m_state==propContext)
1865 else if(m_state==propPluralForm)
1867 else if(m_state==propApproved)
1868 m_approvedString+=ch;
1878 const QString& dbName,
1880 : ThreadWeaver::Job(parent)
1881 , m_filename(filename)
1888 kWarning(
TM_AREA) <<
"ImportTmxJob dtor ";
1893 thread()->setPriority(QThread::IdlePriority);
1897 if (!file.open(QFile::ReadOnly | QFile::Text))
1901 QXmlSimpleReader reader;
1902 reader.setContentHandler(&parser);
1904 QXmlInputSource xmlInputSource(&file);
1905 if (!reader.parse(xmlInputSource))
1915 #include <QXmlStreamWriter>
1918 const QString& dbName,
1920 : ThreadWeaver::Job(parent)
1921 , m_filename(filename)
1928 kWarning(
TM_AREA) <<
"ExportTmxJob dtor ";
1933 thread()->setPriority(QThread::IdlePriority);
1937 if (!out.open(QFile::WriteOnly|QFile::Text))
1940 QXmlStreamWriter xmlOut(&out);
1941 xmlOut.setAutoFormatting(
true);
1942 xmlOut.writeStartDocument(
"1.0");
1946 xmlOut.writeStartElement(
"tmx");
1947 xmlOut.writeAttribute(
"version",
"2.0");
1949 xmlOut.writeStartElement(
"header");
1950 xmlOut.writeAttribute(
"creationtool",
"lokalize");
1952 xmlOut.writeAttribute(
"segtype",
"paragraph");
1953 xmlOut.writeAttribute(
"o-encoding",
"UTF-8");
1954 xmlOut.writeEndElement();
1956 xmlOut.writeStartElement(
"body");
1962 QSqlDatabase db=QSqlDatabase::database(
m_dbName);
1963 QSqlQuery query1(db);
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();
1977 while (query1.next())
1982 xmlOut.writeStartElement(
"tu");
1983 xmlOut.writeAttribute(
"tuid",QString::number(query1.value(0).toLongLong()));
1985 xmlOut.writeStartElement(
"tuv");
1986 xmlOut.writeAttribute(
"xml:lang",
"en");
1987 xmlOut.writeStartElement(
"seg");
1988 xmlOut.writeCharacters(source);
1989 xmlOut.writeEndElement();
1990 xmlOut.writeEndElement();
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())
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();
2010 if (!ctxt.isEmpty())
2012 xmlOut.writeStartElement(
"prop");
2013 xmlOut.writeAttribute(
"type",
"x-context");
2014 xmlOut.writeCharacters(ctxt);
2015 xmlOut.writeEndElement();
2018 QString filePath=query1.value(8).toString();
2019 if (!filePath.isEmpty())
2021 xmlOut.writeStartElement(
"prop");
2022 xmlOut.writeAttribute(
"type",
"x-file");
2023 xmlOut.writeCharacters(filePath);
2024 xmlOut.writeEndElement();
2026 qlonglong bits=query1.value(8).toLongLong();
2028 if (!filePath.isEmpty())
2030 xmlOut.writeStartElement(
"prop");
2031 xmlOut.writeAttribute(
"type",
"x-approved");
2032 xmlOut.writeCharacters(
"no");
2033 xmlOut.writeEndElement();
2035 xmlOut.writeStartElement(
"seg");
2036 xmlOut.writeCharacters(target);
2037 xmlOut.writeEndElement();
2038 xmlOut.writeEndElement();
2039 xmlOut.writeEndElement();
2047 xmlOut.writeEndDocument();
2050 kWarning(
TM_AREA) <<
"Done exporting "<<a.elapsed();
2059 : ThreadWeaver::Job(parent)
2062 , m_query(queryString)
2064 kDebug(
TM_AREA)<<dbName<<queryString;
2075 QSqlDatabase db=QSqlDatabase::database(
m_dbName);
2079 kWarning(
TM_AREA)<<
"db.open()="<<db.open();
QStringList context(const DocPosition &pos) const
CatalogString targetWithTags(const DocPosition &pos) const
SelectJob(const CatalogString &source, const QString &ctxt, const QString &file, const DocPosition &, const QString &dbName, QObject *parent=0)
static bool isPaired(InlineElement type)
CloseDBJob(const QString &dbName, QObject *parent=0)
ExecQueryJob(const QString &queryString, const QString &dbName, QObject *parent=0)
QString targetLangCode() const
QString wordDiff(QStringList s1, QStringList s2)
This is low-level wrapper used for evaluating translation memory search results.
int numberOfPluralForms() const
#define TAGRANGE_IMAGE_SYMBOL
void aboutToBeDequeued(ThreadWeaver::WeaverInterface *)
QString userVisibleWordDiff(const QString &str1ForMatching, const QString &str2ForMatching, const QString &accel, const QString &markup, int options)
static Project * instance()
RemoveJob(const TMEntry &entry, QObject *parent=0)
int uniqueTranslationsCount
bool m_connectionSuccessful
static qlonglong getFileId(const QString &path, QSqlDatabase &db)
QList< TMEntry > m_entries
QString langCode() const
Get LangCode.
OpenDBJob(const QString &dbName, DbType type=TM::Local, bool reconnect=false, const ConnectionParams &connParams=ConnectionParams(), QObject *parent=0)
static void addToIndex(qlonglong sourceId, QString sourceString, QRegExp &rxClean1, const QString &accel, QSqlDatabase &db)
QString markup() const
Get Markup.
int loadFromUrl(const KUrl &url, const KUrl &saidUrl=KUrl(), int *fileSize=0)
static TMConfig getConfig(QSqlDatabase &db, bool useCache=true)
static InlineElement getElementType(const QByteArray &)
static void getStats(const QSqlDatabase &db, int &pairsCount, int &uniqueSourcesCount, int &uniqueTranslationsCount)
ExportTmxJob(const QString &url, const QString &dbName, QObject *parent=0)
This struct represents a position in a catalog.
QString accel() const
Get Accel.
Singleton object that represents project.
ImportTmxJob(const QString &url, const QString &dbName, QObject *parent=0)
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...
static QString escape(QString str)
bool isPlural(uint index) const
Q_SCRIPTABLE QString targetLangCode() const
static void initPgDb(QSqlDatabase &db)
SelectJob * initSelectJob(Catalog *, DocPosition pos, QString db=QString(), int opt=Enqueue)
static int suggCount()
Get SuggCount.
ConnectionParams m_connParams
ScanJob(const KUrl &url, const QString &dbName, QObject *parent=0)
UpdateJob(const QString &filePath, const QString &ctxt, const CatalogString &en, const CatalogString &newTarget, int form, bool approved, const QString &dbName, QObject *parent=0)
static bool doRemoveEntry(qlonglong mainId, QRegExp &rxClean1, const QString &accel, QSqlDatabase &db)
QMap< QString, TMConfig > tmConfigCache
#define TM_DATABASE_EXTENSION
data structure used to pass info about inline elements a XLIFF tag is represented by a TAGRANGE_IMAGE...
#define REMOTETM_DATABASE_EXTENSION
QString projectID() const
Get ProjectID.
QByteArray tagsAsByteArray() const
CatalogString sourceWithTags(const DocPosition &pos) const
bool isApproved(uint index) const
int numberOfEntries() const
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)
static bool initSqliteDb(QSqlDatabase &db)
This class represents a catalog It uses CatalogStorage interface to work with catalogs in different f...
QMap< uint, qlonglong > invertMap(const QMap< qlonglong, uint > &source)
static const char * getElementName(InlineElement type)
static void setConfig(QSqlDatabase &db, const TMConfig &c)
data structure used to pass info about inline elements a XLIFF tag is represented by a TAGRANGE_IMAGE...
static void doSplit(QString &cleanEn, QStringList &words, QRegExp &rxClean1, const QString &accel)
splits string into words, removing any markup
static QString makeAcceledString(QString source, const QString &accel, const QVariant &accelPos)
Q_SCRIPTABLE QString sourceLangCode() const