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