1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
package org.crosswire.jsword.book.sword; |
21 | |
|
22 | |
import java.io.File; |
23 | |
import java.io.IOException; |
24 | |
import java.io.RandomAccessFile; |
25 | |
import java.net.URI; |
26 | |
|
27 | |
import org.crosswire.common.activate.Activatable; |
28 | |
import org.crosswire.common.activate.Activator; |
29 | |
import org.crosswire.common.activate.Lock; |
30 | |
import org.crosswire.common.util.FileUtil; |
31 | |
import org.crosswire.common.util.NetUtil; |
32 | |
import org.crosswire.common.util.Reporter; |
33 | |
import org.crosswire.jsword.JSMsg; |
34 | |
import org.crosswire.jsword.JSOtherMsg; |
35 | |
import org.crosswire.jsword.book.BookException; |
36 | |
import org.crosswire.jsword.passage.DefaultKeyList; |
37 | |
import org.crosswire.jsword.passage.Key; |
38 | |
import org.slf4j.Logger; |
39 | |
import org.slf4j.LoggerFactory; |
40 | |
|
41 | |
|
42 | |
|
43 | |
|
44 | |
|
45 | |
|
46 | |
|
47 | |
|
48 | |
|
49 | |
public class TreeKeyIndex implements Activatable { |
50 | |
|
51 | |
|
52 | |
|
53 | |
|
54 | |
|
55 | 0 | public TreeKeyIndex(SwordBookMetaData sbmd) { |
56 | 0 | bmd = sbmd; |
57 | 0 | } |
58 | |
|
59 | |
|
60 | |
|
61 | |
|
62 | |
|
63 | |
public TreeNode getRoot() throws IOException { |
64 | 0 | return getTreeNode(getOffset(0)); |
65 | |
} |
66 | |
|
67 | |
|
68 | |
|
69 | |
|
70 | |
|
71 | |
|
72 | |
|
73 | |
|
74 | |
|
75 | |
public TreeNode getParent(TreeNode node) throws IOException { |
76 | 0 | return getTreeNode(getOffset(node.getParent())); |
77 | |
} |
78 | |
|
79 | |
|
80 | |
|
81 | |
|
82 | |
|
83 | |
|
84 | |
|
85 | |
|
86 | |
|
87 | |
public TreeNode getFirstChild(TreeNode node) throws IOException { |
88 | 0 | return getTreeNode(getOffset(node.getFirstChild())); |
89 | |
} |
90 | |
|
91 | |
|
92 | |
|
93 | |
|
94 | |
|
95 | |
|
96 | |
|
97 | |
|
98 | |
|
99 | |
public TreeNode getNextSibling(TreeNode node) throws IOException { |
100 | 0 | return getTreeNode(getOffset(node.getNextSibling())); |
101 | |
} |
102 | |
|
103 | |
|
104 | |
|
105 | |
|
106 | |
|
107 | |
|
108 | |
|
109 | |
|
110 | |
|
111 | |
private int getOffset(int index) throws IOException { |
112 | 0 | if (index == -1) { |
113 | 0 | return -1; |
114 | |
} |
115 | |
|
116 | 0 | checkActive(); |
117 | 0 | byte[] buffer = SwordUtil.readRAF(idxRaf, index, 4); |
118 | 0 | return SwordUtil.decodeLittleEndian32(buffer, 0); |
119 | |
} |
120 | |
|
121 | |
|
122 | |
|
123 | |
|
124 | |
|
125 | |
|
126 | |
|
127 | |
|
128 | |
|
129 | |
private TreeNode getTreeNode(int offset) throws IOException { |
130 | 0 | TreeNode node = new TreeNode(offset); |
131 | |
|
132 | 0 | if (offset == -1) { |
133 | 0 | return node; |
134 | |
} |
135 | |
|
136 | 0 | checkActive(); |
137 | 0 | byte[] buffer = SwordUtil.readRAF(datRaf, offset, 12); |
138 | 0 | node.setParent(SwordUtil.decodeLittleEndian32(buffer, 0)); |
139 | 0 | node.setNextSibling(SwordUtil.decodeLittleEndian32(buffer, 4)); |
140 | 0 | node.setFirstChild(SwordUtil.decodeLittleEndian32(buffer, 8)); |
141 | |
|
142 | 0 | buffer = SwordUtil.readUntilRAF(datRaf, (byte) 0); |
143 | 0 | int size = buffer.length; |
144 | 0 | if (buffer[size - 1] == 0) { |
145 | 0 | size--; |
146 | |
} |
147 | |
|
148 | 0 | Key key = new DefaultKeyList(null, bmd.getName()); |
149 | |
|
150 | 0 | node.setName(SwordUtil.decode(key.getName(), buffer, size, bmd.getBookCharset()).trim()); |
151 | |
|
152 | 0 | buffer = SwordUtil.readNextRAF(datRaf, 2); |
153 | 0 | int userDataSize = SwordUtil.decodeLittleEndian16(buffer, 0); |
154 | 0 | if (userDataSize > 0) { |
155 | 0 | node.setUserData(SwordUtil.readNextRAF(datRaf, userDataSize)); |
156 | |
} |
157 | |
|
158 | 0 | return node; |
159 | |
} |
160 | |
|
161 | |
|
162 | |
|
163 | |
|
164 | |
public final void activate(Lock lock) { |
165 | 0 | String path = null; |
166 | |
try { |
167 | 0 | path = getExpandedDataPath(); |
168 | 0 | } catch (BookException e) { |
169 | 0 | Reporter.informUser(this, e); |
170 | 0 | return; |
171 | 0 | } |
172 | |
|
173 | 0 | idxFile = new File(path + EXTENSION_INDEX); |
174 | 0 | datFile = new File(path + EXTENSION_DATA); |
175 | |
|
176 | 0 | if (!idxFile.canRead()) { |
177 | |
|
178 | |
|
179 | 0 | Reporter.informUser(this, new BookException(JSMsg.gettext("Error reading {0}", idxFile.getAbsolutePath()))); |
180 | 0 | return; |
181 | |
} |
182 | |
|
183 | 0 | if (!datFile.canRead()) { |
184 | |
|
185 | |
|
186 | 0 | Reporter.informUser(this, new BookException(JSMsg.gettext("Error reading {0}", datFile.getAbsolutePath()))); |
187 | 0 | return; |
188 | |
} |
189 | |
|
190 | |
try { |
191 | 0 | idxRaf = new RandomAccessFile(idxFile, FileUtil.MODE_READ); |
192 | 0 | datRaf = new RandomAccessFile(datFile, FileUtil.MODE_READ); |
193 | 0 | } catch (IOException ex) { |
194 | 0 | log.error("failed to open files", ex); |
195 | 0 | idxRaf = null; |
196 | 0 | datRaf = null; |
197 | 0 | } |
198 | 0 | active = true; |
199 | 0 | } |
200 | |
|
201 | |
|
202 | |
|
203 | |
|
204 | |
public final void deactivate(Lock lock) { |
205 | |
try { |
206 | 0 | if (idxRaf != null) { |
207 | 0 | idxRaf.close(); |
208 | |
} |
209 | 0 | if (datRaf != null) { |
210 | 0 | datRaf.close(); |
211 | |
} |
212 | 0 | } catch (IOException ex) { |
213 | 0 | log.error("failed to close nt files", ex); |
214 | |
} finally { |
215 | 0 | idxRaf = null; |
216 | 0 | datRaf = null; |
217 | 0 | } |
218 | 0 | active = false; |
219 | 0 | } |
220 | |
|
221 | |
|
222 | |
|
223 | |
|
224 | |
protected final void checkActive() { |
225 | 0 | if (!active) { |
226 | 0 | Activator.activate(this); |
227 | |
} |
228 | 0 | } |
229 | |
|
230 | |
private String getExpandedDataPath() throws BookException { |
231 | 0 | URI loc = NetUtil.lengthenURI(bmd.getLibrary(), bmd.getProperty(SwordBookMetaData.KEY_DATA_PATH)); |
232 | |
|
233 | 0 | if (loc == null) { |
234 | |
|
235 | 0 | throw new BookException(JSOtherMsg.lookupText("Missing data files for old and new testaments in {0}.")); |
236 | |
} |
237 | |
|
238 | 0 | return new File(loc.getPath()).getAbsolutePath(); |
239 | |
} |
240 | |
|
241 | |
private static final String EXTENSION_INDEX = ".idx"; |
242 | |
private static final String EXTENSION_DATA = ".dat"; |
243 | |
|
244 | |
private SwordBookMetaData bmd; |
245 | |
private File idxFile; |
246 | |
private File datFile; |
247 | |
private RandomAccessFile idxRaf; |
248 | |
private RandomAccessFile datRaf; |
249 | |
private boolean active; |
250 | |
|
251 | |
|
252 | |
|
253 | |
|
254 | 0 | private static final Logger log = LoggerFactory.getLogger(TreeKeyIndex.class); |
255 | |
} |