mirror of https://github.com/apache/lucene.git
Contribution of slick Swing models to enable on-the-fly searching of
tables and lists. Created by Jonathan Simon. git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@156591 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
4ddfbcffc6
commit
9824226394
|
@ -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>
|
|
@ -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> </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> </p>
|
||||
<p> </p>
|
||||
</body>
|
||||
</html>
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
Decorators for JTable TableModel and JList ListModel encapsulating Lucene indexing and searching functionality. .
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Jakarta Lucene Swing Component Models</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
**/
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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 - jonathan_s_simon@yahoo.com
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue