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-2021 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 
44 //Change it once the format changed to make all systems rebuild their caches
45 #define CACHE_FORMAT "3"
46 
49  : CSwordModuleInfo(module, backend, Lexicon)
50 {
51  /**
52  See if module keys are consistent with Strong's references
53  and determine if keys start with "G" or "H" and the number
54  of digits in the keys.
55  */
56  module.setPosition(sword::TOP);
57  module.increment();
58  QString key = QString::fromUtf8(module.getKeyText());
59  QRegularExpression rx1(QStringLiteral("^[GH][0-9]+$"));
60  if (rx1.match(key).hasMatch()) {
61  m_hasStrongsKeys = true;
63  m_strongsDigitsLength = key.length() - 1;
64  } else {
65  QRegularExpression rx2(QStringLiteral("^[0-9]+$"));
66  if (rx2.match(key).hasMatch()) {
67  m_hasStrongsKeys = true;
68  m_strongsDigitsLength = key.length();
69  }
70  }
71 }
72 
73 const QStringList &CSwordLexiconModuleInfo::entries() const {
74  namespace DU = util::directory;
75 
76  // If cache is ok, just return it:
77  if (!m_entries.empty()) {
78  return m_entries;
79  }
80 
81  QFile cacheFile(QStringLiteral("%1/%2")
82  .arg(DU::getUserCacheDir().absolutePath(), name()));
83 
84  /*
85  * Try the module's cache
86  */
87  if (cacheFile.open(QIODevice::ReadOnly)) {
88  QDataStream s(&cacheFile);
89  QString ModuleVersion, CacheVersion, QDataStreamVersion;
90  s >> ModuleVersion;
91  s >> CacheVersion;
92  s >> QDataStreamVersion;
93 
94  qDebug() << "Lexicon cache metadata"
95  << "Name" << name()
96  << "ModuleVersion" << ModuleVersion
97  << "CacheVersion" << CacheVersion
98  << "QDataStreamVersion" << QDataStreamVersion;
99 
100  // Check if cache is valid
102  && CacheVersion == CACHE_FORMAT
103  && QDataStreamVersion == QString::number(s.version())) {
104  s >> m_entries;
105 
106  cacheFile.close();
107  qDebug() << "Read" << m_entries.count() << "entries from lexicon cache for module" << name();
108  return m_entries;
109  }
110 
111  cacheFile.close();
112  }
113 
114  /*
115  * Ok, no cache or invalid.
116  */
117  qDebug() << "Read all entries of lexicon" << name();
118 
119  auto & m = swordModule();
120  m.setSkipConsecutiveLinks(true);
121  m.setPosition(sword::TOP);
122  snap(); //snap to top entry
123 
124  if (isUnicode()) {
125  do {
126  m_entries.append(QString::fromUtf8(m.getKeyText()));
127  m.increment();
128  } while (!m.popError());
129  } else {
130  do {
131  m_entries.append(util::cp1252::toUnicode(m.getKeyText()));
132  m.increment();
133  } while (!m.popError());
134  }
135 
136  m.setPosition(sword::TOP); // back to the first entry
137  m.setSkipConsecutiveLinks(false);
138 
139  /// \todo Document why the following code is here:
140  if (!m_entries.empty() && m_entries.front().simplified().isEmpty())
141  m_entries.pop_front();
142 
143  qDebug() << "Writing cache file for lexicon module" << name();
144 
145  if (m_entries.count()) {
146  //create cache
147  if (cacheFile.open( QIODevice::WriteOnly )) {
148  QDataStream s(&cacheFile);
149  s << config(CSwordModuleInfo::ModuleVersion) //store module version
150  << QString(CACHE_FORMAT) //store BT version -- format may change
151  << QString::number(s.version()) //store QDataStream version -- format may change
152  << m_entries;
153  cacheFile.close();
154  }
155  }
156 
157  return m_entries;
158 }
159 
161 { return swordModule().getRawEntry(); }
162 
164  return m_hasStrongsKeys;
165 }
166 
167 QString CSwordLexiconModuleInfo::normalizeStrongsKey(const QString &key) const {
168  if (auto const match =
169  QRegularExpression(QStringLiteral("^([GH]?)0*([0-9]+?)$"))
170  .match(key);
171  match.hasMatch())
172  {
173  auto const lang = match.capturedView(1);
174  auto const digits = match.capturedView(2);
175 
176  qsizetype size = (digits.size() > m_strongsDigitsLength)
177  ? digits.size()
179  auto numPaddingZeroesRequired = size - digits.size();
181  size += lang.size();
182  QString normalized;
183  normalized.reserve(size);
185  normalized.append(lang);
186  while (numPaddingZeroesRequired--)
187  normalized.append('0');
188  normalized.append(digits);
189  return normalized;
190  }
191  return key;
192 }
193 
195 { return new CSwordLDKey(swordModule().getKey(), this); }
The backend layer main class, a backend implementation of Sword.
Definition: cswordbackend.h:56
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
#define CACHE_FORMAT
std::unique_ptr< CSwordBackend > backend(sword::InstallSource const &is)
QString toUnicode(QByteArray const &data)
Definition: cp1252.cpp:40
const QDir & getUserCacheDir()
Definition: directory.cpp:346