1
22 package org.crosswire.jsword.book.sword;
23
24 import java.io.File;
25 import java.io.FileNotFoundException;
26 import java.io.IOException;
27 import java.io.RandomAccessFile;
28 import java.net.URI;
29
30 import org.crosswire.common.activate.Activator;
31 import org.crosswire.common.activate.Lock;
32 import org.crosswire.common.util.FileUtil;
33 import org.crosswire.common.util.Logger;
34 import org.crosswire.common.util.NetUtil;
35 import org.crosswire.common.util.Reporter;
36 import org.crosswire.jsword.JSMsg;
37 import org.crosswire.jsword.JSOtherMsg;
38 import org.crosswire.jsword.book.BookException;
39 import org.crosswire.jsword.passage.Key;
40 import org.crosswire.jsword.passage.KeyUtil;
41 import org.crosswire.jsword.passage.Verse;
42 import org.crosswire.jsword.versification.Testament;
43 import org.crosswire.jsword.versification.Versification;
44 import org.crosswire.jsword.versification.system.Versifications;
45
46
54 public class RawBackend extends AbstractBackend {
55
58 public RawBackend(SwordBookMetaData sbmd, int datasize) {
59 super(sbmd);
60 this.datasize = datasize;
61 this.entrysize = OFFSETSIZE + datasize;
62
63 assert datasize == 2 || datasize == 4;
64 }
65
66
69 @Override
70 public boolean contains(Key key) {
71 checkActive();
72
73 Verse verse = KeyUtil.getVerse(key);
74
75 try {
76 String v11nName = getBookMetaData().getProperty(ConfigEntryType.VERSIFICATION).toString();
77 Versification v11n = Versifications.instance().getVersification(v11nName);
78 int index = v11n.getOrdinal(verse);
79 Testament testament = v11n.getTestament(index);
80 index = v11n.getTestamentOrdinal(index);
81 RandomAccessFile idxRaf = otIdxRaf;
82 if (testament == Testament.NEW) {
83 idxRaf = ntIdxRaf;
84 }
85
86 if (idxRaf == null) {
88 return false;
89 }
90
91 DataIndex dataIndex = getIndex(idxRaf, index);
92
93 return dataIndex.getSize() > 0;
94 } catch (IOException ex) {
95 return false;
96 }
97 }
98
99
102 @Override
103 public String getRawText(Key key) throws BookException {
104 checkActive();
105
106 Verse verse = KeyUtil.getVerse(key);
107 try {
108 String v11nName = getBookMetaData().getProperty(ConfigEntryType.VERSIFICATION).toString();
109 Versification v11n = Versifications.instance().getVersification(v11nName);
110 int index = v11n.getOrdinal(verse);
111 Testament testament = v11n.getTestament(index);
112 index = v11n.getTestamentOrdinal(index);
113 RandomAccessFile idxRaf = otIdxRaf;
114 if (testament == Testament.NEW) {
115 idxRaf = ntIdxRaf;
116 }
117
118 if (idxRaf == null) {
120 return "";
121 }
122
123 return getEntry(key.getName(), testament, index);
124 } catch (IOException ex) {
125 throw new BookException(JSMsg.gettext("Error reading {0}", verse.getName()), ex);
128 }
129 }
130
131
134 @Override
135 public void setRawText(Key key, String text) throws BookException, IOException {
136 }
137
138
141 @Override
142 public boolean isWritable() {
143 if (otIdxFile.canRead() && (otIdxFile.canWrite() || !otTxtFile.canWrite())) {
148 return false;
149 }
150 if (ntIdxFile.canRead() && (ntIdxFile.canWrite() || !ntTxtFile.canWrite())) {
151 return false;
152 }
153 return otIdxFile.canRead() || ntIdxFile.canRead();
154 }
155
156
159 @Override
160 public void setAliasKey(Key alias, Key source) throws IOException {
161 throw new UnsupportedOperationException();
162 }
163
164
167 public final void activate(Lock lock) {
168 URI path = null;
169 try {
170 path = getExpandedDataPath();
171 } catch (BookException e) {
172 Reporter.informUser(this, e);
173 return;
174 }
175
176 URI otPath = NetUtil.lengthenURI(path, File.separator + SwordConstants.FILE_OT);
177 otTxtFile = new File(otPath.getPath());
178 otIdxFile = new File(otPath.getPath() + SwordConstants.EXTENSION_VSS);
179
180 URI ntPath = NetUtil.lengthenURI(path, File.separator + SwordConstants.FILE_NT);
181 ntTxtFile = new File(ntPath.getPath());
182 ntIdxFile = new File(ntPath.getPath() + SwordConstants.EXTENSION_VSS);
183
184 if (!otTxtFile.canRead() && !ntTxtFile.canRead()) {
186 Reporter.informUser(this, new BookException(JSOtherMsg.lookupText("Missing data files for old and new testaments in {0}.", path)));
187 return;
188 }
189
190 String fileMode = isWritable() ? FileUtil.MODE_WRITE : FileUtil.MODE_READ;
191
192 if (otIdxFile.canRead()) {
193 try {
194 otIdxRaf = new RandomAccessFile(otIdxFile, fileMode);
195 otTxtRaf = new RandomAccessFile(otTxtFile, fileMode);
196 } catch (FileNotFoundException ex) {
197 assert false : ex;
198 log.error("Could not open OT", ex);
199 ntIdxRaf = null;
200 ntTxtRaf = null;
201 }
202 }
203
204 if (ntIdxFile.canRead()) {
205 try {
206 ntIdxRaf = new RandomAccessFile(ntIdxFile, fileMode);
207 ntTxtRaf = new RandomAccessFile(ntTxtFile, fileMode);
208 } catch (FileNotFoundException ex) {
209 assert false : ex;
210 log.error("Could not open NT", ex);
211 ntIdxRaf = null;
212 ntTxtRaf = null;
213 }
214 }
215
216 active = true;
217 }
218
219
222 public final void deactivate(Lock lock) {
223 try {
224 otIdxRaf.close();
225 otTxtRaf.close();
226
227 ntIdxRaf.close();
228 ntTxtRaf.close();
229 } catch (IOException ex) {
230 log.error("Failed to close files", ex);
231 } finally {
232 otIdxRaf = null;
233 otTxtRaf = null;
234
235 ntIdxRaf = null;
236 ntTxtRaf = null;
237 }
238
239 active = false;
240 }
241
242
245 protected final void checkActive() {
246 if (!active) {
247 Activator.activate(this);
248 }
249 }
250
251
258 protected DataIndex getIndex(RandomAccessFile raf, long entry) throws IOException {
259 byte[] buffer = SwordUtil.readRAF(raf, entry * entrysize, entrysize);
261 if (buffer == null || buffer.length == 0) {
262 return new DataIndex(0, 0);
263 }
264
265 int entryOffset = SwordUtil.decodeLittleEndian32(buffer, 0);
266 int entrySize = -1;
267 switch (datasize) {
268 case 2:
269 entrySize = SwordUtil.decodeLittleEndian16(buffer, 4);
270 break;
271 case 4:
272 entrySize = SwordUtil.decodeLittleEndian32(buffer, 4);
273 break;
274 default:
275 assert false : datasize;
276 }
277 return new DataIndex(entryOffset, entrySize);
278 }
279
280
293 protected String getEntry(String name, Testament testament, long index) throws IOException {
294 RandomAccessFile idxRaf = otIdxRaf;
295 RandomAccessFile txtRaf = otTxtRaf;
296 if (testament == Testament.NEW) {
297 idxRaf = ntIdxRaf;
298 txtRaf = ntTxtRaf;
299 }
300
301 DataIndex dataIndex = getIndex(idxRaf, index);
302
303 int size = dataIndex.getSize();
304 if (size == 0) {
305 return "";
306 }
307
308 if (size < 0) {
309 log.error("In " + getBookMetaData().getInitials() + ": Verse " + name + " has a bad index size of " + size);
310 return "";
311 }
312
313 byte[] data = SwordUtil.readRAF(txtRaf, dataIndex.getOffset(), size);
314
315 decipher(data);
316
317 return SwordUtil.decode(name, data, getBookMetaData().getBookCharset());
318 }
319
320
323 protected boolean active;
324
325
328 protected int datasize;
329
330
333 protected int entrysize;
334
335 protected RandomAccessFile otIdxRaf;
336 protected RandomAccessFile otTxtRaf;
337 protected File otIdxFile;
338 protected File otTxtFile;
339
340 protected RandomAccessFile ntIdxRaf;
341 protected RandomAccessFile ntTxtRaf;
342 protected File ntIdxFile;
343 protected File ntTxtFile;
344
345
348 protected static final int OFFSETSIZE = 4;
349
350
353 private static final Logger log = Logger.getLogger(RawBackend.class);
354 }
355