31 #include <QDomDocument>
40 #include <kdatetime.h>
41 #include <QXmlSimpleReader>
43 static const QString
noyes[]={
"no",
"yes"};
65 QTime chrono;chrono.start();
68 QXmlSimpleReader reader;
69 reader.setFeature(
"http://qtsoftware.com/xml/features/report-whitespace-only-CharData",
true);
70 reader.setFeature(
"http://xml.org/sax/features/namespaces",
false);
71 QXmlInputSource
source(device);
75 bool success=m_doc.setContent(&source, &reader, &errorMsg, &errorLine);
84 QDomElement file=m_doc.elementsByTagName(
"file").at(0).toElement();
93 entries=m_doc.elementsByTagName(
"trans-unit");
94 int size=entries.size();
97 for(
int i=0;i<
size;++i)
99 QDomElement parentElement=entries.at(i).parentNode().toElement();
103 m_unitsById[entries.at(i).toElement().attribute(
"id")]=i;
105 if (parentElement.tagName()==
"group" && parentElement.attribute(
"restype")==
"x-gettext-plurals")
109 while (--localPluralNum>0 && (++i)<size)
111 QDomElement p=entries.at(i).parentNode().toElement();
112 if (p.tagName()==
"group" && p.attribute(
"restype")==
"x-gettext-plurals")
115 parentElement.appendChild(entries.at(m_map.last()).cloneNode());
120 binEntries=m_doc.elementsByTagName(
"bin-unit");
121 size=binEntries.size();
122 int offset=m_map.size();
123 for(
int i=0;i<
size;++i)
124 m_unitsById[binEntries.at(i).toElement().attribute(
"id")]=offset+i;
147 QDomElement header=file.firstChildElement(
"header");
149 header=file.insertBefore(m_doc.createElement(
"header"), QDomElement()).toElement();
150 QDomElement toolElem=header.firstChildElement(
"tool");
151 while (!toolElem.isNull() && toolElem.attribute(
"tool-id")!=
"lokalize-" LOKALIZE_VERSION)
152 toolElem=toolElem.nextSiblingElement(
"tool");
154 if (toolElem.isNull())
156 toolElem=header.appendChild(m_doc.createElement(
"tool")).toElement();
158 toolElem.setAttribute(
"tool-name",
"Lokalize");
162 kWarning()<<chrono.elapsed();
168 QTextStream stream(device);
169 m_doc.save(stream,2);
187 struct ContentEditingData
191 QList<InlineTag> tags;
192 QString stringToInsert;
194 int lengthOfStringToRemove;
195 ActionType actionType;
198 ContentEditingData(ActionType type=Get)
200 , lengthOfStringToRemove(-1)
205 ContentEditingData(
int p,
int l)
207 , lengthOfStringToRemove(l)
208 , actionType(DeleteText)
212 ContentEditingData(
int p,
const QString& s)
215 , lengthOfStringToRemove(-1)
216 , actionType(InsertText)
220 ContentEditingData(
int p,
const InlineTag& range)
222 , lengthOfStringToRemove(-1)
229 ContentEditingData(
int p)
231 , lengthOfStringToRemove(-1)
237 static QString
doContent(QDomElement elem,
int startingPos, ContentEditingData* data);
245 static QString
content(QDomElement elem, ContentEditingData* data=0)
250 static QString
doContent(QDomElement elem,
int startingPos, ContentEditingData* data)
257 || (!result.isEmpty() && ContentEditingData::CheckLength))
260 bool seenCharacterDataAfterElement=
false;
262 QDomNode n = elem.firstChild();
265 if (n.isCharacterData())
267 seenCharacterDataAfterElement=
true;
269 QDomCharacterData c=n.toCharacterData();
270 QString cData=c.data();
272 if (data && data->pos!=-1 &&
273 data->pos>=startingPos && data->pos<=startingPos+cData.size())
276 int localStartPos=data->pos-startingPos;
279 if (data->actionType==ContentEditingData::DeleteText)
281 if (localStartPos+data->lengthOfStringToRemove>cData.size())
284 int localDelLen=cData.size()-localStartPos;
286 c.deleteData(localStartPos,localDelLen);
288 data->lengthOfStringToRemove=data->lengthOfStringToRemove-localDelLen;
295 c.deleteData(localStartPos,data->lengthOfStringToRemove);
296 data->actionType=ContentEditingData::CheckLength;
302 else if (data->actionType==ContentEditingData::InsertText)
304 c.insertData(localStartPos,data->stringToInsert);
305 data->actionType=ContentEditingData::CheckLength;
312 QString mid=cData.mid(localStartPos);
313 qWarning()<<
"inserting tag"<<tag.
name()<<tag.
id<<tag.
start<<tag.
end<<mid<<data->pos<<startingPos;
315 c.deleteData(localStartPos,mid.size());
316 QDomElement newNode=elem.insertAfter( elem.ownerDocument().createElement(tag.
getElementName()),n).toElement();
317 newNode.setAttribute(
"id",tag.
id);
318 if (!tag.
xid.isEmpty())
319 newNode.setAttribute(
"xid",tag.
xid);
325 int localLen=qMin(len,mid.size());
329 newNode.appendChild( elem.ownerDocument().createTextNode(mid.left(localLen)) );
330 mid=mid.mid(localLen);
334 int missingLen=len-localLen;
337 int childrenCumulativeLen=0;
338 QDomNode sibling=newNode.nextSibling();
339 while(!sibling.isNull())
341 QDomNode tmp=sibling;
342 sibling=sibling.nextSibling();
345 ContentEditingData subData(ContentEditingData::Get);
348 childrenCumulativeLen++;
350 kWarning()<<
"calling sub";
351 QString subContent=
doContent(tmp.toElement(),0,&subData);
352 kWarning()<<
"called sub";
353 childrenCumulativeLen+=subContent.size();
355 else if (tmp.isCharacterData())
356 childrenCumulativeLen+=tmp.toCharacterData().data().size();
360 if (childrenCumulativeLen>missingLen)
362 if (tmp.isCharacterData())
365 const QString& endData=tmp.toCharacterData().data();
366 QString last=endData.left(endData.size()-(childrenCumulativeLen-missingLen));
367 newNode.appendChild( elem.ownerDocument().createTextNode(last));
368 tmp.toCharacterData().deleteData(0,last.size());
373 newNode.appendChild( tmp );
377 if (!newNode.lastChild().isCharacterData())
378 newNode.appendChild( elem.ownerDocument().createTextNode(QString()));
381 elem.insertAfter( elem.ownerDocument().createTextNode(mid),newNode);
382 else if (!newNode.nextSibling().isCharacterData())
383 elem.insertAfter( elem.ownerDocument().createTextNode(QString()),newNode);
385 data->actionType=ContentEditingData::CheckLength;
396 startingPos+=cData.size();
398 else if (n.isElement())
400 QDomElement el=n.toElement();
403 &&data->pos==startingPos)
407 if (data->tags.first().isPaired())
410 ContentEditingData subData(ContentEditingData::Get);
411 QString subContent=
doContent(el,startingPos,&subData);
412 data->tags[0].end=1+startingPos+subContent.size();
416 QDomNode local = n.firstChild();
418 while (!local.isNull())
421 local = local.nextSibling();
425 refNode=elem.insertAfter(tmp,refNode);
432 elem.removeChild(temp);
433 data->actionType=ContentEditingData::CheckLength;
438 if (!seenCharacterDataAfterElement)
439 elem.insertBefore( elem.ownerDocument().createTextNode(QString()),n);
440 seenCharacterDataAfterElement=
false;
444 int oldStartingPos=startingPos;
453 QString recursiveContent=
doContent(el,startingPos,data);
454 if (!recursiveContent.isEmpty())
455 result += recursiveContent; startingPos+=recursiveContent.size();
460 if (data&&data->actionType==ContentEditingData::Get)
462 QString
id=el.attribute(
"id");
464 id=el.attribute(
"mtype");
467 data->tags.append(
InlineTag(oldStartingPos-1,startingPos-1,i,
id,el.attribute(
"xid")));
472 if (!seenCharacterDataAfterElement)
475 elem.appendChild( elem.ownerDocument().createTextNode(QString()));
487 static const QString
names[]={
"source",
"target"};
489 ContentEditingData data(ContentEditingData::Get);
491 catalogString.
tags=data.tags;
511 return nonbin?
content(elem):elem.firstChildElement(
"external-file").attribute(
"href");
527 ContentEditingData data(pos.
offset,count);
533 targetForPos(pos.
entry).firstChildElement(
"external-file").setAttribute(
"href",QString());
539 kWarning()<<pos.
entry<<arg;
540 QDomElement targetEl=targetForPos(pos.
entry);
542 if (targetEl.isNull())
544 QDomNode unitEl=unitForPos(pos.
entry);
545 QDomNode refNode=unitEl.firstChildElement(
"seg-source");
548 targetEl.setAttribute(
"state",
"new");
552 targetEl.appendChild(m_doc.createTextNode(arg));
557 if (arg.isEmpty())
return;
561 QDomElement ef=targetEl.firstChildElement(
"external-file");
563 ef=targetEl.appendChild(m_doc.createElement(
"external-file")).toElement();
564 ef.setAttribute(
"href",arg);
568 ContentEditingData data(pos.
offset,arg);
575 ContentEditingData data(tag.
start,tag);
581 ContentEditingData data(pos.
offset);
583 if (data.tags[0].end==-1) data.tags[0].end=data.tags[0].start;
584 return data.tags.first();
597 QVector<AltTrans> result;
599 QDomElement elem = unitForPos(pos.
entry).firstChildElement(
"alt-trans");
600 while (!elem.isNull())
605 aTrans.
phase=elem.attribute(
"phase-name");
606 aTrans.
origin=elem.attribute(
"origin");
607 aTrans.
score=elem.attribute(
"match-quality").toInt();
608 aTrans.
lang=elem.firstChildElement(
"target").attribute(
"xml:lang");
610 const char*
const types[]={
617 QString typeStr=elem.attribute(
"alttranstype");
619 while (++i<
int(
sizeof(types)/
sizeof(
char*)) && types[i]!=typeStr)
625 elem=elem.nextSiblingElement(
"alt-trans");
630 static QDomElement
phaseElement(QDomDocument m_doc,
const QString& name, QDomElement& phasegroup)
632 QDomElement file=m_doc.elementsByTagName(
"file").at(0).toElement();
633 QDomElement header=file.firstChildElement(
"header");
634 phasegroup=header.firstChildElement(
"phase-group");
635 if (phasegroup.isNull())
637 phasegroup=m_doc.createElement(
"phase-group");
639 QDomElement skl=header.firstChildElement(
"skl");
641 header.insertAfter(phasegroup, skl);
643 header.insertBefore(phasegroup, header.firstChildElement());
645 QDomElement phaseElem=phasegroup.firstChildElement(
"phase");
646 while (!phaseElem.isNull() && phaseElem.attribute(
"phase-name")!=name)
647 phaseElem=phaseElem.nextSiblingElement(
"phase");
655 phase.
name =phaseElem.attribute(
"phase-name");
656 phase.
process =phaseElem.attribute(
"process-name");
657 phase.
company =phaseElem.attribute(
"company-name");
658 phase.
contact =phaseElem.attribute(
"contact-name");
659 phase.
email =phaseElem.attribute(
"contact-email");
660 phase.
phone =phaseElem.attribute(
"contact-phone");
661 phase.
tool =phaseElem.attribute(
"tool-id");
662 phase.
date=QDate::fromString(phaseElem.attribute(
"date"),Qt::ISODate);
668 QDomElement phasegroup;
672 if (phaseElem.isNull()&&!phase.
name.isEmpty())
674 phaseElem=phasegroup.appendChild(m_doc.createElement(
"phase")).toElement();
675 phaseElem.setAttribute(
"phase-name",phase.
name);
678 phaseElem.setAttribute(
"process-name", phase.
process);
679 if (!phase.
company.isEmpty()) phaseElem.setAttribute(
"company-name", phase.
company);
680 phaseElem.setAttribute(
"contact-name", phase.
contact);
681 phaseElem.setAttribute(
"contact-email",phase.
email);
682 if (!phase.
phone.isEmpty()) phaseElem.setAttribute(
"contact-phone",phase.
phone);
683 phaseElem.setAttribute(
"tool-id", phase.
tool);
684 if (phase.
date.isValid()) phaseElem.setAttribute(
"date",phase.
date.toString(Qt::ISODate));
691 QDomElement file=m_doc.elementsByTagName(
"file").at(0).toElement();
692 QDomElement header=file.firstChildElement(
"header");
693 QDomElement phasegroup=header.firstChildElement(
"phase-group");
694 QDomElement phaseElem=phasegroup.firstChildElement(
"phase");
695 while (!phaseElem.isNull())
698 phaseElem=phaseElem.nextSiblingElement(
"phase");
705 QDomElement phasegroup;
706 QDomElement phaseElem=
phaseElement(m_doc,name,phasegroup);
713 QMap<QString,Tool> result;
714 QDomElement file=m_doc.elementsByTagName(
"file").at(0).toElement();
715 QDomElement header=file.firstChildElement(
"header");
716 QDomElement toolElem=header.firstChildElement(
"tool");
717 while (!toolElem.isNull())
720 tool.
tool =toolElem.attribute(
"tool-id");
721 tool.
name =toolElem.attribute(
"tool-name");
722 tool.
version =toolElem.attribute(
"tool-version");
723 tool.
company =toolElem.attribute(
"tool-company");
725 result.insert(tool.
tool, tool);
726 toolElem=toolElem.nextSiblingElement(
"tool");
735 QDomElement elem = unitForPos(pos.
entry).firstChildElement(
"context-group");
736 while (!elem.isNull())
738 if (elem.attribute(
"purpose").contains(
"location"))
740 QDomElement
context = elem.firstChildElement(
"context");
741 while (!context.isNull())
745 if (context.attribute(
"context-type")==
"sourcefile")
746 sourcefile=context.text();
747 else if (context.attribute(
"context-type")==
"linenumber")
748 linenumber=context.text();
749 if (!( sourcefile.isEmpty()&&linenumber.isEmpty() ))
750 result.append(sourcefile+
':'+linenumber);
752 context=context.nextSiblingElement(
"context");
756 elem=elem.nextSiblingElement(
"context-group");
766 note.
from=elem.attribute(
"from");
767 note.
lang=elem.attribute(
"xml:lang");
768 if (elem.attribute(
"annotates")==
"source")
770 else if (elem.attribute(
"annotates")==
"target")
773 note.
priority=elem.attribute(
"priority").toInt(&ok);
781 QDomElement elem = entries.at(m_map.at(pos.
entry)).firstChildElement(
"note");
782 while (!elem.isNull())
787 elem=elem.nextSiblingElement(
"note");
790 return result.toVector();
797 return QVector<Note>();
803 QDomElement unit=unitForPos(pos.
entry);
808 QDomElement ref=unit.lastChildElement(
"note");
809 elem=unit.insertAfter( m_doc.createElement(
"note"),ref).toElement();
810 elem.appendChild(m_doc.createTextNode(QString()));
814 QDomNodeList list=unit.elementsByTagName(
"note");
815 if (pos.
form==-1) pos.
form=list.size()-1;
816 if (pos.
form<list.size())
818 elem = unit.elementsByTagName(
"note").at(pos.
form).toElement();
823 if (elem.isNull())
return oldNote;
825 if (!elem.text().isEmpty())
827 ContentEditingData data(0,elem.text().size());
834 if (!note.
from.isEmpty()) elem.setAttribute(
"from",note.
from);
838 unit.removeChild(elem);
845 QSet<QString> result;
846 QDomNodeList
notes=m_doc.elementsByTagName(
"note");
850 QString from=notes.at(i).toElement().attribute(
"from");
854 return result.toList();
857 QVector<Note>
phaseNotes(QDomDocument m_doc,
const QString& phasename,
bool remove=
false)
859 QVector<Note> result;
861 QDomElement phasegroup;
862 QDomElement phaseElem=
phaseElement(m_doc,phasename,phasegroup);
864 QDomElement noteElem=phaseElem.firstChildElement(
"note");
865 while (!noteElem.isNull())
870 QDomElement old=noteElem;
871 noteElem=noteElem.nextSiblingElement(
"note");
872 if (
remove) phaseElem.removeChild(old);
884 QVector<Note> result=
::phaseNotes(m_doc, phasename,
true);
886 QDomElement phasegroup;
887 QDomElement phaseElem=
phaseElement(m_doc,phasename,phasegroup);
889 foreach(
const Note& note, notes)
891 QDomElement elem=phaseElem.appendChild(m_doc.createElement(
"note")).toElement();
892 elem.appendChild(m_doc.createTextNode(note.
content));
893 if (!note.
from.isEmpty()) elem.setAttribute(
"from",note.
from);
906 QString result=target.attribute(
"phase-name");
908 target.removeAttribute(
"phase-name");
910 target.setAttribute(
"phase-name",phase);
918 return target.attribute(
"phase-name");
925 return QStringList(QString());
931 return QStringList();
936 return unitForPos(pos.
entry).attribute(
"id");
941 return m_plurals.contains(pos.
entry);
956 "new",
"needs-translation",
"needs-l10n",
"needs-adaptation",
"translated",
957 "needs-review-translation",
"needs-review-l10n",
"needs-review-adaptation",
"final",
963 int i=
sizeof(
states)/
sizeof(
char*);
964 while (--i>0 && state!=states[i])
974 target.setAttribute(
"state",states[state]);
981 if (!target.hasAttribute(
"state") && unitForPos(pos.
entry).attribute(
"approved")==
"yes")
988 ContentEditingData data(ContentEditingData::CheckLength);
989 return content(targetForPos(pos.
entry),&data).isEmpty();
994 return targetForPos(pos.
entry).attribute(
"equiv-trans")!=
"no";
999 targetForPos(pos.
entry).setAttribute(
"equiv-trans",
noyes[equivTrans]);
1002 QDomElement XliffStorage::unitForPos(
int pos)
const
1005 return entries.at(m_map.at(pos)).toElement();
1007 return binEntries.at(pos-
size()).toElement();
1010 QDomElement XliffStorage::targetForPos(
int pos)
const
1015 QDomElement XliffStorage::sourceForPos(
int pos)
const
1022 return binEntries.size();
1027 return m_unitsById.contains(
id)?m_unitsById.value(
id):-1;
CatalogString catalogString(const DocPosition &pos) const
static QString doContent(QDomElement elem, int startingPos, ContentEditingData *data)
static bool isPaired(InlineElement type)
#define TAGRANGE_IMAGE_SYMBOL
QVector< Note > phaseNotes(const QString &phase) const
int m_numberOfPluralForms
InlineTag targetDeleteTag(const DocPosition &)
QStringList matchData(const DocPosition &pos) const
user-invisible data for matching, e.g.
QString setPhase(const DocPosition &pos, const QString &phase)
void targetInsertTag(const DocPosition &, const InlineTag &)
static InlineElement getElementType(const QByteArray &)
Phase updatePhase(const Phase &phase)
This struct represents a position in a catalog.
QVector< Note > phaseNotes(QDomDocument m_doc, const QString &phasename, bool remove=false)
TargetState state(const DocPosition &pos) const
static void initNoteFromElement(Note ¬e, QDomElement elem)
static const QString bintargettarget[]
QMap< QString, Tool > allTools() const
void targetInsert(const DocPosition &pos, const QString &arg)
QStringList context(const DocPosition &pos) const
void setTarget(const DocPosition &pos, const QString &arg)
bool isPlural(const DocPosition &pos) const
static Phase phaseFromElement(QDomElement phaseElem)
static const char *const states[]
void setEquivTrans(const DocPosition &pos, bool equivTrans)
static QDomElement phaseElement(QDomDocument m_doc, const QString &name, QDomElement &phasegroup)
const char * name() const
Abstract interface for storage of translation file.
static const QString names[]
QList< Phase > allPhases() const
QStringList noteAuthors() const
static const QString binsourcesource[]
QStringList sourceFiles(const DocPosition &pos) const
static QString content(QDomElement elem, ContentEditingData *data=0)
walks through XLIFF XML and performs actions depending on ContentEditingData:
Phase phase(const QString &name) const
static const QString noyes[]
QString source(const DocPosition &pos) const
flat-model interface (ignores XLIFF grouping)
void targetDelete(const DocPosition &pos, int count)
edit operations used by undo/redo system and sync-mode
static QString genericContent(QDomElement elem, bool nonbin)
QVector< AltTrans > altTrans(const DocPosition &pos) const
data structure used to pass info about inline elements a XLIFF tag is represented by a TAGRANGE_IMAGE...
CatalogString sourceWithTags(DocPosition pos) const
int load(QIODevice *device)
bool save(QIODevice *device, bool belongsToProject=false)
QVector< Note > notes(const DocPosition &pos) const
static TargetState stringToState(const QString &state)
CatalogString targetWithTags(DocPosition pos) const
QString id(const DocPosition &pos) const
entry id unique for this file
TargetState setState(const DocPosition &pos, TargetState state)
Note setNote(DocPosition pos, const Note ¬e)
pos.form is note number
int unitById(const QString &id) const
bool isEquivTrans(const DocPosition &pos) const
QVector< Note > developerNotes(const DocPosition &pos) const
static const char * getElementName(InlineElement type)
QVector< Note > setPhaseNotes(const QString &phase, QVector< Note > notes)
QString target(const DocPosition &pos) const
int binUnitsCount() const
data structure used to pass info about inline elements a XLIFF tag is represented by a TAGRANGE_IMAGE...