| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| DataEntry |
|
| 3.1;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 | } |