~ubuntu-branches/ubuntu/vivid/kdesdk/vivid

« back to all changes in this revision

Viewing changes to lokalize/src/catalog/ts/tsstorage.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2012-06-06 11:49:54 UTC
  • mfrom: (0.4.21)
  • Revision ID: package-import@ubuntu.com-20120606114954-rdls73fzlpzxglbx
Tags: 4:4.8.80-0ubuntu1
* New uptream beta release
* Update dont_export_private_classes.diff

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright 2008-2012 Nick Shaforostoff <shaforostoff@kde.ru>
 
3
 
 
4
This program is free software; you can redistribute it and/or
 
5
modify it under the terms of the GNU General Public License as
 
6
published by the Free Software Foundation; either version 2 of
 
7
the License or (at your option) version 3 or any later version
 
8
accepted by the membership of KDE e.V. (or its successor approved
 
9
by the membership of KDE e.V.), which shall act as a proxy
 
10
defined in Section 14 of version 3 of the license.
 
11
 
 
12
This program is distributed in the hope that it will be useful,
 
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
GNU General Public License for more details.
 
16
 
 
17
You should have received a copy of the GNU General Public License
 
18
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
*/
 
20
 
 
21
#include "tsstorage.h"
 
22
 
 
23
#include "gettextheader.h"
 
24
#include "project.h"
 
25
#include "version.h"
 
26
#include "prefs_lokalize.h"
 
27
 
 
28
#include <QProcess>
 
29
#include <QString>
 
30
#include <QMap>
 
31
#include <QDomDocument>
 
32
#include <QTime>
 
33
#include <QPair>
 
34
#include <QList>
 
35
 
 
36
 
 
37
#include <kdebug.h>
 
38
#include <kglobal.h>
 
39
#include <klocale.h>
 
40
#include <kdatetime.h>
 
41
#include <QXmlSimpleReader>
 
42
 
 
43
static const char* const noyes[]={"no","yes"};
 
44
 
 
45
static const QString names[]={"source" ,"translation","oldsource" ,"translatorcomment","comment" ,"name" ,"numerus"};
 
46
enum TagNames                {SourceTag,TargetTag    ,OldSourceTag,NoteTag            ,DevNoteTag,NameTag,PluralTag};
 
47
 
 
48
static const QString attrnames[]={"location"  ,"type"  ,"obsolete"};
 
49
enum AttrNames                   {LocationAttr,TypeAttr,ObsoleteAttr};
 
50
 
 
51
static const QString attrvalues[]={"obsolete"};
 
52
enum AttValues                    {ObsoleteVal};
 
53
 
 
54
TsStorage::TsStorage()
 
55
 : CatalogStorage()
 
56
{
 
57
}
 
58
 
 
59
TsStorage::~TsStorage()
 
60
{
 
61
}
 
62
 
 
63
int TsStorage::capabilities() const
 
64
{
 
65
    return 0;//MultipleNotes;
 
66
}
 
67
 
 
68
//BEGIN OPEN/SAVE
 
69
 
 
70
int TsStorage::load(QIODevice* device)
 
71
{
 
72
    QTime chrono;chrono.start();
 
73
 
 
74
 
 
75
    QXmlSimpleReader reader;
 
76
    reader.setFeature("http://qtsoftware.com/xml/features/report-whitespace-only-CharData",true);
 
77
    reader.setFeature("http://xml.org/sax/features/namespaces",false);
 
78
    QXmlInputSource source(device);
 
79
 
 
80
    QString errorMsg;
 
81
    int errorLine;//+errorColumn;
 
82
    bool success=m_doc.setContent(&source, &reader, &errorMsg, &errorLine/*,errorColumn*/);
 
83
 
 
84
    if (!success)
 
85
    {
 
86
        kWarning()<<errorMsg;
 
87
        return errorLine+1;
 
88
    }
 
89
 
 
90
 
 
91
    QDomElement file=m_doc.elementsByTagName("TS").at(0).toElement();
 
92
    m_sourceLangCode=file.attribute("sourcelanguage");
 
93
    m_targetLangCode=file.attribute("language");
 
94
    m_numberOfPluralForms=numberOfPluralFormsForLangCode(m_targetLangCode);
 
95
 
 
96
    //Create entry mapping.
 
97
    //Along the way: for langs with more than 2 forms
 
98
    //we create any form-entries additionally needed
 
99
 
 
100
    entries=m_doc.elementsByTagName("message");
 
101
    int size=entries.size();
 
102
 
 
103
    kWarning()<<chrono.elapsed();
 
104
    return 0;
 
105
}
 
106
 
 
107
bool TsStorage::save(QIODevice* device, bool belongsToProject)
 
108
{
 
109
    QTextStream stream(device);
 
110
    m_doc.save(stream,4);
 
111
    return true;
 
112
}
 
113
//END OPEN/SAVE
 
114
 
 
115
//BEGIN STORAGE TRANSLATION
 
116
 
 
117
int TsStorage::size() const
 
118
{
 
119
    //return m_map.size();
 
120
 
 
121
    return entries.size();
 
122
}
 
123
 
 
124
 
 
125
 
 
126
 
 
127
/**
 
128
 * helper structure used during XLIFF XML walk-through
 
129
 */
 
130
struct ContentEditingData
 
131
{
 
132
    enum ActionType{Get,DeleteText,InsertText,CheckLength};
 
133
 
 
134
    QString stringToInsert;
 
135
    int pos;
 
136
    int lengthOfStringToRemove;
 
137
    ActionType actionType;
 
138
 
 
139
    ///Get
 
140
    ContentEditingData(ActionType type=Get)
 
141
    : pos(-1)
 
142
    , lengthOfStringToRemove(-1)
 
143
    , actionType(type)
 
144
    {}
 
145
 
 
146
    ///DeleteText
 
147
    ContentEditingData(int p, int l)
 
148
    : pos(p)
 
149
    , lengthOfStringToRemove(l)
 
150
    , actionType(DeleteText)
 
151
    {}
 
152
 
 
153
    ///InsertText
 
154
    ContentEditingData(int p,const QString& s)
 
155
    : stringToInsert(s)
 
156
    , pos(p)
 
157
    , lengthOfStringToRemove(-1)
 
158
    , actionType(InsertText)
 
159
    {}
 
160
};
 
161
 
 
162
static QString doContent(QDomElement elem, int startingPos, ContentEditingData* data);
 
163
 
 
164
/**
 
165
 * walks through XLIFF XML and performs actions depending on ContentEditingData:
 
166
 * - reads content
 
167
 * - deletes content, or
 
168
 * - inserts content
 
169
 */
 
170
static QString content(QDomElement elem, ContentEditingData* data=0)
 
171
{
 
172
    return doContent(elem, 0, data);
 
173
}
 
174
 
 
175
static QString doContent(QDomElement elem, int startingPos, ContentEditingData* data)
 
176
{
 
177
    //actually startingPos is current pos
 
178
 
 
179
    QString result;
 
180
 
 
181
    if (elem.isNull()
 
182
        || (!result.isEmpty() && ContentEditingData::CheckLength))
 
183
        return QString();
 
184
 
 
185
    bool seenCharacterDataAfterElement=false;
 
186
 
 
187
    QDomNode n = elem.firstChild();
 
188
    while (!n.isNull())
 
189
    {
 
190
        if (n.isCharacterData())
 
191
        {
 
192
            seenCharacterDataAfterElement=true;
 
193
 
 
194
            QDomCharacterData c=n.toCharacterData();
 
195
            QString cData=c.data();
 
196
 
 
197
            if (data && data->pos!=-1 &&
 
198
               data->pos>=startingPos && data->pos<=startingPos+cData.size())
 
199
            {
 
200
                // time to do some action! ;)
 
201
                int localStartPos=data->pos-startingPos;
 
202
 
 
203
                //BEGIN DELETE TEXT
 
204
                if (data->actionType==ContentEditingData::DeleteText) //(data->lengthOfStringToRemove!=-1)
 
205
                {
 
206
                    if (localStartPos+data->lengthOfStringToRemove>cData.size())
 
207
                    {
 
208
                        //text is fragmented into several QDomCharacterData
 
209
                        int localDelLen=cData.size()-localStartPos;
 
210
                        //qWarning()<<"text is fragmented into several QDomCharacterData. localDelLen:"<<localDelLen<<"cData:"<<cData;
 
211
                        c.deleteData(localStartPos,localDelLen);
 
212
                        //setup data for future iterations
 
213
                        data->lengthOfStringToRemove=data->lengthOfStringToRemove-localDelLen;
 
214
                        //data->pos=startingPos;
 
215
                        //qWarning()<<"\tsetup:"<<data->pos<<data->lengthOfStringToRemove;
 
216
                    }
 
217
                    else
 
218
                    {
 
219
                        //qWarning()<<"simple delete"<<localStartPos<<data->lengthOfStringToRemove;
 
220
                        c.deleteData(localStartPos,data->lengthOfStringToRemove);
 
221
                        data->actionType=ContentEditingData::CheckLength;
 
222
                        return QString('a');//so it exits 100%
 
223
                    }
 
224
                }
 
225
                //END DELETE TEXT
 
226
                //INSERT
 
227
                else if (data->actionType==ContentEditingData::InsertText)
 
228
                {
 
229
                    c.insertData(localStartPos,data->stringToInsert);
 
230
                    data->actionType=ContentEditingData::CheckLength;
 
231
                    return QString('a');//so it exits 100%
 
232
                }
 
233
                cData=c.data();
 
234
            }
 
235
            //else
 
236
            //    if (data&&data->pos!=-1/*&& n.nextSibling().isNull()*/)
 
237
            //        kWarning()<<"arg!"<<startingPos<<"data->pos"<<data->pos;
 
238
 
 
239
            result += cData;
 
240
            startingPos+=cData.size();
 
241
        }
 
242
        n = n.nextSibling();
 
243
    }
 
244
    if (!seenCharacterDataAfterElement)
 
245
    {
 
246
        //add empty charData child so that user could add some text
 
247
        elem.appendChild( elem.ownerDocument().createTextNode(QString()) );
 
248
    }
 
249
 
 
250
    return result;
 
251
}
 
252
 
 
253
 
 
254
 
 
255
//flat-model interface (ignores XLIFF grouping)
 
256
 
 
257
CatalogString TsStorage::catalogString(QDomElement contentElement) const
 
258
{
 
259
    CatalogString catalogString;
 
260
    ContentEditingData data(ContentEditingData::Get);
 
261
    catalogString.string=content(contentElement, &data);
 
262
    return catalogString;
 
263
}
 
264
 
 
265
CatalogString TsStorage::catalogString(const DocPosition& pos) const
 
266
{
 
267
    return catalogString(pos.part==DocPosition::Target?targetForPos(pos):sourceForPos(pos.entry));
 
268
}
 
269
 
 
270
CatalogString TsStorage::targetWithTags(DocPosition pos) const
 
271
{
 
272
    return catalogString(targetForPos(pos));
 
273
}
 
274
CatalogString TsStorage::sourceWithTags(DocPosition pos) const
 
275
{
 
276
    return catalogString(sourceForPos(pos.entry));
 
277
}
 
278
 
 
279
QString TsStorage::source(const DocPosition& pos) const
 
280
{
 
281
    return content(sourceForPos(pos.entry));
 
282
}
 
283
QString TsStorage::target(const DocPosition& pos) const
 
284
{
 
285
    return content(targetForPos(pos));
 
286
}
 
287
 
 
288
 
 
289
void TsStorage::targetDelete(const DocPosition& pos, int count)
 
290
{
 
291
    ContentEditingData data(pos.offset,count);
 
292
    content(targetForPos(pos),&data);
 
293
}
 
294
 
 
295
void TsStorage::targetInsert(const DocPosition& pos, const QString& arg)
 
296
{
 
297
    kWarning()<<pos.entry<<arg;
 
298
    QDomElement targetEl=targetForPos(pos);
 
299
    //BEGIN add <*target>
 
300
    if (targetEl.isNull())
 
301
    {
 
302
        QDomNode unitEl=unitForPos(pos.entry);
 
303
        QDomNode refNode=unitEl.firstChildElement(names[SourceTag]);
 
304
        targetEl = unitEl.insertAfter(m_doc.createElement(names[TargetTag]),refNode).toElement();
 
305
  
 
306
        if (pos.entry<size())
 
307
        {
 
308
            targetEl.appendChild(m_doc.createTextNode(arg));//i bet that pos.offset is 0 ;)
 
309
            return;
 
310
        }
 
311
    }
 
312
    //END add <*target>
 
313
    if (arg.isEmpty()) return; //means we were called just to add <taget> tag
 
314
 
 
315
    ContentEditingData data(pos.offset,arg);
 
316
    content(targetEl,&data);
 
317
}
 
318
 
 
319
void TsStorage::setTarget(const DocPosition& pos, const QString& arg)
 
320
{
 
321
    Q_UNUSED(pos);
 
322
    Q_UNUSED(arg);
 
323
//TODO
 
324
}
 
325
 
 
326
 
 
327
QVector<AltTrans> TsStorage::altTrans(const DocPosition& pos) const
 
328
{
 
329
    QVector<AltTrans> result;
 
330
 
 
331
    QString oldsource=content(unitForPos(pos.entry).firstChildElement(names[OldSourceTag]));
 
332
    if (!oldsource.isEmpty())
 
333
        result<<AltTrans(CatalogString(oldsource), i18n("Previous source value, saved by lupdate tool"));
 
334
 
 
335
    return result;
 
336
}
 
337
 
 
338
 
 
339
QStringList TsStorage::sourceFiles(const DocPosition& pos) const
 
340
{
 
341
    QStringList result;
 
342
 
 
343
    QDomElement elem = unitForPos(pos.entry).firstChildElement(attrnames[LocationAttr]);
 
344
    while (!elem.isNull())
 
345
    {
 
346
        QString sourcefile=elem.attribute("filename");
 
347
        QString linenumber=elem.attribute("line");
 
348
        if (!( sourcefile.isEmpty()&&linenumber.isEmpty() ))
 
349
            result.append(sourcefile+':'+linenumber);
 
350
 
 
351
        elem=elem.nextSiblingElement(attrnames[LocationAttr]);
 
352
    }
 
353
    //qSort(result);
 
354
 
 
355
    return result;
 
356
}
 
357
 
 
358
QVector<Note> TsStorage::notes(const DocPosition& pos) const
 
359
{
 
360
    QVector<Note> result;
 
361
 
 
362
    QDomElement elem = unitForPos(pos.entry).firstChildElement(names[NoteTag]);
 
363
    while (!elem.isNull())
 
364
    {
 
365
        Note note;
 
366
        note.content=elem.text();
 
367
        result.append(note);
 
368
 
 
369
        elem=elem.nextSiblingElement(names[NoteTag]);
 
370
    }
 
371
    return result;
 
372
}
 
373
 
 
374
QVector<Note> TsStorage::developerNotes(const DocPosition& pos) const
 
375
{
 
376
    QVector<Note> result;
 
377
 
 
378
    QDomElement elem = unitForPos(pos.entry).firstChildElement(names[DevNoteTag]);
 
379
    while (!elem.isNull())
 
380
    {
 
381
        Note note;
 
382
        note.content=elem.text();
 
383
        result.append(note);
 
384
 
 
385
        elem=elem.nextSiblingElement(names[DevNoteTag]);
 
386
    }
 
387
    return result;
 
388
}
 
389
 
 
390
Note TsStorage::setNote(DocPosition pos, const Note& note)
 
391
{
 
392
    //kWarning()<<int(pos.form)<<note.content;
 
393
    QDomElement unit=unitForPos(pos.entry);
 
394
    QDomElement elem;
 
395
    Note oldNote;
 
396
    if (pos.form==-1 && !note.content.isEmpty())
 
397
    {
 
398
        QDomElement ref=unit.lastChildElement(names[NoteTag]);
 
399
        elem=unit.insertAfter( m_doc.createElement(names[NoteTag]),ref).toElement();
 
400
        elem.appendChild(m_doc.createTextNode(QString()));
 
401
    }
 
402
    else
 
403
    {
 
404
        QDomNodeList list=unit.elementsByTagName(names[NoteTag]);
 
405
        if (pos.form==-1) pos.form=list.size()-1;
 
406
        if (pos.form<list.size())
 
407
        {
 
408
            elem = unit.elementsByTagName(names[NoteTag]).at(pos.form).toElement();
 
409
            oldNote.content=elem.text();
 
410
        }
 
411
    }
 
412
 
 
413
    if (elem.isNull()) return oldNote;
 
414
 
 
415
    if (!elem.text().isEmpty())
 
416
    {
 
417
        ContentEditingData data(0,elem.text().size());
 
418
        content(elem,&data);
 
419
    }
 
420
 
 
421
    if (!note.content.isEmpty())
 
422
    {
 
423
        ContentEditingData data(0,note.content);
 
424
        content(elem,&data);
 
425
    }
 
426
    else
 
427
        unit.removeChild(elem);
 
428
 
 
429
    return oldNote;
 
430
}
 
431
 
 
432
QStringList TsStorage::context(const DocPosition& pos) const
 
433
{
 
434
    QStringList result;
 
435
 
 
436
    QDomElement unit=unitForPos(pos.entry);
 
437
    QDomElement context=unit.parentNode().toElement();
 
438
    //if (context.isNull())
 
439
    //    return result;
 
440
 
 
441
    QDomElement name=context.firstChildElement(names[NameTag]);
 
442
    if (name.isNull())
 
443
        return result;
 
444
 
 
445
    result.append(name.text());
 
446
    return result;
 
447
}
 
448
 
 
449
QStringList TsStorage::matchData(const DocPosition& pos) const
 
450
{
 
451
    Q_UNUSED(pos);
 
452
    return QStringList();
 
453
}
 
454
 
 
455
QString TsStorage::id(const DocPosition& pos) const
 
456
{
 
457
    QString result=source(pos);
 
458
    result.remove('\n');
 
459
    QStringList ctxt=context(pos);
 
460
    if (ctxt.size())
 
461
        result.prepend(ctxt.first());
 
462
    return result;
 
463
}
 
464
 
 
465
bool TsStorage::isPlural(const DocPosition& pos) const
 
466
{
 
467
    QDomElement unit=unitForPos(pos.entry);
 
468
 
 
469
    return unit.hasAttribute(names[PluralTag]);
 
470
}
 
471
 
 
472
void TsStorage::setApproved(const DocPosition& pos, bool approved)
 
473
{
 
474
    targetInsert(pos,QString()); //adds <taget> if needed
 
475
    QDomElement target=unitForPos(pos.entry).firstChildElement(names[TargetTag]); //asking directly to bypass plural state detection
 
476
    if (target.attribute(attrnames[TypeAttr])==attrvalues[ObsoleteVal])
 
477
        return;
 
478
    if (approved)
 
479
        target.removeAttribute(attrnames[TypeAttr]);
 
480
    else
 
481
        target.setAttribute(attrnames[TypeAttr],"unfinished");
 
482
}
 
483
 
 
484
bool TsStorage::isApproved(const DocPosition& pos) const
 
485
{
 
486
    QDomElement target=unitForPos(pos.entry).firstChildElement(names[TargetTag]);
 
487
    return !target.hasAttribute(attrnames[TypeAttr]);
 
488
}
 
489
 
 
490
bool TsStorage::isObsolete(int entry) const
 
491
{
 
492
    QDomElement target=unitForPos(entry).firstChildElement(names[TargetTag]);
 
493
    return target.attribute(attrnames[TypeAttr])==attrvalues[ObsoleteVal];
 
494
}
 
495
 
 
496
bool TsStorage::isEmpty(const DocPosition& pos) const
 
497
{
 
498
    ContentEditingData data(ContentEditingData::CheckLength);
 
499
    return content(targetForPos(pos),&data).isEmpty();
 
500
}
 
501
 
 
502
bool TsStorage::isEquivTrans(const DocPosition& pos) const
 
503
{
 
504
    return true;//targetForPos(pos.entry).attribute("equiv-trans")!="no";
 
505
}
 
506
 
 
507
void TsStorage::setEquivTrans(const DocPosition& pos, bool equivTrans)
 
508
{
 
509
    //targetForPos(pos.entry).setAttribute("equiv-trans",noyes[equivTrans]);
 
510
}
 
511
 
 
512
QDomElement TsStorage::unitForPos(int pos) const
 
513
{
 
514
    return entries.at(pos).toElement();
 
515
}
 
516
 
 
517
QDomElement TsStorage::targetForPos(DocPosition pos) const
 
518
{
 
519
    QDomElement unit=unitForPos(pos.entry);
 
520
    QDomElement translation=unit.firstChildElement(names[TargetTag]);
 
521
    if (!unit.hasAttribute(names[PluralTag]))
 
522
        return translation;
 
523
    
 
524
    if (pos.form==-1) pos.form=0;
 
525
    
 
526
    QDomNodeList forms=translation.elementsByTagName("numerusform");
 
527
    while (pos.form>=forms.size())
 
528
        translation.appendChild( unit.ownerDocument().createElement("numerusform") );
 
529
    return forms.at(pos.form).toElement();
 
530
}
 
531
 
 
532
QDomElement TsStorage::sourceForPos(int pos) const
 
533
{
 
534
    return unitForPos(pos).firstChildElement(names[SourceTag]);
 
535
}
 
536
 
 
537
//END STORAGE TRANSLATION
 
538
 
 
539