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