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