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, 2012 - 2016
18   *
19   */
20  package org.crosswire.jsword.versification;
21  
22  import java.io.Serializable;
23  import java.util.Iterator;
24  import java.util.NoSuchElementException;
25  
26  /**
27   * A BibleBookList is an ordered list of one or more BibleBooks.
28   * Typically, a BibleBookList is a member of a Versification.
29   *
30   * @see gnu.lgpl.License The GNU Lesser General Public License for details.
31   * @author DM Smith
32   */
33  /* pkg protected */ class BibleBookList implements Iterable<BibleBook>, Serializable {
34      /**
35       * Create an ordered BibleBookList from the input.
36       * @param books
37       */
38      BibleBookList(BibleBook[] books) {
39          this.books = books.clone();
40          initialize();
41      }
42  
43      /**
44       * Does this Versification contain the BibleBook.
45       *
46       * @param book
47       * @return true if it is present.
48       */
49      public boolean contains(BibleBook book) {
50          return book != null && bookMap[book.ordinal()] != -1;
51      }
52  
53      /**
54       * Where does the BibleBook come in the order of books of the Bible.
55       * The first book is 0, the next is 1 and so forth.
56       * If the BibleBook is not in this Reference System,
57       * then the return value of this routine is -1.
58       *
59       * @param book
60       * @return the ordinal value of the book or -1 if not present
61       */
62      public int getOrdinal(BibleBook book) {
63          return bookMap[book.ordinal()];
64      }
65  
66      /**
67       * Get the number of books in this Versification.
68       * @return the number of books
69       */
70      public int getBookCount() {
71          return books.length;
72      }
73  
74      /**
75       * Get the BibleBook by its position in this Versification.
76       * If the position is negative, return the first book.
77       * If the position is greater than the last, return the last book.
78       *
79       * @param ordinal
80       * @return the indicated book
81       */
82      public BibleBook getBook(int ordinal) {
83          int ord = ordinal;
84          if (ord < 0) {
85              ord = 0;
86          }
87          if (ord >= books.length) {
88              ord = books.length - 1;
89          }
90          return books[ord];
91      }
92  
93      /**
94       * Get the BibleBooks in this Versification.
95       *
96       * @return an Iterator over the books
97       */
98      public Iterator<BibleBook> iterator() {
99          return new Iterator<BibleBook>() {
100 
101             private BibleBook nextBook = books[0];
102 
103             public boolean hasNext() {
104                 return nextBook != null;
105             }
106 
107             public BibleBook next() {
108 
109                 if (nextBook == null) {
110                     throw new NoSuchElementException();
111                 }
112 
113                 BibleBook current = nextBook;
114                 nextBook = getNextBook(nextBook);
115                 return current;
116             }
117 
118             public void remove() {
119                 throw new UnsupportedOperationException();
120             }
121 
122         };
123     }
124 
125     /**
126      * Return the first book in the list.
127      *
128      * @return the first book in the list
129      */
130     public BibleBook getFirstBook() {
131         return books[0];
132     }
133 
134     /**
135      * Return the first book in the list.
136      *
137      * @return the first book in the list
138      */
139     public BibleBook getLastBook() {
140         return books[books.length - 1];
141     }
142 
143     /**
144      * Given a BibleBook, get the previous BibleBook in this Versification. If it is the first book, return null.
145      * @param book A BibleBook in the Versification
146      * @return the previous BibleBook or null.
147      */
148     public BibleBook getPreviousBook(BibleBook book) {
149         int ordinal = book.ordinal();
150         int position = bookMap[ordinal];
151         if (position > 0) {
152             return books[position - 1];
153         }
154 
155         return null;
156     }
157 
158     /**
159      * Given a BibleBook, get the next BibleBook in this Versification. If it is the last book, return null.
160      * @param book A BibleBook in the Versification
161      * @return the previous BibleBook or null.
162      */
163     public BibleBook getNextBook(BibleBook book) {
164         int ordinal = book.ordinal();
165         int position = bookMap[ordinal];
166         if (position != -1 && position + 1 < books.length) {
167             return books[position + 1];
168         }
169         return null;
170     }
171 
172     /**
173      * The bookMap contains one slot for every BibleBook, indexed by it's ordinal value.
174      * The value of that entry is the position of the book in the BookList.
175      * If the BibleBook is not present in books, it's value is -1.
176      */
177     private void initialize() {
178         bookMap = new int[BibleBook.values().length + 1];
179         // Initialize all slots to -1
180         for (BibleBook b : BibleBook.values()) {
181             bookMap[b.ordinal()] = -1;
182         }
183 
184         // Fill in the position of the books into that list
185         for (int i = 0; i < books.length; i++) {
186             bookMap[books[i].ordinal()] = i;
187         }
188     }
189 
190     /** The ordered books in this list */
191     protected BibleBook[] books;
192 
193     /** The bookMap maps from a BibleBook to the position that it has in <code>books</code>. */
194     private int[] bookMap;
195 
196     /**
197      * Serialization ID
198      */
199     private static final long serialVersionUID = -2681289798451902815L;
200 }
201