1
20 package org.crosswire.jsword.book.sword;
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Iterator;
26 import java.util.List;
27
28 import org.crosswire.common.crypt.Sapphire;
29 import org.crosswire.jsword.JSMsg;
30 import org.crosswire.jsword.JSOtherMsg;
31 import org.crosswire.jsword.book.BookException;
32 import org.crosswire.jsword.book.BookMetaData;
33 import org.crosswire.jsword.book.sword.processing.RawTextToXmlProcessor;
34 import org.crosswire.jsword.book.sword.state.OpenFileState;
35 import org.crosswire.jsword.book.sword.state.OpenFileStateManager;
36 import org.crosswire.jsword.passage.Key;
37 import org.crosswire.jsword.passage.KeyUtil;
38 import org.crosswire.jsword.passage.Passage;
39 import org.crosswire.jsword.passage.RestrictionType;
40 import org.crosswire.jsword.passage.Verse;
41 import org.crosswire.jsword.passage.VerseRange;
42 import org.jdom2.Content;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46
54 public abstract class AbstractBackend<T extends OpenFileState> implements StatefulFileBackedBackend<T>, Backend<T> {
55
56
59
60 public AbstractBackend() {
61 }
62
63
68 public AbstractBackend(SwordBookMetaData sbmd) {
69 bmd = sbmd;
70 }
71
72
75 public BookMetaData getBookMetaData() {
76 return bmd;
77 }
78
79
82 public void decipher(byte[] data) {
83 String cipherKeyString = getBookMetaData().getProperty(SwordBookMetaData.KEY_CIPHER_KEY);
84 if (cipherKeyString != null) {
85 Sapphire cipherEngine = new Sapphire(cipherKeyString.getBytes());
86 for (int i = 0; i < data.length; i++) {
87 data[i] = cipherEngine.cipher(data[i]);
88 }
89 cipherEngine.burn();
91 }
92 }
93
94
97 public void encipher(byte[] data) {
98 decipher(data);
100 }
101
102
105 public Key readIndex() {
106 return null;
109 }
110
111
114 public abstract boolean contains(Key key);
115
116
119 public String getRawText(Key key) throws BookException {
120 T state = null;
121 try {
122 state = initState();
123 return readRawContent(state, key);
124 } catch (IOException e) {
125 throw new BookException("Unable to obtain raw content from backend for key='" + key + '\'', e);
126 } finally {
127 OpenFileStateManager.instance().release(state);
128 }
129 }
130
131
134 public void setAliasKey(Key alias, Key source) throws BookException {
135 T state = null;
136 try {
137 state = initState();
138 setAliasKey(state, alias, source);
139 } catch (IOException e) {
140 throw new BookException(JSOtherMsg.lookupText("Unable to save {0}.", alias.getOsisID()));
141 } finally {
142 OpenFileStateManager.instance().release(state);
143 }
144 }
145
146
149 public int getRawTextLength(Key key) {
150 try {
151 String raw = getRawText(key);
152 return raw == null ? 0 : raw.length();
153 } catch (BookException e) {
154 return 0;
155 }
156 }
157
158
161 public Key getGlobalKeyList() throws BookException {
162 throw new UnsupportedOperationException("Fast global key list unsupported in this backend");
164 }
165
166
169
172 public List<Content> readToOsis(Key key, RawTextToXmlProcessor processor) throws BookException {
173
174 final List<Content> content = new ArrayList<Content>();
175
176 T openFileState = null;
177
178 try {
179 openFileState = initState();
180 switch (this.bmd.getKeyType()) {
181 case LIST:
182 readNormalOsis(key, processor, content, openFileState);
183 break;
184 case TREE:
185 readNormalOsisSingleKey(key, processor, content, openFileState);
186 break;
187 case VERSE:
188 readPassageOsis(key, processor, content, openFileState);
189 break;
190 default:
191 throw new BookException("Book has unsupported type of key");
192 }
193
194 return content;
195 } finally {
196 OpenFileStateManager.instance().release(openFileState);
197 }
198 }
199
200 private void readNormalOsis(Key key, RawTextToXmlProcessor processor, List<Content> content, T openFileState) throws BookException {
201 Iterator<Key> iterator = key.iterator();
203
204 while (iterator.hasNext()) {
205 Key next = iterator.next();
206 String rawText;
207 try {
208 rawText = readRawContent(openFileState, next);
209 processor.postVerse(next, content, rawText);
210 } catch (IOException e) {
211 throwFailedKeyException(key, next, e);
213 }
214 }
215 }
216
217
221 private void readNormalOsisSingleKey(Key key, RawTextToXmlProcessor processor, List<Content> content, T openFileState) throws BookException {
222 String rawText;
223 try {
224 rawText = readRawContent(openFileState, key);
225 processor.postVerse(key, content, rawText);
226 } catch (IOException e) {
227 throwFailedKeyException(key, key, e);
229 }
230 }
231
232
241 private Verse readPassageOsis(Key key, RawTextToXmlProcessor processor, final List<Content> content, T openFileState) throws BookException {
242 Verse currentVerse = null;
243 final Passage ref = KeyUtil.getPassage(key);
244 final Iterator<VerseRange> rit = ref.rangeIterator(RestrictionType.CHAPTER);
245 while (rit.hasNext()) {
246 VerseRange range = rit.next();
247 processor.preRange(range, content);
248
249 for (Key verseInRange : range) {
253 currentVerse = KeyUtil.getVerse(verseInRange);
254 try {
255 String rawText = readRawContent(openFileState, currentVerse);
256 processor.postVerse(verseInRange, content, rawText);
257 } catch (IOException e) {
258 LOGGER.debug(e.getMessage(), e);
261 }
262 }
263 }
264
265 return currentVerse;
266 }
267
268
281 private void throwFailedKeyException(Key masterKey, Key currentKey, IOException e) throws BookException {
282 if (currentKey == null) {
286 throw new BookException(JSMsg.gettext("Error reading {0}", masterKey.getName()), e);
287 }
288 throw new BookException(JSMsg.gettext("Error reading {0}", currentKey.getName()), e);
289 }
290
291
294 public void create() throws IOException, BookException {
295 File dataPath = new File(SwordUtil.getExpandedDataPath(getBookMetaData()));
296 if (!dataPath.exists() && !dataPath.mkdirs()) {
297 throw new IOException("Unable to create module data path!");
298 }
299 }
300
301
304 public boolean isSupported() {
305 return true;
306 }
307
308
311 public boolean isWritable() {
312 return false;
313 }
314
315 private SwordBookMetaData bmd;
316 private static final Logger LOGGER = LoggerFactory.getLogger(AbstractBackend.class);
317 }
318