Coverage Report - org.crosswire.jsword.book.sword.RawBackend
 
Classes in this File Line Coverage Branch Coverage Complexity
RawBackend
0%
0/103
0%
0/47
4.333
 
 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.IOException;
 23  
 import java.io.RandomAccessFile;
 24  
 
 25  
 import org.crosswire.jsword.JSMsg;
 26  
 import org.crosswire.jsword.book.BookException;
 27  
 import org.crosswire.jsword.book.BookMetaData;
 28  
 import org.crosswire.jsword.book.sword.state.OpenFileStateManager;
 29  
 import org.crosswire.jsword.book.sword.state.RawBackendState;
 30  
 import org.crosswire.jsword.passage.BitwisePassage;
 31  
 import org.crosswire.jsword.passage.Key;
 32  
 import org.crosswire.jsword.passage.KeyUtil;
 33  
 import org.crosswire.jsword.passage.RocketPassage;
 34  
 import org.crosswire.jsword.passage.Verse;
 35  
 import org.crosswire.jsword.versification.Testament;
 36  
 import org.crosswire.jsword.versification.Versification;
 37  
 import org.crosswire.jsword.versification.system.Versifications;
 38  
 import org.slf4j.Logger;
 39  
 import org.slf4j.LoggerFactory;
 40  
 
 41  
 /**
 42  
  * Both Books and Commentaries seem to use the same format so this class
 43  
  * abstracts out the similarities.
 44  
  * 
 45  
  * @param <T> The type of the RawBackendState that this class extends.
 46  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 47  
  * @author Joe Walker
 48  
  */
 49  0
 public class RawBackend<T extends RawBackendState> extends AbstractBackend<RawBackendState> {
 50  
 
 51  
     /**
 52  
      * Simple ctor
 53  
      * 
 54  
      * @param sbmd 
 55  
      * @param datasize 
 56  
      */
 57  
     public RawBackend(SwordBookMetaData sbmd, int datasize) {
 58  0
         super(sbmd);
 59  0
         this.datasize = datasize;
 60  0
         this.entrysize = OFFSETSIZE + datasize;
 61  
 
 62  0
         assert datasize == 2 || datasize == 4;
 63  0
     }
 64  
 
 65  
     /* (non-Javadoc)
 66  
      * @see org.crosswire.jsword.book.sword.AbstractBackend#contains(org.crosswire.jsword.passage.Key)
 67  
      */
 68  
     @Override
 69  
     public boolean contains(Key key) {
 70  0
         return getRawTextLength(key) > 0;
 71  
     }
 72  
 
 73  
     /* (non-Javadoc)
 74  
      * @see org.crosswire.jsword.book.sword.AbstractBackend#size(org.crosswire.jsword.passage.Key)
 75  
      */
 76  
     @Override
 77  
     public int getRawTextLength(Key key) {
 78  0
         String v11nName = getBookMetaData().getProperty(BookMetaData.KEY_VERSIFICATION);
 79  0
         Versification v11n = Versifications.instance().getVersification(v11nName);
 80  0
         Verse verse = KeyUtil.getVerse(key);
 81  
 
 82  0
         RawBackendState initState = null;
 83  
         try {
 84  0
             int index = verse.getOrdinal();
 85  0
             Testament testament = v11n.getTestament(index);
 86  0
             index = v11n.getTestamentOrdinal(index);
 87  0
             initState = initState();
 88  0
             RandomAccessFile idxRaf = initState.getIdxRaf(testament);
 89  
 
 90  
             // If this is a single testament Bible, return nothing.
 91  0
             if (idxRaf == null) {
 92  0
                 return 0;
 93  
             }
 94  
 
 95  0
             DataIndex dataIndex = getIndex(idxRaf, index);
 96  
 
 97  0
             return dataIndex.getSize();
 98  0
         } catch (IOException ex) {
 99  0
             return 0;
 100  0
         } catch (BookException e) {
 101  0
             return 0;
 102  
         } finally {
 103  0
             OpenFileStateManager.instance().release(initState);
 104  
         }
 105  
     }
 106  
 
 107  
     @Override
 108  
     public Key getGlobalKeyList() throws BookException {
 109  0
         RawBackendState rafBook = null;
 110  
         try {
 111  0
             rafBook = initState();
 112  
 
 113  0
             String v11nName = getBookMetaData().getProperty(BookMetaData.KEY_VERSIFICATION);
 114  0
             Versification v11n = Versifications.instance().getVersification(v11nName);
 115  
 
 116  0
             Testament[] testaments = new Testament[] {
 117  
                     Testament.OLD, Testament.NEW
 118  
             };
 119  
 
 120  0
             BitwisePassage passage = new RocketPassage(v11n);
 121  0
             passage.raiseEventSuppresion();
 122  0
             passage.raiseNormalizeProtection();
 123  
 
 124  0
             for (Testament currentTestament : testaments) {
 125  0
                 RandomAccessFile idxRaf = rafBook.getIdxRaf(currentTestament);
 126  
 
 127  
                 // If Bible does not contain the desired testament, then false
 128  0
                 if (idxRaf == null) {
 129  
                     // no keys in this testament
 130  0
                     continue;
 131  
                 }
 132  
 
 133  0
                 int maxIndex = v11n.getCount(currentTestament) - 1;
 134  
 
 135  
                 // Read in the whole index, a few hundred Kb at most.
 136  0
                 byte[] temp = SwordUtil.readRAF(idxRaf, 0, entrysize * maxIndex);
 137  
 
 138  
                 // For each entry of entrysize bytes, the length of the verse in bytes
 139  
                 // is in the last datasize bytes. If all bytes are 0, then there is no content.
 140  0
                 if (datasize == 2) {
 141  0
                     for (int ii = 0; ii < temp.length; ii += entrysize) {
 142  
                         // This can be simplified to temp[ii + 4] == 0 && temp[ii + 5] == 0.
 143  
                         // int verseSize = SwordUtil.decodeLittleEndian16(temp, ii + 4);
 144  
                         // if (verseSize > 0) {
 145  0
                         if (temp[ii + 4] != 0 || temp[ii + 5] != 0) {
 146  0
                             int ordinal = ii / entrysize;
 147  0
                             passage.addVersifiedOrdinal(v11n.getOrdinal(currentTestament, ordinal));
 148  
                         }
 149  
                     }
 150  
                 } else { // datasize == 4
 151  0
                     for (int ii = 0; ii < temp.length; ii += entrysize) {
 152  
                         // This can be simplified to temp[ii + 4] == 0 && temp[ii + 5] == 0 && temp[ii + 6] == 0 && temp[ii + 7] == 0.
 153  
                         // int verseSize = SwordUtil.decodeLittleEndian32(temp, ii + 4);
 154  
                         // if (verseSize > 0) {
 155  0
                         if (temp[ii + 4] != 0 || temp[ii + 5] != 0 || temp[ii + 6] != 0 || temp[ii + 7] != 0) {
 156  0
                             int ordinal = ii / entrysize;
 157  0
                             passage.addVersifiedOrdinal(v11n.getOrdinal(currentTestament, ordinal));
 158  
                         }
 159  
                     }
 160  
                 }
 161  
             }
 162  
 
 163  0
             passage.lowerNormalizeProtection();
 164  0
             passage.lowerEventSuppressionAndTest();
 165  
 
 166  0
             return passage;
 167  0
         } catch (IOException e) {
 168  0
             throw new BookException(JSMsg.gettext("Unable to read key list from book."));
 169  
         } finally {
 170  0
             OpenFileStateManager.instance().release(rafBook);
 171  
         }
 172  
     }
 173  
 
 174  
     public T initState() throws BookException {
 175  0
         return (T) OpenFileStateManager.instance().getRawBackendState(getBookMetaData());
 176  
     }
 177  
 
 178  
     public String getRawText(RawBackendState state, Key key) throws IOException {
 179  0
         return readRawContent(state, key);
 180  
     }
 181  
 
 182  
     /* (non-Javadoc)
 183  
      * @see org.crosswire.jsword.book.sword.AbstractBackend#getRawText(org.crosswire.jsword.passage.Key)
 184  
      */
 185  
     public String readRawContent(RawBackendState state, Key key) throws IOException {
 186  0
         String v11nName = getBookMetaData().getProperty(BookMetaData.KEY_VERSIFICATION);
 187  0
         Versification v11n = Versifications.instance().getVersification(v11nName);
 188  0
         Verse verse = KeyUtil.getVerse(key);
 189  
 
 190  0
         int index = verse.getOrdinal();
 191  
 
 192  0
         Testament testament = v11n.getTestament(index);
 193  0
         index = v11n.getTestamentOrdinal(index);
 194  0
         RawBackendState initState = null;
 195  
         try {
 196  0
             initState = initState();
 197  0
             return getEntry(state, verse.getName(), testament, index);
 198  0
         } catch (BookException e) {
 199  0
             return "";
 200  
         } finally {
 201  0
             OpenFileStateManager.instance().release(initState);
 202  
         }
 203  
     }
 204  
 
 205  
     /* (non-Javadoc)
 206  
      * @see org.crosswire.jsword.book.sword.AbstractBackend#setRawText(org.crosswire.jsword.passage.Key, java.lang.String)
 207  
      */
 208  
     public void setRawText(RawBackendState state, Key key, String text) throws BookException, IOException {
 209  0
     }
 210  
 
 211  
     /* (non-Javadoc)
 212  
      * @see org.crosswire.jsword.book.sword.AbstractBackend#isWritable()
 213  
      */
 214  
     @Override
 215  
     public boolean isWritable() {
 216  0
         RawBackendState rawBackendState = null;
 217  
         try {
 218  0
         rawBackendState = initState();
 219  0
         return rawBackendState.isWritable();
 220  0
         } catch (BookException e) {
 221  0
             return false;
 222  
         } finally {
 223  0
             OpenFileStateManager.instance().release(rawBackendState);
 224  
         }
 225  
     }
 226  
 
 227  
     /* (non-Javadoc)
 228  
      * @see org.crosswire.jsword.book.sword.AbstractBackend#setAliasKey(org.crosswire.jsword.passage.Key, org.crosswire.jsword.passage.Key)
 229  
      */
 230  
     public void setAliasKey(RawBackendState state, Key alias, Key source) throws IOException {
 231  0
         throw new UnsupportedOperationException();
 232  
     }
 233  
 
 234  
     /**
 235  
      * Get the Index (that is offset and size) for an entry.
 236  
      * 
 237  
      * @param entry
 238  
      * @return the index for the entry
 239  
      * @throws IOException
 240  
      */
 241  
     protected DataIndex getIndex(RandomAccessFile raf, long entry) throws IOException {
 242  
         // Read the offset and size for this key from the index
 243  0
         byte[] buffer = SwordUtil.readRAF(raf, entry * entrysize, entrysize);
 244  0
         if (buffer == null || buffer.length == 0) {
 245  0
             return new DataIndex(0, 0);
 246  
         }
 247  
 
 248  0
         int entryOffset = SwordUtil.decodeLittleEndian32(buffer, 0);
 249  0
         int entrySize = -1;
 250  0
         switch (datasize) {
 251  
         case 2:
 252  0
             entrySize = SwordUtil.decodeLittleEndian16(buffer, 4);
 253  0
             break;
 254  
         case 4:
 255  0
             entrySize = SwordUtil.decodeLittleEndian32(buffer, 4);
 256  0
             break;
 257  
         default:
 258  0
             assert false : datasize;
 259  
         }
 260  0
         return new DataIndex(entryOffset, entrySize);
 261  
     }
 262  
 
 263  
     /**
 264  
      * Get the text for an indexed entry in the book.
 265  
      * @param state 
 266  
      * 
 267  
      * @param index
 268  
      *            the entry to get
 269  
      * @param name
 270  
      *            name of the entry
 271  
      * @param testament
 272  
      *            the testament for the entry
 273  
      * @return the text for the entry.
 274  
      * @throws IOException
 275  
      *             on a IO problem
 276  
      */
 277  
     protected String getEntry(RawBackendState state, String name, Testament testament, long index) throws IOException {
 278  
         final RandomAccessFile idxRaf;
 279  
         final RandomAccessFile txtRaf;
 280  0
         idxRaf = state.getIdxRaf(testament);
 281  0
         txtRaf = state.getTextRaf(testament);
 282  
 
 283  
         // It may be that this is a single testament Bible
 284  0
         if (idxRaf == null) {
 285  0
             return "";
 286  
         }
 287  
 
 288  0
         DataIndex dataIndex = getIndex(idxRaf, index);
 289  
 
 290  0
         int size = dataIndex.getSize();
 291  0
         if (size == 0) {
 292  0
             return "";
 293  
         }
 294  
 
 295  0
         if (size < 0) {
 296  0
             log.error("In {}: Verse {} has a bad index size of {}", getBookMetaData().getInitials(), name, Integer.toString(size));
 297  0
             return "";
 298  
         }
 299  
 
 300  0
         byte[] data = SwordUtil.readRAF(txtRaf, dataIndex.getOffset(), size);
 301  
 
 302  0
         decipher(data);
 303  
 
 304  0
         return SwordUtil.decode(name, data, getBookMetaData().getBookCharset());
 305  
     }
 306  
 
 307  
     /**
 308  
      * How many bytes in the size count in the index
 309  
      */
 310  
     protected final int datasize;
 311  
 
 312  
     /**
 313  
      * The number of bytes for each entry in the index: either 6 or 8
 314  
      */
 315  
     protected final int entrysize;
 316  
 
 317  
     /**
 318  
      * How many bytes in the offset pointers in the index
 319  
      */
 320  
     protected static final int OFFSETSIZE = 4;
 321  
 
 322  
     /**
 323  
      * The log stream
 324  
      */
 325  0
     private static final Logger log = LoggerFactory.getLogger(RawBackend.class);
 326  
 
 327  
 
 328  
 }