File indexing completed on 2024-12-08 03:29:07

0001 /*
0002     This file is part of Kiten, a KDE Japanese Reference Tool
0003     SPDX-FileCopyrightText: 2006 Joseph Kerian <jkerian@gmail.com>
0004     SPDX-FileCopyrightText: 2011 Daniel E. Moctezuma <democtezuma@gmail.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "dictionarymanager.h"
0010 
0011 #include "dictfile.h"
0012 #include "dictionarypreferencedialog.h"
0013 #include "dictquery.h"
0014 #include "entry.h"
0015 #include "entrylist.h"
0016 #include "kitenmacros.h"
0017 
0018 #include <KConfig>
0019 #include <KConfigSkeleton>
0020 
0021 #include <QString>
0022 
0023 /* Includes to handle various types of dictionaries
0024 IMPORTANT: To add a dictionary type, add the header file here and add it to the
0025  if statement under addDictionary() */
0026 #include "DictEdict/dictfileedict.h"
0027 #include "DictKanjidic/dictfilekanjidic.h"
0028 
0029 using namespace Qt::StringLiterals;
0030 
0031 class DictionaryManager::Private
0032 {
0033 public:
0034     /**
0035      * List of dictionaries, indexed by name
0036      */
0037     QHash<QString, DictFile *> dictManagers;
0038 };
0039 
0040 #if 0
0041 class debug_entry : public Entry
0042 {
0043   public:
0044     debug_entry(QString word) : Entry( QString( "libkiten" ), word
0045                               , QStringList(), QStringList() ), count( counter++ )
0046                               { }
0047     virtual Entry * clone() const { return new debug_entry( *this ); }
0048     virtual bool loadEntry( const QString& ) { return false; }
0049     virtual QString dumpEntry() const { return ""; }
0050     virtual bool sort( const debug_entry &that, const QStringList &dicts,
0051                     const QStringList &fields )
0052                     { return this->count < that.count; }
0053 
0054     int count;
0055     static int counter;
0056 };
0057 int debug_entry::counter = 0;
0058 #endif
0059 
0060 /**
0061  * The constructor. Set autodelete on our dictionary list
0062  */
0063 DictionaryManager::DictionaryManager()
0064     : d(new Private)
0065 {
0066 }
0067 
0068 /**
0069  * Delete everything in our hash
0070  */
0071 DictionaryManager::~DictionaryManager()
0072 {
0073     {
0074         QMutableHashIterator<QString, DictFile *> it(d->dictManagers);
0075         while (it.hasNext()) {
0076             it.next();
0077             delete it.value();
0078             it.remove();
0079         }
0080     }
0081 
0082     delete d;
0083 }
0084 
0085 /**
0086  * Given a named Dict file/name/type... create and add the object if it
0087  * seems to work properly on creation.
0088  */
0089 bool DictionaryManager::addDictionary(const QString &file, const QString &name, const QString &type)
0090 {
0091     if (d->dictManagers.contains(name)) // This name already exists in the list!
0092     {
0093         return false;
0094     }
0095 
0096     DictFile *newDict = makeDictFile(type);
0097     if (newDict == nullptr) {
0098         return false;
0099     }
0100 
0101     if (!newDict->loadDictionary(file, name)) {
0102         qDebug() << "Dictionary load FAILED: " << newDict->getName();
0103         delete newDict;
0104         return false;
0105     }
0106 
0107     qDebug() << "Dictionary Loaded : " << newDict->getName();
0108     d->dictManagers.insert(name, newDict);
0109     return true;
0110 }
0111 
0112 /**
0113  * Examine the DictQuery and farm out the search to the specialized dict
0114  * managers. Note that a global search limit will probably be implemented
0115  * either here or in the DictFile implementations... probably both
0116  *
0117  * @param query the query, see DictQuery documentation
0118  */
0119 EntryList *DictionaryManager::doSearch(const DictQuery &query) const
0120 {
0121     auto ret = new EntryList();
0122 #if 0
0123   if( query.getMeaning() == "(libkiten)" )
0124   {
0125     ret->append( new debug_entry( "Summary of libkiten data" ) );
0126     foreach( const QString &dict, listDictionaries() )
0127     {
0128       ret->append( new debug_entry( dict ) );
0129     }
0130     return ret;
0131   }
0132 #endif
0133 
0134     // There are two basic modes.... one in which the query
0135     // specifies the dictionary list, one in which it does not
0136     QStringList dictsFromQuery = query.getDictionaries();
0137     if (dictsFromQuery.isEmpty()) {
0138         // None specified, search all
0139         for (DictFile *it : d->dictManagers) {
0140             qDebug() << "Searching in " << it->getName() << "dictionary.";
0141             EntryList *temp = it->doSearch(query);
0142             if (temp) {
0143                 ret->appendList(temp);
0144             }
0145             delete temp;
0146         }
0147     } else {
0148         for (const QString &target : dictsFromQuery) {
0149             DictFile *newestFound = d->dictManagers.find(target).value();
0150             if (newestFound != nullptr) {
0151                 EntryList *temp = newestFound->doSearch(query);
0152                 if (temp) {
0153                     ret->appendList(temp);
0154                 }
0155                 delete temp;
0156             }
0157         }
0158     }
0159 
0160     ret->setQuery(query); // Store the query for later use.
0161     qDebug() << "From query: '" << query.toString() << "' Found " << ret->count() << " results";
0162     qDebug() << "Incoming match type: " << query.getMatchType() << " Outgoing: " << ret->getQuery().getMatchType();
0163     return ret;
0164 }
0165 
0166 /**
0167  * For this case, we let polymorphism do most of the work. We assume that the user wants
0168  * to pare down the results, so we let the individual entry matching methods run over the
0169  * new query and accept (and copy) any of those that pass.
0170  */
0171 EntryList *DictionaryManager::doSearchInList(const DictQuery &query, const EntryList *list) const
0172 {
0173     auto ret = new EntryList();
0174 
0175     for (Entry *it : *list) {
0176         if (it->matchesQuery(query)) {
0177             Entry *x = it->clone();
0178             ret->append(x);
0179         }
0180     }
0181 
0182     ret->setQuery(query + list->getQuery());
0183     return ret;
0184 }
0185 
0186 QMap<QString, QString> DictionaryManager::generateExtendedFieldsList()
0187 {
0188     QMap<QString, QString> result;
0189     const QStringList dictTypes = listDictFileTypes();
0190     for (const QString &dictType : dictTypes) {
0191         DictFile *tempDictFile = makeDictFile(dictType);
0192         QMap<QString, QString> tempList = tempDictFile->getSearchableAttributes();
0193         QMap<QString, QString>::const_iterator it = tempList.constBegin();
0194         while (it != tempList.constEnd()) {
0195             if (!result.contains(it.key())) {
0196                 result.insert(it.key(), it.value());
0197             }
0198             ++it;
0199         }
0200         delete tempDictFile;
0201     }
0202 
0203     return result;
0204 }
0205 
0206 QMap<QString, DictionaryPreferenceDialog *> DictionaryManager::generatePreferenceDialogs(KConfigSkeleton *config, QWidget *parent)
0207 {
0208     QMap<QString, DictionaryPreferenceDialog *> result;
0209     const QStringList dictTypes = listDictFileTypes();
0210     for (const QString &dictType : dictTypes) {
0211         DictFile *tempDictFile = makeDictFile(dictType);
0212         DictionaryPreferenceDialog *newDialog = tempDictFile->preferencesWidget(config, parent);
0213 
0214         if (newDialog == nullptr) {
0215             delete tempDictFile;
0216             continue;
0217         }
0218 
0219         result.insert(dictType, newDialog);
0220         delete tempDictFile;
0221     }
0222 
0223     return result;
0224 }
0225 
0226 /**
0227  * Return a list of the dictionaries by their name (our key)
0228  * Note that this dictionary name does not necessarily have to have anything
0229  * to do with the actual dictionary name...
0230  */
0231 QStringList DictionaryManager::listDictionaries() const
0232 {
0233     QStringList ret;
0234     for (DictFile *it : d->dictManagers) {
0235         ret.append(it->getName());
0236     }
0237 
0238     return ret;
0239 }
0240 
0241 /**
0242  * IMPORTANT: To add a dictionary type, you have to manually add the creation
0243  * step here, the prev method, and \#include your header file above. If you have
0244  * fully implemented the interface in DictionaryManager.h, It should simply work.
0245  */
0246 QStringList DictionaryManager::listDictFileTypes()
0247 {
0248     QStringList list;
0249     list.append(EDICT);
0250     list.append(KANJIDIC);
0251 
0252     // Add your dictionary type here!
0253 
0254     return list;
0255 }
0256 
0257 /**
0258  * Return the dictionary type and file used by a named dictionary.
0259  * returns a pair of empty QStrings if you specify an invalid name
0260  *
0261  * @param name the name of the dictionary, as given in the addDictionary method
0262  */
0263 QPair<QString, QString> DictionaryManager::listDictionaryInfo(const QString &name) const
0264 {
0265     if (!d->dictManagers.contains(name)) // This name not in list!
0266     {
0267         return qMakePair(QString(), QString());
0268     }
0269 
0270     return qMakePair(d->dictManagers[name]->getName(), d->dictManagers[name]->getFile());
0271 }
0272 
0273 /**
0274  * Return a list of the names of each dictionary of a given type.
0275  *
0276  * @param type the type of the dictionary we want a list of
0277  */
0278 QStringList DictionaryManager::listDictionariesOfType(const QString &type) const
0279 {
0280     QStringList ret;
0281     QHash<QString, DictFile *>::const_iterator it = d->dictManagers.constBegin();
0282     while (it != d->dictManagers.constEnd()) {
0283         if (it.value()->getType() == type) {
0284             ret.append(it.key());
0285         }
0286 
0287         ++it;
0288     }
0289 
0290     return ret;
0291 }
0292 
0293 /**
0294  * Load preference settings for a particular dictionary
0295  */
0296 void DictionaryManager::loadDictSettings(const QString &dictName, KConfigSkeleton *config)
0297 {
0298     DictFile *dict = this->makeDictFile(dictName);
0299     if (dict != nullptr) {
0300         config->setCurrentGroup("dicts_"_L1 + dictName.toLower());
0301         dict->loadSettings(config);
0302         delete dict;
0303     }
0304 }
0305 
0306 void DictionaryManager::loadSettings(const KConfig &config)
0307 {
0308     Q_UNUSED(config)
0309     // TODO
0310 }
0311 
0312 /**
0313  * IMPORTANT: To add a dictionary type, you have to manually add the creation
0314  * step here, the next method, and \#include your header file above. If you have
0315  * fully implemented the interface in dictionarymanager.h, It should simply work.
0316  */
0317 DictFile *DictionaryManager::makeDictFile(const QString &type)
0318 {
0319     if (type == EDICT) {
0320         return new DictFileEdict();
0321     } else if (type == KANJIDIC) {
0322         return new DictFileKanjidic();
0323     }
0324 
0325     // Add new dictionary types here!!!
0326 
0327     return nullptr;
0328 }
0329 
0330 void DictionaryManager::removeAllDictionaries()
0331 {
0332     qDeleteAll(d->dictManagers);
0333     d->dictManagers.clear();
0334 }
0335 
0336 /**
0337  * Remove a dictionary from the list, and delete the dictionary object
0338  * (it should close files, deallocate memory, etc).
0339  *
0340  * @param name the name of the dictionary, as given in the addDictionary method
0341  */
0342 bool DictionaryManager::removeDictionary(const QString &name)
0343 {
0344     DictFile *file = d->dictManagers.take(name);
0345     delete file;
0346     return true;
0347 }