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: AbstractBook.java 2099 2011-03-07 17:13:00Z dmsmith $
21   */
22  package org.crosswire.jsword.book.basic;
23  
24  import java.util.Map;
25  
26  import org.crosswire.common.activate.Lock;
27  import org.crosswire.common.util.EventListenerList;
28  import org.crosswire.common.util.Language;
29  import org.crosswire.jsword.JSOtherMsg;
30  import org.crosswire.jsword.book.Book;
31  import org.crosswire.jsword.book.BookCategory;
32  import org.crosswire.jsword.book.BookDriver;
33  import org.crosswire.jsword.book.BookException;
34  import org.crosswire.jsword.book.BookMetaData;
35  import org.crosswire.jsword.book.FeatureType;
36  import org.crosswire.jsword.index.IndexStatus;
37  import org.crosswire.jsword.index.IndexStatusEvent;
38  import org.crosswire.jsword.index.IndexStatusListener;
39  import org.crosswire.jsword.index.search.DefaultSearchRequest;
40  import org.crosswire.jsword.index.search.SearchRequest;
41  import org.crosswire.jsword.index.search.Searcher;
42  import org.crosswire.jsword.index.search.SearcherFactory;
43  import org.crosswire.jsword.passage.Key;
44  import org.jdom.Document;
45  
46  /**
47   * AbstractBook implements a few of the more generic methods of Book. This class
48   * does a lot of work in helping make search easier, and implementing some basic
49   * write methods.
50   * 
51   * @see gnu.lgpl.License for license details.<br>
52   *      The copyright to this program is held by it's authors.
53   * @author Joe Walker [joe at eireneh dot com]
54   */
55  public abstract class AbstractBook implements Book {
56      public AbstractBook(BookMetaData bmd) {
57          setBookMetaData(bmd);
58      }
59  
60      /*
61       * (non-Javadoc)
62       * 
63       * @see org.crosswire.jsword.book.Book#getBookMetaData()
64       */
65      public final BookMetaData getBookMetaData() {
66          return bmd;
67      }
68  
69      /*
70       * (non-Javadoc)
71       * 
72       * @see
73       * org.crosswire.jsword.book.Book#setBookMetaData(org.crosswire.jsword.book
74       * .BookMetaData)
75       */
76      public final void setBookMetaData(BookMetaData bmd) {
77          this.bmd = bmd;
78      }
79  
80      /*
81       * (non-Javadoc)
82       * 
83       * @see
84       * org.crosswire.common.activate.Activatable#activate(org.crosswire.common
85       * .activate.Lock)
86       */
87      public void activate(Lock lock) {
88      }
89  
90      /*
91       * (non-Javadoc)
92       * 
93       * @see
94       * org.crosswire.common.activate.Activatable#deactivate(org.crosswire.common
95       * .activate.Lock)
96       */
97      public void deactivate(Lock lock) {
98      }
99  
100     /*
101      * (non-Javadoc)
102      * 
103      * @see org.crosswire.jsword.book.Book#find(java.lang.String)
104      */
105     public Key find(String request) throws BookException {
106         return find(new DefaultSearchRequest(request));
107     }
108 
109     /*
110      * (non-Javadoc)
111      * 
112      * @see
113      * org.crosswire.jsword.book.Book#find(org.crosswire.jsword.index.search
114      * .SearchRequest)
115      */
116     public Key find(SearchRequest request) throws BookException {
117         if (searcher == null) {
118             try {
119                 searcher = SearcherFactory.createSearcher(this);
120             } catch (InstantiationException ex) {
121                 throw new BookException(JSOtherMsg.lookupText("Failed to initialize the search index"), ex);
122             }
123         }
124 
125         return searcher.search(request);
126     }
127 
128     /*
129      * (non-Javadoc)
130      * 
131      * @see org.crosswire.jsword.book.BookMetaData#getBook()
132      */
133     public Book getBook() {
134         return this;
135     }
136 
137     /*
138      * (non-Javadoc)
139      * 
140      * @see org.crosswire.jsword.book.BookMetaData#getDriver()
141      */
142     public BookDriver getDriver() {
143         return bmd == null ? null : bmd.getDriver();
144     }
145 
146     /*
147      * (non-Javadoc)
148      * 
149      * @see org.crosswire.jsword.book.BookMetaData#getDriverName()
150      */
151     public String getDriverName() {
152         return bmd == null ? null : bmd.getDriverName();
153     }
154 
155     /*
156      * (non-Javadoc)
157      * 
158      * @see org.crosswire.jsword.book.Book#match(java.lang.String)
159      */
160     public boolean match(String name) {
161         if (name == null) {
162             return false;
163         }
164         if (name.equals(getInitials())) {
165             return true;
166         }
167         if (name.equals(getName())) {
168             return true;
169         }
170         if (name.equalsIgnoreCase(getInitials())) {
171             return true;
172         }
173         if (name.equalsIgnoreCase(getName())) {
174             return true;
175         }
176         if (name.startsWith(getInitials())) {
177             return true;
178         }
179         if (name.startsWith(getName())) {
180             return true;
181         }
182         return false;
183     }
184 
185     /*
186      * (non-Javadoc)
187      * 
188      * @see org.crosswire.jsword.book.BookMetaData#getIndexStatus()
189      */
190     public IndexStatus getIndexStatus() {
191         return bmd.getIndexStatus();
192     }
193 
194     /*
195      * (non-Javadoc)
196      * 
197      * @see
198      * org.crosswire.jsword.book.BookMetaData#setIndexStatus(org.crosswire.jsword
199      * .book.IndexStatus)
200      */
201     public void setIndexStatus(IndexStatus newStatus) {
202         IndexStatus oldStatus = bmd.getIndexStatus();
203         bmd.setIndexStatus(newStatus);
204         firePropertyChange(oldStatus, newStatus);
205     }
206 
207     /*
208      * (non-Javadoc)
209      * 
210      * @see org.crosswire.jsword.book.BookMetaData#getInitials()
211      */
212     public String getInitials() {
213         return bmd.getInitials();
214     }
215 
216     /*
217      * (non-Javadoc)
218      * 
219      * @see org.crosswire.jsword.book.BookMetaData#getLanguage()
220      */
221     public Language getLanguage() {
222         return bmd.getLanguage();
223     }
224 
225     /*
226      * (non-Javadoc)
227      * 
228      * @see org.crosswire.jsword.book.BookMetaData#getName()
229      */
230     public String getName() {
231         return bmd.getName();
232     }
233 
234     /*
235      * (non-Javadoc)
236      * 
237      * @see org.crosswire.jsword.book.BookMetaData#getOsisID()
238      */
239     public String getOsisID() {
240         return bmd.getOsisID();
241     }
242 
243     /*
244      * (non-Javadoc)
245      * 
246      * @see org.crosswire.jsword.book.BookMetaData#getProperties()
247      */
248     public Map<String, Object> getProperties() {
249         return bmd.getProperties();
250     }
251 
252     /*
253      * (non-Javadoc)
254      * 
255      * @see org.crosswire.jsword.book.Book#getProperty(java.lang.String)
256      */
257     public Object getProperty(String key) {
258         return bmd.getProperty(key);
259     }
260 
261     /*
262      * (non-Javadoc)
263      * 
264      * @see org.crosswire.jsword.book.Book#putProperty(java.lang.String,
265      * java.lang.String)
266      */
267     public void putProperty(String key, Object value) {
268         bmd.putProperty(key, value);
269     }
270 
271     /*
272      * (non-Javadoc)
273      * 
274      * @see org.crosswire.jsword.book.BookMetaData#getType()
275      */
276     public BookCategory getBookCategory() {
277         return bmd.getBookCategory();
278     }
279 
280     /*
281      * (non-Javadoc)
282      * 
283      * @see org.crosswire.jsword.book.BookMetaData#isLeftToRight()
284      */
285     public boolean isLeftToRight() {
286         return bmd.isLeftToRight();
287     }
288 
289     /*
290      * (non-Javadoc)
291      * 
292      * @see org.crosswire.jsword.book.BookMetaData#isSupported()
293      */
294     public boolean isSupported() {
295         return bmd.isSupported();
296     }
297 
298     /*
299      * (non-Javadoc)
300      * 
301      * @see org.crosswire.jsword.book.BookMetaData#isEnciphered()
302      */
303     public boolean isEnciphered() {
304         return bmd.isEnciphered();
305     }
306 
307     /*
308      * (non-Javadoc)
309      * 
310      * @see org.crosswire.jsword.book.BookMetaData#isLocked()
311      */
312     public boolean isLocked() {
313         return bmd.isLocked();
314     }
315 
316     /*
317      * (non-Javadoc)
318      * 
319      * @see org.crosswire.jsword.book.BookMetaData#unlock(String)
320      */
321     public boolean unlock(String unlockKey) {
322         boolean state = bmd.unlock(unlockKey);
323         if (state) {
324             // Double check.
325             return isUnlockKeyValid();
326         }
327         return state;
328     }
329 
330     /**
331      * This is a heuristic that tries out the key.
332      * 
333      * @return true if there were no exceptions in reading the enciphered
334      *         module.
335      */
336     private boolean isUnlockKeyValid() {
337         try {
338             Key key = getGlobalKeyList();
339             if (key == null) {
340                 // weird key == null, assume it is valid
341                 return true;
342             }
343 
344             if (key.getCardinality() > 0) {
345                 key = key.get(0);
346             }
347 
348             getRawText(key);
349 
350             return true;
351         } catch (Exception ex) {
352             return false;
353         }
354     }
355 
356     /*
357      * (non-Javadoc)
358      * 
359      * @see org.crosswire.jsword.book.BookMetaData#getUnlockKey()
360      */
361     public String getUnlockKey() {
362         return bmd.getUnlockKey();
363     }
364 
365     /*
366      * (non-Javadoc)
367      * 
368      * @see org.crosswire.jsword.book.BookMetaData#isQuestionable()
369      */
370     public boolean isQuestionable() {
371         return bmd.isQuestionable();
372     }
373 
374     /*
375      * (non-Javadoc)
376      * 
377      * @see
378      * org.crosswire.jsword.book.BookMetaData#hasFeature(org.crosswire.jsword
379      * .book.FeatureType)
380      */
381     public boolean hasFeature(FeatureType feature) {
382         return bmd.hasFeature(feature);
383     }
384 
385     /*
386      * (non-Javadoc)
387      * 
388      * @see
389      * org.crosswire.jsword.book.Book#addIndexStatusListener(org.crosswire.jsword
390      * .index.IndexStatusListener)
391      */
392     public void addIndexStatusListener(IndexStatusListener listener) {
393         if (listeners == null) {
394             listeners = new EventListenerList();
395         }
396         listeners.add(IndexStatusListener.class, listener);
397     }
398 
399     /*
400      * (non-Javadoc)
401      * 
402      * @see
403      * org.crosswire.jsword.book.Book#removeIndexStatusListener(org.crosswire
404      * .jsword.index.IndexStatusListener)
405      */
406     public void removeIndexStatusListener(IndexStatusListener listener) {
407         if (listeners == null) {
408             return;
409         }
410 
411         listeners.remove(IndexStatusListener.class, listener);
412     }
413 
414     /**
415      * Reports bound property changes. If <code>oldValue</code> and
416      * <code>newValue</code> are not equal and the
417      * <code>PropertyChangeEvent</code> listener list isn't empty, then fire a
418      * <code>PropertyChange</code> event to each listener.
419      * 
420      * @param oldStatus
421      *            the old value of the property (as an Object)
422      * @param newStatus
423      *            the new value of the property (as an Object)
424      */
425     protected void firePropertyChange(IndexStatus oldStatus, IndexStatus newStatus) {
426         if (listeners != null) {
427             if (oldStatus != null && newStatus != null && oldStatus.equals(newStatus)) {
428                 return;
429             }
430 
431             Object[] listenerList = listeners.getListenerList();
432             for (int i = 0; i <= listenerList.length - 2; i += 2) {
433                 if (listenerList[i] == IndexStatusListener.class) {
434                     IndexStatusEvent ev = new IndexStatusEvent(this, newStatus);
435                     IndexStatusListener li = (IndexStatusListener) listenerList[i + 1];
436                     li.statusChanged(ev);
437                 }
438             }
439         }
440     }
441 
442     /*
443      * (non-Javadoc)
444      * 
445      * @see org.crosswire.jsword.book.BookMetaData#toOSIS()
446      */
447     public Document toOSIS() {
448         return bmd.toOSIS();
449     }
450 
451     /*
452      * (non-Javadoc)
453      * 
454      * @see java.lang.Object#equals(java.lang.Object)
455      */
456     @Override
457     public boolean equals(Object obj) {
458         // Since this can not be null
459         if (obj == null) {
460             return false;
461         }
462 
463         // We might consider checking for equality against all Books?
464         // However currently we don't.
465 
466         // Check that that is the same as this
467         // Don't use instanceof since that breaks inheritance
468         if (!obj.getClass().equals(this.getClass())) {
469             return false;
470         }
471 
472         // The real bit ...
473         Book that = (Book) obj;
474 
475         return bmd.equals(that.getBookMetaData());
476     }
477 
478     /*
479      * (non-Javadoc)
480      * 
481      * @see java.lang.Object#hashCode()
482      */
483     @Override
484     public int hashCode() {
485         return bmd.hashCode();
486     }
487 
488     /*
489      * (non-Javadoc)
490      * 
491      * @see java.lang.Comparable#compareTo(java.lang.Object)
492      */
493     public int compareTo(Book obj) {
494         return this.bmd.compareTo(obj.getBookMetaData());
495     }
496 
497     /*
498      * (non-Javadoc)
499      * 
500      * @see java.lang.Object#toString()
501      */
502     @Override
503     public String toString() {
504         return bmd.toString();
505     }
506 
507     /**
508      * How do we perform searches
509      */
510     private Searcher searcher;
511 
512     /**
513      * The meta data for this book
514      */
515     private BookMetaData bmd;
516 
517     /**
518      * The list of property change listeners
519      */
520     private EventListenerList listeners;
521 }
522