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