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)
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         tmpListKey.GetElement()->userData = (void *)buf;
00383         
00384         while (*buf) {
00385                 switch (*buf) {
00386                 case ':':
00387                         number[tonumber] = 0;
00388                         tonumber = 0;
00389                         if (*number)
00390                                 chap = atoi(number);
00391                         *number = 0;
00392                         break;
00393 
00394                 case '-': 
00395                 case ',': // on number new verse
00396                 case ';': // on number new chapter
00397                         number[tonumber] = 0;
00398                         tonumber = 0;
00399                         if (*number) {
00400                                 if (chap >= 0)
00401                                         verse = atoi(number);
00402                                 else    chap = atoi(number);
00403                         }
00404                         *number = 0;
00405                         book[tobook] = 0;
00406                         tobook = 0;
00407                         bookno = -1;
00408                         if (*book) {
00409                                 for (loop = strlen(book) - 1; loop+1; loop--) {
00410                                         if ((isdigit(book[loop])) || (book[loop] == ' ')) {
00411                                                 book[loop] = 0;
00412                                                 continue;
00413                                         }
00414                                         else {
00415                                                 if ((SW_toupper(book[loop])=='F')&&(loop)) {
00416                                                         if ((isdigit(book[loop-1])) || (book[loop-1] == ' ') || (SW_toupper(book[loop-1]) == 'F')) {
00417                                                                 book[loop] = 0;
00418                                                                 continue;
00419                                                         }
00420                                                 }
00421                                         }
00422                                         break;
00423                                 }
00424 
00425                     for (loop = strlen(book) - 1; loop+1; loop--) {
00426                          if (book[loop] == ' ') {
00427                                                 if (isroman(&book[loop+1])) {
00428                                    if (verse == -1) {
00429                                         verse = chap;
00430                                                                 chap = from_rom(&book[loop+1]);
00431                                                                 book[loop] = 0;
00432                                                         }
00433                                                 }
00434                                                 break;
00435                                         }
00436                                 }
00437 
00438                                 if ((!stricmp(book, "V")) || (!stricmp(book, "VER"))) { // Verse abbrev
00439                          if (verse == -1) {
00440                               verse = chap;
00441                                                 chap = VerseKey(tmpListKey).Chapter();
00442                               *book = 0;
00443                          }
00444                     }
00445                                 bookno = getBookAbbrev(book);
00446                         }
00447                         if (((bookno > -1) || (!*book)) && ((*book) || (chap >= 0) || (verse >= 0))) {
00448                                 char partial = 0;
00449                                 curkey.Verse(1);
00450                                 curkey.Chapter(1);
00451                                 curkey.Book(1);
00452 
00453                                 if (bookno < 0) {
00454                                         curkey.Testament(VerseKey(tmpListKey).Testament());
00455                                         curkey.Book(VerseKey(tmpListKey).Book());
00456                                 }
00457                                 else {
00458                                         curkey.Testament(1);
00459                                         curkey.Book(bookno);
00460                                 }
00461 
00462                                 if (((comma)||((verse < 0)&&(bookno < 0)))&&(!lastPartial)) {
00463 //                              if (comma) {
00464                                         curkey.Chapter(VerseKey(tmpListKey).Chapter());
00465                                         curkey.Verse(chap);  // chap because this is the first number captured
00466                                 }
00467                                 else {
00468                                         if (chap >= 0) {
00469                                                 curkey.Chapter(chap);
00470                                         }
00471                                         else {
00472                                                 partial++;
00473                                                 curkey.Chapter(1);
00474                                         }
00475                                         if (verse >= 0) {
00476                                                 curkey.Verse(verse);
00477                                         }
00478                                         else {
00479                                                 partial++;
00480                                                 curkey.Verse(1);
00481                                         }
00482                                 }
00483 
00484                                 if ((*buf == '-') && (expandRange)) {   // if this is a dash save lowerBound and wait for upper
00485                                         VerseKey newElement;
00486                                         newElement.LowerBound(curkey);
00487                                         newElement.setPosition(TOP);
00488                                         tmpListKey << newElement;
00489                                         tmpListKey.GetElement()->userData = (void *)buf;
00490                                 }
00491                                 else {
00492                                         if (!dash) {    // if last separator was not a dash just add
00493                                                 if (expandRange && partial) {
00494                                                         VerseKey newElement;
00495                                                         newElement.LowerBound(curkey);
00496                                                         if (partial > 1)
00497                                                                 curkey.setPosition(MAXCHAPTER);
00498                                                         if (partial > 0)
00499                                                                 curkey = MAXVERSE;
00500                                                         newElement.UpperBound(curkey);
00501                                                         newElement = TOP;
00502                                                         tmpListKey << newElement;
00503                                                         tmpListKey.GetElement()->userData = (void *)buf;
00504                                                 }
00505                                                 else {
00506                                                         tmpListKey << (const SWKey &)(const SWKey)(const char *)curkey;
00507                                                         tmpListKey.GetElement()->userData = (void *)buf;
00508                                                 }
00509                                         }
00510                                         else    if (expandRange) {
00511                                                 VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.GetElement());
00512                                                 if (newElement) {
00513                                                         if (partial > 1)
00514                                                                 curkey = MAXCHAPTER;
00515                                                         if (partial > 0)
00516                                                                 curkey = MAXVERSE;
00517                                                         newElement->UpperBound(curkey);
00518                                                         *newElement = TOP;
00519                                                         tmpListKey.GetElement()->userData = (void *)buf;
00520                                                 }
00521                                         }
00522                                 }
00523                                 lastPartial = partial;
00524                         }
00525                         *book = 0;
00526                         chap = -1;
00527                         verse = -1;
00528                         if (*buf == ',')
00529                                 comma = 1;
00530                         else    comma = 0;
00531                         if (*buf == '-')
00532                                 dash = 1;
00533                         else    dash = 0;
00534                         break;
00535                 case 10:        // ignore these
00536                 case 13: 
00537                         break;
00538                 case '.':
00539                         if (buf > orig)                 // ignore (break) if preceeding char is not a digit
00540                                 if (!isdigit(*(buf-1)))
00541                                         break;
00542                         
00543                 default:
00544                         if (isdigit(*buf)) {
00545                                 number[tonumber++] = *buf;
00546                         }
00547                         else {
00548                                 switch (*buf) {
00549                                 case ' ':    // ignore these and don't reset number
00550                                 case 'f':
00551                                 case 'F':
00552                                         break;
00553                                 default:
00554                                         number[tonumber] = 0;
00555                                         tonumber = 0;
00556                                         break;
00557                                 }
00558                         }
00559                         if (chap == -1)
00560                                 book[tobook++] = SW_toupper(*buf);
00561                 }
00562                 buf++;
00563         }
00564         number[tonumber] = 0;
00565         tonumber = 0;
00566         if (*number) {
00567                 if (chap >= 0)
00568                         verse = atoi(number);
00569                 else    chap = atoi(number);
00570         }
00571         *number = 0;
00572         book[tobook] = 0;
00573         tobook = 0;
00574         if (*book) {
00575                 for (loop = strlen(book) - 1; loop+1; loop--) {
00576                         if ((isdigit(book[loop])) || (book[loop] == ' ')) {
00577                                 book[loop] = 0;
00578                                 continue;
00579                         }
00580                         else {
00581                                 if ((SW_toupper(book[loop])=='F')&&(loop)) {
00582                                         if ((isdigit(book[loop-1])) || (book[loop-1] == ' ') || (SW_toupper(book[loop-1]) == 'F')) {
00583                                                 book[loop] = 0;
00584                                                 continue;
00585                                         }
00586                                 }
00587                         }
00588                         break;
00589                 }
00590 
00591                 for (loop = strlen(book) - 1; loop+1; loop--) {
00592                 if (book[loop] == ' ') {
00593                 if (isroman(&book[loop+1])) {
00594                         if (verse == -1) {
00595                                 verse = chap;
00596                               chap = from_rom(&book[loop+1]);
00597                               book[loop] = 0;
00598                          }
00599                     }
00600                 break;
00601                         }
00602           }
00603                
00604                 if ((!stricmp(book, "V")) || (!stricmp(book, "VER"))) { // Verse abbrev.
00605                         if (verse == -1) {
00606                                 verse = chap;
00607                                 chap = VerseKey(tmpListKey).Chapter();
00608                                 *book = 0;
00609                         }
00610                 }
00611                         
00612                 bookno = getBookAbbrev(book);
00613         }
00614         if (((bookno > -1) || (!*book)) && ((*book) || (chap >= 0) || (verse >= 0))) {
00615                 char partial = 0;
00616                 curkey.Verse(1);
00617                 curkey.Chapter(1);
00618                 curkey.Book(1);
00619 
00620                 if (bookno < 0) {
00621                         curkey.Testament(VerseKey(tmpListKey).Testament());
00622                         curkey.Book(VerseKey(tmpListKey).Book());
00623                 }
00624                 else {
00625                         curkey.Testament(1);
00626                         curkey.Book(bookno);
00627                 }
00628 
00629                 if (((comma)||((verse < 0)&&(bookno < 0)))&&(!lastPartial)) {
00630 //              if (comma) {
00631                         curkey.Chapter(VerseKey(tmpListKey).Chapter());
00632                         curkey.Verse(chap);  // chap because this is the first number captured
00633                 }
00634                 else {
00635                         if (chap >= 0) {
00636                                 curkey.Chapter(chap);
00637                         }
00638                         else {
00639                                 partial++;
00640                                 curkey.Chapter(1);
00641                         }
00642                         if (verse >= 0) {
00643                                 curkey.Verse(verse);
00644                         }
00645                         else {
00646                                 partial++;
00647                                 curkey.Verse(1);
00648                         }
00649                 }
00650 
00651                 if ((*buf == '-') && (expandRange)) {   // if this is a dash save lowerBound and wait for upper
00652                         VerseKey newElement;
00653                         newElement.LowerBound(curkey);
00654                         newElement = TOP;
00655                         tmpListKey << newElement;
00656                         tmpListKey.GetElement()->userData = (void *)buf;
00657                 }
00658                 else {
00659                         if (!dash) {    // if last separator was not a dash just add
00660                                 if (expandRange && partial) {
00661                                         VerseKey newElement;
00662                                         newElement.LowerBound(curkey);
00663                                         if (partial > 1)
00664                                                 curkey = MAXCHAPTER;
00665                                         if (partial > 0)
00666                                                 curkey = MAXVERSE;
00667                                         newElement.UpperBound(curkey);
00668                                         newElement = TOP;
00669                                         tmpListKey << newElement;
00670                                         tmpListKey.GetElement()->userData = (void *)buf;
00671                                 }
00672                                 else {
00673                                         tmpListKey << (const SWKey &)(const SWKey)(const char *)curkey;
00674                                         tmpListKey.GetElement()->userData = (void *)buf;
00675                                 }
00676                         }
00677                         else if (expandRange) {
00678                                 VerseKey *newElement = SWDYNAMIC_CAST(VerseKey, tmpListKey.GetElement());
00679                                 if (newElement) {
00680                                         if (partial > 1)
00681                                                 curkey = MAXCHAPTER;
00682                                         if (partial > 0)
00683                                                 curkey = MAXVERSE;
00684                                         newElement->UpperBound(curkey);
00685                                         *newElement = TOP;
00686                                         tmpListKey.GetElement()->userData = (void *)buf;
00687                                 }
00688                         }
00689                 }
00690         }
00691         *book = 0;
00692         tmpListKey = TOP;
00693         tmpListKey.Remove();    // remove defaultKey
00694         internalListKey = tmpListKey;
00695         internalListKey = TOP;  // Align internalListKey to first element before passing back;
00696 
00697         return internalListKey;
00698 }
00699 
00700 
00701 /******************************************************************************
00702  * VerseKey::LowerBound - sets / gets the lower boundary for this key
00703  */
00704 
00705 VerseKey &VerseKey::LowerBound(const char *lb)
00706 {
00707         if (!lowerBound) 
00708                 initBounds();
00709 
00710         (*lowerBound) = lb;
00711         lowerBound->Normalize();
00712 
00713         return (*lowerBound);
00714 }
00715 
00716 
00717 /******************************************************************************
00718  * VerseKey::UpperBound - sets / gets the upper boundary for this key
00719  */
00720 
00721 VerseKey &VerseKey::UpperBound(const char *ub)
00722 {
00723         if (!upperBound) 
00724                 initBounds();
00725 
00726 // need to set upperbound parsing to resolve to max verse/chap if not specified
00727            (*upperBound) = ub;
00728         if (*upperBound < *lowerBound)
00729                 *upperBound = *lowerBound;
00730         upperBound->Normalize();
00731 
00732 // until we have a proper method to resolve max verse/chap use this kludge
00733         int len = strlen(ub);
00734         bool alpha = false;
00735         bool versespec = false;
00736         bool chapspec = false;
00737         for (int i = 0; i < len; i++) {
00738                 if (isalpha(ub[i]))
00739                         alpha = true;
00740                 if (ub[i] == ':')       // if we have a : we assume verse spec
00741                         versespec = true;
00742                 if ((isdigit(ub[i])) && (alpha))        // if digit after alpha assume chap spec
00743                         chapspec = true;
00744         }
00745         if (!chapspec)
00746                 *upperBound = MAXCHAPTER;
00747         if (!versespec)
00748                 *upperBound = MAXVERSE;
00749         
00750 
00751 // -- end kludge
00752 
00753         return (*upperBound);
00754 }
00755 
00756 
00757 /******************************************************************************
00758  * VerseKey::LowerBound - sets / gets the lower boundary for this key
00759  */
00760 
00761 VerseKey &VerseKey::LowerBound() const
00762 {
00763         if (!lowerBound) 
00764                 initBounds();
00765 
00766         return (*lowerBound);
00767 }
00768 
00769 
00770 /******************************************************************************
00771  * VerseKey::UpperBound - sets / gets the upper boundary for this key
00772  */
00773 
00774 VerseKey &VerseKey::UpperBound() const
00775 {
00776         if (!upperBound) 
00777                 initBounds();
00778 
00779         return (*upperBound);
00780 }
00781 
00782 
00783 /******************************************************************************
00784  * VerseKey::ClearBounds        - clears bounds for this VerseKey
00785  */
00786 
00787 void VerseKey::ClearBounds()
00788 {
00789         initBounds();
00790 }
00791 
00792 
00793 void VerseKey::initBounds() const
00794 {
00795         if (!upperBound) {
00796                 upperBound = new VerseKey();
00797                 upperBound->AutoNormalize(0);
00798                 upperBound->Headings(1);
00799         }
00800         if (!lowerBound) {
00801                 lowerBound = new VerseKey();
00802                 lowerBound->AutoNormalize(0);
00803                 lowerBound->Headings(1);
00804         }
00805 
00806         lowerBound->Testament(0);
00807         lowerBound->Book(0);
00808         lowerBound->Chapter(0);
00809         lowerBound->Verse(0);
00810 
00811         upperBound->Testament(2);
00812         upperBound->Book(BMAX[1]);
00813         upperBound->Chapter(books[1][BMAX[1]-1].chapmax);
00814         upperBound->Verse(books[1][BMAX[1]-1].versemax[upperBound->Chapter()-1]);
00815 }
00816 
00817 
00818 /******************************************************************************
00819  * VerseKey::copyFrom - Equates this VerseKey to another VerseKey
00820  */
00821 
00822 void VerseKey::copyFrom(const VerseKey &ikey) {
00823         SWKey::copyFrom(ikey);
00824 
00825         parse();
00826 }
00827 
00828 
00829 /******************************************************************************
00830  * VerseKey::copyFrom - Equates this VerseKey to another SWKey
00831  */
00832 
00833 void VerseKey::copyFrom(const SWKey &ikey) {
00834         SWKey::copyFrom(ikey);
00835 
00836         parse();
00837 }
00838 
00839 
00840 /******************************************************************************
00841  * VerseKey::getText - refreshes keytext before returning if cast to
00842  *                              a (char *) is requested
00843  */
00844 
00845 const char *VerseKey::getText() const {
00846         freshtext();
00847         return keytext;
00848 }
00849 
00850 
00851 const char *VerseKey::getShortText() const {
00852         static char *stext = 0;
00853         char buf[2047];
00854         freshtext();
00855         if (book < 1) {
00856                 if (testament < 1)
00857                         sprintf(buf, "[ Module Heading ]");
00858                 else sprintf(buf, "[ Testament %d Heading ]", (int)testament);
00859         }
00860         else {
00861                 sprintf(buf, "%s %d:%d", books[testament-1][book-1].prefAbbrev, chapter, verse);
00862         }
00863         stdstr(&stext, buf);
00864         return stext;
00865 }
00866 
00867 
00868 const char *VerseKey::getBookName() const {
00869         return books[testament-1][book-1].name;
00870 }
00871 
00872 
00873 const char *VerseKey::getBookAbbrev() const {
00874         return books[testament-1][book-1].prefAbbrev;
00875 }
00876 /******************************************************************************
00877  * VerseKey::setPosition(SW_POSITION)   - Positions this key
00878  *
00879  * ENT: p       - position
00880  *
00881  * RET: *this
00882  */
00883 
00884 void VerseKey::setPosition(SW_POSITION p) {
00885         switch (p) {
00886         case POS_TOP:
00887                 testament = LowerBound().Testament();
00888                 book      = LowerBound().Book();
00889                 chapter   = LowerBound().Chapter();
00890                 verse     = LowerBound().Verse();
00891                 break;
00892         case POS_BOTTOM:
00893                 testament = UpperBound().Testament();
00894                 book      = UpperBound().Book();
00895                 chapter   = UpperBound().Chapter();
00896                 verse     = UpperBound().Verse();
00897                 break;
00898         case POS_MAXVERSE:
00899                 Normalize();
00900                 verse     = books[testament-1][book-1].versemax[chapter-1];
00901                 break;
00902         case POS_MAXCHAPTER:
00903                 verse     = 1;
00904                 Normalize();
00905                 chapter   = books[testament-1][book-1].chapmax;
00906                 break;
00907         } 
00908         Normalize(1);
00909         Error();        // clear error from normalize
00910 }
00911 
00912 
00913 /******************************************************************************
00914  * VerseKey::increment  - Increments key a number of verses
00915  *
00916  * ENT: step    - Number of verses to jump forward
00917  *
00918  * RET: *this
00919  */
00920 
00921 void VerseKey::increment(int step) {
00922         char ierror = 0;
00923         Index(Index() + step);
00924         while ((!verse) && (!headings) && (!ierror)) {
00925                 Index(Index() + 1);
00926                 ierror = Error();
00927         }
00928 
00929         error = (ierror) ? ierror : error;
00930 }
00931 
00932 
00933 /******************************************************************************
00934  * VerseKey::decrement  - Decrements key a number of verses
00935  *
00936  * ENT: step    - Number of verses to jump backward
00937  *
00938  * RET: *this
00939  */
00940 
00941 void VerseKey::decrement(int step) {
00942         char ierror = 0;
00943 
00944         Index(Index() - step);
00945         while ((!verse) && (!headings) && (!ierror)) {
00946                 Index(Index() - 1);
00947                 ierror = Error();
00948         }
00949         if ((ierror) && (!headings))
00950                 (*this)++;
00951 
00952         error = (ierror) ? ierror : error;
00953 }
00954 
00955 
00956 /******************************************************************************
00957  * VerseKey::Normalize  - checks limits and normalizes if necessary (e.g.
00958  *                              Matthew 29:47 = Mark 2:2).  If last verse is
00959  *                              exceeded, key is set to last Book CH:VS
00960  * RET: *this
00961  */
00962 
00963 void VerseKey::Normalize(char autocheck)
00964 {
00965         error = 0;
00966 
00967         if ((autocheck) && (!autonorm)) // only normalize if we were explicitely called or if autonorm is turned on
00968                 return;
00969 
00970         if ((headings) && (!verse))             // this is cheeze and temporary until deciding what actions should be taken.
00971                 return;                                 // so headings should only be turned on when positioning with Index() or incrementors
00972 
00973         while ((testament < 3) && (testament > 0)) {
00974 
00975                 if (book > BMAX[testament-1]) {
00976                         book -= BMAX[testament-1];
00977                         testament++;
00978                         continue;
00979                 }
00980 
00981                 if (book < 1) {
00982                         if (--testament > 0) {
00983                                 book += BMAX[testament-1];
00984                         }
00985                         continue;
00986                 }
00987 
00988                 if (chapter > books[testament-1][book-1].chapmax) {
00989                         chapter -= books[testament-1][book-1].chapmax;
00990                         book++;
00991                         continue;
00992                 }
00993 
00994                 if (chapter < 1) {
00995                         if (--book > 0) {
00996                                 chapter += books[testament-1][book-1].chapmax;
00997                         }
00998                         else    {
00999                                 if (testament > 1) {
01000                                         chapter += books[0][BMAX[0]-1].chapmax;
01001                                 }
01002                         }
01003                         continue;
01004                 }
01005 
01006                 if (verse > books[testament-1][book-1].versemax[chapter-1]) { // -1 because e.g chapter 1 of Matthew is books[1][0].versemax[0]
01007                         verse -= books[testament-1][book-1].versemax[chapter++ - 1];
01008                         continue;
01009                 }
01010 
01011                 if (verse < 1) {
01012                         if (--chapter > 0) {
01013                                 verse += books[testament-1][book-1].versemax[chapter-1];
01014                         }
01015                         else    {
01016                                 if (book > 1) {
01017                                         verse += books[testament-1][book-2].versemax[books[testament-1][book-2].chapmax-1];
01018                                 }
01019                                 else    {
01020                                         if (testament > 1) {
01021                                                 verse += books[0][BMAX[0]-1].versemax[books[0][BMAX[0]-1].chapmax-1];
01022                                         }
01023                                 }
01024                         }
01025                         continue;
01026                 }
01027 
01028                 break;  // If we've made it this far (all failure checks continue) we're ok
01029         }
01030 
01031         if (testament > 2) {
01032                 testament = 2;
01033                 book      = BMAX[testament-1];
01034                 chapter   = books[testament-1][book-1].chapmax;
01035                 verse     = books[testament-1][book-1].versemax[chapter-1];
01036                 error     = KEYERR_OUTOFBOUNDS;
01037         }
01038 
01039         if (testament < 1) {
01040                 error     = ((!headings) || (testament < 0) || (book < 0)) ? KEYERR_OUTOFBOUNDS : 0;
01041                 testament = ((headings) ? 0 : 1);
01042                 book      = ((headings) ? 0 : 1);
01043                 chapter   = ((headings) ? 0 : 1);
01044                 verse     = ((headings) ? 0 : 1);
01045         }
01046         if (_compare(UpperBound()) > 0) {
01047                 *this = UpperBound();
01048                 error = KEYERR_OUTOFBOUNDS;
01049         }
01050         if (_compare(LowerBound()) < 0) {
01051                 *this = LowerBound();
01052                 error = KEYERR_OUTOFBOUNDS;
01053         }
01054 }
01055 
01056 
01057 /******************************************************************************
01058  * VerseKey::Testament - Gets testament
01059  *
01060  * RET: value of testament
01061  */
01062 
01063 char VerseKey::Testament() const
01064 {
01065         return testament;
01066 }
01067 
01068 
01069 /******************************************************************************
01070  * VerseKey::Book - Gets book
01071  *
01072  * RET: value of book
01073  */
01074 
01075 char VerseKey::Book() const
01076 {
01077         return book;
01078 }
01079 
01080 
01081 /******************************************************************************
01082  * VerseKey::Chapter - Gets chapter
01083  *
01084  * RET: value of chapter
01085  */
01086 
01087 int VerseKey::Chapter() const
01088 {
01089         return chapter;
01090 }
01091 
01092 
01093 /******************************************************************************
01094  * VerseKey::Verse - Gets verse
01095  *
01096  * RET: value of verse
01097  */
01098 
01099 int VerseKey::Verse() const
01100 {
01101         return verse;
01102 }
01103 
01104 
01105 /******************************************************************************
01106  * VerseKey::Testament - Sets/gets testament
01107  *
01108  * ENT: itestament - value which to set testament
01109  *              [MAXPOS(char)] - only get
01110  *
01111  * RET: if unchanged ->          value of testament
01112  *      if   changed -> previous value of testament
01113  */
01114 
01115 char VerseKey::Testament(char itestament)
01116 {
01117         char retval = testament;
01118 
01119         if (itestament != MAXPOS(char)) {
01120                 testament = itestament;
01121                 Normalize(1);
01122         }
01123         return retval;
01124 }
01125 
01126 
01127 /******************************************************************************
01128  * VerseKey::Book - Sets/gets book
01129  *
01130  * ENT: ibook - value which to set book
01131  *              [MAXPOS(char)] - only get
01132  *
01133  * RET: if unchanged ->          value of book
01134  *      if   changed -> previous value of book
01135  */
01136 
01137 char VerseKey::Book(char ibook)
01138 {
01139         char retval = book;
01140 
01141         Chapter(1);
01142         book = ibook;
01143         Normalize(1);
01144 
01145         return retval;
01146 }
01147 
01148 
01149 /******************************************************************************
01150  * VerseKey::Chapter - Sets/gets chapter
01151  *
01152  * ENT: ichapter - value which to set chapter
01153  *              [MAXPOS(int)] - only get
01154  *
01155  * RET: if unchanged ->          value of chapter
01156  *      if   changed -> previous value of chapter
01157  */
01158 
01159 int VerseKey::Chapter(int ichapter)
01160 {
01161         int retval = chapter;
01162 
01163         Verse(1);
01164         chapter = ichapter;
01165         Normalize(1);
01166 
01167         return retval;
01168 }
01169 
01170 
01171 /******************************************************************************
01172  * VerseKey::Verse - Sets/gets verse
01173  *
01174  * ENT: iverse - value which to set verse
01175  *              [MAXPOS(int)] - only get
01176  *
01177  * RET: if unchanged ->          value of verse
01178  *      if   changed -> previous value of verse
01179  */
01180 
01181 int VerseKey::Verse(int iverse)
01182 {
01183         int retval = verse;
01184 
01185         verse = iverse;
01186         Normalize(1);
01187 
01188         return retval;
01189 }
01190 
01191 
01192 /******************************************************************************
01193  * VerseKey::AutoNormalize - Sets/gets flag that tells VerseKey to auto-
01194  *                              matically normalize itself when modified
01195  *
01196  * ENT: iautonorm - value which to set autonorm
01197  *              [MAXPOS(char)] - only get
01198  *
01199  * RET: if unchanged ->          value of autonorm
01200  *              if   changed -> previous value of autonorm
01201  */
01202 
01203 char VerseKey::AutoNormalize(char iautonorm)
01204 {
01205         char retval = autonorm;
01206 
01207         if (iautonorm != MAXPOS(char)) {
01208                 autonorm = iautonorm;
01209                 Normalize(1);
01210         }
01211         return retval;
01212 }
01213 
01214 
01215 /******************************************************************************
01216  * VerseKey::Headings - Sets/gets flag that tells VerseKey to include
01217  *                                      chap/book/testmnt/module headings
01218  *
01219  * ENT: iheadings - value which to set headings
01220  *              [MAXPOS(char)] - only get
01221  *
01222  * RET: if unchanged ->          value of headings
01223  *              if   changed -> previous value of headings
01224  */
01225 
01226 char VerseKey::Headings(char iheadings)
01227 {
01228         char retval = headings;
01229 
01230         if (iheadings != MAXPOS(char)) {
01231                 headings = iheadings;
01232                 Normalize(1);
01233         }
01234         return retval;
01235 }
01236 
01237 
01238 /******************************************************************************
01239  * VerseKey::findindex - binary search to find the index closest, but less
01240  *                                              than the given value.
01241  *
01242  * ENT: array   - long * to array to search
01243  *              size            - number of elements in the array
01244  *              value   - value to find
01245  *
01246  * RET: the index into the array that is less than but closest to value
01247  */
01248 
01249 int VerseKey::findindex(long *array, int size, long value)
01250 {
01251         int lbound, ubound, tval;
01252 
01253         lbound = 0;
01254         ubound = size - 1;
01255         while ((ubound - lbound) > 1) {
01256                 tval = lbound + (ubound-lbound)/2;
01257                 if (array[tval] <= value)
01258                         lbound = tval;
01259                 else ubound = tval;
01260         }
01261         return (array[ubound] <= value) ? ubound : lbound;
01262 }
01263 
01264 
01265 /******************************************************************************
01266  * VerseKey::Index - Gets index based upon current verse
01267  *
01268  * RET: offset
01269  */
01270 
01271 long VerseKey::Index() const
01272 {
01273         long  offset;
01274 
01275         if (!testament) { // if we want module heading
01276                 offset = 0;
01277                 verse  = 0;
01278         }
01279         else {
01280                 if (!book)
01281                         chapter = 0;
01282                 if (!chapter)
01283                         verse   = 0;
01284 
01285                 offset = offsets[testament-1][0][book];
01286                 offset = offsets[testament-1][1][(int)offset + chapter];
01287                 if (!(offset|verse)) // if we have a testament but nothing else.
01288                         offset = 1;
01289         }
01290         return (offset + verse);
01291 }
01292 
01293 
01294 /******************************************************************************
01295  * VerseKey::Index - Gets index based upon current verse
01296  *
01297  * RET: offset
01298  */
01299 
01300 long VerseKey::NewIndex() const
01301 {
01302         static long otMaxIndex = 32300 - 8245;  // total positions - new testament positions
01303 //      static long otMaxIndex = offsets[0][1][(int)offsets[0][0][BMAX[0]] + books[0][BMAX[0]].chapmax];
01304         return ((testament-1) * otMaxIndex) + Index();
01305 }
01306 
01307 
01308 /******************************************************************************
01309  * VerseKey::Index - Sets index based upon current verse
01310  *
01311  * ENT: iindex - value to set index to
01312  *
01313  * RET: offset
01314  */
01315 
01316 long VerseKey::Index(long iindex)
01317 {
01318         long  offset;
01319 
01320 // This is the dirty stuff --------------------------------------------
01321 
01322         if (!testament)
01323                 testament = 1;
01324 
01325         if (iindex < 1) {                               // if (-) or module heading
01326                 if (testament < 2) {
01327                         if (iindex < 0) {
01328                                 testament = 0;  // previously we changed 0 -> 1
01329                                 error     = KEYERR_OUTOFBOUNDS;
01330                         }
01331                         else testament = 0;             // we want module heading
01332                 }
01333                 else {
01334                         testament--;
01335                         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)
01336                 }
01337         }
01338 
01339 // --------------------------------------------------------------------
01340 
01341 
01342         if (testament) {
01343                 if ((!error) && (iindex)) {
01344                         offset  = findindex(offsets[testament-1][1], offsize[testament-1][1], iindex);
01345                         verse   = iindex - offsets[testament-1][1][offset];
01346                         book    = findindex(offsets[testament-1][0], offsize[testament-1][0], offset);
01347                         chapter = offset - offsets[testament-1][0][VerseKey::book];
01348                         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.
01349                         if (verse) {            // only check if -1 won't give negative
01350                                 if (verse > books[testament-1][book-1].versemax[chapter-1]) {
01351                                         if (testament > 1) {
01352                                                 verse = books[testament-1][book-1].versemax[chapter-1];
01353                                                 error = KEYERR_OUTOFBOUNDS;
01354                                         }
01355                                         else {
01356                                                 testament++;
01357                                                 Index(verse - books[testament-2][book-1].versemax[chapter-1]);
01358                                         }
01359                                 }
01360                         }
01361                 }
01362         }
01363         if (_compare(UpperBound()) > 0) {
01364                 *this = UpperBound();
01365                 error = KEYERR_OUTOFBOUNDS;
01366         }
01367         if (_compare(LowerBound()) < 0) {
01368                 *this = LowerBound();
01369                 error = KEYERR_OUTOFBOUNDS;
01370         }
01371         return Index();
01372 }
01373 
01374 
01375 /******************************************************************************
01376  * VerseKey::compare    - Compares another SWKey object
01377  *
01378  * ENT: ikey - key to compare with this one
01379  *
01380  * RET: >0 if this versekey is greater than compare versekey
01381  *      <0 <
01382  *       0 =
01383  */
01384 
01385 int VerseKey::compare(const SWKey &ikey)
01386 {
01387         VerseKey ivkey = (const char *)ikey;
01388         return _compare(ivkey);
01389 }
01390 
01391 
01392 /******************************************************************************
01393  * VerseKey::_compare   - Compares another VerseKey object
01394  *
01395  * ENT: ikey - key to compare with this one
01396  *
01397  * RET: >0 if this versekey is greater than compare versekey
01398  *      <0 <
01399  *       0 =
01400  */
01401 
01402 int VerseKey::_compare(const VerseKey &ivkey)
01403 {
01404         long keyval1 = 0;
01405         long keyval2 = 0;
01406 
01407         keyval1 += Testament() * 1000000000;
01408         keyval2 += ivkey.Testament() * 1000000000;
01409         keyval1 += Book() * 1000000;
01410         keyval2 += ivkey.Book() * 1000000;
01411         keyval1 += Chapter() * 1000;
01412         keyval2 += ivkey.Chapter() * 1000;
01413         keyval1 += Verse();
01414         keyval2 += ivkey.Verse();
01415         keyval1 -= keyval2;
01416         keyval1 = (keyval1) ? ((keyval1 > 0) ? 1 : -1) /*keyval1/labs(keyval1)*/:0; // -1 | 0 | 1
01417         return keyval1;
01418 }
01419 
01420 
01421 const char *VerseKey::getOSISRef() const {
01422         static char buf[5][254];
01423         static char loop = 0;
01424 
01425         if (loop > 4)
01426                 loop = 0;
01427 
01428         static char *osisotbooks[] = {
01429                         "Gen","Exod","Lev","Num","Deut","Josh","Judg","Ruth","_1Sam","_2Sam",
01430                         "_1Kgs","_2Kgs","_1Chr","_2Chr","Ezra","Neh","Esth","Job","Ps",
01431                         "Prov",         // added this.  Was not in OSIS spec
01432                         "Eccl",
01433                         "Song","Isa","Jer","Lam","Ezek","Dan","Hos","Joel","Amos","Obad",
01434                         "Jonah","Mic","Nah","Hab","Zeph","Hag","Zech","Mal","Bar","PrAzar",
01435                         "Bel","Sus","_1Esd","_2Esd","AddEsth","EpJer","Jdt","_1Macc","_2Macc","_3Macc",
01436                         "_4Macc","PrMan","Ps151","Sir","Tob","Wis"};
01437         static char *osisntbooks[] = {
01438                         "Matt","Mark","Luke","John","Acts","Rom","_1Cor","_2Cor","Gal","Eph",
01439                         "Phil","Col","_1Thess","_2Thess","_1Tim","_2Tim","Titus","Phlm","Heb","Jas",
01440                         "_1Pet","_2Pet","_1John","_2John","_3John","Jude","Rev"};
01441         static char **osisbooks[] = { osisotbooks, osisntbooks };
01442         if (Verse())
01443                 sprintf(buf[loop], "%s.%d.%d", osisbooks[Testament()-1][Book()-1], (int)Chapter(), (int)Verse());
01444         else if (Chapter())
01445                 sprintf(buf[loop], "%s.%d", osisbooks[Testament()-1][Book()-1], (int)Chapter());
01446         else if (Book())
01447                 sprintf(buf[loop], "%s", osisbooks[Testament()-1][Book()-1]);
01448         else    sprintf(buf[loop], "");
01449         return buf[loop++];
01450 }

Generated on Thu Jun 20 22:13:01 2002 for The Sword Project by doxygen1.2.15