[jsword-svn] r1792 - in trunk: common common/src/main/java/org/crosswire/common/util jsword/src/main/java/org/crosswire/jsword/book/sword
dmsmith at www.crosswire.org
dmsmith at www.crosswire.org
Thu Apr 10 14:16:24 MST 2008
Author: dmsmith
Date: 2008-04-10 14:16:23 -0700 (Thu, 10 Apr 2008)
New Revision: 1792
Modified:
trunk/common/JSwordDictionary.txt
trunk/common/src/main/java/org/crosswire/common/util/Reporter.java
trunk/common/src/main/java/org/crosswire/common/util/StringUtil.java
trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/AbstractBackend.java
trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/GenBookBackend.java
trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/IndexKey.java
trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/RawBackend.java
trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/RawLDBackend.java
trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/SwordUtil.java
trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/TreeKeyIndex.java
trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/ZLDBackend.java
trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/ZVerseBackend.java
Log:
Incremental improvements in backend, toward ability to write and toward fast dictionaries.
Modified: trunk/common/JSwordDictionary.txt
===================================================================
--- trunk/common/JSwordDictionary.txt 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/common/JSwordDictionary.txt 2008-04-10 21:16:23 UTC (rev 1792)
@@ -46,3 +46,15 @@
internet
editable
throwable
+endian
+refactor
+backend
+unascribed
+backends
+calendarized
+devotionals
+versification
+uninstall
+proxying
+markup
+unicode
Modified: trunk/common/src/main/java/org/crosswire/common/util/Reporter.java
===================================================================
--- trunk/common/src/main/java/org/crosswire/common/util/Reporter.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/common/src/main/java/org/crosswire/common/util/Reporter.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -36,7 +36,7 @@
* in response to a single error.</li>
* <li>The class being implemented may implement an interface that disallows
* nested exceptions and yet does not want to loose the root cause error
- * information. (This is the weakest of the above arguements, but probably
+ * information. (This is the weakest of the above arguments, but probably
* still valid.)</li>
* However in many of the times this class is used, this is the reason:
* <li>Within UI specific code - to throw up a dialog box (or whatever). Now
Modified: trunk/common/src/main/java/org/crosswire/common/util/StringUtil.java
===================================================================
--- trunk/common/src/main/java/org/crosswire/common/util/StringUtil.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/common/src/main/java/org/crosswire/common/util/StringUtil.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -258,7 +258,7 @@
* StringUtils.split(null, *) = null
* StringUtils.split("", *) = []
* StringUtils.split("a.b.c", '.') = ["a", "b", "c"]
- * StringUtils.split("a..b.c", '.') = ["a", "b", "c"]
+ * StringUtils.split("a..b.c", '.') = ["a", "", "b", "c"]
* StringUtils.split("a:b:c", '.') = ["a:b:c"]
* StringUtils.split("a\tb\nc", null) = ["a", "b", "c"]
* StringUtils.split("a b c", ' ') = ["a", "b", "c"]
@@ -307,6 +307,72 @@
}
/**
+ * <p>Splits the provided text into an array, separator specified.
+ * This is an alternative to using StringTokenizer.</p>
+ *
+ * <p>The separator is not included in the returned String array.
+ * Adjacent separators are treated individually.</p>
+ *
+ * <p>A <code>null</code> input String returns <code>null</code>.</p>
+ *
+ * <pre>
+ * StringUtils.split(null, *) = null
+ * StringUtils.split("", *) = []
+ * StringUtils.split("a.b.c", '.') = ["a", "b", "c"]
+ * StringUtils.split("a..b.c", '.') = ["a", "", "b", "c"]
+ * StringUtils.split("a:b:c", '.') = ["a:b:c"]
+ * StringUtils.split("a b c", ' ') = ["a", "b", "c"]
+ * </pre>
+ *
+ * @param str the String to parse, may be null
+ * @param separatorChar the character used as the delimiter
+ * @param max the maximum number of elements to include in the
+ * array. A zero or negative value implies no limit
+ * @return an array of parsed Strings
+ * @since 2.0
+ */
+ public static String[] splitAll(String str, char separatorChar, int max)
+ {
+ // Performance tuned for 2.0 (JDK1.4)
+
+ if (str == null)
+ {
+ return (String[]) EMPTY_STRING_ARRAY.clone();
+ }
+ int len = str.length();
+ if (len == 0)
+ {
+ return (String[]) EMPTY_STRING_ARRAY.clone();
+ }
+ List list = new ArrayList();
+ int sizePlus1 = 1;
+ int i = 0;
+ int start = 0;
+ boolean match = false;
+ while (i < len)
+ {
+ if (str.charAt(i) == separatorChar)
+ {
+ if (sizePlus1++ == max)
+ {
+ i = len;
+ }
+ list.add(str.substring(start, i));
+ start = ++i;
+ match = false;
+ continue;
+ }
+ match = true;
+ i++;
+ }
+ if (match)
+ {
+ list.add(str.substring(start, i));
+ }
+ return (String[]) list.toArray(new String[list.size()]);
+ }
+
+ /**
* <p>Splits the provided text into an array, separators specified.
* This is an alternative to using StringTokenizer.</p>
*
@@ -351,7 +417,7 @@
* StringUtils.split("ab de fg", null, 0) = ["ab", "cd", "ef"]
* StringUtils.split("ab de fg", null, 0) = ["ab", "cd", "ef"]
* StringUtils.split("ab:cd:ef", ":", 0) = ["ab", "cd", "ef"]
- * StringUtils.split("ab:cd:ef", ":", 2) = ["ab", "cdef"]
+ * StringUtils.split("ab:cd:ef", ":", 2) = ["ab", "cd:ef"]
* </pre>
*
* @param str the String to parse, may be null
@@ -406,7 +472,7 @@
}
else if (separatorChars.length() == 1)
{
- // Optimise 1 character case
+ // Optimize 1 character case
char sep = separatorChars.charAt(0);
while (i < len)
{
Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/AbstractBackend.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/AbstractBackend.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/AbstractBackend.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -21,7 +21,6 @@
*/
package org.crosswire.jsword.book.sword;
-import java.io.File;
import java.net.URI;
import org.crosswire.common.activate.Activatable;
@@ -36,6 +35,7 @@
* @see gnu.lgpl.License for license details.
* The copyright to this program is held by it's authors.
* @author Joe Walker [joe at eireneh dot com]
+ * @author DM Smith [dmsmith555 at yahoo dot com]
*/
public abstract class AbstractBackend implements Activatable
{
@@ -71,11 +71,24 @@
{
data[i] = cipherEngine.cipher(data[i]);
}
+ // destroy any evidence!
+ cipherEngine.burn();
}
+ cipherKeyString = null;
}
- public String getExpandedDataPath() throws BookException
+ /**
+ * Encipher the data in place, if there is a key to unlock it.
+ * @param data
+ */
+ public void encipher(byte[] data)
{
+ // Enciphering and deciphering are the same!
+ decipher(data);
+ }
+
+ public URI getExpandedDataPath() throws BookException
+ {
URI loc = NetUtil.lengthenURI(bmd.getLibrary(), (String) bmd.getProperty(ConfigEntryType.DATA_PATH));
if (loc == null)
@@ -83,17 +96,17 @@
throw new BookException(Msg.MISSING_FILE);
}
- return new File(loc.getPath()).getAbsolutePath();
+ return loc;
}
/**
- * Initialise a AbstractBackend before use. This method needs to call addKey() a
+ * Initialize a AbstractBackend before use. This method needs to call addKey() a
* number of times on SwordDictionary
*/
public abstract Key readIndex();
/**
- * Get the bytes alotted for the given verse
+ * Get the text allotted for the given entry
* @param key The key to fetch
* @return String The data for the verse in question
* @throws BookException If the data can not be read.
@@ -101,6 +114,16 @@
public abstract String getRawText(Key key) throws BookException;
/**
+ * Set the text allotted for the given verse
+ * @param key The key to fetch
+ * @throws BookException If the data can not be set.
+ */
+ public void setRawText(Key key, String text) /* throws BookException */
+ {
+ throw new UnsupportedOperationException("Could not set text (" + text + ") for " + key); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
* Returns whether this AbstractBackend is implemented.
* @return true if this AbstractBackend is implemented.
*/
Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/GenBookBackend.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/GenBookBackend.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/GenBookBackend.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -24,6 +24,7 @@
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.net.URI;
import java.util.ArrayList;
import java.util.List;
@@ -54,8 +55,8 @@
index = new TreeKeyIndex(sbmd);
- String path = getExpandedDataPath();
- bdtFile = new File(path + EXTENSION_BDT);
+ URI path = getExpandedDataPath();
+ bdtFile = new File(path.getPath() + EXTENSION_BDT);
if (!bdtFile.canRead())
{
@@ -127,7 +128,7 @@
int size = SwordUtil.decodeLittleEndian32(userData, 4);
byte[] data = SwordUtil.readRAF(bdtRaf, start, size);
decipher(data);
- return SwordUtil.decode(key, data, getBookMetaData().getBookCharset());
+ return SwordUtil.decode(key.getName(), data, getBookMetaData().getBookCharset());
}
return ""; //$NON-NLS-1$
Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/IndexKey.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/IndexKey.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/IndexKey.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -1,4 +1,3 @@
-package org.crosswire.jsword.book.sword;
/**
* Distribution License:
* JSword is free software; you can redistribute it and/or modify it under
@@ -20,6 +19,8 @@
*
* ID: $Id$
*/
+package org.crosswire.jsword.book.sword;
+
import org.crosswire.jsword.passage.DefaultLeafKeyList;
import org.crosswire.jsword.passage.Key;
Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/RawBackend.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/RawBackend.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/RawBackend.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -25,11 +25,13 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.net.URI;
import org.crosswire.common.activate.Activator;
import org.crosswire.common.activate.Lock;
import org.crosswire.common.util.FileUtil;
import org.crosswire.common.util.Logger;
+import org.crosswire.common.util.NetUtil;
import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;
import org.crosswire.jsword.passage.KeyUtil;
@@ -55,13 +57,15 @@
assert (datasize == 2 || datasize == 4);
- String path = getExpandedDataPath();
+ URI path = getExpandedDataPath();
- idxFile[SwordConstants.TESTAMENT_OLD] = new File(path + File.separator + SwordConstants.FILE_OT + SwordConstants.EXTENSION_VSS);
- txtFile[SwordConstants.TESTAMENT_OLD] = new File(path + File.separator + SwordConstants.FILE_OT);
+ URI otPath = NetUtil.lengthenURI(path, File.separator + SwordConstants.FILE_OT);
+ txtFile[SwordConstants.TESTAMENT_OLD] = new File(otPath.getPath());
+ idxFile[SwordConstants.TESTAMENT_OLD] = new File(otPath.getPath() + SwordConstants.EXTENSION_VSS);
- idxFile[SwordConstants.TESTAMENT_NEW] = new File(path + File.separator + SwordConstants.FILE_NT + SwordConstants.EXTENSION_VSS);
- txtFile[SwordConstants.TESTAMENT_NEW] = new File(path + File.separator + SwordConstants.FILE_NT);
+ URI ntPath = NetUtil.lengthenURI(path, File.separator + SwordConstants.FILE_NT);
+ txtFile[SwordConstants.TESTAMENT_NEW] = new File(ntPath.getPath());
+ idxFile[SwordConstants.TESTAMENT_NEW] = new File(ntPath.getPath() + SwordConstants.EXTENSION_VSS);
// It is an error to be neither OT nor NT
if (!txtFile[SwordConstants.TESTAMENT_OLD].canRead() && !txtFile[SwordConstants.TESTAMENT_NEW].canRead())
@@ -75,12 +79,14 @@
*/
public final void activate(Lock lock)
{
+ String fileMode = isWritable() ? FileUtil.MODE_WRITE : FileUtil.MODE_READ;
+
if (idxFile[SwordConstants.TESTAMENT_OLD].canRead())
{
try
{
- idxRaf[SwordConstants.TESTAMENT_OLD] = new RandomAccessFile(idxFile[SwordConstants.TESTAMENT_OLD], FileUtil.MODE_READ);
- txtRaf[SwordConstants.TESTAMENT_OLD] = new RandomAccessFile(txtFile[SwordConstants.TESTAMENT_OLD], FileUtil.MODE_READ);
+ idxRaf[SwordConstants.TESTAMENT_OLD] = new RandomAccessFile(idxFile[SwordConstants.TESTAMENT_OLD], fileMode);
+ txtRaf[SwordConstants.TESTAMENT_OLD] = new RandomAccessFile(txtFile[SwordConstants.TESTAMENT_OLD], fileMode);
}
catch (FileNotFoundException ex)
{
@@ -95,8 +101,8 @@
{
try
{
- idxRaf[SwordConstants.TESTAMENT_NEW] = new RandomAccessFile(idxFile[SwordConstants.TESTAMENT_NEW], FileUtil.MODE_READ);
- txtRaf[SwordConstants.TESTAMENT_NEW] = new RandomAccessFile(txtFile[SwordConstants.TESTAMENT_NEW], FileUtil.MODE_READ);
+ idxRaf[SwordConstants.TESTAMENT_NEW] = new RandomAccessFile(idxFile[SwordConstants.TESTAMENT_NEW], fileMode);
+ txtRaf[SwordConstants.TESTAMENT_NEW] = new RandomAccessFile(txtFile[SwordConstants.TESTAMENT_NEW], fileMode);
}
catch (FileNotFoundException ex)
{
@@ -163,7 +169,7 @@
int entrysize = datasize + OFFSETSIZE;
- // Read the next entrysize byes.
+ // Read the next entry size bytes.
byte[] read = SwordUtil.readRAF(idxRaf[testament], index * entrysize, entrysize);
if (read == null || read.length == 0)
{
@@ -197,7 +203,7 @@
decipher(data);
- return SwordUtil.decode(key, data, charset);
+ return SwordUtil.decode(key.getName(), data, charset);
}
catch (IOException ex)
{
@@ -226,6 +232,35 @@
}
}
+ /* (non-Javadoc)
+ * @see org.crosswire.jsword.book.sword.AbstractBackend#isWritable()
+ */
+ public boolean isWritable()
+ {
+ // For the module to be writable either the old testament or the new testament needs to be present
+ // (i.e. readable) and both the index and the data files need to be readable
+ if (idxFile[SwordConstants.TESTAMENT_OLD].canRead()
+ && (idxFile[SwordConstants.TESTAMENT_OLD].canWrite() || !txtFile[SwordConstants.TESTAMENT_OLD].canWrite()))
+ {
+ return false;
+ }
+ if (idxFile[SwordConstants.TESTAMENT_NEW].canRead()
+ && (idxFile[SwordConstants.TESTAMENT_NEW].canWrite() || !txtFile[SwordConstants.TESTAMENT_NEW].canWrite()))
+ {
+ return false;
+ }
+ return idxFile[SwordConstants.TESTAMENT_OLD].canRead() || idxFile[SwordConstants.TESTAMENT_NEW].canRead();
+ }
+
+ public void create(String path)
+ {
+ idxFile[SwordConstants.TESTAMENT_OLD] = new File(path + File.separator + SwordConstants.FILE_OT + SwordConstants.EXTENSION_VSS);
+ txtFile[SwordConstants.TESTAMENT_OLD] = new File(path + File.separator + SwordConstants.FILE_OT);
+
+ idxFile[SwordConstants.TESTAMENT_NEW] = new File(path + File.separator + SwordConstants.FILE_NT + SwordConstants.EXTENSION_VSS);
+ txtFile[SwordConstants.TESTAMENT_NEW] = new File(path + File.separator + SwordConstants.FILE_NT);
+ }
+
/**
* Are we active
*/
Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/RawLDBackend.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/RawLDBackend.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/RawLDBackend.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -24,20 +24,20 @@
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.net.URI;
import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Locale;
import org.crosswire.common.activate.Activator;
import org.crosswire.common.activate.Lock;
import org.crosswire.common.icu.DateFormatter;
-import org.crosswire.common.util.ClassUtil;
import org.crosswire.common.util.FileUtil;
import org.crosswire.common.util.Logger;
import org.crosswire.common.util.Reporter;
import org.crosswire.common.util.StringUtil;
import org.crosswire.jsword.book.BookCategory;
import org.crosswire.jsword.book.BookException;
-import org.crosswire.jsword.book.DataPolice;
import org.crosswire.jsword.passage.DefaultKeyList;
import org.crosswire.jsword.passage.Key;
@@ -54,28 +54,15 @@
* Simple ctor
* @param datasize We need to know how many bytes in the size portion of the index
*/
- public RawLDBackend(SwordBookMetaData sbmd, int datasize) throws BookException
+ public RawLDBackend(SwordBookMetaData sbmd, int datasize)
{
super(sbmd);
this.datasize = datasize;
this.entrysize = OFFSETSIZE + datasize;
+ this.size = -1;
assert (datasize == 2 || datasize == 4);
- String path = getExpandedDataPath();
-
- idxFile = new File(path + SwordConstants.EXTENSION_INDEX);
- datFile = new File(path + SwordConstants.EXTENSION_DATA);
-
- if (!idxFile.canRead())
- {
- throw new BookException(UserMsg.READ_FAIL, new Object[] { idxFile.getAbsolutePath() });
- }
-
- if (!datFile.canRead())
- {
- throw new BookException(UserMsg.READ_FAIL, new Object[] { datFile.getAbsolutePath() });
- }
}
/* (non-Javadoc)
@@ -85,6 +72,32 @@
{
try
{
+ URI path = null;
+ try
+ {
+ path = getExpandedDataPath();
+ }
+ catch (BookException e)
+ {
+ Reporter.informUser(this, e);
+ return;
+ }
+
+ idxFile = new File(path.getPath() + SwordConstants.EXTENSION_INDEX);
+ datFile = new File(path.getPath() + SwordConstants.EXTENSION_DATA);
+
+ if (!idxFile.canRead())
+ {
+ Reporter.informUser(this, new BookException(UserMsg.READ_FAIL, new Object[] { idxFile.getAbsolutePath() }));
+ return;
+ }
+
+ if (!datFile.canRead())
+ {
+ Reporter.informUser(this, new BookException(UserMsg.READ_FAIL, new Object[] { datFile.getAbsolutePath() }));
+ return;
+ }
+
// Open the files
idxRaf = new RandomAccessFile(idxFile, FileUtil.MODE_READ);
datRaf = new RandomAccessFile(datFile, FileUtil.MODE_READ);
@@ -117,6 +130,7 @@
idxRaf = null;
datRaf = null;
+ size = -1;
active = false;
}
@@ -140,7 +154,7 @@
long entries;
try
{
- entries = getEntryCount();
+ entries = getSize();
}
catch (IOException ex)
{
@@ -153,35 +167,18 @@
try
{
// Read the offset and size for this key from the index
- DataIndex index = getIndex(entry);
- String rawData = getEntry(reply, index);
+ DataEntry dataEntry = getEntry(reply.getName(), entry, true);
+ String keytitle = dataEntry.getKey();
- int keyend = rawData.indexOf(SEPARATOR);
- if (keyend == -1)
- {
- DataPolice.report("Failed to find keyname. offset=" + index.getOffset() + " data='" + rawData + "'"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- continue;
- }
-
- String keytitle = rawData.substring(0, keyend).trim();
-
- // for some weird reason plain text (i.e. SourceType=0) dicts
- // all get \ added to the ends of the index entries.
- if (keytitle.endsWith("\\")) //$NON-NLS-1$
- {
- keytitle = keytitle.substring(0, keytitle.length() - 1);
- }
-
- // Massage keytitle if can be.
if (isDailyDevotional && keytitle.length() >= 3)
{
- String[] parts = StringUtil.splitAll(keytitle, '.');
- greg.set(Calendar.MONTH, Integer.parseInt(parts[0]) - 1);
- greg.set(Calendar.DATE, Integer.parseInt(parts[1]));
+ String[] spec = StringUtil.splitAll(keytitle, '.');
+ greg.set(Calendar.MONTH, Integer.parseInt(spec[0]) - 1);
+ greg.set(Calendar.DATE, Integer.parseInt(spec[1]));
keytitle = nameDF.format(greg.getTime());
}
- Key key = new IndexKey(keytitle, index, reply);
+ Key key = new IndexKey(keytitle);
// remove duplicates, keeping later one.
// This occurs under some conditions:
@@ -212,10 +209,14 @@
* @return the number of entries in the Book
* @throws IOException
*/
- public long getEntryCount() throws IOException
+ public long getSize() throws IOException
{
checkActive();
- return idxRaf.length() / entrysize;
+ if (size == -1)
+ {
+ size = idxRaf.length() / entrysize;
+ }
+ return size;
}
/**
@@ -228,20 +229,20 @@
{
// Read the offset and size for this key from the index
byte[] buffer = SwordUtil.readRAF(idxRaf, entry * entrysize, entrysize);
- int offset = SwordUtil.decodeLittleEndian32(buffer, 0);
- int size = -1;
+ int entryOffset = SwordUtil.decodeLittleEndian32(buffer, 0);
+ int entrySize = -1;
switch (datasize)
{
case 2:
- size = SwordUtil.decodeLittleEndian16(buffer, 4);
+ entrySize = SwordUtil.decodeLittleEndian16(buffer, 4);
break;
case 4:
- size = SwordUtil.decodeLittleEndian32(buffer, 4);
+ entrySize = SwordUtil.decodeLittleEndian32(buffer, 4);
break;
default:
assert false : datasize;
}
- return new DataIndex(offset, size);
+ return new DataIndex(entryOffset, entrySize);
}
/**
@@ -251,15 +252,20 @@
* @return the text for the entry.
* @throws IOException
*/
- public String getEntry(Key reply, DataIndex index) throws IOException
+ public DataEntry getEntry(String reply, long index, boolean decipher) throws IOException
{
+ DataIndex dataIndex = getIndex(index);
// Now read the data file for this key using the offset and size
- byte[] data = SwordUtil.readRAF(datRaf, index.getOffset(), index.getSize());
+ byte[] data = SwordUtil.readRAF(datRaf, dataIndex.getOffset(), dataIndex.getSize());
- decipher(data);
+ if (decipher)
+ {
+ decipher(data);
+ }
- return SwordUtil.decode(reply, data, getBookMetaData().getBookCharset()).trim();
+ return new DataEntry(reply, data, getBookMetaData().getBookCharset());
}
+
/*
* (non-Javadoc)
* @see org.crosswire.jsword.book.sword.AbstractBackend#getRawText(org.crosswire.jsword.passage.Key, java.lang.String)
@@ -269,24 +275,43 @@
{
checkActive();
- if (!(key instanceof IndexKey))
+ try
{
- throw new BookException(Msg.BAD_KEY, new Object[] { ClassUtil.getShortClassName(key.getClass()), key.getName() });
+ long pos = search(key.getName());
+ if (pos >= 0)
+ {
+ DataEntry entry = getEntry(key.getName(), pos, true);
+ if (entry.isLinkEntry())
+ {
+ return getRawText(entry.getLinkTarget());
+ }
+ return entry.getRawText();
+ }
+ throw new BookException(UserMsg.READ_FAIL);
}
+ catch (IOException ex)
+ {
+ throw new BookException(UserMsg.READ_FAIL, ex);
+ }
+ }
- IndexKey ikey = (IndexKey) key;
+ public String getRawText(String key) throws BookException
+ {
+ checkActive();
try
{
- String data = getEntry(ikey, ikey.getDataIndex());
-
- int keyend = data.indexOf(SEPARATOR);
- if (keyend == -1)
+ long pos = search(key);
+ if (pos >= 0)
{
- throw new BookException(UserMsg.READ_FAIL);
+ DataEntry entry = getEntry(key, pos, true);
+ if (entry.isLinkEntry())
+ {
+ return getRawText(entry.getLinkTarget());
+ }
+ return entry.getRawText();
}
-
- return data.substring(keyend + 1);
+ throw new BookException(UserMsg.READ_FAIL);
}
catch (IOException ex)
{
@@ -295,6 +320,83 @@
}
/**
+ * Find a matching entry, returning it's index. Otherwise return < 0, such that
+ * (-pos - 1) gives the insertion index.
+ * @param key
+ * @return
+ * @throws IOException
+ */
+ private long search(String key) throws IOException
+ {
+ SwordBookMetaData bmd = getBookMetaData();
+
+ boolean isDailyDevotional = bmd.getBookCategory().equals(BookCategory.DAILY_DEVOTIONS);
+
+ Calendar greg = new GregorianCalendar();
+ DateFormatter nameDF = DateFormatter.getDateInstance();
+
+ String target = key.toUpperCase(Locale.US);
+
+ long low = 1;
+ long high = getSize() - 1;
+
+ while (low <= high)
+ {
+ long mid = (low + high) >> 1;
+
+ // Get the key for the item at "mid"
+ DataEntry entry = getEntry(key, mid, true);
+ String midVal = entry.getKey();
+
+ // Massage midVal if can be.
+ if (isDailyDevotional && midVal.length() >= 3)
+ {
+ String[] spec = StringUtil.splitAll(midVal, '.');
+ greg.set(Calendar.MONTH, Integer.parseInt(spec[0]) - 1);
+ greg.set(Calendar.DATE, Integer.parseInt(spec[1]));
+ midVal = nameDF.format(greg.getTime());
+ }
+
+ int cmp = midVal.toUpperCase(Locale.US).compareTo(target);
+
+ if (cmp < 0)
+ {
+ low = mid + 1;
+ }
+ else if (cmp > 0)
+ {
+ high = mid - 1;
+ }
+ else
+ {
+ return mid; // key found
+ }
+ }
+
+ // Strong's Greek And Hebrew dictionaries have an introductory entry, so check it for a match.
+ // Get the key for the item at "mid"
+ DataEntry entry = getEntry(key, 0, true);
+ String midVal = entry.getKey();
+
+ // Massage midVal if can be.
+ if (isDailyDevotional && midVal.length() >= 3)
+ {
+ String[] spec = StringUtil.splitAll(midVal, '.');
+ greg.set(Calendar.MONTH, Integer.parseInt(spec[0]) - 1);
+ greg.set(Calendar.DATE, Integer.parseInt(spec[1]));
+ midVal = nameDF.format(greg.getTime());
+ }
+
+ int cmp = midVal.toUpperCase(Locale.US).compareTo(target);
+ if (cmp == 0)
+ {
+ return 0;
+ }
+
+ return -(low + 1); // key not found
+ }
+
+ /**
* Helper method so we can quickly activate ourselves on access
*/
protected final void checkActive()
@@ -306,34 +408,34 @@
}
/**
- * Are we active
+ * How many bytes in the offset pointers in the index
*/
- private boolean active;
+ private static final int OFFSETSIZE = 4;
/**
- * Used to separate the key name from the key value
+ * Flags whether there are open files or not
*/
- private static final byte SEPARATOR = 10; // ^M=CR=13=0x0d=\r ^J=LF=10=0x0a=\n
+ private boolean active;
/**
- * How many bytes in the offset pointers in the index
+ * The number of bytes in the size count in the index
*/
- private static final int OFFSETSIZE = 4;
+ private int datasize;
/**
- * How many bytes in the size count in the index
+ * The number of bytes for each entry in the index: either 6 or 8
*/
- private int datasize;
+ private int entrysize;
/**
- * How many bytes for each entry in the index: either 6 or 8
+ * The number of entries in the book.
*/
- private int entrysize;
+ private long size;
/**
- * The data random access file
+ * The index file
*/
- private RandomAccessFile datRaf;
+ private File idxFile;
/**
* The index random access file
@@ -346,9 +448,9 @@
private File datFile;
/**
- * The index file
+ * The data random access file
*/
- private File idxFile;
+ private RandomAccessFile datRaf;
/**
* The log stream
Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/SwordUtil.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/SwordUtil.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/SwordUtil.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -27,7 +27,6 @@
import org.crosswire.common.util.Logger;
import org.crosswire.jsword.book.DataPolice;
-import org.crosswire.jsword.passage.Key;
/**
* Various utilities used by different Sword classes.
@@ -153,8 +152,27 @@
}
/**
+ * Encode little endian data from a byte array.
+ * This assumes that the number fits in a Java integer.
+ * That is, the range of an unsigned C integer is greater than a signed Java integer.
+ * For a practical limit, 2**31 is way bigger than any document that we can have.
+ * If this ever doesn't work, use a long for the number.
+ *
+ * @param val the number to encode into little endian
+ * @param data the byte[] from which to write 4 bytes
+ * @param offset the offset into the array
+ */
+ protected static void encodeLittleEndian32(int val, byte[] data, int offset)
+ {
+ data[0 + offset] = (byte)(val & 0xFF);
+ data[1 + offset] = (byte)((val >> 8) & 0xFF);
+ data[2 + offset] = (byte)((val >> 16) & 0xFF);
+ data[3 + offset] = (byte)((val >> 24) & 0xFF);
+ }
+
+ /**
* Decode little endian data from a byte array
- * @param data the byte[] from which to read 4 bytes
+ * @param data the byte[] from which to read 2 bytes
* @param offset the offset into the array
* @return The decoded data
*/
@@ -169,6 +187,18 @@
}
/**
+ * Encode a 16-bit little endian from an integer.
+ * It is assumed that the integer's lower 16 bits are the only that are set.
+ * @param data the byte[] from which to write 2 bytes
+ * @param offset the offset into the array
+ */
+ protected static void encodeLittleEndian16(int val, byte[] data, int offset)
+ {
+ data[0 + offset] = (byte)(val & 0xFF);
+ data[1 + offset] = (byte)((val >> 8) & 0xFF);
+ }
+
+ /**
* Find a byte of data in an array
* @param data The array to search
* @param sought The data to search for
@@ -176,7 +206,19 @@
*/
protected static int findByte(byte[] data, byte sought)
{
- for (int i = 0; i < data.length; i++)
+ return findByte(data, 0, sought);
+ }
+
+ /**
+ * Find a byte of data in an array
+ * @param data The array to search
+ * @param offset The position in the array to begin looking
+ * @param sought The data to search for
+ * @return The index of the found position or -1 if not found
+ */
+ protected static int findByte(byte[] data, int offset, byte sought)
+ {
+ for (int i = offset; i < data.length; i++)
{
if (data[i] == sought)
{
@@ -190,24 +232,42 @@
/**
* Transform a byte array into a string given the encoding.
* If the encoding is bad then it just does it as a string.
+ *
* @param data The byte array to be converted
* @param charset The encoding of the byte array
* @return a string that is UTF-8 internally
*/
- public static String decode(Key key, byte[] data, String charset)
+ public static String decode(String key, byte[] data, String charset)
{
- return decode(key, data, data.length, charset);
+ return decode(key, data, 0, data.length, charset);
}
/**
- * Transform a byte array into a string given the encoding.
+ * Transform a portion of a byte array into a string given the encoding.
* If the encoding is bad then it just does it as a string.
+ *
* @param data The byte array to be converted
+ * @param length The number of bytes to use.
* @param charset The encoding of the byte array
* @return a string that is UTF-8 internally
*/
- public static String decode(Key key, byte[] data, int length, String charset)
+ public static String decode(String key, byte[] data, int length, String charset)
{
+ return decode(key, data, 0, length, charset);
+ }
+
+ /**
+ * Transform a portion of a byte array starting at an offset into a string given the encoding.
+ * If the encoding is bad then it just does it as a string.
+ *
+ * @param data The byte array to be converted
+ * @param offset The starting position in the byte array
+ * @param length The number of bytes to use.
+ * @param charset The encoding of the byte array
+ * @return a string that is UTF-8 internally
+ */
+ public static String decode(String key, byte[] data, int offset, int length, String charset)
+ {
if ("WINDOWS-1252".equals(charset)) //$NON-NLS-1$
{
clean1252(key, data, length);
@@ -215,13 +275,13 @@
String txt = ""; //$NON-NLS-1$
try
{
- txt = new String(data, 0, length, charset);
+ txt = new String(data, offset, length, charset);
}
catch (UnsupportedEncodingException ex)
{
// It is impossible! In case, use system default...
log.error(key + ": Encoding: " + charset + " not supported", ex); //$NON-NLS-1$ //$NON-NLS-2$
- txt = new String(data, 0, length);
+ txt = new String(data, offset, length);
}
return txt;
@@ -233,7 +293,7 @@
* and in UTF-8 or are non-printing control characters in the range
* of 0-32.
*/
- public static void clean1252(Key key, byte[] data)
+ public static void clean1252(String key, byte[] data)
{
clean1252(key, data, data.length);
}
@@ -244,7 +304,7 @@
* and in UTF-8 or are non-printing control characters in the range
* of 0-32.
*/
- public static void clean1252(Key key, byte[] data, int length)
+ public static void clean1252(String key, byte[] data, int length)
{
for (int i = 0; i < length; i++)
{
@@ -255,7 +315,7 @@
|| (c == 0x81 || c == 0x8D || c == 0x8F || c == 0x90 || c == 0x9D))
{
data[i] = 0x20;
- DataPolice.report(key.getName() + " has bad character 0x" + Integer.toString(c, 16) + " at position " + i + " in input."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ DataPolice.report(key + " has bad character 0x" + Integer.toString(c, 16) + " at position " + i + " in input."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}
Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/TreeKeyIndex.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/TreeKeyIndex.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/TreeKeyIndex.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -163,7 +163,7 @@
Key key = new DefaultKeyList(null, bmd.getName());
// Some of the keys have extraneous whitespace, so remove it.
- node.setName(SwordUtil.decode(key, buffer, size, bmd.getBookCharset()).trim());
+ node.setName(SwordUtil.decode(key.getName(), buffer, size, bmd.getBookCharset()).trim());
buffer = SwordUtil.readNextRAF(datRaf, 2);
int userDataSize = SwordUtil.decodeLittleEndian16(buffer, 0);
Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/ZLDBackend.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/ZLDBackend.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/ZLDBackend.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -103,32 +103,6 @@
{
super(sbmd);
- String path = getExpandedDataPath();
-
- idxFile = new File(path + EXTENSION_INDEX);
- datFile = new File(path + EXTENSION_DATA);
- zdxFile = new File(path + EXTENSION_Z_INDEX);
- zdtFile = new File(path + EXTENSION_Z_DATA);
-
- if (!idxFile.canRead())
- {
- throw new BookException(UserMsg.READ_FAIL, new Object[] { idxFile.getAbsolutePath() });
- }
-
- if (!datFile.canRead())
- {
- throw new BookException(UserMsg.READ_FAIL, new Object[] { datFile.getAbsolutePath() });
- }
-
- if (!zdxFile.canRead())
- {
- throw new BookException(UserMsg.READ_FAIL, new Object[] { zdxFile.getAbsolutePath() });
- }
-
- if (!zdtFile.canRead())
- {
- throw new BookException(UserMsg.READ_FAIL, new Object[] { zdtFile.getAbsolutePath() });
- }
}
/* (non-Javadoc)
@@ -138,6 +112,46 @@
{
try
{
+ String path = null;
+ try
+ {
+ path = getExpandedDataPath().getPath();
+ }
+ catch (BookException e)
+ {
+ Reporter.informUser(this, e);
+ return;
+ }
+
+ idxFile = new File(path + EXTENSION_INDEX);
+ datFile = new File(path + EXTENSION_DATA);
+ zdxFile = new File(path + EXTENSION_Z_INDEX);
+ zdtFile = new File(path + EXTENSION_Z_DATA);
+
+ if (!idxFile.canRead())
+ {
+ Reporter.informUser(this, new BookException(UserMsg.READ_FAIL, new Object[] { idxFile.getAbsolutePath() }));
+ return;
+ }
+
+ if (!datFile.canRead())
+ {
+ Reporter.informUser(this, new BookException(UserMsg.READ_FAIL, new Object[] { datFile.getAbsolutePath() }));
+ return;
+ }
+
+ if (!zdxFile.canRead())
+ {
+ Reporter.informUser(this, new BookException(UserMsg.READ_FAIL, new Object[] { zdxFile.getAbsolutePath() }));
+ return;
+ }
+
+ if (!zdtFile.canRead())
+ {
+ Reporter.informUser(this, new BookException(UserMsg.READ_FAIL, new Object[] { zdtFile.getAbsolutePath() }));
+ return;
+ }
+
idxRaf = new RandomAccessFile(idxFile, FileUtil.MODE_READ);
datRaf = new RandomAccessFile(datFile, FileUtil.MODE_READ);
zdxRaf = new RandomAccessFile(zdxFile, FileUtil.MODE_READ);
@@ -242,7 +256,7 @@
byte[] keydata = new byte[keyend];
System.arraycopy(data, 0, keydata, 0, keyend);
- String keytitle = SwordUtil.decode(keys, keydata, charset).trim();
+ String keytitle = SwordUtil.decode(keys.getName(), keydata, charset).trim();
// for some weird reason plain text (i.e. SourceType=0) dicts
// all get \ added to the ends of the index entries.
@@ -371,7 +385,7 @@
byte[] entryBytes = new byte[entrySize];
System.arraycopy(uncompressed, entryStart, entryBytes, 0, entrySize);
- return SwordUtil.decode(key, entryBytes, charset).trim();
+ return SwordUtil.decode(key.getName(), entryBytes, charset).trim();
}
catch (IOException e)
{
Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/ZVerseBackend.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/ZVerseBackend.java 2008-04-10 21:04:38 UTC (rev 1791)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/sword/ZVerseBackend.java 2008-04-10 21:16:23 UTC (rev 1792)
@@ -25,12 +25,14 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.net.URI;
import org.crosswire.common.activate.Activator;
import org.crosswire.common.activate.Lock;
import org.crosswire.common.compress.CompressorType;
import org.crosswire.common.util.FileUtil;
import org.crosswire.common.util.Logger;
+import org.crosswire.common.util.NetUtil;
import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;
import org.crosswire.jsword.passage.KeyUtil;
@@ -102,28 +104,10 @@
/**
* Simple ctor
*/
- public ZVerseBackend(SwordBookMetaData sbmd, BlockType blockType) throws BookException
+ public ZVerseBackend(SwordBookMetaData sbmd, BlockType blockType)
{
super(sbmd);
-
- String path = getExpandedDataPath();
- String otAllButLast = path + File.separator + SwordConstants.FILE_OT + '.' + blockType.getIndicator() + SUFFIX_PART1;
- idxFile[SwordConstants.TESTAMENT_OLD] = new File(otAllButLast + SUFFIX_INDEX);
- textFile[SwordConstants.TESTAMENT_OLD] = new File(otAllButLast + SUFFIX_TEXT);
- compFile[SwordConstants.TESTAMENT_OLD] = new File(otAllButLast + SUFFIX_COMP);
-
- String ntAllButLast = path + File.separator + SwordConstants.FILE_NT + '.' + blockType.getIndicator() + SUFFIX_PART1;
- idxFile[SwordConstants.TESTAMENT_NEW] = new File(ntAllButLast + SUFFIX_INDEX);
- textFile[SwordConstants.TESTAMENT_NEW] = new File(ntAllButLast + SUFFIX_TEXT);
- compFile[SwordConstants.TESTAMENT_NEW] = new File(ntAllButLast + SUFFIX_COMP);
-
- // It is an error to be neither OT nor NT
- if (!textFile[SwordConstants.TESTAMENT_OLD].canRead()
- && !textFile[SwordConstants.TESTAMENT_NEW].canRead())
- {
- log.error("Failed to find OT or NT files: '" + otAllButLast + SUFFIX_TEXT + "' and '" + ntAllButLast + SUFFIX_TEXT + "'"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- throw new BookException(Msg.MISSING_FILE, new Object[] { path });
- }
+ this.blockType = blockType;
}
/* (non-Javadoc)
@@ -131,6 +115,35 @@
*/
public final void activate(Lock lock)
{
+ try
+ {
+ if (idxFile[SwordConstants.TESTAMENT_OLD] == null)
+ {
+ URI path = getExpandedDataPath();
+ String otAllButLast = NetUtil.lengthenURI(path, File.separator + SwordConstants.FILE_OT + '.' + blockType.getIndicator() + SUFFIX_PART1).getPath();
+ idxFile[SwordConstants.TESTAMENT_OLD] = new File(otAllButLast + SUFFIX_INDEX);
+ textFile[SwordConstants.TESTAMENT_OLD] = new File(otAllButLast + SUFFIX_TEXT);
+ compFile[SwordConstants.TESTAMENT_OLD] = new File(otAllButLast + SUFFIX_COMP);
+
+ String ntAllButLast = NetUtil.lengthenURI(path, File.separator + SwordConstants.FILE_NT + '.' + blockType.getIndicator() + SUFFIX_PART1).getPath();
+ idxFile[SwordConstants.TESTAMENT_NEW] = new File(ntAllButLast + SUFFIX_INDEX);
+ textFile[SwordConstants.TESTAMENT_NEW] = new File(ntAllButLast + SUFFIX_TEXT);
+ compFile[SwordConstants.TESTAMENT_NEW] = new File(ntAllButLast + SUFFIX_COMP);
+ }
+ }
+ catch (BookException e)
+ {
+ idxFile[SwordConstants.TESTAMENT_OLD] = null;
+ textFile[SwordConstants.TESTAMENT_OLD] = null;
+ compFile[SwordConstants.TESTAMENT_OLD] = null;
+
+ idxFile[SwordConstants.TESTAMENT_NEW] = null;
+ textFile[SwordConstants.TESTAMENT_NEW] = null;
+ compFile[SwordConstants.TESTAMENT_NEW] = null;
+
+ return;
+ }
+
if (idxFile[SwordConstants.TESTAMENT_OLD].canRead())
{
try
@@ -244,7 +257,7 @@
return ""; //$NON-NLS-1$
}
- // 10 because we the index is 10 bytes long for each verse
+ // 10 because the index is 10 bytes long for each verse
byte[] temp = SwordUtil.readRAF(compRaf[testament], index * COMP_ENTRY_SIZE, COMP_ENTRY_SIZE);
// If the Bible does not contain the desired verse, return nothing.
@@ -296,7 +309,7 @@
byte[] chopped = new byte[verseSize];
System.arraycopy(uncompressed, verseStart, chopped, 0, verseSize);
- return SwordUtil.decode(key, chopped, charset);
+ return SwordUtil.decode(key.getName(), chopped, charset);
}
catch (IOException e)
{
@@ -326,6 +339,11 @@
}
/**
+ * Whether the book is blocked by Book, Chapter or Verse.
+ */
+ private BlockType blockType;
+
+ /**
*
*/
private int lastTestament = -1;
More information about the jsword-svn
mailing list