| 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 | } |