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: 2007
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: BookName.java 2140 2011-04-03 02:07:01Z dmsmith $
21   */
22  package org.crosswire.jsword.versification;
23  
24  import java.util.Locale;
25  import java.util.regex.Pattern;
26  
27  import org.crosswire.common.util.StringUtil;
28  import org.crosswire.jsword.book.CaseType;
29  
30  /**
31   * BookName represents the different ways a book of the bible is named.
32   * 
33   * @see gnu.lgpl.License for license details.<br>
34   *      The copyright to this program is held by it's authors.
35   * @author DM Smith [dmsmith555 at yahoo dot com]
36   */
37  public final class BookName {
38      /**
39       * Create a BookName for a Book of the Bible in a given language.
40       * 
41       * @param locale
42       *            the language of this BookName
43       * @param book
44       *            the Book's canonical number
45       * @param longName
46       *            the Book's long name
47       * @param shortName
48       *            the Book's short name, if any
49       * @param alternateNames
50       *            optional comma separated list of alternates for the Book
51       */
52      public BookName(Locale locale, BibleBook book, String longName, String shortName, String alternateNames) {
53          this.locale = locale;
54          this.book = book;
55          this.longName = longName;
56          this.normalizedLongName = normalize(longName, locale);
57          this.shortName = shortName;
58          this.normalizedShortName = normalize(shortName, locale);
59  
60          if (alternateNames != null) {
61              this.alternateNames = StringUtil.split(normalize(alternateNames, locale), ',');
62          }
63      }
64  
65      /**
66       * Get the BibleBook to which this set of names is tied.
67       * 
68       * @return The book
69       */
70      public BibleBook getBook() {
71          return book;
72      }
73  
74      /**
75       * Get the preferred name of a book. Altered by the case setting (see
76       * setBookCase() and isFullBookName())
77       * 
78       * @return The preferred name of the book
79       */
80      public String getPreferredName() {
81          if (BookName.isFullBookName()) {
82              return getLongName();
83          }
84          return getShortName();
85      }
86  
87      /**
88       * Get the full name of a book (e.g. "Genesis"). Altered by the case setting
89       * (see setBookCase())
90       * 
91       * @return The full name of the book
92       */
93      public String getLongName() {
94          CaseType caseType = BookName.getDefaultCase();
95  
96          if (caseType == CaseType.LOWER) {
97              return longName.toLowerCase(locale);
98          }
99  
100         if (caseType == CaseType.UPPER) {
101             return longName.toUpperCase(locale);
102         }
103 
104         return longName;
105     }
106 
107     /**
108      * Get the short name of a book (e.g. "Gen"). Altered by the case setting
109      * (see setBookCase())
110      * 
111      * @return The short name of the book
112      */
113     public String getShortName() {
114         CaseType caseType = BookName.getDefaultCase();
115 
116         if (caseType == CaseType.LOWER) {
117             return shortName.toLowerCase(locale);
118         }
119 
120         if (caseType == CaseType.UPPER) {
121             return shortName.toUpperCase(locale);
122         }
123 
124         return shortName;
125     }
126 
127     /**
128      * @return the normalizedLongName
129      */
130     public String getNormalizedLongName() {
131         return normalizedLongName;
132     }
133 
134     /**
135      * @return the normalizedShortName
136      */
137     public String getNormalizedShortName() {
138         return normalizedShortName;
139     }
140 
141     /**
142      * Match the normalized name as closely as possible. It will match if:
143      * <ol>
144      * <li>it is a prefix of a normalized alternate name</li>
145      * <li>a normalized alternate name is a prefix of it</li>
146      * <li>it is a prefix of a normalized long name</li>
147      * <li>it is a prefix of a normalized short name</li>
148      * <li>a normalized short name is a prefix of it</li>
149      * 
150      * @param normalizedName
151      *            the already normalized name to match against.
152      * @return true of false
153      */
154     public boolean match(String normalizedName) {
155         // Does it match one of the alternative versions
156         for (int j = 0; j < alternateNames.length; j++) {
157             String targetBookName = alternateNames[j];
158             if (targetBookName.startsWith(normalizedName) || normalizedName.startsWith(targetBookName)) {
159                 return true;
160             }
161         }
162 
163         // Does it match a long version of the book
164         if (normalizedLongName.startsWith(normalizedName)) {
165             return true;
166         }
167 
168         // or a short version
169         if (normalizedShortName.startsWith(normalizedName) || normalizedName.startsWith(normalizedShortName)) {
170             return true;
171         }
172 
173         return false;
174     }
175 
176     /*
177      * (non-Javadoc)
178      * 
179      * @see java.lang.Object#hashCode()
180      */
181     @Override
182     public int hashCode() {
183         return book.hashCode();
184     }
185 
186     /*
187      * (non-Javadoc)
188      * 
189      * @see java.lang.Object#equals(java.lang.Object)
190      */
191     @Override
192     public boolean equals(Object obj) {
193         if (this == obj) {
194             return true;
195         }
196 
197         if (obj == null) {
198             return false;
199         }
200 
201         if (getClass() != obj.getClass()) {
202             return false;
203         }
204 
205         final BookName other = (BookName) obj;
206         return book == other.book;
207     }
208 
209     /*
210      * (non-Javadoc)
211      * 
212      * @see java.lang.Object#toString()
213      */
214     @Override
215     public String toString() {
216         return getPreferredName();
217     }
218 
219     /**
220      * Normalize by stripping punctuation and whitespace and lowercasing.
221      * 
222      * @param str
223      *            the string to normalize
224      * @return the normalized string
225      */
226     public static String normalize(String str, Locale locale) {
227         return normPattern.matcher(str).replaceAll("").toLowerCase(locale);
228     }
229 
230     /**
231      * This is only used by config.
232      * 
233      * @param bookCase
234      *            The new case to use for reporting book names
235      * @exception IllegalArgumentException
236      *                If the case is not between 0 and 2
237      * @see #getCase()
238      */
239     public static void setCase(int bookCase) {
240         BookName.bookCase = CaseType.fromInteger(bookCase);
241     }
242 
243     /**
244      * This is only used by config
245      * 
246      * @return The current case setting
247      * @see #setCase(CaseType)
248      */
249     public static int getCase() {
250         return BookName.bookCase.toInteger();
251     }
252 
253     /**
254      * How do we report the names of the books?. These are static. This is on
255      * the assumption that we will not want to have different sections of the
256      * app using a different format. I expect this to be a good assumption, and
257      * it saves passing a Book class around everywhere. CaseType.MIXED is not
258      * allowed
259      * 
260      * @param newBookCase
261      *            The new case to use for reporting book names
262      * @exception IllegalArgumentException
263      *                If the case is not between 0 and 2
264      * @see #getCase()
265      */
266     public static void setCase(CaseType newBookCase) {
267         BookName.bookCase = newBookCase;
268     }
269 
270     /**
271      * This is only used by config
272      * 
273      * @return Whether the name is long or short. Default is Full (true).
274      * @see #setFullBookName(boolean)
275      */
276     public static boolean isFullBookName() {
277         return BookName.fullBookName;
278     }
279 
280     /**
281      * Set whether the name should be full or abbreviated, long or short.
282      * 
283      * @param fullName
284      *            The new case to use for reporting book names
285      * @see #isFullBookName()
286      */
287     public static void setFullBookName(boolean fullName) {
288         BookName.fullBookName = fullName;
289     }
290 
291     /**
292      * How do we report the names of the books?.
293      * 
294      * @return The current case setting
295      * @see #setCase(int)
296      */
297     public static CaseType getDefaultCase() {
298         return BookName.bookCase;
299     }
300 
301     /** remove spaces and some punctuation in Book Name (make sure , is allowed) */
302     private static Pattern normPattern = Pattern.compile("[. ]");
303 
304     private BibleBook book;
305     private String longName;
306     private String normalizedLongName;
307     private String shortName;
308     private String normalizedShortName;
309     private String[] alternateNames;
310 
311     /** The locale for the Book Name */
312     private Locale locale;
313 
314     /** How the book names are reported. */
315     private static CaseType bookCase = CaseType.SENTENCE;
316 
317     /** Whether long or short, full or abbreviated names are used. */
318     private static boolean fullBookName = true;
319 
320 }
321