DataEntry.java |
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 public DataEntry(String name, byte[] data, String charset) { 49 this.name = name; 50 this.data = data.clone(); 51 this.charset = charset; 52 // The key always ends with \n, typically \r\n 53 this.keyEnd = SwordUtil.findByte(this.data, SEP_NL); 54 } 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 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 return charset; 71 } 72 73 /** 74 * Get the key from this DataEntry. 75 * 76 * @return the key 77 */ 78 public String getKey() { 79 if (key == null) { 80 // Some entries are empty 81 if (data.length == 0) { 82 key = ""; 83 return key; 84 } 85 86 if (keyEnd < 0) { 87 key = ""; 88 return key; 89 } 90 91 int end = keyEnd; 92 // remove trailing \r if present 93 if (end > 0 && data[end - 1] == SEP_CR) { 94 --end; 95 } 96 97 // for some weird reason plain text dictionaries 98 // all get \ added to the ends of the index entries. 99 if (end > 0 && data[end - 1] == SEP_BSLASH) { 100 --end; 101 } 102 103 // If the end is 0 then we have an empty key. 104 if (end == 0) { 105 key = ""; 106 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 key = SwordUtil.decode(name, data, end, charset); 112 } 113 114 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 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 int linkStart = keyEnd + 6; 142 int len = getLinkEnd() - linkStart + 1; 143 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 int textStart = keyEnd + 1; 155 cipher(cipherKey, textStart); 156 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 int start = keyEnd + 1; 166 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 if (linkEnd == 0) { 177 linkEnd = SwordUtil.findByte(data, keyEnd + 1, SEP_NL); 178 if (linkEnd == -1) { 179 linkEnd = data.length - 1; 180 } 181 } 182 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 if (cipherKey != null && cipherKey.length > 0) { 195 Sapphire cipherEngine = new Sapphire(cipherKey); 196 for (int i = offset; i < data.length; i++) { 197 data[i] = cipherEngine.cipher(data[i]); 198 } 199 // destroy any evidence! 200 cipherEngine.burn(); 201 } 202 } 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 } 241