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

0001 /*
0002  * read a KEduVocDocument from a KVTML file
0003  * SPDX-FileCopyrightText: 1999-2001 Ewald Arnold <kvoctrain@ewald-arnold.de>
0004  * SPDX-FileCopyrightText: 2005 Eric Pignet <eric at erixpage.com>
0005  * SPDX-FileCopyrightText: 2007 Peter Hedlund <peter.hedlund@kdemail.net>
0006  * SPDX-FileCopyrightText: 2007 Frederik Gladhorn <frederik.gladhorn@kdemail.net>
0007  * SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 
0010 #include "keduvockvtmlreader.h"
0011 
0012 #include <KLocalizedString>
0013 #include <QIODevice>
0014 #include <QTextStream>
0015 
0016 #include <QDebug>
0017 #include <QLocale>
0018 
0019 #include "keduvoccommon_p.h"
0020 #include "keduvoclesson.h"
0021 #include "keduvocwordtype.h"
0022 #include "kvtmldefs.h"
0023 
0024 KEduVocKvtmlReader::KEduVocKvtmlReader(QIODevice &file)
0025     : m_inputFile(&file)
0026 {
0027     m_errorMessage = QLatin1String("");
0028     qDebug() << "KEduVocKvtmlReader for kvtml version 1 files started.";
0029 }
0030 
0031 bool KEduVocKvtmlReader::isParsable()
0032 {
0033     QTextStream ts(m_inputFile);
0034     QString line1(ts.readLine());
0035     QString line2(ts.readLine());
0036 
0037     m_inputFile->seek(0);
0038     return ((line1.startsWith(QLatin1String("<?xml"))) && (line2.indexOf(KV_DOCTYPE, 0) > 0));
0039 }
0040 
0041 KEduVocDocument::ErrorCode KEduVocKvtmlReader::read(KEduVocDocument &doc)
0042 {
0043     m_doc = &doc;
0044     m_cols = 0;
0045     m_lines = 0;
0046 
0047     QDomDocument domDoc(QStringLiteral("KEduVocDocument"));
0048 
0049     if (!domDoc.setContent(m_inputFile, &m_errorMessage))
0050         return KEduVocDocument::InvalidXml;
0051 
0052     QDomElement domElementKvtml = domDoc.documentElement();
0053     if (domElementKvtml.tagName() != KV_DOCTYPE) {
0054         m_errorMessage = i18n("This is not a KDE Vocabulary document.");
0055         return KEduVocDocument::FileTypeUnknown;
0056     }
0057 
0058     //-------------------------------------------------------------------------
0059     // Attributes
0060     //-------------------------------------------------------------------------
0061 
0062     QDomAttr documentAttribute;
0063     documentAttribute = domElementKvtml.attributeNode(KV_ENCODING);
0064     if (!documentAttribute.isNull()) {
0065         // TODO handle old encodings
0066         // Qt DOM API autodetects encoding, so is there anything to do ?
0067     }
0068 
0069     documentAttribute = domElementKvtml.attributeNode(KV_TITLE);
0070     if (!documentAttribute.isNull())
0071         m_doc->setTitle(documentAttribute.value());
0072 
0073     documentAttribute = domElementKvtml.attributeNode(KV_AUTHOR);
0074     if (!documentAttribute.isNull())
0075         m_doc->setAuthor(documentAttribute.value());
0076 
0077     documentAttribute = domElementKvtml.attributeNode(KV_LICENSE);
0078     if (!documentAttribute.isNull())
0079         m_doc->setLicense(documentAttribute.value());
0080 
0081     documentAttribute = domElementKvtml.attributeNode(KV_DOC_REM);
0082     if (!documentAttribute.isNull())
0083         m_doc->setDocumentComment(documentAttribute.value());
0084 
0085     documentAttribute = domElementKvtml.attributeNode(KV_GENERATOR);
0086     if (!documentAttribute.isNull()) {
0087         m_doc->setGenerator(documentAttribute.value());
0088         int pos = m_doc->generator().lastIndexOf(KVD_VERS_PREFIX);
0089         if (pos >= 0)
0090             m_doc->setVersion(m_doc->generator().remove(0, pos + 2));
0091     }
0092 
0093     documentAttribute = domElementKvtml.attributeNode(KV_COLS);
0094     if (!documentAttribute.isNull())
0095         m_cols = documentAttribute.value().toInt(); /// currently not used anywhere
0096 
0097     documentAttribute = domElementKvtml.attributeNode(KV_LINES);
0098     if (!documentAttribute.isNull())
0099         m_lines = documentAttribute.value().toInt();
0100 
0101     //-------------------------------------------------------------------------
0102     // Children
0103     //-------------------------------------------------------------------------
0104 
0105     bool result = readBody(domElementKvtml); // read vocabulary
0106 
0107     return result ? KEduVocDocument::NoError : KEduVocDocument::FileReaderFailed;
0108 }
0109 
0110 bool KEduVocKvtmlReader::readBody(QDomElement &domElementParent)
0111 {
0112     bool result = false;
0113 
0114     QDomElement currentElement;
0115 
0116     currentElement = domElementParent.firstChildElement(KV_LESS_GRP);
0117     if (!currentElement.isNull()) {
0118         result = readLesson(currentElement);
0119         if (!result)
0120             return false;
0121     }
0122 
0123     currentElement = domElementParent.firstChildElement(KV_ARTICLE_GRP);
0124     if (!currentElement.isNull()) {
0125         result = readArticle(currentElement);
0126         if (!result)
0127             return false;
0128     }
0129 
0130     currentElement = domElementParent.firstChildElement(KV_CONJUG_GRP);
0131     if (!currentElement.isNull()) {
0132         int count = 0;
0133 
0134         QDomElement domElementConjugChild = currentElement.firstChildElement(KV_CON_ENTRY);
0135         while (!domElementConjugChild.isNull()) {
0136             QString lang;
0137             QDomAttr domAttrLang = domElementConjugChild.attributeNode(KV_LANG); // "l"
0138             // make sure, the identifier is there
0139             if (!addLanguage(count, domAttrLang.value())) {
0140                 return false;
0141             }
0142 
0143             KEduVocPersonalPronoun pronouns;
0144             if (!readPersonalPronouns(domElementConjugChild, pronouns)) {
0145                 return false;
0146             }
0147             m_doc->identifier(count).setPersonalPronouns(pronouns);
0148 
0149             count++;
0150 
0151             domElementConjugChild = domElementConjugChild.nextSiblingElement(KV_CON_ENTRY);
0152         }
0153     }
0154 
0155     // initialize the list of predefined types
0156     m_compability.setupWordTypes(m_doc->wordTypeContainer());
0157 
0158     currentElement = domElementParent.firstChildElement(KV_TYPE_GRP);
0159     if (!currentElement.isNull()) {
0160         result = readType(currentElement);
0161         if (!result)
0162             return false;
0163     }
0164 
0165     currentElement = domElementParent.firstChildElement(KV_TENSE_GRP);
0166     if (!currentElement.isNull()) {
0167         result = readTense(currentElement);
0168         if (!result)
0169             return false;
0170     }
0171 
0172     QDomNodeList entryList = domElementParent.elementsByTagName(KV_EXPR);
0173     if (entryList.length() <= 0)
0174         return false;
0175 
0176     for (int i = 0; i < entryList.count(); ++i) {
0177         currentElement = entryList.item(i).toElement();
0178         if (currentElement.parentNode() == domElementParent) {
0179             result = readExpression(currentElement);
0180             if (!result)
0181                 return false;
0182         }
0183     }
0184 
0185     for (int i = 0; i < m_doc->identifierCount(); i++) {
0186         m_doc->identifier(i).setTenseList(m_compability.documentTenses());
0187     }
0188 
0189     return true;
0190 }
0191 
0192 bool KEduVocKvtmlReader::readLesson(QDomElement &domElementParent)
0193 {
0194     QString s;
0195     QDomAttr attribute;
0196     QDomElement currentElement;
0197 
0198     //-------------------------------------------------------------------------
0199     // Children
0200     //-------------------------------------------------------------------------
0201 
0202     QDomNodeList entryList = domElementParent.elementsByTagName(KV_LESS_DESC);
0203     if (entryList.length() <= 0)
0204         return false;
0205 
0206     for (int i = 0; i < entryList.count(); ++i) {
0207         currentElement = entryList.item(i).toElement();
0208         if (currentElement.parentNode() == domElementParent) {
0209             int no = -1;
0210 
0211             attribute = currentElement.attributeNode(KV_LESS_NO);
0212             if (!attribute.isNull()) {
0213                 no = attribute.value().toInt();
0214             }
0215 
0216             bool inQuery = false;
0217             attribute = currentElement.attributeNode(KV_LESS_QUERY);
0218             if (!attribute.isNull()) {
0219                 inQuery = (attribute.value().toInt() != 0);
0220             }
0221 
0222             s = currentElement.text();
0223             KEduVocLesson *lesson = new KEduVocLesson(s, m_doc->lesson());
0224             lesson->setInPractice(inQuery);
0225             m_doc->lesson()->appendChildContainer(lesson);
0226             if (m_doc->lesson()->childContainerCount() != no - 1) {
0227                 qDebug() << "Warning! Lesson order may be confused. Are all lessons in order in the file?";
0228             }
0229         }
0230     }
0231 
0232     return true;
0233 }
0234 
0235 bool KEduVocKvtmlReader::readArticle(QDomElement &domElementParent)
0236 /*
0237  <article>
0238   <e l="de">        lang determines also lang order in entries !!
0239    <fi>eine</fi>    which must NOT differ
0240    <fd>die</fd>
0241    <mi>ein</mi>
0242    <md>der</md>
0243    <ni>ein</ni>
0244    <nd>das</nd>
0245   </e>
0246  </article>
0247 */
0248 {
0249     QString s;
0250     QDomAttr attribute;
0251     QDomElement currentElement;
0252     QDomElement article;
0253 
0254     QDomNodeList entryList = domElementParent.elementsByTagName(KV_ART_ENTRY);
0255     if (entryList.length() <= 0)
0256         return false;
0257 
0258     for (int i = 0; i < entryList.count(); ++i) {
0259         // qDebug() << "KEduVocKvtmlReader::readArticle() read " << entryList.count() << " articles. ";
0260         currentElement = entryList.item(i).toElement();
0261         if (currentElement.parentNode() == domElementParent) {
0262             QString lang;
0263             attribute = currentElement.attributeNode(KV_LANG);
0264 
0265             if (!addLanguage(i, attribute.value())) {
0266                 return false;
0267             }
0268 
0269             //---------
0270             // Children
0271 
0272             QString fem_def = QLatin1String("");
0273             QString mal_def = QLatin1String("");
0274             QString nat_def = QLatin1String("");
0275             QString fem_indef = QLatin1String("");
0276             QString mal_indef = QLatin1String("");
0277             QString nat_indef = QLatin1String("");
0278 
0279             article = currentElement.firstChildElement(KV_ART_FD);
0280             if (!article.isNull()) {
0281                 fem_def = article.text();
0282                 if (fem_def.isNull())
0283                     fem_def = QLatin1String("");
0284             }
0285 
0286             article = currentElement.firstChildElement(KV_ART_FI);
0287             if (!article.isNull()) {
0288                 fem_indef = article.text();
0289                 if (fem_indef.isNull())
0290                     fem_indef = QLatin1String("");
0291             }
0292 
0293             article = currentElement.firstChildElement(KV_ART_MD);
0294             if (!article.isNull()) {
0295                 mal_def = article.text();
0296                 if (mal_def.isNull())
0297                     mal_def = QLatin1String("");
0298             }
0299 
0300             article = currentElement.firstChildElement(KV_ART_MI);
0301             if (!article.isNull()) {
0302                 mal_indef = article.text();
0303                 if (mal_indef.isNull())
0304                     mal_indef = QLatin1String("");
0305             }
0306 
0307             article = currentElement.firstChildElement(KV_ART_ND);
0308             if (!article.isNull()) {
0309                 nat_def = article.text();
0310                 if (nat_def.isNull())
0311                     nat_def = QLatin1String("");
0312             }
0313 
0314             article = currentElement.firstChildElement(KV_ART_NI);
0315             if (!article.isNull()) {
0316                 nat_indef = article.text();
0317                 if (nat_indef.isNull())
0318                     nat_indef = QLatin1String("");
0319             }
0320 
0321             KEduVocArticle article;
0322             article.setArticle(mal_def, KEduVocWordFlag::Singular | KEduVocWordFlag::Definite | KEduVocWordFlag::Masculine);
0323             article.setArticle(fem_def, KEduVocWordFlag::Singular | KEduVocWordFlag::Definite | KEduVocWordFlag::Feminine);
0324             article.setArticle(nat_def, KEduVocWordFlag::Singular | KEduVocWordFlag::Definite | KEduVocWordFlag::Neuter);
0325             article.setArticle(mal_indef, KEduVocWordFlag::Singular | KEduVocWordFlag::Indefinite | KEduVocWordFlag::Masculine);
0326             article.setArticle(fem_indef, KEduVocWordFlag::Singular | KEduVocWordFlag::Indefinite | KEduVocWordFlag::Feminine);
0327             article.setArticle(nat_indef, KEduVocWordFlag::Singular | KEduVocWordFlag::Indefinite | KEduVocWordFlag::Neuter);
0328             m_doc->identifier(i).setArticle(article);
0329         }
0330     }
0331 
0332     return true;
0333 }
0334 
0335 bool KEduVocKvtmlReader::readTranslationConjugations(QDomElement &domElementParent, KEduVocTranslation *translation)
0336 {
0337     QString tense;
0338 
0339     QDomElement domElementConjugChild = domElementParent.firstChildElement(KV_CON_TYPE);
0340     while (!domElementConjugChild.isNull()) {
0341         // "n" == is the type is the tense
0342         QDomAttr domAttrLang = domElementConjugChild.attributeNode(KV_CON_NAME);
0343         QString oldShortTense = domAttrLang.value();
0344 
0345         tense = m_compability.tenseFromKvtml1(oldShortTense);
0346         KEduVocConjugation conjugation;
0347         readConjugation(domElementConjugChild, conjugation);
0348         translation->setConjugation(tense, conjugation);
0349 
0350         domElementConjugChild = domElementConjugChild.nextSiblingElement(KV_CON_TYPE);
0351     } // while -> next tense, count++
0352     return true;
0353 }
0354 
0355 bool KEduVocKvtmlReader::readConjugation(QDomElement &domElementParent, KEduVocConjugation &conjugation)
0356 /*
0357  <conjugation>        used in header for definition of "prefix"
0358   <e l="de">          lang determines also lang order in entries !!
0359    <s1>I</s1>         which must NOT differ
0360    <s2>you<2>
0361    <s3f>he</s3f>
0362    <s3m>she</s3m>
0363    <s3n>it</s3n>
0364    <p1>we</p1>
0365    <p2>you</p2>
0366    <p3f>they</p3f>
0367    <p3m>they</p3m>
0368    <p3n>they</p3n>
0369   </e>
0370  </conjugation>
0371 
0372  <conjugation>        and in entry for definition of tenses of (irreg.) verbs
0373   <t n="sipa">
0374    <s1>go</s1>
0375    <s2>go</s2>
0376    <s3f>goes</s3f>
0377    <s3m>goes</s3m>
0378    <s3n>goes</s3n>
0379    <p1>go</p1>
0380    <p2>go</p2>
0381    <p3f>go</p3f>
0382    <p3m>go</p3m>
0383    <p3n>go</p3n>
0384   </t>
0385  </conjugation>
0386 */
0387 {
0388     //     QString s;
0389     bool p3_common;
0390     bool s3_common;
0391     QString pers1_sing;
0392     QString pers2_sing;
0393     QString pers3_m_sing;
0394     QString pers3_f_sing;
0395     QString pers3_n_sing;
0396     QString pers1_plur;
0397     QString pers2_plur;
0398     QString pers3_m_plur;
0399     QString pers3_f_plur;
0400     QString pers3_n_plur;
0401 
0402     p3_common = false;
0403     s3_common = false;
0404 
0405     // get the individual entries for persons...
0406     QDomElement domElementConjugGrandChild = domElementParent.firstChild().toElement();
0407     while (!domElementConjugGrandChild.isNull()) {
0408         if (domElementConjugGrandChild.tagName() == KV_CON_P1S) {
0409             pers1_sing = domElementConjugGrandChild.text();
0410         } else if (domElementConjugGrandChild.tagName() == KV_CON_P2S) {
0411             pers2_sing = domElementConjugGrandChild.text();
0412         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3SF) {
0413             QDomAttr domAttrCommon = domElementConjugGrandChild.attributeNode(KV_CONJ_COMMON);
0414             if (!domAttrCommon.isNull())
0415                 s3_common = domAttrCommon.value().toInt(); // returns 0 if the conversion fails
0416             pers3_f_sing = domElementConjugGrandChild.text();
0417 
0418         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3SM) {
0419             pers3_m_sing = domElementConjugGrandChild.text();
0420 
0421         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3SN) {
0422             pers3_n_sing = domElementConjugGrandChild.text();
0423 
0424         } else if (domElementConjugGrandChild.tagName() == KV_CON_P1P) {
0425             pers1_plur = domElementConjugGrandChild.text();
0426 
0427         } else if (domElementConjugGrandChild.tagName() == KV_CON_P2P) {
0428             pers2_plur = domElementConjugGrandChild.text();
0429 
0430         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3PF) {
0431             QDomAttr domAttrCommon = domElementConjugGrandChild.attributeNode(KV_CONJ_COMMON);
0432             if (!domAttrCommon.isNull())
0433                 p3_common = domAttrCommon.value().toInt(); // returns 0 if the conversion fails
0434 
0435             pers3_f_plur = domElementConjugGrandChild.text();
0436 
0437         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3PM) {
0438             pers3_m_plur = domElementConjugGrandChild.text();
0439 
0440         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3PN) {
0441             pers3_n_plur = domElementConjugGrandChild.text();
0442 
0443         } else {
0444             return false;
0445         }
0446 
0447         domElementConjugGrandChild = domElementConjugGrandChild.nextSibling().toElement();
0448     } // while - probably to be sure, because the persons could be in any order.
0449     // I guess this goes over only one set, such as:
0450     // <s1>traigo</s1><s2>traes</s2><s3fcommon="1">trae</s3f>
0451     // <p1>traemos</p1><p2>traéis</p2><p3f common="1">traen</p3f>
0452     // until no elements are left in that soup.
0453 
0454     // now set the data: [count] - number of conjug?
0455     // type - the tense?
0456     // finally the person
0457 
0458     const KEduVocWordFlags numS = KEduVocWordFlag::Singular;
0459     const KEduVocWordFlags numP = KEduVocWordFlag::Plural;
0460 
0461     conjugation.setConjugation(pers1_sing, KEduVocWordFlag::First | numS);
0462     conjugation.setConjugation(pers2_sing, KEduVocWordFlag::Second | numS);
0463     conjugation.setConjugation(pers1_plur, KEduVocWordFlag::First | numP);
0464     conjugation.setConjugation(pers2_plur, KEduVocWordFlag::Second | numP);
0465 
0466     if (s3_common) {
0467         conjugation.setConjugation(pers3_f_sing, KEduVocWordFlag::Third | KEduVocWordFlag::Neuter | KEduVocWordFlag::Singular);
0468     } else {
0469         conjugation.setConjugation(pers3_m_sing, KEduVocWordFlag::Third | KEduVocWordFlag::Masculine | KEduVocWordFlag::Singular);
0470         conjugation.setConjugation(pers3_f_sing, KEduVocWordFlag::Third | KEduVocWordFlag::Feminine | KEduVocWordFlag::Singular);
0471         conjugation.setConjugation(pers3_n_sing, KEduVocWordFlag::Third | KEduVocWordFlag::Neuter | KEduVocWordFlag::Singular);
0472     }
0473 
0474     if (p3_common) {
0475         conjugation.setConjugation(pers3_f_plur, KEduVocWordFlag::Third | KEduVocWordFlag::Neuter | KEduVocWordFlag::Plural);
0476     } else {
0477         conjugation.setConjugation(pers3_m_plur, KEduVocWordFlag::Third | KEduVocWordFlag::Masculine | KEduVocWordFlag::Plural);
0478         conjugation.setConjugation(pers3_f_plur, KEduVocWordFlag::Third | KEduVocWordFlag::Feminine | KEduVocWordFlag::Plural);
0479         conjugation.setConjugation(pers3_n_plur, KEduVocWordFlag::Third | KEduVocWordFlag::Neuter | KEduVocWordFlag::Plural);
0480     }
0481 
0482     return true;
0483 }
0484 
0485 bool KEduVocKvtmlReader::readPersonalPronouns(QDomElement &domElementParent, KEduVocPersonalPronoun &pronouns)
0486 {
0487     //     QString s;
0488     bool p3_common;
0489     bool s3_common;
0490     QString pers1_sing;
0491     QString pers2_sing;
0492     QString pers3_m_sing;
0493     QString pers3_f_sing;
0494     QString pers3_n_sing;
0495     QString pers1_plur;
0496     QString pers2_plur;
0497     QString pers3_m_plur;
0498     QString pers3_f_plur;
0499     QString pers3_n_plur;
0500 
0501     p3_common = false;
0502     s3_common = false;
0503 
0504     // get the individual entries for persons...
0505     QDomElement domElementConjugGrandChild = domElementParent.firstChild().toElement();
0506     while (!domElementConjugGrandChild.isNull()) {
0507         if (domElementConjugGrandChild.tagName() == KV_CON_P1S) {
0508             pers1_sing = domElementConjugGrandChild.text();
0509         } else if (domElementConjugGrandChild.tagName() == KV_CON_P2S) {
0510             pers2_sing = domElementConjugGrandChild.text();
0511         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3SF) {
0512             QDomAttr domAttrCommon = domElementConjugGrandChild.attributeNode(KV_CONJ_COMMON);
0513             if (!domAttrCommon.isNull())
0514                 s3_common = domAttrCommon.value().toInt(); // returns 0 if the conversion fails
0515             pers3_f_sing = domElementConjugGrandChild.text();
0516 
0517         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3SM) {
0518             pers3_m_sing = domElementConjugGrandChild.text();
0519 
0520         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3SN) {
0521             pers3_n_sing = domElementConjugGrandChild.text();
0522 
0523         } else if (domElementConjugGrandChild.tagName() == KV_CON_P1P) {
0524             pers1_plur = domElementConjugGrandChild.text();
0525 
0526         } else if (domElementConjugGrandChild.tagName() == KV_CON_P2P) {
0527             pers2_plur = domElementConjugGrandChild.text();
0528 
0529         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3PF) {
0530             QDomAttr domAttrCommon = domElementConjugGrandChild.attributeNode(KV_CONJ_COMMON);
0531             if (!domAttrCommon.isNull())
0532                 p3_common = domAttrCommon.value().toInt(); // returns 0 if the conversion fails
0533 
0534             pers3_f_plur = domElementConjugGrandChild.text();
0535 
0536         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3PM) {
0537             pers3_m_plur = domElementConjugGrandChild.text();
0538 
0539         } else if (domElementConjugGrandChild.tagName() == KV_CON_P3PN) {
0540             pers3_n_plur = domElementConjugGrandChild.text();
0541 
0542         } else {
0543             return false;
0544         }
0545 
0546         domElementConjugGrandChild = domElementConjugGrandChild.nextSibling().toElement();
0547     } // while - probably to be sure, because the persons could be in any order.
0548     // I guess this goes over only one set, such as:
0549     // <s1>traigo</s1><s2>traes</s2><s3fcommon="1">trae</s3f>
0550     // <p1>traemos</p1><p2>traéis</p2><p3f common="1">traen</p3f>
0551     // until no elements are left in that soup.
0552 
0553     // now set the data: [count] - number of conjug?
0554     // type - the tense?
0555     // finally the person
0556 
0557     KEduVocWordFlags numS = KEduVocWordFlag::Singular;
0558     pronouns.setMaleFemaleDifferent(false);
0559     pronouns.setPersonalPronoun(pers1_sing, KEduVocWordFlag::First | numS);
0560     pronouns.setPersonalPronoun(pers2_sing, KEduVocWordFlag::Second | numS);
0561 
0562     // used to have common in female
0563     if (s3_common) {
0564         pronouns.setPersonalPronoun(pers3_f_sing, KEduVocWordFlag::Third | KEduVocWordFlag::Neuter | numS);
0565     } else {
0566         pronouns.setPersonalPronoun(pers3_m_sing, KEduVocWordFlag::Third | KEduVocWordFlag::Masculine | numS);
0567         pronouns.setPersonalPronoun(pers3_f_sing, KEduVocWordFlag::Third | KEduVocWordFlag::Feminine | numS);
0568         pronouns.setPersonalPronoun(pers3_n_sing, KEduVocWordFlag::Third | KEduVocWordFlag::Neuter | numS);
0569         pronouns.setMaleFemaleDifferent(true);
0570     }
0571 
0572     KEduVocWordFlags numP = KEduVocWordFlag::Plural;
0573 
0574     pronouns.setPersonalPronoun(pers1_plur, KEduVocWordFlag::First | numP);
0575     pronouns.setPersonalPronoun(pers2_plur, KEduVocWordFlag::Second | numP);
0576     if (p3_common) {
0577         pronouns.setPersonalPronoun(pers3_f_plur, KEduVocWordFlag::Third | KEduVocWordFlag::Neuter | numP);
0578     } else {
0579         pronouns.setPersonalPronoun(pers3_m_plur, KEduVocWordFlag::Third | KEduVocWordFlag::Masculine | numP);
0580         pronouns.setPersonalPronoun(pers3_f_plur, KEduVocWordFlag::Third | KEduVocWordFlag::Feminine | numP);
0581         pronouns.setPersonalPronoun(pers3_n_plur, KEduVocWordFlag::Third | KEduVocWordFlag::Neuter | numP);
0582         pronouns.setMaleFemaleDifferent(true);
0583     }
0584 
0585     return true;
0586 }
0587 
0588 bool KEduVocKvtmlReader::readType(QDomElement &domElementParent)
0589 {
0590     QString s;
0591     QDomElement currentElement;
0592 
0593     QDomNodeList entryList = domElementParent.elementsByTagName(KV_TYPE_DESC);
0594     if (entryList.length() <= 0)
0595         return false;
0596 
0597     for (int i = 0; i < entryList.count(); ++i) {
0598         currentElement = entryList.item(i).toElement();
0599         if (currentElement.parentNode() == domElementParent) {
0600             // We need to even add empty elements since the old system relied on
0601             // the order. So "type1" "" "type2" should be just like that.
0602 
0603             qDebug() << "Adding old self defined type: " << currentElement.text();
0604             // add the type to the list of available types
0605             KEduVocWordType *type = new KEduVocWordType(currentElement.text(), m_doc->wordTypeContainer());
0606             m_doc->wordTypeContainer()->appendChildContainer(type);
0607 
0608             // from this the #1 are transformed to something sensible again
0609             m_oldSelfDefinedTypes.append(currentElement.text());
0610         }
0611     }
0612 
0613     return true;
0614 }
0615 
0616 bool KEduVocKvtmlReader::readTense(QDomElement &domElementParent)
0617 {
0618     QDomElement currentElement;
0619 
0620     currentElement = domElementParent.firstChildElement(KV_TENSE_DESC);
0621     while (!currentElement.isNull()) {
0622         qDebug() << "Reading user defined tense description: " << currentElement.text();
0623         m_compability.addUserdefinedTense(currentElement.text());
0624         currentElement = currentElement.nextSiblingElement(KV_TENSE_DESC);
0625     }
0626     return true;
0627 }
0628 
0629 bool KEduVocKvtmlReader::readComparison(QDomElement &domElementParent, KEduVocTranslation *translation)
0630 /*
0631  <comparison>
0632    <l1>good</l1> --- this one is dead as it always has to be the word itself
0633    <l2>better</l2>
0634    <l3>best</l3>
0635  </comparison>
0636 */
0637 {
0638     QDomElement currentElement;
0639 
0640     currentElement = domElementParent.firstChildElement(KV_COMP_L2);
0641     translation->setComparativeForm(currentElement.text());
0642 
0643     currentElement = domElementParent.firstChildElement(KV_COMP_L3);
0644     translation->setSuperlativeForm(currentElement.text());
0645 
0646     return true;
0647 }
0648 
0649 bool KEduVocKvtmlReader::readMultipleChoice(QDomElement &domElementParent, KEduVocTranslation *translation)
0650 /*
0651  <multiplechoice>
0652    <mc1>good</mc1>
0653    <mc2>better</mc2>
0654    <mc3>best</mc3>
0655    <mc4>best 2</mc4>
0656    <mc5>best 3</mc5>
0657  </multiplechoice>
0658 */
0659 
0660 {
0661     QDomElement currentElement;
0662     QStringList choices = translation->getMultipleChoice();
0663 
0664     currentElement = domElementParent.firstChildElement(KV_MC_1);
0665     if (!currentElement.isNull()) {
0666         choices.append(currentElement.text());
0667     }
0668 
0669     currentElement = domElementParent.firstChildElement(KV_MC_2);
0670     if (!currentElement.isNull()) {
0671         choices.append(currentElement.text());
0672     }
0673 
0674     currentElement = domElementParent.firstChildElement(KV_MC_3);
0675     if (!currentElement.isNull()) {
0676         choices.append(currentElement.text());
0677     }
0678 
0679     currentElement = domElementParent.firstChildElement(KV_MC_4);
0680     if (!currentElement.isNull()) {
0681         choices.append(currentElement.text());
0682     }
0683 
0684     currentElement = domElementParent.firstChildElement(KV_MC_5);
0685     if (!currentElement.isNull()) {
0686         choices.append(currentElement.text());
0687     }
0688 
0689     translation->setMultipleChoice(choices);
0690 
0691     return true;
0692 }
0693 
0694 bool KEduVocKvtmlReader::readExpressionChildAttributes(QDomElement &domElementExpressionChild,
0695                                                        QString &lang,
0696                                                        grade_t &grade,
0697                                                        grade_t &rev_grade,
0698                                                        int &count,
0699                                                        int &rev_count,
0700                                                        QDateTime &date,
0701                                                        QDateTime &rev_date,
0702                                                        QString &remark,
0703                                                        int &bcount,
0704                                                        int &rev_bcount,
0705                                                        QString &query_id,
0706                                                        QString &pronunciation,
0707                                                        int &width,
0708                                                        QString &type,
0709                                                        QString &faux_ami_f,
0710                                                        QString &faux_ami_t,
0711                                                        QString &synonym,
0712                                                        QString &example,
0713                                                        QString &antonym,
0714                                                        QSet<QString> &usages,
0715                                                        QString &paraphrase)
0716 {
0717     Q_UNUSED(usages)
0718     int pos;
0719     QDomAttr attribute;
0720 
0721     lang = QLatin1String("");
0722     attribute = domElementExpressionChild.attributeNode(KV_LANG);
0723     if (!attribute.isNull())
0724         lang = attribute.value();
0725 
0726     width = -1;
0727     attribute = domElementExpressionChild.attributeNode(KV_SIZEHINT);
0728     if (!attribute.isNull())
0729         width = attribute.value().toInt();
0730 
0731     grade = KV_NORM_GRADE;
0732     rev_grade = KV_NORM_GRADE;
0733     attribute = domElementExpressionChild.attributeNode(KV_GRADE);
0734     if (!attribute.isNull()) {
0735         QString s = attribute.value();
0736         if ((pos = s.indexOf(';')) >= 1) {
0737 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0738             grade = s.leftRef(pos).toInt();
0739             rev_grade = s.midRef(pos + 1, s.length()).toInt();
0740 #else
0741             grade = QStringView(s).left(pos).toInt();
0742             rev_grade = QStringView(s).mid(pos + 1, s.length()).toInt();
0743 #endif
0744         } else
0745             grade = s.toInt();
0746     }
0747 
0748     count = 0;
0749     rev_count = 0;
0750     attribute = domElementExpressionChild.attributeNode(KV_COUNT);
0751     if (!attribute.isNull()) {
0752         QString s = attribute.value();
0753         if ((pos = s.indexOf(';')) >= 1) {
0754 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0755             count = s.leftRef(pos).toInt();
0756             rev_count = s.midRef(pos + 1, s.length()).toInt();
0757 #else
0758             count = QStringView(s).left(pos).toInt();
0759             rev_count = QStringView(s).mid(pos + 1, s.length()).toInt();
0760 #endif
0761         } else
0762             count = s.toInt();
0763     }
0764 
0765     bcount = 0;
0766     rev_bcount = 0;
0767     attribute = domElementExpressionChild.attributeNode(KV_BAD);
0768     if (!attribute.isNull()) {
0769         QString s = attribute.value();
0770         if ((pos = s.indexOf(';')) >= 1) {
0771 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0772             bcount = s.leftRef(pos).toInt();
0773             rev_bcount = s.midRef(pos + 1, s.length()).toInt();
0774 #else
0775             bcount = QStringView(s).left(pos).toInt();
0776             rev_bcount = QStringView(s).mid(pos + 1, s.length()).toInt();
0777 #endif
0778         } else
0779             bcount = s.toInt();
0780     }
0781 
0782     date = QDateTime::fromSecsSinceEpoch(0);
0783     rev_date = QDateTime::fromSecsSinceEpoch(0);
0784     attribute = domElementExpressionChild.attributeNode(KV_DATE);
0785     if (!attribute.isNull()) {
0786         QString s = attribute.value();
0787         if ((pos = s.indexOf(';')) >= 1) {
0788 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0789             date = QDateTime::fromSecsSinceEpoch(s.leftRef(pos).toInt());
0790             rev_date.setTime_t(s.midRef(pos + 1, s.length()).toInt());
0791 #else
0792             date = QDateTime::fromSecsSinceEpoch(QStringView(s).left(pos).toInt());
0793             rev_date = QDateTime::fromSecsSinceEpoch(QStringView(s).mid(pos + 1, s.length()).toInt());
0794 #endif
0795         } else
0796             date = QDateTime::fromSecsSinceEpoch(s.toInt());
0797     }
0798 
0799     attribute = domElementExpressionChild.attributeNode(KV_DATE2);
0800     if (!attribute.isNull()) {
0801         // this format is deprecated and ignored.
0802     }
0803 
0804     remark = QLatin1String("");
0805     attribute = domElementExpressionChild.attributeNode(KV_REMARK);
0806     if (!attribute.isNull())
0807         remark = attribute.value();
0808 
0809     faux_ami_f = QLatin1String("");
0810     attribute = domElementExpressionChild.attributeNode(KV_FAUX_AMI_F);
0811     if (!attribute.isNull())
0812         faux_ami_f = attribute.value();
0813 
0814     faux_ami_t = QLatin1String("");
0815     attribute = domElementExpressionChild.attributeNode(KV_FAUX_AMI_T);
0816     if (!attribute.isNull())
0817         faux_ami_t = attribute.value();
0818 
0819     synonym = QLatin1String("");
0820     attribute = domElementExpressionChild.attributeNode(KV_SYNONYM);
0821     if (!attribute.isNull())
0822         synonym = attribute.value();
0823 
0824     example = QLatin1String("");
0825     attribute = domElementExpressionChild.attributeNode(KV_EXAMPLE);
0826     if (!attribute.isNull())
0827         example = attribute.value();
0828 
0829     paraphrase = QLatin1String("");
0830     attribute = domElementExpressionChild.attributeNode(KV_PARAPHRASE);
0831     if (!attribute.isNull())
0832         paraphrase = attribute.value();
0833 
0834     antonym = QLatin1String("");
0835     attribute = domElementExpressionChild.attributeNode(KV_ANTONYM);
0836     if (!attribute.isNull())
0837         antonym = attribute.value();
0838 
0839     // this is all done by reference - so we have to care about "type" :(
0840     attribute = domElementExpressionChild.attributeNode(KV_EXPRTYPE);
0841     if (!attribute.isNull()) {
0842         type = attribute.value();
0843     }
0844 
0845     pronunciation = QLatin1String("");
0846     attribute = domElementExpressionChild.attributeNode(KV_PRONUNCE);
0847     if (!attribute.isNull())
0848         pronunciation = attribute.value();
0849 
0850     query_id = QLatin1String("");
0851     attribute = domElementExpressionChild.attributeNode(KV_QUERY);
0852     if (!attribute.isNull())
0853         query_id = attribute.value();
0854 
0855     return true;
0856 }
0857 
0858 bool KEduVocKvtmlReader::readExpression(QDomElement &domElementParent)
0859 {
0860     grade_t grade;
0861     grade_t r_grade;
0862     int qcount;
0863     int r_qcount;
0864     int bcount;
0865     int r_bcount;
0866     QString remark;
0867     QString pronunciation;
0868     QDateTime qdate;
0869     QDateTime r_qdate;
0870     // bool                      inquery;
0871     bool active;
0872     QString lang;
0873     QString textstr;
0874     QString q_org;
0875     QString q_trans;
0876     QString query_id;
0877     int width;
0878     QString type;
0879     QString faux_ami_f;
0880     QString faux_ami_t;
0881     QString synonym;
0882     QString example;
0883     QString antonym;
0884     QSet<QString> usage;
0885     QString paraphrase;
0886 
0887     QDomAttr attribute;
0888     QDomElement currentElement;
0889     QDomElement currentChild;
0890 
0891     int lessonNumber = -1;
0892 
0893     //-------------------------------------------------------------------------
0894     // Attributes
0895     //-------------------------------------------------------------------------
0896 
0897     attribute = domElementParent.attributeNode(KV_LESS_MEMBER);
0898     if (!attribute.isNull()) {
0899         // we start counting from 0 in new documents
0900         lessonNumber = attribute.value().toInt() - 1;
0901         if (lessonNumber > m_doc->lesson()->childContainerCount()) {
0902             ///@todo can this happen? does it need a while loop?
0903             // it's from a lesson that hasn't been added yet
0904             // so make sure this lesson is in the document
0905             qDebug() << "Warning: lesson > m_doc->lessonCount() in readExpression.";
0906 
0907             KEduVocLesson *lesson = new KEduVocLesson(i18nc("A generic name for a new lesson and its number.", "Lesson %1", lessonNumber), m_doc->lesson());
0908             m_doc->lesson()->appendChildContainer(lesson);
0909         }
0910     }
0911 
0912     attribute = domElementParent.attributeNode(KV_SELECTED);
0913     // if ( !attribute.isNull() )
0914     //     inquery = attribute.value() == "1" ? true : false;
0915     // else
0916     //     inquery = false;
0917 
0918     attribute = domElementParent.attributeNode(KV_INACTIVE);
0919     if (!attribute.isNull())
0920         active = attribute.value() == QLatin1Char('1') ? false : true;
0921     else
0922         active = true;
0923 
0924     // this is all done by reference - so we have to care about "type" :(
0925     attribute = domElementParent.attributeNode(KV_EXPRTYPE);
0926     if (!attribute.isNull()) {
0927         type = attribute.value();
0928     }
0929 
0930     //-------------------------------------------------------------------------
0931     // Children 'Translation'
0932     //-------------------------------------------------------------------------
0933 
0934     // QDomNodeList translationList = domElementParent.elementsByTagName(KV_TRANS);
0935 
0936     // count which translation we are on
0937     int i = 0;
0938 
0939     // kvtml 1: we always have an original element (required)
0940     currentElement = domElementParent.firstChildElement(KV_ORG);
0941     if (currentElement.isNull()) { // sanity check
0942         m_errorMessage = i18n("Data for original language missing");
0943         return false;
0944     }
0945 
0946     KEduVocExpression *entry = nullptr;
0947 
0948     while (!currentElement.isNull()) {
0949         //-----------
0950         // Attributes
0951         //-----------
0952 
0953         // read attributes - the order of the query grades is interchanged!
0954         if (i == 0
0955             && !readExpressionChildAttributes(currentElement,
0956                                               lang,
0957                                               grade,
0958                                               r_grade,
0959                                               qcount,
0960                                               r_qcount,
0961                                               qdate,
0962                                               r_qdate,
0963                                               remark,
0964                                               bcount,
0965                                               r_bcount,
0966                                               query_id,
0967                                               pronunciation,
0968                                               width,
0969                                               type,
0970                                               faux_ami_t,
0971                                               faux_ami_f,
0972                                               synonym,
0973                                               example,
0974                                               antonym,
0975                                               usage,
0976                                               paraphrase)) {
0977             return false;
0978         }
0979 
0980         if (i != 0
0981             && !readExpressionChildAttributes(currentElement,
0982                                               lang,
0983                                               grade,
0984                                               r_grade,
0985                                               qcount,
0986                                               r_qcount,
0987                                               qdate,
0988                                               r_qdate,
0989                                               remark,
0990                                               bcount,
0991                                               r_bcount,
0992                                               query_id,
0993                                               pronunciation,
0994                                               width,
0995                                               type,
0996                                               faux_ami_f,
0997                                               faux_ami_t,
0998                                               synonym,
0999                                               example,
1000                                               antonym,
1001                                               usage,
1002                                               paraphrase)) {
1003             return false;
1004         }
1005 
1006         //---------
1007         // Children
1008 
1009         textstr = currentElement.lastChild().toText().data();
1010 
1011         if (i == 0) {
1012             entry = new KEduVocExpression(textstr);
1013             entry->setActive(active);
1014             if (lessonNumber != -1) {
1015                 static_cast<KEduVocLesson *>(m_doc->lesson()->childContainer(lessonNumber))->appendEntry(entry);
1016             } else {
1017                 m_doc->lesson()->appendEntry(entry);
1018             }
1019         } else {
1020             entry->setTranslation(i, textstr);
1021         }
1022 
1023         if (m_doc->lesson()->entries(KEduVocLesson::Recursive).count() == 1) { // this is because in kvtml the languages are saved in the FIRST ENTRY ONLY.
1024             // new translation
1025             if (!addLanguage(i, lang)) {
1026                 return false;
1027             }
1028         }
1029 
1030         // better make sure, translation(i) already exists...
1031         currentChild = currentElement.firstChildElement(KV_CONJUG_GRP);
1032         if (!currentChild.isNull()) {
1033             if (!readTranslationConjugations(currentChild, entry->translation(i))) {
1034                 return false;
1035             }
1036         }
1037 
1038         currentChild = currentElement.firstChildElement(KV_MULTIPLECHOICE_GRP);
1039         if (!currentChild.isNull()) {
1040             if (!readMultipleChoice(currentChild, entry->translation(i))) {
1041                 return false;
1042             }
1043         }
1044 
1045         currentChild = currentElement.firstChildElement(KV_COMPARISON_GRP);
1046         if (!currentChild.isNull()) {
1047             if (!readComparison(currentChild, entry->translation(i))) {
1048                 return false;
1049             }
1050         }
1051 
1052         if (!type.isEmpty()) {
1053             KEduVocWordType *wordType = m_compability.typeFromOldFormat(m_doc->wordTypeContainer(), type);
1054             entry->translation(i)->setWordType(wordType);
1055         }
1056 
1057         if (!remark.isEmpty())
1058             entry->translation(i)->setComment(remark);
1059         if (!pronunciation.isEmpty())
1060             entry->translation(i)->setPronunciation(pronunciation);
1061 
1062         ///@todo include false friends from kvtml-1 again?
1063         //         if ( !faux_ami_f.isEmpty() )
1064         //             entry->translation( i )->setFalseFriend( 0, faux_ami_f );
1065         //         if ( !faux_ami_t.isEmpty() )
1066         //             entry->translation( 0 )->setFalseFriend( i, faux_ami_t );
1067         ///@todo include synonyms from kvtml-1 again?
1068         //         if ( !synonym.isEmpty() )
1069         //             entry->translation( i )->setSynonym( synonym );
1070         //         if ( !antonym.isEmpty() )
1071         //             entry->translation( i )->setAntonym( antonym );
1072 
1073         if (!example.isEmpty())
1074             entry->translation(i)->setExample(example);
1075         if (!paraphrase.isEmpty())
1076             entry->translation(i)->setParaphrase(paraphrase);
1077 
1078         if (i != 0) {
1079             entry->translation(i)->setGrade(grade);
1080             entry->translation(0)->setGrade(r_grade);
1081             entry->translation(i)->setPracticeCount(qcount);
1082             entry->translation(0)->setPracticeCount(r_qcount);
1083             entry->translation(i)->setBadCount(bcount);
1084             entry->translation(0)->setBadCount(r_bcount);
1085             entry->translation(i)->setPracticeDate(qdate);
1086             entry->translation(0)->setPracticeDate(r_qdate);
1087         }
1088 
1089         // Next translation
1090         currentElement = currentElement.nextSiblingElement(KV_TRANS);
1091         i++;
1092     }
1093 
1094     return true;
1095 }
1096 
1097 bool KEduVocKvtmlReader::addLanguage(int languageId, const QString &locale)
1098 {
1099     if (m_doc->identifierCount() <= languageId) {
1100         m_doc->appendIdentifier();
1101         // first entry
1102         if (!locale.isEmpty()) { // no definition in first entry
1103             m_doc->identifier(languageId).setLocale(locale);
1104 
1105             QString languageName;
1106             // when using from qt-only apps this would crash (converter)
1107             languageName = QLocale::languageToString(QLocale(locale).language());
1108             if (languageName.isEmpty()) {
1109                 languageName = locale;
1110             }
1111 
1112             m_doc->identifier(languageId).setName(languageName);
1113             qDebug() << "addLanguage( " << languageId << ", " << locale << "): " << languageName;
1114         }
1115     } else {
1116         if (!locale.isEmpty()) {
1117             if (locale != m_doc->identifier(languageId).locale()) {
1118                 // different originals ?
1119                 m_errorMessage = i18n("Ambiguous definition of language code");
1120                 return false;
1121             }
1122         }
1123     }
1124     return true;
1125 }
1126 
1127 #include "moc_keduvockvtmlreader.cpp"