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