[jsword-svn] jsword/java/limbo/org/crosswire/jsword/book/search/parse s

jswordcvs at crosswire.org jswordcvs at crosswire.org
Sun Apr 24 18:22:25 MST 2005


Update of /cvs/jsword/jsword/java/limbo/org/crosswire/jsword/book/search/parse
In directory www.crosswire.org:/tmp/cvs-serv22949/java/limbo/org/crosswire/jsword/book/search/parse

Added Files:
	Word.java package.html Word.properties SubLeftParamWord.java 
	RemoveCommandWord.java StartsParamWord.java 
	PassageLeftParamWord.java IndexSearcher.java 
	PassageRightParamWord.java SubRightParamWord.java 
	BlurCommandWord.java CommandWord.java PhraseParamWord.java 
	Msg.java AddCommandWord.java GrammarParamWord.java 
	CustomTokenizer.java Msg.properties ParamWord.java 
	DefaultWord.java RetainCommandWord.java 
Log Message:
Re-implemented the search language to use lucene syntax with range and blur as extensions.

--- NEW FILE: BlurCommandWord.java ---
package org.crosswire.jsword.book.search.parse;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;
import org.crosswire.jsword.passage.RestrictionType;

/**
 * Alter the Passage by calling blur with a
 * number grabbed from the next word in the search string.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: BlurCommandWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class BlurCommandWord implements CommandWord
{
    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.CommandWord#updatePassage(org.crosswire.jsword.book.search.parse.Searcher, org.crosswire.jsword.passage.Passage)
     */
    public void updatePassage(IndexSearcher engine, Key key) throws BookException
    {
        String word = engine.iterateWord();

        try
        {
            key.blur(Integer.parseInt(word), RestrictionType.getDefaultBlurRestriction());
        }
        catch (NumberFormatException ex)
        {
            throw new BookException(Msg.BLUR_FORMAT, ex, new Object[] { word });
        }
    }
}

--- NEW FILE: ParamWord.java ---
package org.crosswire.jsword.book.search.parse;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;

/**
 * A ParamWord extends Word to provide more information
 * to a CommandWord. This will either be in the form of a String
 * or in the form of a Passage (from a search)
 * ParamWords are used by CommandWords that alter the final
 * Passage.
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: ParamWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public interface ParamWord extends Word
{
    /**
     * Get a word for something else to word on.
     * @param engine The controller that can provide access to the search
     *               string or a default Bible.
     * @return The requested text
     * @exception BookException If this action is not appropriate
     */
    public String getWord(IndexSearcher engine) throws BookException;

    /**
     * Get a Passage or throw-up if that is not appropriate
     * for this Word.
     * @param engine The controller that can provide access to the search
     *               string or a default Bible.
     * @return A Passage relevant to this command
     * @exception BookException If this action is not appropriate
     */
    public Key getKeyList(IndexSearcher engine) throws BookException;
}

--- NEW FILE: CustomTokenizer.java ---
package org.crosswire.jsword.book.search.parse;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.crosswire.jsword.book.BookException;

/**
 * Our command line parsing is a little specialized, so StringTokenizer is not
 * up to the job. The specific problem is that there is sometimes no separator
 * between parts of the command, and since this is specialized we also leave the
 * results in a Vector of SearchWords.
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: CustomTokenizer.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class CustomTokenizer
{
    /**
     * Prevent Instansiation
     */
    private CustomTokenizer()
    {
    }

    /**
     * Convenience method to generate a Vector of SearchWords
     * @param sought The text to parse
     * @param commands The Hashtable of SearchWords to select from
     * @return A List of selected SearchWords
     */
    public static List tokenize(String sought, Map commands) throws BookException
    {
        List output = new ArrayList();
        if (sought == null || sought.length()  == 0)
        {
            return output;
        }

        String commandChars = getSingleCharWords(commands);

        char firstChar = sought.charAt(0);
        int currentType = charType(firstChar, commandChars);
        int startIndex = 0;

        // If the first character is a [  or : then we have a problem because
        // the loop starts with the second character because it needs
        // something to compare with - so if we do start with a [ or : then
        // we make sure that we prepend with a " "
        if (sought.length() > 0 && (firstChar == '[' || firstChar == ':'))
        {
            sought = ' ' + sought;
        }

        // Loop, comparing each character with the previous one
        for (int i = 1; i <= sought.length(); i++)
        {
            // An escaped section
            if (i != sought.length() && sought.charAt(i) == '[')
            {
                int end = sought.indexOf(']', i);
                if (end == -1)
                {
                    throw new BookException(Msg.UNMATCHED_ESCAPE);
                }

                addWord(output, commands, "["); //$NON-NLS-1$
                addWord(output, commands, sought.substring(i + 1, end));
                addWord(output, commands, "]"); //$NON-NLS-1$

                currentType = CHAR_SPACE;
                i = end + 1;
            }

            // Pass through everything between pairs of :: e.g. ::bread::
            // as a single word. If there is no trailing :: take it
            // to the end of the line
            if (i != sought.length() && sought.indexOf("::", i) == i) //$NON-NLS-1$
            {
                int end = sought.indexOf("::", i + 2); //$NON-NLS-1$
                if (end == -1)
                {
                    addWord(output, commands, sought.substring(i + 2));
                    i = sought.length();
                }
                else
                {
                    addWord(output, commands, sought.substring(i + 2, end));
                    i = end + 2;
                }
                currentType = CHAR_SPACE;
            }

            // If this is the last word then so long as this letter is not
            // a space (in which case it has been added already) then add all
            // the word in
            if (i == sought.length())
            {
                if (currentType != CHAR_SPACE)
                {
                    addWord(output, commands, sought.substring(startIndex));
                }
            }
            else
            {
                // If this is the start of a new section of the command
                // then add the word in
                int new_type = charType(sought.charAt(i), commandChars);
                if (currentType != new_type || new_type == CHAR_COMMAND)
                {
                    if (currentType != CHAR_SPACE)
                    {
                        addWord(output, commands, sought.substring(startIndex, i));
                    }

                    startIndex = i;
                    currentType = charType(sought.charAt(i), commandChars);
                }
            }
        }

        return output;
    }

    /**
     * What class of character is this?
     * @param sought The string to be searched for
     * @return The chatacter class
     */
    private static final int charType(char sought, String commands)
    {
        if (Character.isWhitespace(sought))
        {
            return CHAR_SPACE;
        }

        if (commands.indexOf(sought) != -1)
        {
            return CHAR_COMMAND;
        }

        return CHAR_PARAM;
    }

    /**
     * Convenience function to add a Word to the Vector being created.
     * @param output The Vector to alter
     * @param commands The Word source
     * @param word The trigger to look for
     */
    private static void addWord(List output, Map commands, String word)
    {
        Object wordObj = commands.get(word);
        if (wordObj == null)
        {
            wordObj = new DefaultWord(word);
        }

        output.add(wordObj);
    }

    /**
     * Convenience function to add a Word to the Vector being created.
     * @param commands The Word source
     */
    private static String getSingleCharWords(Map commands)
    {
        Iterator it = commands.keySet().iterator();
        StringBuffer buf = new StringBuffer();

        while (it.hasNext())
        {
            String cmd = (String) it.next();
            if (cmd.length() == 1)
            {
                buf.append(cmd);
            }
        }

        return buf.toString();
    }

    /**
     * The type of character (see charType)
     */
    private static final int CHAR_PARAM = 0;

    /**
     * The type of character (see charType)
     */
    private static final int CHAR_COMMAND = 1;

    /**
     * The type of character (see charType)
     */
    private static final int CHAR_SPACE = 2;
}

--- NEW FILE: RemoveCommandWord.java ---
package org.crosswire.jsword.book.search.parse;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;

/**
 * Alter the Passage by calling removeAll with a Passage grabbed from the next
 * word in the search string.
 *  
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: RemoveCommandWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class RemoveCommandWord implements CommandWord
{
    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.CommandWord#updatePassage(org.crosswire.jsword.book.search.parse.Searcher, org.crosswire.jsword.passage.Passage)
     */
    public void updatePassage(IndexSearcher engine, Key key) throws BookException
    {
        key.removeAll(engine.iteratePassage());
    }
}

--- NEW FILE: PhraseParamWord.java ---
package org.crosswire.jsword.book.search.parse;

import java.util.Collection;
import java.util.Iterator;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.book.SentanceUtil;
import org.crosswire.jsword.book.search.Grammar;
import org.crosswire.jsword.book.search.Thesaurus;
import org.crosswire.jsword.book.search.ThesaurusFactory;
import org.crosswire.jsword.passage.Key;
import org.crosswire.jsword.passage.PassageTally;

/**
 * The Search Word for a Word to search for. The default
 * if no other SearchWords match.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: PhraseParamWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class PhraseParamWord implements ParamWord
{
    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getWord(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public String getWord(IndexSearcher engine) throws BookException
    {
        throw new BookException(Msg.SINGLE_PARAM);
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#Key(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public Key getKeyList(IndexSearcher engine) throws BookException
    {
        Iterator it = engine.iterator();
        StringBuffer buff = new StringBuffer();

        while (true)
        {
            if (!it.hasNext())
            {
                throw new BookException(Msg.LEFT_BRACKETS);
            }

            Word word = (Word) it.next();

            if (word instanceof PhraseParamWord)
            {
                break;
            }

            buff.append(word);
            buff.append(" "); //$NON-NLS-1$
        }

        return bestMatch(engine, buff.toString());
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.Matcher#bestMatch(java.lang.String, org.crosswire.jsword.passage.Key)
     */
    public Key bestMatch(IndexSearcher engine, String sought) throws BookException
    {
        if (thesaurus == null)
        {
            try
            {
                thesaurus = ThesaurusFactory.createThesaurus();
            }
            catch (InstantiationException ex)
            {
                throw new BookException(Msg.NO_THESAURUS, ex);
            }
        }

        String[] words = SentanceUtil.getWords(sought);
        words = Grammar.stripSmallWords(words);
        // log.fine("words="+StringUtil.toString(words));

        PassageTally tally = new PassageTally();

        for (int i = 0; i < words.length; i++)
        {
            tally.addAll(engine.getIndex().find(words[i]));
        }

        // This uses flatten() so that words like God
        // that have many startsWith() matches, and hence many verse
        // matches, do not end up with wrongly high scores.
        for (int i = 0; i < words.length; i++)
        {
            // log.fine("  root="+root);
            Collection col = thesaurus.getSynonyms(words[i]);
            String[] grWords = (String[]) col.toArray(new String[col.size()]);

            // log.fine("  gr_words="+StringUtil.toString(gr_words));
            PassageTally temp = new PassageTally();

            for (int j = 0; j < grWords.length; j++)
            {
                temp.addAll(engine.getIndex().find(grWords[j]));
            }

            temp.flatten();
            tally.addAll(temp);
        }

        return tally;
    }

    /**
     * How we get related words
     */
    private Thesaurus thesaurus;
}

--- NEW FILE: CommandWord.java ---
package org.crosswire.jsword.book.search.parse;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;

/**
 * CommandWord extends Word to allow actions that alter a base
 * Passage. Implementations of this interface may use the Searcher to
 * get at a default Bible (or they may have one hard coded if necessary)
 * or to get at ParamWords that follow this command.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: CommandWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
*/
public interface CommandWord extends Word
{
    /**
     * Alter a Passage in whatever manner is appropriate for
     * this command.
     * For example the "~" command does something like this:
     * <code>ref.blur(engine.elements.next.getWord);</code>
     * The "&" command looks like this:
     * <code>ref.addAll(engine.elements.next.getPassage);</code>
     * @param engine The controller that can provide access to the search
     *               string or a default Bible.
     * @param ref The Passage to alter (if necessary)
     */
    public void updatePassage(IndexSearcher engine, Key ref) throws BookException;
}

--- NEW FILE: Msg.java ---
package org.crosswire.jsword.book.search.parse;

import org.crosswire.common.util.MsgBase;

/**
 * Compile safe Msg resource settings.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: Msg.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
class Msg extends MsgBase
{
    static final Msg ADD_BLANK = new Msg("AddCommandWord.AddBlank"); //$NON-NLS-1$
    static final Msg RIGHT_PARAM = new Msg("PassageRightParamWord.RightParam"); //$NON-NLS-1$
    static final Msg RIGHT_BRACKETS = new Msg("PassageRightParamWord.RightBrackets"); //$NON-NLS-1$
    static final Msg LEFT_PARAM = new Msg("PassageLeftParamWord.LeftParam"); //$NON-NLS-1$
    static final Msg LEFT_BRACKETS = new Msg("PassageLeftParamWord.LeftBrackets"); //$NON-NLS-1$
    static final Msg STARTS_WORD = new Msg("StartsParamWord.StartsWord"); //$NON-NLS-1$
    static final Msg STARTS_BLANK = new Msg("StartsParamWord.StartsBlank"); //$NON-NLS-1$
    static final Msg RETAIN_BLANK = new Msg("RetainCommandWord.RetainBlank"); //$NON-NLS-1$
    static final Msg REMOVE_BLANK = new Msg("RemoveCommandWord.RemoveBlank"); //$NON-NLS-1$
    static final Msg GRAMMAR_WORD = new Msg("GrammarParamWord.GrammarWord"); //$NON-NLS-1$
    static final Msg GRAMMAR_BLANK = new Msg("GrammarParamWord.GrammarBlank"); //$NON-NLS-1$
    static final Msg BLUR_BLANK = new Msg("BlurCommandWord.BlurBlank"); //$NON-NLS-1$
    static final Msg BLUR_FORMAT = new Msg("BlurCommandWord.BlurFormat"); //$NON-NLS-1$
    static final Msg ENGINE_SYNTAX = new Msg("IndexSearcher.EngineSyntax"); //$NON-NLS-1$
    static final Msg ILLEGAL_PASSAGE = new Msg("PassageLeftParamWord.IllegalPassage"); //$NON-NLS-1$
    static final Msg UNMATCHED_ESCAPE = new Msg("CustomTokenizer.UnmatchedEscape"); //$NON-NLS-1$
    static final Msg SINGLE_PARAM = new Msg("PhraseParamWord.SingleParam"); //$NON-NLS-1$
    static final Msg NO_THESAURUS = new Msg("PhraseParamWord.NoThesaurus"); //$NON-NLS-1$

    /**
     * Passthrough ctor
     */
    private Msg(String name)
    {
        super(name);
    }
}

--- NEW FILE: Word.java ---
package org.crosswire.jsword.book.search.parse;

/**
 * The search.Searcher uses a Vector of SearchWords to calculate a search.
 * SearchWords itself is an empty interface, that is simply a place holder -
 * extended by CommandWord and Word to provide interfaces that
 * actually do some good.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: Word.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public interface Word
{
}

--- NEW FILE: Msg.properties ---
# The naming convention for the keys in the file is ClassName.MessageName
# Where ClassName is the name of the class using the property.
# When the resource is used by more than one class it should be the one
# that the resource is most closely associated.
# The MessageName should be mixed case, with a leading capital.
# It should have no spaces or other punctuation (e.g. _, -, ', ...)

AddCommandWord.AddBlank=Syntax Error: No word to search for.
PassageRightParamWord.RightParam=Can''t get a word from a sub-expression (processing '')'').
PassageRightParamWord.RightBrackets=Syntax Error: Can''t use brackets as a command.
PassageLeftParamWord.LeftParam=Can''t get a word from a sub-expression (processing ''('').
PassageLeftParamWord.LeftBrackets=Syntax Error: Unmatching brackets.
StartsParamWord.StartsWord=Can''t get a word from a starts with command.
StartsParamWord.StartsBlank=Syntax Error: No word to search for. (processing ''blank'')
RetainCommandWord.RetainBlank=Syntax Error: No word to search for. (processing ''retains'')
RemoveCommandWord.RemoveBlank=Syntax Error: No word to search for. (processing ''remove'')
GrammarParamWord.GrammarWord=Can''t get a word from a grammar command.
GrammarParamWord.GrammarBlank=Syntax Error: No word to search for. (processing ''grammar'')
BlurCommandWord.BlurBlank=Syntax Error: Missing number, nothing to blur by.
BlurCommandWord.BlurFormat=Can''t understand "{0}" as a number.
LocalParser.EngineSyntaxSyntax Error: Invalid command "{0}".
PassageLeftParamWord.IllegalPassage=Syntax Error: Invalid passage "{0}"
CustomTokenizer.UnmatchedEscape=Syntax Error: Unmatched brackets - [ and ]
PhraseParamWord.SingleParam=Can''t get a word from a sub-expression (processing ''"'').
PhraseParamWord.NoThesaurus=Couldn't create a thesaurus.

--- NEW FILE: PassageLeftParamWord.java ---
package org.crosswire.jsword.book.search.parse;

import java.util.Iterator;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.book.search.Index;
import org.crosswire.jsword.passage.Key;
import org.crosswire.jsword.passage.NoSuchKeyException;

/**
 * The Search Word for a Word to search for. The default
 * if no other SearchWords match.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: PassageLeftParamWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class PassageLeftParamWord implements ParamWord
{
    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getWord(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public String getWord(IndexSearcher engine) throws BookException
    {
        throw new BookException(Msg.LEFT_PARAM);
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#Key(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public Key getKeyList(IndexSearcher engine) throws BookException
    {
        Iterator it = engine.iterator();
        StringBuffer buff = new StringBuffer();

        int paren_level = 1;
        while (true)
        {
            if (!it.hasNext())
            {
                throw new BookException(Msg.LEFT_BRACKETS);
            }

            Word word = (Word) it.next();

            if (word instanceof PassageLeftParamWord)
            {
                paren_level++;
            }

            if (word instanceof PassageRightParamWord)
            {
                paren_level--;
            }

            if (paren_level == 0)
            {
                break;
            }

            buff.append(word);
            buff.append(" "); //$NON-NLS-1$
        }

        try
        {
            Index index = engine.getIndex();

            return index.getKey(buff.toString());
        }
        catch (NoSuchKeyException ex)
        {
            throw new BookException(Msg.ILLEGAL_PASSAGE, ex, new Object[] { buff.toString() });
        }
    }
}

--- NEW FILE: AddCommandWord.java ---
package org.crosswire.jsword.book.search.parse;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;

/**
 * Alter the Passage by calling addAll with a
 * Passage grabbed from the next word in the search string.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: AddCommandWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class AddCommandWord implements CommandWord
{
    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.CommandWord#updatePassage(org.crosswire.jsword.book.search.parse.Searcher, org.crosswire.jsword.passage.Passage)
     */
    public void updatePassage(IndexSearcher engine, Key key) throws BookException
    {
        key.addAll(engine.iteratePassage());
    }
}

--- NEW FILE: package.html ---
<html>

<head>
<title>com.eireneh.bible.control.search</title>
</head>

<body>

<p>The search package is responsible for taking a text string "God
&amp; loves &amp; world" and turning it into a series of calls to
<code>Book</code> and <code>Passage</code> to find the answer. I am a
little concerned that this design is a little complex, but I'm sure that
stewing on it will make things come clear.</p>

<h4>The Current Design</h4>

<p>These are the requirements of the search.Engine:</p>
<ul>
    <li>Adaptable to new syntaxes </li>
    <li>Extensible to easily add new commands and allow for 
        remote searches </li>
    <li>Simple to extend without any changes to the Engine itself</li>
</ul>

<p>This is how the current design works. The user types a string like
"<i>aaron ~5 &amp; moses &amp; thesarus talk</i>". (This means;
find Moses within 5 verses of Aaron alongside some speach type activity).
The <code>Engine</code> prepends the string "/" and tokenizes
this into a Vector of <code>SearchWords</code> - one
<code>SearchWord</code> for each part of the search string. The Vector
(using Java array syntax) looks like this: { "/",
"aaron", "~", "5", "&amp;",
"moses", "&amp;", "thesaurus",
"talk" }.</p>

<p><code>SearchWord</code> is an interface, implemented in several ways.
The Engine selects which <code>SearchWord</code>to use from a Hashtable
of <code>SearchWord</code>s. The members of this Hashtable are the
available <code>SearchWord</code>s keyed on a token (in this example the
tokens are /, ~, &amp;, &amp; and thesaurus). A
<code>DefaultParamWord</code> is created for the words in the search
string that do not have keys in the Hashtable (aaron, moses and talk)</p>

<p>The Vector is better understood like this:</p>
<ul>
    <li>/ aaron </li>
    <li>~ 5 </li>
    <li>&amp; moses </li>
    <li>&amp; thesaurus talk</li>
</ul>

<p>Each of these 9 elements in the Vector is a <code>SearchWord</code>.
The first element on each line (/, ~, &amp; and &amp;) is a
<code>CommandWord</code>, the others (aaron, 5, moses, thesaurus and
talk) are ParameterWords. <code>CommandWord</code> and
<code>ParamWord</code> inherit from <code>SearchWord</code>.</p>

<p>So in other word you could write the Vector like this, note the new
bullet points are for each CommandWord, the Vector is strictly 1D an does
not care at all for the difference between CommandWords and
ParamWords:</p>
<ul>
    <li>AddCommandWord(), DefaultParamWord("aaron") </li>
    <li>BlurCommandWord(), DefaultParamWord("5") </li>
    <li>RetainCommandWord(), DefaultParamWord("moses") </li>
    <li>RetainCommandWord(), ThesaurusParamWord(),
        DefaultParamWord("talk")</li>
</ul>

<p>It is worth noting that all the <code>DefaultParamWord</code>s are
created from unknown tokens. The other <code>SearchWord</code>s (both
<code>CommandWord</code>s (/, ~ and &amp;) and the <code>ParamWord</code>
(thesaurus)) were members of the Hashtable in the Engine.</p>

<p>The search Engine loops, taking an element from the Vector - expecting
it to be a <code>CommandWord</code> and calling
<code>CommandWord.updatePassage()</code>. These <code>CommandWord</code>s
have the opportunity to take elements from the Vector and treat them as
<code>ParamWord</code>s. Any error is a ClassCastException which is
caught and translated into a sensible error message.</p>

<h4>Historical Designs</h4>

<p>This does NOT represent the current design. I've left it here to show
the steps I went through to get to the current design. There were 2
possible designs. The smart engine model and the smart data model. The
latter won. The ideas were like this:</p>

<h4>Smart Engine Model</h4>

<P>The engine understands how to parse the search string 
into a series of calls to the relevant places. The engine is extensible by 
adding new 'commands' (Which must follow an <code>SearchWord</code>s
interface - now deleted). This model has the advantage of simplicity, and
memory-efficiency.</P>

<h4>Smart Data Model</h4>

<p>The engine simply turns the search string into a data structure, the
nodes of this document are instansiated as classes that follow an
interface with a <code>getAnswer()</code> interface. Calling
<code>getAnswer()</code> on the root node recurses down to find the
answer. The big advantage of this model is that it can be readily
extended to several types of interface - from the most basic GUI find 
dialog to a ridiculously powerful command line version.</p>

<p>I toyed with an XML based engine. The Engine parses the search string
into an XML Document. Something like this:</p>

<p>XML representation of the above search, and the code that implements
it</p>
 
<pre>&lt;search&gt;                        // ref = new Passage();
  &lt;add&gt;                         // ref.addAll(
    &lt;word&gt;aaron&lt;/word&gt;          //   default_bible.getPassages("aaron")
  &lt;/add&gt;                        // );
  &lt;blur&gt;5&lt;/blur&gt;                // ref.blur(5);
  &lt;retain&gt;                      // ref.retainAll(
    &lt;word&gt;moses&lt;/word&gt;          //   default_bible.getPassages("moses")
  &lt;/retain&gt;                     // );
  &lt;add&gt;                         // ref.addAll(
    &lt;words&gt;                     //   default_bible.getPassages(
      &lt;thesarus&gt;talk&lt;/thesarus&gt; //     thesarus.getSynonyms("talk")
    &lt;/words&gt;                    //   )
  &lt;/add&gt;                        // );
&lt;/search&gt;                       // return ref;
</pre>

<p>The benefit of this is that it allows us to easily remote the whole
search engine. I seem to have an XML disease, so why shouldn't it affect
here!</p>

<P>However I decided that a remote search engine was of little benefit
since the individual <code>SearchWord</code>s can be remoted via a very
simple stub - giving an engine that can be remoted piecemeal. The only
drawback to this solution is on high latency networks (erm like the
Internet) where a set of simple requests can take a lot longer than a
single complex one. However I am sure that I could XMLize or serialize
the Vector invented above.</p>

<h4>SoundEx</h4>

<p>Some code to do soundex matching ...</p>

<pre>
// create object listing the SOUNDEX values for each letter
// -1 indicates that the letter is not coded, but is used for coding
//  1 is for BFPV
//  2 is for CGJKQSXZ
//  3 is for DT
//  4 is for L
//  5 is for MN my home state
//  6 is for R
function makesoundex()
{
    this.a = -1
    this.b =  1
    this.c =  2
    this.d =  3
    this.e = -1
    this.f =  1
    this.g =  2
    this.h = -1
    this.i = -1
    this.j =  2
    this.k =  2
    this.l =  4
    this.m =  5
    this.n =  5
    this.o = -1
    this.p =  1
    this.q =  2
    this.r =  6
    this.s =  2
    this.t =  3
    this.u = -1
    this.v =  1
    this.w = -1
    this.x =  2
    this.y = -1
    this.z =  2
}

var sndx=new makesoundex()

// check to see that the input is valid
function isSurname(name)
{
    if (name=="" || name==null)
    {
        alert("Please enter surname for which to generate SOUNDEX code.")
        return false
    }
    else
    {
        for (var i=0; i<name.length; (!(letter
        if
            letter 
              ="name.charAt(i)" var { i++)>='a' &amp;&amp; letter&lt;='z' || letter&gt;='A' &amp;&amp; letter&lt;='Z'))
            {
                alert("Please enter only letters in the surname.")
                return false
            }
        }
    }

    return true
}

// Collapse out directly adjacent sounds
// 1. Assume that surname.length&gt;=1
// 2. Assume that surname contains only lowercase letters
function collapse(surname)
{
    if (surname.length==1)
    {
        return surname
    }

    var lname=(document.myform.surname.value)
    document.myform.lname.value=lname
    var right=collapse(surname.substring(1,surname.length))

    if (sndx[surname.charAt(0)]==sndx[right.charAt(0)])
    {
        return surname.charAt(0)+right.substring(1,right.length)
    }

    return surname.charAt(0)+right  
}

// Compute the SOUNDEX code for the surname
function soundex(form)
{
    form.result.value=""
    if (!isSurname(form.surname.value))
    {
        return
    }
      
    var stage1=collapse(form.surname.value.toLowerCase())
    form.result.value+=stage1.charAt(0).toUpperCase() // Retain first letter
    form.result.value+="-" // Separate letter with a dash
    var stage2=stage1.substring(1,stage1.length)
    var count=0

    for (var i=0; i<stage2.length if { i++)
    (sndx[stage2.charAt(i)]
         count  <3; &&>0)
        {
            form.result.value+=
            sndx[stage2.charAt(i)]
        count++
    }

    } for (;count&lt;3; count++)
    {
        form.result.value+="0"
    }

    form.surname.select()
    form.surname.focus()
}

</pre>

</body>
</html>

--- NEW FILE: Word.properties ---

# Mappings to turn command symbols into classes to execute them
/: org.crosswire.jsword.book.search.parse.AddCommandWord
|: org.crosswire.jsword.book.search.parse.AddCommandWord
&: org.crosswire.jsword.book.search.parse.RetainCommandWord
+: org.crosswire.jsword.book.search.parse.RetainCommandWord
,: org.crosswire.jsword.book.search.parse.RetainCommandWord
-: org.crosswire.jsword.book.search.parse.RemoveCommandWord
~: org.crosswire.jsword.book.search.parse.BlurCommandWord
(: org.crosswire.jsword.book.search.parse.SubLeftParamWord
): org.crosswire.jsword.book.search.parse.SubRightParamWord
[: org.crosswire.jsword.book.search.parse.PassageLeftParamWord
]: org.crosswire.jsword.book.search.parse.PassageRightParamWord
sw: org.crosswire.jsword.book.search.parse.StartsParamWord
startswith: org.crosswire.jsword.book.search.parse.StartsParamWord
gr: org.crosswire.jsword.book.search.parse.GrammarParamWord
grammar: org.crosswire.jsword.book.search.parse.GrammarParamWord
\": org.crosswire.jsword.book.search.parse.PhraseParamWord
\': org.crosswire.jsword.book.search.parse.PhraseParamWord

# Mappings of preferred symbols for each command
org.crosswire.jsword.book.search.parse.AddCommandWord: /
org.crosswire.jsword.book.search.parse.RetainCommandWord: +
org.crosswire.jsword.book.search.parse.RemoveCommandWord: -
org.crosswire.jsword.book.search.parse.BlurCommandWord: ~
org.crosswire.jsword.book.search.parse.SubLeftParamWord: (
org.crosswire.jsword.book.search.parse.SubRightParamWord: )
org.crosswire.jsword.book.search.parse.PassageLeftParamWord: [
org.crosswire.jsword.book.search.parse.PassageRightParamWord: ]
org.crosswire.jsword.book.search.parse.StartsParamWord: sw
org.crosswire.jsword.book.search.parse.GrammarParamWord: gr
org.crosswire.jsword.book.search.parse.PhraseParamWord: "

--- NEW FILE: IndexSearcher.java ---
package org.crosswire.jsword.book.search.parse;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.crosswire.common.util.Logger;
import org.crosswire.common.util.ResourceUtil;
import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.book.search.Index;
import org.crosswire.jsword.book.search.SearchRequest;
import org.crosswire.jsword.book.search.Searcher;
import org.crosswire.jsword.book.search.basic.DefaultSearchRequest;
import org.crosswire.jsword.passage.Key;

/**
 * The central interface to all searching.
 *
 * Functionality the I invisage includes:<ul>
 * <li>A simple search syntax that goes something like this.<ul>
 * <li>aaron, moses     (verses containing aaron and moses. Can also use & or +)
 * <li>aaron/moses      (verses containing aaron or moses. Can also use |)
 * <li>aaron - moses    (verses containing aaron but not moses)
 * <li>aaron ~5 , moses (verses with aaron within 5 verses of moses)
 * <li>soundslike aaron (verses with words that sound like aaron. Can also use sl ...)
 * <li>thesaurus happy  (verses with words that mean happy. Can also use th ...)
 * <li>grammar have     (words like has have had and so on. Can also use gr ...)</ul>
 * <li>The ability to add soundslike type extensions.</ul>
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: IndexSearcher.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class IndexSearcher implements Searcher
{
    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.Searcher#init(org.crosswire.jsword.book.search.Index)
     */
    public void init(Index newindex)
    {
        this.index = newindex;
        this.commands = getWordMap();
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.Searcher#search(java.lang.String)
     */
    public Key search(String request) throws BookException
    {
        return search(new DefaultSearchRequest(request));
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.Searcher#search(org.crosswire.jsword.book.search.SearchRequest)
     */
    public Key search(SearchRequest request) throws BookException
    {
        index.setSearchModifier(request.getSearchModifier());
        List output = CustomTokenizer.tokenize(request.getRequest(), commands);
        Key results = search(output);
        index.setSearchModifier(null);
        return results;
    }

    /**
     * Take a search string and decipher it into a Passage.
     * @param sought The string to be searched for
     * @return The matching verses
     */
    protected Key wordSearch(String sought) throws BookException
    {
        return index.find(sought);
    }

    /**
     * Take a search string and decipher it into a Passage.
     * @return The matching verses
     */
    protected Key search(List matches) throws BookException
    {
        Key key = index.find(null);

        // Need a CommandWord, but a ParamWord we can deal with using an
        // AddCommandWord chucked on the front
        if (matches.get(0) instanceof ParamWord)
        {
            // Add a default AddCommandWord to the front it there is
            matches.add(0, new AddCommandWord());
        }

        wit = matches.iterator();
        while (wit.hasNext())
        {
            Object temp = wit.next();

            try
            {
                CommandWord command = (CommandWord) temp;
                command.updatePassage(this, key);
            }
            catch (ClassCastException ex)
            {
                ex.printStackTrace();
                throw new BookException(Msg.ENGINE_SYNTAX, new Object[] { temp });
            }
        }

        // Set these to null so that people can't play around
        // with them once they're done with, and to save memory.
        matches = null;
        wit = null;

        return key;
    }

    /**
     * A basic version of getPassage(String[]) simply calls getPassage(String)
     * in a loop for each word, adding the Verses to an Passage that is returned
     * @param words The words to search for
     * @return The Passage
     * @throws BookException If anything goes wrong with this method
     */
    protected Key getPassage(String[] words) throws BookException
    {
        Key ref = index.find(null);

        for (int i = 0; i < words.length; i++)
        {
            ref.addAll(wordSearch(words[i]));
        }

        return ref;
    }

    /**
     * Accessor for the Bible to search.
     * @return The current Bible
     */
    protected Index getIndex()
    {
        return index;
    }

    /**
     * Accessor for the available SearchWords. This is probably
     * the same as from Options.getSearchHashtable() but just in
     * case anyone has been playing around with it...
     * @return The Word Hashtable
     */
    protected Map getSearchMap()
    {
        return commands;
    }

    /**
     * Accessor for the available SearchWords. This is probably
     * the same as from Options.getSearchHashtable() but just in
     * case anyone has been playing around with it...
     */
    protected void setSearchMap(Map commands)
    {
        this.commands = commands;
    }

    /**
     * Most Words need to access parameters, this method allows them access to
     * the Searcher's own Enumerator. Use with care, and only if you are a Word
     * taking part in the current search.
     * @return The current Iterator
     */
    protected Iterator iterator()
    {
        return wit;
    }

    /**
     * @throws BookException
     */
    public Key iteratePassage() throws BookException
    {
        if (!iterator().hasNext())
        {
            throw new BookException(Msg.RETAIN_BLANK);
        }

        Object next = iterator().next();
        if (!(next instanceof ParamWord))
        {
            log.error("next=" + next); //$NON-NLS-1$
        }

        ParamWord param = (ParamWord) next;
        Key ref = param.getKeyList(this);

        return ref;
    }

    /**
     * @throws BookException
     */
    public String iterateWord() throws BookException
    {
        if (!iterator().hasNext())
        {
            throw new BookException(Msg.RETAIN_BLANK);
        }

        Object next = iterator().next();
        if (!(next instanceof ParamWord))
        {
            log.error("next=" + next); //$NON-NLS-1$
        }

        ParamWord param = (ParamWord) next;
        String word = param.getWord(this);

        return word;
    }

    /**
     * Accessor for the cached list of known special lookup words
     */
    public static Map getWordMap()
    {
        if (wordMap == null)
        {
            try
            {
                Properties prop = ResourceUtil.getProperties(Word.class);

                wordMap = new HashMap();
                preferredMap = new HashMap();

                for (Iterator it = prop.keySet().iterator(); it.hasNext(); )
                {
                    String key = (String) it.next();
                    String value = prop.getProperty(key);

                    if (key.startsWith(PACKAGE_NAME))
                    {
                        try
                        {
                            Class clazz = Class.forName(key);
                            preferredMap.put(clazz, value);
                        }
                        catch (Exception ex)
                        {
                            log.error("can't add CommandWord: key=" + key + " Class=" + value, ex); //$NON-NLS-1$ //$NON-NLS-2$
                        }
                    }
                    else
                    {
                        try
                        {
                            Class clazz = Class.forName(value);
                            wordMap.put(key, clazz.newInstance());
                        }
                        catch (Exception ex)
                        {
                            log.error("can't add CommandWord: key=" + key + " Class=" + value, ex); //$NON-NLS-1$ //$NON-NLS-2$
                        }
                    }
                }
            }
            catch (IOException ex)
            {
                log.fatal("Missing search words", ex); //$NON-NLS-1$
            }
        }

        return wordMap;
    }

    /**
     * Accessor for the cached list of known special lookup words
     */
    public static String getPreferredSyntax(Class command)
    {
        // Check the maps have been created
        getWordMap();
        return (String) preferredMap.get(command);
    }

    /**
     * To distinguish command mappings from preferred mappings in Word.properties
     */
    private static final String PACKAGE_NAME = "org.crosswire.jsword.book.search.parse"; //$NON-NLS-1$

    /**
     * The log stream
     */
    private static final Logger log = Logger.getLogger(IndexSearcher.class);

    /**
     * The cache of known words
     */
    private static Map wordMap;

    /**
     * The cache of preferred symbols for the words
     */
    private static Map preferredMap;

    /**
     * The commands that we know about
     */
    private Map commands;

    /**
     * While the answer is being worked out ...
     */
    private Iterator wit;

    /**
     * The index
     */
    private Index index;
}

--- NEW FILE: StartsParamWord.java ---
package org.crosswire.jsword.book.search.parse;

import java.util.Collection;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.book.search.Thesaurus;
import org.crosswire.jsword.book.search.ThesaurusFactory;
import org.crosswire.jsword.passage.Key;

/**
 * The Search Word for a Word to search for.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: StartsParamWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class StartsParamWord implements ParamWord
{
    /**
     * Default ctor
     */
    public StartsParamWord() throws InstantiationException
    {
        thesaurus = ThesaurusFactory.createThesaurus();
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getWord(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public String getWord(IndexSearcher engine) throws BookException
    {
        throw new BookException(Msg.STARTS_WORD);
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getKeyList(org.crosswire.jsword.book.search.parse.IndexSearcher)
     */
    public Key getKeyList(IndexSearcher engine) throws BookException
    {
        String word = engine.iterateWord();

        Collection col = thesaurus.getSynonyms(word);
        String[] words = (String[]) col.toArray(new String[col.size()]);

        return engine.getPassage(words);
    }

    /**
     * The source of thesaurus data
     */
    private Thesaurus thesaurus;
}

--- NEW FILE: GrammarParamWord.java ---
package org.crosswire.jsword.book.search.parse;

import java.util.Collection;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.book.search.Grammar;
import org.crosswire.jsword.book.search.Thesaurus;
import org.crosswire.jsword.book.search.ThesaurusFactory;
import org.crosswire.jsword.passage.Key;

/**
 * The Search Word for a Word to search for. The default
 * if no other SearchWords match.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: GrammarParamWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class GrammarParamWord implements ParamWord
{
    /**
     * Default ctor
     */
    public GrammarParamWord() throws InstantiationException
    {
        thesaurus = ThesaurusFactory.createThesaurus();
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getWord(org.crosswire.jsword.book.search.parse.IndexSearcher)
     */
    public String getWord(IndexSearcher engine) throws BookException
    {
        throw new BookException(Msg.GRAMMAR_WORD);
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getPassage(org.crosswire.jsword.book.search.parse.IndexSearcher)
     */
    public Key getKeyList(IndexSearcher engine) throws BookException
    {
        String root = Grammar.getRoot(engine.iterateWord());

        Collection col = thesaurus.getSynonyms(root);
        String[] words = (String[]) col.toArray(new String[col.size()]);

        return engine.getPassage(words);
    }

    /**
     * The source of thesaurus data
     */
    private Thesaurus thesaurus;
}

--- NEW FILE: RetainCommandWord.java ---
package org.crosswire.jsword.book.search.parse;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;

/**
 * Alter the Passage by calling retainAll with a Passage grabbed from the next
 * word in the search string.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: RetainCommandWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class RetainCommandWord implements CommandWord
{
    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.CommandWord#updatePassage(org.crosswire.jsword.book.search.parse.Searcher, org.crosswire.jsword.passage.Passage)
     */
    public void updatePassage(IndexSearcher engine, Key key) throws BookException
    {
        key.retainAll(engine.iteratePassage());
    }
}

--- NEW FILE: SubLeftParamWord.java ---
package org.crosswire.jsword.book.search.parse;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;

/**
 * The Search Word for a Word to search for. The default if no other SearchWords
 * match.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: SubLeftParamWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class SubLeftParamWord implements ParamWord
{
    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getWord(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public String getWord(IndexSearcher engine) throws BookException
    {
        throw new BookException(Msg.LEFT_PARAM);
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getPassage(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public Key getKeyList(IndexSearcher engine) throws BookException
    {
        Iterator it = engine.iterator();
        List output = new ArrayList();

        int parenLevel = 1;
        while (true)
        {
            if (!it.hasNext())
            {
                throw new BookException(Msg.LEFT_BRACKETS);
            }

            Word word = (Word) it.next();

            if (word instanceof SubLeftParamWord)
            {
                parenLevel++;
            }

            if (word instanceof SubRightParamWord)
            {
                parenLevel--;
            }

            if (parenLevel == 0)
            {
                break;
            }

            output.add(word);
        }

        IndexSearcher subEngine = new IndexSearcher();
        subEngine.init(engine.getIndex());
        subEngine.setSearchMap(engine.getSearchMap());
        Key subRef = subEngine.search(output);

        return subRef;
    }
}

--- NEW FILE: SubRightParamWord.java ---
package org.crosswire.jsword.book.search.parse;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;

/**
 * The Search Word for a Word to search for.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: SubRightParamWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class SubRightParamWord implements ParamWord
{
    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getWord(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public String getWord(IndexSearcher engine) throws BookException
    {
        throw new BookException(Msg.RIGHT_PARAM);
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getPassage(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public Key getKeyList(IndexSearcher engine) throws BookException
    {
        throw new BookException(Msg.RIGHT_BRACKETS);
    }
}

--- NEW FILE: DefaultWord.java ---
package org.crosswire.jsword.book.search.parse;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;

/**
 * The Search Word for a Word to search for. This is the default if no other
 * Words match.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: DefaultWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class DefaultWord implements ParamWord, CommandWord
{
    /**
     * Create a the default rule with the (presumably) Bible
     * word that formed part of the original search string
     * @param text The word to search (or otherwise) for
     */
    public DefaultWord(String text)
    {
        this.text = text;
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getWord(org.crosswire.jsword.book.search.parse.IndexSearcher)
     */
    public String getWord(IndexSearcher engine)
    {
        return text;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString()
    {
        return text;
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getPassage(org.crosswire.jsword.book.search.parse.IndexSearcher)
     */
    public Key getKeyList(IndexSearcher engine) throws BookException
    {
        return engine.wordSearch(text);
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.CommandWord#updatePassage(org.crosswire.jsword.book.search.parse.Searcher, org.crosswire.jsword.passage.Passage)
     */
    public void updatePassage(IndexSearcher engine, Key key) throws BookException
    {
        // We need to have DefaultWord pretend to be a CommandWord so that
        // seearches like "moses aaron" work. DefaultWord(moses) has to be a
        // command for DefaultWord(aaron)
        // So if the stack is empty we need to pretend that the search had been
        // done using us as a word.
        if (engine.iterator().hasNext())
        {
            key.retainAll(engine.iteratePassage());
        }
        else
        {
            key.retainAll(engine.wordSearch(text));
        }
    }

    /**
     * The word that we represent
     */
    private String text;
}

--- NEW FILE: PassageRightParamWord.java ---
package org.crosswire.jsword.book.search.parse;

import org.crosswire.jsword.book.BookException;
import org.crosswire.jsword.passage.Key;

/**
 * The end of an escape to specify a passage directly.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * 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
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: PassageRightParamWord.java,v 1.1 2005/04/25 01:22:23 dmsmith Exp $
 */
public class PassageRightParamWord implements ParamWord
{
    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#getWord(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public String getWord(IndexSearcher engine) throws BookException
    {
        throw new BookException(Msg.RIGHT_PARAM);
    }

    /* (non-Javadoc)
     * @see org.crosswire.jsword.book.search.parse.ParamWord#Key(org.crosswire.jsword.book.search.parse.Searcher)
     */
    public Key getKeyList(IndexSearcher engine) throws BookException
    {
        throw new BookException(Msg.RIGHT_BRACKETS);
    }
}



More information about the jsword-svn mailing list