Coverage Report - org.crosswire.jsword.book.sword.SwordBook
 
Classes in this File Line Coverage Branch Coverage Complexity
SwordBook
0%
0/108
0%
0/58
4.417
 
 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.sword;
 21  
 
 22  
 import java.util.List;
 23  
 
 24  
 import org.crosswire.jsword.JSOtherMsg;
 25  
 import org.crosswire.jsword.book.BookException;
 26  
 import org.crosswire.jsword.book.BookMetaData;
 27  
 import org.crosswire.jsword.book.OSISUtil;
 28  
 import org.crosswire.jsword.book.basic.AbstractPassageBook;
 29  
 import org.crosswire.jsword.book.filter.SourceFilter;
 30  
 import org.crosswire.jsword.book.sword.processing.RawTextToXmlProcessor;
 31  
 import org.crosswire.jsword.passage.Key;
 32  
 import org.crosswire.jsword.passage.KeyUtil;
 33  
 import org.crosswire.jsword.passage.NoSuchKeyException;
 34  
 import org.crosswire.jsword.passage.PassageKeyFactory;
 35  
 import org.crosswire.jsword.passage.VerseKey;
 36  
 import org.crosswire.jsword.versification.Versification;
 37  
 import org.jdom2.Attribute;
 38  
 import org.jdom2.Content;
 39  
 import org.jdom2.Element;
 40  
 import org.slf4j.Logger;
 41  
 import org.slf4j.LoggerFactory;
 42  
 
 43  
 /**
 44  
  * SwordBook is a base class for all verse based Sword type books.
 45  
  * 
 46  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 47  
  * @author Joe Walker
 48  
  */
 49  0
 public class SwordBook extends AbstractPassageBook {
 50  
     /**
 51  
      * Construct an SwordBook 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  
     public SwordBook(SwordBookMetaData sbmd, Backend<?> backend) {
 57  0
         super(sbmd, backend);
 58  
 
 59  0
         this.filter = sbmd.getFilter();
 60  
 
 61  0
         if (backend == null) {
 62  0
             throw new IllegalArgumentException("AbstractBackend must not be null.");
 63  
         }
 64  0
     }
 65  
 
 66  
     /* (non-Javadoc)
 67  
      * @see org.crosswire.jsword.book.Book#getGlobalKeyList()
 68  
      */
 69  
     public final Key getGlobalKeyList() {
 70  0
         if (global == null) {
 71  
             try {
 72  0
                 global = getBackend().getGlobalKeyList();
 73  0
                 return global;
 74  0
             } catch (UnsupportedOperationException ex) {
 75  
                 // fail silently, operation not supported by the backend
 76  0
                 log.debug(ex.getMessage());
 77  0
             } catch (BookException ex) {
 78  
                 // failing silently, as previous behavior was to attempt to
 79  
                 // return as much as we can using the slower method
 80  0
                 log.debug(ex.getMessage());
 81  0
             }
 82  
 
 83  0
             Versification v11n = super.getVersification();
 84  
 
 85  0
             global = super.createEmptyKeyList();
 86  0
             Key all = PassageKeyFactory.instance().getGlobalKeyList(v11n);
 87  
 
 88  0
             for (Key key : all) {
 89  0
                 if (contains(key)) {
 90  0
                     global.addAll(key);
 91  
                 }
 92  
             }
 93  
         }
 94  
 
 95  0
         return global;
 96  
     }
 97  
 
 98  
     /* (non-Javadoc)
 99  
      * @see org.crosswire.jsword.book.basic.AbstractBook#getScope()
 100  
      */
 101  
     @Override
 102  
     public Key getScope() {
 103  0
         SwordBookMetaData sbmd = (SwordBookMetaData) getBookMetaData();
 104  
         //if the book type doesn't have verses, then leave it.
 105  0
         if (sbmd.getProperty(BookMetaData.KEY_VERSIFICATION) == null) {
 106  
             //then we're not looking at a versified book
 107  0
             return null;
 108  
         }
 109  
 
 110  0
         Object keyString = sbmd.getProperty(BookMetaData.KEY_SCOPE);
 111  
 
 112  0
         if (keyString != null) {
 113  
             try {
 114  0
                 return getKey((String) keyString);
 115  0
             } catch (NoSuchKeyException ex) {
 116  
                 //the scope defined is not correct
 117  0
                 log.error("Unable to parse scope from book", ex);
 118  0
                 return null;
 119  
             }
 120  
         }
 121  
 
 122  
         //need to calculate the scope
 123  
         //now comes the expensive part
 124  0
         Key bookKeys = getGlobalKeyList();
 125  
 
 126  
         //this is practically impossible, but cater for it just in case.
 127  0
         if (!(bookKeys instanceof VerseKey)) {
 128  0
             log.error("Global key list isn't a verse key. A very expensive no-op has just occurred.");
 129  0
             return null;
 130  
         }
 131  
 
 132  0
         return bookKeys;
 133  
     }
 134  
 
 135  
     /* (non-Javadoc)
 136  
      * @see org.crosswire.jsword.book.Book#contains(org.crosswire.jsword.passage.Key)
 137  
      */
 138  
     public boolean contains(Key key) {
 139  0
         return getBackend().contains(key);
 140  
     }
 141  
 
 142  
     /* (non-Javadoc)
 143  
      * @see org.crosswire.jsword.book.Book#getRawText(org.crosswire.jsword.passage.Key)
 144  
      */
 145  
     public String getRawText(Key key) throws BookException {
 146  0
         return getBackend().getRawText(key);
 147  
     }
 148  
 
 149  
     @Override
 150  
     protected List<Content> getOsis(Key key, RawTextToXmlProcessor processor) throws BookException {
 151  0
         List<Content> result = getBackend().readToOsis(key, processor);
 152  0
         assert result != null;
 153  0
         return result;
 154  
     }
 155  
 
 156  
     @Override
 157  
     public void addOSIS(Key key, Element div, List<Content> osisContent) {
 158  
         // See if the text is marked up with verses
 159  
         // If it is then just add it.
 160  0
         for (Content content : osisContent) {
 161  0
             if (content instanceof Element) {
 162  0
                 Element ele = (Element) content;
 163  0
                 if (ele.getName().equals(OSISUtil.OSIS_ELEMENT_VERSE)) {
 164  0
                     super.addOSIS(key, div, osisContent);
 165  0
                     return;
 166  
                 }
 167  0
             }
 168  
         }
 169  
 
 170  
         // If we get here then the text is not marked up with verse
 171  
         // In this case we add the verse markup, if the verse is not 0.
 172  0
         if (KeyUtil.getVerse(key).getVerse() == 0) {
 173  0
             super.addOSIS(key, div, osisContent);
 174  
         } else {
 175  
             // In a SWORD module, the verse element is to be put
 176  
             // after the "preverse" material.
 177  0
             Element everse = OSISUtil.factory().createVerse();
 178  0
             everse.setAttribute(OSISUtil.OSIS_ATTR_OSISID, key.getOsisID());
 179  0
             div.addContent(everse);
 180  0
             super.addOSIS(key, everse, osisContent);
 181  
         }
 182  0
     }
 183  
 
 184  
     @Override
 185  
     public void addOSIS(Key key, List<Content> contentList, List<Content> osisContent) {
 186  
         // If there is no content, then there is nothing to do.
 187  0
         if (osisContent.size() == 0) {
 188  0
             return;
 189  
         }
 190  
 
 191  
         // Note: Verse 0 is an introduction and not a verse so it never gets verse markup.
 192  
         // However, it should be wrapped by a div as it should be isolated as an intro.
 193  0
         if (KeyUtil.getVerse(key).getVerse() == 0) {
 194  0
             Element div = OSISUtil.factory().createDiv();
 195  0
             div.setAttribute(OSISUtil.OSIS_ATTR_OSISID, key.getOsisID());
 196  
             // Mark it as generated
 197  0
             div.setAttribute(OSISUtil.OSIS_ATTR_TYPE, OSISUtil.GENERATED_CONTENT);
 198  
             // Put the OSIS into the div
 199  0
             div.addContent(osisContent);
 200  
             // Then put the div at the end of the contentList
 201  0
             contentList.add(div);
 202  0
             return;
 203  
         }
 204  
 
 205  
         // SWORD modules typically do not have the verse marker. When they don't
 206  
         // then the verse element needs to wrap the verse content.
 207  
         // However, the verse content may have "pre-verse" content.
 208  
         // This is marked up in one of two ways:
 209  
         // 1) old way
 210  
         //      <title subType="x-preverse">...</title>
 211  
         //    There may be more than one title marked as such.
 212  
         // 2) current way
 213  
         //      <div sID="xxx" type="x-milestone" subType="x-preverse"/>
 214  
         //         ...
 215  
         //      <div eID="xxx" type="x-milestone" subType="x-preverse"/>
 216  
         //      verse content
 217  
         //    In this we only need to look for the ending.
 218  
         // The critical observation is that the verse marker is to
 219  
         // follow the last element marked x-preverse.
 220  
         // Also, there are a good number of modules that have a title marked
 221  
         // type="psalm" and not canonical="true" which they should be.
 222  
 
 223  
         // See if the text is marked up with verse elements
 224  
         // If it is then just add it.
 225  0
         int start = 0;
 226  0
         int found = -1;
 227  0
         boolean wrapped = false;
 228  0
         Element preverse = null;
 229  0
         for (Content content : osisContent) {
 230  0
             if (content instanceof Element) {
 231  0
                 Element ele = (Element) content;
 232  0
                 String name = ele.getName();
 233  0
                 if (OSISUtil.OSIS_ELEMENT_VERSE.equals(name)) {
 234  0
                     wrapped = true;
 235  0
                     continue;
 236  
                 }
 237  0
                 Attribute typeAttr = ele.getAttribute(OSISUtil.OSIS_ATTR_TYPE);
 238  0
                 Attribute subTypeAttr = ele.getAttribute(OSISUtil.OSIS_ATTR_SUBTYPE);
 239  0
                 if (subTypeAttr != null && "x-preverse".equals(subTypeAttr.getValue())) {
 240  0
                     if (OSISUtil.OSIS_ELEMENT_DIV.equals(name) || OSISUtil.OSIS_ELEMENT_TITLE.equals(name)) {
 241  0
                         preverse = ele;
 242  0
                         found = start;
 243  
                     }
 244  0
                 } else if (typeAttr != null && "psalm".equals(typeAttr.getValue()) && OSISUtil.OSIS_ELEMENT_TITLE.equals(name)) {
 245  
                     // Psalm titles should be both canonical and preverse
 246  
                     // set the appropriate attributes if not already set.
 247  0
                     Attribute canonicalAttr = ele.getAttribute(OSISUtil.OSIS_ATTR_CANONICAL);
 248  0
                     if (canonicalAttr == null) {
 249  0
                         ele.setAttribute(OSISUtil.OSIS_ATTR_CANONICAL, "true");
 250  
                     }
 251  0
                     if (subTypeAttr == null) {
 252  0
                         ele.setAttribute(OSISUtil.OSIS_ATTR_SUBTYPE, "x-preverse");
 253  0
                         preverse = ele;
 254  0
                         found = start;
 255  
                     }
 256  
                 }
 257  
             }
 258  0
             start++;
 259  
         }
 260  
 
 261  
         // Check to see whether the text is marked up with verse
 262  0
         if (wrapped) {
 263  0
             super.addOSIS(key, contentList, osisContent);
 264  0
             return;
 265  
         }
 266  
 
 267  
         // If we get here then the text is not marked up with verse
 268  
         // In this case we add the verse markup, if the verse is not 0.
 269  0
         Element everse = OSISUtil.factory().createVerse();
 270  0
         everse.setAttribute(OSISUtil.OSIS_ATTR_OSISID, key.getOsisID());
 271  0
         if (preverse == null) {
 272  0
             everse.addContent(osisContent);
 273  
         } else {
 274  0
             List<Content> sublist = osisContent.subList(found + 1, osisContent.size());
 275  0
             everse.addContent(sublist);
 276  
             // a sub list is actually part of the original list
 277  
             // clearing it removes it from the original list
 278  0
             sublist.clear();
 279  
             // Now append shortened list
 280  0
             super.addOSIS(key, contentList, osisContent);
 281  
         }
 282  
         // Then put the verse at the end of the contentList
 283  0
         contentList.add(everse);
 284  0
     }
 285  
 
 286  
     @Override
 287  
     public boolean isWritable() {
 288  0
         return getBackend().isWritable();
 289  
     }
 290  
 
 291  
     /* (non-Javadoc)
 292  
      * @see org.crosswire.jsword.book.Book#setRawText(org.crosswire.jsword.passage.Key, java.lang.String)
 293  
      */
 294  
     public void setRawText(Key key, String rawData) throws BookException {
 295  0
         throw new BookException(JSOtherMsg.lookupText("This Book is read-only."));
 296  
     }
 297  
 
 298  
     /* (non-Javadoc)
 299  
      * @see org.crosswire.jsword.book.Book#setAliasKey(org.crosswire.jsword.passage.Key, org.crosswire.jsword.passage.Key)
 300  
      */
 301  
     public void setAliasKey(Key alias, Key source) throws BookException {
 302  0
         getBackend().setAliasKey(alias, source);
 303  0
     }
 304  
 
 305  
     /* (non-Javadoc)
 306  
      * @see org.crosswire.jsword.book.basic.AbstractPassageBook#getFilter()
 307  
      */
 308  
     @Override
 309  
     protected SourceFilter getFilter() {
 310  0
         return filter;
 311  
     }
 312  
 
 313  
     /**
 314  
      * The filter to use to convert to OSIS.
 315  
      */
 316  
     private SourceFilter filter;
 317  
 
 318  
     /**
 319  
      * A cached representation of the global key list.
 320  
      */
 321  
     private Key global;
 322  
 
 323  
     /**
 324  
      * The log stream
 325  
      */
 326  0
     private static final Logger log = LoggerFactory.getLogger(SwordBook.class);
 327  
 }