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