| BookFilters.java |
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: 2005
18 * The copyright to this program is held by it's authors.
19 *
20 * ID: $Id: BookFilters.java 1466 2007-07-02 02:48:09Z dmsmith $
21 */
22 package org.crosswire.jsword.book;
23
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import org.crosswire.common.util.Logger;
30
31 /**
32 * Some common implementations of BookFilter.
33 *
34 * @see gnu.lgpl.License for license details.
35 * The copyright to this program is held by it's authors.
36 * @author Joe Walker [joe at eireneh dot com]
37 */
38 public final class BookFilters
39 {
40 /**
41 * Ensure we cant be created
42 */
43 private BookFilters()
44 {
45 }
46
47 /**
48 * A simple default filter that returns everything
49 */
50 public static BookFilter getAll()
51 {
52 return new AllBookFilter();
53 }
54
55 /**
56 * A filter that accepts everything that implements Bible
57 * or Commentary, when commentaries are listed with Bibles.
58 */
59 public static BookFilter getBibles()
60 {
61 if (commentariesWithBibles)
62 {
63 return either(new BookCategoryFilter(BookCategory.BIBLE), new BookCategoryFilter(BookCategory.COMMENTARY));
64 }
65 return new BookCategoryFilter(BookCategory.BIBLE);
66 }
67
68 /**
69 * A filter that accepts everything that implements Bible.
70 */
71 public static BookFilter getOnlyBibles()
72 {
73 return new BookCategoryFilter(BookCategory.BIBLE);
74 }
75
76 /**
77 * A filter that accepts everything that's not a Bible
78 * or a Commentary, when commentaries are listed with Bibles.
79 */
80 public static BookFilter getNonBibles()
81 {
82 if (commentariesWithBibles)
83 {
84 return both(new NotBookCategoryFilter(BookCategory.BIBLE), new NotBookCategoryFilter(BookCategory.COMMENTARY));
85 }
86 return new NotBookCategoryFilter(BookCategory.BIBLE);
87 }
88
89 /**
90 * A filter that accepts everything that implements Dictionary
91 */
92 public static BookFilter getDictionaries()
93 {
94 return new BookCategoryFilter(BookCategory.DICTIONARY);
95 }
96
97 /**
98 * A filter that accepts everything that implements Dictionary
99 */
100 public static BookFilter getGlossaries()
101 {
102 return new BookCategoryFilter(BookCategory.GLOSSARY);
103 }
104
105 /**
106 * A filter that accepts everything that implements DailyDevotionals
107 */
108 public static BookFilter getDailyDevotionals()
109 {
110 return new BookCategoryFilter(BookCategory.DAILY_DEVOTIONS);
111 }
112
113 /**
114 * A filter that accepts everything that implements Commentary
115 */
116 public static BookFilter getCommentaries()
117 {
118 return new BookCategoryFilter(BookCategory.COMMENTARY);
119 }
120
121 /**
122 * A filter that accepts everything that implements GeneralBook
123 */
124 public static BookFilter getGeneralBooks()
125 {
126 return new BookCategoryFilter(BookCategory.GENERAL_BOOK);
127 }
128
129 /**
130 * A filter that accepts everything that is a
131 * Greek Definition Dictionary
132 */
133 public static BookFilter getGreekDefinitions()
134 {
135 return new BookFeatureFilter(FeatureType.GREEK_DEFINITIONS);
136 }
137
138 /**
139 * A filter that accepts everything that is a
140 * Greek Parse/Morphology Dictionary
141 */
142 public static BookFilter getGreekParse()
143 {
144 return new BookFeatureFilter(FeatureType.GREEK_PARSE);
145 }
146
147 /**
148 * A filter that accepts everything that is a
149 * Hebrew Definition Dictionary
150 */
151 public static BookFilter getHebrewDefinitions()
152 {
153 return new BookFeatureFilter(FeatureType.HEBREW_DEFINITIONS);
154 }
155
156 /**
157 * A filter that accepts everything that is a
158 * Hebrew Parse/Morphology Dictionary
159 */
160 public static BookFilter getHebrewParse()
161 {
162 return new BookFeatureFilter(FeatureType.HEBREW_PARSE);
163 }
164
165 /**
166 * Determine whether the getBible should return the current Bible
167 * or the user's chosen default.
168 * @return true if the bible tracks the user's selection
169 */
170 public static boolean isCommentariesWithBibles()
171 {
172 return commentariesWithBibles;
173 }
174
175 /**
176 * Establish whether the getBible should return the current Bible
177 * or the user's chosen default.
178 * @param current
179 */
180 public static void setCommentariesWithBibles(boolean current)
181 {
182 commentariesWithBibles = current;
183 }
184
185 /**
186 * Whether biblesBookFilter includes commentaries. Initally false.
187 */
188 private static boolean commentariesWithBibles;
189
190
191 /**
192 * Filter for all books
193 */
194 static class AllBookFilter implements BookFilter
195 {
196 /* (non-Javadoc)
197 * @see org.crosswire.jsword.book.BookFilter#test(org.crosswire.jsword.book.Book)
198 */
199 public boolean test(Book book)
200 {
201 return true;
202 }
203 }
204
205 /**
206 * Filter for books by category
207 */
208 static class BookCategoryFilter implements BookFilter
209 {
210 BookCategoryFilter(BookCategory category)
211 {
212 this.category = category;
213 }
214
215 /* (non-Javadoc)
216 * @see org.crosswire.jsword.book.BookFilter#test(org.crosswire.jsword.book.Book)
217 */
218 public boolean test(Book book)
219 {
220 return book.getBookCategory().equals(category) && !book.isLocked();
221 }
222
223 private BookCategory category;
224 }
225
226 /**
227 * Filter for books by category
228 */
229 static class NotBookCategoryFilter implements BookFilter
230 {
231 NotBookCategoryFilter(BookCategory category)
232 {
233 this.category = category;
234 }
235
236 /* (non-Javadoc)
237 * @see org.crosswire.jsword.book.BookFilter#test(org.crosswire.jsword.book.Book)
238 */
239 public boolean test(Book book)
240 {
241 return !book.getBookCategory().equals(category) && !book.isLocked();
242 }
243
244 private BookCategory category;
245 }
246
247 /**
248 * Filter for books by feature
249 */
250 public static class BookFeatureFilter implements BookFilter
251 {
252 public BookFeatureFilter(FeatureType feature)
253 {
254 this.feature = feature;
255 }
256
257 /* (non-Javadoc)
258 * @see org.crosswire.jsword.book.BookFilter#test(org.crosswire.jsword.book.Book)
259 */
260 public boolean test(Book book)
261 {
262 return book.hasFeature(feature) && !book.isLocked();
263 }
264
265 private FeatureType feature;
266 }
267
268 /**
269 * A filter that accepts Books that match two criteria.
270 */
271 public static BookFilter both(final BookFilter b1, final BookFilter b2)
272 {
273 return new BookFilter()
274 {
275 public boolean test(Book book)
276 {
277 return b1.test(book) && b2.test(book);
278 }
279 };
280 }
281
282 /**
283 * A filter that accepts Books that match either of two criteria.
284 */
285 public static BookFilter either(final BookFilter b1, final BookFilter b2)
286 {
287 return new BookFilter()
288 {
289 public boolean test(Book book)
290 {
291 return b1.test(book) || b2.test(book);
292 }
293 };
294 }
295
296 /**
297 * A filter that accepts Books that match by book driver.
298 */
299 public static BookFilter getBooksByDriver(final BookDriver driver)
300 {
301 return new BookFilter()
302 {
303 public boolean test(Book book)
304 {
305 return book.getDriver() == driver;
306 }
307 };
308 }
309
310 /**
311 * A simple default filter that returns everything.
312 * The match parameter is a set of name value pairs like this:
313 * <br/>
314 * <code>initials=ESV;type=Bible;driverName=Sword</code><br/>
315 * Before the = there must be the name of a property on Book and after
316 * the value to match (.toString()) is called on the results of the getter.
317 * @param match a ; separated list of properties (of Book) to match
318 * @see Book
319 */
320 public static BookFilter getCustom(String match)
321 {
322 return new CustomBookFilter(match);
323 }
324
325 /**
326 * Custom Filter
327 */
328 static class CustomBookFilter implements BookFilter
329 {
330 /**
331 * Ctor
332 * @param match The match spec.
333 * @see BookFilters#getCustom(String)
334 */
335 public CustomBookFilter(String match)
336 {
337 List cache = new ArrayList();
338 String[] filters = match.split(";"); //$NON-NLS-1$
339 for (int i = 0; i < filters.length; i++)
340 {
341 String[] parts = filters[i].split("="); //$NON-NLS-1$
342 if (parts.length != 2 || parts[0].length() == 0 || parts[1].length() == 0)
343 {
344 throw new IllegalArgumentException("Filter format is 'property=value', given: " + filters[i]); //$NON-NLS-1$
345 }
346
347 Test test = new Test();
348
349 String gettername = "get" + Character.toTitleCase(parts[0].charAt(0)) + parts[0].substring(1); //$NON-NLS-1$
350 try
351 {
352 test.property = Book.class.getMethod(gettername, (Class[]) null);
353 test.result = parts[1];
354 }
355 catch (NoSuchMethodException ex)
356 {
357 throw new IllegalArgumentException("Missing property: " + parts[0] + " in Book"); //$NON-NLS-1$ //$NON-NLS-2$
358 }
359
360 cache.add(test);
361 }
362
363 tests = (Test[]) cache.toArray(new Test[cache.size()]);
364 }
365
366 /* (non-Javadoc)
367 * @see org.crosswire.jsword.book.BookFilter#test(org.crosswire.jsword.book.Book)
368 */
369 public boolean test(Book book)
370 {
371 for (int i = 0; i < tests.length; i++)
372 {
373 Test test = tests[i];
374 try
375 {
376 Object result = test.property.invoke(book, (Object[]) null);
377 if (!test.result.equals(result.toString()))
378 {
379 return false;
380 }
381 }
382 catch (IllegalArgumentException e)
383 {
384 log.warn("Error while testing property " + test.property.getName() + " on " + book.getName(), e); //$NON-NLS-1$ //$NON-NLS-2$
385 return false;
386 }
387 catch (IllegalAccessException e)
388 {
389 log.warn("Error while testing property " + test.property.getName() + " on " + book.getName(), e); //$NON-NLS-1$ //$NON-NLS-2$
390 return false;
391 }
392 catch (InvocationTargetException e)
393 {
394 log.warn("Error while testing property " + test.property.getName() + " on " + book.getName(), e); //$NON-NLS-1$ //$NON-NLS-2$
395 return false;
396 }
397 }
398
399 return true;
400 }
401
402 private Test[] tests;
403
404 /**
405 *
406 */
407 static class Test
408 {
409 protected String result;
410 protected Method property;
411 }
412 }
413
414 /**
415 * The log stream
416 */
417 static final Logger log = Logger.getLogger(BookFilters.class);
418 }
419