Warning, file /education/kiten/lib/entry.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     This file is part of Kiten, a KDE Japanese Reference Tool
0003     SPDX-FileCopyrightText: 2001 Jason Katz-Brown <jason@katzbrown.com>
0004     SPDX-FileCopyrightText: 2006 Joseph Kerian <jkerian@gmail.com>
0005     SPDX-FileCopyrightText: 2006 Eric Kjeldergaard <kjelderg@gmail.com>
0006     SPDX-FileCopyrightText: 2011 Daniel E. Moctezuma <democtezuma@gmail.com>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-or-later
0009 */
0010 
0011 #include "entry.h"
0012 
0013 #include <KLocalizedString>
0014 
0015 #include <cassert>
0016 #include <iostream>
0017 #include <stdio.h>
0018 #include <sys/mman.h>
0019 
0020 /**
0021  * The default constructor, unless you really know what you're doing,
0022  * THIS SHOULD NOT BE USED. For general use, other entities will need
0023  * to have the information provided by the other constructors
0024  * (particularly the sourceDictionary).
0025  */
0026 Entry::Entry()
0027 {
0028     init();
0029 }
0030 
0031 Entry::Entry(const QString &sourceDictionary)
0032     : sourceDict(sourceDictionary)
0033 {
0034     init();
0035 }
0036 
0037 Entry::Entry(const QString &sourceDictionary, const QString &word, const QStringList &reading, const QStringList &meanings)
0038     : Word(word)
0039     , Meanings(meanings)
0040     , Readings(reading)
0041     , sourceDict(sourceDictionary)
0042 {
0043     init();
0044 }
0045 
0046 Entry::Entry(const Entry &src)
0047     : Word(src.Word)
0048     , Meanings(src.Meanings)
0049     , Readings(src.Readings)
0050     , ExtendedInfo(src.ExtendedInfo)
0051     , sourceDict(src.sourceDict)
0052 {
0053     outputListDelimiter = src.outputListDelimiter;
0054 }
0055 
0056 Entry::~Entry()
0057 {
0058     //   kdDebug() << "nuking : " << Word << endl;
0059 }
0060 
0061 bool Entry::extendedItemCheck(const QString &key, const QString &value) const
0062 {
0063     return getExtendedInfoItem(key) == value;
0064 }
0065 
0066 /**
0067  * Get the dictionary name that generated this Entry. I can't think of a reason to be changing this
0068  */
0069 QString Entry::getDictName() const
0070 {
0071     return sourceDict;
0072 }
0073 
0074 /**
0075  * Get the word from this Entry. If the entry is of type kanji/kana/meaning/etc, this will return
0076  * the kanji. If it is of kana/meaning/etc, it will return kana.
0077  */
0078 QString Entry::getWord() const
0079 {
0080     return Word;
0081 }
0082 
0083 /**
0084  * Get a QString containing all of the meanings known, connected by the outputListDelimiter
0085  */
0086 QString Entry::getMeanings() const
0087 {
0088     return Meanings.join(outputListDelimiter);
0089 }
0090 
0091 /**
0092  * Simple accessor
0093  */
0094 QStringList Entry::getMeaningsList() const
0095 {
0096     return Meanings;
0097 }
0098 
0099 /**
0100  * Simple accessor
0101  */
0102 QString Entry::getReadings() const
0103 {
0104     return Readings.join(outputListDelimiter);
0105 }
0106 
0107 /**
0108  * Simple accessor
0109  */
0110 QStringList Entry::getReadingsList() const
0111 {
0112     return Readings;
0113 }
0114 
0115 /**
0116  * Simple accessor
0117  */
0118 QHash<QString, QString> Entry::getExtendedInfo() const
0119 {
0120     return ExtendedInfo;
0121 }
0122 
0123 /**
0124  * Simple accessor
0125  *
0126  * @param x the key for the extended info item to get
0127  */
0128 QString Entry::getExtendedInfoItem(const QString &x) const
0129 {
0130     return ExtendedInfo[x];
0131 }
0132 
0133 /**
0134  * Prepares Meanings for output as HTML
0135  */
0136 inline QString Entry::HTMLMeanings() const
0137 {
0138     return QStringLiteral("<span class=\"Meanings\">%1</span>").arg(Meanings.join(outputListDelimiter));
0139 }
0140 
0141 /* Prepares Readings for output as HTML */
0142 inline QString Entry::HTMLReadings() const
0143 {
0144     QStringList list;
0145     foreach (const QString &it, Readings) {
0146         list += makeLink(it);
0147     }
0148 
0149     return QStringLiteral("<span class=\"Readings\">%1</span>").arg(list.join(outputListDelimiter));
0150 }
0151 
0152 /**
0153  * Prepares Word for output as HTML
0154  */
0155 inline QString Entry::HTMLWord() const
0156 {
0157     return QStringLiteral("<span class=\"Word\">%1</span>").arg(Word);
0158 }
0159 
0160 void Entry::init()
0161 {
0162     outputListDelimiter = i18n("; ");
0163 }
0164 
0165 /**
0166  * Determines whether @param character is a kanji character.
0167  */
0168 bool Entry::isKanji(const QChar &character) const
0169 {
0170     ushort value = character.unicode();
0171     if (value < 255) {
0172         return false;
0173     }
0174     if (0x3040 <= value && value <= 0x30FF) {
0175         return false; // Kana
0176     }
0177 
0178     return true; // Note our folly here... we assuming any non-ascii/kana is kanji
0179 }
0180 
0181 /**
0182  * Returns true if all members of test are in list
0183  */
0184 bool Entry::listMatch(const QStringList &list, const QStringList &test, DictQuery::MatchType type) const
0185 {
0186     if (type == DictQuery::Exact) {
0187         foreach (const QString &it, test) {
0188             if (!list.contains(it)) {
0189                 return false;
0190             }
0191         }
0192     } else if (type == DictQuery::Beginning) {
0193         foreach (const QString &it, test) {
0194             bool found = false;
0195             foreach (const QString &it2, list) {
0196                 if (it2.startsWith(it)) {
0197                     found = true;
0198                     break;
0199                 }
0200             }
0201             if (!found) {
0202                 return false;
0203             }
0204         }
0205     } else if (type == DictQuery::Ending) {
0206         foreach (const QString &it, test) {
0207             bool found = false;
0208             foreach (const QString &it2, list) {
0209                 if (it2.endsWith(it)) {
0210                     found = true;
0211                     break;
0212                 }
0213             }
0214             if (!found) {
0215                 return false;
0216             }
0217         }
0218     } else {
0219         foreach (const QString &it, test) {
0220             bool found = false;
0221             foreach (const QString &it2, list) {
0222                 if (it2.contains(it)) {
0223                     found = true;
0224                     break;
0225                 }
0226             }
0227             if (!found) {
0228                 return false;
0229             }
0230         }
0231     }
0232 
0233     return true;
0234 }
0235 
0236 /**
0237  * New functions for Entry doing direct display.
0238  *
0239  * Creates a link for the given @p entryString.
0240  */
0241 inline QString Entry::makeLink(const QString &entryString) const
0242 {
0243     return QStringLiteral("<a href=\"%1\">%1</a>").arg(entryString);
0244 }
0245 
0246 bool Entry::matchesQuery(const DictQuery &query) const
0247 {
0248     if (!query.getWord().isEmpty()) {
0249         if (query.getMatchType() == DictQuery::Exact && this->getWord() != query.getWord()) {
0250             return false;
0251         }
0252         if (query.getMatchType() == DictQuery::Beginning && !this->getWord().startsWith(query.getWord())) {
0253             return false;
0254         }
0255         if (query.getMatchType() == DictQuery::Ending && !this->getWord().endsWith(query.getWord())) {
0256             return false;
0257         }
0258         if (query.getMatchType() == DictQuery::Anywhere && !this->getWord().contains(query.getWord())) {
0259             return false;
0260         }
0261     }
0262 
0263     if (!query.getPronunciation().isEmpty() && !getReadings().isEmpty()) {
0264         if (!listMatch(Readings, query.getPronunciation().split(DictQuery::mainDelimiter), query.getMatchType())) {
0265             return false;
0266         }
0267     }
0268 
0269     if (!query.getPronunciation().isEmpty() && getReadings().isEmpty() && !getWord().isEmpty()) {
0270         switch (query.getMatchType()) {
0271         case DictQuery::Exact:
0272             if (getWord() != query.getPronunciation()) {
0273                 return false;
0274             }
0275             break;
0276         case DictQuery::Beginning:
0277             if (!getWord().startsWith(query.getPronunciation())) {
0278                 return false;
0279             }
0280             break;
0281         case DictQuery::Ending:
0282             if (!getWord().endsWith(query.getPronunciation())) {
0283                 return false;
0284             } // fallthrough
0285         case DictQuery::Anywhere:
0286             if (!getWord().contains(query.getPronunciation())) {
0287                 return false;
0288             }
0289             break;
0290         }
0291     }
0292 
0293     if (!query.getMeaning().isEmpty()) {
0294         if (!listMatch(Meanings.join(QLatin1Char(' ')).toLower().split(' '),
0295                        query.getMeaning().toLower().split(DictQuery::mainDelimiter),
0296                        query.getMatchType())) {
0297             return false;
0298         }
0299     }
0300 
0301     QList<QString> propList = query.listPropertyKeys();
0302     foreach (const QString &key, propList) {
0303         if (!extendedItemCheck(key, query.getProperty(key))) {
0304             return false;
0305         }
0306     }
0307 
0308     return true;
0309 }
0310 
0311 /**
0312  * Main switching function for displaying to the user
0313  */
0314 QString Entry::toHTML() const
0315 {
0316     return QStringLiteral("<div class=\"Entry\">%1%2%3</div>").arg(HTMLWord()).arg(HTMLReadings()).arg(HTMLMeanings());
0317 }
0318 
0319 inline QString Entry::toKVTML() const
0320 {
0321     /*
0322 <e m="1" s="1">
0323        <o width="414" l="en" q="t">(eh,) excuse me</o>
0324        <t width="417" l="jp" q="o">(あのう、) すみません </t>
0325 </e>
0326 */
0327     // TODO: en should not necessarily be the language here.
0328     return QString(
0329                "<e>\n<o l=\"en\">%1</o>\n"
0330                "<t l=\"jp-kanji\">%2</t>\n"
0331                "<t l=\"jp-kana\">%3</t></e>\n\n")
0332         .arg(getMeanings())
0333         .arg(getWord())
0334         .arg(getReadings());
0335 }
0336 
0337 /**
0338  * This method should return the entry object in a simple QString format
0339  * Brief form should be usable in quick summaries, for example
0340  * Verbose form might be used to save a complete list of entries to a file, for example.
0341  */
0342 QString Entry::toString() const
0343 {
0344     return QStringLiteral("%1 (%2) %3").arg(Word).arg(getReadings()).arg(getMeanings());
0345 }
0346 
0347 /**
0348  * This version of sort only sorts dictionaries...
0349  * This is a replacement for an operator\< function... so we return true if
0350  * "this" should show up first on the list.
0351  */
0352 bool Entry::sort(const Entry &that, const QStringList &dictOrder, const QStringList &fields) const
0353 {
0354     if (this->sourceDict != that.sourceDict) {
0355         foreach (const QString &dict, dictOrder) {
0356             if (dict == that.sourceDict) {
0357                 return false;
0358             }
0359             if (dict == this->sourceDict) {
0360                 return true;
0361             }
0362         }
0363     } else {
0364         foreach (const QString &field, fields) {
0365             if (field == QLatin1String("Word/Kanji")) {
0366                 return this->getWord() < that.getWord();
0367             } else if (field == QLatin1String("Meaning")) {
0368                 return listMatch(that.getMeaningsList(), this->getMeaningsList(), DictQuery::Exact)
0369                     && (that.getMeaningsList().count() != this->getMeaningsList().count());
0370             } else if (field == QLatin1String("Reading")) {
0371                 return listMatch(that.getReadingsList(), this->getReadingsList(), DictQuery::Exact)
0372                     && (that.getReadingsList().count() != this->getReadingsList().count());
0373             } else {
0374                 const QString thisOne = this->getExtendedInfoItem(field);
0375                 const QString thatOne = that.getExtendedInfoItem(field);
0376                 // Only sort by this field if the values differ, otherwise move to the next field
0377                 if (thisOne != thatOne) {
0378                     // If the second item does not have this field, sort this one first
0379                     if (thatOne.isEmpty()) {
0380                         return true;
0381                     }
0382                     // If we don't have this field, sort "this" to second
0383                     if (thisOne.isEmpty()) {
0384                         return false;
0385                     }
0386                     // Otherwise, send it to a virtual function (to allow dictionaries to override sorting)
0387                     return this->sortByField(that, field);
0388                 }
0389             }
0390         }
0391     }
0392     return false; // If we reach here, they match as much as possible
0393 }
0394 
0395 bool Entry::sortByField(const Entry &that, const QString &field) const
0396 {
0397     return this->getExtendedInfoItem(field) < that.getExtendedInfoItem(field);
0398 }