[jsword-svn] r1350 - in trunk: bibledesktop/src/main/java/org/crosswire/bibledesktop/book common/src/main/java/org/crosswire/common/diff common-swing/src/main/java/org/crosswire/common/config/swing jsword/src/main/java/org/crosswire/jsword/book jsword/src/main/java/org/crosswire/jsword/book/basic

dmsmith at www.crosswire.org dmsmith at www.crosswire.org
Fri May 25 08:31:58 MST 2007


Author: dmsmith
Date: 2007-05-25 08:31:57 -0700 (Fri, 25 May 2007)
New Revision: 1350

Added:
   trunk/common/src/main/java/org/crosswire/common/diff/Distance.java
   trunk/jsword/src/main/java/org/crosswire/jsword/book/BookComparators.java
Modified:
   trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/BooksComboBoxModel.java
   trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/BooksListModel.java
   trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/DisplaySelectPane.java
   trunk/common-swing/src/main/java/org/crosswire/common/config/swing/OptionsField.java
   trunk/jsword/src/main/java/org/crosswire/jsword/book/Book.java
   trunk/jsword/src/main/java/org/crosswire/jsword/book/DefaultBook.java
   trunk/jsword/src/main/java/org/crosswire/jsword/book/basic/AbstractBook.java
Log:
Added the ability to find the edit distance between two strings.
Added the ability to find a book by an inexact match.
Added the ability to sort a book list in different ways.

Modified: trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/BooksComboBoxModel.java
===================================================================
--- trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/BooksComboBoxModel.java	2007-05-24 21:01:53 UTC (rev 1349)
+++ trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/BooksComboBoxModel.java	2007-05-25 15:31:57 UTC (rev 1350)
@@ -23,6 +23,7 @@
 
 import java.io.IOException;
 import java.io.ObjectInputStream;
+import java.util.Comparator;
 
 import javax.swing.ComboBoxModel;
 
@@ -45,7 +46,7 @@
      */
     public BooksComboBoxModel()
     {
-        this(null);
+        this(null, null);
     }
 
     /**
@@ -53,7 +54,14 @@
      */
     public BooksComboBoxModel(BookFilter filter)
     {
-        super(filter);
+        this(filter, null);
+    }
+    /**
+     * Basic Constructor
+     */
+    public BooksComboBoxModel(BookFilter filter, Comparator comparator)
+    {
+        super(filter, comparator);
 
         postCacheData();
 

Modified: trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/BooksListModel.java
===================================================================
--- trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/BooksListModel.java	2007-05-24 21:01:53 UTC (rev 1349)
+++ trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/BooksListModel.java	2007-05-25 15:31:57 UTC (rev 1350)
@@ -25,6 +25,7 @@
 import java.io.ObjectInputStream;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import javax.swing.AbstractListModel;
@@ -55,7 +56,7 @@
      */
     public BooksListModel()
     {
-        this(null);
+        this(null, null);
     }
 
     /**
@@ -63,16 +64,25 @@
      */
     public BooksListModel(BookFilter filter)
     {
-        this(filter, Books.installed());
+        this(filter, Books.installed(), null);
     }
 
     /**
-     * Basic constructor
+     * Basic constructor, redefining ordering.
      */
-    public BooksListModel(BookFilter filter, BookList bookList)
+    public BooksListModel(BookFilter filter, Comparator comp)
     {
+        this(filter, Books.installed(), comp);
+    }
+
+    /**
+     * Basic constructor for a filtered list of books, ordered as requested.
+     */
+    public BooksListModel(BookFilter filter, BookList bookList, Comparator comparator)
+    {
         this.filter = filter;
         this.bookList = bookList;
+        this.comparator = comparator;
 
         cacheData();
     }
@@ -159,7 +169,7 @@
     {
         books = new ArrayList();
         books.addAll(bookList.getBooks(filter));
-        Collections.sort(books);
+        Collections.sort(books, comparator);
     }
 
     /**
@@ -239,6 +249,11 @@
     protected List books;
 
     /**
+     * The sort algorithm to use.
+     */
+    protected Comparator comparator;
+
+    /**
      * The log stream
      */
     private static final Logger log = Logger.getLogger(BooksListModel.class);

Modified: trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/DisplaySelectPane.java
===================================================================
--- trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/DisplaySelectPane.java	2007-05-24 21:01:53 UTC (rev 1349)
+++ trunk/bibledesktop/src/main/java/org/crosswire/bibledesktop/book/DisplaySelectPane.java	2007-05-25 15:31:57 UTC (rev 1350)
@@ -54,6 +54,7 @@
 import org.crosswire.common.swing.desktop.event.TitleChangedListener;
 import org.crosswire.common.util.Reporter;
 import org.crosswire.jsword.book.Book;
+import org.crosswire.jsword.book.BookComparators;
 import org.crosswire.jsword.book.BookException;
 import org.crosswire.jsword.book.BookFilters;
 import org.crosswire.jsword.index.IndexStatus;
@@ -106,7 +107,7 @@
         };
 
         // search() and version() rely on this returning only Books indexed by verses
-        mdlBible = new BooksComboBoxModel(BookFilters.getBibles());
+        mdlBible = new BooksComboBoxModel(BookFilters.getBibles(), BookComparators.getInitialComparator());
         JComboBox cboBible = new JComboBox(mdlBible);
         cboBible.setPrototypeDisplayValue(" "); //$NON-NLS-1$
         selected = mdlBible.getSelectedBook();

Added: trunk/common/src/main/java/org/crosswire/common/diff/Distance.java
===================================================================
--- trunk/common/src/main/java/org/crosswire/common/diff/Distance.java	                        (rev 0)
+++ trunk/common/src/main/java/org/crosswire/common/diff/Distance.java	2007-05-25 15:31:57 UTC (rev 1350)
@@ -0,0 +1,116 @@
+/**
+ * Distribution License:
+ * JSword is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License, version 2.1 as published by
+ * the Free Software Foundation. This program is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * The License is available on the internet at:
+ *       http://www.gnu.org/copyleft/lgpl.html
+ * or by writing to:
+ *      Free Software Foundation, Inc.
+ *      59 Temple Place - Suite 330
+ *      Boston, MA 02111-1307, USA
+ *
+ * Copyright: 2007
+ *     The copyright to this program is held by it's authors.
+ *
+ * ID: $Id: org.eclipse.jdt.ui.prefs 1178 2006-11-06 12:48:02Z dmsmith $
+ */
+
+package org.crosswire.common.diff;
+
+/**
+ * Compute the distance between 2 strings. The larger the number the greater the distance.
+ * 
+ * @see gnu.lgpl.License for license details.<br>
+ *      The copyright to this program is held by it's authors.
+ * @author DM Smith [dmsmith555 at yahoo dot com]
+ */
+public class Distance
+{
+    /**
+     * Compute the LevenshteinDistance between two strings.
+     * See <a href="http://www.merriampark.com/ldjava.htm">www.merriampark.com/ldjava.htm</a> for original implementation.
+     * @param source the baseline text
+     * @param target the changed text
+     * @return the distance
+     */
+    public static int getLevenshteinDistance (String source, String target)
+    {
+        if (source == null || target == null)
+        {
+            throw new IllegalArgumentException("Strings must not be null"); //$NON-NLS-1$
+        }
+
+        /*
+           The difference between this impl. and the previous is that, rather 
+           than creating and retaining a matrix of size s.length()+1 by t.length()+1, 
+           we maintain two single-dimensional arrays of length s.length()+1.  The first, d,
+           is the 'current working' distance array that maintains the newest distance cost
+           counts as we iterate through the characters of String s.  Each time we increment
+           the index of String t we are comparing, d is copied to p, the second int[].  Doing so
+           allows us to retain the previous cost counts as required by the algorithm (taking 
+           the minimum of the cost count to the left, up one, and diagonally up and to the left
+           of the current cost count being calculated).  (Note that the arrays aren't really 
+           copied anymore, just switched...this is clearly much better than cloning an array 
+           or doing a System.arraycopy() each time  through the outer loop.)
+
+           Effectively, the difference between the two implementations is this one does not 
+           cause an out of memory condition when calculating the LD over two very large strings.        
+         */      
+
+        int sourceLength = source.length(); // length of source
+        int targetLength = target.length(); // length of target
+
+        if (sourceLength == 0)
+        {
+            return targetLength;
+        }
+        else if (targetLength == 0)
+        {
+            return sourceLength;
+        }
+
+        int prevDist[] = new int[sourceLength + 1]; //'previous' cost array, horizontally
+        int dist[] = new int[sourceLength + 1]; // cost array, horizontally
+        int swap[]; //placeholder to assist in swapping prevDist and dist
+
+        // indexes into strings source and target
+        int i; // iterates through source
+        int j; // iterates through target
+
+        char targetJ; // jth character of t
+
+        int cost;
+
+        for (i = 0; i <= sourceLength; i++)
+        {
+            prevDist[i] = i;
+        }
+
+        for (j = 1; j <= targetLength; j++)
+        {
+            targetJ = target.charAt(j - 1);
+            dist[0] = j;
+
+            for (i = 1; i <= sourceLength; i++)
+            {
+                cost = source.charAt(i - 1) == targetJ ? 0 : 1;
+                // minimum of cell to the left + 1, to the top + 1, diagonally left and up +cost
+                dist[i] = Math.min(Math.min(dist[i - 1] + 1, prevDist[i] + 1),  prevDist[i - 1] + cost);
+            }
+
+            // copy current distance counts to 'previous row' distance counts
+            swap = prevDist;
+            prevDist = dist;
+            dist = swap;
+        }
+
+        // our last action in the above loop was to switch d and p, so p now 
+        // actually has the most recent cost counts
+        return prevDist[sourceLength];
+    }
+}


Property changes on: trunk/common/src/main/java/org/crosswire/common/diff/Distance.java
___________________________________________________________________
Name: svn:kewords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Modified: trunk/common-swing/src/main/java/org/crosswire/common/config/swing/OptionsField.java
===================================================================
--- trunk/common-swing/src/main/java/org/crosswire/common/config/swing/OptionsField.java	2007-05-24 21:01:53 UTC (rev 1349)
+++ trunk/common-swing/src/main/java/org/crosswire/common/config/swing/OptionsField.java	2007-05-25 15:31:57 UTC (rev 1350)
@@ -29,6 +29,7 @@
 
 import org.crosswire.common.config.Choice;
 import org.crosswire.common.config.MultipleChoice;
+import org.crosswire.common.diff.Distance;
 import org.crosswire.common.util.Logger;
 
 /**
@@ -103,6 +104,7 @@
     {
         if (list != null && list.length > 0)
         {
+            int distance = value.length();
             for (int i = 0; i < list.length; i++)
             {
                 if (value.equals(list[i]))
@@ -110,9 +112,28 @@
                     combo.setSelectedItem(list[i]);
                     return;
                 }
+                distance = Math.max(distance, list[i].length());
             }
 
-            combo.setSelectedItem(list[0]);
+            // We didn't find an exact match so look for the closest one.
+            distance++; // A number larger than the length of any of the strings in question.
+            int bestMatch = 0;
+            for (int i = 0; i < list.length; i++)
+            {
+                int newDistance = Distance.getLevenshteinDistance(value, list[i]);
+                if (distance > newDistance)
+                {
+                    bestMatch = i;
+                    distance = newDistance;
+                }
+            }
+
+            combo.setSelectedItem(list[bestMatch]);
+            if (bestMatch > 0)
+            {
+                log.warn("Checked for options without finding exact match: '" + value + "'. Best match is: " + combo.getSelectedItem());  //$NON-NLS-1$//$NON-NLS-2$
+                return;
+            }
         }
 
         log.warn("Checked for options without finding: '" + value + "'. Defaulting to first option: " + combo.getSelectedItem());  //$NON-NLS-1$//$NON-NLS-2$

Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/Book.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/Book.java	2007-05-24 21:01:53 UTC (rev 1349)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/Book.java	2007-05-25 15:31:57 UTC (rev 1350)
@@ -185,6 +185,15 @@
     String getFullName();
 
     /**
+     * Return the likelihood that we have a match.
+     * This allows for calling the book different things
+     * and still be found.
+     * @param name
+     * @return true if we have a match.
+     */
+    boolean match(String name);
+
+    /**
      * Indicate whether this book is supported by JSword.
      * Since the expectation is that all books are supported,
      * abstract implementations should return true and let

Added: trunk/jsword/src/main/java/org/crosswire/jsword/book/BookComparators.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/BookComparators.java	                        (rev 0)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/BookComparators.java	2007-05-25 15:31:57 UTC (rev 1350)
@@ -0,0 +1,79 @@
+/**
+ * Distribution License:
+ * JSword is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License, version 2.1 as published by
+ * the Free Software Foundation. This program is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * The License is available on the internet at:
+ *       http://www.gnu.org/copyleft/lgpl.html
+ * or by writing to:
+ *      Free Software Foundation, Inc.
+ *      59 Temple Place - Suite 330
+ *      Boston, MA 02111-1307, USA
+ *
+ * Copyright: 2005
+ *     The copyright to this program is held by it's authors.
+ *
+ * ID: $Id$
+ */
+package org.crosswire.jsword.book;
+
+import java.util.Comparator;
+
+import org.crosswire.common.util.Logger;
+
+/**
+ * Provides different ways to sort Books.
+ * 
+ * @see gnu.lgpl.License for license details.
+ *      The copyright to this program is held by it's authors.
+ * @author DM Smith [dmsmith555 at yahoo dot com]
+ */
+public final class BookComparators
+{
+    /**
+     * Ensure we cant be created
+     */
+    private BookComparators()
+    {
+    }
+
+    /**
+     * Order by default Book ordering
+     */
+    public static Comparator getDefault()
+    {
+        return new Comparator()
+        {
+            public int compare(Object o1, Object o2)
+            {
+                return ((Book)o1).compareTo(o2);
+            }
+            
+        };
+    }
+
+    /**
+     * Order by Initials.
+     */
+    public static Comparator getInitialComparator()
+    {
+        return new Comparator()
+        {
+            public int compare(Object o1, Object o2)
+            {
+                Book b1 = (Book) o1;
+                Book b2 = (Book) o2;
+                return b1.getInitials().compareTo(b2.getInitials());
+            }
+        };
+    }
+
+    /**
+     * The log stream
+     */
+    static final Logger log = Logger.getLogger(BookComparators.class);
+}


Property changes on: trunk/jsword/src/main/java/org/crosswire/jsword/book/BookComparators.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/DefaultBook.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/DefaultBook.java	2007-05-24 21:01:53 UTC (rev 1349)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/DefaultBook.java	2007-05-25 15:31:57 UTC (rev 1350)
@@ -135,7 +135,7 @@
         while (iter.hasNext())
         {
             Book aBook = (Book) iter.next();
-            if (name.equals(aBook.getFullName()))
+            if (aBook.match(name))
             {
                 setDefault(aBook);
                 return;

Modified: trunk/jsword/src/main/java/org/crosswire/jsword/book/basic/AbstractBook.java
===================================================================
--- trunk/jsword/src/main/java/org/crosswire/jsword/book/basic/AbstractBook.java	2007-05-24 21:01:53 UTC (rev 1349)
+++ trunk/jsword/src/main/java/org/crosswire/jsword/book/basic/AbstractBook.java	2007-05-25 15:31:57 UTC (rev 1350)
@@ -149,6 +149,66 @@
     }
 
     /* (non-Javadoc)
+     * @see org.crosswire.jsword.book.Book#match(java.lang.String)
+     */
+    public boolean match(String name)
+    {
+        if (name == null)
+        {
+            return false;
+        }
+        if (name.equals(getInitials()))
+        {
+            return true;
+        }
+        if (name.equals(getName()))
+        {
+            return true;
+        }
+        if (name.equals(getFullName()))
+        {
+            return true;
+        }
+        if (name.equals(toString()))
+        {
+            return true;
+        }
+        if (name.equalsIgnoreCase(getInitials()))
+        {
+            return true;
+        }
+        if (name.equalsIgnoreCase(getName()))
+        {
+            return true;
+        }
+        if (name.equalsIgnoreCase(getFullName()))
+        {
+            return true;
+        }
+        if (name.equalsIgnoreCase(toString()))
+        {
+            return true;
+        }
+        if (name.startsWith(getInitials()))
+        {
+            return true;
+        }
+        if (name.startsWith(getName()))
+        {
+            return true;
+        }
+        if (name.startsWith(getFullName()))
+        {
+            return true;
+        }
+        if (name.startsWith(toString()))
+        {
+            return true;
+        }
+        return false;
+    }
+
+    /* (non-Javadoc)
      * @see org.crosswire.jsword.book.BookMetaData#getIndexStatus()
      */
     public IndexStatus getIndexStatus()




More information about the jsword-svn mailing list