File indexing completed on 2024-04-21 03:48:21

0001 /*
0002  * export a KEduVocDocument to a KVTML file
0003  * SPDX-FileCopyrightText: 2007 Jeremy Whiting <jpwhiting@kde.org>
0004  * SPDX-FileCopyrightText: 2007-2010 Frederik Gladhorn <gladhorn@kde.org>
0005  * SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 #include "keduvockvtml2writer.h"
0008 
0009 #include <QTextStream>
0010 
0011 #include <QDebug>
0012 #include <QDir>
0013 
0014 #include "keduvocdocument.h"
0015 #include "keduvocexpression.h"
0016 #include "keduvocleitnerbox.h"
0017 #include "keduvoclesson.h"
0018 #include "keduvocwordtype.h"
0019 #include "kvtml2defs.h"
0020 #include <kio/global.h>
0021 
0022 KEduVocKvtml2Writer::KEduVocKvtml2Writer(QFile *file)
0023 {
0024     // the file must be already open
0025     m_outputFile = file;
0026 }
0027 
0028 bool KEduVocKvtml2Writer::writeDoc(KEduVocDocument *doc, const QString &generator)
0029 {
0030     if (createXmlDocument(doc, generator)) {
0031         QTextStream ts(m_outputFile);
0032         m_domDoc.save(ts, 2);
0033         return true;
0034     }
0035     return false;
0036 }
0037 
0038 QByteArray KEduVocKvtml2Writer::toByteArray(KEduVocDocument *doc, const QString &generator)
0039 {
0040     if (createXmlDocument(doc, generator)) {
0041         return m_domDoc.toByteArray();
0042     }
0043     return QByteArray();
0044 }
0045 
0046 bool KEduVocKvtml2Writer::createXmlDocument(KEduVocDocument *doc, const QString &generator)
0047 {
0048     m_doc = doc;
0049 
0050     m_domDoc = QDomDocument(QStringLiteral("kvtml PUBLIC \"kvtml2.dtd\" \"http://edu.kde.org/kvtml/kvtml2.dtd\""));
0051     m_domDoc.appendChild(m_domDoc.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\"")));
0052     QDomElement domElementKvtml = m_domDoc.createElement(QStringLiteral("kvtml"));
0053     m_domDoc.appendChild(domElementKvtml);
0054 
0055     domElementKvtml.setAttribute(KVTML_VERSION, (QString)QStringLiteral("2.0"));
0056 
0057     // information group
0058     QDomElement currentElement = m_domDoc.createElement(KVTML_INFORMATION);
0059     writeInformation(currentElement, generator);
0060     domElementKvtml.appendChild(currentElement);
0061 
0062     // identifiers
0063     currentElement = m_domDoc.createElement(KVTML_IDENTIFIERS);
0064     writeIdentifiers(currentElement);
0065     domElementKvtml.appendChild(currentElement);
0066 
0067     // entries
0068     currentElement = m_domDoc.createElement(KVTML_ENTRIES);
0069     if (!writeEntries(currentElement)) {
0070         // at least one entry is required!
0071         return false;
0072     }
0073     domElementKvtml.appendChild(currentElement);
0074 
0075     // lessons
0076     currentElement = m_domDoc.createElement(KVTML_LESSONS);
0077     writeLessons(m_doc->lesson(), currentElement);
0078     if (currentElement.hasChildNodes()) {
0079         domElementKvtml.appendChild(currentElement);
0080     }
0081 
0082     // types
0083     currentElement = m_domDoc.createElement(KVTML_WORDTYPES);
0084     writeWordTypes(currentElement, m_doc->wordTypeContainer());
0085     if (currentElement.hasChildNodes()) {
0086         domElementKvtml.appendChild(currentElement);
0087     }
0088 
0089     // leitner boxes
0090     currentElement = m_domDoc.createElement(KVTML_LEITNERBOXES);
0091     writeLeitnerBoxes(currentElement, m_doc->leitnerContainer());
0092     if (currentElement.hasChildNodes()) {
0093         domElementKvtml.appendChild(currentElement);
0094     }
0095 
0096     writeSynonymAntonymFalseFriend(domElementKvtml);
0097 
0098     m_domDoc.appendChild(domElementKvtml);
0099 
0100     return true;
0101 }
0102 
0103 bool KEduVocKvtml2Writer::writeInformation(QDomElement &informationElement, const QString &generator)
0104 {
0105     QDomElement currentElement;
0106     QDomText textNode;
0107 
0108     // generator
0109     informationElement.appendChild(newTextElement(KVTML_GENERATOR, generator));
0110 
0111     // title
0112     if (!m_doc->title().isEmpty()) {
0113         informationElement.appendChild(newTextElement(KVTML_TITLE, m_doc->title()));
0114     }
0115 
0116     // author
0117     if (!m_doc->author().isEmpty()) {
0118         informationElement.appendChild(newTextElement(KVTML_AUTHOR, m_doc->author()));
0119     }
0120 
0121     // author contact (mail/homepage)
0122     if (!m_doc->authorContact().isEmpty()) {
0123         informationElement.appendChild(newTextElement(KVTML_AUTHORCONTACT, m_doc->authorContact()));
0124     }
0125 
0126     // license
0127     if (!m_doc->license().isEmpty()) {
0128         informationElement.appendChild(newTextElement(KVTML_LICENSE, m_doc->license()));
0129     }
0130 
0131     // comment
0132     if (!m_doc->documentComment().isEmpty()) {
0133         informationElement.appendChild(newTextElement(KVTML_COMMENT, m_doc->documentComment()));
0134     }
0135 
0136     QDate today = QDate::currentDate();
0137     informationElement.appendChild(newTextElement(KVTML_DATE, today.toString(QStringLiteral("yyyy-MM-dd"))));
0138 
0139     // category
0140     if (!m_doc->category().isEmpty()) {
0141         informationElement.appendChild(newTextElement(KVTML_CATEGORY, m_doc->category()));
0142     }
0143 
0144     return true;
0145 }
0146 
0147 bool KEduVocKvtml2Writer::writeIdentifiers(QDomElement &identifiersElement)
0148 {
0149     for (int i = 0; i < m_doc->identifierCount(); ++i) {
0150         // create the node
0151         QDomElement identifier = m_domDoc.createElement(KVTML_IDENTIFIER);
0152 
0153         // set the id
0154         identifier.setAttribute(KVTML_ID, QString::number(i));
0155 
0156         // record the identifier as the locale for now
0157         // TODO: when support for more parts of the identifier is in the document class (name, type, etc.) store those here as well
0158         identifier.appendChild(newTextElement(KVTML_NAME, m_doc->identifier(i).name()));
0159 
0160         identifier.appendChild(newTextElement(KVTML_LOCALE, m_doc->identifier(i).locale()));
0161 
0162         // record articles
0163         QDomElement article = m_domDoc.createElement(KVTML_ARTICLE);
0164         writeArticle(article, i);
0165         if (article.hasChildNodes()) {
0166             identifier.appendChild(article);
0167         }
0168 
0169         // record personalpronouns
0170         QDomElement personalpronouns = m_domDoc.createElement(KVTML_PERSONALPRONOUNS);
0171         writePersonalPronoun(personalpronouns, m_doc->identifier(i).personalPronouns());
0172         if (personalpronouns.hasChildNodes()) {
0173             identifier.appendChild(personalpronouns);
0174         }
0175 
0176         // tenses
0177         foreach (const QString &tense, m_doc->identifier(i).tenseList()) {
0178             if (!(tense.isNull())) {
0179                 identifier.appendChild(newTextElement(KVTML_TENSE, tense));
0180             }
0181         }
0182         // add this identifier to the group
0183         identifiersElement.appendChild(identifier);
0184     }
0185     return true;
0186 }
0187 
0188 bool KEduVocKvtml2Writer::writeLessons(KEduVocLesson *parentLesson, QDomElement &lessonsElement)
0189 {
0190     // iterate over child lessons.
0191     // the first time this is called with the root lesson which does not have a <lesson> entry.
0192     for (int i = 0; i < parentLesson->childContainerCount(); i++) {
0193         KEduVocLesson *lesson = static_cast<KEduVocLesson *>(parentLesson->childContainer(i));
0194         // make lesson element
0195         QDomElement thisLessonElement = m_domDoc.createElement(KVTML_CONTAINER);
0196 
0197         // add a name
0198         thisLessonElement.appendChild(newTextElement(KVTML_NAME, lesson->name()));
0199 
0200         // add a inquery tag
0201         if (lesson->inPractice()) {
0202             thisLessonElement.appendChild(newTextElement(KVTML_INPRACTICE, KVTML_TRUE));
0203         }
0204 
0205         // child lessons
0206         writeLessons(lesson, thisLessonElement);
0207 
0208         // child entries
0209         foreach (KEduVocExpression *entry, lesson->entries()) {
0210             QDomElement entryElement = m_domDoc.createElement(KVTML_ENTRY);
0211             entryElement.setAttribute(KVTML_ID, QString::number(m_allEntries.indexOf(entry)));
0212             thisLessonElement.appendChild(entryElement);
0213         }
0214         lessonsElement.appendChild(thisLessonElement);
0215     }
0216     return true;
0217 }
0218 
0219 void KEduVocKvtml2Writer::writeSynonymAntonymFalseFriend(QDomElement &parentElement)
0220 {
0221     QList<KEduVocTranslation *> currentList;
0222     QDomElement synonymElement;
0223     // synonym, antonym, false friend
0224     for (int type = KEduVocTranslation::Synonym; type <= KEduVocTranslation::FalseFriend; type++) {
0225         switch (type) {
0226         case KEduVocTranslation::Synonym:
0227             synonymElement = m_domDoc.createElement(KVTML_SYNONYM);
0228             currentList = m_synonyms;
0229             break;
0230         case KEduVocTranslation::Antonym:
0231             synonymElement = m_domDoc.createElement(KVTML_ANTONYM);
0232             currentList = m_antonyms;
0233             break;
0234         case KEduVocTranslation::FalseFriend:
0235             synonymElement = m_domDoc.createElement(KVTML_FALSEFRIEND);
0236             currentList = m_falseFriends;
0237             break;
0238         }
0239 
0240         while (!currentList.isEmpty()) {
0241             // after writing a translation, remove it from the list
0242             KEduVocTranslation *translation = currentList.takeFirst();
0243 
0244             QDomElement relatedElement;
0245             QList<KEduVocTranslation *> list;
0246             switch (type) {
0247             case KEduVocTranslation::Synonym:
0248                 list = translation->synonyms();
0249                 break;
0250             case KEduVocTranslation::Antonym:
0251                 list = translation->antonyms();
0252                 break;
0253             case KEduVocTranslation::FalseFriend:
0254                 list = translation->falseFriends();
0255                 break;
0256             }
0257             foreach (KEduVocTranslation *synonym, list) {
0258                 // if it is not in the list it has already been written and we can move on
0259                 if (currentList.contains(synonym)) {
0260                     relatedElement = m_domDoc.createElement(KVTML_PAIR);
0261 
0262                     // fill the entry element but only add later if it is valid
0263                     QDomElement entryElement = m_domDoc.createElement(KVTML_ENTRY);
0264                     entryElement.setAttribute(KVTML_ID, QString::number(m_allEntries.indexOf(translation->entry())));
0265                     // find out which id that is... silly
0266                     foreach (int index, translation->entry()->translationIndices()) {
0267                         if (translation->entry()->translation(index) == translation) {
0268                             // create <translation id="123">
0269                             QDomElement translationElement = m_domDoc.createElement(KVTML_TRANSLATION);
0270                             translationElement.setAttribute(KVTML_ID, QString::number(index));
0271                             entryElement.appendChild(translationElement);
0272                             break;
0273                         }
0274                     }
0275 
0276                     relatedElement.appendChild(entryElement);
0277 
0278                     QDomElement partnerElement = m_domDoc.createElement(KVTML_ENTRY);
0279                     partnerElement.setAttribute(KVTML_ID, QString::number(m_allEntries.indexOf(synonym->entry())));
0280                     // find out which id that is
0281                     foreach (int index, synonym->entry()->translationIndices()) {
0282                         if (synonym->entry()->translation(index) == synonym) {
0283                             // create <translation id="123">
0284                             QDomElement translationElement = m_domDoc.createElement(KVTML_TRANSLATION);
0285                             translationElement.setAttribute(KVTML_ID, QString::number(index));
0286                             partnerElement.appendChild(translationElement);
0287                             break;
0288                         }
0289                     }
0290                     relatedElement.appendChild(partnerElement);
0291                     synonymElement.appendChild(relatedElement);
0292                 }
0293             }
0294         }
0295         if (synonymElement.hasChildNodes()) {
0296             parentElement.appendChild(synonymElement);
0297         }
0298     } // iterate over types
0299 }
0300 /*
0301 bool KEduVocKvtml2Writer::writeRelated(QDomElement & parentElement, QList< KEduVocTranslation * > relatedList)
0302 {
0303     foreach (KEduVocTranslation* synonym, translation->synonyms()) {
0304         QDomElement entryElement = m_domDoc.createElement( KVTML_ENTRY );
0305         entryElement.setAttribute( KVTML_ID, QString::number(m_allEntries.indexOf(translation->entry())) );
0306 
0307         // find out which id that is... silly
0308         foreach(int index, translation->entry()->translationIndices()) {
0309             if (translation->entry()->translation(index) == translation) {
0310                 // create <translation id="123">
0311                 QDomElement translationElement = m_domDoc.createElement( KVTML_TRANSLATION );
0312                 translationElement.setAttribute( KVTML_ID, QString::number(index) );
0313                 entryElement.appendChild(translationElement);
0314             }
0315         }
0316         parentElement.appendChild( entryElement );
0317     }
0318 }*/
0319 
0320 bool KEduVocKvtml2Writer::writeArticle(QDomElement &articleElement, int language)
0321 {
0322     ///@todo only write if not empty
0323     QMap<int, KEduVocWordFlag::Flags> numbers;
0324     numbers[0] = KEduVocWordFlag::Singular;
0325     numbers[1] = KEduVocWordFlag::Dual;
0326     numbers[2] = KEduVocWordFlag::Plural;
0327     QMap<int, KEduVocWordFlag::Flags> genders;
0328     genders[0] = KEduVocWordFlag::Masculine;
0329     genders[1] = KEduVocWordFlag::Feminine;
0330     genders[2] = KEduVocWordFlag::Neuter;
0331     QMap<int, KEduVocWordFlag::Flags> defs;
0332     defs[0] = KEduVocWordFlag::Definite;
0333     defs[1] = KEduVocWordFlag::Indefinite;
0334 
0335     for (int num = 0; num <= 2; num++) {
0336         QDomElement numberElement = m_domDoc.createElement(KVTML_GRAMMATICAL_NUMBER[num]);
0337 
0338         for (int def = 0; def <= 1; def++) {
0339             QDomElement defElement = m_domDoc.createElement(KVTML_GRAMMATICAL_DEFINITENESS[def]);
0340 
0341             for (int gen = 0; gen <= 2; gen++) {
0342                 QString articleString = m_doc->identifier(language).article().article(numbers[num] | genders[gen] | defs[def]);
0343                 if (!articleString.isEmpty()) {
0344                     defElement.appendChild(newTextElement(KVTML_GRAMMATICAL_GENDER[gen], articleString));
0345                 }
0346             }
0347             if (defElement.hasChildNodes()) {
0348                 numberElement.appendChild(defElement);
0349             }
0350         }
0351         if (numberElement.hasChildNodes()) {
0352             articleElement.appendChild(numberElement);
0353         }
0354     }
0355     return true;
0356 }
0357 
0358 bool KEduVocKvtml2Writer::writeWordTypes(QDomElement &typesElement, KEduVocWordType *parentContainer)
0359 {
0360     foreach (KEduVocContainer *container, parentContainer->childContainers()) {
0361         KEduVocWordType *wordType = static_cast<KEduVocWordType *>(container);
0362 
0363         QDomElement typeDefinitionElement = m_domDoc.createElement(KVTML_CONTAINER);
0364         typeDefinitionElement.appendChild(newTextElement(KVTML_NAME, wordType->name()));
0365 
0366         if (wordType->wordType().testFlag(KEduVocWordFlag::Noun)) {
0367             if (wordType->wordType().testFlag(KEduVocWordFlag::Masculine))
0368                 typeDefinitionElement.appendChild(newTextElement(KVTML_SPECIALWORDTYPE, KVTML_SPECIALWORDTYPE_NOUN_MALE));
0369 
0370             else if (wordType->wordType().testFlag(KEduVocWordFlag::Feminine))
0371                 typeDefinitionElement.appendChild(newTextElement(KVTML_SPECIALWORDTYPE, KVTML_SPECIALWORDTYPE_NOUN_FEMALE));
0372 
0373             else if (wordType->wordType().testFlag(KEduVocWordFlag::Neuter))
0374                 typeDefinitionElement.appendChild(newTextElement(KVTML_SPECIALWORDTYPE, KVTML_SPECIALWORDTYPE_NOUN_NEUTRAL));
0375             else
0376                 typeDefinitionElement.appendChild(newTextElement(KVTML_SPECIALWORDTYPE, KVTML_SPECIALWORDTYPE_NOUN));
0377         } else if (wordType->wordType().testFlag(KEduVocWordFlag::Verb))
0378             typeDefinitionElement.appendChild(newTextElement(KVTML_SPECIALWORDTYPE, KVTML_SPECIALWORDTYPE_VERB));
0379 
0380         else if (wordType->wordType().testFlag(KEduVocWordFlag::Adjective))
0381             typeDefinitionElement.appendChild(newTextElement(KVTML_SPECIALWORDTYPE, KVTML_SPECIALWORDTYPE_ADJECTIVE));
0382 
0383         else if (wordType->wordType().testFlag(KEduVocWordFlag::Adverb))
0384             typeDefinitionElement.appendChild(newTextElement(KVTML_SPECIALWORDTYPE, KVTML_SPECIALWORDTYPE_ADVERB));
0385 
0386         else if (wordType->wordType().testFlag(KEduVocWordFlag::Conjunction))
0387             typeDefinitionElement.appendChild(newTextElement(KVTML_SPECIALWORDTYPE, KVTML_SPECIALWORDTYPE_CONJUNCTION));
0388 
0389         // child entries
0390 
0391         // child entries
0392         foreach (KEduVocExpression *entry, wordType->entries()) {
0393             QDomElement entryElement = m_domDoc.createElement(KVTML_ENTRY);
0394             entryElement.setAttribute(KVTML_ID, QString::number(m_allEntries.indexOf(entry)));
0395             for (int translation = 0; translation < m_doc->identifierCount(); translation++) {
0396                 if (entry->translation(translation)->wordType() == wordType) {
0397                     QDomElement translationElement = m_domDoc.createElement(KVTML_TRANSLATION);
0398                     // create <translation id="123">
0399                     translationElement.setAttribute(KVTML_ID, QString::number(translation));
0400                     // append both
0401                     entryElement.appendChild(translationElement);
0402                 }
0403             }
0404             typeDefinitionElement.appendChild(entryElement);
0405         }
0406 
0407         writeWordTypes(typeDefinitionElement, wordType);
0408 
0409         typesElement.appendChild(typeDefinitionElement);
0410     }
0411     return true;
0412 }
0413 
0414 bool KEduVocKvtml2Writer::writeLeitnerBoxes(QDomElement &leitnerParentElement, KEduVocLeitnerBox *parentContainer)
0415 {
0416     foreach (KEduVocContainer *container, parentContainer->childContainers()) {
0417         KEduVocLeitnerBox *leitnerBox = static_cast<KEduVocLeitnerBox *>(container);
0418 
0419         QDomElement containerElement = m_domDoc.createElement(KVTML_CONTAINER);
0420         containerElement.appendChild(newTextElement(KVTML_NAME, leitnerBox->name()));
0421 
0422         // child entries
0423         foreach (KEduVocExpression *entry, leitnerBox->entries()) {
0424             QDomElement entryElement = m_domDoc.createElement(KVTML_ENTRY);
0425             entryElement.setAttribute(KVTML_ID, QString::number(m_allEntries.indexOf(entry)));
0426             for (int translation = 0; translation < m_doc->identifierCount(); translation++) {
0427                 if (entry->translation(translation)->leitnerBox() == leitnerBox) {
0428                     QDomElement translationElement = m_domDoc.createElement(KVTML_TRANSLATION);
0429                     // create <translation id="123">
0430                     translationElement.setAttribute(KVTML_ID, QString::number(translation));
0431                     // append both
0432                     entryElement.appendChild(translationElement);
0433                 }
0434             }
0435             containerElement.appendChild(entryElement);
0436         }
0437 
0438         leitnerParentElement.appendChild(containerElement);
0439     }
0440     return true;
0441 }
0442 
0443 bool KEduVocKvtml2Writer::writeEntries(QDomElement &entriesElement)
0444 {
0445     m_allEntries = m_doc->lesson()->entries(KEduVocLesson::Recursive);
0446 
0447     // loop through entries
0448     for (int i = 0; i < m_allEntries.count(); ++i) {
0449         KEduVocExpression *thisEntry = m_allEntries.value(i);
0450 
0451         // write entry tag
0452         QDomElement entryElement = m_domDoc.createElement(KVTML_ENTRY);
0453 
0454         // add id
0455         entryElement.setAttribute(KVTML_ID, QString::number(i));
0456 
0457         // write deactivated
0458         if (!thisEntry->isActive()) {
0459             entryElement.appendChild(newTextElement(KVTML_DEACTIVATED, KVTML_TRUE));
0460         }
0461 
0462         // loop through translations
0463         foreach (int trans, thisEntry->translationIndices()) {
0464             // write translations
0465             QDomElement translation = m_domDoc.createElement(KVTML_TRANSLATION);
0466             translation.setAttribute(KVTML_ID, QString::number(trans));
0467             writeTranslation(translation, thisEntry->translation(trans));
0468             entryElement.appendChild(translation);
0469         }
0470         // add this entry to the entriesElement
0471         entriesElement.appendChild(entryElement);
0472     }
0473     return true;
0474 }
0475 
0476 bool KEduVocKvtml2Writer::writeTranslation(QDomElement &translationElement, KEduVocTranslation *translation)
0477 {
0478     // so far only for KEduVocWord - text and grades
0479     translation->toKVTML2(translationElement);
0480 
0481     // comparison
0482     if (!(translation->comparativeForm().text().isEmpty() || translation->superlativeForm().text().isEmpty())) {
0483         qDebug() << "Write comp";
0484         QDomElement comparisonElement = m_domDoc.createElement(KVTML_COMPARISON);
0485         translationElement.appendChild(comparisonElement);
0486 
0487         QDomElement comparativeElement = m_domDoc.createElement(KVTML_COMPARATIVE);
0488         comparisonElement.appendChild(comparativeElement);
0489         translation->comparativeForm().toKVTML2(comparativeElement);
0490 
0491         QDomElement superlativeElement = m_domDoc.createElement(KVTML_SUPERLATIVE);
0492         comparisonElement.appendChild(superlativeElement);
0493         translation->superlativeForm().toKVTML2(superlativeElement);
0494     }
0495 
0496     if (translation->article().practiceCount() != 0) {
0497         QDomElement articleElement = m_domDoc.createElement(KVTML_ARTICLE);
0498         translation->article().toKVTML2(articleElement);
0499         translationElement.appendChild(articleElement);
0500     }
0501 
0502     // multiplechoice
0503     if (!translation->getMultipleChoice().isEmpty()) {
0504         QDomElement multipleChoiceElement = m_domDoc.createElement(KVTML_MULTIPLECHOICE);
0505         writeMultipleChoice(multipleChoiceElement, translation);
0506         translationElement.appendChild(multipleChoiceElement);
0507     }
0508 
0509     // image
0510     if (!translation->imageUrl().isEmpty()) {
0511         QString urlString;
0512         const QUrl docDirUrl = m_doc->url().adjusted(QUrl::RemoveFilename);
0513         if (docDirUrl.isParentOf(translation->imageUrl())) {
0514             // try to save as relative url
0515             const QDir dir(docDirUrl.toLocalFile());
0516             urlString = QUrl::fromLocalFile(dir.relativeFilePath(translation->imageUrl().toLocalFile())).url();
0517         } else {
0518             urlString = translation->imageUrl().url();
0519         }
0520         translationElement.appendChild(newTextElement(KVTML_IMAGE, urlString));
0521     }
0522 
0523     // sound
0524     if (!translation->soundUrl().isEmpty()) {
0525         QString urlString;
0526         const QUrl docDirUrl = m_doc->url().adjusted(QUrl::RemoveFilename);
0527         if (docDirUrl.isParentOf(translation->soundUrl())) {
0528             // try to save as relative url
0529             const QDir dir(docDirUrl.toLocalFile());
0530             urlString = QUrl::fromLocalFile(dir.relativeFilePath(translation->soundUrl().toLocalFile())).url();
0531         } else {
0532             urlString = translation->soundUrl().url();
0533         }
0534         translationElement.appendChild(newTextElement(KVTML_SOUND, urlString));
0535     }
0536 
0537     // synonym, antonym, false friend
0538     // add to the list if it has any, write later since we want them separate
0539     if (!translation->synonyms().isEmpty()) {
0540         m_synonyms.append(translation);
0541     }
0542     if (!translation->antonyms().isEmpty()) {
0543         m_antonyms.append(translation);
0544     }
0545     if (!translation->falseFriends().isEmpty()) {
0546         m_falseFriends.append(translation);
0547     }
0548     return true;
0549 }
0550 
0551 ///@todo write false friends
0552 // <falsefriend fromid="0"></falsefriend>
0553 // loop through the identifiers
0554 //     for ( int i = 0; i < m_doc->identifierCount(); ++i ) {
0555 //         // see if this identifier has a falsefriend in this translation
0556 //         QString thisFriend = translation->falseFriend( i );
0557 //         if ( !thisFriend.isEmpty() ) {
0558 //             // if so, create it, and set the fromid to i
0559 //             QDomElement thisFriendElement = newTextElement( KVTML_FALSEFRIEND, thisFriend );
0560 //             thisFriendElement.setAttribute( KVTML_FROMID, QString::number( i ) );
0561 //             translationElement.appendChild( thisFriendElement );
0562 //         }
0563 //     }
0564 
0565 bool KEduVocKvtml2Writer::writeMultipleChoice(QDomElement &multipleChoiceElement, KEduVocTranslation *translation)
0566 /*
0567  <multiplechoice>
0568    <choice>good</choice>
0569    <choice>better</choice>
0570    <choice>best</choice>
0571    <choice>best 2</choice>
0572    <choice>best 3</choice>
0573  </multiplechoice>
0574 */
0575 {
0576     foreach (const QString &choice, translation->getMultipleChoice()) {
0577         multipleChoiceElement.appendChild(newTextElement(KVTML_CHOICE, choice));
0578     }
0579     return true;
0580 }
0581 
0582 QDomElement KEduVocKvtml2Writer::newTextElement(const QString &elementName, const QString &text)
0583 {
0584     QDomElement retval = m_domDoc.createElement(elementName);
0585     QDomText textNode = m_domDoc.createTextNode(text);
0586     retval.appendChild(textNode);
0587     return retval;
0588 }
0589 
0590 bool KEduVocKvtml2Writer::writePersonalPronoun(QDomElement &pronounElement, const KEduVocPersonalPronoun &pronoun)
0591 {
0592     // general pronoun properties
0593     if (pronoun.maleFemaleDifferent()) {
0594         pronounElement.appendChild(m_domDoc.createElement(KVTML_THIRD_PERSON_MALE_FEMALE_DIFFERENT));
0595     }
0596     if (pronoun.neutralExists()) {
0597         pronounElement.appendChild(m_domDoc.createElement(KVTML_THIRD_PERSON_NEUTRAL_EXISTS));
0598     }
0599     if (pronoun.dualExists()) {
0600         pronounElement.appendChild(m_domDoc.createElement(KVTML_DUAL_EXISTS));
0601     }
0602 
0603     QMap<int, KEduVocWordFlag::Flags> numbers;
0604     numbers[0] = KEduVocWordFlag::Singular;
0605     numbers[1] = KEduVocWordFlag::Dual;
0606     numbers[2] = KEduVocWordFlag::Plural;
0607     QMap<int, KEduVocWordFlag::Flags> persons;
0608     persons[0] = KEduVocWordFlag::First;
0609     persons[1] = KEduVocWordFlag::Second;
0610     persons[2] = (KEduVocWordFlag::Flags)((int)KEduVocWordFlag::Third | (int)KEduVocWordFlag::Masculine);
0611     persons[3] = (KEduVocWordFlag::Flags)((int)KEduVocWordFlag::Third | (int)KEduVocWordFlag::Feminine);
0612     persons[4] = (KEduVocWordFlag::Flags)((int)KEduVocWordFlag::Third | (int)KEduVocWordFlag::Neuter);
0613 
0614     // the actual pronouns
0615     for (int num = 0; num < 3; num++) {
0616         QDomElement numberElement = m_domDoc.createElement(KVTML_GRAMMATICAL_NUMBER[num]);
0617         for (int person = 0; person < 5; person++) {
0618             QString pronounString = pronoun.personalPronoun(numbers[num] | persons[person]);
0619             if (!pronounString.isEmpty()) {
0620                 numberElement.appendChild(newTextElement(KVTML_GRAMMATICAL_PERSON[person], pronounString));
0621             }
0622         }
0623         if (numberElement.hasChildNodes()) {
0624             pronounElement.appendChild(numberElement);
0625         }
0626     }
0627     return true;
0628 }
0629 
0630 void KEduVocKvtml2Writer::appendTextElement(QDomElement &parent, const QString &elementName, const QString &text)
0631 {
0632     // empty will never be written
0633     if (text.isEmpty()) {
0634         return;
0635     }
0636 
0637     QDomDocument domDoc = parent.ownerDocument();
0638     QDomElement element = domDoc.createElement(elementName);
0639     parent.appendChild(element);
0640     QDomText textNode = domDoc.createTextNode(text);
0641     element.appendChild(textNode);
0642 }