Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members  

versekey.cpp

00001 /******************************************************************************
00002  *  VerseKey.cpp - code for class 'VerseKey'- a standard Biblical verse key
00003  */
00004 
00005 #include <swmacs.h>
00006 #include <utilfuns.h>
00007 #include <string.h>
00008 #include <stdio.h>
00009 #include <fcntl.h>
00010 #include <stdlib.h>
00011 
00012 #ifndef __GNUC__
00013 #include <io.h>
00014 #else
00015 #include <unistd.h>
00016 #endif
00017 
00018 #include <utilstr.h>
00019 #include <swkey.h>
00020 #include <swlog.h>
00021 #include <versekey.h>
00022 #include <localemgr.h>
00023 extern "C" {
00024 #include <roman.h>
00025 }
00026 
00027 
00028 static const char *classes[] = {"VerseKey", "SWKey", "SWObject", 0};
00029 SWClass VerseKey::classdef(classes);
00030 
00031 /******************************************************************************
00032  *  Initialize static members of VerseKey
00033  */
00034 
00035 #include <canon.h>      // Initialize static members of canonical books structure
00036 
00037 struct sbook *VerseKey::builtin_books[2]       = {0,0};
00038 const char    VerseKey::builtin_BMAX[2]        = {39, 27};
00039 long         *VerseKey::offsets[2][2]  = {{VerseKey::otbks, VerseKey::otcps}, {VerseKey::ntbks, VerseKey::ntcps}};
00040 int           VerseKey::instance       = 0;
00041 VerseKey::LocaleCache   VerseKey::localeCache;
00042 
00043 
00044 /******************************************************************************
00045  * VerseKey::init - initializes instance of VerseKey
00046  */
00047 
00048 void VerseKey::init() {
00049         myclass = &classdef;
00050         if (!instance)
00051                 initstatics();
00052 
00053         instance++;
00054         autonorm = 1;           // default auto normalization to true
00055         headings = 0;           // default display headings option is false
00056         upperBound = 0;
00057         lowerBound = 0;
00058         testament = 0;
00059         book = 0;
00060         chapter = 0;
00061         verse = 0;
00062         locale = 0;
00063 
00064         setLocale(LocaleMgr::systemLocaleMgr.getDefaultLocaleName());
00065 }
00066 
00067 /******************************************************************************
00068  * VerseKey Constructor - initializes instance of VerseKey
00069  *
00070  * ENT: ikey - base key (will take various forms of 'BOOK CH:VS'.  See
00071  *              VerseKey::parse for more detailed information)
00072  */
00073 
00074 VerseKey::VerseKey(const SWKey *ikey) : SWKey(*ikey)
00075 {
00076         init();
00077         if (ikey)
00078                 parse();
00079 }
00080 
00081 
00082 /******************************************************************************
00083  * VerseKey Constructor - initializes instance of VerseKey
00084  *
00085  * ENT: ikey - text key (will take various forms of 'BOOK CH:VS'.  See
00086  *              VerseKey::parse for more detailed information)
00087  */
00088 
00089 VerseKey::VerseKey(const char *ikey) : SWKey(ikey)
00090 {
00091         init();
00092         if (ikey)
00093                 parse();
00094 }
00095 
00096 
00097 VerseKey::VerseKey(VerseKey const &k) : SWKey(k.keytext)
00098 {
00099         init();
00100         autonorm = k.autonorm;
00101         headings = k.headings;
00102         testament = k.Testament();
00103         book = k.Book();
00104         chapter = k.Chapter();
00105         verse = k.Verse();
00106         LowerBound(k.LowerBound());
00107         UpperBound(k.UpperBound());
00108 }
00109 
00110 
00111 VerseKey::VerseKey(const char *min, const char *max) : SWKey()
00112 {
00113         init();
00114         LowerBound(min);
00115         UpperBound(max);
00116         setPosition(TOP);
00117 }
00118 
00119 
00120 SWKey *VerseKey::clone() const
00121 {
00122         return new VerseKey(*this);
00123 }
00124 
00125 
00126 /******************************************************************************
00127  * VerseKey Destructor - cleans up instance of VerseKey
00128  *
00129  * ENT: ikey - text key
00130  */
00131 
00132 VerseKey::~VerseKey() {
00133         if (upperBound)
00134                 delete upperBound;
00135         if (lowerBound)
00136                 delete lowerBound;
00137         if (locale)
00138                 delete [] locale;
00139 
00140         --instance;
00141 }
00142 
00143 
00144 void VerseKey::setLocale(const char *name) {
00145         char *BMAX;
00146         struct sbook **books;
00147         bool useCache = false;
00148 
00149         if (localeCache.name)
00150                 useCache = (!strcmp(localeCache.name, name));
00151 
00152         if (!useCache)  {       // if we're setting params for a new locale
00153                 stdstr(&(localeCache.name), name);
00154                 localeCache.abbrevsCnt = 0;
00155         }
00156 
00157         SWLocale *locale = (useCache) ? localeCache.locale : LocaleMgr::systemLocaleMgr.getLocale(name);
00158         localeCache.locale = locale;
00159 
00160         if (locale) {
00161                 locale->getBooks(&BMAX, &books);
00162                 setBooks(BMAX, books);
00163                 setBookAbbrevs(locale->getBookAbbrevs(), localeCache.abbrevsCnt);
00164                 localeCache.abbrevsCnt = abbrevsCnt;
00165         }
00166         else {
00167                 setBooks(builtin_BMAX, builtin_books);
00168                 setBookAbbrevs(builtin_abbrevs, localeCache.abbrevsCnt);
00169                 localeCache.abbrevsCnt = abbrevsCnt;
00170         }
00171         stdstr(&(this->locale), localeCache.name);
00172 }
00173 
00174 
00175 void VerseKey::setBooks(const char *iBMAX, struct sbook **ibooks) {
00176         BMAX = iBMAX;
00177         books = ibooks;
00178 }
00179 
00180 
00181 void VerseKey::setBookAbbrevs(const struct abbrev *bookAbbrevs, unsigned int size) {
00182         abbrevs = bookAbbrevs;
00183         if (!size) {
00184                 for (abbrevsCnt = 1; *abbrevs[abbrevsCnt].ab; abbrevsCnt++) {
00185                         /*
00186                         if (strcmp(abbrevs[abbrevsCnt-1].ab, abbrevs[abbrevsCnt].ab) > 0) {
00187                                 fprintf(stderr, "ERROR: book abbreviation (canon.h or locale) misordered at entry: %s\n", abbrevs[abbrevsCnt].ab);
00188                                 exit(-1);
00189                         }
00190                         */
00191                 }
00192         for (int t = 0; t < 2; t++) {
00193             for (int i = 0; i < BMAX[t]; i++) {
00194                 int bn = getBookAbbrev(books[t][i].name);
00195                 if ((bn-1)%39 != i) {
00196                     SWLog::systemlog->LogError("Book: %s does not have a matching toupper abbrevs entry! book number returned was: %d", books[t][i].name, bn);
00197                 }
00198             }
00199         }
00200     }
00201         else abbrevsCnt = size;
00202 }
00203 
00204 
00205 /******************************************************************************
00206  * VerseKey::initstatics - initializes statics.  Performed only when first
00207  *                                              instance on VerseKey (or descendent) is created.
00208  */
00209 
00210 void VerseKey::initstatics() {
00211         int l1, l2, chaptmp = 0;
00212 
00213         builtin_books[0] = otbooks;
00214         builtin_books[1] = ntbooks;
00215 
00216         for (l1 = 0; l1 < 2; l1++) {
00217                 for (l2 = 0; l2 < builtin_BMAX[l1]; l2++) {
00218                         builtin_books[l1][l2].versemax = &vm[chaptmp];
00219                         chaptmp += builtin_books[l1][l2].chapmax;
00220                 }
00221         }
00222 }
00223 
00224 
00225 /******************************************************************************
00226  * VerseKey::parse - parses keytext into testament|book|chapter|verse
00227  *
00228  * RET: error status
00229  */
00230 
00231 char VerseKey::parse()
00232 {
00233 
00234         
00235         testament = 1;
00236         book      = 1;
00237         chapter   = 1;
00238         verse     = 1;
00239 
00240         error     = 0;
00241 
00242         if (keytext) {
00243                 ListKey tmpListKey = VerseKey::ParseVerseList(keytext);
00244                 if (tmpListKey.Count()) {
00245                         SWKey::setText((const char *)tmpListKey);
00246                         for (testament = 1; testament < 3; testament++) {
00247                                 for (book = 1; book <= BMAX[testament-1]; book++) {
00248                                         if (!strncmp(keytext, books[testament-1][book-1].name, strlen(books[testament-1][book-1].name)))
00249                                                 break;
00250                                 }
00251                                 if (book <= BMAX[testament-1])
00252                                         break;
00253                         }
00254 
00255                         if (testament < 3) {
00256                                 sscanf(&keytext[strlen(books[testament-1][book-1].name)], "%d:%d", &chapter, &verse);
00257                         }
00258                         else    error = 1;
00259                 }
00260         }
00261         Normalize(1);
00262         freshtext();
00263 
00264         return error;
00265 }
00266 
00267 
00268 /******************************************************************************
00269  * VerseKey::freshtext - refreshes keytext based on
00270  *                              testament|book|chapter|verse
00271  */
00272 
00273 void VerseKey::freshtext() const
00274 {
00275         char buf[2024];
00276         int realtest = testament;
00277         int realbook = book;
00278 
00279         if (book < 1) {
00280                 if (testament < 1)
00281                         sprintf(buf, "[ Module Heading ]");
00282                 else sprintf(buf, "[ Testament %d Heading ]", (int)testament);
00283         }
00284         else {
00285                 if (realbook > BMAX[realtest-1]) {
00286                         realbook -= BMAX[realtest-1];
00287                         if (realtest < 2)
00288                                 realtest++;
00289                         if (realbook > BMAX[realtest-1])
00290                                 realbook = BMAX[realtest-1];
00291                 }
00292                 sprintf(buf, "%s %d:%d", books[realtest-1][realbook-1].name, chapter, verse);
00293         }
00294 
00295         stdstr((char **)&keytext, buf);
00296 }
00297 
00298 
00299 
00300 /******************************************************************************
00301  * VerseKey::getBookAbbrev - Attempts to find a book abbreviation for a buffer
00302  *
00303  * ENT: abbr - key for which to search;
00304  * RET: book number or < 0 = not valid
00305  */
00306 
00307 int VerseKey::getBookAbbrev(const char *iabbr)
00308 {
00309     int loop, diff, abLen, min, max, target, retVal = -1;
00310 
00311     char *abbr = 0;
00312 
00313         stdstr(&abbr, iabbr);
00314         strstrip(abbr);
00315         abLen = strlen(abbr);
00316         for (loop = 0; loop < abLen; loop++)
00317                 abbr[loop] = SW_toupper(abbr[loop]);
00318 
00319         if (abLen) {
00320                 min = 0;
00321 //              max = abbrevsCnt - 1;
00322                 max = abbrevsCnt;
00323                 while(1) {
00324                         target = min + ((max - min) / 2);
00325                         diff = strncmp(abbr, abbrevs[target].ab, abLen);
00326                         if ((!diff)||(target >= max)||(target <= min))
00327                                 break;
00328                         if (diff > 0)
00329                                 min = target;
00330                         else    max = target;
00331                 }
00332                 for (; target > 0; target--) {
00333                         if (strncmp(abbr, abbrevs[target-1].ab, abLen))
00334                                 break;
00335                 }
00336                         
00337                 retVal = (!diff) ? abbrevs[target].book : -1;
00338         }
00339         delete [] abbr;
00340         return retVal;
00341 }
00342 
00343 /******************************************************************************
00344  * VerseKey::ParseVerseList - Attempts to parse a buffer into separate
00345  *                              verse entries returned in a ListKey
00346  *
00347  * ENT: buf             - buffer to parse;
00348  *      defaultKey      - if verse, chap, book, or testament is left off,
00349  *                              pull info from this key (ie. Gen 2:3; 4:5;
00350  *                              Gen would be used when parsing the 4:5 section)
00351  *      expandRange     - whether or not to expand eg. John 1:10-12 or just
00352  *                              save John 1:10
00353  *
00354  * RET: ListKey reference filled with verse entries contained in buf
00355  *
00356  * COMMENT: This code works but wreaks.  Rewrite to make more maintainable.
00357  */
00358 
00359 ListKey VerseKey::ParseVerseList(const char *buf, const char *defaultKey, bool expandRange) {
00360         SWKey textkey;
00361 
00362         char book[255];
00363         char number[255];
00364         int tobook = 0;
00365         int tonumber = 0;
00366         int chap = -1, verse = -1;
00367         int bookno = 0;
00368         VerseKey curkey, lBound;
00369         curkey.setLocale(getLocale());
00370         lBound.setLocale(getLocale());
00371         int loop;
00372         char comma = 0;
00373         char dash = 0;
00374         const char *orig = buf;
00375         ListKey tmpListKey;
00376         ListKey internalListKey;
00377         SWKey tmpDefaultKey = defaultKey;
00378         char lastPartial = 0;
00379 
00380         curkey.AutoNormalize(0);
00381         tmpListKey << tmpDefaultKey;
00382         
00383         while (*buf) {
00384                 switch (*buf) {
00385                 case ':':
00386                         number[tonumber] = 0;
00387                         tonumber = 0;
00388                         if (*number)
00389                                 chap = atoi(number);
00390                         *number = 0;
00391                         break;
00392 
00393                 case '-': 
00394                 case ',': // on number new verse
00395                 case ';': // on number new chapter
00396                         number[tonumber] = 0;
00397                         tonumber = 0;
00398                         if (*number) {
00399                                 if (chap >= 0)
00400                                         verse = atoi(number);
00401                                 else    chap = atoi(number);
00402                         }
00403                         *number = 0;
00404                         book[tobook] = 0;
00405                         tobook = 0;
00406                         bookno = -1;
00407                         if (*book) {
00408                                 for (loop = strlen(book) - 1; loop+1; loop--) {
00409                                         if ((isdigit(book[loop])) || (book[loop] == ' ')) {
00410                                                 book[loop] = 0;
00411                                                 continue;
00412                                         }
00413                                         else {
00414                                                 if ((SW_toupper(book[loop])=='F')&&(loop)) {
00415                                                         if ((isdigit(book[loop-1])) || (book[loop-1] == ' ') || (SW_toupper(book[loop-1]) == 'F')) {
00416                                                                 book[loop] = 0;
00417                                                                 continue;
00418                                                         }
00419                                                 }
00420                                         }
00421                                         break;
00422                                 }
00423 
00424                     for (loop = strlen(book) - 1; loop+1; loop--) {
00425                          if (book[loop] == ' ') {
00426                                                 if (isroman(&book[loop+1])) {
00427                                    if (verse == -1) {
00428                                         verse = chap;
00429                                                                 chap = from_rom(&book[loop+1]);
00430                                                                 book[loop] = 0;
00431                                                         }
00432                                                 }
00433                                                 break;
00434                                         }
00435                                 }
00436 
00437                                 if ((!stricmp(book, "V")) || (!stricmp(book, "VER"))) { // Verse abbrev
00438                          if (verse == -1) {
00439                               verse = chap;
00440                                                 chap = VerseKey(tmpListKey).Chapter();
00441                               *book = 0;
00442                          }
00443                     }
00444                                 bookno = getBookAbbrev(book);
00445                         }
00446                         if (((bookno > -1) || (!*book)) && ((*book) || (chap >= 0) || (verse >= 0))) {
00447                                 char partial = 0;
00448                                 curkey.Verse(1);
00449                                 curkey.Chapter(1);
00450                                 curkey.Book(1);
00451 
00452                                 if (bookno < 0) {
00453                                         curkey.Testament(VerseKey(tmpListKey).Testament());
00454                                         curkey.Book(VerseKey(tmpListKey).Book());
00455                                 }
00456                                 else {
00457                                         curkey.Testament(1);
00458                                         curkey.Book(bookno);
00459                                 }
00460 
00461                                 if (((comma)||((verse < 0)&&(bookno < 0)))&&(!lastPartial)) {
00462 //                              if (comma) {
00463                                         curkey.Chapter(VerseKey(tmpListKey).Chapter());
00464                                         curkey.Verse(chap);  // chap because this is the first number captured
00465                                 }
00466                                 else {
00467                                         if (chap >= 0) {
00468                                                 curkey.Chapter(chap);
00469                                         }
00470                                         else {
00471                                                 partial++;
00472                                                 curkey.Chapter(1);
00473                                         }
00474                                         if (verse >= 0) {
00475                                                 curkey.Verse(verse);
00476                                         }
00477                                         else {
00478                                                 partial++;
00479                                                 curkey.Verse(1);
00480                                         }
00481                                 }
00482 
00483                                 if ((*buf == '-') && (expandRange)) {   // if this is a dash save lowerBound and wait for upper
00484                                         VerseKey newElement;
00485                                         newElement.LowerBound(curkey);
00486                                         newElement.setPosition(TOP);
00487                                         tmpListKey << newElement;
00488                                 }
00489                                 else {
00490                                         if (!dash) {    // if last separator was not a dash just add
00491                                                 if (expandRange && partial) {
00492                                                         VerseKey newElement;
00493                                                         newElement.LowerBound(curkey);
00494                                                         if (partial > 1)
00495                                                                 curkey.setPosition(MAXCHAPTER);
00496                                                         if (partial > 0)
00497                                                                 curkey = MAXVERSE;
00498                                                         newElement.UpperBound(curkey);
00499                                                         newElement = TOP;
00500                                                         tmpListKey << newElement;
00501                                                 }
00502                                                 else tmpListKey << (const SWKey &)(const SWKey)(const char *)curkey;
00503                                         }
00504                                         else    if (expandRange) {
00505                                                 VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.GetElement());
00506                                                 if (newElement) {
00507                                                         if (partial > 1)
00508                                                                 curkey = MAXCHAPTER;
00509                                                         if (partial > 0)
00510                                                                 curkey = MAXVERSE;
00511                                                         newElement->UpperBound(curkey);
00512                                                         *newElement = TOP;
00513                                                 }
00514                                         }
00515                                 }
00516                                 lastPartial = partial;
00517                         }
00518                         *book = 0;
00519                         chap = -1;
00520                         verse = -1;
00521                         if (*buf == ',')
00522                                 comma = 1;
00523                         else    comma = 0;
00524                         if (*buf == '-')
00525                                 dash = 1;
00526                         else    dash = 0;
00527                         break;
00528                 case 10:        // ignore these
00529                 case 13: 
00530                         break;
00531                 case '.':
00532                         if (buf > orig)                 // ignore (break) if preceeding char is not a digit
00533                                 if (!isdigit(*(buf-1)))
00534                                         break;
00535                         
00536                 default:
00537                         if (isdigit(*buf)) {
00538                                 number[tonumber++] = *buf;
00539                         }
00540                         else {
00541                                 switch (*buf) {
00542                                 case ' ':    // ignore these and don't reset number
00543                                 case 'f':
00544                                 case 'F':
00545                                         break;
00546                                 default:
00547                                         number[tonumber] = 0;
00548                                         tonumber = 0;
00549                                         break;
00550                                 }
00551                         }
00552                         if (chap == -1)
00553                                 book[tobook++] = SW_toupper(*buf);
00554                 }
00555                 buf++;
00556         }
00557         number[tonumber] = 0;
00558         tonumber = 0;
00559         if (*number) {
00560                 if (chap >= 0)
00561                         verse = atoi(number);
00562                 else    chap = atoi(number);
00563         }
00564         *number = 0;
00565         book[tobook] = 0;
00566         tobook = 0;
00567         if (*book) {
00568                 for (loop = strlen(book) - 1; loop+1; loop--) {
00569                         if ((isdigit(book[loop])) || (book[loop] == ' ')) {
00570                                 book[loop] = 0;
00571                                 continue;
00572                         }
00573                         else {
00574                                 if ((SW_toupper(book[loop])=='F')&&(loop)) {
00575                                         if ((isdigit(book[loop-1])) || (book[loop-1] == ' ') || (SW_toupper(book[loop-1]) == 'F')) {
00576                                                 book[loop] = 0;
00577                                                 continue;
00578                                         }
00579                                 }
00580                         }
00581                         break;
00582                 }
00583 
00584                 for (loop = strlen(book) - 1; loop+1; loop--) {
00585                 if (book[loop] == ' ') {
00586                 if (isroman(&book[loop+1])) {
00587                         if (verse == -1) {
00588                                 verse = chap;
00589                               chap = from_rom(&book[loop+1]);
00590                               book[loop] = 0;
00591                          }
00592                     }
00593                 break;
00594                         }
00595           }
00596                
00597                 if ((!stricmp(book, "V")) || (!stricmp(book, "VER"))) { // Verse abbrev.
00598                         if (verse == -1) {
00599                                 verse = chap;
00600                                 chap = VerseKey(tmpListKey).Chapter();
00601                                 *book = 0;
00602                         }
00603                 }
00604                         
00605                 bookno = getBookAbbrev(book);
00606         }
00607         if (((bookno > -1) || (!*book)) && ((*book) || (chap >= 0) || (verse >= 0))) {
00608                 char partial = 0;
00609                 curkey.Verse(1);
00610                 curkey.Chapter(1);
00611                 curkey.Book(1);
00612 
00613                 if (bookno < 0) {
00614                         curkey.Testament(VerseKey(tmpListKey).Testament());
00615                         curkey.Book(VerseKey(tmpListKey).Book());
00616                 }
00617                 else {
00618                         curkey.Testament(1);
00619                         curkey.Book(bookno);
00620                 }
00621 
00622                 if (((comma)||((verse < 0)&&(bookno < 0)))&&(!lastPartial)) {
00623 //              if (comma) {
00624                         curkey.Chapter(VerseKey(tmpListKey).Chapter());
00625                         curkey.Verse(chap);  // chap because this is the first number captured
00626                 }
00627                 else {
00628                         if (chap >= 0) {
00629                                 curkey.Chapter(chap);
00630                         }
00631                         else {
00632                                 partial++;
00633                                 curkey.Chapter(1);
00634                         }
00635                         if (verse >= 0) {
00636                                 curkey.Verse(verse);
00637                         }
00638                         else {
00639                                 partial++;
00640                                 curkey.Verse(1);
00641                         }
00642                 }
00643 
00644                 if ((*buf == '-') && (expandRange)) {   // if this is a dash save lowerBound and wait for upper
00645                         VerseKey newElement;
00646                         newElement.LowerBound(curkey);
00647                         newElement = TOP;
00648                         tmpListKey << newElement;
00649                 }
00650                 else {
00651                         if (!dash) {    // if last separator was not a dash just add
00652                                 if (expandRange && partial) {
00653                                         VerseKey newElement;
00654                                         newElement.LowerBound(curkey);
00655                                         if (partial > 1)
00656                                                 curkey = MAXCHAPTER;
00657                                         if (partial > 0)
00658                                                 curkey = MAXVERSE;
00659                                         newElement.UpperBound(curkey);
00660                                         newElement = TOP;
00661                                         tmpListKey << newElement;
00662                                 }
00663                                 else tmpListKey << (const SWKey &)(const SWKey)(const char *)curkey;
00664                         }
00665                         else if (expandRange) {
00666                                 VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.GetElement());
00667                                 if (newElement) {
00668                                         if (partial > 1)
00669                                                 curkey = MAXCHAPTER;
00670                                         if (partial > 0)
00671                                                 curkey = MAXVERSE;
00672                                         newElement->UpperBound(curkey);
00673                                         *newElement = TOP;
00674                                 }
00675                         }
00676                 }
00677         }
00678         *book = 0;
00679         tmpListKey = TOP;
00680         tmpListKey.Remove();    // remove defaultKey
00681         internalListKey = tmpListKey;
00682         internalListKey = TOP;  // Align internalListKey to first element before passing back;
00683 
00684         return internalListKey;
00685 }
00686 
00687 
00688 /******************************************************************************
00689  * VerseKey::LowerBound - sets / gets the lower boundary for this key
00690  */
00691 
00692 VerseKey &VerseKey::LowerBound(const char *lb)
00693 {
00694         if (!lowerBound) 
00695                 initBounds();
00696 
00697         (*lowerBound) = lb;
00698         lowerBound->Normalize();
00699 
00700         return (*lowerBound);
00701 }
00702 
00703 
00704 /******************************************************************************
00705  * VerseKey::UpperBound - sets / gets the upper boundary for this key
00706  */
00707 
00708 VerseKey &VerseKey::UpperBound(const char *ub)
00709 {
00710         if (!upperBound) 
00711                 initBounds();
00712 
00713 // need to set upperbound parsing to resolve to max verse/chap if not specified
00714            (*upperBound) = ub;
00715         if (*upperBound < *lowerBound)
00716                 *upperBound = *lowerBound;
00717         upperBound->Normalize();
00718 
00719 // until we have a proper method to resolve max verse/chap use this kludge
00720         int len = strlen(ub);
00721         bool alpha = false;
00722         bool versespec = false;
00723         bool chapspec = false;
00724         for (int i = 0; i < len; i++) {
00725                 if (isalpha(ub[i]))
00726                         alpha = true;
00727                 if (ub[i] == ':')       // if we have a : we assume verse spec
00728                         versespec = true;
00729                 if ((isdigit(ub[i])) && (alpha))        // if digit after alpha assume chap spec
00730                         chapspec = true;
00731         }
00732         if (!chapspec)
00733                 *upperBound = MAXCHAPTER;
00734         if (!versespec)
00735                 *upperBound = MAXVERSE;
00736         
00737 
00738 // -- end kludge
00739 
00740         return (*upperBound);
00741 }
00742 
00743 
00744 /******************************************************************************
00745  * VerseKey::LowerBound - sets / gets the lower boundary for this key
00746  */
00747 
00748 VerseKey &VerseKey::LowerBound() const
00749 {
00750         if (!lowerBound) 
00751                 initBounds();
00752 
00753         return (*lowerBound);
00754 }
00755 
00756 
00757 /******************************************************************************
00758  * VerseKey::UpperBound - sets / gets the upper boundary for this key
00759  */
00760 
00761 VerseKey &VerseKey::UpperBound() const
00762 {
00763         if (!upperBound) 
00764                 initBounds();
00765 
00766         return (*upperBound);
00767 }
00768 
00769 
00770 /******************************************************************************
00771  * VerseKey::ClearBounds        - clears bounds for this VerseKey
00772  */
00773 
00774 void VerseKey::ClearBounds()
00775 {
00776         initBounds();
00777 }
00778 
00779 
00780 void VerseKey::initBounds() const
00781 {
00782         if (!upperBound) {
00783                 upperBound = new VerseKey();
00784                 upperBound->AutoNormalize(0);
00785                 upperBound->Headings(1);
00786         }
00787         if (!lowerBound) {
00788                 lowerBound = new VerseKey();
00789                 lowerBound->AutoNormalize(0);
00790                 lowerBound->Headings(1);
00791         }
00792 
00793         lowerBound->Testament(0);
00794         lowerBound->Book(0);
00795         lowerBound->Chapter(0);
00796         lowerBound->Verse(0);
00797 
00798         upperBound->Testament(2);
00799         upperBound->Book(BMAX[1]);
00800         upperBound->Chapter(books[1][BMAX[1]-1].chapmax);
00801         upperBound->Verse(books[1][BMAX[1]-1].versemax[upperBound->Chapter()-1]);
00802 }
00803 
00804 
00805 /******************************************************************************
00806  * VerseKey::copyFrom - Equates this VerseKey to another VerseKey
00807  */
00808 
00809 void VerseKey::copyFrom(const VerseKey &ikey) {
00810         SWKey::copyFrom(ikey);
00811 
00812         parse();
00813 }
00814 
00815 
00816 /******************************************************************************
00817  * VerseKey::copyFrom - Equates this VerseKey to another SWKey
00818  */
00819 
00820 void VerseKey::copyFrom(const SWKey &ikey) {
00821         SWKey::copyFrom(ikey);
00822 
00823         parse();
00824 }
00825 
00826 
00827 /******************************************************************************
00828  * VerseKey::getText - refreshes keytext before returning if cast to
00829  *                              a (char *) is requested
00830  */
00831 
00832 const char *VerseKey::getText() const {
00833         freshtext();
00834         return keytext;
00835 }
00836 
00837 
00838 const char *VerseKey::getShortText() const {
00839         static char *stext = 0;
00840         char buf[2047];
00841         freshtext();
00842         if (book < 1) {
00843                 if (testament < 1)
00844                         sprintf(buf, "[ Module Heading ]");
00845                 else sprintf(buf, "[ Testament %d Heading ]", (int)testament);
00846         }
00847         else {
00848                 sprintf(buf, "%s %d:%d", books[testament-1][book-1].prefAbbrev, chapter, verse);
00849         }
00850         stdstr(&stext, buf);
00851         return stext;
00852 }
00853 
00854 
00855 const char *VerseKey::getBookName() const {
00856         return books[testament-1][book-1].name;
00857 }
00858 
00859 
00860 const char *VerseKey::getBookAbbrev() const {
00861         return books[testament-1][book-1].prefAbbrev;
00862 }
00863 /******************************************************************************
00864  * VerseKey::setPosition(SW_POSITION)   - Positions this key
00865  *
00866  * ENT: p       - position
00867  *
00868  * RET: *this
00869  */
00870 
00871 void VerseKey::setPosition(SW_POSITION p) {
00872         switch (p) {
00873         case POS_TOP:
00874                 testament = LowerBound().Testament();
00875                 book      = LowerBound().Book();
00876                 chapter   = LowerBound().Chapter();
00877                 verse     = LowerBound().Verse();
00878                 break;
00879         case POS_BOTTOM:
00880                 testament = UpperBound().Testament();
00881                 book      = UpperBound().Book();
00882                 chapter   = UpperBound().Chapter();
00883                 verse     = UpperBound().Verse();
00884                 break;
00885         case POS_MAXVERSE:
00886                 Normalize();
00887                 verse     = books[testament-1][book-1].versemax[chapter-1];
00888                 break;
00889         case POS_MAXCHAPTER:
00890                 verse     = 1;
00891                 Normalize();
00892                 chapter   = books[testament-1][book-1].chapmax;
00893                 break;
00894         } 
00895         Normalize(1);
00896         Error();        // clear error from normalize
00897 }
00898 
00899 
00900 /******************************************************************************
00901  * VerseKey::increment  - Increments key a number of verses
00902  *
00903  * ENT: step    - Number of verses to jump forward
00904  *
00905  * RET: *this
00906  */
00907 
00908 void VerseKey::increment(int step) {
00909         char ierror = 0;
00910         Index(Index() + step);
00911         while ((!verse) && (!headings) && (!ierror)) {
00912                 Index(Index() + 1);
00913                 ierror = Error();
00914         }
00915 
00916         error = (ierror) ? ierror : error;
00917 }
00918 
00919 
00920 /******************************************************************************
00921  * VerseKey::decrement  - Decrements key a number of verses
00922  *
00923  * ENT: step    - Number of verses to jump backward
00924  *
00925  * RET: *this
00926  */
00927 
00928 void VerseKey::decrement(int step) {
00929         char ierror = 0;
00930 
00931         Index(Index() - step);
00932         while ((!verse) && (!headings) && (!ierror)) {
00933                 Index(Index() - 1);
00934                 ierror = Error();
00935         }
00936         if ((ierror) && (!headings))
00937                 (*this)++;
00938 
00939         error = (ierror) ? ierror : error;
00940 }
00941 
00942 
00943 /******************************************************************************
00944  * VerseKey::Normalize  - checks limits and normalizes if necessary (e.g.
00945  *                              Matthew 29:47 = Mark 2:2).  If last verse is
00946  *                              exceeded, key is set to last Book CH:VS
00947  * RET: *this
00948  */
00949 
00950 void VerseKey::Normalize(char autocheck)
00951 {
00952         error = 0;
00953 
00954         if ((autocheck) && (!autonorm)) // only normalize if we were explicitely called or if autonorm is turned on
00955                 return;
00956 
00957         if ((headings) && (!verse))             // this is cheeze and temporary until deciding what actions should be taken.
00958                 return;                                 // so headings should only be turned on when positioning with Index() or incrementors
00959 
00960         while ((testament < 3) && (testament > 0)) {
00961 
00962                 if (book > BMAX[testament-1]) {
00963                         book -= BMAX[testament-1];
00964                         testament++;
00965                         continue;
00966                 }
00967 
00968                 if (book < 1) {
00969                         if (--testament > 0) {
00970                                 book += BMAX[testament-1];
00971                         }
00972                         continue;
00973                 }
00974 
00975                 if (chapter > books[testament-1][book-1].chapmax) {
00976                         chapter -= books[testament-1][book-1].chapmax;
00977                         book++;
00978                         continue;
00979                 }
00980 
00981                 if (chapter < 1) {
00982                         if (--book > 0) {
00983                                 chapter += books[testament-1][book-1].chapmax;
00984                         }
00985                         else    {
00986                                 if (testament > 1) {
00987                                         chapter += books[0][BMAX[0]-1].chapmax;
00988                                 }
00989                         }
00990                         continue;
00991                 }
00992 
00993                 if (verse > books[testament-1][book-1].versemax[chapter-1]) { // -1 because e.g chapter 1 of Matthew is books[1][0].versemax[0]
00994                         verse -= books[testament-1][book-1].versemax[chapter++ - 1];
00995                         continue;
00996                 }
00997 
00998                 if (verse < 1) {
00999                         if (--chapter > 0) {
01000                                 verse += books[testament-1][book-1].versemax[chapter-1];
01001                         }
01002                         else    {
01003                                 if (book > 1) {
01004                                         verse += books[testament-1][book-2].versemax[books[testament-1][book-2].chapmax-1];
01005                                 }
01006                                 else    {
01007                                         if (testament > 1) {
01008                                                 verse += books[0][BMAX[0]-1].versemax[books[0][BMAX[0]-1].chapmax-1];
01009                                         }
01010                                 }
01011                         }
01012                         continue;
01013                 }
01014 
01015                 break;  // If we've made it this far (all failure checks continue) we're ok
01016         }
01017 
01018         if (testament > 2) {
01019                 testament = 2;
01020                 book      = BMAX[testament-1];
01021                 chapter   = books[testament-1][book-1].chapmax;
01022                 verse     = books[testament-1][book-1].versemax[chapter-1];
01023                 error     = KEYERR_OUTOFBOUNDS;
01024         }
01025 
01026         if (testament < 1) {
01027                 error     = ((!headings) || (testament < 0) || (book < 0)) ? KEYERR_OUTOFBOUNDS : 0;
01028                 testament = ((headings) ? 0 : 1);
01029                 book      = ((headings) ? 0 : 1);
01030                 chapter   = ((headings) ? 0 : 1);
01031                 verse     = ((headings) ? 0 : 1);
01032         }
01033         if (_compare(UpperBound()) > 0) {
01034                 *this = UpperBound();
01035                 error = KEYERR_OUTOFBOUNDS;
01036         }
01037         if (_compare(LowerBound()) < 0) {
01038                 *this = LowerBound();
01039                 error = KEYERR_OUTOFBOUNDS;
01040         }
01041 }
01042 
01043 
01044 /******************************************************************************
01045  * VerseKey::Testament - Gets testament
01046  *
01047  * RET: value of testament
01048  */
01049 
01050 char VerseKey::Testament() const
01051 {
01052         return testament;
01053 }
01054 
01055 
01056 /******************************************************************************
01057  * VerseKey::Book - Gets book
01058  *
01059  * RET: value of book
01060  */
01061 
01062 char VerseKey::Book() const
01063 {
01064         return book;
01065 }
01066 
01067 
01068 /******************************************************************************
01069  * VerseKey::Chapter - Gets chapter
01070  *
01071  * RET: value of chapter
01072  */
01073 
01074 int VerseKey::Chapter() const
01075 {
01076         return chapter;
01077 }
01078 
01079 
01080 /******************************************************************************
01081  * VerseKey::Verse - Gets verse
01082  *
01083  * RET: value of verse
01084  */
01085 
01086 int VerseKey::Verse() const
01087 {
01088         return verse;
01089 }
01090 
01091 
01092 /******************************************************************************
01093  * VerseKey::Testament - Sets/gets testament
01094  *
01095  * ENT: itestament - value which to set testament
01096  *              [MAXPOS(char)] - only get
01097  *
01098  * RET: if unchanged ->          value of testament
01099  *      if   changed -> previous value of testament
01100  */
01101 
01102 char VerseKey::Testament(char itestament)
01103 {
01104         char retval = testament;
01105 
01106         if (itestament != MAXPOS(char)) {
01107                 testament = itestament;
01108                 Normalize(1);
01109         }
01110         return retval;
01111 }
01112 
01113 
01114 /******************************************************************************
01115  * VerseKey::Book - Sets/gets book
01116  *
01117  * ENT: ibook - value which to set book
01118  *              [MAXPOS(char)] - only get
01119  *
01120  * RET: if unchanged ->          value of book
01121  *      if   changed -> previous value of book
01122  */
01123 
01124 char VerseKey::Book(char ibook)
01125 {
01126         char retval = book;
01127 
01128         Chapter(1);
01129         book = ibook;
01130         Normalize(1);
01131 
01132         return retval;
01133 }
01134 
01135 
01136 /******************************************************************************
01137  * VerseKey::Chapter - Sets/gets chapter
01138  *
01139  * ENT: ichapter - value which to set chapter
01140  *              [MAXPOS(int)] - only get
01141  *
01142  * RET: if unchanged ->          value of chapter
01143  *      if   changed -> previous value of chapter
01144  */
01145 
01146 int VerseKey::Chapter(int ichapter)
01147 {
01148         int retval = chapter;
01149 
01150         Verse(1);
01151         chapter = ichapter;
01152         Normalize(1);
01153 
01154         return retval;
01155 }
01156 
01157 
01158 /******************************************************************************
01159  * VerseKey::Verse - Sets/gets verse
01160  *
01161  * ENT: iverse - value which to set verse
01162  *              [MAXPOS(int)] - only get
01163  *
01164  * RET: if unchanged ->          value of verse
01165  *      if   changed -> previous value of verse
01166  */
01167 
01168 int VerseKey::Verse(int iverse)
01169 {
01170         int retval = verse;
01171 
01172         verse = iverse;
01173         Normalize(1);
01174 
01175         return retval;
01176 }
01177 
01178 
01179 /******************************************************************************
01180  * VerseKey::AutoNormalize - Sets/gets flag that tells VerseKey to auto-
01181  *                              matically normalize itself when modified
01182  *
01183  * ENT: iautonorm - value which to set autonorm
01184  *              [MAXPOS(char)] - only get
01185  *
01186  * RET: if unchanged ->          value of autonorm
01187  *              if   changed -> previous value of autonorm
01188  */
01189 
01190 char VerseKey::AutoNormalize(char iautonorm)
01191 {
01192         char retval = autonorm;
01193 
01194         if (iautonorm != MAXPOS(char)) {
01195                 autonorm = iautonorm;
01196                 Normalize(1);
01197         }
01198         return retval;
01199 }
01200 
01201 
01202 /******************************************************************************
01203  * VerseKey::Headings - Sets/gets flag that tells VerseKey to include
01204  *                                      chap/book/testmnt/module headings
01205  *
01206  * ENT: iheadings - value which to set headings
01207  *              [MAXPOS(char)] - only get
01208  *
01209  * RET: if unchanged ->          value of headings
01210  *              if   changed -> previous value of headings
01211  */
01212 
01213 char VerseKey::Headings(char iheadings)
01214 {
01215         char retval = headings;
01216 
01217         if (iheadings != MAXPOS(char)) {
01218                 headings = iheadings;
01219                 Normalize(1);
01220         }
01221         return retval;
01222 }
01223 
01224 
01225 /******************************************************************************
01226  * VerseKey::findindex - binary search to find the index closest, but less
01227  *                                              than the given value.
01228  *
01229  * ENT: array   - long * to array to search
01230  *              size            - number of elements in the array
01231  *              value   - value to find
01232  *
01233  * RET: the index into the array that is less than but closest to value
01234  */
01235 
01236 int VerseKey::findindex(long *array, int size, long value)
01237 {
01238         int lbound, ubound, tval;
01239 
01240         lbound = 0;
01241         ubound = size - 1;
01242         while ((ubound - lbound) > 1) {
01243                 tval = lbound + (ubound-lbound)/2;
01244                 if (array[tval] <= value)
01245                         lbound = tval;
01246                 else ubound = tval;
01247         }
01248         return (array[ubound] <= value) ? ubound : lbound;
01249 }
01250 
01251 
01252 /******************************************************************************
01253  * VerseKey::Index - Gets index based upon current verse
01254  *
01255  * RET: offset
01256  */
01257 
01258 long VerseKey::Index() const
01259 {
01260         long  offset;
01261 
01262         if (!testament) { // if we want module heading
01263                 offset = 0;
01264                 verse  = 0;
01265         }
01266         else {
01267                 if (!book)
01268                         chapter = 0;
01269                 if (!chapter)
01270                         verse   = 0;
01271 
01272                 offset = offsets[testament-1][0][book];
01273                 offset = offsets[testament-1][1][(int)offset + chapter];
01274                 if (!(offset|verse)) // if we have a testament but nothing else.
01275                         offset = 1;
01276         }
01277         return (offset + verse);
01278 }
01279 
01280 
01281 /******************************************************************************
01282  * VerseKey::Index - Gets index based upon current verse
01283  *
01284  * RET: offset
01285  */
01286 
01287 long VerseKey::NewIndex() const
01288 {
01289         static long otMaxIndex = 32300 - 8245;  // total positions - new testament positions
01290 //      static long otMaxIndex = offsets[0][1][(int)offsets[0][0][BMAX[0]] + books[0][BMAX[0]].chapmax];
01291         return ((testament-1) * otMaxIndex) + Index();
01292 }
01293 
01294 
01295 /******************************************************************************
01296  * VerseKey::Index - Sets index based upon current verse
01297  *
01298  * ENT: iindex - value to set index to
01299  *
01300  * RET: offset
01301  */
01302 
01303 long VerseKey::Index(long iindex)
01304 {
01305         long  offset;
01306 
01307 // This is the dirty stuff --------------------------------------------
01308 
01309         if (!testament)
01310                 testament = 1;
01311 
01312         if (iindex < 1) {                               // if (-) or module heading
01313                 if (testament < 2) {
01314                         if (iindex < 0) {
01315                                 testament = 0;  // previously we changed 0 -> 1
01316                                 error     = KEYERR_OUTOFBOUNDS;
01317                         }
01318                         else testament = 0;             // we want module heading
01319                 }
01320                 else {
01321                         testament--;
01322                         iindex = (offsets[testament-1][1][offsize[testament-1][1]-1] + books[testament-1][BMAX[testament-1]-1].versemax[books[testament-1][BMAX[testament-1]-1].chapmax-1]) + iindex; // What a doozy! ((offset of last chapter + number of verses in the last chapter) + iindex)
01323                 }
01324         }
01325 
01326 // --------------------------------------------------------------------
01327 
01328 
01329         if (testament) {
01330                 if ((!error) && (iindex)) {
01331                         offset  = findindex(offsets[testament-1][1], offsize[testament-1][1], iindex);
01332                         verse   = iindex - offsets[testament-1][1][offset];
01333                         book    = findindex(offsets[testament-1][0], offsize[testament-1][0], offset);
01334                         chapter = offset - offsets[testament-1][0][VerseKey::book];
01335                         verse   = (chapter) ? verse : 0;  // funny check. if we are index=1 (testmt header) all gets set to 0 exept verse.  Don't know why.  Fix if you figure out.  Think its in the offsets table.
01336                         if (verse) {            // only check if -1 won't give negative
01337                                 if (verse > books[testament-1][book-1].versemax[chapter-1]) {
01338                                         if (testament > 1) {
01339                                                 verse = books[testament-1][book-1].versemax[chapter-1];
01340                                                 error = KEYERR_OUTOFBOUNDS;
01341                                         }
01342                                         else {
01343                                                 testament++;
01344                                                 Index(verse - books[testament-2][book-1].versemax[chapter-1]);
01345                                         }
01346                                 }
01347                         }
01348                 }
01349         }
01350         if (_compare(UpperBound()) > 0) {
01351                 *this = UpperBound();
01352                 error = KEYERR_OUTOFBOUNDS;
01353         }
01354         if (_compare(LowerBound()) < 0) {
01355                 *this = LowerBound();
01356                 error = KEYERR_OUTOFBOUNDS;
01357         }
01358         return Index();
01359 }
01360 
01361 
01362 /******************************************************************************
01363  * VerseKey::compare    - Compares another SWKey object
01364  *
01365  * ENT: ikey - key to compare with this one
01366  *
01367  * RET: >0 if this versekey is greater than compare versekey
01368  *      <0 <
01369  *       0 =
01370  */
01371 
01372 int VerseKey::compare(const SWKey &ikey)
01373 {
01374         VerseKey ivkey = (const char *)ikey;
01375         return _compare(ivkey);
01376 }
01377 
01378 
01379 /******************************************************************************
01380  * VerseKey::_compare   - Compares another VerseKey object
01381  *
01382  * ENT: ikey - key to compare with this one
01383  *
01384  * RET: >0 if this versekey is greater than compare versekey
01385  *      <0 <
01386  *       0 =
01387  */
01388 
01389 int VerseKey::_compare(const VerseKey &ivkey)
01390 {
01391         long keyval1 = 0;
01392         long keyval2 = 0;
01393 
01394         keyval1 += Testament() * 1000000000;
01395         keyval2 += ivkey.Testament() * 1000000000;
01396         keyval1 += Book() * 1000000;
01397         keyval2 += ivkey.Book() * 1000000;
01398         keyval1 += Chapter() * 1000;
01399         keyval2 += ivkey.Chapter() * 1000;
01400         keyval1 += Verse();
01401         keyval2 += ivkey.Verse();
01402         keyval1 -= keyval2;
01403         keyval1 = (keyval1) ? ((keyval1 > 0) ? 1 : -1) /*keyval1/labs(keyval1)*/:0; // -1 | 0 | 1
01404         return keyval1;
01405 }

Generated on Wed Apr 3 22:34:16 2002 for The Sword Project by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002