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  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          this(v11n, null);
64      }
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      protected AbstractPassage(Versification v11n, String passageName) {
75          this.v11n = v11n;
76          this.originalName = passageName;
77          this.listeners = new ArrayList<PassageListener>();
78      }
79  
80      /* (non-Javadoc)
81       * @see org.crosswire.jsword.passage.Passage#getVersification()
82       */
83      public Versification getVersification() {
84          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          if (v11n.equals(newVersification)) {
92              return this;
93          }
94          throw new UnsupportedOperationException();
95      }
96  
97      /* (non-Javadoc)
98       * @see org.crosswire.jsword.passage.VerseKey#isWhole()
99       */
100     public boolean isWhole() {
101         throw new UnsupportedOperationException();
102     }
103 
104     /* (non-Javadoc)
105      * @see org.crosswire.jsword.passage.VerseKey#getWhole()
106      */
107     public Passage getWhole() {
108         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         Passage thatref = (Passage) obj;
116         if (thatref.countVerses() == 0) {
117             if (countVerses() == 0) {
118                 return 0;
119             }
120             // that is empty so he should come before me
121             return -1;
122         }
123 
124         if (countVerses() == 0) {
125             // we are empty be he isn't so we are first
126             return 1;
127         }
128 
129         Verse thatfirst = thatref.getVerseAt(0);
130         Verse thisfirst = getVerseAt(0);
131 
132         return getVersification().distance(thatfirst, thisfirst);
133     }
134 
135     @Override
136     public AbstractPassage clone() {
137         // This gets us a shallow copy
138         AbstractPassage copy = null;
139 
140         try {
141             copy = (AbstractPassage) super.clone();
142             copy.listeners = new ArrayList<PassageListener>();
143             copy.listeners.addAll(listeners);
144 
145             copy.originalName = originalName;
146         } catch (CloneNotSupportedException e) {
147             assert false : e;
148         }
149 
150         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         if (!(obj instanceof Passage)) {
162             return false;
163         }
164         Passage that = (Passage) obj;
165         // The real test
166         // FIXME: this is not really true since the versification any longer.
167         return that.getOsisRef().equals(getOsisRef());
168     }
169 
170     @Override
171     public int hashCode() {
172         return getOsisRef().hashCode();
173     }
174 
175     /* (non-Javadoc)
176      * @see org.crosswire.jsword.passage.Key#getName()
177      */
178     public String getName() {
179         if (PassageUtil.isPersistentNaming() && originalName != null) {
180             return originalName;
181         }
182 
183         StringBuilder retcode = new StringBuilder();
184 
185         Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
186         Verse current = null;
187         while (it.hasNext()) {
188             VerseRange range = it.next();
189             retcode.append(range.getName(current));
190 
191             // FIXME: Potential bug. According to iterator contract hasNext and
192             // next must be paired.
193             if (it.hasNext()) {
194                 retcode.append(AbstractPassage.REF_PREF_DELIM);
195             }
196 
197             current = range.getStart();
198         }
199 
200         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         return getName();
208     }
209 
210     /* (non-Javadoc)
211      * @see org.crosswire.jsword.passage.Key#getRootName()
212      */
213     public String getRootName() {
214         Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
215         while (it.hasNext()) {
216             VerseRange range = it.next();
217             return range.getRootName();
218         }
219 
220         return getName();
221     }
222 
223     /* (non-Javadoc)
224      * @see org.crosswire.jsword.passage.Key#getOsisRef()
225      */
226     public String getOsisRef() {
227         StringBuilder retcode = new StringBuilder();
228 
229         Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
230         boolean hasNext = it.hasNext();
231         while (hasNext) {
232             Key range = it.next();
233             retcode.append(range.getOsisRef());
234 
235             hasNext = it.hasNext();
236             if (hasNext) {
237                 retcode.append(AbstractPassage.REF_OSIS_DELIM);
238             }
239         }
240 
241         return retcode.toString();
242     }
243 
244     /* (non-Javadoc)
245      * @see org.crosswire.jsword.passage.Key#getOsisID()
246      */
247     public String getOsisID() {
248         StringBuilder retcode = new StringBuilder();
249 
250         Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
251         boolean hasNext = it.hasNext();
252         while (hasNext) {
253             Key range = it.next();
254             retcode.append(range.getOsisID());
255 
256             hasNext = it.hasNext();
257             if (hasNext) {
258                 retcode.append(AbstractPassage.REF_OSIS_DELIM);
259             }
260         }
261 
262         return retcode.toString();
263     }
264 
265     @Override
266     public String toString() {
267         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         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         return !iterator().hasNext();
301     }
302 
303     /* (non-Javadoc)
304      * @see org.crosswire.jsword.passage.Passage#countVerses()
305      */
306     public int countVerses() {
307         int count = 0;
308 
309         for (Iterator<?> iter = iterator(); iter.hasNext(); iter.next()) {
310             count++;
311         }
312 
313         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         int count = 0;
321 
322         Iterator<VerseRange> it = rangeIterator(restrict);
323         while (it.hasNext()) {
324             it.next();
325             count++;
326             if (count == 2) {
327                 return true;
328             }
329         }
330 
331         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         int count = 0;
339 
340         Iterator<VerseRange> it = rangeIterator(restrict);
341         while (it.hasNext()) {
342             it.next();
343             count++;
344         }
345 
346         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         BibleBook currentBook = null;
355         int bookCount = 0;
356 
357         for (Key aKey : this) {
358             Verse verse = (Verse) aKey;
359             if (currentBook != verse.getBook()) {
360                 currentBook = verse.getBook();
361                 bookCount++;
362             }
363         }
364 
365         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         Iterator<Key> it = iterator();
373         Object retcode = null;
374 
375         for (int i = 0; i <= offset; i++) {
376             if (!it.hasNext()) {
377                 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             retcode = it.next();
381         }
382 
383         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         Iterator<VerseRange> it = rangeIterator(restrict);
391         Object retcode = null;
392 
393         for (int i = 0; i <= offset; i++) {
394             if (!it.hasNext()) {
395                 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             retcode = it.next();
399         }
400 
401         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         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         if (that instanceof RangedPassage) {
416             Iterator<VerseRange> iter = null;
417 
418             iter = ((RangedPassage) that).rangeIterator(RestrictionType.NONE);
419             while (iter.hasNext()) {
420                 if (!contains(iter.next())) {
421                     return false;
422                 }
423             }
424         } else {
425             Iterator<Key> iter = that.iterator();
426             while (iter.hasNext()) {
427                 if (!contains(iter.next())) {
428                     return false;
429                 }
430             }
431 
432         }
433 
434         return true;
435     }
436 
437     /* (non-Javadoc)
438      * @see org.crosswire.jsword.passage.Passage#trimVerses(int)
439      */
440     public Passage trimVerses(int count) {
441         optimizeWrites();
442         raiseNormalizeProtection();
443 
444         int i = 0;
445         boolean overflow = false;
446 
447         Passage remainder = this.clone();
448 
449         for (Key verse : this) {
450             i++;
451             if (i > count) {
452                 remove(verse);
453                 overflow = true;
454             } else {
455                 remainder.remove(verse);
456             }
457         }
458 
459         lowerNormalizeProtection();
460         // The event notification is done by the remove above
461 
462         if (overflow) {
463             return remainder;
464         }
465         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         optimizeWrites();
473         raiseNormalizeProtection();
474 
475         int i = 0;
476         boolean overflow = false;
477 
478         Passage remainder = this.clone();
479 
480         Iterator<VerseRange> it = rangeIterator(restrict);
481         while (it.hasNext()) {
482             i++;
483             Key range = it.next();
484 
485             if (i > count) {
486                 remove(range);
487                 overflow = true;
488             } else {
489                 remainder.remove(range);
490             }
491         }
492 
493         lowerNormalizeProtection();
494         // The event notification is done by the remove above
495 
496         if (overflow) {
497             return remainder;
498         }
499         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         if (key.isEmpty()) {
509             //nothing to add
510             return;
511         }
512 
513         optimizeWrites();
514         raiseEventSuppresion();
515         raiseNormalizeProtection();
516 
517 
518         if (key instanceof RangedPassage) {
519             Iterator<VerseRange> it = ((RangedPassage) key).rangeIterator(RestrictionType.NONE);
520             while (it.hasNext()) {
521                 // Avoid touching store to make thread safety easier.
522                 add(it.next());
523             }
524         } else {
525             for (Key subkey : key) {
526                 add(subkey);
527             }
528         }
529 
530         lowerNormalizeProtection();
531         if (lowerEventSuppressionAndTest()) {
532             if (key instanceof Passage) {
533                 Passage that = (Passage) key;
534                 fireIntervalAdded(this, that.getVerseAt(0), that.getVerseAt(that.countVerses() - 1));
535             } else if (key instanceof VerseRange) {
536                 VerseRange that = (VerseRange) key;
537                 fireIntervalAdded(this, that.getStart(), that.getEnd());
538             } else if (key instanceof Verse) {
539                 Verse that = (Verse) key;
540                 fireIntervalAdded(this, that, that);
541             }
542         }
543     }
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         optimizeWrites();
550         raiseEventSuppresion();
551         raiseNormalizeProtection();
552 
553         if (key instanceof RangedPassage) {
554             Iterator<VerseRange> it = ((RangedPassage) key).rangeIterator(RestrictionType.NONE);
555             while (it.hasNext()) {
556                 // Avoid touching store to make thread safety easier.
557                 remove(it.next());
558             }
559         } else {
560             Iterator<Key> it = key.iterator();
561             while (it.hasNext()) {
562                 // Avoid touching store to make thread safety easier.
563                 remove(it.next());
564             }
565         }
566 
567         lowerNormalizeProtection();
568         if (lowerEventSuppressionAndTest()) {
569             if (key instanceof Passage) {
570                 Passage that = (Passage) key;
571                 fireIntervalRemoved(this, that.getVerseAt(0), that.getVerseAt(that.countVerses() - 1));
572             } else if (key instanceof VerseRange) {
573                 VerseRange that = (VerseRange) key;
574                 fireIntervalRemoved(this, that.getStart(), that.getEnd());
575             } else if (key instanceof Verse) {
576                 Verse that = (Verse) key;
577                 fireIntervalRemoved(this, that, that);
578             }
579         }
580     }
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         optimizeWrites();
587         raiseEventSuppresion();
588         raiseNormalizeProtection();
589 
590         Passage temp = this.clone();
591         for (Key verse : temp) {
592             if (!key.contains(verse)) {
593                 remove(verse);
594             }
595         }
596 
597         lowerNormalizeProtection();
598         if (lowerEventSuppressionAndTest()) {
599             fireIntervalRemoved(this, null, null);
600         }
601     }
602 
603     /* (non-Javadoc)
604      * @see org.crosswire.jsword.passage.Key#clear()
605      */
606     public void clear() {
607         optimizeWrites();
608         raiseNormalizeProtection();
609 
610         remove(getVersification().getAllVerses());
611 
612         if (lowerEventSuppressionAndTest()) {
613             fireIntervalRemoved(this, null, null);
614         }
615     }
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         optimizeWrites();
622         raiseEventSuppresion();
623         raiseNormalizeProtection();
624 
625         Passage temp = this.clone();
626         Iterator<VerseRange> it = temp.rangeIterator(RestrictionType.NONE);
627 
628         while (it.hasNext()) {
629             VerseRange range = restrict.blur(getVersification(), it.next(), verses, verses);
630             add(range);
631         }
632 
633         lowerNormalizeProtection();
634         if (lowerEventSuppressionAndTest()) {
635             fireIntervalAdded(this, null, null);
636         }
637     }
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         BufferedWriter bout = new BufferedWriter(out);
644         bout.write(v11n.getName());
645         bout.newLine();
646 
647         Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
648 
649         while (it.hasNext()) {
650             Key range = it.next();
651             bout.write(range.getName());
652             bout.newLine();
653         }
654 
655         bout.flush();
656     }
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         raiseEventSuppresion();
663         raiseNormalizeProtection();
664 
665         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         BufferedReader bin = new BufferedReader(in, 8192);
669 
670         String v11nName = bin.readLine();
671         v11n = Versifications.instance().getVersification(v11nName);
672 
673         while (true) {
674             String line = bin.readLine();
675             if (line == null) {
676                 break;
677             }
678 
679             count++;
680             addVerses(line, null);
681         }
682 
683         // If the file was empty then there is nothing to do
684         if (count == 0) {
685             return;
686         }
687 
688         lowerNormalizeProtection();
689         if (lowerEventSuppressionAndTest()) {
690             fireIntervalAdded(this, getVerseAt(0), getVerseAt(countVerses() - 1));
691         }
692     }
693 
694     /* (non-Javadoc)
695      * @see org.crosswire.jsword.passage.Passage#optimizeReads()
696      */
697     public void optimizeReads() {
698     }
699 
700     /**
701      * Simple method to instruct children to stop caching results
702      */
703     protected void optimizeWrites() {
704     }
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         synchronized (listeners) {
711             listeners.add(li);
712         }
713     }
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         synchronized (listeners) {
720             listeners.remove(li);
721         }
722     }
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         Passage ref = KeyUtil.getPassage(key);
729         return containsAll(ref);
730     }
731 
732     /* (non-Javadoc)
733      * @see org.crosswire.jsword.passage.Key#getCardinality()
734      */
735     public int getCardinality() {
736         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         int index = 0;
744         for (Key key : this) {
745             if (key.equals(that)) {
746                 return index;
747             }
748 
749             index++;
750         }
751 
752         return -1;
753     }
754 
755     /* (non-Javadoc)
756      * @see org.crosswire.jsword.passage.Key#canHaveChildren()
757      */
758     public boolean canHaveChildren() {
759         return false;
760     }
761 
762     /* (non-Javadoc)
763      * @see org.crosswire.jsword.passage.Key#getChildCount()
764      */
765     public int getChildCount() {
766         return 0;
767     }
768 
769     /* (non-Javadoc)
770      * @see org.crosswire.jsword.passage.Key#get(int)
771      */
772     public Key get(int index) {
773         return getVerseAt(index);
774     }
775 
776     /* (non-Javadoc)
777      * @see org.crosswire.jsword.passage.Key#getParent()
778      */
779     public Key getParent() {
780         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         this.parent = parent;
792     }
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         if (suppressEvents != 0) {
809             return;
810         }
811 
812         // Create Event
813         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         synchronized (listeners) {
818             temp = new ArrayList<PassageListener>();
819             temp.addAll(listeners);
820         }
821 
822         // And run through the list shouting
823         for (int i = 0; i < temp.size(); i++) {
824             PassageListener rl = temp.get(i);
825             rl.versesAdded(ev);
826         }
827     }
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         if (suppressEvents != 0) {
844             return;
845         }
846 
847         // Create Event
848         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         synchronized (listeners) {
853             temp = new ArrayList<PassageListener>();
854             temp.addAll(listeners);
855         }
856 
857         // And run through the list shouting
858         for (int i = 0; i < temp.size(); i++) {
859             PassageListener rl = temp.get(i);
860             rl.versesRemoved(ev);
861         }
862     }
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         if (suppressEvents != 0) {
879             return;
880         }
881 
882         // Create Event
883         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         synchronized (listeners) {
888             temp = new ArrayList<PassageListener>();
889             temp.addAll(listeners);
890         }
891 
892         // And run through the list shouting
893         for (int i = 0; i < temp.size(); i++) {
894             PassageListener rl = temp.get(i);
895             rl.versesChanged(ev);
896         }
897     }
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         optimizeWrites();
914 
915         String[] parts = StringUtil.split(refs, AbstractPassage.REF_ALLOWED_DELIMS);
916         if (parts.length == 0) {
917             return;
918         }
919 
920         int start = 0;
921         VerseRange vrBasis = null;
922         if (basis instanceof Verse) {
923             vrBasis = new VerseRange(v11n, (Verse) basis);
924         } else if (basis instanceof VerseRange) {
925             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             vrBasis = VerseRangeFactory.fromString(v11n, parts[0].trim());
931             // We add it because it was part of the given input
932             add(vrBasis);
933             start = 1;
934         }
935 
936         // Loop for the other verses, interpreting each on the
937         // basis of the one before.
938         for (int i = start; i < parts.length; i++) {
939             VerseRange next = VerseRangeFactory.fromString(v11n, parts[i].trim(), vrBasis);
940             add(next);
941             vrBasis = next;
942         }
943     }
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     }
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         skipNormalization++;
963 
964         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             log.warn("skip_normalization={}", Integer.toString(skipNormalization));
970         }
971     }
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         skipNormalization--;
981 
982         if (skipNormalization == 0) {
983             normalize();
984         }
985 
986         assert skipNormalization >= 0;
987     }
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         suppressEvents++;
996 
997         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            log.warn("suppress_events={}", Integer.toString(suppressEvents));
1003        }
1004    }
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        suppressEvents--;
1015        assert suppressEvents >= 0;
1016
1017        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        assert base != null;
1032
1033        if (base instanceof VerseRange) {
1034            return (VerseRange) base;
1035        } else if (base instanceof Verse) {
1036            return new VerseRange(v11n, (Verse) base);
1037        }
1038
1039        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    protected static final class VerseRangeIterator implements Iterator<VerseRange> {
1046        /**
1047         * iterate, amalgamating Verses into VerseRanges
1048         */
1049        protected VerseRangeIterator(Versification v11n, Iterator<Key> it, RestrictionType restrict) {
1050            this.v11n = v11n;
1051            this.it = it;
1052            this.restrict = restrict;
1053
1054            if (it.hasNext()) {
1055                nextVerse = (Verse) it.next();
1056            }
1057
1058            calculateNext();
1059        }
1060
1061        /* (non-Javadoc)
1062         * @see java.util.Iterator#hasNext()
1063         */
1064        public boolean hasNext() {
1065            return nextRange != null;
1066        }
1067
1068        /* (non-Javadoc)
1069         * @see java.util.Iterator#next()
1070         */
1071        public VerseRange next() throws NoSuchElementException {
1072            VerseRange retcode = nextRange;
1073
1074            if (retcode == null) {
1075                throw new NoSuchElementException();
1076            }
1077
1078            calculateNext();
1079            return retcode;
1080        }
1081
1082        /* (non-Javadoc)
1083         * @see java.util.Iterator#remove()
1084         */
1085        public void remove() throws UnsupportedOperationException {
1086            throw new UnsupportedOperationException();
1087        }
1088
1089        /**
1090         * Find the next VerseRange
1091         */
1092        private void calculateNext() {
1093            if (nextVerse == null) {
1094                nextRange = null;
1095                return;
1096            }
1097
1098            Verse start = nextVerse;
1099            Verse end = nextVerse;
1100
1101            findnext: while (true) {
1102                if (!it.hasNext()) {
1103                    nextVerse = null;
1104                    break;
1105                }
1106
1107                nextVerse = (Verse) it.next();
1108
1109                // If the next verse adjacent
1110                if (!v11n.isAdjacentVerse(end, nextVerse)) {
1111                    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                if (!restrict.isSameScope(v11n, end, nextVerse)) {
1117                    break findnext;
1118                }
1119
1120                end = nextVerse;
1121            }
1122
1123            nextRange = new VerseRange(v11n, start, end);
1124        }
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        out.writeUTF(v11n.getName());
1177
1178        // the size in bits of teach storage method
1179        int bitwiseSize = v11n.maximumOrdinal();
1180        int rangedSize = 8 * countRanges(RestrictionType.NONE);
1181        int distinctSize = 4 * countVerses();
1182
1183        // if bitwise is equal smallest
1184        if (bitwiseSize <= rangedSize && bitwiseSize <= distinctSize) {
1185            out.writeInt(BITWISE);
1186
1187            BitSet store = new BitSet(bitwiseSize);
1188            Iterator<Key> iter = iterator();
1189            while (iter.hasNext()) {
1190                Verse verse = (Verse) iter.next();
1191                store.set(verse.getOrdinal());
1192            }
1193
1194            out.writeObject(store);
1195        } else if (distinctSize <= rangedSize) {
1196            // if distinct is not bigger than ranged
1197            // write the Passage type and the number of verses
1198            out.writeInt(DISTINCT);
1199            out.writeInt(countVerses());
1200
1201            // write the verse ordinals in a loop
1202            for (Key aKey : this) {
1203                Verse verse = (Verse) aKey;
1204                out.writeInt(verse.getOrdinal());
1205            }
1206        } else {
1207            // otherwise use ranges
1208            // write the Passage type and the number of ranges
1209            out.writeInt(RANGED);
1210            out.writeInt(countRanges(RestrictionType.NONE));
1211
1212            // write the verse ordinals in a loop
1213            Iterator<VerseRange> it = rangeIterator(RestrictionType.NONE);
1214            while (it.hasNext()) {
1215                VerseRange range = it.next();
1216                out.writeInt(range.getStart().getOrdinal());
1217                out.writeInt(range.getCardinality());
1218            }
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    }
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        listeners = new ArrayList<PassageListener>();
1235        originalName = null;
1236        parent = null;
1237        skipNormalization = 0;
1238        suppressEvents = 0;
1239
1240        is.defaultReadObject();
1241    }
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        raiseEventSuppresion();
1255        raiseNormalizeProtection();
1256
1257        // Read the versification by name
1258        String v11nName = is.readUTF();
1259        v11n = Versifications.instance().getVersification(v11nName);
1260
1261        int type = is.readInt();
1262        switch (type) {
1263        case BITWISE:
1264            BitSet store = (BitSet) is.readObject();
1265            for (int i = 0; i < v11n.maximumOrdinal(); i++) {
1266                if (store.get(i)) {
1267                    add(v11n.decodeOrdinal(i));
1268                }
1269            }
1270            break;
1271
1272        case DISTINCT:
1273            int verses = is.readInt();
1274            for (int i = 0; i < verses; i++) {
1275                int ord = is.readInt();
1276                add(v11n.decodeOrdinal(ord));
1277            }
1278            break;
1279
1280        case RANGED:
1281            int ranges = is.readInt();
1282            for (int i = 0; i < ranges; i++) {
1283                int ord = is.readInt();
1284                int count = is.readInt();
1285                add(RestrictionType.NONE.toRange(getVersification(), v11n.decodeOrdinal(ord), count));
1286            }
1287            break;
1288
1289        default:
1290            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        lowerEventSuppressionAndTest();
1299        lowerNormalizeProtection();
1300    }
1301
1302    /**
1303     * The log stream
1304     */
1305    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}
1384