Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
BibleNames |
|
| 3.142857142857143;3.143 | ||||
BibleNames$NameList |
|
| 3.142857142857143;3.143 |
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.versification; | |
21 | ||
22 | import java.util.HashMap; | |
23 | import java.util.LinkedHashMap; | |
24 | import java.util.Locale; | |
25 | import java.util.Map; | |
26 | import java.util.MissingResourceException; | |
27 | import java.util.ResourceBundle; | |
28 | ||
29 | import org.crosswire.common.util.CWClassLoader; | |
30 | import org.crosswire.common.util.ClassUtil; | |
31 | import org.crosswire.common.util.StringUtil; | |
32 | import org.crosswire.jsword.internationalisation.LocaleProviderManager; | |
33 | ||
34 | /** | |
35 | * BibleNames deals with locale sensitive BibleBook name lookup conversions. | |
36 | * | |
37 | * @see gnu.lgpl.License The GNU Lesser General Public License for details. | |
38 | * @author Joe Walker | |
39 | * @author DM Smith | |
40 | */ | |
41 | public final class BibleNames { | |
42 | /** | |
43 | * Get the singleton instance of BibleNames. | |
44 | * | |
45 | * @return the singleton | |
46 | */ | |
47 | public static BibleNames instance() { | |
48 | 0 | return instance; |
49 | } | |
50 | ||
51 | /** | |
52 | * Get the BookName. | |
53 | * | |
54 | * @param book the desired book | |
55 | * @return The requested BookName or null if not in this versification | |
56 | */ | |
57 | public BookName getBookName(BibleBook book) { | |
58 | 0 | return getLocalizedBibleNames().getBookName(book); |
59 | } | |
60 | ||
61 | /** | |
62 | * Get the preferred name of a book. Altered by the case setting (see | |
63 | * setBookCase() and isFullBookName()) | |
64 | * | |
65 | * @param book the desired book | |
66 | * @return The full name of the book or blank if not in this versification | |
67 | */ | |
68 | public String getPreferredName(BibleBook book) { | |
69 | 0 | return getLocalizedBibleNames().getPreferredName(book); |
70 | } | |
71 | ||
72 | /** | |
73 | * Get the full name of a book (e.g. "Genesis"). Altered by the case setting | |
74 | * (see setBookCase()) | |
75 | * | |
76 | * @param book the book | |
77 | * @return The full name of the book or blank if not in this versification | |
78 | */ | |
79 | public String getLongName(BibleBook book) { | |
80 | 0 | return getLocalizedBibleNames().getLongName(book); |
81 | } | |
82 | ||
83 | /** | |
84 | * Get the short name of a book (e.g. "Gen"). Altered by the case setting | |
85 | * (see setBookCase()) | |
86 | * | |
87 | * @param book the book | |
88 | * @return The short name of the book or blank if not in this versification | |
89 | */ | |
90 | public String getShortName(BibleBook book) { | |
91 | 0 | return getLocalizedBibleNames().getShortName(book); |
92 | } | |
93 | ||
94 | /** | |
95 | * Get a book from its name. | |
96 | * | |
97 | * @param find | |
98 | * The string to identify | |
99 | * @return The BibleBook, On error null | |
100 | */ | |
101 | public BibleBook getBook(String find) { | |
102 | 0 | BibleBook book = null; |
103 | 0 | if (containsLetter(find)) { |
104 | 0 | book = BibleBook.fromOSIS(find); |
105 | ||
106 | 0 | if (book == null) { |
107 | 0 | book = getLocalizedBibleNames().getBook(find, false); |
108 | } | |
109 | ||
110 | 0 | if (book == null) { |
111 | 0 | book = englishBibleNames.getBook(find, false); |
112 | } | |
113 | ||
114 | 0 | if (book == null) { |
115 | 0 | book = getLocalizedBibleNames().getBook(find, true); |
116 | } | |
117 | ||
118 | 0 | if (book == null) { |
119 | 0 | book = englishBibleNames.getBook(find, true); |
120 | } | |
121 | } | |
122 | ||
123 | 0 | return book; |
124 | } | |
125 | ||
126 | /** | |
127 | * Is the given string a valid book name. If this method returns true then | |
128 | * getBook() will return a BibleBook and not null. | |
129 | * | |
130 | * @param find | |
131 | * The string to identify | |
132 | * @return true when the book name is recognized | |
133 | */ | |
134 | public boolean isBook(String find) { | |
135 | 0 | return getBook(find) != null; |
136 | } | |
137 | ||
138 | /** | |
139 | * Load name information for BibleNames for a given locale. | |
140 | * This routine is for testing the underlying NameList. | |
141 | * | |
142 | * @param locale | |
143 | */ | |
144 | void load(Locale locale) { | |
145 | 0 | NameList bibleNames = new NameList(locale); |
146 | 0 | if (localizedBibleNames.get(locale) == null) { |
147 | 0 | localizedBibleNames.put(locale, bibleNames); |
148 | } | |
149 | 0 | } |
150 | ||
151 | /** | |
152 | * This class is a singleton, enforced by a private constructor. | |
153 | */ | |
154 | 0 | private BibleNames() { |
155 | 0 | localizedBibleNames = new HashMap<Locale, NameList>(); |
156 | 0 | englishBibleNames = getBibleNamesForLocale(Locale.ENGLISH); |
157 | 0 | localizedBibleNames.put(Locale.ENGLISH, englishBibleNames); |
158 | 0 | } |
159 | ||
160 | /** | |
161 | * Gets the localized bible names, based on the {@link LocaleProviderManager} | |
162 | * | |
163 | * @return the localized bible names | |
164 | */ | |
165 | private NameList getLocalizedBibleNames() { | |
166 | // Get the current Locale | |
167 | 0 | return getBibleNamesForLocale(LocaleProviderManager.getLocale()); |
168 | } | |
169 | ||
170 | /** | |
171 | * Gets the bible names for a specific locale. | |
172 | * | |
173 | * @param locale the locale | |
174 | * @return the bible names for locale | |
175 | */ | |
176 | private NameList getBibleNamesForLocale(Locale locale) { | |
177 | 0 | NameList bibleNames = localizedBibleNames.get(locale); |
178 | 0 | if (bibleNames == null) { |
179 | 0 | bibleNames = new NameList(locale); |
180 | 0 | localizedBibleNames.put(locale, bibleNames); |
181 | } | |
182 | ||
183 | 0 | return bibleNames; |
184 | } | |
185 | ||
186 | /** | |
187 | * This is simply a convenience function to wrap Character.isLetter() | |
188 | * | |
189 | * @param text | |
190 | * The string to be parsed | |
191 | * @return true if the string contains letters | |
192 | */ | |
193 | private static boolean containsLetter(String text) { | |
194 | 0 | for (int i = 0; i < text.length(); i++) { |
195 | 0 | if (Character.isLetter(text.charAt(i))) { |
196 | 0 | return true; |
197 | } | |
198 | } | |
199 | ||
200 | 0 | return false; |
201 | } | |
202 | ||
203 | /** | |
204 | * NameList is the internal, internationalize list of names | |
205 | * for a locale. | |
206 | * | |
207 | * @see gnu.lgpl.License The GNU Lesser General Public License for details.<br> | |
208 | * The copyright to this program is held by its authors. | |
209 | * @author DM Smith | |
210 | */ | |
211 | 0 | private class NameList { |
212 | /** | |
213 | * Create NameList for the given locale | |
214 | */ | |
215 | 0 | NameList(Locale locale) { |
216 | 0 | this.locale = locale; |
217 | 0 | initialize(); |
218 | 0 | } |
219 | ||
220 | BookName getBookName(BibleBook book) { | |
221 | 0 | return books.get(book); |
222 | } | |
223 | ||
224 | /** | |
225 | * Get the preferred name of a book. Altered by the case setting (see | |
226 | * setBookCase() and isFullBookName()) | |
227 | * | |
228 | * @param book | |
229 | * The book of the Bible | |
230 | * @return The full name of the book | |
231 | */ | |
232 | String getPreferredName(BibleBook book) { | |
233 | 0 | return getBookName(book).getPreferredName(); |
234 | } | |
235 | ||
236 | /** | |
237 | * Get the full name of a book (e.g. "Genesis"). Altered by the case | |
238 | * setting (see setBookCase()) | |
239 | * | |
240 | * @param book | |
241 | * The book of the Bible | |
242 | * @return The full name of the book | |
243 | */ | |
244 | String getLongName(BibleBook book) { | |
245 | 0 | return getBookName(book).getLongName(); |
246 | } | |
247 | ||
248 | /** | |
249 | * Get the short name of a book (e.g. "Gen"). Altered by the case | |
250 | * setting (see setBookCase()) | |
251 | * | |
252 | * @param book | |
253 | * The book of the Bible | |
254 | * @return The short name of the book | |
255 | */ | |
256 | String getShortName(BibleBook book) { | |
257 | 0 | return getBookName(book).getShortName(); |
258 | } | |
259 | ||
260 | /** | |
261 | * Get a book from its name. | |
262 | * | |
263 | * @param find | |
264 | * The string to identify | |
265 | * @param fuzzy | |
266 | * Whether to also find bible books where only a substring matches | |
267 | * @return The BibleBook, On error null | |
268 | */ | |
269 | BibleBook getBook(String find, boolean fuzzy) { | |
270 | 0 | String match = BookName.normalize(find, locale); |
271 | ||
272 | 0 | BookName bookName = fullNT.get(match); |
273 | 0 | if (bookName != null) { |
274 | 0 | return bookName.getBook(); |
275 | } | |
276 | ||
277 | 0 | bookName = shortNT.get(match); |
278 | 0 | if (bookName != null) { |
279 | 0 | return bookName.getBook(); |
280 | } | |
281 | ||
282 | 0 | bookName = altNT.get(match); |
283 | 0 | if (bookName != null) { |
284 | 0 | return bookName.getBook(); |
285 | } | |
286 | ||
287 | 0 | bookName = fullOT.get(match); |
288 | 0 | if (bookName != null) { |
289 | 0 | return bookName.getBook(); |
290 | } | |
291 | ||
292 | 0 | bookName = shortOT.get(match); |
293 | 0 | if (bookName != null) { |
294 | 0 | return bookName.getBook(); |
295 | } | |
296 | ||
297 | 0 | bookName = altOT.get(match); |
298 | 0 | if (bookName != null) { |
299 | 0 | return bookName.getBook(); |
300 | } | |
301 | ||
302 | 0 | bookName = fullNC.get(match); |
303 | 0 | if (bookName != null) { |
304 | 0 | return bookName.getBook(); |
305 | } | |
306 | ||
307 | 0 | bookName = shortNC.get(match); |
308 | 0 | if (bookName != null) { |
309 | 0 | return bookName.getBook(); |
310 | } | |
311 | ||
312 | 0 | bookName = altNC.get(match); |
313 | 0 | if (bookName != null) { |
314 | 0 | return bookName.getBook(); |
315 | } | |
316 | ||
317 | 0 | if (!fuzzy) { |
318 | 0 | return null; |
319 | } | |
320 | ||
321 | 0 | for (BookName aBook : books.values()) { |
322 | 0 | if (aBook.match(match)) { |
323 | 0 | return aBook.getBook(); |
324 | } | |
325 | } | |
326 | ||
327 | 0 | return null; |
328 | } | |
329 | ||
330 | /** | |
331 | * Load up the resources for Bible book and section names, and cache the | |
332 | * upper and lower versions of them. | |
333 | */ | |
334 | private void initialize() { | |
335 | 0 | int ntCount = 0; |
336 | 0 | int otCount = 0; |
337 | 0 | int ncCount = 0; |
338 | 0 | BibleBook[] bibleBooks = BibleBook.values(); |
339 | 0 | for (BibleBook book : bibleBooks) { |
340 | 0 | int ordinal = book.ordinal(); |
341 | 0 | if (ordinal > BibleBook.INTRO_OT.ordinal() && ordinal < BibleBook.INTRO_NT.ordinal()) { |
342 | 0 | ++ntCount; |
343 | 0 | } else if (ordinal > BibleBook.INTRO_NT.ordinal() && ordinal <= BibleBook.REV.ordinal()) { |
344 | 0 | ++otCount; |
345 | } else { | |
346 | 0 | ++ncCount; |
347 | } | |
348 | } | |
349 | ||
350 | // Create the book name maps | |
351 | 0 | books = new LinkedHashMap<BibleBook, BookName>(ntCount + otCount + ncCount); |
352 | ||
353 | 0 | String className = BibleNames.class.getName(); |
354 | 0 | String shortClassName = ClassUtil.getShortClassName(className); |
355 | 0 | ResourceBundle resources = ResourceBundle.getBundle(shortClassName, locale, CWClassLoader.instance(BibleNames.class)); |
356 | ||
357 | 0 | fullNT = new HashMap<String, BookName>(ntCount); |
358 | 0 | shortNT = new HashMap<String, BookName>(ntCount); |
359 | 0 | altNT = new HashMap<String, BookName>(ntCount); |
360 | 0 | for (int i = BibleBook.MATT.ordinal(); i <= BibleBook.REV.ordinal(); ++i) { |
361 | 0 | BibleBook book = bibleBooks[i]; |
362 | 0 | store(resources, book, fullNT, shortNT, altNT); |
363 | } | |
364 | ||
365 | 0 | fullOT = new HashMap<String, BookName>(otCount); |
366 | 0 | shortOT = new HashMap<String, BookName>(otCount); |
367 | 0 | altOT = new HashMap<String, BookName>(otCount); |
368 | 0 | for (int i = BibleBook.GEN.ordinal(); i <= BibleBook.MAL.ordinal(); ++i) { |
369 | 0 | BibleBook book = bibleBooks[i]; |
370 | 0 | store(resources, book, fullOT, shortOT, altOT); |
371 | } | |
372 | ||
373 | 0 | fullNC = new HashMap<String, BookName>(ncCount); |
374 | 0 | shortNC = new HashMap<String, BookName>(ncCount); |
375 | 0 | altNC = new HashMap<String, BookName>(ncCount); |
376 | 0 | store(resources, BibleBook.INTRO_BIBLE, fullNC, shortNC, altNC); |
377 | 0 | store(resources, BibleBook.INTRO_OT, fullNC, shortNC, altNC); |
378 | 0 | store(resources, BibleBook.INTRO_NT, fullNC, shortNC, altNC); |
379 | 0 | for (int i = BibleBook.REV.ordinal() + 1; i < bibleBooks.length; ++i) { |
380 | 0 | BibleBook book = bibleBooks[i]; |
381 | 0 | store(resources, book, fullNC, shortNC, altNC); |
382 | } | |
383 | 0 | } |
384 | ||
385 | private void store(ResourceBundle resources, BibleBook book, Map fullMap, Map shortMap, Map altMap) { | |
386 | 0 | String osisName = book.getOSIS(); |
387 | ||
388 | 0 | String fullBook = getString(resources, osisName + FULL_KEY); |
389 | ||
390 | 0 | String shortBook = getString(resources, osisName + SHORT_KEY); |
391 | 0 | if (shortBook.length() == 0) { |
392 | 0 | shortBook = fullBook; |
393 | } | |
394 | ||
395 | 0 | String altBook = getString(resources, osisName + ALT_KEY); |
396 | ||
397 | 0 | BookName bookName = new BookName(locale, BibleBook.fromOSIS(osisName), fullBook, shortBook, altBook); |
398 | 0 | books.put(book, bookName); |
399 | ||
400 | 0 | fullMap.put(bookName.getNormalizedLongName(), bookName); |
401 | ||
402 | 0 | shortMap.put(bookName.getNormalizedShortName(), bookName); |
403 | ||
404 | 0 | String[] alternates = StringUtil.split(BookName.normalize(altBook, locale), ','); |
405 | ||
406 | 0 | for (int j = 0; j < alternates.length; j++) { |
407 | 0 | altMap.put(alternates[j], bookName); |
408 | } | |
409 | 0 | } |
410 | ||
411 | /* | |
412 | * Helper to make the code more readable. | |
413 | */ | |
414 | private String getString(ResourceBundle resources, String key) { | |
415 | try { | |
416 | 0 | return resources.getString(key); |
417 | 0 | } catch (MissingResourceException e) { |
418 | 0 | assert false; |
419 | } | |
420 | 0 | return null; |
421 | } | |
422 | ||
423 | private static final String FULL_KEY = ".Full"; | |
424 | private static final String SHORT_KEY = ".Short"; | |
425 | private static final String ALT_KEY = ".Alt"; | |
426 | ||
427 | /** The locale for the Bible Names */ | |
428 | private Locale locale; | |
429 | ||
430 | /** | |
431 | * The collection of BookNames by BibleBooks. | |
432 | */ | |
433 | private LinkedHashMap<BibleBook, BookName> books; | |
434 | ||
435 | /** | |
436 | * The full names of the New Testament books of the Bible normalized, | |
437 | * generated at runtime | |
438 | */ | |
439 | private Map<String, BookName> fullNT; | |
440 | ||
441 | /** | |
442 | * The full names of the Old Testament books of the Bible normalized, | |
443 | * generated at runtime | |
444 | */ | |
445 | private Map<String, BookName> fullOT; | |
446 | ||
447 | /** | |
448 | * The full names of the Deuterocanonical books of the Bible normalized, | |
449 | * generated at runtime | |
450 | */ | |
451 | private Map<String, BookName> fullNC; | |
452 | ||
453 | /** | |
454 | * Standard shortened names for the New Testament books of the Bible, | |
455 | * normalized, generated at runtime. | |
456 | */ | |
457 | private Map<String, BookName> shortNT; | |
458 | ||
459 | /** | |
460 | * Standard shortened names for the Old Testament books of the Bible | |
461 | * normalized, generated at runtime. | |
462 | */ | |
463 | private Map<String, BookName> shortOT; | |
464 | ||
465 | /** | |
466 | * Standard shortened names for the Deuterocanonical books of the Bible | |
467 | * normalized, generated at runtime. | |
468 | */ | |
469 | private Map<String, BookName> shortNC; | |
470 | ||
471 | /** | |
472 | * Alternative shortened names for the New Testament books of the Bible | |
473 | * normalized, generated at runtime. | |
474 | */ | |
475 | private Map<String, BookName> altNT; | |
476 | ||
477 | /** | |
478 | * Alternative shortened names for the Old Testament books of the Bible | |
479 | * normalized, generated at runtime. | |
480 | */ | |
481 | private Map<String, BookName> altOT; | |
482 | ||
483 | /** | |
484 | * Alternative shortened names for the Deuterocanonical books of the | |
485 | * Bible normalized, generated at runtime. | |
486 | */ | |
487 | private Map<String, BookName> altNC; | |
488 | } | |
489 | ||
490 | /** we cache the Localized Bible Names because there is quite a bit of processing going on for each individual Locale */ | |
491 | private transient Map<Locale, NameList> localizedBibleNames; | |
492 | ||
493 | /** English BibleNames, or null when using the program's default locale */ | |
494 | private static NameList englishBibleNames; | |
495 | ||
496 | 0 | private static final BibleNames instance = new BibleNames(); |
497 | } |