Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DwrBridge |
|
| 4.6;4.6 |
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.bridge; | |
21 | ||
22 | import java.io.File; | |
23 | import java.util.ArrayList; | |
24 | import java.util.List; | |
25 | import java.util.Locale; | |
26 | ||
27 | import org.crosswire.common.xml.SAXEventProvider; | |
28 | import org.crosswire.common.xml.SerializingContentHandler; | |
29 | import org.crosswire.jsword.book.Book; | |
30 | import org.crosswire.jsword.book.BookCategory; | |
31 | import org.crosswire.jsword.book.BookData; | |
32 | import org.crosswire.jsword.book.BookException; | |
33 | import org.crosswire.jsword.book.sword.SwordBookPath; | |
34 | import org.crosswire.jsword.index.IndexManagerFactory; | |
35 | import org.crosswire.jsword.passage.Key; | |
36 | import org.crosswire.jsword.passage.NoSuchKeyException; | |
37 | import org.crosswire.jsword.passage.Passage; | |
38 | import org.crosswire.jsword.versification.BookName; | |
39 | import org.xml.sax.ContentHandler; | |
40 | import org.xml.sax.SAXException; | |
41 | ||
42 | /** | |
43 | * The DWR DwrBridge adapts JSword to DWR. This is based upon APIExamples. | |
44 | * | |
45 | * @see gnu.lgpl.License The GNU Lesser General Public License for details. | |
46 | * @author DM Smith | |
47 | */ | |
48 | 0 | public class DwrBridge { |
49 | /** | |
50 | * Get a listing of all the available books. | |
51 | * | |
52 | * @param filter | |
53 | * The custom filter specification string | |
54 | * @return a list of (initial, name) string pairs | |
55 | * @see BookInstaller#getInstalledBook(String) | |
56 | */ | |
57 | public String[][] getInstalledBooks(String filter) { | |
58 | 0 | List<String[]> reply = new ArrayList<String[]>(); |
59 | ||
60 | 0 | for (Book book : BookInstaller.getInstalledBooks(filter)) { |
61 | 0 | String[] rbook = new String[] { |
62 | book.getInitials(), book.getName() | |
63 | }; | |
64 | 0 | reply.add(rbook); |
65 | 0 | } |
66 | ||
67 | // If we can't find a book, indicate that. | |
68 | 0 | if (reply.isEmpty()) { |
69 | 0 | reply.add(new String[] { |
70 | "", "No Books installed"}); | |
71 | } | |
72 | ||
73 | 0 | return reply.toArray(new String[reply.size()][]); |
74 | } | |
75 | ||
76 | /** | |
77 | * Determine whether the named book can be searched, that is, whether the | |
78 | * book is indexed. | |
79 | * | |
80 | * @param bookInitials | |
81 | * the named book to check. | |
82 | * @return true if searching can be performed | |
83 | */ | |
84 | public boolean isIndexed(String bookInitials) { | |
85 | 0 | return isBookIndexed(BookInstaller.getInstalledBook(bookInitials)); |
86 | } | |
87 | ||
88 | /** | |
89 | * Determine the size of this reference. | |
90 | * | |
91 | * @param bookInitials | |
92 | * the book to which the reference applies. | |
93 | * @param reference | |
94 | * the actual reference | |
95 | * @return the number of entries for this reference. | |
96 | * @throws NoSuchKeyException | |
97 | */ | |
98 | public int getCardinality(String bookInitials, String reference) throws NoSuchKeyException { | |
99 | 0 | Book book = BookInstaller.getInstalledBook(bookInitials); |
100 | 0 | if (book != null) { |
101 | 0 | Key key = book.getKey(reference); |
102 | 0 | return key.getCardinality(); |
103 | } | |
104 | 0 | return 0; |
105 | } | |
106 | ||
107 | /** | |
108 | * Obtain the OSIS representation from a book for a reference, pruning a | |
109 | * reference to a limited number of keys. | |
110 | * | |
111 | * @param bookInitials | |
112 | * the book to use | |
113 | * @param reference | |
114 | * a reference, appropriate for the book, for one or more keys | |
115 | * @param start | |
116 | * @param count | |
117 | * @return the OSIS as a string | |
118 | * @throws BookException | |
119 | * @throws NoSuchKeyException | |
120 | */ | |
121 | public String getOSISString(String bookInitials, String reference, int start, int count) throws BookException, NoSuchKeyException { | |
122 | 0 | String result = ""; |
123 | try { | |
124 | 0 | SAXEventProvider sep = getOSISProvider(bookInitials, reference, start, count); |
125 | 0 | if (sep != null) { |
126 | 0 | ContentHandler ser = new SerializingContentHandler(); |
127 | 0 | sep.provideSAXEvents(ser); |
128 | 0 | result = ser.toString(); |
129 | } | |
130 | 0 | return result; |
131 | 0 | } catch (SAXException ex) { |
132 | // This is allowed | |
133 | // throw new BookException(Msg.JSWORD_SAXPARSE, ex); | |
134 | } | |
135 | 0 | return result; |
136 | } | |
137 | ||
138 | /** | |
139 | * Get a reference list for a search result against a book. | |
140 | * | |
141 | * @param bookInitials | |
142 | * @param searchRequest | |
143 | * @return The reference for the matching. | |
144 | * @throws BookException | |
145 | */ | |
146 | public String search(String bookInitials, String searchRequest) throws BookException { | |
147 | 0 | Book book = BookInstaller.getInstalledBook(bookInitials); |
148 | 0 | if (isBookIndexed(book) && searchRequest != null) { |
149 | 0 | if (BookCategory.BIBLE.equals(book.getBookCategory())) { |
150 | 0 | BookName.setFullBookName(false); |
151 | } | |
152 | 0 | return book.find(searchRequest).getName(); |
153 | } | |
154 | 0 | return ""; |
155 | } | |
156 | ||
157 | /** | |
158 | * Get close matches for a target in a book whose keys have a meaningful | |
159 | * sort. This is not true of keys that are numeric or contain numbers. | |
160 | * (unless the numbers are 0 filled.) | |
161 | * | |
162 | * @param bookInitials | |
163 | * @param searchRequest | |
164 | * @param maxMatchCount | |
165 | * @return the matches | |
166 | */ | |
167 | public String[] match(String bookInitials, String searchRequest, int maxMatchCount) { | |
168 | 0 | Book book = BookInstaller.getInstalledBook(bookInitials); |
169 | 0 | if (book == null || searchRequest == null || maxMatchCount < 1) { |
170 | 0 | return new String[0]; |
171 | } | |
172 | ||
173 | // Need to use the locale of the book so that we can find stuff in the | |
174 | // proper order | |
175 | 0 | Locale sortLocale = new Locale(book.getLanguage().getCode()); |
176 | 0 | String target = searchRequest.toLowerCase(sortLocale); |
177 | ||
178 | // Get everything with target as the prefix. | |
179 | // In Unicode \uFFFF is reserved for internal use | |
180 | // and is greater than every character defined in Unicode | |
181 | 0 | String endTarget = target + '\uffff'; |
182 | ||
183 | // This whole getGlobalKeyList is messy. | |
184 | // 1) Some drivers cache the list which is slow. | |
185 | // 2) Binary lookup would be much better. | |
186 | // 3) Caching the whole list here is dumb. | |
187 | // What is needed is that all this be pushed into JSword proper. | |
188 | // TODO(dms): Push this into Book interface. | |
189 | 0 | List<String> result = new ArrayList<String>(); |
190 | 0 | int count = 0; |
191 | 0 | for (Key key : book.getGlobalKeyList()) { |
192 | 0 | String entry = key.getName().toLowerCase(sortLocale); |
193 | 0 | if (entry.compareTo(target) >= 0) { |
194 | 0 | if (entry.compareTo(endTarget) < 0) { |
195 | 0 | result.add(entry); |
196 | 0 | count++; |
197 | } | |
198 | ||
199 | // Have we seen enough? | |
200 | 0 | if (count >= maxMatchCount) { |
201 | 0 | break; |
202 | } | |
203 | } | |
204 | 0 | } |
205 | ||
206 | 0 | return result.toArray(new String[result.size()]); |
207 | } | |
208 | ||
209 | /** | |
210 | * For the sake of diagnostics, return the locations that JSword will look | |
211 | * for books. | |
212 | * | |
213 | * @return the SWORD path | |
214 | */ | |
215 | public String[] getSwordPath() { | |
216 | 0 | File[] filePath = SwordBookPath.getSwordPath(); |
217 | 0 | if (filePath.length == 0) { |
218 | 0 | return new String[] { |
219 | "No path"}; | |
220 | } | |
221 | 0 | String[] path = new String[filePath.length]; |
222 | 0 | for (int i = 0; i < filePath.length; i++) { |
223 | 0 | path[i] = filePath[i].getAbsolutePath(); |
224 | } | |
225 | 0 | return path; |
226 | } | |
227 | ||
228 | /** | |
229 | * Determine whether the book can be searched, that is, whether the book is | |
230 | * indexed. | |
231 | * | |
232 | * @param book | |
233 | * the book to check. | |
234 | * @return true if searching can be performed | |
235 | */ | |
236 | public boolean isBookIndexed(Book book) { | |
237 | 0 | return book != null && IndexManagerFactory.getIndexManager().isIndexed(book); |
238 | } | |
239 | ||
240 | /** | |
241 | * Get BookData representing one or more Book entries, but capped to a | |
242 | * maximum number of entries. | |
243 | * | |
244 | * @param bookInitials | |
245 | * the book to use | |
246 | * @param reference | |
247 | * a reference, appropriate for the book, of one or more entries | |
248 | * @param start | |
249 | * the starting point where 0 is the first. | |
250 | * @param count | |
251 | * the maximum number of entries to use | |
252 | * | |
253 | * @throws NoSuchKeyException | |
254 | */ | |
255 | private BookData getBookData(String bookInitials, String reference, int start, int count) throws NoSuchKeyException { | |
256 | 0 | Book book = BookInstaller.getInstalledBook(bookInitials); |
257 | 0 | if (book == null || reference == null || count < 1) { |
258 | 0 | return null; |
259 | } | |
260 | ||
261 | // TODO(dms): add trim(count) and trim(start, count) to the key | |
262 | // interface. | |
263 | 0 | Key key = null; |
264 | 0 | if (BookCategory.BIBLE.equals(book.getBookCategory())) { |
265 | 0 | key = book.getKey(reference); |
266 | 0 | Passage remainder = (Passage) key; |
267 | 0 | if (start > 0) { |
268 | 0 | remainder = remainder.trimVerses(start); |
269 | } | |
270 | 0 | remainder.trimVerses(count); |
271 | 0 | key = remainder; |
272 | 0 | } else if (BookCategory.GENERAL_BOOK.equals(book.getBookCategory())) { |
273 | // At this time we cannot trim a General Book | |
274 | 0 | key = book.getKey(reference); |
275 | } else { | |
276 | 0 | key = book.getKey(reference); |
277 | ||
278 | // Do we need to trim? | |
279 | 0 | if (start > 0 || key.getCardinality() > count) { |
280 | 0 | key = book.createEmptyKeyList(); |
281 | 0 | int i = 0; |
282 | 0 | for (Key aKey : key) { |
283 | 0 | i++; |
284 | 0 | if (i <= start) { |
285 | 0 | continue; |
286 | } | |
287 | 0 | if (i >= count) { |
288 | 0 | break; |
289 | } | |
290 | 0 | key.addAll(aKey); |
291 | } | |
292 | } | |
293 | } | |
294 | ||
295 | 0 | return new BookData(book, key); |
296 | } | |
297 | ||
298 | /** | |
299 | * Obtain a SAX event provider for the OSIS document representation of one | |
300 | * or more book entries. | |
301 | * | |
302 | * @param bookInitials | |
303 | * the book to use | |
304 | * @param reference | |
305 | * a reference, appropriate for the book, of one or more entries | |
306 | */ | |
307 | private SAXEventProvider getOSISProvider(String bookInitials, String reference, int start, int count) throws BookException, NoSuchKeyException { | |
308 | 0 | BookData data = getBookData(bookInitials, reference, start, count); |
309 | 0 | SAXEventProvider provider = null; |
310 | 0 | if (data != null) { |
311 | 0 | provider = data.getSAXEventProvider(); |
312 | } | |
313 | 0 | return provider; |
314 | } | |
315 | ||
316 | } |