DevTools:JSword/Personal Commentary

From CrossWire Bible Society
Jump to: navigation, search


JSword does not currently support the SWORD Personal Commentary Module. The purpose of this page is to discuss what is necessary to create one. Once implemented and documented in the code, this page should no longer be necessary.

Structure of a SWORD Personal Commentary Module

The SWORD Personal Commentary Module is of type RawFiles.

The empty module consists of the following files:

  • In mods.d:
    • personal.conf
  • In modules/comments/rawfiles/personal
    • ot -- Old Testament data
    • ot.vss -- Old Testament index
    • nt -- New Testament data
    • nt.vss -- New Testament index

nt and ot are the data files. nt.vss and ot.vss are the index files.

After writing the first comment on a verse, two more files appear:

  • incfile -- This file has an entry indicating how many verses have been written.
  • 0000000 -- This file has the content of the first comment.

If the written entry is on a New Testament verse, then nt and nt.vss are modified. If it is an Old Testament verse, then ot and ot.vss are modified.

Editing the comment on the same verse changes the 0000000 file but not any other files.

Upon writing a second verse another file appears:

  • 0000001 -- The content of the second verse's comment.

So these files represent the comments that have been written.

While the stock Personal Commentary module is not encrypted, SWORD allows for it to be.


This file merely contains a 32-bit little-endian integer, indicating how many verses have been written. It can also be thought of as containing the number for the next entry.

Index file structure (ot.vss and nt.vss)

In SWORD, the index file is an ordered list of entries, with each entry consisting of the tuple (offset,size). In a SWORD module, offset is always a 32-bit little-endian integer. Size can be either 32-bit or 16-bit. In the case of the Personal commentary it is 16-bit. (This can be easily seen by comparing the size of the index to a rawcom (16-bit) and a rawcom4 (32-bit) commentary.)

The index is ordered by the canonical ordering of all the verses in the KJV canon with allowances for meta-verses to hold testament, book and chapter introductions.

Doing a binary compare on the index of before and after writes, will show that the size is always 9 and the offset is always 9 bytes shy of the end of the data file. That is the index records an append to the datafile.

Data file structure (ot and nt)

Each write of the data file appends 9 bytes to the end. These 9 bytes are the name of the file (7 characters) followed by a windows-style newline (\r\n). Note, anything in the datafile that is not indexed is garbage and both the ot and nt files contain garbage.

General Structure of a JSword Driver


Currently, JSword does not have support for reading or writing a Personal Commentary. So this section will give the anatomy of a JSword driver for a SWORD module.

In JSword, modules are known as Books. The capabilities of a Book are described by BookMetaData. A SWORD module will have a derivative SwordBookMetaData, which essentially is a representation of the module's conf.

The piece of code that actually reads or writes a Book is a Backend, actually a derivative of AbstractBackend. The code that puts a SwordBookMetaData together with an AbstractBackend is BookType.fromString(...). Currently, this will return a RawBackend for a module type of RawFiles. This needs to point to a new AbstractBackend.

If we added write capabilites to RawBackend, the code would work but there would be some problems:

  • It would be a RawText or RawCom (these are the same) layout.
  • Other SWORD applications would not be able to read it as they would expect a different layout.
  • The RawText and RawCom will, when writing is added, append entries to the data file and repoint the index to the new location. The old data remains in the file. Over time this would leave to unbounded growth.

Base Class

The Backend of a Personal Commentary is a variation of a RawBackend. The index is the same, with a datasize of 2. The data file is read the same as before, but rather than

Existing Methods

The following methods probably don't need to change:

  • contains(Key key);
  • getRawText(Key key);
  • isWriteable();
  • activate(Lock lock);
  • deactivate(Lock lock);
  • checkActive();
  • getIndex(RandomAccessFile raf, long entry);

The following method does need to change:

  • getEntry(String name, int testament, long index);

It needs to read the index and validate the result. Then it needs to read the data file to get the filename, removing trailing \r\n and any other spurious whitespace. (It won't be enciphered and its content is ASCII.) Then the named file needs to be read in its entirety, deciphered, decoded with the proper charset, stuffed into a string and returned.

The following method is currently unused and incomplete:

  • create(String path);

Its purpose is to create the files if they do not exist. It does not do that.

Unimplemented Base Class Methods

The following method in AbstractBackend needs to be implemented.
setRawText(Key key, String text);
It needs to do the following:

  • If the path to the files does not exist those directories should be created, with write permissions for the final directory.
  • If the index and data files for the testament do not exist, they should be created. The index file should be pre-populated with entries for every verse in the canon and each index should contain (0,0). The data file can be empty.
  • If this did not work, throw an IOException.
  • Then close the files and then call checkActive(); (checkActive will properly open the module for use and prevents another thread from closing this thread.)
  • The index needs to be read to determine whether the index points to something or not.
    • If it does, the data file should be read to get the filename.
    • If it does not then a new file name should be obtained, created and its name stored in the appropriate data file, with the index pointing to it.
    • If any of this does not work, an IOException should be thrown.
  • The text should be encoded into a byte array as either UTF-8 or cp-1252.
  • The byte array should be handled to the encipher routine (which does nothing if the SwordBookMetaData does not indicate encryption)
  • Then the byte array should be written to the appropriate file and that file should be closed.

incfile and filename generation

A private method should be added that works on "incfile" to generate a new file name:
If the file does not exist, it should create it, open it, write a 1 to it as a 32-bit little endian number and close it. It should then generate a file name of '0000000' (Note: The SWORD convention is to zero padded the number to a total of 7 digits, but it doesn't matter what the methodology actually is. It just needs to make sure that it uses incfile the same way. It would be fine to call it anything. E.g. 1.comment. And it would work with multiple SWORD applications generating other kinds of file names. The reason it doesn't matter is that the module records the name that was generated in the data file.)

This routine should be synchronized so that two different threads don't collide.