Coverage Report - org.crosswire.jsword.book.sword.AbstractBackend
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractBackend
0%
0/89
0%
0/22
2.474
AbstractBackend$1
0%
0/1
N/A
2.474
 
 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.io.File;
 23  
 import java.io.IOException;
 24  
 import java.util.ArrayList;
 25  
 import java.util.Iterator;
 26  
 import java.util.List;
 27  
 
 28  
 import org.crosswire.common.crypt.Sapphire;
 29  
 import org.crosswire.jsword.JSMsg;
 30  
 import org.crosswire.jsword.JSOtherMsg;
 31  
 import org.crosswire.jsword.book.BookException;
 32  
 import org.crosswire.jsword.book.BookMetaData;
 33  
 import org.crosswire.jsword.book.sword.processing.RawTextToXmlProcessor;
 34  
 import org.crosswire.jsword.book.sword.state.OpenFileState;
 35  
 import org.crosswire.jsword.book.sword.state.OpenFileStateManager;
 36  
 import org.crosswire.jsword.passage.Key;
 37  
 import org.crosswire.jsword.passage.KeyUtil;
 38  
 import org.crosswire.jsword.passage.Passage;
 39  
 import org.crosswire.jsword.passage.RestrictionType;
 40  
 import org.crosswire.jsword.passage.Verse;
 41  
 import org.crosswire.jsword.passage.VerseRange;
 42  
 import org.jdom2.Content;
 43  
 import org.slf4j.Logger;
 44  
 import org.slf4j.LoggerFactory;
 45  
 
 46  
 /**
 47  
  * A generic way to read data from disk for later formatting.
 48  
  *
 49  
  * @param <T> The type of the OpenFileState that this class extends.
 50  
  * @author Joe Walker
 51  
  * @author DM Smith
 52  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 53  
  */
 54  
 public abstract class AbstractBackend<T extends OpenFileState> implements StatefulFileBackedBackend<T>, Backend<T> {
 55  
 
 56  
     /**
 57  
      * Default constructor for the sake of serialization.
 58  
      */
 59  
     /* protected */
 60  0
     public AbstractBackend() {
 61  0
     }
 62  
 
 63  
     /**
 64  
      * Construct a minimal backend
 65  
      *
 66  
      * @param sbmd
 67  
      */
 68  0
     public AbstractBackend(SwordBookMetaData sbmd) {
 69  0
         bmd = sbmd;
 70  0
     }
 71  
 
 72  
     /* (non-Javadoc)
 73  
      * @see org.crosswire.jsword.book.sword.Backend#getBookMetaData()
 74  
      */
 75  
     public BookMetaData getBookMetaData() {
 76  0
         return bmd;
 77  
     }
 78  
 
 79  
     /* (non-Javadoc)
 80  
      * @see org.crosswire.jsword.book.sword.Backend#decipher(byte[])
 81  
      */
 82  
     public void decipher(byte[] data) {
 83  0
         String cipherKeyString = getBookMetaData().getProperty(SwordBookMetaData.KEY_CIPHER_KEY);
 84  0
         if (cipherKeyString != null) {
 85  0
             Sapphire cipherEngine = new Sapphire(cipherKeyString.getBytes());
 86  0
             for (int i = 0; i < data.length; i++) {
 87  0
                 data[i] = cipherEngine.cipher(data[i]);
 88  
             }
 89  
             // destroy any evidence!
 90  0
             cipherEngine.burn();
 91  
         }
 92  0
     }
 93  
 
 94  
     /* (non-Javadoc)
 95  
      * @see org.crosswire.jsword.book.sword.Backend#encipher(byte[])
 96  
      */
 97  
     public void encipher(byte[] data) {
 98  
         // Enciphering and deciphering are the same!
 99  0
         decipher(data);
 100  0
     }
 101  
 
 102  
     /* (non-Javadoc)
 103  
      * @see org.crosswire.jsword.book.sword.Backend#readIndex()
 104  
      */
 105  
     public Key readIndex() {
 106  
         // TODO(dms): Eliminate readIndex by deriving GenBookBackend from
 107  
         // AbstractKeyBackend
 108  0
         return null;
 109  
     }
 110  
 
 111  
     /* (non-Javadoc)
 112  
      * @see org.crosswire.jsword.book.sword.Backend#contains(org.crosswire.jsword.passage.Key)
 113  
      */
 114  
     public abstract boolean contains(Key key);
 115  
 
 116  
     /* (non-Javadoc)
 117  
      * @see org.crosswire.jsword.book.sword.Backend#getRawText(org.crosswire.jsword.passage.Key)
 118  
      */
 119  
     public String getRawText(Key key) throws BookException {
 120  0
         T state = null;
 121  
         try {
 122  0
             state = initState();
 123  0
             return readRawContent(state, key);
 124  0
         } catch (IOException e) {
 125  0
             throw new BookException("Unable to obtain raw content from backend for key='" + key + '\'', e);
 126  
         } finally {
 127  0
             OpenFileStateManager.instance().release(state);
 128  
         }
 129  
     }
 130  
 
 131  
     /* (non-Javadoc)
 132  
      * @see org.crosswire.jsword.book.sword.Backend#setAliasKey(org.crosswire.jsword.passage.Key, org.crosswire.jsword.passage.Key)
 133  
      */
 134  
     public void setAliasKey(Key alias, Key source) throws BookException {
 135  0
         T state = null;
 136  
         try {
 137  0
             state = initState();
 138  0
             setAliasKey(state, alias, source);
 139  0
         } catch (IOException e) {
 140  0
             throw new BookException(JSOtherMsg.lookupText("Unable to save {0}.", alias.getOsisID()));
 141  
         } finally {
 142  0
             OpenFileStateManager.instance().release(state);
 143  0
         }
 144  0
     }
 145  
 
 146  
     /* (non-Javadoc)
 147  
      * @see org.crosswire.jsword.book.sword.Backend#size(org.crosswire.jsword.passage.Key)
 148  
      */
 149  
     public int getRawTextLength(Key key) {
 150  
         try {
 151  0
             String raw = getRawText(key);
 152  0
             return raw == null ? 0 : raw.length();
 153  0
         } catch (BookException e) {
 154  0
             return 0;
 155  
         }
 156  
     }
 157  
 
 158  
     /* (non-Javadoc)
 159  
      * @see org.crosswire.jsword.book.sword.Backend#getGlobalKeyList()
 160  
      */
 161  
     public Key getGlobalKeyList() throws BookException {
 162  
         //by default, this is not implemented
 163  0
         throw new UnsupportedOperationException("Fast global key list unsupported in this backend");
 164  
     }
 165  
 
 166  
     /* (non-Javadoc)
 167  
      * @see org.crosswire.jsword.book.sword.Backend#readToOsis(org.crosswire.jsword.passage.Key, org.crosswire.jsword.book.sword.processing.RawTextToXmlProcessor)
 168  
      */
 169  
     /* (non-Javadoc)
 170  
      * @see org.crosswire.jsword.book.sword.AbstractBackend#getRawText(org.crosswire.jsword.passage.Key)
 171  
      */
 172  
     public List<Content> readToOsis(Key key, RawTextToXmlProcessor processor) throws BookException {
 173  
 
 174  0
         final List<Content> content = new ArrayList<Content>();
 175  
 
 176  0
         T openFileState = null;
 177  
 
 178  
         try {
 179  0
             openFileState = initState();
 180  0
             switch (this.bmd.getKeyType()) {
 181  
                 case LIST:
 182  0
                     readNormalOsis(key, processor, content, openFileState);
 183  0
                     break;
 184  
                 case TREE:
 185  0
                     readNormalOsisSingleKey(key, processor, content, openFileState);
 186  0
                     break;
 187  
                 case VERSE:
 188  0
                     readPassageOsis(key, processor, content, openFileState);
 189  0
                     break;
 190  
                 default:
 191  0
                     throw new BookException("Book has unsupported type of key");
 192  
             }
 193  
 
 194  0
             return content;
 195  
         } finally {
 196  0
             OpenFileStateManager.instance().release(openFileState);
 197  
         }
 198  
     }
 199  
 
 200  
     private void readNormalOsis(Key key, RawTextToXmlProcessor processor, List<Content> content, T openFileState) throws BookException {
 201  
         // simply lookup the key and process the relevant information
 202  0
         Iterator<Key> iterator = key.iterator();
 203  
 
 204  0
         while (iterator.hasNext()) {
 205  0
             Key next = iterator.next();
 206  
             String rawText;
 207  
             try {
 208  0
                 rawText = readRawContent(openFileState, next);
 209  0
                 processor.postVerse(next, content, rawText);
 210  0
             } catch (IOException e) {
 211  
                 // failed to process key 'next'
 212  0
                 throwFailedKeyException(key, next, e);
 213  0
             }
 214  0
         }
 215  0
     }
 216  
 
 217  
     /**
 218  
      * Avoid using iterator for GenBook TreeKeys which would cause a GenBook nodes children to be appended to their parent 
 219  
      * e.g. the top level page would include the whole book and result in OOM error 
 220  
      */
 221  
     private void readNormalOsisSingleKey(Key key, RawTextToXmlProcessor processor, List<Content> content, T openFileState) throws BookException {
 222  
         String rawText;
 223  
         try {
 224  0
             rawText = readRawContent(openFileState, key);
 225  0
             processor.postVerse(key, content, rawText);
 226  0
         } catch (IOException e) {
 227  
             // failed to process key
 228  0
             throwFailedKeyException(key, key, e);
 229  0
         }
 230  0
     }
 231  
 
 232  
     /**
 233  
      * Reads a passage as OSIS
 234  
      *
 235  
      * @param key           the given key
 236  
      * @param processor     a processor for which to do things with
 237  
      * @param content       a list of content to be appended to (i.e. the OSIS data)
 238  
      * @param openFileState the open file state, from which we read things
 239  
      * @throws BookException a book exception if we failed to read the book
 240  
      */
 241  
     private Verse readPassageOsis(Key key, RawTextToXmlProcessor processor, final List<Content> content, T openFileState) throws BookException {
 242  0
         Verse currentVerse = null;
 243  0
         final Passage ref = KeyUtil.getPassage(key);
 244  0
         final Iterator<VerseRange> rit = ref.rangeIterator(RestrictionType.CHAPTER);
 245  0
         while (rit.hasNext()) {
 246  0
             VerseRange range = rit.next();
 247  0
             processor.preRange(range, content);
 248  
 
 249  
             // FIXME(CJB): can this now be optimized since we can calculate
 250  
             // the buffer size of what to read?
 251  
             // now iterate through all verses in range
 252  0
             for (Key verseInRange : range) {
 253  0
                 currentVerse = KeyUtil.getVerse(verseInRange);
 254  
                 try {
 255  0
                     String rawText = readRawContent(openFileState, currentVerse);
 256  0
                     processor.postVerse(verseInRange, content, rawText);
 257  0
                 } catch (IOException e) {
 258  
                     //some versifications have more verses than modules contain - so can't throw
 259  
                     //an error here...
 260  0
                     LOGGER.debug(e.getMessage(), e);
 261  0
                 }
 262  
             }
 263  0
         }
 264  
 
 265  0
         return currentVerse;
 266  
     }
 267  
 
 268  
     /**
 269  
      * If non-null, currentKey is used to throw the exception, other, masterKey
 270  
      * is used instead, which will be more general.
 271  
      *
 272  
      * @param masterKey
 273  
      *            the key containing currentKey
 274  
      * @param currentKey
 275  
      *            the currentKey
 276  
      * @param e
 277  
      *            the exception that occured
 278  
      * @throws BookException
 279  
      *             always thrown, a {@link BookException}
 280  
      */
 281  
     private void throwFailedKeyException(Key masterKey, Key currentKey, IOException e) throws BookException {
 282  
         // TRANSLATOR: Common error condition: The file could not be read.
 283  
         // There can be many reasons.
 284  
         // {0} is a placeholder for the key.
 285  0
         if (currentKey == null) {
 286  0
             throw new BookException(JSMsg.gettext("Error reading {0}", masterKey.getName()), e);
 287  
         }
 288  0
         throw new BookException(JSMsg.gettext("Error reading {0}", currentKey.getName()), e);
 289  
     }
 290  
 
 291  
     /* (non-Javadoc)
 292  
      * @see org.crosswire.jsword.book.sword.Backend#create()
 293  
      */
 294  
     public void create() throws IOException, BookException {
 295  0
         File dataPath = new File(SwordUtil.getExpandedDataPath(getBookMetaData()));
 296  0
         if (!dataPath.exists() && !dataPath.mkdirs()) {
 297  0
             throw new IOException("Unable to create module data path!");
 298  
         }
 299  0
     }
 300  
 
 301  
     /* (non-Javadoc)
 302  
      * @see org.crosswire.jsword.book.sword.Backend#isSupported()
 303  
      */
 304  
     public boolean isSupported() {
 305  0
         return true;
 306  
     }
 307  
 
 308  
     /* (non-Javadoc)
 309  
      * @see org.crosswire.jsword.book.sword.Backend#isWritable()
 310  
      */
 311  
     public boolean isWritable() {
 312  0
         return false;
 313  
     }
 314  
 
 315  
     private SwordBookMetaData bmd;
 316  0
     private static final Logger LOGGER = LoggerFactory.getLogger(AbstractBackend.class);
 317  
 }