Coverage Report - org.crosswire.jsword.book.sword.DataEntry
 
Classes in this File Line Coverage Branch Coverage Complexity
DataEntry
0%
0/45
0%
0/38
3.1
 
 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, 2008 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.jsword.book.sword;
 21  
 
 22  
 import org.crosswire.common.crypt.Sapphire;
 23  
 
 24  
 /**
 25  
  * Data entry represents an entry in a Data file. The entry consists of a key
 26  
  * and an optional payload.
 27  
  * <p>The payload may be:</p>
 28  
  * <ul>
 29  
  * <li>the content, that is raw text</li>
 30  
  * <li>an alias (@LINK) for another entry</li>
 31  
  * <li>a block locator</li>
 32  
  * </ul>
 33  
  * 
 34  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 35  
  * @author DM Smith
 36  
  */
 37  
 public class DataEntry {
 38  
     /**
 39  
      * Construct a data entry.
 40  
      * 
 41  
      * @param name
 42  
      *            A name used for diagnostics.
 43  
      * @param data
 44  
      *            The data for this entry.
 45  
      * @param charset
 46  
      *            The character encoding for this entry.
 47  
      */
 48  0
     public DataEntry(String name, byte[] data, String charset) {
 49  0
         this.name = name;
 50  0
         this.data = data.clone();
 51  0
         this.charset = charset;
 52  
         // The key always ends with \n, typically \r\n
 53  0
         this.keyEnd = SwordUtil.findByte(this.data, SEP_NL);
 54  0
     }
 55  
 
 56  
     /**
 57  
      * Get the name, that is, the diagnostic label, for this DataEntry.
 58  
      * 
 59  
      * @return the diagnostic name.
 60  
      */
 61  
     public String getName() {
 62  0
         return name;
 63  
     }
 64  
 
 65  
     /**
 66  
      * Get the charset in which the data is encoded.
 67  
      * @return this entry's charset
 68  
      */
 69  
     public String getCharset() {
 70  0
         return charset;
 71  
     }
 72  
 
 73  
     /**
 74  
      * Get the key from this DataEntry.
 75  
      * 
 76  
      * @return the key
 77  
      */
 78  
     public String getKey() {
 79  0
         if (key == null) {
 80  
             // Some entries are empty
 81  0
             if (data.length == 0) {
 82  0
                 key = "";
 83  0
                 return key;
 84  
             }
 85  
 
 86  0
             if (keyEnd < 0) {
 87  0
                 key = "";
 88  0
                 return key;
 89  
             }
 90  
 
 91  0
             int end = keyEnd;
 92  
             // remove trailing \r if present
 93  0
             if (end > 0 && data[end - 1] == SEP_CR) {
 94  0
                 --end;
 95  
             }
 96  
 
 97  
             // for some weird reason plain text dictionaries
 98  
             // all get \ added to the ends of the index entries.
 99  0
             if (end > 0 && data[end - 1] == SEP_BSLASH) {
 100  0
                 --end;
 101  
             }
 102  
 
 103  
             // If the end is 0 then we have an empty key.
 104  0
             if (end == 0) {
 105  0
                 key = "";
 106  0
                 return key;
 107  
             }
 108  
 
 109  
             // The key may have whitespace, including \r on the end,
 110  
             // that is not actually part of the key.
 111  0
             key = SwordUtil.decode(name, data, end, charset);
 112  
         }
 113  
 
 114  0
         return key;
 115  
     }
 116  
 
 117  
     /**
 118  
      * Determine whether this entry is an alias for another.
 119  
      * 
 120  
      * @return whether this is an alias entry
 121  
      */
 122  
     public boolean isLinkEntry() {
 123  
         // 6 represents the length of "@LINK" when keyEnd is -1
 124  0
         return keyEnd + 6 < data.length
 125  
                 && data[keyEnd + 1] == '@'
 126  
                 && data[keyEnd + 2] == 'L'
 127  
                 && data[keyEnd + 3] == 'I'
 128  
                 && data[keyEnd + 4] == 'N'
 129  
                 && data[keyEnd + 5] == 'K';
 130  
     }
 131  
 
 132  
     /**
 133  
      * Get the link target for this entry. One entry can be chained to another.
 134  
      * If the entry is not linked then it is an error to call this method.
 135  
      * 
 136  
      * @return the key to look up
 137  
      * @see #isLinkEntry()
 138  
      */
 139  
     public String getLinkTarget() {
 140  
         // 6 represents the length of "@LINK" + 1 to skip the last separator.
 141  0
         int linkStart = keyEnd + 6;
 142  0
         int len = getLinkEnd() - linkStart + 1;
 143  0
         return SwordUtil.decode(name, data, linkStart, len, charset).trim();
 144  
     }
 145  
 
 146  
     /**
 147  
      * Get the raw text from this entry.
 148  
      * 
 149  
      * @param cipherKey
 150  
      *            the key, if any, to (un)lock the text
 151  
      * @return the raw text
 152  
      */
 153  
     public String getRawText(byte[] cipherKey) {
 154  0
         int textStart = keyEnd + 1;
 155  0
         cipher(cipherKey, textStart);
 156  0
         return SwordUtil.decode(name, data, textStart, data.length - textStart, charset).trim();
 157  
     }
 158  
 
 159  
     /**
 160  
      * Get the block start and entry position.
 161  
      * 
 162  
      * @return the index of the block
 163  
      */
 164  
     public DataIndex getBlockIndex() {
 165  0
         int start = keyEnd + 1;
 166  0
         return new DataIndex(SwordUtil.decodeLittleEndian32(data, start), SwordUtil.decodeLittleEndian32(data, start + 4));
 167  
     }
 168  
 
 169  
     /**
 170  
      * Get the position of the second \n in the data. This represents the end of
 171  
      * the link and the start of the rest of the data.
 172  
      * 
 173  
      * @return the end of the link or -1 if not found.
 174  
      */
 175  
     private int getLinkEnd() {
 176  0
         if (linkEnd == 0) {
 177  0
             linkEnd = SwordUtil.findByte(data, keyEnd + 1, SEP_NL);
 178  0
             if (linkEnd == -1) {
 179  0
                 linkEnd = data.length - 1;
 180  
             }
 181  
         }
 182  0
         return linkEnd;
 183  
     }
 184  
 
 185  
     /**
 186  
      * Decipher/Encipher the data in place, if there is a cipher key.
 187  
      * 
 188  
      * @param cipherKey
 189  
      *            the key to the cipher
 190  
      * @param offset
 191  
      *            the start of the cipher data
 192  
      */
 193  
     public void cipher(byte[] cipherKey, int offset) {
 194  0
         if (cipherKey != null && cipherKey.length > 0) {
 195  0
             Sapphire cipherEngine = new Sapphire(cipherKey);
 196  0
             for (int i = offset; i < data.length; i++) {
 197  0
                 data[i] = cipherEngine.cipher(data[i]);
 198  
             }
 199  
             // destroy any evidence!
 200  0
             cipherEngine.burn();
 201  
         }
 202  0
     }
 203  
 
 204  
     /**
 205  
      * Used to separate the key name from the key value Note: it may be \r\n or
 206  
      * just \n, so only need \n. ^M=CR=13=0x0d=\r ^J=LF=10=0x0a=\n
 207  
      */
 208  
     private static final byte SEP_NL = (byte) '\n'; // 10;
 209  
     private static final byte SEP_CR = (byte) '\r'; // 13;
 210  
     private static final byte SEP_BSLASH = (byte) '\\'; // 92;
 211  
     /**
 212  
      * A diagnostic name.
 213  
      */
 214  
     private String name;
 215  
 
 216  
     /**
 217  
      * The data entry as it comes out of the data file.
 218  
      */
 219  
     private byte[] data;
 220  
 
 221  
     /**
 222  
      * The character set of the data entry.
 223  
      */
 224  
     private String charset;
 225  
 
 226  
     /**
 227  
      * The key in the data entry.
 228  
      */
 229  
     private String key;
 230  
 
 231  
     /**
 232  
      * The index of the separator between the key and the payload.
 233  
      */
 234  
     private int keyEnd;
 235  
 
 236  
     /**
 237  
      * The index of the separator between the link and the payload.
 238  
      */
 239  
     private int linkEnd;
 240  
 }