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