BibleTime
cswordlexiconmoduleinfo.cpp
Go to the documentation of this file.
1/*********
2*
3* In the name of the Father, and of the Son, and of the Holy Spirit.
4*
5* This file is part of BibleTime's source code, https://bibletime.info/
6*
7* Copyright 1999-2025 by the BibleTime developers.
8* The BibleTime source code is licensed under the GNU General Public License
9* version 2.0.
10*
11**********/
12
14
15#include <QChar>
16#include <QDataStream>
17#include <QDebug>
18#include <QDir>
19#include <QFile>
20#include <QIODevice>
21#include <QRegularExpression>
22#include <QRegularExpressionMatch>
23#include "../../util/cp1252.h"
24#include "../../util/directory.h"
25#include "../keys/cswordldkey.h"
26
27// Sword includes:
28#pragma GCC diagnostic push
29#pragma GCC diagnostic ignored "-Wextra-semi"
30#pragma GCC diagnostic ignored "-Wsuggest-override"
31#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
32#ifdef __clang__
33#pragma clang diagnostic push
34#pragma clang diagnostic ignored "-Wsuggest-destructor-override"
35#endif
36#include <swkey.h>
37#include <swmodule.h>
38#ifdef __clang__
39#pragma clang diagnostic pop
40#endif
41#pragma GCC diagnostic pop
42
43
44namespace {
45
46// Change it once the format changed to make all systems rebuild their caches
47constexpr quint8 const cacheFormat = 4;
48
49} // Anonymous namespace
50
52 CSwordBackend & backend)
53 : CSwordModuleInfo(module, backend, Lexicon)
54{
55 /**
56 See if module keys are consistent with Strong's references
57 and determine if keys start with "G" or "H" and the number
58 of digits in the keys.
59 */
60 module.setPosition(sword::TOP);
61 module.increment();
62 QString key = QString::fromUtf8(module.getKeyText());
63 QRegularExpression rx1(QStringLiteral("^[GH][0-9]+$"));
64 if (rx1.match(key).hasMatch()) {
65 m_hasStrongsKeys = true;
67 m_strongsDigitsLength = key.length() - 1;
68 } else {
69 QRegularExpression rx2(QStringLiteral("^[0-9]+$"));
70 if (rx2.match(key).hasMatch()) {
71 m_hasStrongsKeys = true;
72 m_strongsDigitsLength = key.length();
73 }
74 }
75}
76
77const QStringList &CSwordLexiconModuleInfo::entries() const {
78 namespace DU = util::directory;
79
80 // If cache is ok, just return it:
81 if (!m_entries.empty()) {
82 return m_entries;
83 }
84
85 QFile cacheFile(QStringLiteral("%1/%2")
86 .arg(DU::getUserCacheDir().absolutePath(), name()));
87
88 QString const moduleVersion = config(CSwordModuleInfo::ModuleVersion);
89
90 auto const writeCache = [this,&cacheFile,&moduleVersion]{
91 qDebug() << "Writing cache file" << cacheFile.fileName();
92 if (cacheFile.open(QIODevice::WriteOnly)) {
93 QDataStream s(&cacheFile);
94 s.setVersion(QDataStream::Qt_5_15);
95 s << moduleVersion
96 << QString::number(cacheFormat)
97 << QString::number(QDataStream::Qt_DefaultCompiledVersion);
98 s.setVersion(QDataStream::Qt_DefaultCompiledVersion);
99 s << m_entries;
100 cacheFile.close();
101
102 if (s.status() == QDataStream::Ok) {
103 qDebug() << "Cache file written successfully!";
104 } else {
105 qDebug() << "Failed to write cache file! Attempting to remove.";
106 if (cacheFile.remove()) {
107 qDebug() << "Removed potentially corrupt cache.";
108 } else {
109 qDebug() << "Failed to remove potentially corrupt cache!";
110 }
111 }
112 } else {
113 qDebug() << "Failed to open cache file for writing!";
114 }
115 };
116
117 /*
118 * Try the module's cache
119 */
120 if (cacheFile.open(QIODevice::ReadOnly)) {
121 qDebug() << "Reading lexicon cache for module" << name() << "...";
122 QDataStream s(&cacheFile);
123 s.setVersion(QDataStream::Qt_5_15);
124 QString str;
125 s >> str;
126 qDebug() << " module version:" << str;
127 if (str == moduleVersion) {
128 s >> str;
129 qDebug() << " cache version:" << str;
130 bool isNumber = false;
131 auto const cacheVersion = str.toInt(&isNumber);
132 if (isNumber && cacheVersion <= cacheFormat) {
133 s >> str;
134 qDebug() << " QDataStream version:" << str;
135 auto const dataStreamVersion = str.toInt(&isNumber);
136 if (isNumber
137 && dataStreamVersion
138 <= QDataStream::Qt_DefaultCompiledVersion)
139 {
140 auto const resetVersion = cacheVersion >= 4;
141 qDebug() << " QDataStream version reset:" << resetVersion;
142 if (resetVersion)
143 s.setVersion(dataStreamVersion);
144 s >> m_entries;
145 if (s.status() == QDataStream::Ok) {
146 qDebug() << " entries read:" << m_entries.size();
147 cacheFile.close();
148
149 // Update cache format:
150 if (cacheVersion != cacheFormat)
151 writeCache();
152
153 return m_entries;
154 }
155 m_entries.clear();
156 }
157 }
158 }
159
160 cacheFile.close();
161 }
162
163 /*
164 * Ok, no cache or invalid.
165 */
166 qDebug() << "Read all entries of lexicon" << name();
167
168 auto & m = swordModule();
169 m.setSkipConsecutiveLinks(true);
170 m.setPosition(sword::TOP);
171 snap(); //snap to top entry
172
173 if (isUnicode()) {
174 do {
175 m_entries.append(QString::fromUtf8(m.getKeyText()));
176 m.increment();
177 } while (!m.popError());
178 } else {
179 do {
180 m_entries.append(util::cp1252::toUnicode(m.getKeyText()));
181 m.increment();
182 } while (!m.popError());
183 }
184
185 m.setPosition(sword::TOP); // back to the first entry
186 m.setSkipConsecutiveLinks(false);
187
188 /// \todo Document why the following code is here:
189 if (!m_entries.empty() && m_entries.front().simplified().isEmpty())
190 m_entries.pop_front();
191
192 writeCache();
193
194 return m_entries;
195}
196
198{ return swordModule().getRawEntry(); }
199
203
204QString CSwordLexiconModuleInfo::normalizeStrongsKey(const QString &key) const {
205 if (auto const match =
206 QRegularExpression(QStringLiteral("^([GH]?)0*([0-9]+?)$"))
207 .match(key);
208 match.hasMatch())
209 {
210 auto const lang = match.capturedView(1);
211 auto const digits = match.capturedView(2);
212
213 qsizetype size = (digits.size() > m_strongsDigitsLength)
214 ? digits.size()
216 auto numPaddingZeroesRequired = size - digits.size();
218 size += lang.size();
219 QString normalized;
220 normalized.reserve(size);
222 normalized.append(lang);
223 while (numPaddingZeroesRequired--)
224 normalized.append('0');
225 normalized.append(digits);
226 return normalized;
227 }
228 return key;
229}
230
232{ return new CSwordLDKey(swordModule().getKey(), this); }
The backend layer main class, a backend implementation of Sword.
CSwordLexiconModuleInfo(sword::SWModule &module, CSwordBackend &backend)
QString normalizeStrongsKey(const QString &key) const
CSwordKey * createKey() const final override
const QStringList & entries() const
bool snap() const final override
QString config(const CSwordModuleInfo::ConfigEntry entry) const
bool isUnicode() const noexcept
QString const & name() const
sword::SWModule & swordModule() const
QString toUnicode(QByteArray const &data)
Definition cp1252.cpp:23