Coverage Report - org.crosswire.jsword.book.basic.AbstractPassageBook
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractPassageBook
0%
0/79
0%
0/44
2.263
AbstractPassageBook$1
0%
0/13
0%
0/8
2.263
 
 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.book.basic;
 21  
 
 22  
 import java.util.HashSet;
 23  
 import java.util.Iterator;
 24  
 import java.util.LinkedHashSet;
 25  
 import java.util.List;
 26  
 import java.util.Set;
 27  
 
 28  
 import org.crosswire.common.util.StringUtil;
 29  
 import org.crosswire.jsword.book.BookData;
 30  
 import org.crosswire.jsword.book.BookException;
 31  
 import org.crosswire.jsword.book.BookMetaData;
 32  
 import org.crosswire.jsword.book.OSISUtil;
 33  
 import org.crosswire.jsword.book.filter.SourceFilter;
 34  
 import org.crosswire.jsword.book.sword.Backend;
 35  
 import org.crosswire.jsword.book.sword.processing.RawTextToXmlProcessor;
 36  
 import org.crosswire.jsword.passage.Key;
 37  
 import org.crosswire.jsword.passage.KeyUtil;
 38  
 import org.crosswire.jsword.passage.NoSuchKeyException;
 39  
 import org.crosswire.jsword.passage.Passage;
 40  
 import org.crosswire.jsword.passage.PassageKeyFactory;
 41  
 import org.crosswire.jsword.passage.RestrictionType;
 42  
 import org.crosswire.jsword.passage.Verse;
 43  
 import org.crosswire.jsword.passage.VerseKey;
 44  
 import org.crosswire.jsword.passage.VerseRange;
 45  
 import org.crosswire.jsword.versification.BibleBook;
 46  
 import org.crosswire.jsword.versification.Versification;
 47  
 import org.crosswire.jsword.versification.VersificationsMapper;
 48  
 import org.crosswire.jsword.versification.system.Versifications;
 49  
 import org.jdom2.Content;
 50  
 import org.jdom2.Element;
 51  
 import org.slf4j.Logger;
 52  
 import org.slf4j.LoggerFactory;
 53  
 
 54  
 /**
 55  
  * An abstract implementation of Book that lets implementors just concentrate on
 56  
  * reading book data.
 57  
  *
 58  
  * @author Joe Walker
 59  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.<br>
 60  
  * The copyright to this program is held by its authors.
 61  
  */
 62  0
 public abstract class AbstractPassageBook extends AbstractBook {
 63  
 
 64  
     /**
 65  
      * Construct an AbstractPassageBook given the BookMetaData and the AbstractBackend.
 66  
      *
 67  
      * @param bmd     the metadata that describes the book
 68  
      * @param backend the means by which the resource is accessed
 69  
      */
 70  
     public AbstractPassageBook(BookMetaData bmd, Backend backend) {
 71  0
         super(bmd, backend);
 72  0
         keyf = PassageKeyFactory.instance();
 73  0
         this.versification = bmd.getProperty(BookMetaData.KEY_VERSIFICATION);
 74  0
     }
 75  
 
 76  
     /* (non-Javadoc)
 77  
      * @see org.crosswire.jsword.book.Book#getOsisIterator(org.crosswire.jsword.passage.Key, boolean, boolean)
 78  
      */
 79  
     public Iterator<Content> getOsisIterator(final Key key, final boolean allowEmpty, final boolean allowGenTitles) throws BookException {
 80  
         // Note: allowEmpty indicates parallel view
 81  
         // TODO(DMS): make the iterator be demand driven
 82  0
         final SourceFilter filter = getFilter();
 83  
 
 84  
         // For all the ranges in this Passage
 85  
         //TODO(CJB): I'd prefer to do the key mapping in KeyUtil, and pass in our current versification.
 86  
         //we could remove the method that doesn't support the versification parameter.
 87  
         //but that has far reaching consequences.
 88  0
         Passage ref = VersificationsMapper.instance().map(KeyUtil.getPassage(key), this.getVersification());
 89  
 
 90  
         // Generated titles are shown when
 91  
         // there are 2 or more ranges or
 92  
         // empty are not allowed and generated titles are allowed
 93  0
         final boolean showTitles = ref.hasRanges(RestrictionType.CHAPTER) || (!allowEmpty && allowGenTitles);
 94  
 
 95  0
         RawTextToXmlProcessor processor = new RawTextToXmlProcessor() {
 96  
             // track previous text to exclude duplicates caused by merged verses
 97  0
             private String previousVerseText = "";
 98  
 
 99  
             public void preRange(VerseRange range, List<Content> partialDom) {
 100  0
                 if (showTitles) {
 101  0
                     Element title = OSISUtil.factory().createGeneratedTitle();
 102  0
                     title.addContent(range.getName());
 103  0
                     partialDom.add(title);
 104  
                 }
 105  0
             }
 106  
 
 107  
             public void postVerse(Key verse, List<Content> partialDom, String rawText) {
 108  
                 // If the verse is empty or repeated then we shouldn't add the verse
 109  0
                 if ((allowEmpty || rawText.length() > 0) && !previousVerseText.equals(rawText)) {
 110  0
                     List<Content> osisContent = filter.toOSIS(AbstractPassageBook.this, verse, rawText);
 111  0
                     addOSIS(verse, partialDom, osisContent);
 112  
                 }
 113  0
                 previousVerseText = rawText;
 114  0
             }
 115  
 
 116  
             public void init(List<Content> partialDom) {
 117  
                 // no-op
 118  0
             }
 119  
         };
 120  
 
 121  0
         return getOsis(ref, processor).iterator();
 122  
     }
 123  
 
 124  
     /**
 125  
      * Add the OSIS elements to the div element. Note, this assumes that the
 126  
      * data is fully marked up.
 127  
      *
 128  
      * @param key         The key being added
 129  
      * @param div         The div element to which the key's OSIS representation is
 130  
      *                    being added
 131  
      * @param osisContent The OSIS representation of the key being added.
 132  
      */
 133  
     public void addOSIS(Key key, Element div, List<Content> osisContent) {
 134  0
         assert key != null;
 135  0
         div.addContent(osisContent);
 136  0
     }
 137  
 
 138  
     /**
 139  
      * Add the OSIS elements to the content list. Note, this assumes that the
 140  
      * data is fully marked up.
 141  
      *
 142  
      * @param key         The key being added
 143  
      * @param content     The list to which the key's OSIS representation is being added
 144  
      * @param osisContent The OSIS representation of the key being added.
 145  
      */
 146  
     public void addOSIS(Key key, List<Content> content, List<Content> osisContent) {
 147  0
         assert key != null;
 148  0
         content.addAll(osisContent);
 149  0
     }
 150  
 
 151  
     /**
 152  
      * What filter should be used to filter data in the format produced by this
 153  
      * Book?. In some ways this method is more suited to BookMetaData however we
 154  
      * do not have a specialization of BookMetaData to fit AbstractPassageBook
 155  
      * and it doesn't like any higher in the hierarchy at the moment so I will
 156  
      * leave this here.
 157  
      */
 158  
     protected abstract SourceFilter getFilter();
 159  
 
 160  
     /**
 161  
      * For when we want to add writing functionality. This does not work.
 162  
      *
 163  
      * @param key
 164  
      * @param bdata
 165  
      * @throws BookException
 166  
      */
 167  
     public void setDocument(Key key, BookData bdata) throws BookException {
 168  
         // For all of the sections
 169  0
         for (Content nextElem : OSISUtil.getFragment(bdata.getOsisFragment())) {
 170  0
             if (nextElem instanceof Element) {
 171  0
                 Element div = (Element) nextElem;
 172  
 
 173  
                 // For all of the Verses in the section
 174  0
                 for (Content data : div.getContent()) {
 175  0
                     if (data instanceof Element) {
 176  0
                         Element overse = (Element) data;
 177  0
                         String text = OSISUtil.getPlainText(overse);
 178  
 
 179  0
                         setRawText(key, text);
 180  0
                     } else {
 181  0
                         log.error("Ignoring non OSIS/Verse content of DIV.");
 182  
                     }
 183  
                 }
 184  0
             } else {
 185  0
                 log.error("Ignoring non OSIS/Verse content of DIV.");
 186  
             }
 187  
         }
 188  0
     }
 189  
 
 190  
     /* (non-Javadoc)
 191  
      * @see org.crosswire.jsword.book.Book#isWritable()
 192  
      */
 193  
     public boolean isWritable() {
 194  0
         return false;
 195  
     }
 196  
 
 197  
     /* (non-Javadoc)
 198  
      * @see org.crosswire.jsword.passage.KeyFactory#createEmptyKeyList()
 199  
      */
 200  
     public final Key createEmptyKeyList() {
 201  0
         return keyf.createEmptyKeyList(Versifications.instance().getVersification(versification));
 202  
     }
 203  
 
 204  
 
 205  
     /* (non-Javadoc)
 206  
      * @see org.crosswire.jsword.passage.KeyFactory#getValidKey(java.lang.String)
 207  
      */
 208  
     public Key getValidKey(String name) {
 209  
         try {
 210  0
             return getKey(name);
 211  0
         } catch (NoSuchKeyException e) {
 212  0
             return createEmptyKeyList();
 213  
         }
 214  
     }
 215  
 
 216  
     /* (non-Javadoc)
 217  
      * @see org.crosswire.jsword.passage.KeyFactory#getKey(java.lang.String)
 218  
      */
 219  
     public final Key getKey(String text) throws NoSuchKeyException {
 220  0
         return PassageKeyFactory.instance().getKey(Versifications.instance().getVersification(versification), text);
 221  
     }
 222  
 
 223  
     public Versification getVersification() {
 224  0
         if (this.versificationSystem == null) {
 225  0
             this.versificationSystem = Versifications.instance().getVersification(getBookMetaData().getProperty(BookMetaData.KEY_VERSIFICATION));
 226  
         }
 227  0
         return versificationSystem;
 228  
     }
 229  
 
 230  
     /**
 231  
      * This implementation lazily inits, saves to the JSword conf file and also caches the book list for future use.
 232  
      *
 233  
      * @return the list of Bible books contained in the module
 234  
      */
 235  
     public Set<BibleBook> getBibleBooks() {
 236  0
         if (bibleBooks == null) {
 237  0
             synchronized (this) {
 238  0
                 if (bibleBooks == null) {
 239  0
                     bibleBooks = getBibleBooksInternal();
 240  
                 }
 241  0
             }
 242  
         }
 243  
 
 244  0
         return bibleBooks;
 245  
     }
 246  
 
 247  
     /**
 248  
      * Obtains the set of bible books from the internal configuration file, creating it if required.
 249  
      * @return the bible books relevant to this module.
 250  
      */
 251  
     private Set<BibleBook> getBibleBooksInternal() {
 252  0
         String list = this.getBookMetaData().getProperty(BookMetaData.KEY_BOOKLIST);
 253  
         Set<BibleBook> books;
 254  0
         if (list == null) {
 255  
             //calculate and store
 256  0
             books = calculateBibleBookList();
 257  0
             String listOfBooks = toString(books);
 258  0
             this.putProperty(BookMetaData.KEY_BOOKLIST, listOfBooks);
 259  0
         } else {
 260  
             //iterate through each item and get the books as a bible books
 261  0
             books = fromString(list);
 262  
         }
 263  
 
 264  0
         return books;
 265  
     }
 266  
 
 267  
     private Set<BibleBook> fromString(String list) {
 268  0
         Set<BibleBook> books = new LinkedHashSet<BibleBook>(list.length() / 2);
 269  0
         final String[] bookOsis = StringUtil.split(list, ' ');
 270  0
         for (String s : bookOsis) {
 271  0
             books.add(BibleBook.fromExactOSIS(s));
 272  
         }
 273  0
         return books;
 274  
     }
 275  
 
 276  
     private String toString(Set<BibleBook> books) {
 277  0
         StringBuilder sb = new StringBuilder(books.size() * 8);
 278  0
         for (Iterator<BibleBook> iterator = books.iterator(); iterator.hasNext(); ) {
 279  0
             BibleBook b = iterator.next();
 280  0
             sb.append(b.getOSIS());
 281  0
             if (iterator.hasNext()) {
 282  0
                 sb.append(' ');
 283  
             }
 284  
 
 285  0
         }
 286  0
         return sb.toString();
 287  
     }
 288  
 
 289  
     /**
 290  
      * Iterate all books checking if document contains a verse from the book
 291  
      */
 292  
     private Set<BibleBook> calculateBibleBookList() {
 293  0
         final BookMetaData bookMetaData = this.getBookMetaData();
 294  0
         final VerseKey scope = (VerseKey) getScope();
 295  0
         if (scope == null) {
 296  0
             return new HashSet<BibleBook>();
 297  
         }
 298  
 
 299  0
         final Set<BibleBook> bookList = new LinkedHashSet<BibleBook>();
 300  
 
 301  
         // iterate over all book possible in this document
 302  0
         final Versification v11n = Versifications.instance().getVersification(bookMetaData.getProperty(BookMetaData.KEY_VERSIFICATION));
 303  0
         final Iterator<BibleBook> v11nBookIterator = v11n.getBookIterator();
 304  
 
 305  0
         while (v11nBookIterator.hasNext()) {
 306  0
             BibleBook bibleBook = v11nBookIterator.next();
 307  
             // test some random verses - normally ch1 v 1 is sufficient - but we don't want to miss any
 308  0
             if (scope.contains(new Verse(v11n, bibleBook, 1, 1))
 309  
                 || scope.contains(new Verse(v11n, bibleBook, 1, 2)))
 310  
             {
 311  0
                 bookList.add(bibleBook);
 312  
             }
 313  0
         }
 314  
 
 315  0
         return bookList;
 316  
     }
 317  
 
 318  
     /**
 319  
      * The name of the versification or null
 320  
      */
 321  
     private String versification;
 322  
 
 323  
     /**
 324  
      * Versification system, created lazily, so use getter
 325  
      */
 326  
     private Versification versificationSystem;
 327  
 
 328  
     /**
 329  
      * Our key manager
 330  
      */
 331  
     private PassageKeyFactory keyf;
 332  
 
 333  
     /**
 334  
      * lazy of cache of bible books contained in the Book
 335  
      */
 336  
     private volatile Set<BibleBook> bibleBooks;
 337  
 
 338  
     /**
 339  
      * The log stream
 340  
      */
 341  0
     private static final Logger log = LoggerFactory.getLogger(AbstractPassageBook.class);
 342  
 
 343  
 }