Coverage Report - org.crosswire.jsword.versification.Versification
 
Classes in this File Line Coverage Branch Coverage Complexity
Versification
0%
0/347
0%
0/202
3.327
 
 1  
 /**
 2  
  * Distribution License:
 3  
  * JSword is free software; you can redistribute it and/or modify it under
 4  
  * the terms of the GNU Lesser General Public License, version 2.1 or later
 5  
  * as published by the Free Software Foundation. This program is distributed
 6  
  * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 7  
  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 8  
  * See the GNU Lesser General Public License for more details.
 9  
  *
 10  
  * The License is available on the internet at:
 11  
  *      http://www.gnu.org/copyleft/lgpl.html
 12  
  * or by writing to:
 13  
  *      Free Software Foundation, Inc.
 14  
  *      59 Temple Place - Suite 330
 15  
  *      Boston, MA 02111-1307, USA
 16  
  *
 17  
  * © CrossWire Bible Society, 2012 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.jsword.versification;
 21  
 
 22  
 import java.io.PrintStream;
 23  
 import java.io.Serializable;
 24  
 import java.util.Iterator;
 25  
 
 26  
 import org.crosswire.jsword.JSMsg;
 27  
 import org.crosswire.jsword.JSOtherMsg;
 28  
 import org.crosswire.jsword.book.ReferenceSystem;
 29  
 import org.crosswire.jsword.passage.NoSuchVerseException;
 30  
 import org.crosswire.jsword.passage.Verse;
 31  
 import org.crosswire.jsword.passage.VerseRange;
 32  
 
 33  
 /**
 34  
  * A named Versification defines
 35  
  * the order of BibleBooks by Testament,
 36  
  * the number of chapters in each BibleBook,
 37  
  * the number of verses in each chapter.
 38  
  *
 39  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 40  
  * @author DM Smith
 41  
  */
 42  0
 public class Versification implements ReferenceSystem, Serializable {
 43  0
     public Versification() {
 44  0
     }
 45  
 
 46  
     /**
 47  
      * Construct a Versification.
 48  
      * 
 49  
      * @param name
 50  
      *            The name of this reference system
 51  
      * @param booksOT
 52  
      *            An ordered list of books in this reference system. The list
 53  
      *            should not include INTRO_BIBLE, or INTRO_OT.
 54  
      * @param booksNT
 55  
      *            An ordered list of books in this reference system. The list
 56  
      *            should not include INTRO_NT.
 57  
      * @param lastVerseOT
 58  
      *            For each book in booksOT, this has an array with one entry for
 59  
      *            each chapter whose value is the highest numbered verse in that
 60  
      *            chapter. Do not include chapter 0.
 61  
      * @param lastVerseNT
 62  
      *            For each book in booksNT, this has an array with one entry for
 63  
      *            each chapter whose value is the highest numbered verse in that
 64  
      *            chapter. Do not include chapter 0.
 65  
      */
 66  0
     public Versification(String name, BibleBook[] booksOT, BibleBook[] booksNT, int[][] lastVerseOT, int[][] lastVerseNT) {
 67  0
         this.name = name;
 68  
 
 69  
         // Copy the books into an aggregated BibleBook array
 70  
         // including INTRO_BIBLE and INTRO_OT/INTRO_NT for non-null book lists
 71  0
         int bookCount = 1; // Always include the INTRO_BIBLE
 72  0
         if (booksOT.length > 0) {
 73  0
             bookCount += booksOT.length + 1; // All of the OT books and INTRO_OT
 74  
         }
 75  
 
 76  0
         int ntStart = bookCount;
 77  0
         if (booksNT.length > 0) {
 78  0
             bookCount += booksNT.length + 1; // All of the NT books and INTRO_NT
 79  
         }
 80  0
         BibleBook[] books = new BibleBook[bookCount];
 81  0
         books[0] = BibleBook.INTRO_BIBLE;
 82  0
         if (booksOT.length > 0) {
 83  0
             books[1] = BibleBook.INTRO_OT;
 84  0
             System.arraycopy(booksOT, 0, books, 2, booksOT.length);
 85  
         }
 86  
 
 87  0
         if (booksNT.length > 0) {
 88  0
             books[ntStart] = BibleBook.INTRO_NT;
 89  0
             System.arraycopy(booksNT, 0, books, ntStart + 1, booksNT.length);
 90  
         }
 91  
 
 92  0
         this.bookList = new BibleBookList(books);
 93  
 
 94  0
         int ordinal = 0;
 95  
 
 96  
         // Create an independent copy of lastVerse.
 97  0
         this.lastVerse = new int[bookCount][];
 98  0
         int bookIndex = 0;
 99  
 
 100  
         // Add in the bible introduction
 101  0
         int[] chapters = new int[1];
 102  0
         chapters[0] = 0;
 103  0
         this.lastVerse[bookIndex++] = chapters;
 104  
 
 105  
         // Now append the OT info
 106  0
         if (lastVerseOT.length > 0) {
 107  
             // Add in the testament intro
 108  0
             chapters = new int[1];
 109  0
             chapters[0] = 0;
 110  0
             this.lastVerse[bookIndex++] = chapters;
 111  
             // then all the testament info
 112  0
             for (int i = 0; i < lastVerseOT.length; i++) {
 113  0
                 int[] src = lastVerseOT[i];
 114  
                 // Add one as the location for chapter 0.
 115  0
                 int[] dest = new int[src.length + 1];
 116  0
                 this.lastVerse[bookIndex++] = dest;
 117  
                 // The last verse of chapter 0 is 0
 118  0
                 dest[0] = 0;
 119  
                 // copy the last verse array for the chapter
 120  0
                 System.arraycopy(src, 0, dest, 1, src.length);
 121  
             }
 122  
         }
 123  
 
 124  
         // Now append the NT info
 125  0
         if (lastVerseNT.length > 0) {
 126  
             // Add in the testament intro
 127  0
             chapters = new int[1];
 128  0
             chapters[0] = 0;
 129  0
             this.lastVerse[bookIndex++] = chapters;
 130  
             // then all the testament info
 131  0
             for (int i = 0; i < lastVerseNT.length; i++) {
 132  0
                 int[] src = lastVerseNT[i];
 133  
                 // Add one as the location for chapter 0.
 134  0
                 int[] dest = new int[src.length + 1];
 135  0
                 this.lastVerse[bookIndex++] = dest;
 136  
                 // The last verse of chapter 0 is 0
 137  0
                 dest[0] = 0;
 138  
                 // copy the last verse array for the chapter
 139  0
                 System.arraycopy(src, 0, dest, 1, src.length);
 140  
             }
 141  
         }
 142  
 
 143  
         // Initialize chapterStarts to be a parallel array to lastVerse,
 144  
         // but with chapter starts
 145  0
         this.chapterStarts = new int[bookCount][];
 146  0
         for (bookIndex = 0; bookIndex < bookCount; bookIndex++) {
 147  
 
 148  
             // Remember where the OT ends
 149  0
             if (bookList.getBook(bookIndex) == BibleBook.INTRO_NT) {
 150  
                 // This is not reached for a v11n without a NT.
 151  0
                 this.otMaxOrdinal = ordinal - 1;
 152  
             }
 153  
 
 154  
             // Save off the chapter starts
 155  0
             int[] src = this.lastVerse[bookIndex];
 156  0
             int numChapters = src.length;
 157  0
             int[] dest = new int[numChapters];
 158  0
             this.chapterStarts[bookIndex] = dest;
 159  0
             for (int chapterIndex = 0; chapterIndex < numChapters; chapterIndex++) {
 160  
                 // Save off the chapter start
 161  0
                 dest[chapterIndex] = ordinal;
 162  
 
 163  
                 // Set ordinal to the start of the next chapter or book introduction.
 164  
                 // The number of verses in each chapter, when including verse 0,
 165  
                 // is one more that the largest numbered verse in the chapter.
 166  0
                 ordinal += src[chapterIndex] + 1;
 167  
             }
 168  
         }
 169  
 
 170  
         // Remember where the NT ends
 171  0
         this.ntMaxOrdinal = ordinal - 1;
 172  
 
 173  
         // The MT v11n has no NT, so at this point otMaxOrdinal == 0
 174  0
         if (booksNT.length == 0) {
 175  0
             this.otMaxOrdinal = this.ntMaxOrdinal;
 176  
         }
 177  
 //        Versification.dump(System.out, this.osisName, this.bookList, this.lastVerse);
 178  
 //        Versification.dump(System.out, this.osisName, this.bookList, this.chapterStarts);
 179  0
     }
 180  
 
 181  
     /**
 182  
      * Get the OSIS name for this Versification.
 183  
      * @return the OSIS name of the Versification
 184  
      */
 185  
     public String getName() {
 186  0
         return name;
 187  
     }
 188  
 
 189  
     /**
 190  
      * Does this Versification contain the BibleBook.
 191  
      *
 192  
      * @param book
 193  
      * @return true if it is present.
 194  
      */
 195  
     public boolean containsBook(BibleBook book) {
 196  0
         return bookList.contains(book);
 197  
     }
 198  
 
 199  
     /**
 200  
      * Get the BibleBook by its position in this Versification.
 201  
      * If the position is negative, return the first book.
 202  
      * If the position is greater than the last, return the last book.
 203  
      *
 204  
      * @param ordinal
 205  
      * @return the indicated book
 206  
      */
 207  
     public BibleBook getBook(int ordinal) {
 208  0
         return bookList.getBook(ordinal);
 209  
     }
 210  
 
 211  
     /**
 212  
      * Get a book from its name.
 213  
      *
 214  
      * @param find
 215  
      *            The string to identify
 216  
      * @return The BibleBook, On error null
 217  
      */
 218  
     public BibleBook getBook(String find) {
 219  0
         BibleBook book = BibleNames.instance().getBook(find);
 220  0
         if (containsBook(book)) {
 221  0
             return book;
 222  
         }
 223  0
         return null;
 224  
     }
 225  
 
 226  
     /**
 227  
      * Get the number of books in this Versification.
 228  
      * @return the number of books
 229  
      */
 230  
     public int getBookCount() {
 231  0
         return bookList.getBookCount();
 232  
     }
 233  
 
 234  
     /**
 235  
      * The number of books between two verses includes
 236  
      * the books of the two verses and everything in between.
 237  
      * 
 238  
      * @param start
 239  
      *            The first Verse in the range
 240  
      * @param end The last Verse in the range
 241  
      * @return The number of books. Always &gt;= 1.
 242  
      */
 243  
     public int getBookCount(Verse start, Verse end) {
 244  0
         int startBook = bookList.getOrdinal(start.getBook());
 245  0
         int endBook = bookList.getOrdinal(end.getBook());
 246  
 
 247  0
         return endBook - startBook + 1;
 248  
     }
 249  
 
 250  
     /**
 251  
      * Return the first book in the list.
 252  
      *
 253  
      * @return the first book in the list
 254  
      */
 255  
     public BibleBook getFirstBook() {
 256  0
         return bookList.getFirstBook();
 257  
     }
 258  
 
 259  
     /**
 260  
      * Return the first book in the list.
 261  
      *
 262  
      * @return the first book in the list
 263  
      */
 264  
     public BibleBook getLastBook() {
 265  0
         return bookList.getLastBook();
 266  
     }
 267  
 
 268  
     /**
 269  
      * Given a BibleBook, get the next BibleBook in this Versification. If it is the last book, return null.
 270  
      * @param book A BibleBook in the Versification
 271  
      * @return the previous BibleBook or null.
 272  
      */
 273  
     public BibleBook getNextBook(BibleBook book) {
 274  0
         return bookList.getNextBook(book);
 275  
     }
 276  
 
 277  
     /**
 278  
      * Given a BibleBook, get the previous BibleBook in this Versification. If it is the first book, return null.
 279  
      * @param book A BibleBook in the Versification
 280  
      * @return the previous BibleBook or null.
 281  
      */
 282  
     public BibleBook getPreviousBook(BibleBook book) {
 283  0
         return bookList.getPreviousBook(book);
 284  
     }
 285  
 
 286  
     /**
 287  
      * Get the BibleBooks in this Versification.
 288  
      *
 289  
      * @return an Iterator over the books
 290  
      */
 291  
     public Iterator<BibleBook> getBookIterator() {
 292  0
         return bookList.iterator();
 293  
     }
 294  
 
 295  
     /**
 296  
      * Get the BookName.
 297  
      *
 298  
      * @param book the desired book
 299  
      * @return The requested BookName or null if not in this versification
 300  
      */
 301  
     public BookName getBookName(BibleBook book) {
 302  0
         if (containsBook(book)) {
 303  0
             return BibleNames.instance().getBookName(book);
 304  
         }
 305  0
         return null;
 306  
     }
 307  
 
 308  
     /**
 309  
      * Get the preferred name of a book. Altered by the case setting (see
 310  
      * setBookCase() and isFullBookName())
 311  
      *
 312  
      * @param book the desired book
 313  
      * @return The full name of the book or null if not in this versification
 314  
      */
 315  
     public String getPreferredName(BibleBook book) {
 316  0
         if (containsBook(book)) {
 317  0
             return BibleNames.instance().getPreferredName(book);
 318  
         }
 319  0
         return null;
 320  
       }
 321  
 
 322  
     /**
 323  
      * Get the full name of a book (e.g. "Genesis"). Altered by the case setting
 324  
      * (see setBookCase())
 325  
      *
 326  
      * @param book the book of the Bible
 327  
      * @return The full name of the book or null if not in this versification
 328  
      */
 329  
     public String getLongName(BibleBook book) {
 330  0
         if (containsBook(book)) {
 331  0
             return BibleNames.instance().getLongName(book);
 332  
         }
 333  0
         return null;
 334  
       }
 335  
 
 336  
     /**
 337  
      * Get the short name of a book (e.g. "Gen"). Altered by the case setting
 338  
      * (see setBookCase())
 339  
      *
 340  
      * @param book the book of the Bible
 341  
      * @return The short name of the book or null if not in this versification
 342  
      */
 343  
     public String getShortName(BibleBook book) {
 344  0
         if (containsBook(book)) {
 345  0
             return BibleNames.instance().getShortName(book);
 346  
         }
 347  0
         return null;
 348  
       }
 349  
 
 350  
     /**
 351  
      * Is the given string a valid book name. If this method returns true then
 352  
      * getBook() will return a BibleBook and not null.
 353  
      *
 354  
      * @param find
 355  
      *            The string to identify
 356  
      * @return true when the book name is recognized
 357  
      */
 358  
     public boolean isBook(String find) {
 359  0
         return getBook(find) != null;
 360  
     }
 361  
 
 362  
     /**
 363  
      * Get the last valid chapter number for a book.
 364  
      *
 365  
      * @param book
 366  
      *            The book part of the reference.
 367  
      * @return The last valid chapter number for a book.
 368  
      */
 369  
     public int getLastChapter(BibleBook book) {
 370  
         // This is faster than doing the check explicitly, unless
 371  
         // The exception is actually thrown, then it is a lot slower
 372  
         // I'd like to think that the norm is to get it right
 373  
         try {
 374  0
             return lastVerse[bookList.getOrdinal(book)].length - 1;
 375  0
         } catch (NullPointerException ex) {
 376  0
             return 0;
 377  0
         } catch (ArrayIndexOutOfBoundsException ex) {
 378  0
             return 0;
 379  
         }
 380  
     }
 381  
 
 382  
     /**
 383  
      * Get the last valid verse number for a chapter.
 384  
      *
 385  
      * @param book
 386  
      *            The book part of the reference.
 387  
      * @param chapter
 388  
      *            The current chapter
 389  
      * @return The last valid verse number for a chapter
 390  
      */
 391  
     public int getLastVerse(BibleBook book, int chapter) {
 392  
         // This is faster than doing the check explicitly, unless
 393  
         // The exception is actually thrown, then it is a lot slower
 394  
         // I'd like to think that the norm is to get it right
 395  
         try {
 396  0
             return lastVerse[bookList.getOrdinal(book)][chapter];
 397  0
         } catch (NullPointerException ex) {
 398  0
             return 0;
 399  0
         } catch (ArrayIndexOutOfBoundsException ex) {
 400  0
             return 0;
 401  
         }
 402  
     }
 403  
 
 404  
     /**
 405  
      * Get a VerseRange encompassing this Versification.
 406  
      * 
 407  
      * @return a VerseRange for the whole versification
 408  
      */
 409  
     public VerseRange getAllVerses() {
 410  0
         Verse first = new Verse(this, bookList.getFirstBook(), 0, 0);
 411  0
         BibleBook book = bookList.getLastBook();
 412  0
         int chapter = getLastChapter(book);
 413  0
         Verse last = new Verse(this, book, chapter, getLastVerse(book, chapter));
 414  0
         return new VerseRange(this, first, last);
 415  
     }
 416  
 
 417  
     /**
 418  
      * An introduction is a Verse that has a verse number of 0.
 419  
      *
 420  
      * @param verse the verse to test
 421  
      * @return true or false ...
 422  
      */
 423  
     public boolean isIntro(Verse verse) {
 424  0
         int v = verse.getVerse();
 425  0
         return v == 0;
 426  
     }
 427  
 
 428  
     /**
 429  
      * A book introduction is an introduction
 430  
      * that has a chapter of 0.
 431  
      *
 432  
      * @param verse the verse to test
 433  
      * @return true or false ...
 434  
      */
 435  
     public boolean isBookIntro(Verse verse) {
 436  0
         return 0 == verse.getChapter() && isIntro(verse);
 437  
     }
 438  
 
 439  
     /**
 440  
      * A chapter introduction is an introduction
 441  
      * that has a chapter other than 0
 442  
      *
 443  
      * @param verse the verse to test
 444  
      * @return true or false ...
 445  
      */
 446  
     public boolean isChapterIntro(Verse verse) {
 447  0
         return 0 != verse.getChapter() && isIntro(verse);
 448  
     }
 449  
 
 450  
     /**
 451  
      * The start of a chapter is indicated by
 452  
      * a verse number of 0 or 1
 453  
      *
 454  
      * @param verse the verse to test
 455  
      * @return true or false ...
 456  
      */
 457  
     public boolean isStartOfChapter(Verse verse) {
 458  0
         int v = verse.getVerse();
 459  0
         return v <= 1;
 460  
     }
 461  
 
 462  
     /**
 463  
      * The end of the chapter is indicated by
 464  
      * the verse number matching the last in the chapter.
 465  
      *
 466  
      * @param verse the verse to test
 467  
      * @return true or false ...
 468  
      */
 469  
     public boolean isEndOfChapter(Verse verse) {
 470  0
         BibleBook b = verse.getBook();
 471  0
         int v = verse.getVerse();
 472  0
         int c = verse.getChapter();
 473  0
         return v == getLastVerse(b, c);
 474  
     }
 475  
 
 476  
     /**
 477  
      * The start of a book is indicated by
 478  
      * a chapter number of 0 or 1 and
 479  
      * a verse number of 0 or 1.
 480  
      *
 481  
      * @param verse the verse to test
 482  
      * @return true or false ...
 483  
      */
 484  
     public boolean isStartOfBook(Verse verse) {
 485  0
         int v = verse.getVerse();
 486  0
         int c = verse.getChapter();
 487  0
         return v <= 1 && c <= 1;
 488  
     }
 489  
 
 490  
     /**
 491  
      * The end of the book is indicated by
 492  
      * the chapter number matching the last chapter
 493  
      * in the book and the verse number matching
 494  
      * the last verse in the chapter.
 495  
      *
 496  
      * @param verse the verse to test
 497  
      * @return true or false ...
 498  
      */
 499  
     public boolean isEndOfBook(Verse verse) {
 500  0
         BibleBook b = verse.getBook();
 501  0
         int v = verse.getVerse();
 502  0
         int c = verse.getChapter();
 503  0
         return v == getLastVerse(b, c) && c == getLastChapter(b);
 504  
     }
 505  
 
 506  
     /**
 507  
      * Two verses are in the same chapter if both
 508  
      * the book and chapter agree.
 509  
      *
 510  
      * @param first
 511  
      *            The verse to compare to
 512  
      * @param second
 513  
      *            The verse to compare to
 514  
      * @return true or false ...
 515  
      */
 516  
     public boolean isSameChapter(Verse first, Verse second) {
 517  0
         return first.getBook() == second.getBook() && first.getChapter() == second.getChapter();
 518  
     }
 519  
 
 520  
     /**
 521  
      * Two verse are adjacent if one immediately follows the other,
 522  
      * even across book boundaries. Introductions are considered
 523  
      * as having "zero width" in this determination. That is the
 524  
      * last verse in a chapter or book is adjacent every verse that
 525  
      * follows up to and including verse 1 of the next chapter in
 526  
      * the versification.
 527  
      * <br>
 528  
      * For example:<br>
 529  
      * The last verse in the Old Testament is adjacent to:
 530  
      * <ul>
 531  
      * <li>Intro.NT - the New Testament introduction</li>
 532  
      * <li>Matt 0:0 - the book introduction</li>
 533  
      * <li>Matt 1:0 - the chapter introduction</li>
 534  
      * <li>Matt 1:1 - the first verse of Matt</li>
 535  
      * </ul>
 536  
      * Note: the verses can be in any order.
 537  
      *
 538  
      * @param first
 539  
      *            The verse to compare to
 540  
      * @param second
 541  
      *            The verse to compare to
 542  
      * @return true or false ...
 543  
      */
 544  
     public boolean isAdjacentChapter(Verse first, Verse second) {
 545  0
         Verse before = min(first, second);
 546  0
         Verse after = max(first, second);
 547  0
         if (isSameBook(first, second)) {
 548  0
             return after.getChapter() - before.getChapter() == 1;
 549  
         }
 550  
         // The earlier verse has to be the  last chapter
 551  0
         return isAdjacentBook(before, after)
 552  
              && getLastChapter(before.getBook()) == before.getChapter()
 553  
              && after.getChapter() <= 1;
 554  
     }
 555  
 
 556  
     /**
 557  
      * Two verses are in the same book
 558  
      * when they have the same book.
 559  
      *
 560  
      * @param first
 561  
      *            The verse to compare to
 562  
      * @param second
 563  
      *            The verse to compare to
 564  
      * @return true or false ...
 565  
      */
 566  
     public boolean isSameBook(Verse first, Verse second) {
 567  0
         return first.getBook() == second.getBook();
 568  
     }
 569  
 
 570  
     /**
 571  
      * Two verses are in adjacent books if one book
 572  
      * follows the other in this versification.
 573  
      * Note: the verses can be in any order.
 574  
      *
 575  
      * @param first
 576  
      *            The verse to compare to
 577  
      * @param second
 578  
      *            The verse to compare to
 579  
      * @return true or false ...
 580  
      */
 581  
     public boolean isAdjacentBook(Verse first, Verse second) {
 582  0
         return Math.abs(bookList.getOrdinal(second.getBook()) - bookList.getOrdinal(first.getBook())) == 1;
 583  
     }
 584  
 
 585  
     /**
 586  
      * Is this verse adjacent to another verse
 587  
      *
 588  
      * @param first
 589  
      *            The first verse in the comparison
 590  
      * @param second
 591  
      *            The second verse in the comparison
 592  
      * @return true if the verses are adjacent.
 593  
      */
 594  
     public boolean isAdjacentVerse(Verse first, Verse second) {
 595  0
         Verse before = min(first, second);
 596  0
         Verse after = max(first, second);
 597  0
         if (isSameChapter(first, second)) {
 598  0
             return after.getVerse() - before.getVerse() == 1;
 599  
         }
 600  
         // The earlier verse has to be the last verse in the chapter
 601  0
         return isAdjacentChapter(before, after)
 602  
              && getLastVerse(before.getBook(), before.getChapter()) == before.getVerse()
 603  
              && after.getVerse() <= 1;
 604  
     }
 605  
 
 606  
     /**
 607  
      * How many verses are there in between the 2 Verses. The answer is -ve if
 608  
      * start is bigger than end. The answer is inclusive of start and exclusive
 609  
      * of end, so that <code>distance(gen11, gen12) == 1</code>
 610  
      *
 611  
      * @param start
 612  
      *            The first Verse in the range
 613  
      * @param end The last Verse in the range
 614  
      * @return The count of verses between this and that.
 615  
      */
 616  
     public int distance(Verse start, Verse end) {
 617  0
         return end.getOrdinal() - start.getOrdinal();
 618  
     }
 619  
 
 620  
     /**
 621  
      * Determine the earlier of the two verses.
 622  
      * If first == second then return first.
 623  
      * 
 624  
      * @param first the first verse to compare
 625  
      * @param second the second verse to compare
 626  
      * @return The earlier of the two verses
 627  
      */
 628  
     public Verse min(Verse first, Verse second) {
 629  0
         return first.getOrdinal() <= second.getOrdinal() ? first : second;
 630  
     }
 631  
 
 632  
     /**
 633  
      * Determine the later of the two verses.
 634  
      * If first == second then return first.
 635  
      * 
 636  
      * @param first the first verse to compare
 637  
      * @param second the second verse to compare
 638  
      * @return The later of the two verses
 639  
      */
 640  
     public Verse max(Verse first, Verse second) {
 641  0
         return first.getOrdinal() > second.getOrdinal() ? first : second;
 642  
     }
 643  
 
 644  
     /**
 645  
      * Get the verse n down from here this Verse.
 646  
      *
 647  
      * @param verse
 648  
      *            The verse to use as a start
 649  
      * @param n
 650  
      *            The number to count down by
 651  
      * @return The new Verse
 652  
      */
 653  
     public Verse subtract(Verse verse, int n) {
 654  0
         int newVerse = verse.getVerse() - n;
 655  
         // Try the simple case of the verse being in the same chapter
 656  0
         if (newVerse >= 0) {
 657  0
             return new Verse(verse.getVersification(), verse.getBook(), verse.getChapter(), newVerse);
 658  
         }
 659  0
         return decodeOrdinal(verse.getOrdinal() - n);
 660  
     }
 661  
 
 662  
     /**
 663  
      * Get the verse that is a verses on from the one we've got.
 664  
      *
 665  
      * @param verse
 666  
      *            The verse to use as a start
 667  
      * @return The new verse or null if there is no next verse
 668  
      */
 669  
     public Verse next(Verse verse) {
 670  
         // Cannot increment past the end.
 671  0
         if (verse.getOrdinal() == ntMaxOrdinal) {
 672  0
             return null;
 673  
         }
 674  
 
 675  0
         BibleBook nextBook = verse.getBook();
 676  0
         int nextChapter = verse.getChapter();
 677  0
         int nextVerse = verse.getVerse() + 1;
 678  0
         if (nextVerse > getLastVerse(nextBook, nextChapter)) {
 679  
             // Go to an introduction.
 680  0
             nextVerse = 0;
 681  
             // of the next chapter
 682  0
             nextChapter += 1;
 683  
             // check to see that the chapter is valid for the book
 684  0
             if (nextChapter > getLastChapter(nextBook)) {
 685  
                 // To to an introduction
 686  0
                 nextChapter = 0;
 687  
                 // of the next book
 688  0
                 nextBook = bookList.getNextBook(verse.getBook());
 689  
             }
 690  
         }
 691  
 
 692  
         // nextBook is null when we try to increment past the last verse
 693  
         // The test at the beginning is designed to prevent that
 694  0
         if (nextBook == null) {
 695  0
             assert false;
 696  0
             return null;
 697  
         }
 698  
 
 699  0
         return new Verse(this, nextBook, nextChapter, nextVerse);
 700  
     }
 701  
 
 702  
     /**
 703  
      * Get the verse that is a few verses on from the one we've got.
 704  
      *
 705  
      * @param verse
 706  
      *            The verse to use as a start
 707  
      * @param n
 708  
      *            the number of verses later than the one we're one
 709  
      * @return The new verse
 710  
      */
 711  
     public Verse add(Verse verse, int n) {
 712  0
         int newVerse = verse.getVerse() + n;
 713  
         // Try the simple case of the verse being in the same chapter
 714  0
         if (newVerse <= getLastVerse(verse.getBook(), verse.getChapter())) {
 715  0
             return new Verse(verse.getVersification(), verse.getBook(), verse.getChapter(), newVerse);
 716  
         }
 717  0
         return decodeOrdinal(verse.getOrdinal() + n);
 718  
     }
 719  
 
 720  
     /**
 721  
      * The number of chapters between two verses includes
 722  
      * the chapters of the two verses and everything in between.
 723  
      * 
 724  
      * @param start
 725  
      *            The first Verse in the range
 726  
      * @param end The last Verse in the range
 727  
      * @return The number of chapters. Always &gt;= 1.
 728  
      */
 729  
     public int getChapterCount(Verse start, Verse end) {
 730  0
         BibleBook startBook = start.getBook();
 731  0
         int startChap = start.getChapter();
 732  0
         BibleBook endBook = end.getBook();
 733  0
         int endChap = end.getChapter();
 734  
 
 735  0
         if (startBook == endBook) {
 736  0
             return endChap - startChap + 1;
 737  
         }
 738  
 
 739  
         // So we are going to have to count up chapters from start to end
 740  0
         int total = getLastChapter(startBook) - startChap;
 741  0
         startBook = bookList.getNextBook(startBook);
 742  0
         endBook = bookList.getPreviousBook(endBook);
 743  0
         for (BibleBook b =  startBook; b != endBook; b = bookList.getNextBook(b)) {
 744  0
             total += getLastChapter(b);
 745  
         }
 746  0
         total += endChap;
 747  
 
 748  0
         return total;
 749  
     }
 750  
 
 751  
     /**
 752  
      * The maximum number of verses in the Bible, including module, testament, book and chapter introductions.
 753  
      *
 754  
      * @return the number of addressable verses in this versification.
 755  
      */
 756  
     public int maximumOrdinal() {
 757  
         // This is the same as the last ordinal in the Reference System.
 758  0
         return ntMaxOrdinal;
 759  
     }
 760  
 
 761  
     /**
 762  
      * Where does this verse come in the Bible. The value that this returns should be treated as opaque, useful for a bit set.
 763  
      * The introductions to the Book, OT/NT Testaments, Bible books and chapters are included here.
 764  
      * <ul>
 765  
      * <li>0 - INTRO_BIBLE 0:0 - The Book introduction</li>
 766  
      * <li>1 - INTRO_OT 0:0 - The OT Testament introduction</li>
 767  
      * <li>2 - Gen 0:0 - The introduction to the book of Genesis</li>
 768  
      * <li>3 - Gen 1:0 - The introduction to Genesis chapter 1</li>
 769  
      * <li>4 - Gen 1:1</li>
 770  
      * <li>...</li>
 771  
      * <li>35 - Gen 1:31</li>
 772  
      * <li>36 - Gen 2:0 - The introduction to Genesis chapter 2</li>
 773  
      * <li>37 - Gen 2:1</li>
 774  
      * <li>...</li>
 775  
      * <li>n - last verse in the OT</li>
 776  
      * <li>n + 1 - INTRO_NT, 0, 0 - The New Testament introduction</li>
 777  
      * <li>n + 2 - Matt 0:0 - The introduction to Matt</li>
 778  
      * <li>n + 3 - Matt 1:0 - The introduction to Matt 1</li>
 779  
      * <li>n + 4 - Matt 1:1</li>
 780  
      * <li>...</li>
 781  
      * </ul>
 782  
      * 
 783  
      * If the verse is not in this versification, return 0.
 784  
      *
 785  
      * @param verse
 786  
      *            The verse to convert
 787  
      * @return The ordinal number of verses
 788  
     */
 789  
     public int getOrdinal(Verse verse) {
 790  
         try {
 791  0
             return chapterStarts[bookList.getOrdinal(verse.getBook())][verse.getChapter()] + verse.getVerse();
 792  0
         } catch (ArrayIndexOutOfBoundsException e) {
 793  0
             return 0;
 794  
         }
 795  
     }
 796  
 
 797  
     /**
 798  
      * Determine the ordinal value for this versification given the
 799  
      * ordinal value in a testament. If the ordinal is out of bounds it
 800  
      * is constrained to be within the boundaries of the testament.
 801  
      * This unwinds getTestamentOrdinal.
 802  
      * 
 803  
      * @param testament the testament in which the ordinal value pertains
 804  
      * @param testamentOrdinal the ordinal value within the testament
 805  
      * @return the ordinal value for the versification as a whole
 806  
      */
 807  
     public int getOrdinal(Testament testament, int testamentOrdinal) {
 808  0
         int ordinal = testamentOrdinal >= 0 ? testamentOrdinal : 0;
 809  0
         if (Testament.NEW == testament) {
 810  0
             ordinal = otMaxOrdinal + testamentOrdinal;
 811  0
             return ordinal <= ntMaxOrdinal ? ordinal : ntMaxOrdinal;
 812  
         }
 813  0
         return ordinal <= otMaxOrdinal ? ordinal : otMaxOrdinal;
 814  
     }
 815  
 
 816  
     /**
 817  
      * Where does this verse come in the Bible. The value that this returns should be treated as opaque, useful for a bit set.
 818  
      * The introductions to the Book, OT/NT Testaments, Bible books and chapters are included here.
 819  
      * <ul>
 820  
      * <li>0 - INTRO_BIBLE 0:0 - The Book introduction</li>
 821  
      * <li>1 - INTRO_OT 0:0 - The OT Testament introduction</li>
 822  
      * <li>2 - Gen 0:0 - The introduction to the book of Genesis</li>
 823  
      * <li>3 - Gen 1:0 - The introduction to Genesis chapter 1</li>
 824  
      * <li>4 - Gen 1:1</li>
 825  
      * <li>...</li>
 826  
      * <li>35 - Gen 1:31</li>
 827  
      * <li>36 - Gen 2:0 - The introduction to Genesis chapter 2</li>
 828  
      * <li>37 - Genesis 2:1</li>
 829  
      * <li>...</li>
 830  
      * <li>n - last verse in the OT</li>
 831  
      * <li>0 - INTRO_NT, 0, 0 - The New Testament introduction</li>
 832  
      * <li>1 - Matt 0:0 - The introduction to Matt</li>
 833  
      * <li>2 - Matt 1:0 - The introduction to Matt 1</li>
 834  
      * <li>3 - Matt 1:1</li>
 835  
      * <li>...</li>
 836  
      * </ul>
 837  
      *
 838  
      * @param ordinal
 839  
      *            The ordinal number of the verse to convert
 840  
      * @return The ordinal number of the Verse within its Testament
 841  
      */
 842  
     public int getTestamentOrdinal(int ordinal) {
 843  0
         int ntOrdinal = otMaxOrdinal + 1;
 844  0
         if (ordinal >= ntOrdinal) {
 845  0
             return ordinal - ntOrdinal + 1;
 846  
         }
 847  0
         return ordinal;
 848  
     }
 849  
 
 850  
     /**
 851  
      * Get the testament of a given verse
 852  
      * @param ordinal the ordinal position of the verse in the whole Bible
 853  
      * @return the testament in which that verse is found
 854  
      */
 855  
     public Testament getTestament(int ordinal) {
 856  0
         if (ordinal > otMaxOrdinal) {
 857  
             // This is an NT verse
 858  0
             return Testament.NEW;
 859  
         }
 860  
         // This is an OT verse
 861  0
         return Testament.OLD;
 862  
     }
 863  
 
 864  
     /**
 865  
      * Give the count of verses in the testament or the whole Bible.
 866  
      *
 867  
      * @param testament The testament to count. If null, then all testaments.
 868  
      * @return the number of verses in the testament
 869  
      */
 870  
     public int getCount(Testament testament) {
 871  0
         int total = ntMaxOrdinal + 1;
 872  0
         if (testament == null) {
 873  0
             return total;
 874  
         }
 875  
 
 876  0
         int otCount = otMaxOrdinal + 1;
 877  0
         if (testament == Testament.OLD) {
 878  0
             return otCount;
 879  
         }
 880  
 
 881  0
         return total - otCount;
 882  
     }
 883  
 
 884  
     /**
 885  
      * Where does this verse come in the Bible. This will unwind the value returned by getOrdinal(Verse).
 886  
      * If the ordinal value is less than 0 or greater than the last verse in this Versification,
 887  
      * then constrain it to the first or last verse in this Versification.
 888  
      *
 889  
      * @param ordinal
 890  
      *            The ordinal number of the verse
 891  
      * @return A Verse
 892  
      */
 893  
     public Verse decodeOrdinal(int ordinal) {
 894  0
         int ord = ordinal;
 895  
 
 896  0
         if (ord < 0) {
 897  0
             ord = 0;
 898  0
         } else if (ord > ntMaxOrdinal) {
 899  0
             ord = ntMaxOrdinal;
 900  
         }
 901  
 
 902  
         // Handle three special cases
 903  
         // Book/Module introduction
 904  0
         if (ord == 0) {
 905  0
             return new Verse(this, BibleBook.INTRO_BIBLE, 0, 0);
 906  
         }
 907  
 
 908  
         // OT introduction
 909  0
         if (ord == 1) {
 910  0
             return new Verse(this, BibleBook.INTRO_OT, 0, 0);
 911  
         }
 912  
 
 913  
         // NT introduction
 914  0
         if (ord == otMaxOrdinal + 1) {
 915  0
             return new Verse(this, BibleBook.INTRO_NT, 0, 0);
 916  
         }
 917  
 
 918  
         // To find the book, do a binary search in chapterStarts against chapter 0
 919  0
         int low = 0;
 920  0
         int high = chapterStarts.length;
 921  0
         int match = -1;
 922  
 
 923  0
         while (high - low > 1) {
 924  
             // use >>> to keep mid always in range
 925  0
             int mid = (low + high) >>> 1;
 926  
 
 927  
             // Compare the for the item at "mid"
 928  0
             int cmp = chapterStarts[mid][0] - ord;
 929  0
             if (cmp < 0) {
 930  0
                 low = mid;
 931  0
             } else if (cmp > 0) {
 932  0
                 high = mid;
 933  
             } else {
 934  0
                 match = mid;
 935  0
                 break;
 936  
             }
 937  0
         }
 938  
 
 939  
         // If we didn't have an exact match then use the low value
 940  0
         int bookIndex = match >= 0 ? match : low;
 941  0
         BibleBook book = bookList.getBook(bookIndex);
 942  
 
 943  
         // To find the chapter, do a binary search in chapterStarts against bookIndex
 944  0
         low = 0;
 945  0
         high = chapterStarts[bookIndex].length;
 946  0
         match = -1;
 947  
 
 948  0
         while (high - low > 1) {
 949  
             // use >>> to keep mid always in range
 950  0
             int mid = (low + high) >>> 1;
 951  
 
 952  
             // Compare the for the item at "mid"
 953  0
             int cmp = chapterStarts[bookIndex][mid] - ord;
 954  0
             if (cmp < 0) {
 955  0
                 low = mid;
 956  0
             } else if (cmp > 0) {
 957  0
                 high = mid;
 958  
             } else {
 959  0
                 match = mid;
 960  0
                 break;
 961  
             }
 962  0
         }
 963  
 
 964  
         // If we didn't have an exact match then use the low value
 965  0
         int chapterIndex = match >= 0 ? match : low;
 966  0
         int verse = chapterIndex == 0 ? 0 : ord - chapterStarts[bookIndex][chapterIndex];
 967  0
         return new Verse(this, book, chapterIndex, verse);
 968  
     }
 969  
 
 970  
     /**
 971  
      * Does the following represent a real verse?. It is code like this that
 972  
      * makes me wonder if I18 is done well/worth doing. All this code does is
 973  
      * check if the numbers are valid, but the exception handling code is huge
 974  
      * :(
 975  
      *
 976  
      * @param book
 977  
      *            The book part of the reference.
 978  
      * @param chapter
 979  
      *            The chapter part of the reference.
 980  
      * @param verse
 981  
      *            The verse part of the reference.
 982  
      * @exception NoSuchVerseException
 983  
      *                If the reference is illegal
 984  
      */
 985  
     public void validate(BibleBook book, int chapter, int verse) throws NoSuchVerseException {
 986  0
         validate(book, chapter, verse, false);
 987  0
     }
 988  
 
 989  
     /**
 990  
      * Does the following represent a real verse?. It is code like this that
 991  
      * makes me wonder if I18 is done well/worth doing. All this code does is
 992  
      * check if the numbers are valid, but the exception handling code is huge
 993  
      * :(
 994  
      *
 995  
      * @param book
 996  
      *            The book part of the reference.
 997  
      * @param chapter
 998  
      *            The chapter part of the reference.
 999  
      * @param verse
 1000  
      *            The verse part of the reference.
 1001  
      * @param silent
 1002  
      *            true to indicate we do not want to throw an exception
 1003  
      * @return true if validation was succesful
 1004  
      * @exception NoSuchVerseException
 1005  
      *                If the reference is illegal and silent was false
 1006  
      */
 1007  
     public boolean validate(BibleBook book, int chapter, int verse, boolean silent) throws NoSuchVerseException {
 1008  
         // Check the book
 1009  0
         if (book == null) {
 1010  0
             if (silent) {
 1011  0
                 return false;
 1012  
             }
 1013  
             // TRANSLATOR: The user did not supply a book for a verse reference.
 1014  0
             throw new NoSuchVerseException(JSOtherMsg.lookupText("Book must not be null"));
 1015  
         }
 1016  
 
 1017  
         // Check the chapter
 1018  0
         int maxChapter = getLastChapter(book);
 1019  0
         if (chapter < 0 || chapter > maxChapter) {
 1020  0
             if (silent) {
 1021  0
                 return false;
 1022  
             }
 1023  
             // TRANSLATOR: The user supplied a chapter that was out of bounds. This tells them what is allowed.
 1024  
             // {0} is the lowest value that is allowed. This is always 0.
 1025  
             // {1,number,integer} is the place holder for the highest chapter number in the book. The format is special in that it will present it in the user's preferred format.
 1026  
             // {2} is a placeholder for the Bible book name.
 1027  
             // {3,number,integer} is a placeholder for the chapter number that the user gave.
 1028  0
             throw new NoSuchVerseException(JSMsg.gettext("Chapter should be between {0} and {1,number,integer} for {2} (given {3,number,integer}).",
 1029  
                     Integer.valueOf(0), Integer.valueOf(maxChapter), getPreferredName(book), Integer.valueOf(chapter)
 1030  
             ));
 1031  
         }
 1032  
 
 1033  
         // Check the verse
 1034  0
         int maxVerse = getLastVerse(book, chapter);
 1035  0
         if (verse < 0 || verse > maxVerse) {
 1036  0
             if (silent) {
 1037  0
                 return false;
 1038  
             }
 1039  
             // TRANSLATOR: The user supplied a verse number that was out of bounds. This tells them what is allowed.
 1040  
             // {0} is the lowest value that is allowed. This is always 0.
 1041  
             // {1,number,integer} is the place holder for the highest verse number in the chapter. The format is special in that it will present it in the user's preferred format.
 1042  
             // {2} is a placeholder for the Bible book name.
 1043  
             // {3,number,integer} is a placeholder for the chapter number that the user gave.
 1044  
             // {4,number,integer} is a placeholder for the verse number that the user gave.
 1045  0
             throw new NoSuchVerseException(JSMsg.gettext("Verse should be between {0} and {1,number,integer} for {2} {3,number,integer} (given {4,number,integer}).",
 1046  
                     Integer.valueOf(0), Integer.valueOf(maxVerse), getPreferredName(book), Integer.valueOf(chapter), Integer.valueOf(verse)
 1047  
                     ));
 1048  
         }
 1049  0
         return true;
 1050  
     }
 1051  
 
 1052  
     /**
 1053  
      * Fix up these verses so that they are as valid a possible. This is
 1054  
      * currently done so that we can say "Gen 1:1" + 31 = "Gen 1:32" and
 1055  
      * "Gen 1:32".patch() is "Gen 2:1".
 1056  
      * <p>
 1057  
      * There is another patch system that allows us to use large numbers to mean
 1058  
      * "the end of" so "Gen 1:32".otherPatch() gives "Gen 1:31". This could be
 1059  
      * useful to allow the user to enter things like "Gen 1:99" meaning the end
 1060  
      * of the chapter. Or "Isa 99:1" to mean the last chapter in Isaiah verse 1
 1061  
      * or even "Rev 99:99" to mean the last verse in the Bible.
 1062  
      * <p>
 1063  
      * However I have not implemented this because I've used a different
 1064  
      * convention: "Gen 1:$" (OLB compatible) or "Gen 1:ff" (common commentary
 1065  
      * usage) to mean the end of the chapter - So the functionality is there
 1066  
      * anyway.
 1067  
      * <p>
 1068  
      * I think that getting into the habit of typing "Gen 1:99" is bad. It could
 1069  
      * be the source of surprises "Psa 119:99" is not what you'd might expect,
 1070  
      * and neither is "Psa 99:1" is you wanted the last chapter in Psalms -
 1071  
      * expecting us to type "Psa 999:1" seems like we're getting silly.
 1072  
      * <p>
 1073  
      * However despite this maybe we should provide the functionality anyway.
 1074  
      *
 1075  
      * @param book the book to obtain
 1076  
      * @param chapter the supposed chapter
 1077  
      * @param verse the supposed verse
 1078  
      * @return The resultant verse.
 1079  
      */
 1080  
     public Verse patch(BibleBook book, int chapter, int verse) {
 1081  0
         BibleBook patchedBook = book;
 1082  0
         int patchedChapter = chapter;
 1083  0
         int patchedVerse = verse;
 1084  
 
 1085  
         // If the book is null,
 1086  
         // then patch to the first book in the reference system
 1087  0
         if (patchedBook == null) {
 1088  0
             patchedBook = bookList.getFirstBook();
 1089  
         }
 1090  
         // If they are too small
 1091  0
         if (patchedChapter < 0) {
 1092  0
             patchedChapter = 0;
 1093  
         }
 1094  0
         if (patchedVerse < 0) {
 1095  0
             patchedVerse = 0;
 1096  
         }
 1097  
 
 1098  
         // Goal is to start in the current book and go forward that number of chapters
 1099  
         // which might cause one to land in a later book.
 1100  
         // For each book, the chapters number from 0 to n, where n is the last chapter number.
 1101  
         // So if we want Genesis 53, then that would be 3 chapters into Exodus,
 1102  
         // which would be chapter 2.
 1103  0
         while (patchedBook != null && patchedChapter > getLastChapter(patchedBook)) {
 1104  0
             patchedChapter -= getLastChapter(patchedBook) + 1;
 1105  0
             patchedBook = bookList.getNextBook(patchedBook);
 1106  
         }
 1107  
 
 1108  
         // At this point we have a valid chapter.
 1109  
         // Now we do the same for the verses.
 1110  
         // For each book, the chapters number from 0 to n, where n is the last chapter number.
 1111  
         // So if we want Genesis 49:36, then that would be 3 verses into Genesis 50,
 1112  
         // which would be verse 50:2.     
 1113  0
         while (patchedBook != null && patchedVerse > getLastVerse(patchedBook, patchedChapter)) {
 1114  0
             patchedVerse -= getLastVerse(patchedBook, patchedChapter) + 1;
 1115  0
             patchedChapter += 1;
 1116  
 
 1117  0
             if (patchedChapter > getLastChapter(patchedBook)) {
 1118  0
                 patchedChapter -= getLastChapter(patchedBook) + 1;
 1119  0
                 patchedBook = bookList.getNextBook(patchedBook);
 1120  
             }
 1121  
         }
 1122  
 
 1123  
         // If we have gone beyond the last book
 1124  
         // then return the last chapter and verse in the last book
 1125  0
         if (patchedBook == null) {
 1126  0
             patchedBook = bookList.getLastBook();
 1127  0
             patchedChapter = getLastChapter(patchedBook);
 1128  0
             patchedVerse = getLastVerse(patchedBook, patchedChapter);
 1129  
         }
 1130  
 
 1131  0
         return new Verse(this, patchedBook, patchedChapter, patchedVerse);
 1132  
     }
 1133  
 
 1134  
     public static void dump(PrintStream out, String name, BibleBookList bookList, int[][] array) {
 1135  0
         String vstr1 = "";
 1136  0
         String vstr2 = "";
 1137  0
         int count = 0;
 1138  0
         out.println("    private final int[][] " + name + " =");
 1139  0
         out.println("    {");
 1140  
         // Output an array just like lastVerse, indexed by book and chapter,
 1141  
         // that accumulates verse counts for offsets,
 1142  
         // having a sentinel at the end.
 1143  0
         int bookCount = array.length;
 1144  0
         for (int bookIndex = 0; bookIndex < bookCount; bookIndex++) {
 1145  0
             count = 0;
 1146  0
             out.print("        // ");
 1147  0
             if (bookIndex < bookList.getBookCount()) {
 1148  0
                 BibleBook book = bookList.getBook(bookIndex);
 1149  0
                 out.println(book.getOSIS());
 1150  0
             } else {
 1151  0
                 out.println("Sentinel");
 1152  
             }
 1153  0
             out.print("        { ");
 1154  
 
 1155  0
             int numChapters = array[bookIndex].length;
 1156  0
             for (int chapterIndex = 0; chapterIndex < numChapters; chapterIndex++) {
 1157  
 
 1158  
                 // Pretty print with 10 items per line
 1159  0
                 if (count++ % 10 == 0) {
 1160  0
                     out.println();
 1161  0
                     out.print("            ");
 1162  
                 }
 1163  
 
 1164  
                 // Output the offset for the chapter introduction
 1165  
                 // This is referenced with a verse number of 0
 1166  0
                 vstr1 = "     " + array[bookIndex][chapterIndex];
 1167  0
                 vstr2 = vstr1.substring(vstr1.length() - 5);
 1168  0
                 out.print(vstr2 + ", ");
 1169  
             }
 1170  0
             out.println();
 1171  0
             out.println("        },");
 1172  
         }
 1173  0
         out.println("    };");
 1174  0
     }
 1175  
 
 1176  
     public static void optimize(PrintStream out, BibleBookList bookList, int[][] lastVerse) {
 1177  0
         String vstr1 = "";
 1178  0
         String vstr2 = "";
 1179  0
         int count = 0;
 1180  0
         int ordinal = 0;
 1181  0
         out.println("    private final int[][] chapterStarts =");
 1182  0
         out.println("    {");
 1183  
         // Output an array just like lastVerse, indexed by book and chapter,
 1184  
         // that accumulates verse counts for offsets,
 1185  
         // having a sentinel at the end.
 1186  0
         int bookIndex = 0;
 1187  0
         int ntStartOrdinal = 0;
 1188  0
         for (BibleBook book = bookList.getBook(0); book != null; book = bookList.getNextBook(book)) {
 1189  0
             count = 0;
 1190  0
             out.print("        // ");
 1191  0
             out.println(book.getOSIS());
 1192  0
             out.print("        { ");
 1193  
 
 1194  
             // Remember where the NT Starts
 1195  0
             if (book == BibleBook.INTRO_NT) {
 1196  0
                 ntStartOrdinal = ordinal;
 1197  
             }
 1198  
 
 1199  0
             int numChapters = lastVerse[bookIndex].length;
 1200  0
             for (int chapterIndex = 0; chapterIndex < numChapters; chapterIndex++) {
 1201  
 
 1202  
                 // Pretty print with 10 items per line
 1203  0
                 if (count++ % 10 == 0) {
 1204  0
                     out.println();
 1205  0
                     out.print("            ");
 1206  
                 }
 1207  
 
 1208  
                 // Output the offset for the chapter introduction
 1209  
                 // This is referenced with a verse number of 0
 1210  0
                 vstr1 = "     " + ordinal;
 1211  0
                 vstr2 = vstr1.substring(vstr1.length() - 5);
 1212  0
                 out.print(vstr2 + ", ");
 1213  
                 // Set ordinal to the start of the next chapter or book introduction
 1214  0
                 int versesInChapter = lastVerse[bookIndex][chapterIndex] + 1;
 1215  0
                 ordinal += versesInChapter;
 1216  
             }
 1217  0
             out.println();
 1218  0
             out.println("        },");
 1219  0
             bookIndex++;
 1220  
         }
 1221  
 
 1222  
         // Output a sentinel value:
 1223  
         // It is a book of one chapter starting with what would be the ordinal of the next chapter's introduction.
 1224  0
         vstr1 = "     " + ordinal;
 1225  0
         vstr2 = vstr1.substring(vstr1.length() - 5);
 1226  0
         out.println("        // Sentinel");
 1227  0
         out.println("        { ");
 1228  0
         out.println("            " + vstr2 + ", ");
 1229  0
         out.println("        },");
 1230  0
         out.println("    };");
 1231  0
         out.println();
 1232  0
         out.println("    /** The last ordinal number of the Old Testament */");
 1233  0
         out.println("    private int otMaxOrdinal = " + (ntStartOrdinal - 1) + ";");
 1234  0
         out.println("    /** The last ordinal number of the New Testament and the maximum ordinal number of this Reference System */");
 1235  0
         out.println("    private int ntMaxOrdinal = " + (ordinal - 1) + ";");
 1236  0
     }
 1237  
 
 1238  
 
 1239  
     /** The OSIS name of the reference system. */
 1240  
     private String name;
 1241  
 
 1242  
     /** The ordered list of books in this versification. */
 1243  
     private BibleBookList bookList;
 1244  
 
 1245  
     /** The last ordinal number of the Old Testament */
 1246  
     private int otMaxOrdinal;
 1247  
 
 1248  
     /** The last ordinal number of the New Testament and the maximum ordinal number of this Reference System */
 1249  
     private int ntMaxOrdinal;
 1250  
 
 1251  
     /** Constant for the max verse number in each chapter */
 1252  
     private int[][] lastVerse;
 1253  
 
 1254  
     /**
 1255  
      * Constant for the ordinal number of the first verse in each chapter.
 1256  
      */
 1257  
     private int[][] chapterStarts;
 1258  
 
 1259  
     /**
 1260  
      * Serialization ID
 1261  
      */
 1262  
     private static final long serialVersionUID = -6226916242596368765L;
 1263  
 }