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:
Erik Hatcher 2005-03-09 01:52:13 +00:00
parent 4ddfbcffc6
commit 9824226394
18 changed files with 1764 additions and 0 deletions

24
contrib/swing/build.xml Normal file
View File

@ -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>

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,5 @@
<html>
<body>
Decorators for JTable TableModel and JList ListModel encapsulating Lucene indexing and searching functionality. .
</body>
</html>

View File

@ -0,0 +1,7 @@
<html>
<head>
<title>Jakarta Lucene Swing Component Models</title>
</head>
<body>
</body>
</html>

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}