Coverage Report - org.crosswire.jsword.passage.AbstractPassage
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractPassage
0%
0/437
0%
0/196
3
AbstractPassage$VerseRangeIterator
0%
0/32
0%
0/14
3
 
 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.BufferedReader;
 23  
 import java.io.BufferedWriter;
 24  
 import java.io.IOException;
 25  
 import java.io.ObjectInputStream;
 26  
 import java.io.ObjectOutputStream;
 27  
 import java.io.Reader;
 28  
 import java.io.Writer;
 29  
 import java.util.ArrayList;
 30  
 import java.util.BitSet;
 31  
 import java.util.Iterator;
 32  
 import java.util.List;
 33  
 import java.util.NoSuchElementException;
 34  
 
 35  
 import org.crosswire.common.util.StringUtil;
 36  
 import org.crosswire.jsword.JSMsg;
 37  
 import org.crosswire.jsword.JSOtherMsg;
 38  
 import org.crosswire.jsword.versification.BibleBook;
 39  
 import org.crosswire.jsword.versification.Versification;
 40  
 import org.crosswire.jsword.versification.system.Versifications;
 41  
 import org.slf4j.Logger;
 42  
 import org.slf4j.LoggerFactory;
 43  
 
 44  
 /**
 45  
  * This is a base class to help with some of the common implementation details
 46  
  * of being a Passage.
 47  
  * <p>
 48  
  * Importantly, this class takes care of Serialization in a general yet
 49  
  * optimized way. I think I am going to have a look at replacement here.
 50  
  *
 51  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 52  
  * @author Joe Walker
 53  
  * @author DM Smith
 54  
  */
 55  0
 public abstract class AbstractPassage implements Passage {
 56  
     /**
 57  
      * Setup that leaves original name being null
 58  
      *
 59  
      * @param v11n
 60  
      *            The Versification to which this Passage belongs.
 61  
      */
 62  
     protected AbstractPassage(Versification v11n) {
 63  0
         this(v11n, null);
 64  0
     }
 65  
 
 66  
     /**
 67  
      * Setup the original name of this reference
 68  
      *
 69  
      * @param v11n
 70  
      *            The Versification to which this Passage belongs.
 71  
      * @param passageName
 72  
      *            The text originally used to create this Passage.
 73  
      */
 74  0
     protected AbstractPassage(Versification v11n, String passageName) {
 75  0
         this.v11n = v11n;
 76  0
         this.originalName = passageName;
 77  0
         this.listeners = new ArrayList<PassageListener>();
 78  0
     }
 79  
 
 80  
     /* (non-Javadoc)
 81  
      * @see org.crosswire.jsword.passage.Passage#getVersification()
 82  
      */
 83  
     public Versification getVersification() {
 84  0
         return v11n;
 85  
     }
 86  
 
 87  
     /* (non-Javadoc)
 88  
      * @see org.crosswire.jsword.passage.VerseKey#reversify(org.crosswire.jsword.versification.Versification)
 89  
      */
 90  
     public Passage reversify(Versification newVersification) {
 91  0
         if (v11n.equals(newVersification)) {
 92  0
             return this;
 93  
         }
 94  0
         throw new UnsupportedOperationException();
 95  
     }
 96  
 
 97  
     /* (non-Javadoc)
 98  
      * @see org.crosswire.jsword.passage.VerseKey#isWhole()
 99  
      */
 100  
     public boolean isWhole() {
 101  0
         throw new UnsupportedOperationException();
 102  
     }
 103  
 
 104  
     /* (non-Javadoc)
 105  
      * @see org.crosswire.jsword.passage.VerseKey#getWhole()
 106  
      */
 107  
     public Passage getWhole() {
 108  0
         throw new UnsupportedOperationException();
 109  
     }
 110  
 
 111  
     /* (non-Javadoc)
 112  
      * @see java.lang.Comparable#compareTo(java.lang.Object)
 113  
      */
 114  
     public int compareTo(Key obj) {
 115  0
         Passage thatref = (Passage) obj;
 116  0
         if (thatref.countVerses() == 0) {
 117  0
             if (countVerses() == 0) {
 118  0
                 return 0;
 119  
             }
 120  
             // that is empty so he should come before me
 121  0
             return -1;
 122  
         }
 123  
 
 124  0
         if (countVerses() == 0) {
 125  
             // we are empty be he isn't so we are first
 126  0
             return 1;
 127  
         }
 128  
 
 129  0
         Verse thatfirst = thatref.getVerseAt(0);
 130  0
         Verse thisfirst = getVerseAt(0);
 131  
 
 132  0
         return getVersification().distance(thatfirst, thisfirst);
 133  
     }
 134  
 
 135  
     @Override
 136  
     public AbstractPassage clone() {
 137  
         // This gets us a shallow copy
 138  0
         AbstractPassage copy = null;
 139  
 
 140  
         try {
 141  0
             copy = (AbstractPassage) super.clone();
 142  0
             copy.listeners = new ArrayList<PassageListener>();
 143  0
             copy.listeners.addAll(listeners);
 144  
 
 145  0
             copy.originalName = originalName;
 146  0
         } catch (CloneNotSupportedException e) {
 147  0
             assert false : e;
 148  0
         }
 149  
 
 150  0
         return copy;
 151  
     }
 152  
 
 153  
     @Override
 154  
     public boolean equals(Object obj) {
 155  
         // This is cheating because I am supposed to say:
 156  
         // <code>!obj.getClass().equals(this.getClass())</code>
 157  
         // However I think it is entirely valid for a RangedPassage
 158  
         // to equal a DistinctPassage since the point of the Factory
 159  
         // is that the user does not need to know the actual type of the
 160  
         // Object he is using.
 161  0
         if (!(obj instanceof Passage)) {
 162  0
             return false;
 163  
         }
 164  0
         Passage that = (Passage) obj;
 165  
         // The real test
 166  
         // FIXME: this is not really true since the versification any longer.
 167  0
         return that.getOsisRef().equals(getOsisRef());
 168  
     }
 169  
 
 170  
     @Override
 171  
     public int hashCode() {
 172  0
         return getOsisRef().hashCode();
 173  
     }
 174  
 
 175  
     /* (non-Javadoc)
 176  
      * @see org.crosswire.jsword.passage.Key#getName()
 177  
      */
 178  
     public String getName() {
 179  0
         if (PassageUtil.isPersistentNaming() && originalName != null) {
 180  0
             return originalName;
 181  
         }
 182  
 
 183  0
         StringBuilder retcode = new StringBuilder();
 184  
 
 185  0
         Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
 186  0
         Verse current = null;
 187  0
         while (it.hasNext()) {
 188  0
             VerseRange range = it.next();
 189  0
             retcode.append(range.getName(current));
 190  
 
 191  
             // FIXME: Potential bug. According to iterator contract hasNext and
 192  
             // next must be paired.
 193  0
             if (it.hasNext()) {
 194  0
                 retcode.append(AbstractPassage.REF_PREF_DELIM);
 195  
             }
 196  
 
 197  0
             current = range.getStart();
 198  0
         }
 199  
 
 200  0
         return retcode.toString();
 201  
     }
 202  
 
 203  
     /* (non-Javadoc)
 204  
      * @see org.crosswire.jsword.passage.Key#getName(org.crosswire.jsword.passage.Key)
 205  
      */
 206  
     public String getName(Key base) {
 207  0
         return getName();
 208  
     }
 209  
 
 210  
     /* (non-Javadoc)
 211  
      * @see org.crosswire.jsword.passage.Key#getRootName()
 212  
      */
 213  
     public String getRootName() {
 214  0
         Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
 215  0
         while (it.hasNext()) {
 216  0
             VerseRange range = it.next();
 217  0
             return range.getRootName();
 218  
         }
 219  
 
 220  0
         return getName();
 221  
     }
 222  
 
 223  
     /* (non-Javadoc)
 224  
      * @see org.crosswire.jsword.passage.Key#getOsisRef()
 225  
      */
 226  
     public String getOsisRef() {
 227  0
         StringBuilder retcode = new StringBuilder();
 228  
 
 229  0
         Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
 230  0
         boolean hasNext = it.hasNext();
 231  0
         while (hasNext) {
 232  0
             Key range = it.next();
 233  0
             retcode.append(range.getOsisRef());
 234  
 
 235  0
             hasNext = it.hasNext();
 236  0
             if (hasNext) {
 237  0
                 retcode.append(AbstractPassage.REF_OSIS_DELIM);
 238  
             }
 239  0
         }
 240  
 
 241  0
         return retcode.toString();
 242  
     }
 243  
 
 244  
     /* (non-Javadoc)
 245  
      * @see org.crosswire.jsword.passage.Key#getOsisID()
 246  
      */
 247  
     public String getOsisID() {
 248  0
         StringBuilder retcode = new StringBuilder();
 249  
 
 250  0
         Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
 251  0
         boolean hasNext = it.hasNext();
 252  0
         while (hasNext) {
 253  0
             Key range = it.next();
 254  0
             retcode.append(range.getOsisID());
 255  
 
 256  0
             hasNext = it.hasNext();
 257  0
             if (hasNext) {
 258  0
                 retcode.append(AbstractPassage.REF_OSIS_DELIM);
 259  
             }
 260  0
         }
 261  
 
 262  0
         return retcode.toString();
 263  
     }
 264  
 
 265  
     @Override
 266  
     public String toString() {
 267  0
         return getName();
 268  
     }
 269  
 
 270  
     /* (non-Javadoc)
 271  
      * @see org.crosswire.jsword.passage.Passage#getOverview()
 272  
      */
 273  
     public String getOverview() {
 274  
         // TRANSLATOR: This provides an overview of the verses in one or more books. The placeholders here deserve extra comment.
 275  
         // {0,number,integer} is a placeholder for the count of verses. It will be displayed as an integer using the number system of the user's locale.
 276  
         // {0,choice,0#verses|1#verse|1<verses} uses the value of the number of verses to display the correct singular or plural form for the word "verse"
 277  
         //    Choices are separated by |. And each choice consists of a number, a comparison and the value to use when the comparison is met.
 278  
         //    Choices are ordered from smallest to largest. The numbers represent boundaries that determine when a choice is used.
 279  
         //    The comparison # means to match exactly.
 280  
         //    The comparison < means that the number on the left is less than the number being evaluated.
 281  
         //    Here, 0 is the first boundary specified by a #. So every number less than or equal to 0 get the first choice.
 282  
         //    In this situation, we are dealing with counting numbers, so we'll never have negative numbers.
 283  
         //    Next choice is 1 with a boundary specified by #. So all numbers greater than 0 (the first choice) but less than or equal to 1 get the second choice.
 284  
         //    In this situation, the only number that will match is 1.
 285  
         //    The final choice is 1<. This means that every number greater than 1 will get this choice.
 286  
         // Putting the first two placeholders together we get "0 verses", "1 verse" or "n verses" (where n is 2 or more)
 287  
         // The reason to go into this is that this pattern works for English. Other languages might have different ways of representing singular and plurals.
 288  
         // {1,number,integer} is a placeholder for the count of Bible books. It works the same way as the count of verses.
 289  
         // {1,choice,0#books|1#book|1<books} is the placeholder for the singular or plural of "book"
 290  0
         return JSMsg.gettext("{0,number,integer} {0,choice,0#verses|1#verse|1<verses} in {1,number,integer} {1,choice,0#books|1#book|1<books}",
 291  
                 Integer.valueOf(countVerses()), Integer.valueOf(booksInPassage()
 292  
                 ));
 293  
     }
 294  
 
 295  
     /* (non-Javadoc)
 296  
      * @see org.crosswire.jsword.passage.Key#isEmpty()
 297  
      */
 298  
     public boolean isEmpty() {
 299  
         // Is there any content?
 300  0
         return !iterator().hasNext();
 301  
     }
 302  
 
 303  
     /* (non-Javadoc)
 304  
      * @see org.crosswire.jsword.passage.Passage#countVerses()
 305  
      */
 306  
     public int countVerses() {
 307  0
         int count = 0;
 308  
 
 309  0
         for (Iterator<?> iter = iterator(); iter.hasNext(); iter.next()) {
 310  0
             count++;
 311  
         }
 312  
 
 313  0
         return count;
 314  
     }
 315  
 
 316  
     /* (non-Javadoc)
 317  
      * @see org.crosswire.jsword.passage.Passage#hasRanges(org.crosswire.jsword.passage.RestrictionType)
 318  
      */
 319  
     public boolean hasRanges(RestrictionType restrict) {
 320  0
         int count = 0;
 321  
 
 322  0
         Iterator<VerseRange> it = rangeIterator(restrict);
 323  0
         while (it.hasNext()) {
 324  0
             it.next();
 325  0
             count++;
 326  0
             if (count == 2) {
 327  0
                 return true;
 328  
             }
 329  
         }
 330  
 
 331  0
         return false;
 332  
     }
 333  
 
 334  
     /* (non-Javadoc)
 335  
      * @see org.crosswire.jsword.passage.Passage#countRanges(org.crosswire.jsword.passage.RestrictionType)
 336  
      */
 337  
     public int countRanges(RestrictionType restrict) {
 338  0
         int count = 0;
 339  
 
 340  0
         Iterator<VerseRange> it = rangeIterator(restrict);
 341  0
         while (it.hasNext()) {
 342  0
             it.next();
 343  0
             count++;
 344  
         }
 345  
 
 346  0
         return count;
 347  
     }
 348  
 
 349  
     /* (non-Javadoc)
 350  
      * @see org.crosswire.jsword.passage.Passage#booksInPassage()
 351  
      */
 352  
     public int booksInPassage() {
 353  
         // FIXME(DMS): a passage does not have to be ordered, for example PassageTally.
 354  0
         BibleBook currentBook = null;
 355  0
         int bookCount = 0;
 356  
 
 357  0
         for (Key aKey : this) {
 358  0
             Verse verse = (Verse) aKey;
 359  0
             if (currentBook != verse.getBook()) {
 360  0
                 currentBook = verse.getBook();
 361  0
                 bookCount++;
 362  
             }
 363  0
         }
 364  
 
 365  0
         return bookCount;
 366  
     }
 367  
 
 368  
     /* (non-Javadoc)
 369  
      * @see org.crosswire.jsword.passage.Passage#getVerseAt(int)
 370  
      */
 371  
     public Verse getVerseAt(int offset) throws ArrayIndexOutOfBoundsException {
 372  0
         Iterator<Key> it = iterator();
 373  0
         Object retcode = null;
 374  
 
 375  0
         for (int i = 0; i <= offset; i++) {
 376  0
             if (!it.hasNext()) {
 377  0
                 throw new ArrayIndexOutOfBoundsException(JSOtherMsg.lookupText("Index out of range (Given {0,number,integer}, Max {1,number,integer}).", Integer.valueOf(offset), Integer.valueOf(countVerses())));
 378  
             }
 379  
 
 380  0
             retcode = it.next();
 381  
         }
 382  
 
 383  0
         return (Verse) retcode;
 384  
     }
 385  
 
 386  
     /* (non-Javadoc)
 387  
      * @see org.crosswire.jsword.passage.Passage#getRangeAt(int, org.crosswire.jsword.passage.RestrictionType)
 388  
      */
 389  
     public VerseRange getRangeAt(int offset, RestrictionType restrict) throws ArrayIndexOutOfBoundsException {
 390  0
         Iterator<VerseRange> it = rangeIterator(restrict);
 391  0
         Object retcode = null;
 392  
 
 393  0
         for (int i = 0; i <= offset; i++) {
 394  0
             if (!it.hasNext()) {
 395  0
                 throw new ArrayIndexOutOfBoundsException(JSOtherMsg.lookupText("Index out of range (Given {0,number,integer}, Max {1,number,integer}).", Integer.valueOf(offset), Integer.valueOf(countVerses())));
 396  
             }
 397  
 
 398  0
             retcode = it.next();
 399  
         }
 400  
 
 401  0
         return (VerseRange) retcode;
 402  
     }
 403  
 
 404  
     /* (non-Javadoc)
 405  
      * @see org.crosswire.jsword.passage.Passage#rangeIterator(org.crosswire.jsword.passage.RestrictionType)
 406  
      */
 407  
     public Iterator<VerseRange> rangeIterator(RestrictionType restrict) {
 408  0
         return new VerseRangeIterator(getVersification(), iterator(), restrict);
 409  
     }
 410  
 
 411  
     /* (non-Javadoc)
 412  
      * @see org.crosswire.jsword.passage.Passage#containsAll(org.crosswire.jsword.passage.Passage)
 413  
      */
 414  
     public boolean containsAll(Passage that) {
 415  0
         if (that instanceof RangedPassage) {
 416  0
             Iterator<VerseRange> iter = null;
 417  
 
 418  0
             iter = ((RangedPassage) that).rangeIterator(RestrictionType.NONE);
 419  0
             while (iter.hasNext()) {
 420  0
                 if (!contains(iter.next())) {
 421  0
                     return false;
 422  
                 }
 423  
             }
 424  0
         } else {
 425  0
             Iterator<Key> iter = that.iterator();
 426  0
             while (iter.hasNext()) {
 427  0
                 if (!contains(iter.next())) {
 428  0
                     return false;
 429  
                 }
 430  
             }
 431  
 
 432  
         }
 433  
 
 434  0
         return true;
 435  
     }
 436  
 
 437  
     /* (non-Javadoc)
 438  
      * @see org.crosswire.jsword.passage.Passage#trimVerses(int)
 439  
      */
 440  
     public Passage trimVerses(int count) {
 441  0
         optimizeWrites();
 442  0
         raiseNormalizeProtection();
 443  
 
 444  0
         int i = 0;
 445  0
         boolean overflow = false;
 446  
 
 447  0
         Passage remainder = this.clone();
 448  
 
 449  0
         for (Key verse : this) {
 450  0
             i++;
 451  0
             if (i > count) {
 452  0
                 remove(verse);
 453  0
                 overflow = true;
 454  
             } else {
 455  0
                 remainder.remove(verse);
 456  
             }
 457  
         }
 458  
 
 459  0
         lowerNormalizeProtection();
 460  
         // The event notification is done by the remove above
 461  
 
 462  0
         if (overflow) {
 463  0
             return remainder;
 464  
         }
 465  0
         return null;
 466  
     }
 467  
 
 468  
     /* (non-Javadoc)
 469  
      * @see org.crosswire.jsword.passage.Passage#trimRanges(int, org.crosswire.jsword.passage.RestrictionType)
 470  
      */
 471  
     public Passage trimRanges(int count, RestrictionType restrict) {
 472  0
         optimizeWrites();
 473  0
         raiseNormalizeProtection();
 474  
 
 475  0
         int i = 0;
 476  0
         boolean overflow = false;
 477  
 
 478  0
         Passage remainder = this.clone();
 479  
 
 480  0
         Iterator<VerseRange> it = rangeIterator(restrict);
 481  0
         while (it.hasNext()) {
 482  0
             i++;
 483  0
             Key range = it.next();
 484  
 
 485  0
             if (i > count) {
 486  0
                 remove(range);
 487  0
                 overflow = true;
 488  
             } else {
 489  0
                 remainder.remove(range);
 490  
             }
 491  0
         }
 492  
 
 493  0
         lowerNormalizeProtection();
 494  
         // The event notification is done by the remove above
 495  
 
 496  0
         if (overflow) {
 497  0
             return remainder;
 498  
         }
 499  0
         return null;
 500  
     }
 501  
 
 502  
     /* Now supports adding keys from different versifications.
 503  
      * (non-Javadoc)
 504  
      * @see org.crosswire.jsword.passage.Key#addAll(org.crosswire.jsword.passage.Key)
 505  
      */
 506  
     public void addAll(Key key) {
 507  
         //check for key empty. This avoids the AIOBounds with that.getVerseAt, during event firing
 508  0
         if (key.isEmpty()) {
 509  
             //nothing to add
 510  0
             return;
 511  
         }
 512  
 
 513  0
         optimizeWrites();
 514  0
         raiseEventSuppresion();
 515  0
         raiseNormalizeProtection();
 516  
 
 517  
 
 518  0
         if (key instanceof RangedPassage) {
 519  0
             Iterator<VerseRange> it = ((RangedPassage) key).rangeIterator(RestrictionType.NONE);
 520  0
             while (it.hasNext()) {
 521  
                 // Avoid touching store to make thread safety easier.
 522  0
                 add(it.next());
 523  
             }
 524  0
         } else {
 525  0
             for (Key subkey : key) {
 526  0
                 add(subkey);
 527  
             }
 528  
         }
 529  
 
 530  0
         lowerNormalizeProtection();
 531  0
         if (lowerEventSuppressionAndTest()) {
 532  0
             if (key instanceof Passage) {
 533  0
                 Passage that = (Passage) key;
 534  0
                 fireIntervalAdded(this, that.getVerseAt(0), that.getVerseAt(that.countVerses() - 1));
 535  0
             } else if (key instanceof VerseRange) {
 536  0
                 VerseRange that = (VerseRange) key;
 537  0
                 fireIntervalAdded(this, that.getStart(), that.getEnd());
 538  0
             } else if (key instanceof Verse) {
 539  0
                 Verse that = (Verse) key;
 540  0
                 fireIntervalAdded(this, that, that);
 541  
             }
 542  
         }
 543  0
     }
 544  
 
 545  
     /* (non-Javadoc)
 546  
      * @see org.crosswire.jsword.passage.Key#removeAll(org.crosswire.jsword.passage.Key)
 547  
      */
 548  
     public void removeAll(Key key) {
 549  0
         optimizeWrites();
 550  0
         raiseEventSuppresion();
 551  0
         raiseNormalizeProtection();
 552  
 
 553  0
         if (key instanceof RangedPassage) {
 554  0
             Iterator<VerseRange> it = ((RangedPassage) key).rangeIterator(RestrictionType.NONE);
 555  0
             while (it.hasNext()) {
 556  
                 // Avoid touching store to make thread safety easier.
 557  0
                 remove(it.next());
 558  
             }
 559  0
         } else {
 560  0
             Iterator<Key> it = key.iterator();
 561  0
             while (it.hasNext()) {
 562  
                 // Avoid touching store to make thread safety easier.
 563  0
                 remove(it.next());
 564  
             }
 565  
         }
 566  
 
 567  0
         lowerNormalizeProtection();
 568  0
         if (lowerEventSuppressionAndTest()) {
 569  0
             if (key instanceof Passage) {
 570  0
                 Passage that = (Passage) key;
 571  0
                 fireIntervalRemoved(this, that.getVerseAt(0), that.getVerseAt(that.countVerses() - 1));
 572  0
             } else if (key instanceof VerseRange) {
 573  0
                 VerseRange that = (VerseRange) key;
 574  0
                 fireIntervalRemoved(this, that.getStart(), that.getEnd());
 575  0
             } else if (key instanceof Verse) {
 576  0
                 Verse that = (Verse) key;
 577  0
                 fireIntervalRemoved(this, that, that);
 578  
             }
 579  
         }
 580  0
     }
 581  
 
 582  
     /* (non-Javadoc)
 583  
      * @see org.crosswire.jsword.passage.Key#retainAll(org.crosswire.jsword.passage.Key)
 584  
      */
 585  
     public void retainAll(Key key) {
 586  0
         optimizeWrites();
 587  0
         raiseEventSuppresion();
 588  0
         raiseNormalizeProtection();
 589  
 
 590  0
         Passage temp = this.clone();
 591  0
         for (Key verse : temp) {
 592  0
             if (!key.contains(verse)) {
 593  0
                 remove(verse);
 594  
             }
 595  
         }
 596  
 
 597  0
         lowerNormalizeProtection();
 598  0
         if (lowerEventSuppressionAndTest()) {
 599  0
             fireIntervalRemoved(this, null, null);
 600  
         }
 601  0
     }
 602  
 
 603  
     /* (non-Javadoc)
 604  
      * @see org.crosswire.jsword.passage.Key#clear()
 605  
      */
 606  
     public void clear() {
 607  0
         optimizeWrites();
 608  0
         raiseNormalizeProtection();
 609  
 
 610  0
         remove(getVersification().getAllVerses());
 611  
 
 612  0
         if (lowerEventSuppressionAndTest()) {
 613  0
             fireIntervalRemoved(this, null, null);
 614  
         }
 615  0
     }
 616  
 
 617  
     /* (non-Javadoc)
 618  
      * @see org.crosswire.jsword.passage.Key#blur(int, org.crosswire.jsword.passage.RestrictionType)
 619  
      */
 620  
     public void blur(int verses, RestrictionType restrict) {
 621  0
         optimizeWrites();
 622  0
         raiseEventSuppresion();
 623  0
         raiseNormalizeProtection();
 624  
 
 625  0
         Passage temp = this.clone();
 626  0
         Iterator<VerseRange> it = temp.rangeIterator(RestrictionType.NONE);
 627  
 
 628  0
         while (it.hasNext()) {
 629  0
             VerseRange range = restrict.blur(getVersification(), it.next(), verses, verses);
 630  0
             add(range);
 631  0
         }
 632  
 
 633  0
         lowerNormalizeProtection();
 634  0
         if (lowerEventSuppressionAndTest()) {
 635  0
             fireIntervalAdded(this, null, null);
 636  
         }
 637  0
     }
 638  
 
 639  
     /* (non-Javadoc)
 640  
      * @see org.crosswire.jsword.passage.Passage#writeDescription(java.io.Writer)
 641  
      */
 642  
     public void writeDescription(Writer out) throws IOException {
 643  0
         BufferedWriter bout = new BufferedWriter(out);
 644  0
         bout.write(v11n.getName());
 645  0
         bout.newLine();
 646  
 
 647  0
         Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
 648  
 
 649  0
         while (it.hasNext()) {
 650  0
             Key range = it.next();
 651  0
             bout.write(range.getName());
 652  0
             bout.newLine();
 653  0
         }
 654  
 
 655  0
         bout.flush();
 656  0
     }
 657  
 
 658  
     /* (non-Javadoc)
 659  
      * @see org.crosswire.jsword.passage.Passage#readDescription(java.io.Reader)
 660  
      */
 661  
     public void readDescription(Reader in) throws IOException, NoSuchVerseException {
 662  0
         raiseEventSuppresion();
 663  0
         raiseNormalizeProtection();
 664  
 
 665  0
         int count = 0; // number of lines read
 666  
         // Quiet Android from complaining about using the default BufferReader buffer size.
 667  
         // The actual buffer size is undocumented. So this is a good idea any way.
 668  0
         BufferedReader bin = new BufferedReader(in, 8192);
 669  
 
 670  0
         String v11nName = bin.readLine();
 671  0
         v11n = Versifications.instance().getVersification(v11nName);
 672  
 
 673  
         while (true) {
 674  0
             String line = bin.readLine();
 675  0
             if (line == null) {
 676  0
                 break;
 677  
             }
 678  
 
 679  0
             count++;
 680  0
             addVerses(line, null);
 681  0
         }
 682  
 
 683  
         // If the file was empty then there is nothing to do
 684  0
         if (count == 0) {
 685  0
             return;
 686  
         }
 687  
 
 688  0
         lowerNormalizeProtection();
 689  0
         if (lowerEventSuppressionAndTest()) {
 690  0
             fireIntervalAdded(this, getVerseAt(0), getVerseAt(countVerses() - 1));
 691  
         }
 692  0
     }
 693  
 
 694  
     /* (non-Javadoc)
 695  
      * @see org.crosswire.jsword.passage.Passage#optimizeReads()
 696  
      */
 697  
     public void optimizeReads() {
 698  0
     }
 699  
 
 700  
     /**
 701  
      * Simple method to instruct children to stop caching results
 702  
      */
 703  
     protected void optimizeWrites() {
 704  0
     }
 705  
 
 706  
     /* (non-Javadoc)
 707  
      * @see org.crosswire.jsword.passage.Passage#addPassageListener(org.crosswire.jsword.passage.PassageListener)
 708  
      */
 709  
     public void addPassageListener(PassageListener li) {
 710  0
         synchronized (listeners) {
 711  0
             listeners.add(li);
 712  0
         }
 713  0
     }
 714  
 
 715  
     /* (non-Javadoc)
 716  
      * @see org.crosswire.jsword.passage.Passage#removePassageListener(org.crosswire.jsword.passage.PassageListener)
 717  
      */
 718  
     public void removePassageListener(PassageListener li) {
 719  0
         synchronized (listeners) {
 720  0
             listeners.remove(li);
 721  0
         }
 722  0
     }
 723  
 
 724  
     /* (non-Javadoc)
 725  
      * @see org.crosswire.jsword.passage.Passage#contains(org.crosswire.jsword.passage.Key)
 726  
      */
 727  
     public boolean contains(Key key) {
 728  0
         Passage ref = KeyUtil.getPassage(key);
 729  0
         return containsAll(ref);
 730  
     }
 731  
 
 732  
     /* (non-Javadoc)
 733  
      * @see org.crosswire.jsword.passage.Key#getCardinality()
 734  
      */
 735  
     public int getCardinality() {
 736  0
         return countVerses();
 737  
     }
 738  
 
 739  
     /* (non-Javadoc)
 740  
      * @see org.crosswire.jsword.passage.Key#indexOf(org.crosswire.jsword.passage.Key)
 741  
      */
 742  
     public int indexOf(Key that) {
 743  0
         int index = 0;
 744  0
         for (Key key : this) {
 745  0
             if (key.equals(that)) {
 746  0
                 return index;
 747  
             }
 748  
 
 749  0
             index++;
 750  
         }
 751  
 
 752  0
         return -1;
 753  
     }
 754  
 
 755  
     /* (non-Javadoc)
 756  
      * @see org.crosswire.jsword.passage.Key#canHaveChildren()
 757  
      */
 758  
     public boolean canHaveChildren() {
 759  0
         return false;
 760  
     }
 761  
 
 762  
     /* (non-Javadoc)
 763  
      * @see org.crosswire.jsword.passage.Key#getChildCount()
 764  
      */
 765  
     public int getChildCount() {
 766  0
         return 0;
 767  
     }
 768  
 
 769  
     /* (non-Javadoc)
 770  
      * @see org.crosswire.jsword.passage.Key#get(int)
 771  
      */
 772  
     public Key get(int index) {
 773  0
         return getVerseAt(index);
 774  
     }
 775  
 
 776  
     /* (non-Javadoc)
 777  
      * @see org.crosswire.jsword.passage.Key#getParent()
 778  
      */
 779  
     public Key getParent() {
 780  0
         return parent;
 781  
     }
 782  
 
 783  
     /**
 784  
      * Set a parent Key. This allows us to follow the Key interface more
 785  
      * closely, although the concept of a parent for a verse is fairly alien.
 786  
      *
 787  
      * @param parent
 788  
      *            The parent Key for this verse
 789  
      */
 790  
     public void setParent(Key parent) {
 791  0
         this.parent = parent;
 792  0
     }
 793  
 
 794  
     /**
 795  
      * AbstractPassage subclasses must call this method <b>after</b> one or more
 796  
      * elements of the list are added. The changed elements are specified by a
 797  
      * closed interval from start to end.
 798  
      *
 799  
      * @param source
 800  
      *            The thing that changed, typically "this".
 801  
      * @param start
 802  
      *            One end of the new interval.
 803  
      * @param end
 804  
      *            The other end of the new interval.
 805  
      * @see PassageListener
 806  
      */
 807  
     protected void fireIntervalAdded(Object source, Verse start, Verse end) {
 808  0
         if (suppressEvents != 0) {
 809  0
             return;
 810  
         }
 811  
 
 812  
         // Create Event
 813  0
         PassageEvent ev = new PassageEvent(source, PassageEvent.EventType.ADDED, start, end);
 814  
 
 815  
         // Copy listener vector so it won't change while firing
 816  
         List<PassageListener> temp;
 817  0
         synchronized (listeners) {
 818  0
             temp = new ArrayList<PassageListener>();
 819  0
             temp.addAll(listeners);
 820  0
         }
 821  
 
 822  
         // And run through the list shouting
 823  0
         for (int i = 0; i < temp.size(); i++) {
 824  0
             PassageListener rl = temp.get(i);
 825  0
             rl.versesAdded(ev);
 826  
         }
 827  0
     }
 828  
 
 829  
     /**
 830  
      * AbstractPassage subclasses must call this method <b>before</b> one or
 831  
      * more elements of the list are added. The changed elements are specified
 832  
      * by a closed interval from start to end.
 833  
      *
 834  
      * @param source
 835  
      *            The thing that changed, typically "this".
 836  
      * @param start
 837  
      *            One end of the new interval.
 838  
      * @param end
 839  
      *            The other end of the new interval.
 840  
      * @see PassageListener
 841  
      */
 842  
     protected void fireIntervalRemoved(Object source, Verse start, Verse end) {
 843  0
         if (suppressEvents != 0) {
 844  0
             return;
 845  
         }
 846  
 
 847  
         // Create Event
 848  0
         PassageEvent ev = new PassageEvent(source, PassageEvent.EventType.REMOVED, start, end);
 849  
 
 850  
         // Copy listener vector so it won't change while firing
 851  
         List<PassageListener> temp;
 852  0
         synchronized (listeners) {
 853  0
             temp = new ArrayList<PassageListener>();
 854  0
             temp.addAll(listeners);
 855  0
         }
 856  
 
 857  
         // And run through the list shouting
 858  0
         for (int i = 0; i < temp.size(); i++) {
 859  0
             PassageListener rl = temp.get(i);
 860  0
             rl.versesRemoved(ev);
 861  
         }
 862  0
     }
 863  
 
 864  
     /**
 865  
      * AbstractPassage subclasses must call this method <b>before</b> one or
 866  
      * more elements of the list are added. The changed elements are specified
 867  
      * by a closed interval from start to end.
 868  
      *
 869  
      * @param source
 870  
      *            The thing that changed, typically "this".
 871  
      * @param start
 872  
      *            One end of the new interval.
 873  
      * @param end
 874  
      *            The other end of the new interval.
 875  
      * @see PassageListener
 876  
      */
 877  
     protected void fireContentsChanged(Object source, Verse start, Verse end) {
 878  0
         if (suppressEvents != 0) {
 879  0
             return;
 880  
         }
 881  
 
 882  
         // Create Event
 883  0
         PassageEvent ev = new PassageEvent(source, PassageEvent.EventType.CHANGED, start, end);
 884  
 
 885  
         // Copy listener vector so it won't change while firing
 886  
         List<PassageListener> temp;
 887  0
         synchronized (listeners) {
 888  0
             temp = new ArrayList<PassageListener>();
 889  0
             temp.addAll(listeners);
 890  0
         }
 891  
 
 892  
         // And run through the list shouting
 893  0
         for (int i = 0; i < temp.size(); i++) {
 894  0
             PassageListener rl = temp.get(i);
 895  0
             rl.versesChanged(ev);
 896  
         }
 897  0
     }
 898  
 
 899  
     /**
 900  
      * Create a Passage from a human readable string. The opposite of
 901  
      * <code>toString()</code>. Since this method is not public it leaves
 902  
      * control of <code>suppress_events</code> up to the people
 903  
      * that call it.
 904  
      *
 905  
      * @param refs
 906  
      *            A String containing the text of the RangedPassage
 907  
      * @param basis
 908  
      *            The basis for understanding refs
 909  
      * @throws NoSuchVerseException
 910  
      *             if the string is invalid
 911  
      */
 912  
     protected void addVerses(String refs, Key basis) throws NoSuchVerseException {
 913  0
         optimizeWrites();
 914  
 
 915  0
         String[] parts = StringUtil.split(refs, AbstractPassage.REF_ALLOWED_DELIMS);
 916  0
         if (parts.length == 0) {
 917  0
             return;
 918  
         }
 919  
 
 920  0
         int start = 0;
 921  0
         VerseRange vrBasis = null;
 922  0
         if (basis instanceof Verse) {
 923  0
             vrBasis = new VerseRange(v11n, (Verse) basis);
 924  0
         } else if (basis instanceof VerseRange) {
 925  0
             vrBasis = (VerseRange) basis;
 926  
         } else {
 927  
             // If we are not passed a useful basis,
 928  
             // then we treat the first as a special case because there is
 929  
             // nothing to sensibly base this reference on
 930  0
             vrBasis = VerseRangeFactory.fromString(v11n, parts[0].trim());
 931  
             // We add it because it was part of the given input
 932  0
             add(vrBasis);
 933  0
             start = 1;
 934  
         }
 935  
 
 936  
         // Loop for the other verses, interpreting each on the
 937  
         // basis of the one before.
 938  0
         for (int i = start; i < parts.length; i++) {
 939  0
             VerseRange next = VerseRangeFactory.fromString(v11n, parts[i].trim(), vrBasis);
 940  0
             add(next);
 941  0
             vrBasis = next;
 942  
         }
 943  0
     }
 944  
 
 945  
     /**
 946  
      * We sometimes need to sort ourselves out ... I don't think we need to be
 947  
      * synchronized since we are private and we could check that all public
 948  
      * calling of normalize() are synchronized, however this is safe, and I
 949  
      * don't think there is a cost associated with a double synchronize. (?)
 950  
      */
 951  
     /* protected */void normalize() {
 952  
         // before doing any normalization we should be checking that
 953  
         // skip_normalization == 0, and just returning if so.
 954  0
     }
 955  
 
 956  
     /**
 957  
      * If things want to prevent normalization because they are doing a set of
 958  
      * changes that should be normalized in one go, this is what to call. Be
 959  
      * sure to call lowerNormalizeProtection() when you are done.
 960  
      */
 961  
     public void raiseNormalizeProtection() {
 962  0
         skipNormalization++;
 963  
 
 964  0
         if (skipNormalization > 10) {
 965  
             // This is a bit drastic and does not give us much
 966  
             // chance to fix the error
 967  
             // throw new LogicError();
 968  
 
 969  0
             log.warn("skip_normalization={}", Integer.toString(skipNormalization));
 970  
         }
 971  0
     }
 972  
 
 973  
     /**
 974  
      * If things want to prevent normalization because they are doing a set of
 975  
      * changes that should be normalized in one go, they should call
 976  
      * raiseNormalizeProtection() and when done call this. This also calls
 977  
      * normalize() if the count reaches zero.
 978  
      */
 979  
     public void lowerNormalizeProtection() {
 980  0
         skipNormalization--;
 981  
 
 982  0
         if (skipNormalization == 0) {
 983  0
             normalize();
 984  
         }
 985  
 
 986  0
         assert skipNormalization >= 0;
 987  0
     }
 988  
 
 989  
     /**
 990  
      * If things want to prevent event firing because they are doing a set of
 991  
      * changes that should be notified in one go, this is what to call. Be sure
 992  
      * to call lowerEventSuppression() when you are done.
 993  
      */
 994  
     public void raiseEventSuppresion() {
 995  0
         suppressEvents++;
 996  
 
 997  0
         if (suppressEvents > 10) {
 998  
             // This is a bit drastic and does not give us much
 999  
             // chance to fix the error
 1000  
             // throw new LogicError();
 1001  
 
 1002  0
             log.warn("suppress_events={}", Integer.toString(suppressEvents));
 1003  
         }
 1004  0
     }
 1005  
 
 1006  
     /**
 1007  
      * If things want to prevent event firing because they are doing a set of
 1008  
      * changes that should be notified in one go, they should call
 1009  
      * raiseEventSuppression() and when done call this.
 1010  
      *
 1011  
      * @return true if it is then safe to fire an event.
 1012  
      */
 1013  
     public boolean lowerEventSuppressionAndTest() {
 1014  0
         suppressEvents--;
 1015  0
         assert suppressEvents >= 0;
 1016  
 
 1017  0
         return suppressEvents == 0;
 1018  
     }
 1019  
 
 1020  
     /**
 1021  
      * Convert the Object to a VerseRange. If base is a Verse then return a
 1022  
      * VerseRange of zero length.
 1023  
      *
 1024  
      * @param base
 1025  
      *            The object to be cast
 1026  
      * @return The VerseRange
 1027  
      * @exception java.lang.ClassCastException
 1028  
      *                If this is not a Verse or a VerseRange
 1029  
      */
 1030  
     protected static VerseRange toVerseRange(Versification v11n, Object base) throws ClassCastException {
 1031  0
         assert base != null;
 1032  
 
 1033  0
         if (base instanceof VerseRange) {
 1034  0
             return (VerseRange) base;
 1035  0
         } else if (base instanceof Verse) {
 1036  0
             return new VerseRange(v11n, (Verse) base);
 1037  
         }
 1038  
 
 1039  0
         throw new ClassCastException(JSOtherMsg.lookupText("Can only use Verses and VerseRanges in this Collection"));
 1040  
     }
 1041  
 
 1042  
     /**
 1043  
      * Skip over verses that are part of a range
 1044  
      */
 1045  0
     protected static final class VerseRangeIterator implements Iterator<VerseRange> {
 1046  
         /**
 1047  
          * iterate, amalgamating Verses into VerseRanges
 1048  
          */
 1049  0
         protected VerseRangeIterator(Versification v11n, Iterator<Key> it, RestrictionType restrict) {
 1050  0
             this.v11n = v11n;
 1051  0
             this.it = it;
 1052  0
             this.restrict = restrict;
 1053  
 
 1054  0
             if (it.hasNext()) {
 1055  0
                 nextVerse = (Verse) it.next();
 1056  
             }
 1057  
 
 1058  0
             calculateNext();
 1059  0
         }
 1060  
 
 1061  
         /* (non-Javadoc)
 1062  
          * @see java.util.Iterator#hasNext()
 1063  
          */
 1064  
         public boolean hasNext() {
 1065  0
             return nextRange != null;
 1066  
         }
 1067  
 
 1068  
         /* (non-Javadoc)
 1069  
          * @see java.util.Iterator#next()
 1070  
          */
 1071  
         public VerseRange next() throws NoSuchElementException {
 1072  0
             VerseRange retcode = nextRange;
 1073  
 
 1074  0
             if (retcode == null) {
 1075  0
                 throw new NoSuchElementException();
 1076  
             }
 1077  
 
 1078  0
             calculateNext();
 1079  0
             return retcode;
 1080  
         }
 1081  
 
 1082  
         /* (non-Javadoc)
 1083  
          * @see java.util.Iterator#remove()
 1084  
          */
 1085  
         public void remove() throws UnsupportedOperationException {
 1086  0
             throw new UnsupportedOperationException();
 1087  
         }
 1088  
 
 1089  
         /**
 1090  
          * Find the next VerseRange
 1091  
          */
 1092  
         private void calculateNext() {
 1093  0
             if (nextVerse == null) {
 1094  0
                 nextRange = null;
 1095  0
                 return;
 1096  
             }
 1097  
 
 1098  0
             Verse start = nextVerse;
 1099  0
             Verse end = nextVerse;
 1100  
 
 1101  
             findnext: while (true) {
 1102  0
                 if (!it.hasNext()) {
 1103  0
                     nextVerse = null;
 1104  0
                     break;
 1105  
                 }
 1106  
 
 1107  0
                 nextVerse = (Verse) it.next();
 1108  
 
 1109  
                 // If the next verse adjacent
 1110  0
                 if (!v11n.isAdjacentVerse(end, nextVerse)) {
 1111  0
                     break;
 1112  
                 }
 1113  
 
 1114  
                 // Even if the next verse is adjacent we might want to break
 1115  
                 // if we have moved into a new chapter/book
 1116  0
                 if (!restrict.isSameScope(v11n, end, nextVerse)) {
 1117  0
                     break findnext;
 1118  
                 }
 1119  
 
 1120  0
                 end = nextVerse;
 1121  
             }
 1122  
 
 1123  0
             nextRange = new VerseRange(v11n, start, end);
 1124  0
         }
 1125  
 
 1126  
         /**
 1127  
          * The Versification to which these verses belong.
 1128  
          */
 1129  
         private Versification v11n;
 1130  
 
 1131  
         /**
 1132  
          * The Iterator that we are proxying to
 1133  
          */
 1134  
         private Iterator<Key> it;
 1135  
 
 1136  
         /**
 1137  
          * What is the next VerseRange to be considered
 1138  
          */
 1139  
         private VerseRange nextRange;
 1140  
 
 1141  
         /**
 1142  
          * What is the next Verse to be considered
 1143  
          */
 1144  
         private Verse nextVerse;
 1145  
 
 1146  
         /**
 1147  
          * Do we restrict ranges to not crossing chapter boundaries
 1148  
          */
 1149  
         private RestrictionType restrict;
 1150  
     }
 1151  
 
 1152  
     /**
 1153  
      * Write out the object to the given ObjectOutputStream. There are 3 ways of
 1154  
      * doing this - according to the 3 implementations of Passage.
 1155  
      * <ul>
 1156  
      * <li>Distinct: If we write out a list if verse ordinals then the space
 1157  
      * used is 4 bytes per verse.
 1158  
      * <li>Bitwise: If we write out a bitmap then the space used is something
 1159  
      * like 31104/8 = 4k bytes.
 1160  
      * <li>Ranged: The we write a list of start/end pairs then the space used is
 1161  
      * 8 bytes per range.
 1162  
      * </ul>
 1163  
      * Since we can take our time about this section, we calculate the optimal
 1164  
      * storage method before we do the saving. If some methods come out equal
 1165  
      * first then bitwise is preferred, then distinct, then ranged, because I
 1166  
      * imagine that for speed of deserialization this is the sensible order.
 1167  
      * I've not tested it though.
 1168  
      *
 1169  
      * @param out
 1170  
      *            The stream to write our state to
 1171  
      * @throws IOException
 1172  
      *             if the read fails
 1173  
      */
 1174  
     protected void writeObjectSupport(ObjectOutputStream out) throws IOException {
 1175  
         // Save off the versification by name
 1176  0
         out.writeUTF(v11n.getName());
 1177  
 
 1178  
         // the size in bits of teach storage method
 1179  0
         int bitwiseSize = v11n.maximumOrdinal();
 1180  0
         int rangedSize = 8 * countRanges(RestrictionType.NONE);
 1181  0
         int distinctSize = 4 * countVerses();
 1182  
 
 1183  
         // if bitwise is equal smallest
 1184  0
         if (bitwiseSize <= rangedSize && bitwiseSize <= distinctSize) {
 1185  0
             out.writeInt(BITWISE);
 1186  
 
 1187  0
             BitSet store = new BitSet(bitwiseSize);
 1188  0
             Iterator<Key> iter = iterator();
 1189  0
             while (iter.hasNext()) {
 1190  0
                 Verse verse = (Verse) iter.next();
 1191  0
                 store.set(verse.getOrdinal());
 1192  0
             }
 1193  
 
 1194  0
             out.writeObject(store);
 1195  0
         } else if (distinctSize <= rangedSize) {
 1196  
             // if distinct is not bigger than ranged
 1197  
             // write the Passage type and the number of verses
 1198  0
             out.writeInt(DISTINCT);
 1199  0
             out.writeInt(countVerses());
 1200  
 
 1201  
             // write the verse ordinals in a loop
 1202  0
             for (Key aKey : this) {
 1203  0
                 Verse verse = (Verse) aKey;
 1204  0
                 out.writeInt(verse.getOrdinal());
 1205  0
             }
 1206  
         } else {
 1207  
             // otherwise use ranges
 1208  
             // write the Passage type and the number of ranges
 1209  0
             out.writeInt(RANGED);
 1210  0
             out.writeInt(countRanges(RestrictionType.NONE));
 1211  
 
 1212  
             // write the verse ordinals in a loop
 1213  0
             Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
 1214  0
             while (it.hasNext()) {
 1215  0
                 VerseRange range = it.next();
 1216  0
                 out.writeInt(range.getStart().getOrdinal());
 1217  0
                 out.writeInt(range.getCardinality());
 1218  0
             }
 1219  
         }
 1220  
 
 1221  
         // Ignore the original name. Is this wise?
 1222  
         // I am expecting that people are not that fussed about it and
 1223  
         // it could make everything far more verbose
 1224  0
     }
 1225  
 
 1226  
     /**
 1227  
      * Serialization support.
 1228  
      *
 1229  
      * @param is
 1230  
      * @throws IOException
 1231  
      * @throws ClassNotFoundException
 1232  
      */
 1233  
     private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
 1234  0
         listeners = new ArrayList<PassageListener>();
 1235  0
         originalName = null;
 1236  0
         parent = null;
 1237  0
         skipNormalization = 0;
 1238  0
         suppressEvents = 0;
 1239  
 
 1240  0
         is.defaultReadObject();
 1241  0
     }
 1242  
 
 1243  
     /**
 1244  
      * Write out the object to the given ObjectOutputStream
 1245  
      *
 1246  
      * @param is
 1247  
      *            The stream to read our state from
 1248  
      * @throws IOException
 1249  
      *             if the read fails
 1250  
      * @throws ClassNotFoundException
 1251  
      *             If the read data is incorrect
 1252  
      */
 1253  
     protected void readObjectSupport(ObjectInputStream is) throws IOException, ClassNotFoundException {
 1254  0
         raiseEventSuppresion();
 1255  0
         raiseNormalizeProtection();
 1256  
 
 1257  
         // Read the versification by name
 1258  0
         String v11nName = is.readUTF();
 1259  0
         v11n = Versifications.instance().getVersification(v11nName);
 1260  
 
 1261  0
         int type = is.readInt();
 1262  0
         switch (type) {
 1263  
         case BITWISE:
 1264  0
             BitSet store = (BitSet) is.readObject();
 1265  0
             for (int i = 0; i < v11n.maximumOrdinal(); i++) {
 1266  0
                 if (store.get(i)) {
 1267  0
                     add(v11n.decodeOrdinal(i));
 1268  
                 }
 1269  
             }
 1270  0
             break;
 1271  
 
 1272  
         case DISTINCT:
 1273  0
             int verses = is.readInt();
 1274  0
             for (int i = 0; i < verses; i++) {
 1275  0
                 int ord = is.readInt();
 1276  0
                 add(v11n.decodeOrdinal(ord));
 1277  
             }
 1278  0
             break;
 1279  
 
 1280  
         case RANGED:
 1281  0
             int ranges = is.readInt();
 1282  0
             for (int i = 0; i < ranges; i++) {
 1283  0
                 int ord = is.readInt();
 1284  0
                 int count = is.readInt();
 1285  0
                 add(RestrictionType.NONE.toRange(getVersification(), v11n.decodeOrdinal(ord), count));
 1286  
             }
 1287  0
             break;
 1288  
 
 1289  
         default:
 1290  0
             throw new ClassCastException(JSOtherMsg.lookupText("Can only use Verses and VerseRanges in this Collection"));
 1291  
         }
 1292  
 
 1293  
         // We are ignoring the originalName. It was set to null in the
 1294  
         // default ctor so I will ignore it here.
 1295  
 
 1296  
         // We don't bother to call fireContentsChanged(...) because
 1297  
         // nothing can have registered at this point
 1298  0
         lowerEventSuppressionAndTest();
 1299  0
         lowerNormalizeProtection();
 1300  0
     }
 1301  
 
 1302  
     /**
 1303  
      * The log stream
 1304  
      */
 1305  0
     private static final Logger log = LoggerFactory.getLogger(AbstractPassage.class);
 1306  
 
 1307  
     /**
 1308  
      * Serialization type constant for a BitWise layout
 1309  
      */
 1310  
     protected static final int BITWISE = 0;
 1311  
 
 1312  
     /**
 1313  
      * Serialization type constant for a Distinct layout
 1314  
      */
 1315  
     protected static final int DISTINCT = 1;
 1316  
 
 1317  
     /**
 1318  
      * Serialization type constant for a Ranged layout
 1319  
      */
 1320  
     protected static final int RANGED = 2;
 1321  
 
 1322  
     /**
 1323  
      * Count of serializations methods
 1324  
      */
 1325  
     protected static final int METHOD_COUNT = 3;
 1326  
 
 1327  
     /**
 1328  
      * The Versification to which this passage belongs.
 1329  
      */
 1330  
     private transient Versification v11n;
 1331  
 
 1332  
     /**
 1333  
      * The parent key. See the key interface for more information. NOTE(joe):
 1334  
      * These keys are not serialized, should we?
 1335  
      *
 1336  
      * @see Key
 1337  
      */
 1338  
     private transient Key parent;
 1339  
 
 1340  
     /**
 1341  
      * Support for change notification
 1342  
      */
 1343  
     protected transient List<PassageListener> listeners;
 1344  
 
 1345  
     /**
 1346  
      * The original string for picky users
 1347  
      */
 1348  
     protected transient String originalName;
 1349  
 
 1350  
     /**
 1351  
      * If we have several changes to make then we increment this and then
 1352  
      * decrement it when done (and fire an event off). If the cost of
 1353  
      * calculating the parameters to the fire is high then we can check that
 1354  
      * this is 0 before doing the calculation.
 1355  
      */
 1356  
     protected transient int suppressEvents;
 1357  
 
 1358  
     /**
 1359  
      * Do we skip normalization for now - if we want to skip then we increment
 1360  
      * this, and the decrement it when done.
 1361  
      */
 1362  
     protected transient int skipNormalization;
 1363  
 
 1364  
     /**
 1365  
      * What characters can we use to separate VerseRanges in a Passage
 1366  
      */
 1367  
     public static final String REF_ALLOWED_DELIMS = ",;\n\r\t";
 1368  
 
 1369  
     /**
 1370  
      * What characters should we use to separate VerseRanges in a Passage
 1371  
      */
 1372  
     public static final String REF_PREF_DELIM = ", ";
 1373  
 
 1374  
     /**
 1375  
      * What characters should we use to separate VerseRanges in a Passage
 1376  
      */
 1377  
     public static final String REF_OSIS_DELIM = " ";
 1378  
 
 1379  
     /**
 1380  
      * Serialization ID
 1381  
      */
 1382  
     private static final long serialVersionUID = -5931560451407396276L;
 1383  
 }