1 /*
2  * read a KEduVocDocument from a KVTML file
3  * SPDX-FileCopyrightText: 1999-2001 Ewald Arnold <kvoctrain@ewald-arnold.de>
4  * SPDX-FileCopyrightText: 2005 Eric Pignet <eric at erixpage.com>
5  * SPDX-FileCopyrightText: 2007 Peter Hedlund <peter.hedlund@kdemail.net>
6  * SPDX-FileCopyrightText: 2007-2010 Frederik Gladhorn <gladhorn@kde.org>
7  * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include "keduvockvtml2reader.h"
11 
12 #include <KLocalizedString>
13 #include <QTextStream>
14 #include <QList>
15 #include <QIODevice>
16 #include <QDir>
17 
18 #include "keduvoclesson.h"
19 #include "keduvocleitnerbox.h"
20 #include "keduvocwordtype.h"
21 #include "kvtml2defs.h"
22 #include "keduvockvtmlreader.h"
23 #include "keduvoccommon_p.h"
24 
25 #include <QDebug>
26 
KEduVocKvtml2Reader(QIODevice & file)27 KEduVocKvtml2Reader::KEduVocKvtml2Reader(QIODevice & file)
28         : m_inputFile( &file )
29 {
30 }
31 
isParsable()32 bool KEduVocKvtml2Reader::isParsable()
33 {
34     QTextStream ts( m_inputFile );
35     QString line1( ts.readLine() );
36     QString line2( ts.readLine() );
37 
38     m_inputFile->seek( 0 );
39     return  ( ( line1.startsWith(QLatin1String("<?xml")) )
40               && ( line2.indexOf( KVTML_TAG, 0 ) >  0 ) );
41 }
42 
fileTypeHandled()43 KEduVocDocument::FileType KEduVocKvtml2Reader::fileTypeHandled()
44 {
45     return KEduVocDocument::Kvtml;
46 }
47 
read(KEduVocDocument & doc)48 KEduVocDocument::ErrorCode KEduVocKvtml2Reader::read(KEduVocDocument &doc)
49 {
50     m_doc = &doc;
51 
52     QDomDocument domDoc( QStringLiteral("KEduVocDocument") );
53 
54     if ( !domDoc.setContent( m_inputFile, &m_errorMessage ) )
55         return KEduVocDocument::InvalidXml;
56 
57     QDomElement domElementKvtml = domDoc.documentElement();
58     if ( domElementKvtml.tagName() != KVTML_TAG ) {
59         m_errorMessage = i18n( "This is not a KDE Vocabulary document." );
60         return KEduVocDocument::FileTypeUnknown;
61     }
62 
63     if ( domElementKvtml.attribute( KVTML_VERSION ).toFloat() < 2.0 ) {
64         // read the file with the old format
65 
66         // first reset the file to the beginning
67         m_inputFile->seek( 0 );
68         KEduVocKvtmlReader oldFormat( *m_inputFile );
69 
70         // get the return value
71         KEduVocDocument::ErrorCode retval = oldFormat.read( doc );
72 
73         // pass the errormessage up
74         m_errorMessage = oldFormat.errorMessage();
75         return retval;
76     }
77 
78     //-------------------------------------------------------------------------
79     // Information
80     //-------------------------------------------------------------------------
81 
82     QDomElement info = domElementKvtml.firstChildElement( KVTML_INFORMATION );
83     if ( !info.isNull() ) {
84         if ( !readInformation( info ) )
85             return KEduVocDocument::FileReaderFailed;
86     }
87 
88     bool result = readGroups( domElementKvtml ); // read sub-groups
89 
90     return result ? KEduVocDocument::NoError : KEduVocDocument::FileReaderFailed;
91 }
92 
readInformation(QDomElement & informationElement)93 bool KEduVocKvtml2Reader::readInformation( QDomElement &informationElement )
94 {
95     // read the generator
96     QDomElement currentElement = informationElement.firstChildElement( KVTML_GENERATOR );
97     if ( !currentElement.isNull() ) {
98         m_doc->setGenerator( currentElement.text() );
99         // add the version if it's there
100         int pos = m_doc->generator().lastIndexOf( KVD_VERS_PREFIX );
101         if ( pos >= 0 ) {
102             m_doc->setVersion( m_doc->generator().remove( 0, pos + 2 ) );
103         }
104     }
105 
106     // read the title
107     currentElement = informationElement.firstChildElement( KVTML_TITLE );
108     if ( !currentElement.isNull() ) {
109         m_doc->setTitle( currentElement.text() );
110     }
111 
112     // read the author
113     currentElement = informationElement.firstChildElement( KVTML_AUTHOR );
114     if ( !currentElement.isNull() ) {
115         m_doc->setAuthor( currentElement.text() );
116     }
117 
118     currentElement = informationElement.firstChildElement( KVTML_AUTHORCONTACT );
119     if ( !currentElement.isNull() ) {
120         m_doc->setAuthorContact( currentElement.text() );
121     }
122 
123     // read the license
124     currentElement = informationElement.firstChildElement( KVTML_LICENSE );
125     if ( !currentElement.isNull() ) {
126         m_doc->setLicense( currentElement.text() );
127     }
128 
129     // read the comment
130     currentElement = informationElement.firstChildElement( KVTML_COMMENT );
131     if ( !currentElement.isNull() ) {
132         m_doc->setDocumentComment( currentElement.text() );
133     }
134 
135     // read the category
136     currentElement = informationElement.firstChildElement( KVTML_CATEGORY );
137     if ( !currentElement.isNull() ) {
138         m_doc->setCategory( currentElement.text() );
139     }
140 
141     return true;
142 }
143 
readGroups(QDomElement & domElementParent)144 bool KEduVocKvtml2Reader::readGroups( QDomElement &domElementParent )
145 {
146     bool result = false;
147 
148     QDomElement groupElement = domElementParent.firstChildElement( KVTML_IDENTIFIERS );
149 
150     QDomElement currentElement;
151 
152     // ensure backwards compatibility - in kde 4.1 and earlier tenses were direct properties of the document class.
153     // now they are moved into the individual identifiers
154     QStringList tensesCompability;
155     groupElement = groupElement.firstChildElement( KVTML_TENSES );
156     if ( !groupElement.isNull() ) {
157         tensesCompability = readTenses( groupElement );
158     }
159 
160     groupElement = domElementParent.firstChildElement( KVTML_IDENTIFIERS );
161     if ( !groupElement.isNull() ) {
162         QDomNodeList entryList = groupElement.elementsByTagName( KVTML_IDENTIFIER );
163         if ( entryList.length() <= 0 ) {
164             m_errorMessage = i18n( "missing identifier elements from identifiers tag" );
165             return false;
166         }
167 
168         for ( int i = 0; i < entryList.count(); ++i ) {
169             currentElement = entryList.item( i ).toElement();
170             if ( currentElement.parentNode() == groupElement ) {
171                 result = readIdentifier( currentElement );
172                 if ( !result ) {
173                     return false;
174                 }
175                 if (!tensesCompability.isEmpty()) {
176                     m_doc->identifier(i).setTenseList(tensesCompability);
177                 }
178             }
179         }
180     }
181 
182     groupElement = domElementParent.firstChildElement( KVTML_ENTRIES );
183     if ( !groupElement.isNull() ) {
184         QDomNodeList entryList = groupElement.elementsByTagName( KVTML_ENTRY );
185         for ( int i = 0; i < entryList.count(); ++i ) {
186             currentElement = entryList.item( i ).toElement();
187             if ( currentElement.parentNode() == groupElement ) {
188                 result = readEntry( currentElement );
189                 if ( !result )
190                     return false;
191             }
192         }
193     }
194 
195     readSynonymsAntonymsFalseFriends( domElementParent );
196 
197     groupElement = domElementParent.firstChildElement( KVTML_WORDTYPES );
198     if ( !groupElement.isNull() ) {
199         readChildWordTypes( m_doc->wordTypeContainer(), groupElement );
200     }
201 
202     groupElement = domElementParent.firstChildElement( KVTML_LEITNERBOXES );
203     if ( !groupElement.isNull() ) {
204         readLeitner( m_doc->leitnerContainer(), groupElement );
205     }
206 
207     groupElement = domElementParent.firstChildElement( KVTML_LESSONS );
208     if ( !groupElement.isNull() ) {
209         readChildLessons(m_doc->lesson(), groupElement);
210     }
211 
212     // Additional cleanup: Put orphaned entries without a lesson into a default lesson.
213     KEduVocLesson *defaultLesson = new KEduVocLesson(i18n("Default Lesson"), m_doc->lesson());
214 
215     // now make sure we don't have any orphan entries
216     foreach (KEduVocExpression * entry, m_allEntries) {
217         if (!entry->lesson())
218         {
219             defaultLesson->appendEntry(entry);
220         }
221     }
222 
223     if (defaultLesson->entryCount() > 0)
224     {
225         m_doc->lesson()->appendChildContainer(defaultLesson);
226     } else {
227         delete defaultLesson;
228     }
229 
230     return true;
231 }
232 
233 
readIdentifier(QDomElement & identifierElement)234 bool KEduVocKvtml2Reader::readIdentifier( QDomElement &identifierElement )
235 {
236     bool result = true;
237     int id = identifierElement.attribute( KVTML_ID ).toInt( &result );
238     if ( !result ) {
239         m_errorMessage = i18n( "identifier missing id" );
240         return false;
241     }
242 
243     // generate empty identifiers in the doc
244     for ( int i = m_doc->identifierCount(); i <= id; i++ ) {
245         m_doc->appendIdentifier( KEduVocIdentifier() );
246     }
247 
248     // the first element, create the identifier, even if empty
249     QDomElement currentElement = identifierElement.firstChildElement( KVTML_NAME );
250     m_doc->identifier(id).setName( currentElement.text() );
251 
252     currentElement = identifierElement.firstChildElement( KVTML_LOCALE );
253     m_doc->identifier(id).setLocale( currentElement.text() );
254 
255     currentElement = identifierElement.firstChildElement( KVTML_IDENTIFIERTYPE );
256     if ( !currentElement.isNull() ) {
257         // TODO: do something with the type
258     }
259 
260     // read sub-parts
261     currentElement = identifierElement.firstChildElement( KVTML_ARTICLE );
262     if ( !currentElement.isNull() ) {
263         readArticle( currentElement, id );
264     }
265 
266     currentElement = identifierElement.firstChildElement( KVTML_PERSONALPRONOUNS );
267     if ( !currentElement.isNull() ) {
268         KEduVocPersonalPronoun personalPronoun;
269         readPersonalPronoun( currentElement, personalPronoun );
270         m_doc->identifier(id).setPersonalPronouns( personalPronoun );
271     }
272 
273     QStringList tenses = readTenses(identifierElement);
274 
275     m_doc->identifier(id).setTenseList(tenses);
276 
277     return result;
278 }
279 
readEntry(QDomElement & entryElement)280 bool KEduVocKvtml2Reader::readEntry( QDomElement &entryElement )
281 {
282     QDomElement currentElement;
283     bool result = true;
284 
285     // get entry id
286     int id = entryElement.attribute( KVTML_ID ).toInt( &result );
287     if ( !result ) {
288         m_errorMessage = i18n( "entry missing id" );
289         return false;
290     }
291 
292     KEduVocExpression *expr = new KEduVocExpression;
293 
294     // read info tags: inactive, inquery, and sizehint
295     currentElement = entryElement.firstChildElement( KVTML_DEACTIVATED );
296     if ( !currentElement.isNull() ) {
297         // set the active state of the expression
298         if ( currentElement.text() == KVTML_TRUE ) {
299             expr->setActive( false );
300         } else {
301             expr->setActive( true );
302         }
303     }
304 
305     // read translation children
306     QDomNodeList translationList = entryElement.elementsByTagName( KVTML_TRANSLATION );
307 
308     for ( int i = 0; i < translationList.count(); ++i ) {
309         currentElement = translationList.item( i ).toElement();
310         if ( currentElement.parentNode() == entryElement ) {
311             result = readTranslation( currentElement, expr, i );
312             if ( !result )
313                 return false;
314         }
315     }
316 
317     if ( expr->translationIndices().size() == 0 ) {
318         qDebug() << "Found entry with no words in it." << id;
319         expr->setTranslation(0, QString());
320     }
321 
322     Q_ASSERT(expr);
323 
324     // TODO: probably should insert at id position with a check to see if it exists
325     // may be useful for detecting corrupt documents
326     m_allEntries[id] = expr;
327     return result;
328 }
329 
readTranslation(QDomElement & translationElement,KEduVocExpression * expr,int index)330 bool KEduVocKvtml2Reader::readTranslation( QDomElement &translationElement,
331         KEduVocExpression *expr, int index )
332 {
333     // read the text, grade, declension and conjugation
334     expr->translation(index)->fromKVTML2(translationElement);
335     QDomElement currentElement;
336 
337     // article grade
338     currentElement = translationElement.firstChildElement( KVTML_ARTICLE );
339     if ( !currentElement.isNull() ) {
340         KEduVocText article;
341         article.fromKVTML2(currentElement);
342         expr->translation(index)->setArticle(article);
343     }
344 
345     // comparisons
346     currentElement = translationElement.firstChildElement( KVTML_COMPARISON );
347     if ( !currentElement.isNull() ) {
348         readComparison( currentElement, expr->translation(index) );
349     }
350 
351     // multiple choice
352     currentElement = translationElement.firstChildElement( KVTML_MULTIPLECHOICE );
353     if ( !currentElement.isNull() ) {
354         readMultipleChoice( currentElement, expr->translation(index) );
355     }
356 
357     // image
358     currentElement = translationElement.firstChildElement( KVTML_IMAGE );
359     if ( !currentElement.isNull() ) {
360         QUrl imageUrl(currentElement.text());
361         if (imageUrl.isLocalFile() && QDir::isRelativePath(imageUrl.toLocalFile())) {
362             imageUrl = QUrl(m_doc->url().toString(QUrl::RemoveFilename) + imageUrl.toLocalFile());
363         }
364         expr->translation(index)->setImageUrl(imageUrl);
365     }
366 
367     // sound
368     currentElement = translationElement.firstChildElement( KVTML_SOUND );
369     if ( !currentElement.isNull() ) {
370         QUrl soundUrl(currentElement.text());
371         if (soundUrl.isLocalFile() && QDir::isRelativePath(soundUrl.toLocalFile())) {
372             soundUrl = QUrl(m_doc->url().toString(QUrl::RemoveFilename) + soundUrl.toLocalFile());
373         }
374         expr->translation(index)->setSoundUrl(soundUrl);
375     }
376 
377     return true;
378 }
379 
readChildLessons(KEduVocLesson * parentLesson,QDomElement & lessonElement)380 bool KEduVocKvtml2Reader::readChildLessons( KEduVocLesson* parentLesson, QDomElement &lessonElement )
381 {
382     QDomElement currentElement = lessonElement.firstChildElement( KVTML_CONTAINER );
383     while ( !currentElement.isNull() ) {
384         readLesson(parentLesson, currentElement);
385         currentElement = currentElement.nextSiblingElement( KVTML_CONTAINER );
386     }
387     return true;
388 }
389 
readLesson(KEduVocLesson * parentLesson,QDomElement & lessonElement)390 bool KEduVocKvtml2Reader::readLesson( KEduVocLesson* parentLesson, QDomElement &lessonElement )
391 {
392     //<name>Lesson name</name>
393     QDomElement currentElement = lessonElement.firstChildElement( KVTML_NAME );
394     KEduVocLesson * lesson = new KEduVocLesson(currentElement.text(), parentLesson);
395     parentLesson->appendChildContainer( lesson );
396 
397     readChildLessons( lesson, lessonElement );
398 
399     //<query>true</query>
400     currentElement = lessonElement.firstChildElement( KVTML_INPRACTICE );
401     lesson->setInPractice(currentElement.text() == KVTML_TRUE);
402 
403     //<entry id="123"/>
404     currentElement = lessonElement.firstChildElement( KVTML_ENTRY );
405     while ( !currentElement.isNull() ) {
406         bool result = false;
407         int entryId = currentElement.attribute( KVTML_ID ).toInt( &result );
408         if(result) {
409             if (m_allEntries[entryId]) {
410                 lesson->appendEntry( m_allEntries[entryId] );
411             }
412         }
413         currentElement = currentElement.nextSiblingElement( KVTML_ENTRY );
414     }
415     return true;
416 }
417 
readSynonymsAntonymsFalseFriends(QDomElement & rootElement)418 bool KEduVocKvtml2Reader::readSynonymsAntonymsFalseFriends( QDomElement &rootElement )
419 {
420     QDomElement pairElement;
421     for(int type = KEduVocTranslation::Synonym; type <= KEduVocTranslation::FalseFriend; type++) {
422         switch (type) {
423         case KEduVocTranslation::Synonym:
424             pairElement= rootElement.firstChildElement( KVTML_SYNONYM );
425             break;
426         case KEduVocTranslation::Antonym:
427             pairElement= rootElement.firstChildElement( KVTML_ANTONYM );
428             break;
429         case KEduVocTranslation::FalseFriend:
430             pairElement= rootElement.firstChildElement( KVTML_FALSEFRIEND );
431             break;
432         }
433         // pair
434         pairElement = pairElement.firstChildElement( KVTML_PAIR );
435         while ( !pairElement.isNull() ) {
436             //<entry id="123"/>
437             QDomElement entryElement = pairElement.firstChildElement( KVTML_ENTRY );
438             int firstEntryId = entryElement.attribute( KVTML_ID ).toInt();
439 
440             QDomElement translationElement = entryElement.firstChildElement( KVTML_TRANSLATION );
441             int firstTranslationId = translationElement.attribute( KVTML_ID ).toInt();
442 
443             // second entry
444             entryElement = entryElement.nextSiblingElement( KVTML_ENTRY );
445             int secondEntryId = entryElement.attribute( KVTML_ID ).toInt();
446             translationElement = entryElement.firstChildElement( KVTML_TRANSLATION );
447             int secondTranslationId = translationElement.attribute( KVTML_ID ).toInt();
448 
449             // pair them up
450             KEduVocTranslation *first = m_allEntries[firstEntryId]->translation(firstTranslationId);
451             KEduVocTranslation *second = m_allEntries[secondEntryId]->translation(secondTranslationId);
452 
453             switch (type) {
454             case KEduVocTranslation::Synonym:
455                 first->addSynonym(second);
456                 second->addSynonym(first);
457                 break;
458             case KEduVocTranslation::Antonym:
459                 first->addAntonym(second);
460                 second->addAntonym(first);
461                 break;
462             case KEduVocTranslation::FalseFriend:
463                 first->addFalseFriend(second);
464                 second->addFalseFriend(first);
465                 break;
466             }
467             pairElement = pairElement.nextSiblingElement( KVTML_PAIR );
468         }
469     }
470     return true;
471 }
472 
readArticle(QDomElement & articleElement,int identifierNum)473 bool KEduVocKvtml2Reader::readArticle( QDomElement &articleElement, int identifierNum )
474 /*
475  <article>
476   <singlular>
477     <definite>
478         <male>der</male>
479         <female>die</female>
480         <neutral>das</neutral>
481     </definite>
482     <indefinite>
483         <male>ein</male>
484         <female>eine</female>
485         <neutral>ein</neutral>
486     </indefinite>
487   </singular>
488   <dual>
489   </dual>
490  </article>
491 */
492 {
493     QMap<int, KEduVocWordFlag::Flags> numbers;
494     numbers[0] = KEduVocWordFlag::Singular;
495     numbers[1] = KEduVocWordFlag::Dual;
496     numbers[2] = KEduVocWordFlag::Plural;
497     QMap<int, KEduVocWordFlag::Flags> genders;
498     genders[0] = KEduVocWordFlag::Masculine;
499     genders[1] = KEduVocWordFlag::Feminine;
500     genders[2] = KEduVocWordFlag::Neuter;
501     QMap<int, KEduVocWordFlag::Flags> defs;
502     defs[0] = KEduVocWordFlag::Definite;
503     defs[1] = KEduVocWordFlag::Indefinite;
504 
505     for ( int num = 0; num <= 2; ++num) {
506         QDomElement numberElement = articleElement.firstChildElement( KVTML_GRAMMATICAL_NUMBER[num] );
507         if (!numberElement.isNull()) {
508             // definite
509             for ( int def = 0; def <= 1; ++def ) {
510                 QDomElement defElement = numberElement.firstChildElement( KVTML_GRAMMATICAL_DEFINITENESS[def] );
511                 if (!defElement.isNull()) {
512                     // male
513                     for ( int gen = 0; gen <= 2; ++gen ) {
514                         QDomElement genderElement = defElement.firstChildElement( KVTML_GRAMMATICAL_GENDER[gen] );
515                         if (!genderElement.isNull()) {
516                             m_doc->identifier(identifierNum).article().setArticle( genderElement.text(), numbers[num] | defs[def] | genders[gen]);
517                         }
518                     }
519                 }
520             }
521         }
522     }
523 
524     return true;
525 }
526 
527 
readChildWordTypes(KEduVocWordType * parentContainer,QDomElement & lessonElement)528 bool KEduVocKvtml2Reader::readChildWordTypes(KEduVocWordType* parentContainer, QDomElement &lessonElement)
529 {
530     QDomElement currentElement = lessonElement.firstChildElement( KVTML_CONTAINER );
531     while ( !currentElement.isNull() ) {
532         readWordType(parentContainer, currentElement);
533         currentElement = currentElement.nextSiblingElement( KVTML_CONTAINER );
534     }
535     return true;
536 }
537 
readLeitner(KEduVocLeitnerBox * parentContainer,QDomElement & leitnerParentElement)538 bool KEduVocKvtml2Reader::readLeitner( KEduVocLeitnerBox* parentContainer, QDomElement &leitnerParentElement )
539 {
540     QDomElement leitnerElement = leitnerParentElement.firstChildElement( KVTML_CONTAINER );
541     while ( !leitnerElement.isNull() ) {
542         QString name = leitnerElement.firstChildElement( KVTML_NAME ).text();
543 
544         KEduVocLeitnerBox * leitner = new KEduVocLeitnerBox(name, parentContainer);
545         parentContainer->appendChildContainer(leitner);
546         // for leitner we only allow a flat list, no sub boxes.
547 
548         // read entries
549         QDomElement entryElement = leitnerElement.firstChildElement( KVTML_ENTRY );
550         while ( !entryElement.isNull() ) {
551             // read <entry id="123"></entryid>
552             int entryId = entryElement.attribute( KVTML_ID ).toInt();
553             QDomElement translationElement = entryElement.firstChildElement( KVTML_TRANSLATION );
554             while( !translationElement.isNull() ) {
555                 // <translation id="234"/>
556                 int translationId = translationElement.attribute( KVTML_ID ).toInt();
557                 m_allEntries.value(entryId)->translation(translationId)->setLeitnerBox(leitner);
558                 translationElement = translationElement.nextSiblingElement( KVTML_TRANSLATION );
559             }
560             entryElement = entryElement.nextSiblingElement( KVTML_ENTRY );
561         }
562         leitnerElement = leitnerElement.nextSiblingElement( KVTML_CONTAINER );
563     }
564     return true;
565 }
566 
readWordType(KEduVocWordType * parentContainer,QDomElement & typeElement)567 bool KEduVocKvtml2Reader::readWordType( KEduVocWordType* parentContainer, QDomElement &typeElement )
568 {
569     // set type and specialtype
570     QString typeName =
571         typeElement.firstChildElement( KVTML_NAME ).text();
572 
573     KEduVocWordType * wordTypeContainer = new KEduVocWordType(typeName, parentContainer);
574     parentContainer->appendChildContainer(wordTypeContainer);
575 
576     QString specialType = typeElement.firstChildElement( KVTML_SPECIALWORDTYPE ).text();
577     if ( !specialType.isEmpty() ) {
578         // get the localized version
579         if ( specialType == KVTML_SPECIALWORDTYPE_NOUN ) {
580             wordTypeContainer->setWordType(KEduVocWordFlag::Noun);
581         }
582         if ( specialType == KVTML_SPECIALWORDTYPE_VERB ) {
583             wordTypeContainer->setWordType(KEduVocWordFlag::Verb);
584         }
585         if ( specialType == KVTML_SPECIALWORDTYPE_ADVERB ) {
586             wordTypeContainer->setWordType(KEduVocWordFlag::Adverb);
587         }
588         if ( specialType == KVTML_SPECIALWORDTYPE_ADJECTIVE ) {
589             wordTypeContainer->setWordType(KEduVocWordFlag::Adjective);
590         }
591         if ( specialType == KVTML_SPECIALWORDTYPE_NOUN_MALE ) {
592             wordTypeContainer->setWordType(KEduVocWordFlag::Noun | KEduVocWordFlag::Masculine);
593         }
594         if ( specialType == KVTML_SPECIALWORDTYPE_NOUN_FEMALE ) {
595             wordTypeContainer->setWordType(KEduVocWordFlag::Noun | KEduVocWordFlag::Feminine);
596         }
597         if ( specialType == KVTML_SPECIALWORDTYPE_NOUN_NEUTRAL ) {
598             wordTypeContainer->setWordType(KEduVocWordFlag::Noun| KEduVocWordFlag::Neuter);
599         }
600         if ( specialType == KVTML_SPECIALWORDTYPE_CONJUNCTION ) {
601             wordTypeContainer->setWordType(KEduVocWordFlag::Conjunction);
602         }
603     } // special type
604 
605     // read entries
606     QDomElement entryElement = typeElement.firstChildElement( KVTML_ENTRY );
607     while ( !entryElement.isNull() ) {
608         // read <entry id="123"></entryid>
609         int entryId = entryElement.attribute( KVTML_ID ).toInt();
610         QDomElement translationElement = entryElement.firstChildElement( KVTML_TRANSLATION );
611         while( !translationElement.isNull() ) {
612             // <translation id="234"/>
613             int translationId = translationElement.attribute( KVTML_ID ).toInt();
614             m_allEntries.value(entryId)->translation(translationId)->setWordType(wordTypeContainer);
615             translationElement = translationElement.nextSiblingElement( KVTML_TRANSLATION );
616         }
617         entryElement = entryElement.nextSiblingElement( KVTML_ENTRY );
618     }
619 
620     readChildWordTypes(wordTypeContainer, typeElement);
621 
622     return true;
623 }
624 
readTenses(QDomElement & tensesElement)625 QStringList KEduVocKvtml2Reader::readTenses( QDomElement &tensesElement )
626 {
627     QStringList tenses;
628 
629     QDomNodeList tenseNodes = tensesElement.elementsByTagName( KVTML_TENSE );
630     for ( int i = 0; i < tenseNodes.count(); ++i ) {
631         QDomElement currentElement = tenseNodes.item( i ).toElement();
632         if ( currentElement.parentNode() == tensesElement ) {
633             tenses.append( currentElement.text() );
634         }
635     }
636 
637     return tenses;
638 }
639 
readComparison(QDomElement & domElementParent,KEduVocTranslation * translation)640 bool KEduVocKvtml2Reader::readComparison( QDomElement &domElementParent, KEduVocTranslation* translation )
641 /*
642  <comparison>
643    <comparative>better</comparative>
644    <superlative>best</superlative>
645  </comparison>
646 */
647 {
648     QDomElement currentElement;
649 
650     currentElement = domElementParent.firstChildElement( KVTML_COMPARATIVE );
651     if ( !currentElement.isNull() )
652     {
653         KEduVocText comparative;
654         comparative.fromKVTML2(currentElement);
655 
656         // be compatible for KDE < 4.5
657         if (comparative.text().isEmpty()) {
658             comparative.setText(currentElement.text());
659         }
660         translation->setComparativeForm(comparative);
661     }
662 
663     currentElement = domElementParent.firstChildElement( KVTML_SUPERLATIVE );
664     if ( !currentElement.isNull() )
665     {
666         KEduVocText superlative;
667         superlative.fromKVTML2(currentElement);
668 
669         // be compatible for KDE < 4.5
670         if (superlative.text().isEmpty()) {
671             superlative.setText(currentElement.text());
672         }
673         translation->setSuperlativeForm(superlative);
674     }
675     return true;
676 }
677 
readMultipleChoice(QDomElement & multipleChoiceElement,KEduVocTranslation * translation)678 bool KEduVocKvtml2Reader::readMultipleChoice( QDomElement &multipleChoiceElement, KEduVocTranslation* translation )
679 /*
680  <multiplechoice>
681    <choice>good</choice>
682    <choice>better</choice>
683    <choice>best</choice>
684    <choice>best 2</choice>
685    <choice>best 3</choice>
686  </multiplechoice>
687 */
688 {
689     QDomElement currentElement;
690     QDomNodeList choiceNodes = multipleChoiceElement.elementsByTagName( KVTML_CHOICE );
691     for ( int i = 0; i < choiceNodes.count(); ++i )
692     {
693         currentElement = choiceNodes.item( i ).toElement();
694         if ( currentElement.parentNode() == multipleChoiceElement ) {
695             QStringList choices = translation->getMultipleChoice();
696             choices.append(currentElement.text());
697             translation->setMultipleChoice(choices);
698         }
699     }
700     return true;
701 }
702 
703 
readPersonalPronoun(QDomElement & pronounElement,KEduVocPersonalPronoun & pronoun)704 bool KEduVocKvtml2Reader::readPersonalPronoun(QDomElement & pronounElement, KEduVocPersonalPronoun & pronoun)
705 {
706     pronoun.setMaleFemaleDifferent(!pronounElement.firstChildElement(
707         KVTML_THIRD_PERSON_MALE_FEMALE_DIFFERENT).isNull());
708     pronoun.setNeutralExists( !pronounElement.firstChildElement(
709         KVTML_THIRD_PERSON_NEUTRAL_EXISTS).isNull() );
710     pronoun.setDualExists( !pronounElement.firstChildElement(
711         KVTML_DUAL_EXISTS).isNull() );
712 
713     QDomElement personElement = pronounElement.firstChildElement( KVTML_GRAMMATICAL_NUMBER[0] );
714     if ( !personElement.isNull() ) {
715         readPersonalPronounChild( personElement, pronoun, KEduVocWordFlag::Singular );
716     }
717 
718     personElement = pronounElement.firstChildElement( KVTML_GRAMMATICAL_NUMBER[1] );
719     if ( !personElement.isNull() ) {
720         readPersonalPronounChild( personElement, pronoun, KEduVocWordFlag::Dual );
721     }
722 
723     personElement = pronounElement.firstChildElement( KVTML_GRAMMATICAL_NUMBER[2] );
724     if ( !personElement.isNull() ) {
725         readPersonalPronounChild( personElement, pronoun, KEduVocWordFlag::Plural );
726     }
727     return true;
728 }
729 
730 
readPersonalPronounChild(QDomElement & personElement,KEduVocPersonalPronoun & pronoun,KEduVocWordFlags number)731 bool KEduVocKvtml2Reader::readPersonalPronounChild(QDomElement & personElement, KEduVocPersonalPronoun & pronoun, KEduVocWordFlags number)
732 {
733     QMap<int, KEduVocWordFlag::Flags> persons;
734     persons[0] = KEduVocWordFlag::First;
735     persons[1] = KEduVocWordFlag::Second;
736     persons[2] = (KEduVocWordFlag::Flags)((int)KEduVocWordFlag::Third | (int)KEduVocWordFlag::Masculine);
737     persons[3] = (KEduVocWordFlag::Flags)((int)KEduVocWordFlag::Third | (int)KEduVocWordFlag::Feminine);
738     persons[4] = (KEduVocWordFlag::Flags)((int)KEduVocWordFlag::Third | (int)KEduVocWordFlag::Neuter);
739 
740 
741 
742     for (int person = 0; person < 5; person++) {
743         QDomElement currentElement = personElement.firstChildElement( KVTML_GRAMMATICAL_PERSON[person] );
744         pronoun.setPersonalPronoun( currentElement.text(), persons[person] | number );
745     }
746 
747     return true;
748 }
749