1 /******************************************************************************
2 *
3 * versificationmgr.cpp - implementation of class VersificationMgr used
4 * for managing versification systems
5 *
6 * $Id: versificationmgr.cpp 3515 2017-11-01 11:38:09Z scribe $
7 *
8 * Copyright 2008-2013 CrossWire Bible Society (http://www.crosswire.org)
9 * CrossWire Bible Society
10 * P. O. Box 2528
11 * Tempe, AZ 85280-2528
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation version 2.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 */
23
24 #include <versificationmgr.h>
25 #include <vector>
26 #include <map>
27 #include <treekey.h>
28 #include <canon.h> // KJV internal versification system
29 #include <swlog.h>
30 #include <algorithm>
31
32 #include <canon_null.h> // null v11n system
33
34 #include <canon_leningrad.h> // Leningrad Codex (WLC) v11n system
35 #include <canon_mt.h> // Masoretic Text (MT) v11n system
36 #include <canon_kjva.h> // KJV + Apocrypha v11n system
37 #include <canon_nrsv.h> // NRSV v11n system
38 #include <canon_nrsva.h> // NRSV + Apocrypha v11n system
39 #include <canon_synodal.h> // Russian Synodal v11n system
40 #include <canon_synodalprot.h> // Russian Synodal v11n system
41 #include <canon_vulg.h> // Vulgate v11n system
42 #include <canon_german.h> // German v11n system
43 #include <canon_luther.h> // Luther v11n system
44 #include <canon_catholic.h> // Catholic v11n system (10 chapter Esther)
45 #include <canon_catholic2.h> // Catholic2 v11n system (16 chapter Esther)
46 #include <canon_lxx.h> // General LXX v11n system (includes GNT, as used in Orthodox Bibles)
47 #include <canon_orthodox.h> // Orthodox v11n system as used in Orthodox Bibles
48 #include <canon_segond.h> // French v11n system as used by Segond Bibles and its derivatives
49 #include <canon_calvin.h> // French v11n system
50 #include <canon_darbyfr.h> // French v11n system based on John Darby's French translation
51
52 using std::vector;
53 using std::map;
54 using std::distance;
55 using std::lower_bound;
56
57
58 SWORD_NAMESPACE_START
59
60
getSystemVersificationMgr()61 VersificationMgr *VersificationMgr::getSystemVersificationMgr() {
62 if (!systemVersificationMgr) {
63 systemVersificationMgr = new VersificationMgr();
64 systemVersificationMgr->registerVersificationSystem("KJV", otbooks, ntbooks, vm);
65 systemVersificationMgr->registerVersificationSystem("Leningrad", otbooks_leningrad, ntbooks_null, vm_leningrad);
66 systemVersificationMgr->registerVersificationSystem("MT", otbooks_mt, ntbooks_null, vm_mt);
67 systemVersificationMgr->registerVersificationSystem("KJVA", otbooks_kjva, ntbooks, vm_kjva);
68 systemVersificationMgr->registerVersificationSystem("NRSV", otbooks, ntbooks, vm_nrsv, mappings_nrsv);
69 systemVersificationMgr->registerVersificationSystem("NRSVA", otbooks_nrsva, ntbooks, vm_nrsva);
70 systemVersificationMgr->registerVersificationSystem("Synodal", otbooks_synodal, ntbooks_synodal, vm_synodal, mappings_synodal);
71 systemVersificationMgr->registerVersificationSystem("SynodalProt", otbooks_synodalProt, ntbooks_synodal, vm_synodalProt);
72 systemVersificationMgr->registerVersificationSystem("Vulg", otbooks_vulg, ntbooks_vulg, vm_vulg, mappings_vulg);
73 systemVersificationMgr->registerVersificationSystem("German", otbooks_german, ntbooks, vm_german);
74 systemVersificationMgr->registerVersificationSystem("Luther", otbooks_luther, ntbooks_luther, vm_luther);
75 systemVersificationMgr->registerVersificationSystem("Catholic", otbooks_catholic, ntbooks, vm_catholic);
76 systemVersificationMgr->registerVersificationSystem("Catholic2", otbooks_catholic2, ntbooks, vm_catholic2);
77 systemVersificationMgr->registerVersificationSystem("LXX", otbooks_lxx, ntbooks, vm_lxx);
78 systemVersificationMgr->registerVersificationSystem("Orthodox", otbooks_orthodox, ntbooks, vm_orthodox);
79 systemVersificationMgr->registerVersificationSystem("Calvin", otbooks, ntbooks, vm_calvin, mappings_calvin);
80 systemVersificationMgr->registerVersificationSystem("DarbyFr", otbooks, ntbooks, vm_darbyfr, mappings_darbyfr);
81 systemVersificationMgr->registerVersificationSystem("Segond", otbooks, ntbooks, vm_segond, mappings_segond);
82 }
83 return systemVersificationMgr;
84 }
85
86
87 class VersificationMgr::System::Private {
88 public:
89 /** Array[chapmax] of maximum verses in chapters */
90 vector<Book> books;
91 map<SWBuf, int> osisLookup;
92 /** General mapping rule is that first verse of every chapter corresponds first
93 verse of another chapter in default intermediate canon(kjva), so mapping data
94 contains expections. Intermediate canon could not contain corresponding data.
95
96 pointers on uchar[7]: 1 value - book id 1-based, ot+nt, 2-4 map to, 5-7 map
97 from (chap,verse from, verse to if greater then "verse from")
98
99 TODO what if book name in one v11n differs from cannon
100 special section in mapping for book transformation
101 */
102 typedef vector<const unsigned char*> mapping;
103 vector<mapping> mappings;
104 vector<const char*> mappingsExtraBooks;
105
Private()106 Private() {
107 }
Private(const VersificationMgr::System::Private & other)108 Private(const VersificationMgr::System::Private &other) {
109 books = other.books;
110 osisLookup = other.osisLookup;
111 }
operator =(const VersificationMgr::System::Private & other)112 VersificationMgr::System::Private &operator =(const VersificationMgr::System::Private &other) {
113 books = other.books;
114 osisLookup = other.osisLookup;
115 return *this;
116 }
117 };
118
119
120 class VersificationMgr::Book::Private {
121 friend struct BookOffsetLess;
122 public:
123 /** Array[chapmax] of maximum verses in chapters */
124 vector<int> verseMax;
125 vector<long> offsetPrecomputed;
126
Private()127 Private() {
128 verseMax.clear();
129 }
Private(const VersificationMgr::Book::Private & other)130 Private(const VersificationMgr::Book::Private &other) {
131 verseMax.clear();
132 verseMax = other.verseMax;
133 offsetPrecomputed = other.offsetPrecomputed;
134 }
operator =(const VersificationMgr::Book::Private & other)135 VersificationMgr::Book::Private &operator =(const VersificationMgr::Book::Private &other) {
136 verseMax.clear();
137 int s = (int)other.verseMax.size();
138 if (s) verseMax = other.verseMax;
139 offsetPrecomputed = other.offsetPrecomputed;
140 return *this;
141 }
142 };
143
144
145 struct BookOffsetLess {
operator ()BookOffsetLess146 bool operator() (const VersificationMgr::Book &o1, const VersificationMgr::Book &o2) const { return o1.p->offsetPrecomputed[0] < o2.p->offsetPrecomputed[0]; }
operator ()BookOffsetLess147 bool operator() (const long &o1, const VersificationMgr::Book &o2) const { return o1 < o2.p->offsetPrecomputed[0]; }
operator ()BookOffsetLess148 bool operator() (const VersificationMgr::Book &o1, const long &o2) const { return o1.p->offsetPrecomputed[0] < o2; }
operator ()BookOffsetLess149 bool operator() (const long &o1, const long &o2) const { return o1 < o2; }
150 };
151
152
init()153 void VersificationMgr::Book::init() {
154 p = new Private();
155 }
156
157
init()158 void VersificationMgr::System::init() {
159 p = new Private();
160 BMAX[0] = 0;
161 BMAX[1] = 0;
162 ntStartOffset = 0;
163 }
164
165
System(const System & other)166 VersificationMgr::System::System(const System &other) {
167 init();
168 name = other.name;
169 BMAX[0] = other.BMAX[0];
170 BMAX[1] = other.BMAX[1];
171 (*p) = *(other.p);
172 ntStartOffset = other.ntStartOffset;
173 }
174
175
operator =(const System & other)176 VersificationMgr::System &VersificationMgr::System::operator =(const System &other) {
177 name = other.name;
178 BMAX[0] = other.BMAX[0];
179 BMAX[1] = other.BMAX[1];
180 (*p) = *(other.p);
181 ntStartOffset = other.ntStartOffset;
182 return *this;
183 }
184
185
~System()186 VersificationMgr::System::~System() {
187 delete p;
188 }
189
190
getBook(int number) const191 const VersificationMgr::Book *VersificationMgr::System::getBook(int number) const {
192 return (number < (signed int)p->books.size()) ? &(p->books[number]) : 0;
193 }
194
195
getBookNumberByOSISName(const char * bookName) const196 int VersificationMgr::System::getBookNumberByOSISName(const char *bookName) const {
197 map<SWBuf, int>::const_iterator it = p->osisLookup.find(bookName);
198 return (it != p->osisLookup.end()) ? it->second : -1;
199 }
200
201
loadFromSBook(const sbook * ot,const sbook * nt,int * chMax,const unsigned char * mappings)202 void VersificationMgr::System::loadFromSBook(const sbook *ot, const sbook *nt, int *chMax, const unsigned char *mappings) {
203 int chap = 0;
204 int book = 0;
205 long offset = 0; // module heading
206 offset++; // testament heading
207 while (ot->chapmax) {
208 p->books.push_back(Book(ot->name, ot->osis, ot->prefAbbrev, ot->chapmax));
209 offset++; // book heading
210 Book &b = p->books[p->books.size()-1];
211 p->osisLookup[b.getOSISName()] = (int)p->books.size();
212 for (int i = 0; i < ot->chapmax; i++) {
213 b.p->verseMax.push_back(chMax[chap]);
214 offset++; // chapter heading
215 b.p->offsetPrecomputed.push_back(offset);
216 offset += chMax[chap++];
217 }
218 ot++;
219 book++;
220 }
221 BMAX[0] = book;
222 book = 0;
223 ntStartOffset = offset;
224 offset++; // testament heading
225 while (nt->chapmax) {
226 p->books.push_back(Book(nt->name, nt->osis, nt->prefAbbrev, nt->chapmax));
227 offset++; // book heading
228 Book &b = p->books[p->books.size()-1];
229 p->osisLookup[b.getOSISName()] = (int)p->books.size();
230 for (int i = 0; i < nt->chapmax; i++) {
231 b.p->verseMax.push_back(chMax[chap]);
232 offset++; // chapter heading
233 b.p->offsetPrecomputed.push_back(offset);
234 offset += chMax[chap++];
235 }
236 nt++;
237 book++;
238 }
239 BMAX[1] = book;
240
241 // TODO: build offset speed array
242
243 // parse mappings
244 if (mappings != NULL) {
245 const unsigned char *m=mappings;
246 for (; *m != 0; m += strlen((const char*)m)+1) {
247 p->mappingsExtraBooks.push_back((const char*)m);
248 }
249 p->mappings.resize(p->books.size()+p->mappingsExtraBooks.size());
250
251 for (++m; *m != 0; m += 7) {
252 p->mappings[m[0]-1].push_back(m);
253 if (*m > p->books.size()) {
254 p->mappings[m[7]-1].push_back(m);
255 m += 1;
256 }
257 }
258 }
259 }
260
261
Book(const Book & other)262 VersificationMgr::Book::Book(const Book &other) {
263 longName = other.longName;
264 osisName = other.osisName;
265 prefAbbrev = other.prefAbbrev;
266 chapMax = other.chapMax;
267 init();
268 (*p) = *(other.p);
269 }
270
271
operator =(const Book & other)272 VersificationMgr::Book& VersificationMgr::Book::operator =(const Book &other) {
273 longName = other.longName;
274 osisName = other.osisName;
275 prefAbbrev = other.prefAbbrev;
276 chapMax = other.chapMax;
277 init();
278 (*p) = *(other.p);
279 return *this;
280 }
281
282
~Book()283 VersificationMgr::Book::~Book() {
284 delete p;
285 }
286
287
getVerseMax(int chapter) const288 int VersificationMgr::Book::getVerseMax(int chapter) const {
289 chapter--;
290 return (p && (chapter < (signed int)p->verseMax.size()) && (chapter > -1)) ? p->verseMax[chapter] : -1;
291 }
292
293
getBookCount() const294 int VersificationMgr::System::getBookCount() const {
295 return (int)(p ? p->books.size() : 0);
296 }
297
298
getOffsetFromVerse(int book,int chapter,int verse) const299 long VersificationMgr::System::getOffsetFromVerse(int book, int chapter, int verse) const {
300 long offset = -1;
301 chapter--;
302
303 const Book *b = getBook(book);
304
305 if (!b) return -1; // assert we have a valid book
306 if ((chapter > -1) && (chapter >= (signed int)b->p->offsetPrecomputed.size())) return -1; // assert we have a valid chapter
307
308 offset = b->p->offsetPrecomputed[(chapter > -1)?chapter:0];
309 if (chapter < 0) offset--;
310
311 /* old code
312 *
313 offset = offsets[testament-1][0][book];
314 offset = offsets[testament-1][1][(int)offset + chapter];
315 if (!(offset|verse)) // if we have a testament but nothing else.
316 offset = 1;
317
318 */
319
320 return (offset + verse);
321 }
322
323
getVerseFromOffset(long offset,int * book,int * chapter,int * verse) const324 char VersificationMgr::System::getVerseFromOffset(long offset, int *book, int *chapter, int *verse) const {
325
326 if (offset < 1) { // just handle the module heading corner case up front (and error case)
327 (*book) = -1;
328 (*chapter) = 0;
329 (*verse) = 0;
330 return offset; // < 0 = error
331 }
332
333 // binary search for book
334 vector<Book>::iterator b = lower_bound(p->books.begin(), p->books.end(), offset, BookOffsetLess());
335 if (b == p->books.end()) b--;
336 (*book) = distance(p->books.begin(), b)+1;
337 if (offset < (*(b->p->offsetPrecomputed.begin()))-((((!(*book)) || (*book)==BMAX[0]+1))?2:1)) { // -1 for chapter headings
338 (*book)--;
339 if (b != p->books.begin()) {
340 b--;
341 }
342 }
343 vector<long>::iterator c = lower_bound(b->p->offsetPrecomputed.begin(), b->p->offsetPrecomputed.end(), offset);
344
345 // if we're a book heading, we are lessthan chapter precomputes, but greater book. This catches corner case.
346 if (c == b->p->offsetPrecomputed.end()) {
347 c--;
348 }
349 if ((offset < *c) && (c == b->p->offsetPrecomputed.begin())) {
350 (*chapter) = (offset - *c)+1; // should be 0 or -1 (for testament heading)
351 (*verse) = 0;
352 }
353 else {
354 if (offset < *c) c--;
355 (*chapter) = distance(b->p->offsetPrecomputed.begin(), c)+1;
356 (*verse) = (offset - *c);
357 }
358 return ((*chapter > 0) && (*verse > b->getVerseMax(*chapter))) ? KEYERR_OUTOFBOUNDS : 0;
359 }
360
361
362 /***************************************************
363 * VersificationMgr
364 */
365
366 class VersificationMgr::Private {
367 public:
Private()368 Private() {
369 }
Private(const VersificationMgr::Private & other)370 Private(const VersificationMgr::Private &other) {
371 systems = other.systems;
372 }
operator =(const VersificationMgr::Private & other)373 VersificationMgr::Private &operator =(const VersificationMgr::Private &other) {
374 systems = other.systems;
375 return *this;
376 }
377 map<SWBuf, System> systems;
378 };
379 // ---------------- statics -----------------
380 VersificationMgr *VersificationMgr::systemVersificationMgr = 0;
381
382 class __staticsystemVersificationMgr {
383 public:
__staticsystemVersificationMgr()384 __staticsystemVersificationMgr() { }
~__staticsystemVersificationMgr()385 ~__staticsystemVersificationMgr() { delete VersificationMgr::systemVersificationMgr; }
386 } _staticsystemVersificationMgr;
387
388
init()389 void VersificationMgr::init() {
390 p = new Private();
391 }
392
393
~VersificationMgr()394 VersificationMgr::~VersificationMgr() {
395 delete p;
396 }
397
398
setSystemVersificationMgr(VersificationMgr * newVersificationMgr)399 void VersificationMgr::setSystemVersificationMgr(VersificationMgr *newVersificationMgr) {
400 if (systemVersificationMgr)
401 delete systemVersificationMgr;
402 systemVersificationMgr = newVersificationMgr;
403 }
404
405
getVersificationSystem(const char * name) const406 const VersificationMgr::System *VersificationMgr::getVersificationSystem(const char *name) const {
407 map<SWBuf, System>::const_iterator it = p->systems.find(name);
408 return (it != p->systems.end()) ? &(it->second) : 0;
409 }
410
411
registerVersificationSystem(const char * name,const sbook * ot,const sbook * nt,int * chMax,const unsigned char * mappings)412 void VersificationMgr::registerVersificationSystem(const char *name, const sbook *ot, const sbook *nt, int *chMax, const unsigned char *mappings) {
413 p->systems[name] = name;
414 System &s = p->systems[name];
415 s.loadFromSBook(ot, nt, chMax, mappings);
416 }
417
418
registerVersificationSystem(const char * name,const TreeKey * tk)419 void VersificationMgr::registerVersificationSystem(const char *name, const TreeKey *tk) {
420 }
421
422
getVersificationSystems() const423 const StringList VersificationMgr::getVersificationSystems() const {
424 StringList retVal;
425 for (map<SWBuf, System>::const_iterator it = p->systems.begin(); it != p->systems.end(); it++) {
426 retVal.push_back(it->first);
427 }
428 return retVal;
429 }
430
translateVerse(const System * dstSys,const char ** book,int * chapter,int * verse,int * verse_end) const431 void VersificationMgr::System::translateVerse(const System *dstSys, const char **book, int *chapter, int *verse, int *verse_end) const {
432 SWLog::getSystemLog()->logDebug("translate verse from %s to %s: %s.%i.%i-%i\n",getName(), dstSys->getName(), *book, *chapter, *verse, *verse_end);
433
434 if (!strcmp(getName(),"KJVA") || !strcmp(getName(),"KJV")) {
435 if (!strcmp(dstSys->getName(),"KJVA") || !strcmp(dstSys->getName(),"KJV"))
436 return;
437 // reversed mapping
438 SWLog::getSystemLog()->logDebug("Perform reversed mapping.\n");
439 int b = dstSys->getBookNumberByOSISName(*book)-1;
440
441 SWLog::getSystemLog()->logDebug("\tgetBookNumberByOSISName %i %s.\n", b, *book);
442
443 if (b < 0) {
444 SWLog::getSystemLog()->logDebug("\tmappingsExtraBooks.size() %i.\n", dstSys->p->mappingsExtraBooks.size());
445 for (int i=0; i<(int)dstSys->p->mappingsExtraBooks.size(); ++i) {
446 SWLog::getSystemLog()->logDebug("\t%s %s.\n", *book, dstSys->p->mappingsExtraBooks[i]);
447 if (!strcmp(*book, dstSys->p->mappingsExtraBooks[i])) {
448 b = (int)p->books.size()+i-2;
449 break;
450 }
451 }
452 }
453
454 SWLog::getSystemLog()->logDebug("\tb %i.\n", b);
455
456 if (b >= (int)dstSys->p->mappings.size() || b < 0) {
457 SWLog::getSystemLog()->logDebug("no modification");
458 return;
459 }
460
461 const unsigned char *a = NULL;
462
463 // reversed mapping should use forward search for item
464 for (unsigned int i=0; i<dstSys->p->mappings[b].size(); ++i) {
465 const unsigned char *m = dstSys->p->mappings[b][i];
466 if (m[4] == *chapter && m[5] <= *verse) {
467 SWLog::getSystemLog()->logDebug("found mapping %i %i %i %i %i %i\n",m[1],m[2],m[3],m[4],m[5],m[6]);
468 if (m[5] == *verse || (m[6] >= *verse && m[5] <= *verse)) {
469 // inside of any mapping range
470 *chapter = m[1];
471 *verse = m[2];
472 *verse_end = m[3];
473 if (*m >= dstSys->p->books.size()) {
474 SWLog::getSystemLog()->logWarning("map to extra books, possible bug source\n");
475 *book = dstSys->getBook(m[7]-1)->getOSISName();
476 }
477 return;
478 }
479 // destination mapping can have duplicate items, use the last (by using <=)
480 if (a == NULL || (a[5]>a[6]?a[5]:a[6]) <= (m[5]>m[6]?m[5]:m[6]))
481 a = m;
482 }
483 }
484 if (a != NULL) {
485 SWLog::getSystemLog()->logDebug("set appropriate: %i %i %i %i %i %i\n",a[1],a[2],a[3],a[4],a[5],a[6]);
486 (*chapter) = a[1];
487 // shift verse
488 const int d = (a[3]>a[2]?a[3]:a[2])-(a[6]>a[5]?a[6]:a[5]);
489 if (*verse < *verse_end)
490 *verse_end += d;
491 else
492 *verse_end = (*verse) + d;
493 *verse += d;
494 if (*a > dstSys->p->books.size()) {
495 SWLog::getSystemLog()->logDebug("appropriate: %i %i %i %i %i %i %i %i\n",a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]);
496 SWLog::getSystemLog()->logDebug("book: %s\n", dstSys->getBook(a[7]-1)->getOSISName());
497 *book = dstSys->getBook(a[7]-1)->getOSISName();
498 }
499 return;
500 }
501 SWLog::getSystemLog()->logDebug("There is no mapping.\n");
502 }
503 else if (strcmp(dstSys->getName(),"KJVA") && strcmp(dstSys->getName(),"KJV")) {
504 const System *kjva = getSystemVersificationMgr()->getVersificationSystem("KJVA");
505 const int src_verse = *verse;
506
507 translateVerse(kjva, book, chapter, verse, verse_end);
508
509 int interm_verse = *verse, interm_range = *verse_end, interm_chapter = *chapter;
510 const char *interm_book = *book;
511
512 kjva->translateVerse(dstSys, book, chapter, verse, verse_end);
513
514 // contraction->expansion fix
515 if (verse < verse_end && !(interm_verse < interm_range)) {
516 kjva->translateVerse(this, &interm_book, &interm_chapter, &interm_verse, &interm_range);
517 if (interm_verse < interm_range) {
518 *verse += src_verse - interm_verse;
519 if (*verse > *verse_end)
520 *verse = *verse_end;
521 else
522 *verse_end = *verse;
523 }
524 }
525 }
526 else {
527 SWLog::getSystemLog()->logDebug("Perform forward mapping.\n");
528 const int b = getBookNumberByOSISName(*book)-1;
529 if (b >= (int)p->mappings.size())
530 return;
531 // forward mapping should use reversed search for item
532 for (int i = (int)p->mappings[b].size()-1; i>=0; --i) {
533 const unsigned char *m = p->mappings[b][i];
534 if (m[1] < *chapter) {
535 SWLog::getSystemLog()->logWarning("There is no mapping for this chapter.\n");
536 return;
537 }
538 if (m[1] == *chapter && m[2] <= *verse) {
539 SWLog::getSystemLog()->logDebug("found mapping %i %i %i %i %i %i\n",m[1],m[2],m[3],m[4],m[5],m[6]);
540 if (m[2] == *verse || (m[3] >= *verse && m[2] <= *verse)) {
541 *chapter = m[4];
542 *verse = m[5];
543 *verse_end = m[6];
544 }
545 else {
546 *chapter = m[4];
547 // shift verse
548 const int d = (m[6]>m[5]?m[6]:m[5])-(m[3]>m[2]?m[3]:m[2]);
549 if (*verse < *verse_end)
550 *verse_end += d;
551 else
552 *verse_end = (*verse) + d;
553 *verse += d;
554 }
555 if (*m > p->books.size())
556 *book = p->mappingsExtraBooks[m[0]-p->books.size()-1];
557 return;
558 }
559 }
560 SWLog::getSystemLog()->logDebug("No mapping.\n");
561 }
562 }
563
564 SWORD_NAMESPACE_END
565
566