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