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.examples;
22  
23  import java.net.URL;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.xml.transform.TransformerException;
29  
30  import org.crosswire.common.util.NetUtil;
31  import org.crosswire.common.util.ResourceUtil;
32  import org.crosswire.common.xml.Converter;
33  import org.crosswire.common.xml.SAXEventProvider;
34  import org.crosswire.common.xml.TransformingSAXEventProvider;
35  import org.crosswire.common.xml.XMLUtil;
36  import org.crosswire.jsword.book.Book;
37  import org.crosswire.jsword.book.BookCategory;
38  import org.crosswire.jsword.book.BookData;
39  import org.crosswire.jsword.book.BookException;
40  import org.crosswire.jsword.book.BookFilter;
41  import org.crosswire.jsword.book.BookFilters;
42  import org.crosswire.jsword.book.BookMetaData;
43  import org.crosswire.jsword.book.Books;
44  import org.crosswire.jsword.book.BooksEvent;
45  import org.crosswire.jsword.book.BooksListener;
46  import org.crosswire.jsword.book.OSISUtil;
47  import org.crosswire.jsword.book.install.InstallException;
48  import org.crosswire.jsword.book.install.InstallManager;
49  import org.crosswire.jsword.book.install.Installer;
50  import org.crosswire.jsword.index.search.DefaultSearchModifier;
51  import org.crosswire.jsword.index.search.DefaultSearchRequest;
52  import org.crosswire.jsword.passage.Key;
53  import org.crosswire.jsword.passage.NoSuchKeyException;
54  import org.crosswire.jsword.passage.Passage;
55  import org.crosswire.jsword.passage.PassageTally;
56  import org.crosswire.jsword.passage.RestrictionType;
57  import org.crosswire.jsword.util.ConverterFactory;
58  import org.xml.sax.SAXException;
59  
60  /**
61   * All the methods in this class highlight some are of the API and how to use
62   * it.
63   * 
64   * @see gnu.lgpl.License for license details.<br>
65   *      The copyright to this program is held by it's authors.
66   * @author Joe Walker [joe at eireneh dot com]
67   */
68  public class APIExamples {
69      /**
70       * The name of a Bible to find
71       */
72      private static final String BIBLE_NAME = "KJV";
73  
74      /**
75       * Get a particular installed book by initials.
76       * 
77       * @param bookInitials
78       *            The book name to search for
79       * @return The found book. Null otherwise.
80       */
81      public Book getBook(String bookInitials) {
82          return Books.installed().getBook(bookInitials);
83      }
84  
85      /**
86       * Get just the canonical text of one or more book entries without any
87       * markup.
88       * 
89       * @param bookInitials
90       *            the book to use
91       * @param reference
92       *            a reference, appropriate for the book, of one or more entries
93       */
94      public String getPlainText(String bookInitials, String reference) throws BookException, NoSuchKeyException {
95          Book book = getBook(bookInitials);
96          if (book == null) {
97              return "";
98          }
99  
100         Key key = book.getKey(reference);
101         BookData data = new BookData(book, key);
102         return OSISUtil.getCanonicalText(data.getOsisFragment());
103     }
104 
105     /**
106      * Obtain a SAX event provider for the OSIS document representation of one
107      * or more book entries.
108      * 
109      * @param bookInitials
110      *            the book to use
111      * @param reference
112      *            a reference, appropriate for the book, of one or more entries
113      */
114     public SAXEventProvider getOSIS(String bookInitials, String reference, int maxKeyCount) throws BookException, NoSuchKeyException {
115         if (bookInitials == null || reference == null) {
116             return null;
117         }
118 
119         Book book = getBook(bookInitials);
120 
121         Key key = null;
122         if (BookCategory.BIBLE.equals(book.getBookCategory())) {
123             key = book.getKey(reference);
124             ((Passage) key).trimVerses(maxKeyCount);
125         } else {
126             key = book.createEmptyKeyList();
127 
128             int count = 0;
129             for (Key aKey : book.getKey(reference)) {
130                 if (++count >= maxKeyCount) {
131                     break;
132                 }
133                 key.addAll(aKey);
134             }
135         }
136 
137         BookData data = new BookData(book, key);
138 
139         return data.getSAXEventProvider();
140     }
141 
142     /**
143      * Obtain styled text (in this case HTML) for a book reference.
144      * 
145      * @param bookInitials
146      *            the book to use
147      * @param reference
148      *            a reference, appropriate for the book, of one or more entries
149      * @return the styled text
150      * @see Book
151      * @see SAXEventProvider
152      */
153     public String readStyledText(String bookInitials, String reference, int maxKeyCount) throws NoSuchKeyException, BookException, TransformerException,
154             SAXException
155     {
156         Book book = getBook(bookInitials);
157         SAXEventProvider osissep = getOSIS(bookInitials, reference, maxKeyCount);
158         if (osissep == null) {
159             return "";
160         }
161 
162         Converter styler = ConverterFactory.getConverter();
163 
164         TransformingSAXEventProvider htmlsep = (TransformingSAXEventProvider) styler.convert(osissep);
165 
166         // You can also pass parameters to the XSLT. What you pass depends upon
167         // what the XSLT can use.
168         BookMetaData bmd = book.getBookMetaData();
169         boolean direction = bmd.isLeftToRight();
170         htmlsep.setParameter("direction", direction ? "ltr" : "rtl");
171 
172         // Finally you can get the styled text.
173         return XMLUtil.writeToString(htmlsep);
174     }
175 
176     /**
177      * While Bible and Commentary are very similar, a Dictionary is read in a
178      * slightly different way. It is also worth looking at the JavaDoc for Book
179      * that has a way of treating Bible, Commentary and Dictionary the same.
180      * 
181      * @see Book
182      */
183     public void readDictionary() throws BookException {
184         // This just gets a list of all the known dictionaries and picks the
185         // first. In a real world app you will probably have a better way
186         // of doing this.
187         List<Book> dicts = Books.installed().getBooks(BookFilters.getDictionaries());
188         Book dict = dicts.get(0);
189 
190         // If I want every key in the Dictionary then I do this (or something
191         // like it - in the real world you want to call hasNext() on an iterator
192         // before next() but the point is the same:
193         Key keys = dict.getGlobalKeyList();
194         Key first = keys.iterator().next();
195 
196         System.out.println("The first Key in the default dictionary is " + first);
197 
198         BookData data = new BookData(dict, first);
199         System.out.println("And the text against that key is " + OSISUtil.getPlainText(data.getOsisFragment()));
200     }
201 
202     /**
203      * An example of how to search for various bits of data.
204      */
205     public void search() throws BookException {
206         Book bible = Books.installed().getBook(BIBLE_NAME);
207 
208         // This does a standard operator search. See the search documentation
209         // for more examples of how to search
210         Key key = bible.find("+moses +aaron");
211 
212         System.out.println("The following verses contain both moses and aaron: " + key.getName());
213 
214         // You can also trim the result to a more manageable quantity.
215         // The test here is not necessary since we are working with a bible. It
216         // is necessary if we don't know what it
217         // is.
218         if (key instanceof Passage) {
219             Passage remaining = ((Passage) key).trimVerses(5);
220             System.out.println("The first 5 verses containing both moses and aaron: " + key.getName());
221             if (remaining != null) {
222                 System.out.println("The rest of the verses are: " + remaining.getName());
223             } else {
224                 System.out.println("There are only 5 verses containing both moses and aaron");
225             }
226         }
227     }
228 
229     /**
230      * An example of how to perform a ranked search.
231      * 
232      * @throws BookException
233      */
234     void rankedSearch() throws BookException {
235         Book bible = Books.installed().getBook(BIBLE_NAME);
236 
237         // For a more complex example:
238         // Rank the verses and show the first 20
239         boolean rank = true;
240         int max = 20;
241 
242         DefaultSearchModifier modifier = new DefaultSearchModifier();
243         modifier.setRanked(rank);
244         modifier.setMaxResults(max);
245 
246         Key results = bible.find(new DefaultSearchRequest("for god so loved the world", modifier));
247         int total = results.getCardinality();
248         int partial = total;
249 
250         // we get PassageTallys for rank searches
251         if (results instanceof PassageTally || rank) {
252             PassageTally tally = (PassageTally) results;
253             tally.setOrdering(PassageTally.Order.TALLY);
254             int rankCount = max;
255             if (rankCount > 0 && rankCount < total) {
256                 // Here we are trimming by ranges, where a range is a set of
257                 // continuous verses.
258                 tally.trimRanges(rankCount, RestrictionType.NONE);
259                 partial = rankCount;
260             }
261         }
262         System.out.println("Showing the first " + partial + " of " + total + " verses.");
263         System.out.println(results);
264     }
265 
266     /**
267      * An example of how to do a search and then get text for each range of
268      * verses.
269      * 
270      * @throws BookException
271      * @throws SAXException
272      */
273     void searchAndShow() throws BookException, SAXException {
274         Book bible = Books.installed().getBook(BIBLE_NAME);
275 
276         // Search for words like Melchezedik
277         Key key = bible.find("melchesidec~");
278 
279         // Here is an example of how to iterate over the ranges and get the text
280         // for each.
281         // The key's iterator would have iterated over verses.
282 
283         // The following shows how to use a stylesheet of your own choosing
284         String path = "xsl/cswing/simple.xsl";
285         URL xslurl = ResourceUtil.getResource(path);
286         // Make ranges  break  on  chapter
287         Iterator<Key> rangeIter = ((Passage) key).rangeIterator(RestrictionType.CHAPTER);
288         // boundaries.
289         while (rangeIter.hasNext()) {
290             Key range = rangeIter.next();
291             BookData data = new BookData(bible, range);
292             SAXEventProvider osissep = data.getSAXEventProvider();
293             SAXEventProvider htmlsep = new TransformingSAXEventProvider(NetUtil.toURI(xslurl), osissep);
294             String text = XMLUtil.writeToString(htmlsep);
295             System.out.println("The html text of " + range.getName() + " is " + text);
296         }
297     }
298 
299     /**
300      * This is an example of the different ways to select a Book from the
301      * selection available.
302      * 
303      * @see org.crosswire.common.config.Config
304      * @see Books
305      */
306     public void pickBible() {
307         // The Default Bible - JSword does everything it can to make this work
308         Book book = Books.installed().getBook(BIBLE_NAME);
309 
310         // And you can find out more too:
311         System.out.println(book.getLanguage());
312 
313         // If you want a greater selection of Books:
314         List<Book> books = Books.installed().getBooks();
315         book = books.get(0);
316 
317         // Or you can narrow the range a bit
318         books = Books.installed().getBooks(BookFilters.getOnlyBibles());
319         book = books.get(0);
320 
321         // There are implementations of BookFilter for all sorts of things in
322         // the BookFilters class
323 
324         // If you are wanting to get really fancy you can implement your own
325         // BookFilter easily
326         List<Book> test = Books.installed().getBooks(new MyBookFilter("ESV"));
327         book = test.get(0);
328 
329         if (book != null) {
330             System.out.println(book.getInitials());
331         }
332 
333         // If you want to know about new books as they arrive:
334         Books.installed().addBooksListener(new MyBooksListener());
335     }
336 
337     public void installBook() {
338         // An installer knows how to install books
339         Installer installer = null;
340 
341         InstallManager imanager = new InstallManager();
342 
343         // Ask the Install Manager for a map of all known module sites
344         Map<String, Installer> installers = imanager.getInstallers();
345 
346         // Get all the installers one after the other
347         String name = null;
348         for (Map.Entry<String, Installer> mapEntry : installers.entrySet()) {
349             name = mapEntry.getKey();
350             installer = mapEntry.getValue();
351             System.out.println(name + ": " + installer.getInstallerDefinition());
352         }
353 
354         name = "CrossWire";
355         // If we know the name of the installer we can get it directly
356         installer = imanager.getInstaller(name);
357 
358         // Now we can get the list of books
359         try {
360             installer.reloadBookList();
361         } catch (InstallException e) {
362             e.printStackTrace();
363         }
364 
365         // Get a list of all the available books
366         List<Book> availableBooks = installer.getBooks();
367 
368         Book book = availableBooks.get(0);
369         if (book != null) {
370             System.out.println("Book " + book.getInitials() + " is available");
371         }
372 
373         // get some available books. In this case, just one book.
374         availableBooks = installer.getBooks(new MyBookFilter("ESV"));
375 
376         book = availableBooks.get(0);
377 
378         if (book != null) {
379             System.out.println("Book " + book.getInitials() + " is available");
380 
381             // Delete the book, if present
382             // At the moment, JSword will not re-install. Later it will, if the
383             // remote version is greater.
384             try {
385                 if (Books.installed().getBook("ESV") != null) {
386                     // Make the book unavailable.
387                     // This is normally done via listeners.
388                     Books.installed().removeBook(book);
389 
390                     // Actually do the delete
391                     // This should be a call on installer.
392                     book.getDriver().delete(book);
393                 }
394             } catch (BookException e1) {
395                 e1.printStackTrace();
396             }
397 
398             try {
399                 // Now install it. Note this is a background task.
400                 installer.install(book);
401             } catch (InstallException e) {
402                 e.printStackTrace();
403             }
404         }
405     }
406 
407     /**
408      * A simple BookFilter that looks for a Bible by name.
409      */
410     static class MyBookFilter implements BookFilter {
411         public MyBookFilter(String bookName) {
412             name = bookName;
413         }
414 
415         public boolean test(Book bk) {
416             return bk.getInitials().equals(name);
417         }
418 
419         private String name;
420     }
421 
422     /**
423      * A simple BooksListener that actually does nothing.
424      */
425     static class MyBooksListener implements BooksListener {
426         /*
427          * (non-Javadoc)
428          * 
429          * @see
430          * org.crosswire.jsword.book.BooksListener#bookAdded(org.crosswire.jsword
431          * .book.BooksEvent)
432          */
433         public void bookAdded(BooksEvent ev) {
434         }
435 
436         /*
437          * (non-Javadoc)
438          * 
439          * @see
440          * org.crosswire.jsword.book.BooksListener#bookRemoved(org.crosswire
441          * .jsword.book.BooksEvent)
442          */
443         public void bookRemoved(BooksEvent ev) {
444         }
445     }
446 
447     /**
448      * Quick Demo
449      * 
450      * @throws NoSuchKeyException
451      * @throws BookException
452      * @throws SAXException
453      * @throws TransformerException
454      */
455     public static void main(String[] args) throws BookException, NoSuchKeyException, TransformerException, SAXException {
456         APIExamples examples = new APIExamples();
457 
458         examples.installBook();
459         System.out.println("The plain text of Gen 1:1 is " + examples.getPlainText(BIBLE_NAME, "Gen 1:1"));
460         System.out.println("The html text of Gen 1:1 is " + examples.readStyledText(BIBLE_NAME, "Gen 1:1", 100));
461         examples.readDictionary();
462         examples.search();
463         examples.rankedSearch();
464         examples.searchAndShow();
465     }
466 }
467