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: SwordGenBook.java 2222 2012-01-25 22:11:28Z dmsmith $
21   */
22  package org.crosswire.jsword.book.sword;
23  
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.crosswire.common.activate.Activator;
31  import org.crosswire.common.activate.Lock;
32  import org.crosswire.jsword.JSMsg;
33  import org.crosswire.jsword.JSOtherMsg;
34  import org.crosswire.jsword.book.BookException;
35  import org.crosswire.jsword.book.basic.AbstractBook;
36  import org.crosswire.jsword.book.filter.Filter;
37  import org.crosswire.jsword.passage.DefaultKeyList;
38  import org.crosswire.jsword.passage.Key;
39  import org.crosswire.jsword.passage.NoSuchKeyException;
40  import org.crosswire.jsword.passage.ReadOnlyKeyList;
41  import org.jdom.Content;
42  
43  /**
44   * A Sword version of Dictionary.
45   * 
46   * @see gnu.lgpl.License for license details.<br>
47   *      The copyright to this program is held by it's authors.
48   * @author Joe Walker [joe at eireneh dot com]
49   */
50  public class SwordGenBook extends AbstractBook {
51      /**
52       * Start and to as much checking as we can without using memory. (i.e.
53       * actually reading the indexes)
54       */
55      protected SwordGenBook(SwordBookMetaData sbmd, AbstractBackend backend) {
56          super(sbmd);
57  
58          this.backend = backend;
59          this.filter = sbmd.getFilter();
60          map = null;
61          set = null;
62          global = null;
63          active = false;
64      }
65  
66      /* (non-Javadoc)
67       * @see org.crosswire.jsword.book.basic.AbstractBook#activate(org.crosswire.common.activate.Lock)
68       */
69      @Override
70      public final void activate(Lock lock) {
71          super.activate(lock);
72  
73          set = backend.readIndex();
74  
75          map = new HashMap<String, Key>();
76          for (Key key : set) {
77              map.put(key.getName(), key);
78          }
79  
80          global = new ReadOnlyKeyList(set, false);
81  
82          active = true;
83  
84          // We don't need to activate the backend because it should be capable
85          // of doing it for itself.
86      }
87  
88      /* (non-Javadoc)
89       * @see org.crosswire.jsword.book.basic.AbstractBook#deactivate(org.crosswire.common.activate.Lock)
90       */
91      @Override
92      public final void deactivate(Lock lock) {
93          super.deactivate(lock);
94  
95          map = null;
96          set = null;
97          global = null;
98  
99          Activator.deactivate(backend);
100 
101         active = false;
102     }
103 
104     /* (non-Javadoc)
105      * @see org.crosswire.jsword.book.Book#getOsisIterator(org.crosswire.jsword.passage.Key, boolean)
106      */
107     public Iterator<Content> getOsisIterator(Key key, boolean allowEmpty) throws BookException {
108         checkActive();
109 
110         assert key != null;
111         assert backend != null;
112 
113         List<Content> content = new ArrayList<Content>();
114 
115         String txt = backend.getRawText(key);
116 
117         List<Content> osisContent = filter.toOSIS(this, key, txt);
118         content.addAll(osisContent);
119 
120         return content.iterator();
121     }
122 
123     /* (non-Javadoc)
124      * @see org.crosswire.jsword.book.Book#contains(org.crosswire.jsword.passage.Key)
125      */
126     public boolean contains(Key key) {
127         return backend != null && backend.contains(key);
128     }
129 
130     /* (non-Javadoc)
131      * @see org.crosswire.jsword.book.Book#getRawText(org.crosswire.jsword.passage.Key)
132      */
133     public String getRawText(Key key) throws BookException {
134         checkActive();
135 
136         assert key != null;
137         assert backend != null;
138 
139         return backend.getRawText(key);
140     }
141 
142     /* (non-Javadoc)
143      * @see org.crosswire.jsword.book.Book#isWritable()
144      */
145     public boolean isWritable() {
146         return backend.isWritable();
147     }
148 
149     /* (non-Javadoc)
150      * @see org.crosswire.jsword.book.Book#setRawText(org.crosswire.jsword.passage.Key, java.lang.String)
151      */
152     public void setRawText(Key key, String rawData) throws BookException {
153         throw new BookException(JSOtherMsg.lookupText("This Book is read-only."));
154     }
155 
156     /* (non-Javadoc)
157      * @see org.crosswire.jsword.book.Book#setAliasKey(org.crosswire.jsword.passage.Key, org.crosswire.jsword.passage.Key)
158      */
159     public void setAliasKey(Key alias, Key source) throws BookException {
160         throw new BookException(JSOtherMsg.lookupText("This Book is read-only."));
161     }
162 
163     /* (non-Javadoc)
164      * @see org.crosswire.jsword.passage.KeyFactory#getGlobalKeyList()
165      */
166     public Key getGlobalKeyList() {
167         checkActive();
168 
169         return global;
170     }
171 
172     /* (non-Javadoc)
173      * @see org.crosswire.jsword.passage.KeyFactory#getValidKey(java.lang.String)
174      */
175     public Key getValidKey(String name) {
176         try {
177             return getKey(name);
178         } catch (NoSuchKeyException e) {
179             return createEmptyKeyList();
180         }
181     }
182 
183     /* (non-Javadoc)
184      * @see org.crosswire.jsword.passage.KeyFactory#getKey(java.lang.String)
185      */
186     public Key getKey(String text) throws NoSuchKeyException {
187         checkActive();
188 
189         Key key = map.get(text);
190         if (key != null) {
191             return key;
192         }
193 
194         // First check for keys that match ignoring case
195         for (String keyName : map.keySet()) {
196             if (keyName.equalsIgnoreCase(text)) {
197                 return map.get(keyName);
198             }
199         }
200 
201         // Next keys that start with the given text
202         for (String keyName : map.keySet()) {
203             if (keyName.startsWith(text)) {
204                 return map.get(keyName);
205             }
206         }
207 
208         // Next try keys that contain the given text
209         for (String keyName : map.keySet()) {
210             if (keyName.indexOf(text) != -1) {
211                 return map.get(keyName);
212             }
213         }
214 
215         // TRANSLATOR: Error condition: Indicates that something could not be found in the book.
216         // {0} is a placeholder for the unknown key.
217         // {1} is the short name of the book
218         throw new NoSuchKeyException(JSMsg.gettext("No entry for '{0}' in {1}.", text, getInitials()));
219     }
220 
221     /* (non-Javadoc)
222      * @see org.crosswire.jsword.passage.KeyFactory#createEmptyKeyList()
223      */
224     public Key createEmptyKeyList() {
225         return new DefaultKeyList();
226     }
227 
228     /**
229      * Helper method so we can quickly activate ourselves on access
230      */
231     private void checkActive() {
232         if (!active) {
233             Activator.activate(this);
234         }
235     }
236 
237     /**
238      * The global key list
239      */
240     private Key global;
241 
242     /**
243      * Are we active
244      */
245     private boolean active;
246 
247     /**
248      * So we can quickly find a Key given the text for the key
249      */
250     private Map<String, Key> map;
251 
252     /**
253      * So we can implement getIndex() easily
254      */
255     private Key set;
256 
257     /**
258      * To read the data from the disk
259      */
260     private AbstractBackend backend;
261 
262     /**
263      * The filter to use to convert to OSIS.
264      */
265     private Filter filter;
266 }
267