// ########### FROM SVN REVISION 3895 /****************************************************************************** * * versekey.cpp - code for class 'VerseKey'- a standard Biblical * verse key * * $Id$ * * Copyright 1998-2013 CrossWire Bible Society (http://www.crosswire.org) * CrossWire Bible Society * P. O. Box 2528 * Tempe, AZ 85280-2528 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation version 2. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * */ package org.crosswire.sword.keys; import org.crosswire.utils.Utils; import org.crosswire.sword.mgr.VersificationMgr; import org.crosswire.sword.mgr.SWLocale; import org.crosswire.sword.mgr.LocaleMgr; import org.apache.log4j.Logger; /** * Class VerseKey * The SWKey implementation used for verse based modules like Bibles or commentaries. */ public class VerseKey extends SWKey { private static Logger logger = Logger.getLogger(VerseKey.class); public static final int MAXVERSE = 3; public static final int MAXCHAPTER = 4; public static final int MAXBOOK = 5; static String defaultV11n = "LXXNU"; static { try { String v = Utils.getSysConfig().getProperty("PrimaryVersification"); if (v != null) defaultV11n = v; System.out.println("******* Using specified primary versification: " + defaultV11n); } catch (Exception e) {} } /** number of instantiated VerseKey objects or derivitives */ static int instance; ListKey internalListKey; VersificationMgr.System refSys; /** flag for auto normalization */ boolean autonorm; /** flag for intros on/off */ boolean intros; // internal upper/lower bounds optimizations long lowerBound, upperBound; // if autonorms is on VerseKey tmpClone = null; class VerseComponents { int test; int book; int chap; int verse; char suffix; } VerseComponents lowerBoundComponents = new VerseComponents(), upperBoundComponents = new VerseComponents(); // if autonorms is off, we can't optimize with index /** The Testament: 0 - Module Intro; 1 - Old; 2 - New */ private int testament; private int book; private int chapter; private int verse; private char suffix; int BMAX[] = null; /****************************************************************************** * VerseKey::init - initializes instance of VerseKey */ void init() { init(defaultV11n); } void init(String v11n) { BMAX = new int[2]; autonorm = true; // default auto normalization to true intros = false; // default display intros option is false upperBound = 0; lowerBound = 0; boundSet = false; testament = 1; book = 1; chapter = 1; verse = 1; suffix = 0; tmpClone = null; refSys = null; setVersificationSystem(v11n); } /****************************************************************************** * VerseKey Constructor - initializes instance of VerseKey * * ENT: ikey - base key (will take various forms of 'BOOK CH:VS'. See * VerseKey::parse for more detailed information) */ public VerseKey(SWKey ikey) { super(ikey); init(); if (ikey != null) { copyFrom(ikey); } } /****************************************************************************** * VerseKey Constructor - initializes instance of VerseKey * * ENT: ikey - text key (will take various forms of 'BOOK CH:VS'. See * VerseKey::parse for more detailed information) */ public VerseKey() { this((String)null); } public VerseKey(String ikeyText) { super(ikeyText); init(); if (ikeyText != null) parse(); } public VerseKey(VerseKey k) { init(); copyFrom(k); } // hash numbers are TBBBCCCVVV public VerseKey(int hashNumber) { this((String)null); setHashNumber(hashNumber); } public void setHashNumber(int hashNumber) { setTestament((int)hashNumber/1000000000, false); setBook((int)hashNumber/1000000%1000, false); setChapter((int)hashNumber/1000%1000, false); setVerse((int)hashNumber%1000, false); normalize(true); } public int getHashNumber() { return getTestament() * 1000000000 + getBook() * 1000000 + getChapter() * 1000 + getVerse(); } // bounds caching is mutable, thus const void initBounds() { if (tmpClone == null) { lowerBoundComponents = new VerseComponents(); upperBoundComponents = new VerseComponents(); tmpClone = (VerseKey)this.clone(); tmpClone.setAutoNormalize(false); tmpClone.setIntros(true); tmpClone.setTestament((BMAX[1]!=0)?2:1, false); tmpClone.setBook(BMAX[(BMAX[1]!=0)?1:0]); tmpClone.setChapter(tmpClone.getChapterMax()); tmpClone.setVerse(tmpClone.getVerseMax()); upperBound = tmpClone.getIndex(); upperBoundComponents.test = tmpClone.getTestament(); upperBoundComponents.book = tmpClone.getBook(); upperBoundComponents.chap = tmpClone.getChapter(); upperBoundComponents.verse = tmpClone.getVerse(); upperBoundComponents.suffix = tmpClone.getSuffix(); lowerBound = 0; lowerBoundComponents.test = 0; lowerBoundComponents.book = 0; lowerBoundComponents.chap = 0; lowerBoundComponents.verse = 0; lowerBoundComponents.suffix = 0; } else tmpClone.setLocale(getLocale()); } public int getTestamentMax() { return 2; } public int getBookMax() { return BMAX[testament-1]; } public int getChapterMax() { if (book < 1) return 0; VersificationMgr.Book b = refSys.getBook(((testament>1)?BMAX[0]:0)+book-1); return (b!=null) ? b.getChapterMax() : -1; } public int getVerseMax() { if (book < 1) return 0; VersificationMgr.Book b = refSys.getBook(((testament>1)?BMAX[0]:0)+book-1); return (b!=null) ? b.getVerseMax(chapter) : -1; } /****************************************************************************** * VerseKey::setFromOther - Positions this VerseKey to another VerseKey */ // private with no bounds check void setFromOther(VerseKey ikey) { if (refSys == ikey.refSys) { testament = ikey.getTestament(); book = ikey.getBook(); chapter = ikey.getChapter(); verse = ikey.getVerse(); suffix = ikey.getSuffix(); } else { // map verse between systems VersificationMgr.System.TranslatedVerse mapVerse = new VersificationMgr.System.TranslatedVerse( ikey.getOSISBookName(), ikey.getChapter(), ikey.getVerse(), ikey.getVerse() ); ikey.refSys.translateVerse(refSys, mapVerse); book = refSys.getBookNumberByOSISName(mapVerse.book); // check existence if (book == -1) { book = 1; error = KEYERR_OUTOFBOUNDS; } else if (refSys.getBook(book-1).getChapterMax() < mapVerse.chapter) { mapVerse.chapter = refSys.getBook(book-1).getChapterMax(); mapVerse.verse = refSys.getBook(book-1).getVerseMax(mapVerse.chapter); error = KEYERR_OUTOFBOUNDS; } else if (mapVerse.chapter > 0 && refSys.getBook(book-1).getVerseMax(mapVerse.chapter) < mapVerse.verse) { mapVerse.verse = refSys.getBook(book-1).getVerseMax(mapVerse.chapter); error = KEYERR_OUTOFBOUNDS; } // set values if (book > BMAX[0]) { book -= BMAX[0]; testament = 2; } else { testament = 1; } //if (mapVerse.verse == 0) Headings(1); chapter = mapVerse.chapter; verse = mapVerse.verse; suffix = ikey.getSuffix(); if (mapVerse.verse < mapVerse.verseEnd) { if (mapVerse.verseEnd > refSys.getBook(((testament>1)?BMAX[0]:0)+book-1).getVerseMax(chapter)) { ++mapVerse.verseEnd; } verse = mapVerse.verseEnd; setUpperBound(this); verse = mapVerse.verse; setLowerBound(this); } } } /************************************************************************ * VerseKey::getBookFromAbbrev - Attempts to find a book no from a name or * abbreviation * * ENT: abbr - key for which to search; * RET: book number or < 0 = not valid */ public int getBookFromAbbrev(String iabbr) { int diff, abLen, min, max, target, retVal = -1; String abbr = null; VersificationMgr.Abbreviation abbrevs[] = refSys != null && refSys.getDefaultAbbreviations() != null ? refSys.getDefaultAbbreviations() : getPrivateLocale().getBookAbbrevs(); for (int i = 0; i < 2; i++) { abbr = iabbr.trim(); if (i == 0) { abbr = abbr.toUpperCase(); } if (abbr.length() > 0) { min = 0; max = abbrevs.length; // binary search for a match while(true) { target = min + ((max - min) / 2); diff = abbr.compareTo(abbrevs[target].ab.substring(0, Math.min(abbrevs[target].ab.length(), abbr.length()))); if ((diff == 0)||(target >= max)||(target <= min)) break; if (diff > 0) min = target; else max = target; } // lets keep backing up till we find the 'first' valid match for (; target > 0; target--) { if (abbr.compareTo(abbrevs[target-1].ab.substring(0, Math.min(abbrevs[target-1].ab.length(), abbr.length())))!=0) break; } if (diff == 0) { // lets keep moving forward till we find an abbrev in our refSys retVal = refSys.getBookNumberByOSISName(abbrevs[target].osis); while ((retVal < 0) && (target < max) && (0==abbr.compareTo(abbrevs[target+1].ab.substring(0, Math.min(abbrevs[target+1].ab.length(), abbr.length()))))) { target++; retVal = refSys.getBookNumberByOSISName(abbrevs[target].osis); } } else retVal = -1; } if (retVal > 0) break; } return retVal; } public String getBookAbbrev() { return getPrivateLocale().translate("prefAbbr_"+refSys.getBook(((testament > 1) ? BMAX[0] : 0) + book - 1).getPreferredAbbreviation()); } /****************************************************************************** * VerseKey::freshtext - refreshes keytext based on * testament|book|chapter|verse */ void freshtext() { String buf; int realTest = testament; int realbook = book; if (book < 1) { if (testament < 1) buf = "[ Module Heading ]"; else buf = "[ Testament "+(int)testament+" Heading ]"; } else { if (realbook > BMAX[realTest-1]) { realbook -= BMAX[realTest-1]; if (realTest < 2) realTest++; if (realbook > BMAX[realTest-1]) realbook = BMAX[realTest-1]; } buf = getBookName() + " " + chapter + ":" + verse +((suffix != 0)?suffix:""); } keytext = buf; } /****************************************************************************** * VerseKey::parse - parses keytext into testament|book|chapter|verse * * RET: error status */ char parse() { return parse(true); } char parse(boolean checkAutoNormalize) { /* testament = BMAX[1]!=0?2:1; book = BMAX[BMAX[1]!=0?1:0]; chapter = 1; verse = 1; */ char error = 0; if (keytext != null) { // pass our own copy of keytext as keytext memory may be freshed during parse ListKey tmpListKey = ParseVerseList(new String(keytext)); if (tmpListKey.getCount() != 0) { this.positionFrom(tmpListKey.getElement(0)); error = this.error; } else error = 1; } if (checkAutoNormalize) { normalize(true); } freshtext(); return (this.error != 0) ? this.error : (this.error = error); } public void setText(String ikey, boolean checkNormalize) { super.setText(ikey); parse(checkNormalize); } public void setText(String ikey) { super.setText(ikey); parse(); } public boolean isTraversable() { return true; } /****************************************************************************** * VerseKey::getTestament - Gets testament * * RET: value of testament */ public int getTestament() { return testament; } /****************************************************************************** * VerseKey::getBook - Gets book * * RET: value of book */ public int getBook() { return book; } /****************************************************************************** * VerseKey::getChapter - Gets chapter * * RET: value of chapter */ public int getChapter() { return chapter; } /****************************************************************************** * VerseKey::getVerse - Gets verse * * RET: value of verse */ public int getVerse() { return verse; } /****************************************************************************** * VerseKey::setTestament - Sets/gets testament * * ENT: itestament - value which to set testament * */ public void setTestament(int itestament) { setTestament(itestament, true); } public void setTestament(int itestament, boolean norm) { suffix = 0; verse = (intros) ? 0 : 1; chapter = (intros) ? 0 : 1; book = (intros) ? 0 : 1; testament = itestament; if (norm) normalize(true); } /****************************************************************************** * VerseKey::setBook - Sets/gets book * * ENT: ibook - value which to set book */ public void setBook(int ibook) { setBook(ibook, true); } public void setBook(int ibook, boolean norm) { suffix = 0; verse = (intros) ? 0 : 1; chapter = (intros) ? 0 : 1; book = ibook; if (norm) normalize(true); } /****************************************************************************** * VerseKey::setBookName - Sets/gets book by name * * ENT: bname - book name/abbrev */ public void setBookName(String bname) { int bnum = getBookFromAbbrev(bname); if (bnum > -1) { if (bnum > BMAX[0]) { bnum -= BMAX[0]; testament = 2; } else testament = 1; setBook(bnum); } else error = KEYERR_OUTOFBOUNDS; } /****************************************************************************** * VerseKey::setChapter - Sets/gets chapter * * ENT: ichapter - value which to set chapter */ public void setChapter(int ichapter) { setChapter(ichapter, true); } public void setChapter(int ichapter, boolean norm) { suffix = 0; verse = (intros) ? 0 : 1; chapter = ichapter; if (norm) normalize(true); } /****************************************************************************** * VerseKey::setVerse - Sets verse * * ENT: iverse - value which to set verse * [MAXPOS(int)] - only get * * RET: if unchanged . value of verse * if changed . previous value of verse */ public void setVerse(int iverse) { setVerse(iverse, true); } public void setVerse(int iverse, boolean norm) { suffix = (char)0; verse = iverse; if (norm) normalize(true); } public char getSuffix() { return suffix; } public void setSuffix(char suf) { suffix = suf; } /****************************************************************************** * VerseKey::isAutoNormalize - Gets flag that tells VerseKey to auto- * matically normalize itself when modified */ public boolean isAutoNormalize() { return autonorm; } /****************************************************************************** * VerseKey::setAutoNormalize - Sets flag that tells VerseKey to auto- * matically normalize itself when modified */ public void setAutoNormalize(boolean iautonorm) { autonorm = iautonorm; normalize(true); } /****************************************************************************** * VerseKey::setIntros - Sets flag that tells VerseKey to include * chap/book/testmnt/module intros * * ENT: iintros - value which to set intros */ public void setIntros(boolean val) { intros = val; normalize(true); } /****************************************************************************** * VerseKey::isIntros - Gets flag that tells VerseKey to include * chap/book/testmnt/module intros */ public boolean isIntros() { return intros; } /****************************************************************************** * VerseKey::getIndex - Gets index based upon current verse * * RET: offset */ public long getIndex() { long offset; if (testament == 0) { // if we want module intro offset = 0; } else if (book == 0) { // we want testament intro offset = ((testament == 2) ? refSys.getNTStartOffset():0) + 1; } else { offset = refSys.getOffsetFromVerse((((testament>1)?BMAX[0]:0)+book-1), chapter, verse); } return offset; } /****************************************************************************** * VerseKey::getTestamentIndex - Gets index based upon current verse * * RET: offset */ public long getTestamentIndex() { long offset = getIndex(); return (testament > 1) ? offset - refSys.getNTStartOffset() : offset; } /****************************************************************************** * VerseKey::setIndex - Sets index based upon current verse * * ENT: iindex - value to set index to * */ public void setIndex(long iindex) { // assert we're sane if (iindex < 0) { error = KEYERR_OUTOFBOUNDS; return; } int b; VersificationMgr.BCV bcv = refSys.getVerseFromOffset(iindex); error = bcv.error; book = bcv.book; chapter = bcv.chapter; verse = bcv.verse; testament = 1; if (book > BMAX[0]) { book -= BMAX[0]; testament = 2; } // special case for Module and Testament intro if (book < 0) { testament = 0; book = 0; } if (chapter < 0) { book = 0; chapter = 0; } checkBounds(); } void checkBounds() { long i = getIndex(); initBounds(); if (i > upperBound) { setIndex(upperBound); i = getIndex(); error = KEYERR_OUTOFBOUNDS; } if (i < lowerBound) { setIndex(lowerBound); error = KEYERR_OUTOFBOUNDS; } } /****************************************************************************** * VerseKey::compare - Compares another SWKey object * * ENT: ikey - key to compare with this one * * RET: >0 if this versekey is greater than compare versekey * <0 < * 0 = */ public int compare(SWKey ikey) { if (ikey instanceof VerseKey) { return _compare((VerseKey)ikey); } return _compare(new VerseKey(ikey)); } /****************************************************************************** * VerseKey::_compare - Compares another VerseKey object * * ENT: ikey - key to compare with this one * * RET: >0 if this versekey is greater than compare versekey * <0 < * 0 = */ public int _compare(VerseKey ivkey) { int k1 = getHashNumber() - ivkey.getHashNumber(); if (k1 != 0) { return k1 > 0 ? 1 : -1; } k1 = getSuffix() - ivkey.getSuffix(); if (k1 != 0) { return k1 > 0 ? 1 : -1; } return 0; /* long keyval1 = 0; long keyval2 = 0; keyval1 += getTestament() * 1000000000; keyval2 += ivkey.getTestament() * 1000000000; keyval1 += getBook() * 10000000; keyval2 += ivkey.getBook() * 10000000; keyval1 += getChapter() * 10000; keyval2 += ivkey.getChapter() * 10000; keyval1 += getVerse() * 50; keyval2 += ivkey.getVerse() * 50; keyval1 += (int)getSuffix(); keyval2 += (int)ivkey.getSuffix(); keyval1 = (keyval1 != keyval2) ? ((keyval1 > keyval2) ? 1 : -1) : 0; // -1 | 0 | 1 return (int) keyval1; */ } public String getOSISRef() { return ((getBook()!=0)?getOSISBookName() +((getChapter()!=0)?("."+(int)getChapter()) +((getVerse()!=0)?("."+(int)getVerse()) :"") :"") :""); } /****************************************************************************** * VerseKey::getRangeText - returns parsable range text for this key */ public String getRangeText() { String rangeText; if (isBoundSet() && lowerBound != upperBound) { rangeText = getLowerBound().getText(); rangeText += "-"; rangeText += getUpperBound().getText(); } else rangeText = getText(); return rangeText; } /****************************************************************************** * VerseKey::getRangeText - returns parsable range text for this key */ public String getShortRangeText() { String rangeText; if (isBoundSet() && lowerBound != upperBound) { rangeText = getLowerBound().getShortText(); rangeText += "-"; // make shorter by removing book and/or chapter if (getLowerBound().getTestament() == getUpperBound().getTestament() && getLowerBound().getBook() == getUpperBound().getBook()) { if (getLowerBound().getChapter() != getUpperBound().getChapter()) { rangeText += getUpperBound().getChapter() + ":"; } rangeText += getUpperBound().getVerse(); } else rangeText += getUpperBound().getShortText(); } else rangeText = getShortText(); return rangeText; } /****************************************************************************** * VerseKey::getOSISRefRangeText - returns parsable range text for this key */ public String getOSISRefRangeText() { String rangeText; if (isBoundSet() && lowerBound != upperBound) { rangeText = getLowerBound().getOSISRef(); rangeText += "-"; rangeText += getUpperBound().getOSISRef(); } else rangeText = getOSISRef(); return rangeText; } public String getBookName() { String tName = null; SWLocale l = getPrivateLocale(); int bn = ((testament>1)?BMAX[0]:0)+book-1; logger.debug("bn: " + bn); VersificationMgr.Book b = refSys.getBook(bn); if (b == null) { logger.error("Couldn't find bn: " + bn + " in v11n system."); tName = Integer.toString(bn); } else { String name = b.getLongName(); tName = l.translate(name); } return tName; } public String getOSISBookName() { return refSys.getBook(((testament>1)?BMAX[0]:0)+book-1).getOSISName(); } /** Tries to parse a string and convert it into an OSIS reference * @param inRef reference string to try to parse * @param defaultKey @see ParseVerseList(..., defaultKey, ...) * static String convertToOSIS(String inRef, const SWKey *defaultKey); virtual String getRangeText() const; virtual String getOSISRefRangeText() const; /** Compares another SWKey object * * @param ikey key to compare with this one * @return >0 if this VerseKey is greater than compare SWKey, * <0 if this VerseKey is smaller than compare SWKey, * 0 if the keys are the same * virtual int compare(const SWKey &ikey); /** Compares another VerseKey object * * @param ikey key to compare with this one * @return >0 if this VerseKey is greater than compare VerseKey, * <0 if this VerseKey is smaller than compare VerseKey, * 0 if the keys are the same * virtual int _compare(const VerseKey &ikey); */ public void setVersificationSystem(String name) { // assert we have been given a valid name if (name == null || name.length() < 1) return; VersificationMgr.System newRefSys = VersificationMgr.getSystemVersificationMgr().getVersificationSystem(name); //logger.error("name: " + name + "; newRefSys: " + newRefSys); // TODO: cheese, but what should we do if requested v11n system isn't found and we don't have a current refSystem? if (newRefSys == null) newRefSys = VersificationMgr.getSystemVersificationMgr().getVersificationSystem("LXXNU"); //logger.error("newRefSys: " + newRefSys); if (refSys != newRefSys) { refSys = newRefSys; BMAX[0] = refSys.getBMAX()[0]; BMAX[1] = refSys.getBMAX()[1]; // TODO: adjust bounds for versificaion system ??? // TODO: when we have mapping done, rethink this //necessary as our bounds might not mean anything in the new v11n system clearBound(); } } public String getVersificationSystem() { return refSys.getName(); } /* // DEBUG void validateCurrentLocale() const; */ /** Only repositions this VerseKey to another VerseKey */ public void positionFrom(SWKey ikey) { error = 0; if (ikey instanceof ListKey) { SWKey k = ((ListKey)ikey).getElement(); if (k!=null) ikey = k; } if (ikey instanceof VerseKey) { setFromOther((VerseKey)ikey); } else { super.positionFrom(ikey); // extraneous parse which inadvertently clears error flag // SWKey::positionFrom already calls copyFrom which calls setText, which VerseKey::setText already calls parse() // parse(); } // should we always perform bounds checks? Tried but seems to cause infinite recursion if (_compare(getUpperBound()) > 0) { setFromOther(getUpperBound()); error = KEYERR_OUTOFBOUNDS; } if (_compare(getLowerBound()) < 0) { setFromOther(getLowerBound()); error = KEYERR_OUTOFBOUNDS; } } /****************************************************************************** * VerseKey::copyFrom - Equates this VerseKey to another VerseKey */ public void copyFrom(VerseKey ikey) { autonorm = ikey.autonorm; intros = ikey.intros; testament = ikey.getTestament(); book = ikey.getBook(); chapter = ikey.getChapter(); verse = ikey.getVerse(); suffix = ikey.getSuffix(); setLocale(ikey.getLocale()); setVersificationSystem(ikey.getVersificationSystem()); if (ikey.isBoundSet()) { setLowerBound(ikey.getLowerBound()); setUpperBound(ikey.getUpperBound()); } } /****************************************************************************** * VerseKey::copyFrom - Equates this VerseKey to another SWKey */ public void copyFrom(SWKey ikey) { // check to see if we can do a more specific copy // plus some optimizations if (ikey instanceof ListKey) { SWKey k = ((ListKey)ikey).getElement(); if (k!=null) ikey = k; } if (ikey instanceof VerseKey) { copyFrom((VerseKey)ikey); } else { super.copyFrom(ikey); // extraneous parse which inadvertently clears error flag // SWKey::copyFrom already calls setText, which VerseKey::setText already calls parse() // parse(); } } public VerseKey(String min, String max) { this(min, max, defaultV11n); } public VerseKey(String min, String max, String v11n) { super(); init(v11n); ListKey tmpListKey = ParseVerseList(min); if (tmpListKey.getCount() != 0) { setLowerBound((VerseKey)tmpListKey.getElement(0)); } tmpListKey = ParseVerseList(max, min, true); if (tmpListKey.getCount() != 0) { VerseKey newElement = (VerseKey)tmpListKey.getElement(0); setUpperBound((newElement.isBoundSet())?newElement.getUpperBound():newElement); } setPosition(TOP); } /** Creates a new SWKey based on the current VerseKey * see also the Copy Constructor */ public SWKey clone() { return new VerseKey(this); } /****************************************************************************** * VerseKey::validateCurrentLocale - be sure a locale book abbrevs set is complete * */ public void validateCurrentLocale() { /* if (SWLog::getSystemLog().getLogLevel() >= SWLog::LOG_DEBUG) { //make sure log is wanted, this loop stuff costs a lot of time for (int i = 0; i < refSys.getBookCount(); i++) { const int bn = getBookFromAbbrev(getPrivateLocale().translate(refSys.getBook(i).getLongName())); if (bn != i+1) { char *abbr = 0; stdstr(&abbr, getPrivateLocale().translate(refSys.getBook(i).getLongName()), 2); strstrip(abbr); SWLog::getSystemLog().logWarning("VerseKey::Book: %s does not have a matching toupper abbrevs entry! book number returned was: %d, should be %d. Required entry to add to locale:", abbr, bn, i); StringMgr* stringMgr = StringMgr::getSystemStringMgr(); const bool hasUTF8Support = StringMgr::hasUTF8Support(); if (hasUTF8Support) { //we have support for UTF-8 handling; we expect UTF-8 encoded locales stringMgr.upperUTF8(abbr, strlen(abbr)*2); } else { stringMgr.upperLatin1(abbr); } SWLog::getSystemLog().logDebug("%s=%s\n", abbr, refSys.getBook(i).getOSISName()); delete [] abbr; } } } */ } /****************************************************************************** * VerseKey::ParseVerseList - Attempts to parse a buffer into separate * verse entries returned in a ListKey * * ENT: buf - buffer to parse; * defaultKey - if verse, chap, book, or testament is left off, * pull info from this key (ie. Gen 2:3; 4:5; * Gen would be used when parsing the 4:5 section) * expandRange - whether or not to expand eg. John 1:10-12 or just * save John 1:10 * * RET: ListKey reference filled with verse entries contained in buf * * COMMENT: This code works but wreaks. Rewrite to make more maintainable. */ public ListKey ParseVerseList(String buf) { return parseVerseList(buf, null); } public ListKey ParseVerseList(String buf, String defaultKey) { return parseVerseList(buf, defaultKey, false); } public ListKey parseVerseList(String buf, String defaultKey) { return parseVerseList(buf, defaultKey, false); } public ListKey ParseVerseList(String buf, String defaultKey, boolean expandRange) { return parseVerseList(buf, defaultKey, expandRange, false); } public ListKey parseVerseList(String buf, String defaultKey, boolean expandRange) { return parseVerseList(buf, defaultKey, expandRange, false); } public ListKey ParseVerseList(String buf, String defaultKey, boolean expandRange, boolean useChapterAsVerse) { return parseVerseList(buf, defaultKey, expandRange, useChapterAsVerse); } public ListKey parseVerseList(String buf, String defaultKey, boolean expandRange, boolean useChapterAsVerse) { String book = ""; String number = ""; char suffix = 0; int chap = -1, verse = -1; int bookno = 0; int loop; char comma = 0; char dash = 0; int q; ListKey tmpListKey = new ListKey(); ListKey internalListKey = new ListKey(); char lastPartial = 0; boolean inTerm = true; int notAllDigits = 0; boolean doubleF = false; // assert we have a buffer if (buf == null) return internalListKey; if (buf.length() > 6 && buf.charAt(0) == 'B' && Character.isDigit(buf.charAt(1)) && Character.isDigit(buf.charAt(2)) && (buf.charAt(3) == 'K' || buf.charAt(3) == 'i' || buf.charAt(3) == 'e')) { VerseKey vk = (VerseKey)this.clone(); vk.clearBounds(); // vk.setTestament(2); String bk = "" + buf.charAt(1) + buf.charAt(2); vk.setBook(Integer.parseInt(bk.trim()), false); if (buf.length() > 9 && "incipit".equals(buf.substring(3,10))) { vk.setChapter(0, false); vk.setVerse(0); } else if (buf.length() > 10 && "explicit".equals(buf.substring(3,11))) { vk.setChapter(1, false); vk.setVerse(0); } else { int offset = buf.indexOf('V'); String ch = null; String vs = null; if (offset > -1) { ch = buf.substring(4, offset); if ("Inscriptio".equals(ch)) { vk.setChapter(0, false); vk.setVerse(0); } else if ("Subscriptio".equals(ch)) { vk.setChapter(1, false); vk.setVerse(0); } else { try { vk.setChapter(Integer.parseInt(ch.trim()), false); vs = buf.substring(offset + 1); vk.setVerse(Integer.parseInt(vs.trim())); } catch(Exception e) { logger.error("Couldn't parse BKV: " + buf, e); } } } } internalListKey.add(vk); return internalListKey; } VerseKey curKey = (VerseKey)this.clone(); VerseKey lastKey = (VerseKey)this.clone(); lastKey.clearBounds(); curKey.clearBounds(); // some silly checks for corner cases if ("[ Module Heading ]".equals(buf)) { curKey.setVerse(0, false); curKey.setChapter(0, false); curKey.setBook(0, false); curKey.setTestament(0); lastKey.setLowerBound(curKey); lastKey.setUpperBound(curKey); internalListKey.add(lastKey); return internalListKey; } if (buf.startsWith("[ Testament ") && (Character.isDigit(buf.charAt(12))) && (buf.endsWith(" Heading ]"))) { curKey.setVerse(0, false); curKey.setChapter(0, false); curKey.setBook(0, false); curKey.setTestament(Integer.parseInt(buf.substring(12, 13).trim())); lastKey.setLowerBound(curKey); lastKey.setUpperBound(curKey); internalListKey.add(lastKey); return internalListKey; } curKey.setAutoNormalize(isAutoNormalize()); lastKey.setAutoNormalize(false); if (defaultKey != null) lastKey.setText(defaultKey); int offset = 0; boolean resetNumber = false; while (offset < buf.length()) { boolean gotoTerminateRange = false; if (resetNumber) { resetNumber = false; number = ""; } switch (buf.charAt(offset)) { case ':': if (buf.charAt(offset+1) != ' ') { // for silly "Mat 1:1: this verse...." if (number.length() > 0) { if (chap >= 0) verse = Integer.parseInt(number.trim()); else chap = Integer.parseInt(number.trim()); } number = ""; comma = 0; break; } gotoTerminateRange = true; // otherwise drop down to next case case ' ': if (!gotoTerminateRange) { inTerm = true; while (true) { if ((number.length() == 0) || (chap < 0)) break; for (q = 1; (((offset+q) < buf.length()) && (buf.charAt(offset+q) != ' ')); q++); if (((offset+q) < buf.length()) && buf.charAt(offset+q) == ':') break; inTerm = false; break; } if (inTerm) { if (!book.endsWith(" ")) { book += ' '; } break; } } case '-': if (!gotoTerminateRange) { if (chap == -1) { int checkBook = getBookFromAbbrev(book+buf.substring(offset, offset+2)); if (checkBook > -1) { book += buf.substring(offset, offset+2); offset++; break; } } } // terminateRange: case ',': // on number new verse case ';': // on number new chapter if (number.length() > 0) { if (chap >= 0) verse = Integer.parseInt(number.trim()); else chap = Integer.parseInt(number.trim()); } number = ""; bookno = -1; if (book.length() > 0) { book = book.trim(); if (book.length() > 1 && Character.isDigit(book.charAt(book.length()-2)) && book.charAt(book.length()-1) >= 'a' && book.charAt(book.length()-1) <= 'z') { book = book.substring(0, book.length()-2); } loop = book.length() - 1; for (; loop+1 > 0; loop--) { if (book.charAt(loop) == ' ') book = book.substring(0, loop); else break; } if (loop > 0 && Character.isDigit(book.charAt(loop-1)) && book.charAt(loop) >= 'a' && book.charAt(loop) <= 'z') { book = book.substring(0, loop--); } for (; loop+1 > 0; loop--) { if ((Character.isDigit(book.charAt(loop))) || (book.charAt(loop) == ' ')) { book = book.substring(0, loop); continue; } else { if ((Character.toUpperCase(book.charAt(loop))=='F')&&(loop > 0)) { if (Character.isDigit(book.charAt(loop-1)) || (book.charAt(loop-1) == ' ') || (Character.toUpperCase(book.charAt(loop-1)) == 'F')) { book = book.substring(0, loop); continue; } } } break; } for (loop = book.length() - 1; loop+1 > 0; loop--) { if (book.charAt(loop) == ' ') { // "PS C" is ok, but "II C" is not ok if (isRoman(book.substring(loop+1), 0) && !isRoman(book,loop)) { if (verse == -1) { verse = chap; chap = fromRoman(book.substring(loop+1)); book = book.substring(0, loop); } } break; } } // check for special inscriptio and subscriptio which are saved as book intro and chap 1 intro (for INTF) for (loop = book.length() - 1; loop+1 > 0; loop--) { if (book.charAt(loop) == ' ') { if (book.regionMatches(true, loop+1, "inscriptio", 0, book.length() - (loop+1))) { book = book.substring(0, loop); verse = 0; chap = 0; } else if (book.regionMatches(true, loop+1, "subscriptio", 0, book.length() - (loop+1))) { book = book.substring(0, loop); verse = 0; chap = 1; } break; } } if ("V".equalsIgnoreCase(book) || ("VER".equalsIgnoreCase(book))) { // Verse abbrev if (verse == -1) { verse = chap; chap = lastKey.getChapter(); book = ""; } } if ("ch".equalsIgnoreCase(book) || ("chap".equalsIgnoreCase(book))) { // Verse abbrev book = lastKey.getBookName(); } bookno = getBookFromAbbrev(book); if ((bookno > -1) && (suffix == 'f') && (book.endsWith("f"))) { suffix = 0; } } if (((bookno > -1) || (book.length() == 0)) && ((book.length() > 0) || (chap >= 0) || (verse >= 0))) { char partial = 0; curKey.setVerse(1, false); curKey.setChapter(1, false); curKey.setBook(1); if (bookno < 0) { curKey.setTestament(lastKey.getTestament(), false); curKey.setBook(lastKey.getBook()); } else { int t = 1; if (bookno > BMAX[0]) { t++; bookno -= BMAX[0]; } curKey.setTestament(t, false); curKey.setBook(bookno); } if (((comma != 0) || ((verse < 0) && (bookno < 0))) && (lastPartial == 0)) { curKey.setChapter(lastKey.getChapter(), false); curKey.setVerse(chap); // chap because this is the first number captured if (suffix != 0) { curKey.setSuffix(suffix); } } else { if (useChapterAsVerse && verse < 0 && chap > 0 && curKey.getChapterMax() == 1) { verse = chap; chap = 1; } if (chap >= 0) { curKey.setChapter(chap); } else { partial++; curKey.setChapter(1); } if (verse >= 0) { curKey.setVerse(verse); if (suffix != 0) { curKey.setSuffix(suffix); } } else { partial++; curKey.setVerse(1); } } // check for '-' for (q = 0; ((q+offset < buf.length()) && (buf.charAt(offset+q) == ' ')); q++); if ((buf.charAt(q+offset) == '-') && (expandRange)) { // if this is a dash save lowerBound and wait for upper offset += q; lastKey.setLowerBound(curKey); lastKey.setPosition(TOP); tmpListKey.add(lastKey); ((VerseKey)tmpListKey.getElement()).setAutoNormalize(isAutoNormalize()); tmpListKey.getElement().userData = buf.substring(offset); } else { if (dash == 0) { // if last separator was not a dash just add if (expandRange && partial != 0) { lastKey.setLowerBound(curKey); if (partial > 1) curKey.setPosition(MAXCHAPTER); if (partial > 0) curKey.setPosition(MAXVERSE); lastKey.setUpperBound(curKey); lastKey.setPosition(TOP); tmpListKey.add(lastKey); ((VerseKey)tmpListKey.getElement()).setAutoNormalize(isAutoNormalize()); tmpListKey.getElement().userData = buf.substring(offset); } else { boolean f = false; if (curKey.getSuffix() == 'f') { curKey.setSuffix('\0'); f = true; } lastKey.setLowerBound(curKey); if (f && doubleF) curKey.setPosition(MAXVERSE); else if (f) curKey.increment(); lastKey.setUpperBound(curKey); lastKey.setPosition(TOP); tmpListKey.add(lastKey); ((VerseKey)tmpListKey.getElement()).setAutoNormalize(isAutoNormalize()); tmpListKey.getElement().userData = buf.substring(offset); } } else if (expandRange) { if (tmpListKey.getElement() instanceof VerseKey) { VerseKey newElement = (VerseKey) tmpListKey.getElement(); if (partial > 1) curKey.setPosition(MAXCHAPTER); if (partial > 0) curKey.setPosition(MAXVERSE); newElement.setUpperBound(curKey); lastKey = (VerseKey)curKey.clone(); newElement.setPosition(TOP); tmpListKey.getElement().userData = buf.substring(offset); } } } lastPartial = partial; } book = ""; chap = -1; verse = -1; suffix = 0; if (buf.charAt(offset) == ',') comma = 1; else comma = 0; if (buf.charAt(offset) == '-') dash = 1; else dash = 0; break; case 10: // ignore these case 13: case '[': case ']': case '(': case ')': case '{': case '}': break; case '.': if (offset > 0) { // ignore (break) if preceeding char is not a digit notAllDigits = 0; for (char ch : book.toCharArray()) { if ((!Character.isDigit(ch)) && (" .".indexOf(ch) < 0)) { notAllDigits = 1; break; } } if (notAllDigits == 0 && !Character.isDigit(buf.charAt(offset+1))) break; } if (number.length() > 0) { if (chap >= 0) verse = Integer.parseInt(number.trim()); else chap = Integer.parseInt(number.trim()); } else if (chap == -1 && (!book.endsWith(" "))) { book += ' '; } number = ""; break; default: if (Character.isDigit(buf.charAt(offset))) { number += buf.charAt(offset); suffix = 0; doubleF = false; } else { switch (buf.charAt(offset)) { case ' ': // ignore these and don't reset number case 'F': break; default: // suffixes (and oddly 'f'-- ff.) if ((buf.charAt(offset) >= 'a' && buf.charAt(offset) <= 'z' && (chap >=0 || bookno > -1 || lastKey.isBoundSet())) || buf.charAt(offset) == 'f') { // if suffix is already an 'f', then we need to mark if we're doubleF. doubleF = (buf.charAt(offset) == 'f' && suffix == 'f'); if (suffix != 0 && !doubleF) { // we've already had a suffix one, so this is another letter, thus any number is not a number, e.g., '2jn'. We're on 'n' resetNumber = true; } suffix = buf.charAt(offset); } else { resetNumber = true; } break; } } if (chap == -1) book += buf.charAt(offset); } offset++; } if (resetNumber) { resetNumber = false; number = ""; } if (number.length() > 0) { if (chap >= 0) verse = Integer.parseInt(number.trim()); else chap = Integer.parseInt(number.trim()); } number = ""; if (book.length() > 0) { book = book.trim(); if (book.length() > 1 && Character.isDigit(book.charAt(book.length()-2)) && book.charAt(book.length()-1) >= 'a' && book.charAt(book.length()-1) <= 'z') { book = book.substring(0, book.length()-2); } for (loop = book.length() - 1; loop+1 > 0; loop--) { if ((Character.isDigit(book.charAt(loop))) || (book.charAt(loop) == ' ')) { book = book.substring(0, loop); continue; } else { if ((Character.toUpperCase(book.charAt(loop)) == 'F') && (loop > 0)) { if ((Character.isDigit(book.charAt(loop-1))) || (book.charAt(loop-1) == ' ') || (Character.toUpperCase(book.charAt(loop-1)) == 'F')) { book = book.substring(0, loop); continue; } } } break; } // check for roman numeral chapter for (loop = book.length() - 1; loop + 1 > 0; loop--) { if (book.charAt(loop) == ' ') { // "PS C" is ok, but "II C" is not ok if (isRoman(book.substring(loop+1), 0) && !isRoman(book,loop)) { if (verse == -1) { verse = chap; chap = fromRoman(book.substring(loop+1)); book = book.substring(0, loop); } } break; } } // check for special inscriptio and subscriptio which are saved as book intro and chap 1 intro (for INTF) for (loop = book.length() - 1; loop+1 > 0; loop--) { if (book.charAt(loop) == ' ') { if (book.regionMatches(true, loop+1, "inscriptio", 0, book.length() - (loop+1))) { book = book.substring(0, loop); verse = 0; chap = 0; suffix = 0; } else if (book.regionMatches(true, loop+1, "subscriptio", 0, book.length() - (loop+1))) { book = book.substring(0, loop); verse = 0; chap = 1; suffix = 0; } break; } } if ("V".equalsIgnoreCase(book) || ("VER".equalsIgnoreCase(book))) { // Verse abbrev if (verse == -1) { verse = chap; chap = lastKey.getChapter(); book = ""; } } if ("ch".equalsIgnoreCase(book) || ("chap".equalsIgnoreCase(book))) { // Verse abbrev book = lastKey.getBookName(); } bookno = getBookFromAbbrev(book); if ((bookno > -1) && (suffix == 'f') && (book.endsWith("f"))) { suffix = 0; } } if (((bookno > -1) || (book.length() == 0)) && ((book.length() > 0) || (chap >= 0) || (verse >= 0))) { char partial = 0; curKey.setVerse(1, false); curKey.setChapter(1, false); curKey.setBook(1); if (bookno < 0) { curKey.setTestament(lastKey.getTestament(), false); curKey.setBook(lastKey.getBook()); } else { int t = 1; if (bookno > BMAX[0]) { t++; bookno -= BMAX[0]; } curKey.setTestament(t, false); curKey.setBook(bookno); } if (((comma != 0) || ((verse < 0) && (bookno < 0))) && (lastPartial == 0)) { curKey.setChapter(lastKey.getChapter(), false); curKey.setVerse(chap); // chap because this is the first number captured if (suffix != 0) { curKey.setSuffix(suffix); } } else { if (useChapterAsVerse && verse < 0 && chap > 0 && curKey.getChapterMax() == 1) { verse = chap; chap = 1; } if (chap >= 0) { curKey.setChapter(chap); } else { partial++; curKey.setChapter(1); } if (verse >= 0) { curKey.setVerse(verse); if (suffix != 0) { curKey.setSuffix(suffix); } } else { partial++; curKey.setVerse(1); } } if ((offset < buf.length() && buf.charAt(offset) == '-') && (expandRange)) { // if this is a dash save lowerBound and wait for upper lastKey.setLowerBound(curKey); lastKey.setPosition(TOP); tmpListKey.add(lastKey); tmpListKey.getElement().userData = buf.substring(offset); } else { if (dash == 0) { // if last separator was not a dash just add if (expandRange && partial != 0) { lastKey.setLowerBound(curKey); if (partial > 1) curKey.setPosition(MAXCHAPTER); if (partial > 0) curKey.setPosition(MAXVERSE); lastKey.setUpperBound(curKey); lastKey.setPosition(TOP); tmpListKey.add(lastKey); tmpListKey.getElement().userData = buf.substring(offset); } else { boolean f = false; if (curKey.getSuffix() == 'f') { curKey.setSuffix('\0'); f = true; } lastKey.setLowerBound(curKey); if (f && doubleF) curKey.setPosition(MAXVERSE); else if (f) curKey.increment(); lastKey.setUpperBound(curKey); lastKey.setPosition(TOP); tmpListKey.add(lastKey); tmpListKey.getElement().userData = buf.substring(offset); } } else if (expandRange) { if (tmpListKey.getElement() instanceof VerseKey) { VerseKey newElement = (VerseKey) tmpListKey.getElement(); if (partial > 1) curKey.setPosition(MAXCHAPTER); if (partial > 0) curKey.setPosition(MAXVERSE); newElement.setUpperBound(curKey); newElement.setPosition(TOP); tmpListKey.getElement().userData = buf.substring(offset); } } } } book = ""; tmpListKey.setPosition(TOP); internalListKey = tmpListKey; internalListKey.setPosition(TOP); // Align internalListKey to first element before passing back; return internalListKey; } /****************************************************************************** * VerseKey::setLowerBound - sets the lower boundary for this key */ public void setLowerBound(VerseKey lb) { initBounds(); lowerBound = lb.getIndex(); lowerBoundComponents.test = lb.getTestament(); lowerBoundComponents.book = lb.getBook(); lowerBoundComponents.chap = lb.getChapter(); lowerBoundComponents.verse = lb.getVerse(); lowerBoundComponents.suffix = lb.getSuffix(); // both this following check and UpperBound check force upperBound to // change allowing LowerBound then UpperBound logic to always flow // and set values without restrictions, as expected if (upperBound < lowerBound) upperBound = lowerBound; boundSet = true; } /****************************************************************************** * VerseKey::setUpperBound - sets the upper boundary for this key */ public void setUpperBound(VerseKey ub) { initBounds(); upperBound = ub.getIndex(); upperBoundComponents.test = ub.getTestament(); upperBoundComponents.book = ub.getBook(); upperBoundComponents.chap = ub.getChapter(); upperBoundComponents.verse = ub.getVerse(); upperBoundComponents.suffix = ub.getSuffix(); // see setLowerBound comment, above if (upperBound < lowerBound) upperBound = lowerBound; boundSet = true; } /****************************************************************************** * VerseKey::getLowerBound - gets the lower boundary for this key */ public VerseKey getLowerBound() { initBounds(); if (!isAutoNormalize()) { tmpClone.testament = lowerBoundComponents.test; tmpClone.book = lowerBoundComponents.book; tmpClone.chapter = lowerBoundComponents.chap; tmpClone.setVerse (lowerBoundComponents.verse); tmpClone.setSuffix (lowerBoundComponents.suffix); } else { tmpClone.setIndex(lowerBound); tmpClone.setSuffix (lowerBoundComponents.suffix); } return tmpClone; } /****************************************************************************** * VerseKey::getUpperBound - gets the upper boundary for this key */ public VerseKey getUpperBound() { initBounds(); if (!isAutoNormalize()) { tmpClone.testament = upperBoundComponents.test; tmpClone.book = upperBoundComponents.book; tmpClone.chapter = upperBoundComponents.chap; tmpClone.setVerse (upperBoundComponents.verse); tmpClone.setSuffix (upperBoundComponents.suffix); } else { tmpClone.setIndex(upperBound); tmpClone.setSuffix (upperBoundComponents.suffix); } return tmpClone; } /****************************************************************************** * VerseKey::ClearBounds - clears bounds for this VerseKey */ public void clearBounds() { tmpClone = null; super.clearBound(); } /****************************************************************************** * VerseKey::getText - refreshes keytext before returning if cast to * a (char *) is requested */ public String getText() { freshtext(); return keytext; } public String getBKVText() { return "B"+((book < 10)?"0":"")+book+"K"+chapter+"V"+verse; } public String getShortText() { String buf; freshtext(); if (book < 1) { if (testament < 1) buf = "[ Module Heading ]"; else buf = "[ Testament "+testament+" Heading ]"; } else { if (chapter == 0) buf = getBookAbbrev() + " inscriptio"; else if (chapter == 1 && verse == 0) buf = getBookAbbrev() + " subscriptio"; else buf = getBookAbbrev() + " " + chapter + ":" +verse; } return buf; } /****************************************************************************** * VerseKey::setPosition(SW_POSITION) - Positions this key * * ENT: p - position * * RET: *this */ public void setPosition(int p) { switch (p) { case TOP: { VerseKey lb = getLowerBound(); testament = (lb.getTestament() != 0 || intros) ? lb.getTestament() : 1; book = (lb.getBook() != 0 || intros) ? lb.getBook() : 1; chapter = (lb.getChapter() != 0 || intros) ? lb.getChapter() : 1; verse = (lb.getVerse() != 0 || intros) ? lb.getVerse() : 1; suffix = lb.getSuffix(); break; } case BOTTOM: { VerseKey ub = getUpperBound(); testament = (ub.getTestament() != 0 || intros) ? ub.getTestament() : 1; book = (ub.getBook() != 0 || intros) ? ub.getBook() : 1; chapter = (ub.getChapter() != 0 || intros) ? ub.getChapter() : 1; verse = (ub.getVerse() != 0 || intros) ? ub.getVerse() : 1; suffix = ub.getSuffix(); break; } case MAXVERSE: suffix = 0; verse = 1; normalize(); verse = getVerseMax(); suffix = 0; break; case MAXCHAPTER: suffix = 0; verse = 1; chapter = 1; normalize(); chapter = getChapterMax(); break; } normalize(true); popError(); // clear error from normalize } /****************************************************************************** * VerseKey::increment - Increments key a number of verses * * ENT: step - Number of verses to jump forward * * RET: *this */ public void increment() { increment(1); } public void increment(int step) { // if we're not autonormalizing and we're already not normalized if (!autonorm && chapter > 0 && verse > getVerseMax()) { verse += step; checkBounds(); return; } char ierror = 0; int singleStep = step < 0 ? -1 : 1; for (; step != 0; step -= singleStep) { setIndex(getIndex() + singleStep); while ((verse==0) && (!intros) && (ierror==0)) { setIndex(getIndex() + 1); ierror = popError(); } error = (ierror != 0) ? ierror : error; if (error != 0) return; } } /****************************************************************************** * VerseKey::decrement - Decrements key a number of verses * * ENT: step - Number of verses to jump backward * * RET: *this */ public void decrement() { decrement(1); } public void decrement(int step) { // if we're not autonormalizing and we're already not normalized if (!autonorm && chapter > 0 && verse > getVerseMax()) { verse -= step; checkBounds(); return; } char ierror = 0; setIndex(getIndex() - step); while ((verse==0) && (!intros) && (ierror==0)) { setIndex(getIndex() - 1); ierror = popError(); } if ((ierror!=0) && (!intros)) increment(1); error = (ierror!=0) ? ierror : error; } /****************************************************************************** * VerseKey::normalize - checks limits and normalizes if necessary (e.g. * Matthew 29:47 = Mark 2:2). If last verse is * exceeded, key is set to last Book CH:VS * RET: *this */ public void normalize() { normalize(false); } public void normalize(boolean autocheck) { if ((!autocheck || autonorm) // only normalize if we were explicitely called or if autonorm is turned on ) { error = 0; while ((testament < 3) && (testament > 0)) { if (book > BMAX[testament-1]) { book -= (BMAX[testament-1] + (intros?1:0)); testament++; continue; } if (book < (intros?0:1)) { if (--testament > 0) { book += (BMAX[testament-1] + (intros?1:0)); } continue; } if (chapter > getChapterMax()) { chapter -= (getChapterMax() + (intros?1:0)); book++; continue; } if (chapter < (intros?0:1)) { --book; if (book < (intros?0:1)) { if (--testament > 0) { book += (BMAX[testament-1] + (intros?1:0)); } } chapter += (getChapterMax() + (intros?1:0)); continue; } if (chapter > 0 && verse > getVerseMax()) { verse -= (getVerseMax() + (intros?1:0)); chapter++; continue; } if (verse < (intros?0:1)) { if (--chapter < (intros?0:1)) { --book; if (book < (intros?0:1)) { if (--testament > 0) { book += (BMAX[testament-1] + (intros?1:0)); } } chapter += (getChapterMax() + (intros?1:0)); } verse += (getVerseMax() + (intros?1:0)); continue; } break; // If we've made it this far (all failure checks continue) we're ok } if (testament > (BMAX[1]!=0?2:1)) { testament = BMAX[1]!=0?2:1; book = BMAX[testament-1]; chapter = getChapterMax(); verse = getVerseMax(); error = KEYERR_OUTOFBOUNDS; } if (testament < 1) { error = ((!intros) || (testament < 0) || (book < 0)) ? KEYERR_OUTOFBOUNDS : 0; testament = ((intros) ? 0 : 1); book = ((intros) ? 0 : 1); chapter = ((intros) ? 0 : 1); verse = ((intros) ? 0 : 1); } // should we always perform bounds checks? Tried but seems to cause infinite recursion if (boundSet && _compare(getUpperBound()) > 0) { positionFrom(getUpperBound()); error = KEYERR_OUTOFBOUNDS; } if (boundSet && _compare(getLowerBound()) < 0) { positionFrom(getLowerBound()); error = KEYERR_OUTOFBOUNDS; } } } /* // TODO: this is static so we have no context. We can only parse LXXNU v11n now // possibly add a const char *versification = LXXNU param? const char *VerseKey::convertToOSIS(const char *inRef, const SWKey *lastKnownKey) { static SWBuf outRef; outRef = ""; VerseKey defLanguage; ListKey verses = defLanguage.ParseVerseList(inRef, (*lastKnownKey), true); const char *startFrag = inRef; for (int i = 0; i < verses.Count(); i++) { SWKey *element = verses.GetElement(i); // VerseKey *element = SWDYNAMIC_CAST(VerseKey, verses.GetElement(i)); SWBuf buf; // TODO: This code really needs to not use fixed size arrays char frag[800]; char preJunk[800]; char postJunk[800]; memset(frag, 0, 800); memset(preJunk, 0, 800); memset(postJunk, 0, 800); while ((*startFrag) && (strchr(" {}:;,()[].", *startFrag))) { outRef += *startFrag; startFrag++; } memmove(frag, startFrag, (size_t)((const char *)element.userData - startFrag) + 1); frag[((const char *)element.userData - startFrag) + 1] = 0; int j; for (j = strlen(frag)-1; j && (strchr(" {}:;,()[].", frag[j])); j--); if (frag[j+1]) strcpy(postJunk, frag+j+1); frag[j+1]=0; startFrag += ((const char *)element.userData - startFrag) + 1; buf = ""; buf += frag; buf += ""; buf += postJunk; outRef += buf; } if (startFrag < (inRef + strlen(inRef))) outRef += startFrag; return outRef.c_str(); } */ public static boolean isRoman(String str, int maxChars) { for (char ch : str.toCharArray()) { if ((maxChars--)-1 == 1) break; if ("IVXLCDMivxlcdm ".indexOf(ch) < 0) return false; } return true; } public static int fromRoman(String str) { int i, n = str.length(); short num[] = new short[n]; for (i = 0; i < n; i++) { switch(str.charAt(i)) { case 'i': case 'I': num[i] = 1; break; case 'v': case 'V': num[i] = 5; break; case 'x': case 'X': num[i] = 10; break; case 'l': case 'L': num[i] = 50; break; case 'c': case 'C': num[i] = 100; break; case 'd': case 'D': num[i] = 500; break; case 'm': case 'M': num[i] = 1000; break; default: num[i] = 0; } } for (i = 1; i < n; i++) { if (num[i] > num[i-1]) { num[i] -= num[i-1]; num[i-1] = 0; } } int retVal = 0; for (i = 0; i < n; i++) { retVal += num[i]; } return retVal; } @Override public String toString() { return getText(); } public static void main(String args[]) { /* VerseKey yo = new VerseKey(); yo.setIntros(true); ListKey lk = new ListKey(); yo.setHashNumber(1005009027); lk.add(yo); yo.setHashNumber(1005009028); lk.add(yo); yo.setHashNumber(1005009029); lk.add(yo); yo.setHashNumber(1005010001); lk.add(yo); yo.setHashNumber(1005010002); lk.add(yo); lk.mergeContiguousElements(); System.out.println(lk.getShortRangeText()); */ /* yo.increment(-1); System.out.println(yo); // VerseKey yo = new VerseKey("B06KInscriptioV0"); */ /* yo.setIntros(true); yo.setTestament(2); yo.setText("B02incipit"); System.out.println(yo); if (true) return; /* if ((argv.length < 1) || (argv.length > 6)) { System.err.println("usage: VerseKey.main <\"string to parse\"> [locale_name] [v11n] [context] [echo params 1|0] [test-in-set-verse]"); return; } */ /* argv = new String[] {"1Tim.1.1-20;2:1-4"}; boolean first = true; for (String arg : argv) { System.out.print(((first)?"":" ")+arg); first = false; } System.out.print(": \n"); VerseKey vk = new VerseKey("gen.1.1"); ListKey verses = vk.ParseVerseList(argv[0], vk.toString(), true); System.out.println("RangeText: " + verses.getRangeText()); System.out.println("RangeText: " + verses.getRangeText()); System.out.println("ShortRangeText: " + verses.getShortRangeText()); if (verses.getCount() > 0) vk = (VerseKey)verses.getElement(0); System.out.println("elements:"); for (verses.setPosition(TOP);verses.popError()==0; verses.increment()) { System.out.println("vk: " + verses.getShortText()); } */ // PARSETEST C++ test code if ((args.length < 1) || (args.length > 8)) { System.out.println("usage: " + VerseKey.class + " <\"string to parse\"> [locale_name] [v11n] [context] [echo params 1|0] [test-in-set-verse 1|0] [intros 1|0] [to_v11n]"); return; } if (args.length > 1) { LocaleMgr.getSystemLocaleMgr().setDefaultLocaleName(args[1]); } VerseKey defaultVSKey = new VerseKey(); if (args.length > 2) { defaultVSKey.setVersificationSystem(args[2]); } String context = (args.length > 3) ? args[3] : "gen.1.1"; boolean echo = (args.length > 4) ? "1".equals(args[4]) : false; boolean inSetTest = (args.length > 5) ? "1".equals(args[5]) : false; boolean intros = (args.length > 6) ? "1".equals(args[6]) : false; defaultVSKey.setIntros(intros); // SWLog.getSystemLog().setLogLevel(SWLog.LOG_DEBUG); defaultVSKey.validateCurrentLocale(); defaultVSKey.setText(context); ListKey verses = defaultVSKey.parseVerseList(args[0], defaultVSKey.getText(), true); if (echo) { for (int i = 0; i < args.length; i++) { if (i > 0) System.out.print(" "); System.out.print(args[i]); } System.out.print(": "); } System.out.println(verses.getRangeText()); if (inSetTest) { verses.setText(context); System.out.println("Verse is" + ((verses.popError() != 0) ? " NOT" : "") + " in set."); System.out.println(""); } if (args.length > 7) { VerseKey toVSKey = new VerseKey(); toVSKey.copyFrom(defaultVSKey); toVSKey.setVersificationSystem(args[7]); defaultVSKey.setText(args[0]); toVSKey.setFromOther(defaultVSKey); System.out.println(defaultVSKey.getVersificationSystem() + ": " + defaultVSKey.getRangeText() + " => " + toVSKey.getVersificationSystem() + ": " + toVSKey.getRangeText()); } } }