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: Passage.java 2226 2012-02-02 19:25:21Z dmsmith $
21   */
22  package org.crosswire.jsword.passage;
23  
24  import java.io.IOException;
25  import java.io.Reader;
26  import java.io.Writer;
27  import java.util.Iterator;
28  
29  import org.crosswire.jsword.versification.Versification;
30  
31  /**
32   * A Passage is a specialized Collection of Verses. The additions are:
33   * <ul>
34   * <li>List blurring
35   * <li>Range Counting and iteration (in addition to Verse counting etc)
36   * <li>List change notification, so you can register to update yourself, and
37   * this goes hand in hand with a added thread-safe contract.
38   * <li>getName() to be more VerseBase like.
39   * <li>Human readable serialization. So we can read and write to and from OLB
40   * style Passage files.
41   * </ul>
42   * 
43   * <p>
44   * Passage no longer extends the Collection interface to avoid J2SE 1.1/1.2
45   * portability problems, and because many of the things that a Passage does rely
46   * on consecutive Verses which are an alien concept to Collections. So users
47   * would have to use the Passage interface anyway.
48   * 
49   * <p>
50   * Other arguments for and against.
51   * <ul>
52   * <li>The generic version will postpone some type errors to runtime. Is this a
53   * huge problem? Are there many syntax errors that would be lost? Probably not.
54   * <li>The specific version would stop enhancements like add("Gen 1:1"); (But
55   * this is just syntactical sugar anyway).
56   * <li>The specific version allows functionality by is-a as well as has-a. But a
57   * Passage is fundamentally different so this is not that much use.
58   * <li>At the end of the day I expect people to use getName() instead of
59   * toString() and blur(), both of which are Passage things not Collection
60   * things. So the general use of these classes is via a Passage interface not a
61   * Collections one.
62   * <li>Note that the implementations of Passage could not adhere strictly to the
63   * Collections interface in returning false from add(), remove() etc, to specify
64   * if the Collection was changed. Given ranges and the like this can get very
65   * time consuming and complex.
66   * </ul>
67   * 
68   * <p>
69   * The upshot of all this is that I am removing the Collections interface from
70   * Passage.
71   * 
72   * <p>
73   * I considered giving Passages names to allow for a CLI that could use named
74   * RangedPassages, however that is perhaps better left to another class.
75   * 
76   * @see gnu.lgpl.License for license details.<br>
77   *      The copyright to this program is held by it's authors.
78   * @author Joe Walker [joe at eireneh dot com]
79   */
80  public interface Passage extends Key {
81      /**
82       * Get the Versification that defines the Verses in the passage.
83       * 
84       * @return this Passage's Versification.
85       */
86      Versification getVersification();
87  
88      /**
89       * A summary of the verses in this Passage For example
90       * "10 verses in 4 books"
91       * 
92       * @return a String containing an overview of the verses
93       */
94      String getOverview();
95  
96      /**
97       * Returns the number of verses in this collection. Like Collection.size()
98       * This does not mean the Passage needs to use Verses, just that it
99       * understands the concept.
100      * 
101      * @return the number of Verses in this collection
102      * @see Verse
103      */
104     int countVerses();
105 
106     /**
107      * Determine whether there are two or more ranges.
108      * 
109      * @param restrict
110      *            Do we break ranges at chapter/book boundaries
111      * @return whether there are two or more ranges
112      * @see VerseRange
113      */
114     boolean hasRanges(RestrictionType restrict);
115 
116     /**
117      * Like countVerses() that counts VerseRanges instead of Verses Returns the
118      * number of fragments in this collection. This does not mean the Passage
119      * needs to use VerseRanges, just that it understands the concept.
120      * 
121      * @param restrict
122      *            Do we break ranges at chapter/book boundaries
123      * @return the number of VerseRanges in this collection
124      * @see VerseRange
125      */
126     int countRanges(RestrictionType restrict);
127 
128     /**
129      * Ensures that there are a maximum of <code>count</code> Verses in this
130      * Passage. If there were more than <code>count</code> Verses then a new
131      * Passage is created containing the Verses from <code>count</code>+1
132      * onwards. If there was not greater than <code>count</code> in the Passage,
133      * then the passage remains unchanged, and null is returned.
134      * 
135      * @param count
136      *            The maximum number of Verses to allow in this collection
137      * @return A new Passage containing the remaining verses or null
138      * @see Verse
139      */
140     Passage trimVerses(int count);
141 
142     /**
143      * Ensures that there are a maximum of <code>count</code> VerseRanges in
144      * this Passage. If there were more than <code>count</code> VerseRanges then
145      * a new Passage is created containing the VerseRanges from
146      * <code>count</code>+1 onwards. If there was not greater than
147      * <code>count</code> in the Passage, then the passage remains unchanged,
148      * and null is returned.
149      * 
150      * @param count
151      *            The maximum number of VerseRanges to allow in this collection
152      * @param restrict
153      *            Do we break ranges at chapter/book boundaries
154      * @return A new Passage containing the remaining verses or null
155      * @see VerseRange
156      */
157     Passage trimRanges(int count, RestrictionType restrict);
158 
159     /**
160      * How many books are there in this Passage
161      * 
162      * @return The number of distinct books
163      */
164     int booksInPassage();
165 
166     /**
167      * Get a specific Verse from this collection
168      * 
169      * @param offset
170      *            The verse offset (legal values are 0 to countVerses()-1)
171      * @return The Verse
172      * @throws ArrayIndexOutOfBoundsException
173      *             If the offset is out of range
174      */
175     Verse getVerseAt(int offset) throws ArrayIndexOutOfBoundsException;
176 
177     /**
178      * Get a specific VerseRange from this collection
179      * 
180      * @param offset
181      *            The verse range offset (legal values are 0 to countRanges()-1)
182      * @param restrict
183      *            Do we break ranges at chapter/book boundaries
184      * @return The Verse Range
185      * @throws ArrayIndexOutOfBoundsException
186      *             If the offset is out of range
187      */
188     VerseRange getRangeAt(int offset, RestrictionType restrict) throws ArrayIndexOutOfBoundsException;
189 
190     /**
191      * Like verseElements() that iterates over VerseRanges instead of Verses.
192      * Exactly the same data will be traversed, however using rangeIterator()
193      * will usually give less iterations (and never more)
194      * 
195      * @param restrict
196      *            Do we break ranges over chapters
197      * @return A list enumerator
198      */
199     Iterator<Key> rangeIterator(RestrictionType restrict);
200 
201     /**
202      * Returns true if this collection contains all the specified Verse
203      * 
204      * @param that
205      *            Verse or VerseRange that may exist in this Passage
206      * @return true if this collection contains that
207      */
208     boolean contains(Key that);
209 
210     /**
211      * Add this Verse/VerseRange to this Passage
212      * 
213      * @param that
214      *            The Verses to be removed from this Passage
215      */
216     void add(Key that);
217 
218     /**
219      * Remove this Verse/VerseRange from this Passage
220      * 
221      * @param that
222      *            The Verses to be removed from this Passage
223      */
224     void remove(Key that);
225 
226     /**
227      * Returns true if this Passage contains all of the verses in that Passage
228      * 
229      * @param that
230      *            Passage to be checked for containment in this collection.
231      * @return true if this reference contains all of the Verses in that Passage
232      */
233     boolean containsAll(Passage that);
234 
235     /**
236      * To be compatible with humans we read/write ourselves to a file that a
237      * human can read and even edit. OLB verse.lst integration is a good goal
238      * here.
239      * 
240      * @param in
241      *            The stream to read from
242      * @exception java.io.IOException
243      *                If the file/network etc breaks
244      * @exception NoSuchVerseException
245      *                If the file was invalid
246      */
247     void readDescription(Reader in) throws IOException, NoSuchVerseException;
248 
249     /**
250      * To be compatible with humans we read/write ourselves to a file that a
251      * human can read and even edit. OLB verse.lst integration is a good goal
252      * here.
253      * 
254      * @param out
255      *            The stream to write to
256      * @exception java.io.IOException
257      *                If the file/network etc breaks
258      */
259     void writeDescription(Writer out) throws IOException;
260 
261     /**
262      * For performance reasons we may well want to hint to the Passage that we
263      * have done editing it for now and that it is safe to cache certain values
264      * to speed up future reads. Any action taken by this method will be undone
265      * simply by making a future edit, and the only loss in calling
266      * optimizeReads() is a loss of time if you then persist in writing to the
267      * Passage.
268      */
269     void optimizeReads();
270 
271     /**
272      * Event Listeners - Add Listener
273      * 
274      * @param li
275      *            The listener to add
276      */
277     void addPassageListener(PassageListener li);
278 
279     /**
280      * Event Listeners - Remove Listener
281      * 
282      * @param li
283      *            The listener to remove
284      */
285     void removePassageListener(PassageListener li);
286 }
287