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