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 }