Coverage Report - org.crosswire.jsword.passage.VerseRange
 
Classes in this File Line Coverage Branch Coverage Complexity
VerseRange
0%
0/259
0%
0/166
2.845
VerseRange$VerseIterator
0%
0/14
0%
0/6
2.845
 
 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, 2005 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.jsword.passage;
 21  
 
 22  
 import java.io.IOException;
 23  
 import java.io.ObjectInputStream;
 24  
 import java.io.ObjectOutputStream;
 25  
 import java.util.Iterator;
 26  
 import java.util.NoSuchElementException;
 27  
 
 28  
 import org.crosswire.common.icu.NumberShaper;
 29  
 import org.crosswire.jsword.versification.BibleBook;
 30  
 import org.crosswire.jsword.versification.Versification;
 31  
 
 32  
 /**
 33  
  * A VerseRange is one step between a Verse and a Passage - it is a Verse plus a
 34  
  * verseCount. Every VerseRange has a start, a verseCount and an end. A
 35  
  * VerseRange is designed to be immutable. This is a necessary from a
 36  
  * collections point of view. A VerseRange should always be valid, although some
 37  
  * versions may not return any text for verses that they consider to be
 38  
  * miss-translated in some way.
 39  
  * 
 40  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 41  
  * @author Joe Walker
 42  
  * @author DM Smith
 43  
  */
 44  0
 public final class VerseRange implements VerseKey<VerseRange> {
 45  
     /**
 46  
      * The default VerseRange is a single verse - Genesis 1:1. I didn't want to
 47  
      * provide this constructor however, you are supposed to provide a default
 48  
      * ctor for all beans. For this reason I suggest you don't use it.
 49  
      * 
 50  
      * @param v11n
 51  
      *            The versification for the range
 52  
      */
 53  
     public VerseRange(Versification v11n) {
 54  0
         this(v11n, Verse.DEFAULT, Verse.DEFAULT);
 55  0
     }
 56  
 
 57  
     /**
 58  
      * Construct a VerseRange from a Verse. The resultant VerseRange will be 1
 59  
      * verse in verseCount.
 60  
      * 
 61  
      * @param v11n
 62  
      *            The versification for the range
 63  
      * @param start
 64  
      *            The verse to start from
 65  
      */
 66  
     public VerseRange(Versification v11n, Verse start) {
 67  0
         this(v11n, start, start);
 68  0
     }
 69  
 
 70  0
     public VerseRange(Versification v11n, Verse start, Verse end) {
 71  0
         assert v11n != null;
 72  0
         assert start != null;
 73  0
         assert end != null;
 74  
 
 75  0
         this.v11n = v11n;
 76  0
         shaper = new NumberShaper();
 77  
 
 78  0
         int distance = v11n.distance(start, end);
 79  
 
 80  0
         if (distance < 0) {
 81  0
             this.start = end;
 82  0
             this.end = start;
 83  0
             this.verseCount = calcVerseCount();
 84  0
         } else if (distance == 0) {
 85  0
             this.start = start;
 86  0
             this.end = start;
 87  0
             this.verseCount = 1;
 88  
         } else {
 89  0
             this.start = start;
 90  0
             this.end = end;
 91  0
             this.verseCount = calcVerseCount();
 92  
         }
 93  0
     }
 94  
 
 95  
     /* (non-Javadoc)
 96  
      * @see org.crosswire.jsword.passage.VerseKey#getVersification()
 97  
      */
 98  
     public Versification getVersification() {
 99  0
         return v11n;
 100  
     }
 101  
 
 102  
     /* (non-Javadoc)
 103  
      * @see org.crosswire.jsword.passage.VerseKey#reversify(org.crosswire.jsword.versification.Versification)
 104  
      */
 105  
     public VerseRange reversify(Versification newVersification) {
 106  0
         if (v11n.equals(newVersification)) {
 107  0
             return this;
 108  
         }
 109  0
         Verse newStart = start.reversify(newVersification);
 110  0
         if (newStart == null) {
 111  0
             return null;
 112  
         }
 113  0
         Verse newEnd = end.reversify(newVersification);
 114  0
         if (newEnd == null) {
 115  0
             return null;
 116  
         }
 117  0
         return new VerseRange(newVersification, newStart, newEnd);
 118  
     }
 119  
 
 120  
     /* (non-Javadoc)
 121  
      * @see org.crosswire.jsword.passage.VerseKey#isWhole()
 122  
      */
 123  
     public boolean isWhole() {
 124  0
         return start.isWhole() && end.isWhole();
 125  
     }
 126  
 
 127  
     /* (non-Javadoc)
 128  
      * @see org.crosswire.jsword.passage.VerseKey#getWhole()
 129  
      */
 130  
     public VerseRange getWhole() {
 131  0
         if (isWhole()) {
 132  0
             return this;
 133  
         }
 134  0
         return new VerseRange(v11n, start.getWhole(), end.getWhole());
 135  
     }
 136  
 
 137  
     /**
 138  
      * Merge 2 VerseRanges together. The resulting range will encompass
 139  
      * Everything in-between the extremities of the 2 ranges.
 140  
      * 
 141  
      * @param a
 142  
      *            The first verse range to be merged
 143  
      * @param b
 144  
      *            The second verse range to be merged
 145  
      */
 146  0
     public VerseRange(VerseRange a, VerseRange b) {
 147  0
         v11n = a.v11n;
 148  0
         shaper = new NumberShaper();
 149  0
         start = v11n.min(a.getStart(), b.getStart());
 150  0
         end = v11n.max(a.getEnd(), b.getEnd());
 151  0
         verseCount = calcVerseCount();
 152  0
     }
 153  
 
 154  
     /* (non-Javadoc)
 155  
      * @see org.crosswire.jsword.passage.Key#getName()
 156  
      */
 157  
     public String getName() {
 158  0
         return getName(null);
 159  
     }
 160  
 
 161  
     /* (non-Javadoc)
 162  
      * @see org.crosswire.jsword.passage.Key#getName(org.crosswire.jsword.passage.Key)
 163  
      */
 164  
     public String getName(Key base) {
 165  0
         if (PassageUtil.isPersistentNaming() && originalName != null) {
 166  0
             return originalName;
 167  
         }
 168  
 
 169  0
         String rangeName = doGetName(base);
 170  
         // Only shape it if it can be unshaped.
 171  0
         if (shaper.canUnshape()) {
 172  0
             return shaper.shape(rangeName);
 173  
         }
 174  
 
 175  0
         return rangeName;
 176  
     }
 177  
 
 178  
     /* (non-Javadoc)
 179  
      * @see org.crosswire.jsword.passage.Key#getRootName()
 180  
      */
 181  
     public String getRootName() {
 182  0
         return start.getRootName();
 183  
     }
 184  
 
 185  
     /* (non-Javadoc)
 186  
      * @see org.crosswire.jsword.passage.Key#getOsisRef()
 187  
      */
 188  
     public String getOsisRef() {
 189  0
         BibleBook startBook = start.getBook();
 190  0
         BibleBook endBook = end.getBook();
 191  0
         int startChapter = start.getChapter();
 192  0
         int endChapter = end.getChapter();
 193  
 
 194  
         // If this is in 2 separate books
 195  0
         if (startBook != endBook) {
 196  0
             StringBuilder buf = new StringBuilder();
 197  0
             if (v11n.isStartOfBook(start)) {
 198  0
                 buf.append(startBook.getOSIS());
 199  0
             } else if (v11n.isStartOfChapter(start)) {
 200  0
                 buf.append(startBook.getOSIS());
 201  0
                 buf.append(Verse.VERSE_OSIS_DELIM);
 202  0
                 buf.append(startChapter);
 203  
             } else {
 204  0
                 buf.append(start.getOsisRef());
 205  
             }
 206  
 
 207  0
             buf.append(VerseRange.RANGE_PREF_DELIM);
 208  
 
 209  0
             if (v11n.isEndOfBook(end)) {
 210  0
                 buf.append(endBook.getOSIS());
 211  0
             } else if (v11n.isEndOfChapter(end)) {
 212  0
                 buf.append(endBook.getOSIS());
 213  0
                 buf.append(Verse.VERSE_OSIS_DELIM);
 214  0
                 buf.append(endChapter);
 215  
             } else {
 216  0
                 buf.append(end.getOsisRef());
 217  
             }
 218  
 
 219  0
             return buf.toString();
 220  
         }
 221  
 
 222  
         // This range is exactly a whole book
 223  0
         if (isWholeBook()) {
 224  
             // Just report the name of the book, we don't need to worry
 225  
             // about the
 226  
             // base since we start at the start of a book, and should have
 227  
             // been
 228  
             // recently normalized()
 229  0
             return startBook.getOSIS();
 230  
         }
 231  
 
 232  
         // If this is 2 separate chapters in the same book
 233  0
         if (startChapter != endChapter) {
 234  0
             StringBuilder buf = new StringBuilder();
 235  0
             if (v11n.isStartOfChapter(start)) {
 236  0
                 buf.append(startBook.getOSIS());
 237  0
                 buf.append(Verse.VERSE_OSIS_DELIM);
 238  0
                 buf.append(startChapter);
 239  
             } else {
 240  0
                 buf.append(start.getOsisRef());
 241  
             }
 242  
 
 243  0
             buf.append(VerseRange.RANGE_PREF_DELIM);
 244  
 
 245  0
             if (v11n.isEndOfChapter(end)) {
 246  0
                 buf.append(endBook.getOSIS());
 247  0
                 buf.append(Verse.VERSE_OSIS_DELIM);
 248  0
                 buf.append(endChapter);
 249  
             } else {
 250  0
                 buf.append(end.getOsisRef());
 251  
             }
 252  
 
 253  0
             return buf.toString();
 254  
         }
 255  
 
 256  
         // If this range is exactly a whole chapter
 257  0
         if (isWholeChapter()) {
 258  
             // Just report the name of the book and the chapter
 259  0
             StringBuilder buf = new StringBuilder();
 260  0
             buf.append(startBook.getOSIS());
 261  0
             buf.append(Verse.VERSE_OSIS_DELIM);
 262  0
             buf.append(startChapter);
 263  0
             return buf.toString();
 264  
         }
 265  
 
 266  
         // If this is 2 separate verses
 267  0
         if (start.getVerse() != end.getVerse()) {
 268  0
             StringBuilder buf = new StringBuilder();
 269  0
             buf.append(start.getOsisRef());
 270  0
             buf.append(VerseRange.RANGE_PREF_DELIM);
 271  0
             buf.append(end.getOsisRef());
 272  0
             return buf.toString();
 273  
         }
 274  
 
 275  
         // The range is a single verse
 276  0
         return start.getOsisRef();
 277  
     }
 278  
 
 279  
     /* (non-Javadoc)
 280  
      * @see org.crosswire.jsword.passage.Key#getOsisID()
 281  
      */
 282  
     public String getOsisID() {
 283  
 
 284  
         // This range is exactly a whole book
 285  0
         if (isWholeBook()) {
 286  
             // Just report the name of the book, we don't need to worry
 287  
             // about the base since we start at the start of a book, and
 288  
             // should have been recently normalized()
 289  0
             return start.getBook().getOSIS();
 290  
         }
 291  
 
 292  
         // If this range is exactly a whole chapter
 293  0
         if (isWholeChapter()) {
 294  
             // Just report the name of the book and the chapter
 295  0
             return start.getBook().getOSIS() + Verse.VERSE_OSIS_DELIM + start.getChapter();
 296  
         }
 297  
 
 298  0
         int startOrdinal = start.getOrdinal();
 299  0
         int endOrdinal = end.getOrdinal();
 300  
 
 301  
         // to see if it is wholly contained in the range and output it if it is.
 302  
 
 303  
         // Estimate the size of the buffer: book.dd.dd (where book is 3-5, 3 typical)
 304  0
         StringBuilder buf = new StringBuilder((endOrdinal - startOrdinal + 1) * 10);
 305  0
         buf.append(start.getOsisID());
 306  0
         for (int i = startOrdinal + 1; i < endOrdinal; i++) {
 307  0
             buf.append(AbstractPassage.REF_OSIS_DELIM);
 308  0
             buf.append(v11n.decodeOrdinal(i).getOsisID());
 309  
         }
 310  
 
 311  
         // It just might be a single verse range!
 312  0
         if (startOrdinal != endOrdinal) {
 313  0
             buf.append(AbstractPassage.REF_OSIS_DELIM);
 314  0
             buf.append(end.getOsisID());
 315  
         }
 316  
 
 317  0
         return buf.toString();
 318  
     }
 319  
 
 320  
     @Override
 321  
     public String toString() {
 322  0
         return getName();
 323  
     }
 324  
 
 325  
     /**
 326  
      * Fetch the first verse in this range.
 327  
      * 
 328  
      * @return The first verse in the range
 329  
      */
 330  
     public Verse getStart() {
 331  0
         return start;
 332  
     }
 333  
 
 334  
     /**
 335  
      * Fetch the last verse in this range.
 336  
      * 
 337  
      * @return The last verse in the range
 338  
      */
 339  
     public Verse getEnd() {
 340  0
         return end;
 341  
     }
 342  
 
 343  
     @Override
 344  
     public VerseRange clone() {
 345  
         // This gets us a shallow copy
 346  0
         VerseRange copy = null;
 347  
         try {
 348  0
             copy = (VerseRange) super.clone();
 349  0
             copy.start = start;
 350  0
             copy.end = end;
 351  0
             copy.verseCount = verseCount;
 352  0
             copy.originalName = originalName;
 353  0
             copy.shaper = new NumberShaper();
 354  0
             copy.v11n = v11n;
 355  0
         } catch (CloneNotSupportedException e) {
 356  0
             assert false : e;
 357  0
         }
 358  
 
 359  0
         return copy;
 360  
     }
 361  
 
 362  
     @Override
 363  
     public boolean equals(Object obj) {
 364  0
         if (!(obj instanceof VerseRange)) {
 365  0
             return false;
 366  
         }
 367  0
         VerseRange vr = (VerseRange) obj;
 368  0
         return verseCount == vr.verseCount && start.equals(vr.start) && v11n.equals(vr.v11n);
 369  
     }
 370  
 
 371  
     @Override
 372  
     public int hashCode() {
 373  0
         int result = start.hashCode();
 374  0
         result = 31 * result + verseCount;
 375  0
         return 31 * result + ((v11n == null) ? 0 : v11n.hashCode());
 376  
     }
 377  
 
 378  
     /* (non-Javadoc)
 379  
      * @see java.lang.Comparable#compareTo(java.lang.Object)
 380  
      */
 381  
     public int compareTo(Key obj) {
 382  0
         VerseRange that = (VerseRange) obj;
 383  
 
 384  0
         int result = start.compareTo(that.start);
 385  0
         return result == 0 ? this.verseCount - that.verseCount : result;
 386  
     }
 387  
 
 388  
     /**
 389  
      * Are the 2 VerseRanges in question contiguous. that is - could they be
 390  
      * represented by a single VerseRange. Note that one range could be entirely
 391  
      * contained within the other and they would be considered adjacentTo() For
 392  
      * example Gen 1:1-2 is adjacent to Gen 1:1-5 and Gen 1:3-4 but not to Gen
 393  
      * 1:4-10. Also Gen 1:29-30 is adjacent to Gen 2:1-10
 394  
      * 
 395  
      * @param that
 396  
      *            The VerseRange to compare to
 397  
      * @return true if the ranges are adjacent
 398  
      */
 399  
     public boolean adjacentTo(VerseRange that) {
 400  0
         int thatStart = that.getStart().getOrdinal();
 401  0
         int thatEnd = that.getEnd().getOrdinal();
 402  0
         int thisStart = getStart().getOrdinal();
 403  0
         int thisEnd = getEnd().getOrdinal();
 404  
 
 405  
         // if that starts inside or is next to this we are adjacent.
 406  0
         if (thatStart >= thisStart - 1 && thatStart <= thisEnd + 1) {
 407  0
             return true;
 408  
         }
 409  
 
 410  
         // if this starts inside or is next to that we are adjacent.
 411  0
         if (thisStart >= thatStart - 1 && thisStart <= thatEnd + 1) {
 412  0
             return true;
 413  
         }
 414  
 
 415  
         // otherwise we're not adjacent
 416  0
         return false;
 417  
     }
 418  
 
 419  
     /**
 420  
      * Do the 2 VerseRanges in question actually overlap. This is slightly more
 421  
      * restrictive than the adjacentTo() test which could be satisfied by ranges
 422  
      * like Gen 1:1-2 and Gen 1:3-4. overlaps() however would return false given
 423  
      * these ranges. For example Gen 1:1-2 is adjacent to Gen 1:1-5 but not to
 424  
      * Gen 1:3-4 not to Gen 1:4-10. Also Gen 1:29-30 does not overlap Gen 2:1-10
 425  
      * 
 426  
      * @param that
 427  
      *            The VerseRange to compare to
 428  
      * @return true if the ranges are adjacent
 429  
      */
 430  
     public boolean overlaps(VerseRange that) {
 431  0
         int thatStart = that.getStart().getOrdinal();
 432  0
         int thatEnd = that.getEnd().getOrdinal();
 433  0
         int thisStart = getStart().getOrdinal();
 434  0
         int thisEnd = getEnd().getOrdinal();
 435  
 
 436  
         // if that starts inside this we are adjacent.
 437  0
         if (thatStart >= thisStart && thatStart <= thisEnd) {
 438  0
             return true;
 439  
         }
 440  
 
 441  
         // if this starts inside that we are adjacent.
 442  0
         if (thisStart >= thatStart && thisStart <= thatEnd) {
 443  0
             return true;
 444  
         }
 445  
 
 446  
         // otherwise we're not adjacent
 447  0
         return false;
 448  
     }
 449  
 
 450  
     /**
 451  
      * Is the given verse entirely within our range. For example if this =
 452  
      * "Gen 1:1-31" then: <tt>contains(Verse("Gen 1:3")) == true</tt>
 453  
      * <tt>contains(Verse("Gen 2:1")) == false</tt>
 454  
      * 
 455  
      * @param that
 456  
      *            The Verse to compare to
 457  
      * @return true if we contain it.
 458  
      */
 459  
     public boolean contains(Verse that) {
 460  0
         return v11n.distance(start, that) >= 0 && v11n.distance(that, end) >= 0;
 461  
     }
 462  
 
 463  
     /**
 464  
      * Is the given range within our range. For example if this = "Gen 1:1-31"
 465  
      * then: <tt>this.contains(Verse("Gen 1:3-10")) == true</tt>
 466  
      * <tt>this.contains(Verse("Gen 2:1-1")) == false</tt>
 467  
      * 
 468  
      * @param that
 469  
      *            The Verse to compare to
 470  
      * @return true if we contain it.
 471  
      */
 472  
     public boolean contains(VerseRange that) {
 473  0
         return v11n.distance(start, that.getStart()) >= 0 && v11n.distance(that.getEnd(), end) >= 0;
 474  
     }
 475  
 
 476  
     /* (non-Javadoc)
 477  
      * @see org.crosswire.jsword.passage.Key#contains(org.crosswire.jsword.passage.Key)
 478  
      */
 479  
     public boolean contains(Key key) {
 480  0
         if (key instanceof VerseRange) {
 481  0
             return contains((VerseRange) key);
 482  
         }
 483  0
         if (key instanceof Verse) {
 484  0
             return contains((Verse) key);
 485  
         }
 486  0
         return false;
 487  
     }
 488  
 
 489  
     /**
 490  
      * Does this range represent exactly one chapter, no more or less.
 491  
      * 
 492  
      * @return true if we are exactly one chapter.
 493  
      */
 494  
     public boolean isWholeChapter() {
 495  0
         return v11n.isSameChapter(start, end) && isWholeChapters();
 496  
     }
 497  
 
 498  
     /**
 499  
      * Does this range represent a number of whole chapters
 500  
      * 
 501  
      * @return true if we are a whole number of chapters.
 502  
      */
 503  
     public boolean isWholeChapters() {
 504  0
         return v11n.isStartOfChapter(start) && v11n.isEndOfChapter(end);
 505  
     }
 506  
 
 507  
     /**
 508  
      * Does this range represent exactly one book, no more or less.
 509  
      * 
 510  
      * @return true if we are exactly one book.
 511  
      */
 512  
     public boolean isWholeBook() {
 513  0
         return v11n.isSameBook(start, end) && isWholeBooks();
 514  
     }
 515  
 
 516  
     /**
 517  
      * Does this range represent a whole number of books.
 518  
      * 
 519  
      * @return true if we are a whole number of books.
 520  
      */
 521  
     public boolean isWholeBooks() {
 522  0
         return v11n.isStartOfBook(start) && v11n.isEndOfBook(end);
 523  
     }
 524  
 
 525  
     /**
 526  
      * Does this range occupy more than one book;
 527  
      * 
 528  
      * @return true if we occupy 2 or more books
 529  
      */
 530  
     public boolean isMultipleBooks() {
 531  0
         return start.getBook() != end.getBook();
 532  
     }
 533  
 
 534  
     /**
 535  
      * Create an array of Verses
 536  
      * 
 537  
      * @return The array of verses that this makes up
 538  
      */
 539  
     public Verse[] toVerseArray() {
 540  0
         Verse[] retcode = new Verse[verseCount];
 541  0
         int ord = start.getOrdinal();
 542  0
         for (int i = 0; i < verseCount; i++) {
 543  0
             retcode[i] = v11n.decodeOrdinal(ord + i);
 544  
         }
 545  
 
 546  0
         return retcode;
 547  
     }
 548  
 
 549  
     /**
 550  
      * Enumerate the subranges in this range
 551  
      * 
 552  
      * @param restrict 
 553  
      * @return a range iterator
 554  
      */
 555  
     public Iterator<VerseRange> rangeIterator(RestrictionType restrict) {
 556  0
         return new AbstractPassage.VerseRangeIterator(v11n, iterator(), restrict);
 557  
     }
 558  
 
 559  
     /* (non-Javadoc)
 560  
      * @see org.crosswire.jsword.passage.Key#getParent()
 561  
      */
 562  
     public Key getParent() {
 563  0
         return parent;
 564  
     }
 565  
 
 566  
     /**
 567  
      * Set a parent Key. This allows us to follow the Key interface more
 568  
      * closely, although the concept of a parent for a verse is fairly alien.
 569  
      * 
 570  
      * @param parent
 571  
      *            The parent Key for this verse
 572  
      */
 573  
     public void setParent(Key parent) {
 574  0
         this.parent = parent;
 575  0
     }
 576  
 
 577  
     /**
 578  
      * Create a VerseRange that is the stuff left of VerseRange a when you
 579  
      * remove the stuff in VerseRange b.
 580  
      * 
 581  
      * @param a
 582  
      *            Verses at the start or end of b
 583  
      * @param b
 584  
      *            All the verses
 585  
      * @return A list of the Verses outstanding
 586  
      */
 587  
     public static VerseRange[] remainder(VerseRange a, VerseRange b) {
 588  0
         VerseRange rstart = null;
 589  0
         VerseRange rend = null;
 590  
 
 591  0
         Versification v11n = a.getVersification();
 592  
 
 593  
         // If a starts before b get the Range of the prequel
 594  0
         if (v11n.distance(a.getStart(), b.getStart()) > 0) {
 595  0
             rstart = new VerseRange(v11n, a.getStart(), v11n.subtract(b.getEnd(), 1));
 596  
         }
 597  
 
 598  
         // If a ends after b get the Range of the sequel
 599  0
         if (v11n.distance(a.getEnd(), b.getEnd()) < 0) {
 600  0
             rend = new VerseRange(v11n, v11n.add(b.getEnd(), 1), a.getEnd());
 601  
         }
 602  
 
 603  0
         if (rstart == null) {
 604  0
             if (rend == null) {
 605  0
                 return new VerseRange[] {};
 606  
             }
 607  0
             return new VerseRange[] {
 608  
                 rend
 609  
             };
 610  
         }
 611  
 
 612  0
         if (rend == null) {
 613  0
             return new VerseRange[] {
 614  
                 rstart
 615  
             };
 616  
         }
 617  0
         return new VerseRange[] {
 618  
                 rstart, rend
 619  
         };
 620  
     }
 621  
 
 622  
     /**
 623  
      * Create a VerseRange that is the stuff in VerseRange a that is also
 624  
      * in VerseRange b.
 625  
      * 
 626  
      * @param a
 627  
      *            The verses that you might want
 628  
      * @param b
 629  
      *            The verses that you definitely don't
 630  
      * @return A list of the Verses outstanding
 631  
      */
 632  
     public static VerseRange intersection(VerseRange a, VerseRange b) {
 633  0
         Versification v11n = a.getVersification();
 634  0
         Verse newStart = v11n.max(a.getStart(), b.getStart());
 635  0
         Verse newEnd = v11n.min(a.getEnd(), b.getEnd());
 636  
 
 637  0
         if (v11n.distance(newStart, newEnd) >= 0) {
 638  0
             return new VerseRange(a.getVersification(), newStart, newEnd);
 639  
         }
 640  
 
 641  0
         return null;
 642  
     }
 643  
 
 644  
     private String doGetName(Key base) {
 645  
         // Cache these we're going to be using them a lot.
 646  0
         BibleBook startBook = start.getBook();
 647  0
         int startChapter = start.getChapter();
 648  0
         int startVerse = start.getVerse();
 649  0
         BibleBook endBook = end.getBook();
 650  0
         int endChapter = end.getChapter();
 651  0
         int endVerse = end.getVerse();
 652  
 
 653  
         // If this is in 2 separate books
 654  0
         if (startBook != endBook) {
 655  
             // This range is exactly a whole book
 656  0
             if (isWholeBooks()) {
 657  
                 // Just report the name of the book, we don't need to worry
 658  
                 // about the base since we start at the start of a book,
 659  
                 // and should have been recently normalized()
 660  0
                 return v11n.getPreferredName(startBook) + VerseRange.RANGE_PREF_DELIM + v11n.getPreferredName(endBook);
 661  
             }
 662  
 
 663  
             // If this range is exactly a whole chapter
 664  0
             if (isWholeChapters()) {
 665  
                 // Just report book and chapter names
 666  0
                 return v11n.getPreferredName(startBook) + Verse.VERSE_PREF_DELIM1 + startChapter + VerseRange.RANGE_PREF_DELIM
 667  
                         + v11n.getPreferredName(endBook) + Verse.VERSE_PREF_DELIM1 + endChapter;
 668  
             }
 669  
 
 670  0
             if (v11n.isChapterIntro(start)) {
 671  0
                 return v11n.getPreferredName(startBook) + Verse.VERSE_PREF_DELIM1 + startChapter + VerseRange.RANGE_PREF_DELIM  + end.getName(base);
 672  
             }
 673  0
             if (v11n.isBookIntro(start)) {
 674  0
                 return v11n.getPreferredName(startBook) + VerseRange.RANGE_PREF_DELIM + end.getName(base);
 675  
             }
 676  0
             return start.getName(base) + VerseRange.RANGE_PREF_DELIM + end.getName(base);
 677  
         }
 678  
 
 679  
         // This range is exactly a whole book
 680  0
         if (isWholeBook()) {
 681  
             // Just report the name of the book, we don't need to worry about
 682  
             // the
 683  
             // base since we start at the start of a book, and should have been
 684  
             // recently normalized()
 685  0
             return v11n.getPreferredName(startBook);
 686  
         }
 687  
 
 688  
         // If this is 2 separate chapters
 689  0
         if (startChapter != endChapter) {
 690  
             // If this range is a whole number of chapters
 691  0
             if (isWholeChapters()) {
 692  
                 // Just report the name of the book and the chapters
 693  0
                 return v11n.getPreferredName(startBook) + Verse.VERSE_PREF_DELIM1 + startChapter + VerseRange.RANGE_PREF_DELIM + endChapter;
 694  
             }
 695  
 
 696  0
             return start.getName(base) + VerseRange.RANGE_PREF_DELIM + endChapter + Verse.VERSE_PREF_DELIM2 + endVerse;
 697  
         }
 698  
 
 699  
         // If this range is exactly a whole chapter
 700  0
         if (isWholeChapter()) {
 701  
             // Just report the name of the book and the chapter
 702  0
             return v11n.getPreferredName(startBook) + Verse.VERSE_PREF_DELIM1 + startChapter;
 703  
         }
 704  
 
 705  
         // If this is 2 separate verses
 706  0
         if (startVerse != endVerse) {
 707  0
             return start.getName(base) + VerseRange.RANGE_PREF_DELIM + endVerse;
 708  
         }
 709  
 
 710  
         // The range is a single verse
 711  0
         return start.getName(base);
 712  
     }
 713  
 
 714  
     /**
 715  
      * Calculate the last verse in this range.
 716  
      * 
 717  
      * @return The last verse in the range
 718  
      */
 719  
     private Verse calcEnd() {
 720  0
         if (verseCount == 1) {
 721  0
             return start;
 722  
         }
 723  0
         return v11n.add(start, verseCount - 1);
 724  
     }
 725  
 
 726  
     /**
 727  
      * Calculate how many verses in this range
 728  
      * 
 729  
      * @return The number of verses. Always&gt;= 1.
 730  
      */
 731  
     private int calcVerseCount() {
 732  0
         return v11n.distance(start, end) + 1;
 733  
     }
 734  
 
 735  
     /**
 736  
      * Check to see that everything is ok with the Data
 737  
      */
 738  
     private void verifyData() {
 739  0
         assert verseCount == calcVerseCount() : "start=" + start + ", end=" + end + ", verseCount=" + verseCount;
 740  0
     }
 741  
 
 742  
     /**
 743  
      * Write out the object to the given ObjectOutputStream
 744  
      * 
 745  
      * @param out
 746  
      *            The stream to write our state to
 747  
      * @throws IOException
 748  
      *             If the write fails
 749  
      * @serialData Write the ordinal number of this verse
 750  
      */
 751  
     private void writeObject(ObjectOutputStream out) throws IOException {
 752  0
         out.defaultWriteObject();
 753  
 
 754  
         // Ignore the original name. Is this wise?
 755  
         // I am expecting that people are not that fussed about it and
 756  
         // it could make everything far more verbose
 757  0
     }
 758  
 
 759  
     /**
 760  
      * Write out the object to the given ObjectOutputStream
 761  
      * 
 762  
      * @param in
 763  
      *            The stream to read our state from
 764  
      * @throws IOException
 765  
      *             If the write fails
 766  
      * @throws ClassNotFoundException
 767  
      *             If the read data is incorrect
 768  
      * @serialData Write the ordinal number of this verse
 769  
      */
 770  
     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 771  0
         in.defaultReadObject();
 772  
 
 773  0
         end = calcEnd();
 774  0
         shaper = new NumberShaper();
 775  
 
 776  0
         verifyData();
 777  
 
 778  
         // We are ignoring the originalName and parent.
 779  0
     }
 780  
 
 781  
     /**
 782  
      * Iterate over the Verses in the VerseRange
 783  
      */
 784  0
     private static final class VerseIterator implements Iterator<Key> {
 785  
         /**
 786  
          * Ctor
 787  
          */
 788  0
         protected VerseIterator(VerseRange range) {
 789  0
             v11n = range.getVersification();
 790  0
             nextVerse = range.getStart();
 791  0
             total = range.getCardinality();
 792  0
             count = 0;
 793  0
         }
 794  
 
 795  
         /* (non-Javadoc)
 796  
          * @see java.util.Iterator#hasNext()
 797  
          */
 798  
         public boolean hasNext() {
 799  0
             return nextVerse != null;
 800  
         }
 801  
 
 802  
         /* (non-Javadoc)
 803  
          * @see java.util.Iterator#next()
 804  
          */
 805  
         public Key next() throws NoSuchElementException {
 806  0
             if (nextVerse == null) {
 807  0
                 throw new NoSuchElementException();
 808  
             }
 809  0
             Verse currentVerse = nextVerse;
 810  0
             nextVerse = ++count < total ? v11n.next(nextVerse) : null;
 811  0
             return currentVerse;
 812  
         }
 813  
 
 814  
         /* (non-Javadoc)
 815  
          * @see java.util.Iterator#remove()
 816  
          */
 817  
         public void remove() throws UnsupportedOperationException {
 818  0
             throw new UnsupportedOperationException();
 819  
         }
 820  
 
 821  
         private Versification v11n;
 822  
         private Verse nextVerse;
 823  
         private int count;
 824  
         private int total;
 825  
     }
 826  
 
 827  
     /* (non-Javadoc)
 828  
      * @see org.crosswire.jsword.passage.Key#canHaveChildren()
 829  
      */
 830  
     public boolean canHaveChildren() {
 831  0
         return false;
 832  
     }
 833  
 
 834  
     /* (non-Javadoc)
 835  
      * @see org.crosswire.jsword.passage.Key#getChildCount()
 836  
      */
 837  
     public int getChildCount() {
 838  0
         return 0;
 839  
     }
 840  
 
 841  
     /* (non-Javadoc)
 842  
      * @see org.crosswire.jsword.passage.Key#getCardinality()
 843  
      */
 844  
     public int getCardinality() {
 845  0
         return verseCount;
 846  
     }
 847  
 
 848  
     /* (non-Javadoc)
 849  
      * @see org.crosswire.jsword.passage.Key#isEmpty()
 850  
      */
 851  
     public boolean isEmpty() {
 852  0
         return verseCount == 0;
 853  
     }
 854  
 
 855  
     /*
 856  
      * (non-Javadoc)
 857  
      * 
 858  
      * @see org.crosswire.jsword.passage.Key#iterator()
 859  
      */
 860  
     public Iterator<Key> iterator() {
 861  0
         return new VerseIterator(this);
 862  
     }
 863  
 
 864  
     /* (non-Javadoc)
 865  
      * @see org.crosswire.jsword.passage.Key#addAll(org.crosswire.jsword.passage.Key)
 866  
      */
 867  
     public void addAll(Key key) {
 868  0
         throw new UnsupportedOperationException();
 869  
     }
 870  
 
 871  
     /* (non-Javadoc)
 872  
      * @see org.crosswire.jsword.passage.Key#removeAll(org.crosswire.jsword.passage.Key)
 873  
      */
 874  
     public void removeAll(Key key) {
 875  0
         throw new UnsupportedOperationException();
 876  
     }
 877  
 
 878  
     /* (non-Javadoc)
 879  
      * @see org.crosswire.jsword.passage.Key#retainAll(org.crosswire.jsword.passage.Key)
 880  
      */
 881  
     public void retainAll(Key key) {
 882  0
         throw new UnsupportedOperationException();
 883  
     }
 884  
 
 885  
     /* (non-Javadoc)
 886  
      * @see org.crosswire.jsword.passage.Key#clear()
 887  
      */
 888  
     public void clear() {
 889  0
     }
 890  
 
 891  
     /* (non-Javadoc)
 892  
      * @see org.crosswire.jsword.passage.Key#get(int)
 893  
      */
 894  
     public Key get(int index) {
 895  0
         return null;
 896  
     }
 897  
 
 898  
     /* (non-Javadoc)
 899  
      * @see org.crosswire.jsword.passage.Key#indexOf(org.crosswire.jsword.passage.Key)
 900  
      */
 901  
     public int indexOf(Key that) {
 902  0
         return -1;
 903  
     }
 904  
 
 905  
     /* (non-Javadoc)
 906  
      * @see org.crosswire.jsword.passage.Key#blur(int, org.crosswire.jsword.passage.RestrictionType)
 907  
      */
 908  
     public void blur(int by, RestrictionType restrict) {
 909  0
         VerseRange newRange = restrict.blur(v11n, this, by, by);
 910  0
         start = newRange.start;
 911  0
         end = newRange.end;
 912  0
         verseCount = newRange.verseCount;
 913  0
     }
 914  
 
 915  
     /**
 916  
      * What characters can we use to separate the 2 parts to a VerseRanges
 917  
      */
 918  
     public static final char RANGE_OSIS_DELIM = '-';
 919  
 
 920  
     /**
 921  
      * What characters should we use to separate VerseRange parts on output
 922  
      */
 923  
     public static final char RANGE_PREF_DELIM = RANGE_OSIS_DELIM;
 924  
 
 925  
     /**
 926  
      * The Versification with which this range is defined.
 927  
      */
 928  
     private transient Versification v11n;
 929  
 
 930  
     /**
 931  
      * The start of the range
 932  
      */
 933  
     private Verse start;
 934  
 
 935  
     /**
 936  
      * The number of verses in the range
 937  
      */
 938  
     private int verseCount;
 939  
 
 940  
     /**
 941  
      * The last verse. Not actually needed, since it can be computed.
 942  
      */
 943  
     private transient Verse end;
 944  
 
 945  
     /**
 946  
      * Allow the conversion to and from other number representations.
 947  
      */
 948  
     private transient NumberShaper shaper;
 949  
 
 950  
     /**
 951  
      * The parent key.
 952  
      */
 953  
     private transient Key parent;
 954  
 
 955  
     /**
 956  
      * The original string for picky users
 957  
      */
 958  
     private transient String originalName;
 959  
 
 960  
     /**
 961  
      * Serialization ID
 962  
      */
 963  
     static final long serialVersionUID = 8307795549869653580L;
 964  
 }