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