Author: ehatcher
Date: Tue Mar  8 17:52:13 2005
New Revision: 156591

URL: http://svn.apache.org/viewcvs?view=rev&rev=156591
Log:
Contribution of slick Swing models to enable on-the-fly searching of
tables and lists.  Created by Jonathan Simon.


Added:
    lucene/java/trunk/contrib/swing/
    lucene/java/trunk/contrib/swing/build.xml
    lucene/java/trunk/contrib/swing/docs/
    lucene/java/trunk/contrib/swing/docs/index.html
    lucene/java/trunk/contrib/swing/src/
    lucene/java/trunk/contrib/swing/src/java/
    lucene/java/trunk/contrib/swing/src/java/org/
    lucene/java/trunk/contrib/swing/src/java/org/apache/
    lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/
    lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/
    lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/
    
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/ListSearcher.java
    
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/TableSearcher.java
    
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/package.html
    lucene/java/trunk/contrib/swing/src/java/overview.html
    lucene/java/trunk/contrib/swing/src/test/
    lucene/java/trunk/contrib/swing/src/test/org/
    lucene/java/trunk/contrib/swing/src/test/org/apache/
    lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/
    lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/
    lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/BaseListModel.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/BaseTableModel.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/DataStore.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/ListSearcherSimulator.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/RestaurantInfo.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TableSearcherSimulator.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestBasicList.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestBasicTable.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestSearchingList.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestSearchingTable.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestUpdatingList.java
    
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestUpdatingTable.java

Added: lucene/java/trunk/contrib/swing/build.xml
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/build.xml?view=auto&rev=156591
==============================================================================
--- lucene/java/trunk/contrib/swing/build.xml (added)
+++ lucene/java/trunk/contrib/swing/build.xml Tue Mar  8 17:52:13 2005
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<project name="swing" default="default">
+
+  <description>
+    Swing Models
+  </description>
+
+  <import file="../common.xml"/>
+
+  <target name="list-demo" depends="compile">
+    <java classname="org.apache.lucene.swing.models.ListSearcherSimulator"
+          fork="yes" spawn="yes"
+          classpathref="test.classpath"
+    />
+  </target>
+
+  <target name="table-demo" depends="compile">
+    <java classname="org.apache.lucene.swing.models.TableSearcherSimulator"
+          fork="yes" spawn="yes"
+          classpathref="test.classpath"
+    />
+  </target>
+</project>

Added: lucene/java/trunk/contrib/swing/docs/index.html
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/docs/index.html?view=auto&rev=156591
==============================================================================
--- lucene/java/trunk/contrib/swing/docs/index.html (added)
+++ lucene/java/trunk/contrib/swing/docs/index.html Tue Mar  8 17:52:13 2005
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+<body>
+<h1><strong> Lucene Powered Swing Data Models </strong></h1>
+<p><strong>by Jonathan Simon </strong></p>
+<p>&nbsp;</p>
+<p><strong>What it is.</strong></p>
+<p>This package contains classes that help you easily integrate Lucene based 
searching 
+  into your Swing components. Currently there are classes to index and search 
+  JTables and JLists. This is done using model decorators rather than custom 
models 
+  to make it easier to search current models as well as new ones. </p>
+<p><em>These models do not actually contain any data</em>. Rather, the 
ListModel 
+  decorator (ListSearcher) and the TableModel decorator (TableSearcher) take a 
+  model in the constructor and delegate all calls to it (after a little 
alteration, 
+  but we'll get to that). That said, these are not full fledged models 
themselves. 
+  You still have to have another model to decorate with the searching models. 
+  If you are adding searching to a pre-existing model, you can use your 
pre-existing 
+  model directly. Otherwise, you can implement a model from scratch or use a 
pre-existing 
+  one to get started. </p>
+<p><strong>What it isn't. </strong></p>
+<p>A complete component: These are just models. They are not complete 
components 
+  with search fields and buttons laid out like a searchable interface. You 
still 
+  have to build that since the UI changes drastically between applciations.</p>
+<p>A complete model: There are just model decorators. You can't just set the 
model 
+  of a JList or JTable to one of these models, and you can't add data directly 
+  to these models. </p>
+<p>A front end for a lucene index: In other words, you can't use these classes 
+  to point a JTable directly to a Lucene index. Although that's interesting in 
+  its own right, this is not that. </p>
+<p><strong>Usage: </strong></p>
+<p>Coding to both models nearly identical. They both take the model to 
decorate 
+  at construction time. Here is the code from the demo to decorate a JTable 
model 
+  with the TableSearcher and set it as the table model. </p>
+<pre><code>//make a new JTable
+JTable table = new JTable();
+//make my base model, the model with the data
+BaseTableModel tableModel = new BaseTableModel(DataStore.getRestaurants());
+//decorate the tableModel with the TableSearcher 
+TableSearcher searchTableModel = new TableSearcher(tableModel);
+//set the TableModel in the table to the TableSearcher
+table.setModel(searchTableModel);
+</code></pre>
+<p>Initially, you won't notice a difference. This is because there is no 
active 
+  search which displays all data from the underlying model. You search by 
calling 
+  the <code>search</code>() method passing a search string. This filters the 
data 
+  set down without changing the underlying data model -- one of the main 
reasons 
+  for decorating in the first place. Any valid Lucene search string should 
work 
+  (see notes for more info on this). You'll probaby have some code somewhere 
like 
+  this in your app to connect a text field and search button to the model. </p>
+<pre><code>//create components
+final JTextField searchField = new JTextField();
+JButton searchButton = new JButton("Go");
+
+//make an action listener
+ActionListener searchListener = new ActionListener() {
+       public void actionPerformed(ActionEvent e) {
+          searchTableModel.search(searchField.getText().trim().toLowerCase());
+       }
+};
+
+//register listeners
+searchButton.addActionListener(searchListener);
+searchField.addActionListener(searchListener);</code></pre>
+<p>You also might want to have a clear search button, working the same way. 
But 
+  to keep things simple, if you search will a <code>null </code>String or an 
empty 
+  String, the search clears and you will once again see all of your data. </p>
+<p><strong>Demo notes:</strong> </p>
+<p>The list demo does real time searching. In other words, as you type, 
searches 
+  run and the result set updates. The table demo has a search button, and only 
+  searches when the button is clicked. They both work, I just implemented them 
+  this way to show the different UI metaphors and that they both work.</p>
+<p><strong>Implementation notes: </strong></p>
+<p>This code started as a proof of concept so it's not a <em>fully</em> 
featured 
+  model. Don't get me wrong, it <em>fully</em> works, but it could use some 
improvement 
+  that it will hopefully get over time. I just wanted to get it out there and 
+  get people using it. I'm also trying to keep everything as simple as 
possible. 
+  Here are some of the issues. </p>
+<ul>
+  <li>You can't change the model after the Searcher is constructed. </li>
+  <li>The search model decorators <em>do</em> update when the decorated model 
+    is updated, but not in a very efficient way. The whole search model is 
reindexed 
+    when anything changes. This is a definite scaling issue. </li>
+  <li>The indexing and searching logic needs to be generally more configurable 
+    to allow custom tailoring of searched and indexing. </li>
+  <li>The TableSearcher uses column names to index column values. This could 
be 
+    an issue with multiple word column names. </li>
+  <li>The ListSearcher uses MultiFieldQueryParser even though its not really 
indexing 
+    multiple fields. </li>
+</ul>
+<p>&nbsp;</p>
+<p>&nbsp;</p>
+</body>
+</html>

Added: 
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/ListSearcher.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/ListSearcher.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/ListSearcher.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/ListSearcher.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,279 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Hits;
+import org.apache.lucene.queryParser.MultiFieldQueryParser;
+
+import javax.swing.*;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListDataEvent;
+import java.util.ArrayList;
+
+/**
+ * See table searcher explanation.
+ *
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class ListSearcher extends AbstractListModel {
+    private ListModel listModel;
+
+    /**
+     * The reference links between the decorated ListModel
+     * and this list model based on search criteria
+     */
+    private ArrayList rowToModelIndex = new ArrayList();
+
+    /**
+     * In memory lucene index
+     */
+    private RAMDirectory directory;
+
+    /**
+     * Cached lucene analyzer
+     */
+    private Analyzer analyzer;
+
+    /**
+     * Links between this list model and the decorated list model
+     * are maintained through links based on row number. This is a
+     * key constant to denote "row number" for indexing
+     */
+    private static final String ROW_NUMBER = "ROW_NUMBER";
+
+    /**
+     * Since we only have one field, unlike lists with multiple
+     * fields -- we are just using a constant to denote field name.
+     * This is most likely unnecessary and should be removed at
+     * a later date
+     */
+    private static final String FIELD_NAME = "FIELD_NAME";
+
+    /**
+     * Cache the current search String. Also used internally to
+     * key whether there is an active search running or not. i.e. if
+     * searchString is null, there is no active search.
+     */
+    private String searchString = null;
+    private ListDataListener listModelListener;
+
+    public ListSearcher(ListModel newModel) {
+        analyzer = new WhitespaceAnalyzer();
+        setListModel(newModel);
+        listModelListener = new ListModelHandler();
+        newModel.addListDataListener(listModelListener);
+        clearSearchingState();
+    }
+
+    private void setListModel(ListModel newModel) {
+        //remove listeners if there...
+        if (newModel != null) {
+            newModel.removeListDataListener(listModelListener);
+        }
+
+        listModel = newModel;
+        if (listModel != null) {
+            listModel.addListDataListener(listModelListener);
+        }
+
+        //recalculate the links between this list model and
+        //the inner list model since the decorated model just changed
+        reindex();
+
+        // let all listeners know the list has changed
+        fireContentsChanged(this, 0, getSize());
+    }
+
+    private void reindex() {
+        try {
+            // recreate the RAMDirectory
+            directory = new RAMDirectory();
+            IndexWriter writer = new IndexWriter(directory, analyzer, true);
+
+            // iterate through all rows
+            for (int row=0; row < listModel.getSize(); row++){
+
+                //for each row make a new document
+                Document document = new Document();
+                //add the row number of this row in the decorated list model
+                //this will allow us to retrive the results later
+                //and map this list model's row to a row in the decorated
+                //list model
+                document.add(new Field(ROW_NUMBER, "" + row, true, true, 
true));
+                //add the string representation of the row to the index
+                document.add(new Field(FIELD_NAME, 
String.valueOf(listModel.getElementAt(row)).toLowerCase(), true, true, true));
+                writer.addDocument(document);
+            }
+            writer.optimize();
+            writer.close();
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Run a new search.
+     *
+     * @param searchString Any valid lucene search string
+     */
+    public void search(String searchString){
+
+        //if search string is null or empty, clear the search == search all
+        if (searchString == null || searchString.equals("")){
+            clearSearchingState();
+            fireContentsChanged(this, 0, getSize());
+            return;
+        }
+
+
+        try {
+            //cache search String
+            this.searchString = searchString;
+
+            //make a new index searcher with the in memory (RAM) index.
+            IndexSearcher is = new IndexSearcher(directory);
+
+            //make an array of fields - one for each column
+            String[] fields = {FIELD_NAME};
+
+            //build a query based on the fields, searchString and cached 
analyzer
+            //NOTE: This is an area for improvement since the 
MultiFieldQueryParser
+            // has some weirdness.
+            Query query = MultiFieldQueryParser.parse(searchString, fields, 
analyzer);
+            //run the search
+            Hits hits = is.search(query);
+            //reset this list model with the new results
+            resetSearchResults(hits);
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+
+        //notify all listeners that the list has been changed
+        fireContentsChanged(this, 0, getSize());
+    }
+
+    /**
+     *
+     * @param hits The new result set to set this list to.
+     */
+    private void resetSearchResults(Hits hits) {
+        try {
+            //clear our index mapping this list model rows to
+            //the decorated inner list model
+            rowToModelIndex.clear();
+            //iterate through the hits
+            //get the row number stored at the index
+            //that number is the row number of the decorated
+            //tabble model row that we are mapping to
+            for (int t=0; t<hits.length(); t++){
+                Document document = hits.doc(t);
+                Field field = document.getField(ROW_NUMBER);
+                rowToModelIndex.add(new Integer(field.stringValue()));
+            }
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * @return The current lucene analyzer
+     */
+    public Analyzer getAnalyzer() {
+        return analyzer;
+    }
+
+    /**
+     * @param analyzer The new analyzer to use
+     */
+    public void setAnalyzer(Analyzer analyzer) {
+        this.analyzer = analyzer;
+        //reindex from the model with the new analyzer
+        reindex();
+
+        //rerun the search if there is an active search
+        if (isSearching()){
+            search(searchString);
+        }
+    }
+
+    private boolean isSearching() {
+        return searchString != null;
+    }
+
+    private void clearSearchingState() {
+        searchString = null;
+        rowToModelIndex.clear();
+        for (int t=0; t<listModel.getSize(); t++){
+            rowToModelIndex.add(new Integer(t));
+        }
+    }
+
+    private int getModelRow(int row){
+        return ((Integer) rowToModelIndex.get(row)).intValue();
+    }
+
+    public int getSize() {
+        return (listModel == null) ? 0 : rowToModelIndex.size();
+    }
+
+    public Object getElementAt(int index) {
+        return listModel.getElementAt(getModelRow(index));
+    }
+
+
+    class ListModelHandler implements ListDataListener {
+
+        public void contentsChanged(ListDataEvent e) {
+            somethingChanged();
+        }
+
+        public void intervalAdded(ListDataEvent e) {
+            somethingChanged();
+        }
+
+        public void intervalRemoved(ListDataEvent e) {
+            somethingChanged();
+        }
+
+        private void somethingChanged(){
+            // If we're not searching, just pass the event along.
+            if (!isSearching()) {
+                clearSearchingState();
+                reindex();
+                fireContentsChanged(ListSearcher.this, 0, getSize());
+                return;
+            }
+
+            // Something has happened to the data that may have invalidated 
the search.
+            reindex();
+            search(searchString);
+            fireContentsChanged(ListSearcher.this, 0, getSize());
+            return;
+        }
+
+    }
+
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/TableSearcher.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/TableSearcher.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/TableSearcher.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/TableSearcher.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,354 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Hits;
+import org.apache.lucene.queryParser.MultiFieldQueryParser;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List;
+
+import javax.swing.*;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.*;
+
+
+/**
+ * This is a TableModel that encapsulates Lucene
+ * search logic within a TableModel implementation.
+ * It is implemented as a TableModel decorator,
+ * similar to the TableSorter demo from Sun that decorates
+ * a TableModel and provides sorting functionality. The benefit
+ * of this architecture is that you can decorate any TableModel
+ * implementation with this searching table model -- making it
+ * easy to add searching functionaliy to existing JTables -- or
+ * making new search capable table lucene.
+ *
+ * This decorator works by holding a reference to a decorated ot inner
+ * TableModel. All data is stored within that table model, not this
+ * table model. Rather, this table model simply manages links to
+ * data in the inner table model according to the search. All methods on
+ * TableSearcher forward to the inner table model with subtle filtering
+ * or alteration according to the search criteria.
+ *
+ * Using the table model:
+ *
+ * Pass the TableModel you want to decorate in at the constructor. When
+ * the TableModel initializes, it displays all search results. Call
+ * the search methid with any vaid Lucene search String and the data
+ * will be filtered by the search string. Users can always clear the search
+ * at any time by searching with an empty string. Additionally, you can
+ * add a button calling the clearSearch() method.
+ *
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class TableSearcher extends AbstractTableModel {
+
+    /**
+     * The inner table model we are decorating
+     */
+    protected TableModel tableModel;
+
+    /**
+     * This listener is used to register this class as a listener to
+     * the decorated table model for update events
+     */
+    private TableModelListener tableModelListener;
+
+    /**
+     * these keeps reference to the decorated table model for data
+     * only rows that match the search criteria are linked
+     */
+    private ArrayList rowToModelIndex = new ArrayList();
+
+
+    //Lucene stuff.
+
+    /**
+     * In memory lucene index
+     */
+    private RAMDirectory directory;
+
+    /**
+     * Cached lucene analyzer
+     */
+    private Analyzer analyzer;
+
+    /**
+     * Links between this table model and the decorated table model
+     * are maintained through links based on row number. This is a
+     * key constant to denote "row number" for indexing
+     */
+    private static final String ROW_NUMBER = "ROW_NUMBER";
+
+    /**
+     * Cache the current search String. Also used internally to
+     * key whether there is an active search running or not. i.e. if
+     * searchString is null, there is no active search.
+     */
+    private String searchString = null;
+
+    /**
+     * @param tableModel The table model to decorate
+     */
+    public TableSearcher(TableModel tableModel) {
+        analyzer = new WhitespaceAnalyzer();
+        tableModelListener = new TableModelHandler();
+        setTableModel(tableModel);
+        tableModel.addTableModelListener(tableModelListener);
+        clearSearchingState();
+    }
+
+    /**
+     *
+     * @return The inner table model this table model is decorating
+     */
+    public TableModel getTableModel() {
+        return tableModel;
+    }
+
+    /**
+     * Set the table model used by this table model
+     * @param tableModel The new table model to decorate
+     */
+    public void setTableModel(TableModel tableModel) {
+
+        //remove listeners if there...
+        if (this.tableModel != null) {
+            this.tableModel.removeTableModelListener(tableModelListener);
+        }
+
+        this.tableModel = tableModel;
+        if (this.tableModel != null) {
+            this.tableModel.addTableModelListener(tableModelListener);
+        }
+
+        //recalculate the links between this table model and
+        //the inner table model since the decorated model just changed
+        reindex();
+
+        // let all listeners know the table has changed
+        fireTableStructureChanged();
+    }
+
+
+    /**
+     * Reset the search results and links to the decorated (inner) table
+     * model from this table model.
+     */
+    private void reindex() {
+        try {
+            // recreate the RAMDirectory
+            directory = new RAMDirectory();
+            IndexWriter writer = new IndexWriter(directory, analyzer, true);
+
+            // iterate through all rows
+            for (int row=0; row < tableModel.getRowCount(); row++){
+
+                //for each row make a new document
+                Document document = new Document();
+                //add the row number of this row in the decorated table model
+                //this will allow us to retrive the results later
+                //and map this table model's row to a row in the decorated
+                //table model
+                document.add(new Field(ROW_NUMBER, "" + row, true, true, 
true));
+                //iterate through all columns
+                //index the value keyed by the column name
+                //NOTE: there could be a problem with using column names with 
spaces
+                for (int column=0; column < tableModel.getColumnCount(); 
column++){
+                    String columnName = tableModel.getColumnName(column);
+                    String columnValue = 
String.valueOf(tableModel.getValueAt(row, column)).toLowerCase();
+                    document.add(new Field(columnName, columnValue, true, 
true, true));
+                }
+                writer.addDocument(document);
+            }
+            writer.optimize();
+            writer.close();
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * @return The current lucene analyzer
+     */
+    public Analyzer getAnalyzer() {
+        return analyzer;
+    }
+
+    /**
+     * @param analyzer The new analyzer to use
+     */
+    public void setAnalyzer(Analyzer analyzer) {
+        this.analyzer = analyzer;
+        //reindex from the model with the new analyzer
+        reindex();
+
+        //rerun the search if there is an active search
+        if (isSearching()){
+            search(searchString);
+        }
+    }
+
+    /**
+     * Run a new search.
+     *
+     * @param searchString Any valid lucene search string
+     */
+    public void search(String searchString){
+
+        //if search string is null or empty, clear the search == search all
+        if (searchString == null || searchString.equals("")){
+            clearSearchingState();
+            fireTableDataChanged();
+            return;
+        }
+
+
+        try {
+            //cache search String
+            this.searchString = searchString;
+
+            //make a new index searcher with the in memory (RAM) index.
+            IndexSearcher is = new IndexSearcher(directory);
+
+            //make an array of fields - one for each column
+            String[] fields = new String[tableModel.getColumnCount()];
+            for (int t=0; t<tableModel.getColumnCount(); t++){
+                fields[t]=tableModel.getColumnName(t);
+            }
+
+            //build a query based on the fields, searchString and cached 
analyzer
+            //NOTE: This is an area for improvement since the 
MultiFieldQueryParser
+            // has some weirdness.
+            Query query = MultiFieldQueryParser.parse(searchString, fields, 
analyzer);
+            //run the search
+            Hits hits = is.search(query);
+            //reset this table model with the new results
+            resetSearchResults(hits);
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+
+        //notify all listeners that the table has been changed
+        fireTableStructureChanged();
+    }
+
+    /**
+     *
+     * @param hits The new result set to set this table to.
+     */
+    private void resetSearchResults(Hits hits) {
+        try {
+            //clear our index mapping this table model rows to
+            //the decorated inner table model
+            rowToModelIndex.clear();
+            //iterate through the hits
+            //get the row number stored at the index
+            //that number is the row number of the decorated
+            //tabble model row that we are mapping to
+            for (int t=0; t<hits.length(); t++){
+                Document document = hits.doc(t);
+                Field field = document.getField(ROW_NUMBER);
+                rowToModelIndex.add(new Integer(field.stringValue()));
+            }
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    private int getModelRow(int row){
+        return ((Integer) rowToModelIndex.get(row)).intValue();
+    }
+
+    /**
+     * Clear the currently active search
+     * Resets the complete dataset of the decorated
+     * table model.
+     */
+    private void clearSearchingState(){
+        searchString = null;
+        rowToModelIndex.clear();
+        for (int t=0; t<tableModel.getRowCount(); t++){
+            rowToModelIndex.add(new Integer(t));
+        }
+    }
+
+    // TableModel interface methods
+    public int getRowCount() {
+        return (tableModel == null) ? 0 : rowToModelIndex.size();
+    }
+
+    public int getColumnCount() {
+        return (tableModel == null) ? 0 : tableModel.getColumnCount();
+    }
+
+    public String getColumnName(int column) {
+        return tableModel.getColumnName(column);
+    }
+
+    public Class getColumnClass(int column) {
+        return tableModel.getColumnClass(column);
+    }
+
+    public boolean isCellEditable(int row, int column) {
+        return tableModel.isCellEditable(getModelRow(row), column);
+    }
+
+    public Object getValueAt(int row, int column) {
+        return tableModel.getValueAt(getModelRow(row), column);
+    }
+
+    public void setValueAt(Object aValue, int row, int column) {
+        tableModel.setValueAt(aValue, getModelRow(row), column);
+    }
+
+    private boolean isSearching() {
+        return searchString != null;
+    }
+
+    private class TableModelHandler implements TableModelListener {
+        public void tableChanged(TableModelEvent e) {
+            // If we're not searching, just pass the event along.
+            if (!isSearching()) {
+                clearSearchingState();
+                reindex();
+                fireTableChanged(e);
+                return;
+            }
+
+            // Something has happened to the data that may have invalidated 
the search.
+            reindex();
+            search(searchString);
+            fireTableDataChanged();
+            return;
+        }
+
+    }
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/package.html
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/package.html?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/package.html
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/java/org/apache/lucene/swing/models/package.html
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,5 @@
+<html>
+<body>
+Decorators for JTable TableModel and JList ListModel encapsulating Lucene 
indexing and searching functionality. .
+</body>
+</html>

Added: lucene/java/trunk/contrib/swing/src/java/overview.html
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/java/overview.html?view=auto&rev=156591
==============================================================================
--- lucene/java/trunk/contrib/swing/src/java/overview.html (added)
+++ lucene/java/trunk/contrib/swing/src/java/overview.html Tue Mar  8 17:52:13 
2005
@@ -0,0 +1,7 @@
+<html>
+  <head>
+    <title>Jakarta Lucene Swing Component Models</title>
+  </head>
+  <body>
+  </body>
+</html>
\ No newline at end of file

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/BaseListModel.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/BaseListModel.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/BaseListModel.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/BaseListModel.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,55 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class BaseListModel extends AbstractListModel {
+    private ArrayList data = new ArrayList();
+
+    public BaseListModel(Iterator iterator) {
+        while (iterator.hasNext()) {
+            data.add(iterator.next());
+        }
+    }
+
+    public int getSize() {
+        return data.size();
+    }
+
+    public Object getElementAt(int index) {
+        return data.get(index);
+    }
+
+    public void addRow(Object toAdd) {
+        data.add(toAdd);
+        fireContentsChanged(this, 0, getSize());
+    }
+
+    public void removeRow(Object toRemove) {
+        data.remove(toRemove);
+        fireContentsChanged(this, 0, getSize());
+    }
+
+
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/BaseTableModel.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/BaseTableModel.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/BaseTableModel.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/BaseTableModel.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,100 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import javax.swing.table.AbstractTableModel;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class BaseTableModel extends AbstractTableModel {
+    private ArrayList columnNames = new ArrayList();
+    private ArrayList rows = new ArrayList();
+
+    public BaseTableModel(Iterator data) {
+        columnNames.add("Name");
+        columnNames.add("Type");
+        columnNames.add("Phone");
+        columnNames.add("Street");
+        columnNames.add("City");
+        columnNames.add("State");
+        columnNames.add("Zip");
+
+        while (data.hasNext()) {
+            Object nextRow = (Object) data.next();
+            rows.add(nextRow);
+        }
+    }
+
+    public int getColumnCount() {
+        return columnNames.size();
+    }
+
+    public int getRowCount() {
+        return rows.size();
+    }
+
+    public void addRow(RestaurantInfo info){
+        rows.add(info);
+        fireTableDataChanged();
+    }
+
+    public void removeRow(RestaurantInfo info){
+        rows.remove(info);
+        fireTableDataChanged();
+    }
+
+    public boolean isCellEditable(int rowIndex, int columnIndex) {
+        return false;
+    }
+
+    public Class getColumnClass(int columnIndex) {
+        return String.class;
+    }
+
+    public Object getValueAt(int rowIndex, int columnIndex) {
+        RestaurantInfo restaurantInfo = (RestaurantInfo) rows.get(rowIndex);
+        if (columnIndex == 0){ // name
+            return restaurantInfo.getName();
+        } else if (columnIndex == 1){ // category
+            return restaurantInfo.getType();
+        } else if (columnIndex == 2){ // phone
+            return restaurantInfo.getPhone();
+        } else if (columnIndex == 3){ // street
+            return restaurantInfo.getStreet();
+        } else if (columnIndex == 4){ // city
+            return restaurantInfo.getCity();
+        } else if (columnIndex == 5){ // state
+            return restaurantInfo.getState();
+        } else if (columnIndex == 6){ // zip
+            return restaurantInfo.getZip();
+        } else {
+            return "";
+        }
+    }
+
+    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+        //no op
+    }
+
+    public String getColumnName(int columnIndex) {
+        return columnNames.get(columnIndex).toString();
+    }
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/DataStore.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/DataStore.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/DataStore.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/DataStore.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,205 @@
+package org.apache.lucene.swing.models;
+
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.math.BigDecimal;
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class DataStore {
+
+    private static final String ITALIAN_CATEGORY = "Italian";
+    private static final String CUBAN_CATEGORY = "Cuban";
+    private static final String STEAK_CATEGORY = "Steak";
+    private static int id = 0;
+
+    static Collection restaurants = new ArrayList();
+    static RestaurantInfo pinos = new RestaurantInfo();
+    static RestaurantInfo canolis = new RestaurantInfo();
+    static RestaurantInfo picadillo = new RestaurantInfo();
+    static RestaurantInfo versailles = new RestaurantInfo();
+    static RestaurantInfo laCaretta = new RestaurantInfo();
+    static RestaurantInfo laCaretta2 = new RestaurantInfo();
+    static RestaurantInfo laCaretta3 = new RestaurantInfo();
+    static RestaurantInfo ranchaLuna = new RestaurantInfo();
+    static RestaurantInfo leMerais = new RestaurantInfo();
+    static RestaurantInfo chris = new RestaurantInfo();
+    static RestaurantInfo outback = new RestaurantInfo();
+    static RestaurantInfo outback2 = new RestaurantInfo();
+    static RestaurantInfo outback3 = new RestaurantInfo();
+    static RestaurantInfo outback4 = new RestaurantInfo();
+
+
+    public static Iterator getRestaurants(){
+        return restaurants.iterator();
+    }
+
+    static {
+        pinos.setId(getNextId());
+        pinos.setType(ITALIAN_CATEGORY);
+        pinos.setName("Pino's");
+        pinos.setPhone("(305) 111-2222");
+        pinos.setStreet("12115 105th Street ");
+        pinos.setCity("Miami");
+        pinos.setState("FL");
+        pinos.setZip("33176");
+        restaurants.add(pinos);
+
+        canolis.setId(getNextId());
+        canolis.setType(ITALIAN_CATEGORY);
+        canolis.setName("Canoli's");
+        canolis.setPhone("(305) 234-5543");
+        canolis.setStreet("12123 85th Street ");
+        canolis.setCity("Miami");
+        canolis.setState("FL");
+        canolis.setZip("33176");
+        restaurants.add(canolis);
+
+        picadillo.setId(getNextId());
+        picadillo.setType(CUBAN_CATEGORY);
+        picadillo.setName("Picadillo");
+        picadillo.setPhone("(305) 746-7865");
+        picadillo.setStreet("109 12th Street ");
+        picadillo.setCity("Miami");
+        picadillo.setState("FL");
+        picadillo.setZip("33176");
+        restaurants.add(picadillo);
+
+        versailles.setId(getNextId());
+        versailles.setType(CUBAN_CATEGORY);
+        versailles.setName("Cafe Versailles");
+        versailles.setPhone("(305) 201-5438");
+        versailles.setStreet("312 8th Street ");
+        versailles.setCity("Miami");
+        versailles.setState("FL");
+        versailles.setZip("33176");
+        restaurants.add(versailles);
+
+        laCaretta.setId(getNextId());
+        laCaretta.setType(CUBAN_CATEGORY);
+        laCaretta.setName("La Carretta");
+        laCaretta.setPhone("(305) 342-9876");
+        laCaretta.setStreet("348 8th Street ");
+        laCaretta.setCity("Miami");
+        laCaretta.setState("FL");
+        laCaretta.setZip("33176");
+        restaurants.add(laCaretta);
+
+        laCaretta2.setId(getNextId());
+        laCaretta2.setType(CUBAN_CATEGORY);
+        laCaretta2.setName("La Carretta");
+        laCaretta2.setPhone("(305) 556-9876");
+        laCaretta2.setStreet("31224 23rd Street ");
+        laCaretta2.setCity("Miami");
+        laCaretta2.setState("FL");
+        laCaretta2.setZip("33176");
+        restaurants.add(laCaretta2);
+
+        laCaretta3.setId(getNextId());
+        laCaretta3.setType(CUBAN_CATEGORY);
+        laCaretta3.setName("La Carretta");
+        laCaretta3.setPhone("(305) 682-9876");
+        laCaretta3.setStreet("23543 107th Street ");
+        laCaretta3.setCity("Miami");
+        laCaretta3.setState("FL");
+        laCaretta3.setZip("33176");
+        restaurants.add(laCaretta3);
+
+        ranchaLuna.setId(getNextId());
+        ranchaLuna.setType(CUBAN_CATEGORY);
+        ranchaLuna.setName("Rancha Luna");
+        ranchaLuna.setPhone("(305) 777-4384");
+        ranchaLuna.setStreet("110 23rd Street ");
+        ranchaLuna.setCity("Miami");
+        ranchaLuna.setState("FL");
+        ranchaLuna.setZip("33176");
+        restaurants.add(ranchaLuna);
+
+        leMerais.setId(getNextId());
+        leMerais.setType(STEAK_CATEGORY);
+        leMerais.setName("Le Merais");
+        leMerais.setPhone("(212) 654-9187");
+        leMerais.setStreet("11 West 46th Street");
+        leMerais.setCity("New York");
+        leMerais.setState("NY");
+        leMerais.setZip("10018");
+        restaurants.add(leMerais);
+
+        chris.setId(getNextId());
+        chris.setType(STEAK_CATEGORY);
+        chris.setName("Ruth's Chris Seakhouse");
+        chris.setPhone("(305) 354-8885");
+        chris.setStreet("12365 203rd Street ");
+        chris.setCity("Miami");
+        chris.setState("FL");
+        chris.setZip("33176");
+        restaurants.add(chris);
+
+        outback.setId(getNextId());
+        outback.setType(STEAK_CATEGORY);
+        outback.setName("Outback");
+        outback.setPhone("(305) 244-7623");
+        outback.setStreet("348 136th Street ");
+        outback.setCity("Miami");
+        outback.setState("FL");
+        outback.setZip("33176");
+        restaurants.add(outback);
+
+        outback2.setId(getNextId());
+        outback2.setType(STEAK_CATEGORY);
+        outback2.setName("Outback");
+        outback2.setPhone("(305) 533-6522");
+        outback2.setStreet("21 207th Street ");
+        outback2.setCity("Miami");
+        outback2.setState("FL");
+        outback2.setZip("33176");
+        restaurants.add(outback2);
+
+        outback3.setId(getNextId());
+        outback3.setType(STEAK_CATEGORY);
+        outback3.setName("Outback");
+        outback3.setPhone("(305) 244-7623");
+        outback3.setStreet("10117 107th Street ");
+        outback3.setCity("Miami");
+        outback3.setState("FL");
+        outback3.setZip("33176");
+        restaurants.add(outback3);
+
+        outback4.setId(getNextId());
+        outback4.setType(STEAK_CATEGORY);
+        outback4.setName("Outback");
+        outback4.setPhone("(954) 221-3312");
+        outback4.setStreet("10 11th Street ");
+        outback4.setCity("Aventura");
+        outback4.setState("FL");
+        outback4.setZip("32154");
+        restaurants.add(outback4);
+
+    }
+
+    private static int getNextId(){
+        id++;
+        return id;
+    }
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/ListSearcherSimulator.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/ListSearcherSimulator.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/ListSearcherSimulator.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/ListSearcherSimulator.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,86 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import javax.swing.*;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.DocumentEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.awt.*;
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class ListSearcherSimulator {
+
+    public ListSearcherSimulator() {
+        JFrame frame = new JFrame();
+        frame.setBounds(200,200, 400,250);
+
+        JList list = new JList();
+        JScrollPane scrollPane = new JScrollPane(list);
+
+        final BaseListModel listModel = new 
BaseListModel(DataStore.getRestaurants());
+        final ListSearcher listSearcher = new ListSearcher(listModel);
+
+        list.setModel(listSearcher);
+
+        final JTextField searchField = new JTextField();
+        searchField.getDocument().addDocumentListener(
+                new DocumentListener(){
+                    public void changedUpdate(DocumentEvent e) {
+                        
listSearcher.search(searchField.getText().trim().toLowerCase());
+                    }
+
+                    public void insertUpdate(DocumentEvent e) {
+                        
listSearcher.search(searchField.getText().trim().toLowerCase());
+                    }
+
+                    public void removeUpdate(DocumentEvent e) {
+                        
listSearcher.search(searchField.getText().trim().toLowerCase());
+                    }
+                }
+        );
+
+        frame.getContentPane().setLayout(new BorderLayout());
+        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
+
+        JPanel searchPanel = new JPanel();
+        searchPanel.setLayout(new BorderLayout(10,10));
+        searchPanel.add(searchField, BorderLayout.CENTER);
+        searchPanel.add(new JLabel("Search: "), BorderLayout.WEST);
+
+        JPanel topPanel = new JPanel(new BorderLayout());
+        topPanel.add(searchPanel, BorderLayout.CENTER);
+        topPanel.add(new JPanel(), BorderLayout.EAST);
+        topPanel.add(new JPanel(), BorderLayout.WEST);
+        topPanel.add(new JPanel(), BorderLayout.NORTH);
+        topPanel.add(new JPanel(), BorderLayout.SOUTH);
+
+        frame.getContentPane().add(topPanel, BorderLayout.NORTH);
+
+        frame.setTitle("Lucene powered table searching");
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.show();
+    }
+
+    public static void main(String[] args) {
+        new ListSearcherSimulator();
+    }
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/RestaurantInfo.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/RestaurantInfo.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/RestaurantInfo.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/RestaurantInfo.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,102 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class RestaurantInfo {
+    private int id;
+    private String name;
+
+    private String type;
+
+    private String phone;
+    private String street;
+    private String city;
+    private String state;
+    private String zip;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getStreet() {
+        return street;
+    }
+
+    public void setStreet(String street) {
+        this.street = street;
+    }
+
+    public String getCity() {
+        return city;
+    }
+
+    public void setCity(String city) {
+        this.city = city;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public void setState(String state) {
+        this.state = state;
+    }
+
+    public String getZip() {
+        return zip;
+    }
+
+    public void setZip(String zip) {
+        this.zip = zip;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String toString() {
+        return getName() + " - " + getPhone();
+    }
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TableSearcherSimulator.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TableSearcherSimulator.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TableSearcherSimulator.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TableSearcherSimulator.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,83 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class TableSearcherSimulator {
+
+    public TableSearcherSimulator() {
+        JFrame frame = new JFrame();
+        frame.setBounds(200,200, 400,250);
+
+        JTable table = new JTable();
+        final BaseTableModel tableModel = new 
BaseTableModel(DataStore.getRestaurants());
+        final TableSearcher searchTableModel = new TableSearcher(tableModel);
+
+        table.setModel(searchTableModel);
+        JScrollPane scrollPane = new JScrollPane(table);
+
+        final JTextField searchField = new JTextField();
+        JButton searchButton = new JButton("Go");
+
+        ActionListener searchListener = new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+               
searchTableModel.search(searchField.getText().trim().toLowerCase());
+                searchField.requestFocus();
+            }
+        };
+
+        searchButton.addActionListener(searchListener);
+        searchField.addActionListener(searchListener);
+
+
+
+        frame.getContentPane().setLayout(new BorderLayout());
+        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
+
+        JPanel searchPanel = new JPanel();
+        searchPanel.setLayout(new BorderLayout(10,10));
+        searchPanel.add(searchField, BorderLayout.CENTER);
+        searchPanel.add(searchButton, BorderLayout.EAST);
+
+        JPanel topPanel = new JPanel(new BorderLayout());
+        topPanel.add(searchPanel, BorderLayout.CENTER);
+        topPanel.add(new JPanel(), BorderLayout.EAST);
+        topPanel.add(new JPanel(), BorderLayout.WEST);
+        topPanel.add(new JPanel(), BorderLayout.NORTH);
+        topPanel.add(new JPanel(), BorderLayout.SOUTH);
+
+        frame.getContentPane().add(topPanel, BorderLayout.NORTH);
+
+        frame.setTitle("Lucene powered table searching");
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.show();
+
+    }
+
+
+    public static void main(String[] args) {
+        new TableSearcherSimulator();
+    }
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestBasicList.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestBasicList.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestBasicList.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestBasicList.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,52 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.util.ArrayList;
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ **/
+public class TestBasicList extends TestCase {
+    private ListModel baseListModel;
+    private ListSearcher listSearcher;
+    private ArrayList list;
+
+    protected void setUp() throws Exception {
+        list = new ArrayList();
+        list.add(DataStore.canolis);
+        list.add(DataStore.chris);
+
+        baseListModel = new BaseListModel(list.iterator());
+        listSearcher = new ListSearcher(baseListModel);
+    }
+
+
+    public void testRows(){
+        assertEquals(list.size(), listSearcher.getSize());
+    }
+
+    public void testValueAt(){
+        assertEquals(baseListModel.getElementAt(0), 
listSearcher.getElementAt(0));
+        assertNotSame(baseListModel.getElementAt(1), 
listSearcher.getElementAt(0));
+    }
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestBasicTable.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestBasicTable.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestBasicTable.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestBasicTable.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,59 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import javax.swing.table.TableModel;
+import java.util.ArrayList;
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class TestBasicTable extends TestCase {
+    private TableModel baseTableModel;
+    private TableSearcher tableSearcher;
+    private ArrayList list;
+
+    protected void setUp() throws Exception {
+        list = new ArrayList();
+        list.add(DataStore.canolis);
+        list.add(DataStore.chris);
+
+        baseTableModel = new BaseTableModel(list.iterator());
+        tableSearcher = new TableSearcher(baseTableModel);
+    }
+
+    public void testColumns(){
+
+        assertEquals(baseTableModel.getColumnCount(), 
tableSearcher.getColumnCount());
+        assertEquals(baseTableModel.getColumnName(0), 
tableSearcher.getColumnName(0));
+        assertNotSame(baseTableModel.getColumnName(0), 
tableSearcher.getColumnName(1));
+        assertEquals(baseTableModel.getColumnClass(0), 
tableSearcher.getColumnClass(0));
+    }
+
+    public void testRows(){
+        assertEquals(list.size(), tableSearcher.getRowCount());
+    }
+
+    public void testValueAt(){
+        assertEquals(baseTableModel.getValueAt(0,0), 
tableSearcher.getValueAt(0,0));
+        assertEquals(baseTableModel.getValueAt(0,3), 
tableSearcher.getValueAt(0,3));
+    }
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestSearchingList.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestSearchingList.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestSearchingList.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestSearchingList.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,48 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import junit.framework.TestCase;
+
+import javax.swing.table.TableModel;
+import javax.swing.*;
+
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */ 
+public class TestSearchingList extends TestCase {
+    private ListModel baseListModel;
+    private ListSearcher listSearcher;
+
+    protected void setUp() throws Exception {
+        baseListModel = new BaseListModel(DataStore.getRestaurants());
+        listSearcher = new ListSearcher(baseListModel);
+    }
+
+    public void testSearch(){
+        //make sure data is there
+        assertEquals(baseListModel.getSize(), listSearcher.getSize());
+        //search for pino's
+        listSearcher.search("pino's");
+        assertEquals(1, listSearcher.getSize());
+        //clear search and check that
+        listSearcher.search(null);
+        assertEquals(baseListModel.getSize(), listSearcher.getSize());
+    }
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestSearchingTable.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestSearchingTable.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestSearchingTable.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestSearchingTable.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,46 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import junit.framework.TestCase;
+
+import javax.swing.table.TableModel;
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class TestSearchingTable extends TestCase {
+    private TableModel baseTableModel;
+    private TableSearcher tableSearcher;
+
+    protected void setUp() throws Exception {
+        baseTableModel = new BaseTableModel(DataStore.getRestaurants());
+        tableSearcher = new TableSearcher(baseTableModel);
+    }
+
+    public void testSearch(){
+        //make sure data is there
+        assertEquals(baseTableModel.getRowCount(), 
tableSearcher.getRowCount());
+        //search for pino's
+        tableSearcher.search("pino's");
+        assertEquals(1, tableSearcher.getRowCount());
+        //clear search and check that
+        tableSearcher.search(null);
+        assertEquals(baseTableModel.getRowCount(), 
tableSearcher.getRowCount());
+    }
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestUpdatingList.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestUpdatingList.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestUpdatingList.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestUpdatingList.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,81 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import junit.framework.TestCase;
+
+import javax.swing.table.TableModel;
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class TestUpdatingList extends TestCase {
+    private BaseListModel baseListModel;
+    private ListSearcher listSearcher;
+
+    RestaurantInfo infoToAdd1, infoToAdd2;
+
+    protected void setUp() throws Exception {
+        baseListModel = new BaseListModel(DataStore.getRestaurants());
+        listSearcher = new ListSearcher(baseListModel);
+
+        infoToAdd1 = new RestaurantInfo();
+        infoToAdd1.setName("Pino's");
+
+        infoToAdd2 = new RestaurantInfo();
+        infoToAdd2.setName("Pino's");
+        infoToAdd2.setType("Italian");
+    }
+
+    public void testAddWithoutSearch(){
+        assertEquals(baseListModel.getSize(), listSearcher.getSize());
+        int count = listSearcher.getSize();
+        baseListModel.addRow(infoToAdd1);
+        count++;
+        assertEquals(count, listSearcher.getSize());
+    }
+
+    public void testRemoveWithoutSearch(){
+        assertEquals(baseListModel.getSize(), listSearcher.getSize());
+        baseListModel.addRow(infoToAdd1);
+        int count = listSearcher.getSize();
+        baseListModel.removeRow(infoToAdd1);
+        count--;
+        assertEquals(count, listSearcher.getSize());
+    }
+
+    public void testAddWithSearch(){
+        assertEquals(baseListModel.getSize(), listSearcher.getSize());
+        listSearcher.search("pino's");
+        int count = listSearcher.getSize();
+        baseListModel.addRow(infoToAdd2);
+        count++;
+        assertEquals(count, listSearcher.getSize());
+    }
+
+    public void testRemoveWithSearch(){
+        assertEquals(baseListModel.getSize(), listSearcher.getSize());
+        baseListModel.addRow(infoToAdd1);
+        listSearcher.search("pino's");
+        int count = listSearcher.getSize();
+        baseListModel.removeRow(infoToAdd1);
+        count--;
+        assertEquals(count, listSearcher.getSize());
+    }
+
+
+}

Added: 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestUpdatingTable.java
URL: 
http://svn.apache.org/viewcvs/lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestUpdatingTable.java?view=auto&rev=156591
==============================================================================
--- 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestUpdatingTable.java
 (added)
+++ 
lucene/java/trunk/contrib/swing/src/test/org/apache/lucene/swing/models/TestUpdatingTable.java
 Tue Mar  8 17:52:13 2005
@@ -0,0 +1,81 @@
+package org.apache.lucene.swing.models;
+
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import junit.framework.TestCase;
+
+import javax.swing.table.TableModel;
+
+/**
+ * @author Jonathan Simon - [EMAIL PROTECTED]
+ */
+public class TestUpdatingTable extends TestCase {
+    private BaseTableModel baseTableModel;
+    private TableSearcher tableSearcher;
+
+    RestaurantInfo infoToAdd1, infoToAdd2;
+
+    protected void setUp() throws Exception {
+        baseTableModel = new BaseTableModel(DataStore.getRestaurants());
+        tableSearcher = new TableSearcher(baseTableModel);
+
+        infoToAdd1 = new RestaurantInfo();
+        infoToAdd1.setName("Pino's");
+        infoToAdd1.setType("Italian");
+
+        infoToAdd2 = new RestaurantInfo();
+        infoToAdd2.setName("Pino's");
+        infoToAdd2.setType("Italian");
+    }
+
+    public void testAddWithoutSearch(){
+        assertEquals(baseTableModel.getRowCount(), 
tableSearcher.getRowCount());
+        int count = tableSearcher.getRowCount();
+        baseTableModel.addRow(infoToAdd1);
+        count++;
+        assertEquals(count, tableSearcher.getRowCount());
+    }
+
+    public void testRemoveWithoutSearch(){
+        assertEquals(baseTableModel.getRowCount(), 
tableSearcher.getRowCount());
+        int count = tableSearcher.getRowCount();
+        baseTableModel.addRow(infoToAdd1);
+        baseTableModel.removeRow(infoToAdd1);
+        assertEquals(count, tableSearcher.getRowCount());
+    }
+
+    public void testAddWithSearch(){
+        assertEquals(baseTableModel.getRowCount(), 
tableSearcher.getRowCount());
+        tableSearcher.search("pino's");
+        int count = tableSearcher.getRowCount();
+        baseTableModel.addRow(infoToAdd2);
+        count++;
+        assertEquals(count, tableSearcher.getRowCount());
+    }
+
+    public void testRemoveWithSearch(){
+        assertEquals(baseTableModel.getRowCount(), 
tableSearcher.getRowCount());
+        baseTableModel.addRow(infoToAdd1);
+        tableSearcher.search("pino's");
+        int count = tableSearcher.getRowCount();
+        baseTableModel.removeRow(infoToAdd1);
+        count--;
+        assertEquals(count, tableSearcher.getRowCount());
+    }
+
+
+}


Reply via email to