1
22 package org.crosswire.jsword.book.sword;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.RandomAccessFile;
27 import java.net.URI;
28
29 import org.crosswire.common.activate.Activatable;
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.DefaultKeyList;
40 import org.crosswire.jsword.passage.Key;
41
42
51 public class TreeKeyIndex implements Activatable {
52
57 public TreeKeyIndex(SwordBookMetaData sbmd) {
58 bmd = sbmd;
59 }
60
61
65 public TreeNode getRoot() throws IOException {
66 return getTreeNode(getOffset(0));
67 }
68
69
77 public TreeNode getParent(TreeNode node) throws IOException {
78 return getTreeNode(getOffset(node.getParent()));
79 }
80
81
89 public TreeNode getFirstChild(TreeNode node) throws IOException {
90 return getTreeNode(getOffset(node.getFirstChild()));
91 }
92
93
101 public TreeNode getNextSibling(TreeNode node) throws IOException {
102 return getTreeNode(getOffset(node.getNextSibling()));
103 }
104
105
113 private int getOffset(int index) throws IOException {
114 if (index == -1) {
115 return -1;
116 }
117
118 checkActive();
119 byte[] buffer = SwordUtil.readRAF(idxRaf, index, 4);
120 return SwordUtil.decodeLittleEndian32(buffer, 0);
121 }
122
123
131 private TreeNode getTreeNode(int offset) throws IOException {
132 TreeNode node = new TreeNode(offset);
133
134 if (offset == -1) {
135 return node;
136 }
137
138 checkActive();
139 byte[] buffer = SwordUtil.readRAF(datRaf, offset, 12);
140 node.setParent(SwordUtil.decodeLittleEndian32(buffer, 0));
141 node.setNextSibling(SwordUtil.decodeLittleEndian32(buffer, 4));
142 node.setFirstChild(SwordUtil.decodeLittleEndian32(buffer, 8));
143
144 buffer = SwordUtil.readUntilRAF(datRaf, (byte) 0);
145 int size = buffer.length;
146 if (buffer[size - 1] == 0) {
147 size--;
148 }
149
150 Key key = new DefaultKeyList(null, bmd.getName());
151 node.setName(SwordUtil.decode(key.getName(), buffer, size, bmd.getBookCharset()).trim());
153
154 buffer = SwordUtil.readNextRAF(datRaf, 2);
155 int userDataSize = SwordUtil.decodeLittleEndian16(buffer, 0);
156 if (userDataSize > 0) {
157 node.setUserData(SwordUtil.readNextRAF(datRaf, userDataSize));
158 }
159
160 return node;
161 }
162
163
166 public final void activate(Lock lock) {
167 String path = null;
168 try {
169 path = getExpandedDataPath();
170 } catch (BookException e) {
171 Reporter.informUser(this, e);
172 return;
173 }
174
175 idxFile = new File(path + EXTENSION_INDEX);
176 datFile = new File(path + EXTENSION_DATA);
177
178 if (!idxFile.canRead()) {
179 Reporter.informUser(this, new BookException(JSMsg.gettext("Error reading {0}", idxFile.getAbsolutePath())));
182 return;
183 }
184
185 if (!datFile.canRead()) {
186 Reporter.informUser(this, new BookException(JSMsg.gettext("Error reading {0}", datFile.getAbsolutePath())));
189 return;
190 }
191
192 try {
193 idxRaf = new RandomAccessFile(idxFile, FileUtil.MODE_READ);
194 datRaf = new RandomAccessFile(datFile, FileUtil.MODE_READ);
195 } catch (IOException ex) {
196 log.error("failed to open files", ex);
197 idxRaf = null;
198 datRaf = null;
199 }
200 active = true;
201 }
202
203
206 public final void deactivate(Lock lock) {
207 try {
208 if (idxRaf != null) {
209 idxRaf.close();
210 }
211 if (datRaf != null) {
212 datRaf.close();
213 }
214 } catch (IOException ex) {
215 log.error("failed to close nt files", ex);
216 } finally {
217 idxRaf = null;
218 datRaf = null;
219 }
220 active = false;
221 }
222
223
226 protected final void checkActive() {
227 if (!active) {
228 Activator.activate(this);
229 }
230 }
231
232 private String getExpandedDataPath() throws BookException {
233 URI loc = NetUtil.lengthenURI(bmd.getLibrary(), (String) bmd.getProperty(ConfigEntryType.DATA_PATH));
234
235 if (loc == null) {
236 throw new BookException(JSOtherMsg.lookupText("Missing data files for old and new testaments in {0}."));
238 }
239
240 return new File(loc.getPath()).getAbsolutePath();
241 }
242
243 private static final String EXTENSION_INDEX = ".idx";
244 private static final String EXTENSION_DATA = ".dat";
245
246 private SwordBookMetaData bmd;
247 private File idxFile;
248 private File datFile;
249 private RandomAccessFile idxRaf;
250 private RandomAccessFile datRaf;
251 private boolean active;
252
253
256 private static final Logger log = Logger.getLogger(TreeKeyIndex.class);
257 }
258