org.jdesktop.swingx.search.TableSearchable Maven / Gradle / Ivy
Show all versions of swingx-all Show documentation
/*
* $Id: TableSearchable.java 3194 2009-01-21 11:39:19Z kleopatra $
*
* Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jdesktop.swingx.search;
import java.awt.Rectangle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.AbstractHighlighter;
import org.jdesktop.swingx.decorator.Highlighter;
/**
* An Searchable implementation for use in JXTable.
*
* @author Jeanette Winzenburg
*/
public class TableSearchable extends AbstractSearchable {
/** The target JXTable. */
protected JXTable table;
/**
* Instantiates a TableSearchable with the given table as target.
*
* @param table the JXTable to search.
*/
public TableSearchable(JXTable table) {
this.table = table;
}
/**
* {@inheritDoc}
*
*
* This implementation loops through the cells in a row to find a match.
*/
@Override
protected void findMatchAndUpdateState(Pattern pattern, int startRow,
boolean backwards) {
SearchResult matchRow = null;
if (backwards) {
// CHECK: off-one end still needed?
// Probably not - the findXX don't have side-effects any longer
// hmmm... still needed: even without side-effects we need to
// guarantee calling the notfound update at the very end of the
// loop.
for (int r = startRow; r >= -1 && matchRow == null; r--) {
matchRow = findMatchBackwardsInRow(pattern, r);
updateState(matchRow);
}
} else {
for (int r = startRow; r <= getSize() && matchRow == null; r++) {
matchRow = findMatchForwardInRow(pattern, r);
updateState(matchRow);
}
}
// KEEP - JW: Needed to update if loop wasn't entered!
// the alternative is to go one off in the loop. Hmm - which is
// preferable?
// updateState(matchRow);
}
/**
* {@inheritDoc}
*
*
* Implemented to search for an extension in the cell given by row and
* foundColumn.
*/
@Override
protected SearchResult findExtendedMatch(Pattern pattern, int row) {
return findMatchAt(pattern, row, lastSearchResult.foundColumn);
}
/**
* Searches forward through columns of the given row. Starts at
* lastFoundColumn or first column if lastFoundColumn < 0. returns an
* appropriate SearchResult if a matching cell is found in this row or null
* if no match is found. A row index out off range results in a no-match.
*
* @param pattern Pattern
that we will try to locate
* @param row the row to search
* @return an appropriate SearchResult
if a matching cell is
* found in this row or null if no match is found
*/
private SearchResult findMatchForwardInRow(Pattern pattern, int row) {
int startColumn = (lastSearchResult.foundColumn < 0) ? 0
: lastSearchResult.foundColumn;
if (isValidIndex(row)) {
for (int column = startColumn; column < table.getColumnCount(); column++) {
SearchResult result = findMatchAt(pattern, row, column);
if (result != null)
return result;
}
}
return null;
}
/**
* Searches forward through columns of the given row. Starts at
* lastFoundColumn or first column if lastFoundColumn < 0. returns an
* appropriate SearchResult if a matching cell is found in this row or null
* if no match is found. A row index out off range results in a no-match.
*
* @param pattern Pattern
that we will try to locate
* @param row the row to search
* @return an appropriate SearchResult
if a matching cell is
* found in this row or null if no match is found
*/
private SearchResult findMatchBackwardsInRow(Pattern pattern, int row) {
int startColumn = (lastSearchResult.foundColumn < 0) ? table
.getColumnCount() - 1 : lastSearchResult.foundColumn;
if (isValidIndex(row)) {
for (int column = startColumn; column >= 0; column--) {
SearchResult result = findMatchAt(pattern, row, column);
if (result != null)
return result;
}
}
return null;
}
/**
* Matches the cell content at row/col against the given Pattern. Returns an
* appropriate SearchResult if matching or null if no matching
*
* @param pattern Pattern
that we will try to locate
* @param row a valid row index in view coordinates
* @param column a valid column index in view coordinates
* @return an appropriate SearchResult
if matching or null
*/
protected SearchResult findMatchAt(Pattern pattern, int row, int column) {
String text = table.getStringAt(row, column);
if ((text != null) && (text.length() > 0)) {
Matcher matcher = pattern.matcher(text);
if (matcher.find()) {
return createSearchResult(matcher, row, column);
}
}
return null;
}
/**
*
* {@inheritDoc}
*
*
* Overridden to adjust the column index to -1.
*/
@Override
protected int adjustStartPosition(int startIndex, boolean backwards) {
lastSearchResult.foundColumn = -1;
return super.adjustStartPosition(startIndex, backwards);
}
/**
* {@inheritDoc}
*
*
* Overridden to loop through all columns in a row.
*/
@Override
protected int moveStartPosition(int startRow, boolean backwards) {
if (backwards) {
lastSearchResult.foundColumn--;
if (lastSearchResult.foundColumn < 0) {
startRow--;
}
} else {
lastSearchResult.foundColumn++;
if (lastSearchResult.foundColumn >= table.getColumnCount()) {
lastSearchResult.foundColumn = -1;
startRow++;
}
}
return startRow;
}
/**
* {@inheritDoc}
*
*
* Overridden to check the column index of last find.
*/
@Override
protected boolean isEqualStartIndex(final int startIndex) {
return super.isEqualStartIndex(startIndex)
&& isValidColumn(lastSearchResult.foundColumn);
}
/**
* Checks if row is in range: 0 <= row < getRowCount().
*
* @param column the column index to check in view coordinates.
* @return true if the column is in range, false otherwise
*/
private boolean isValidColumn(int column) {
return column >= 0 && column < table.getColumnCount();
}
/**
* {@inheritDoc}
*/
@Override
protected int getSize() {
return table.getRowCount();
}
/**
* {@inheritDoc}
*/
@Override
public JXTable getTarget() {
return table;
}
/**
* Configures the match highlighter to the current match. Ensures that the
* matched cell is visible, if there is a match.
*
* PRE: markByHighlighter
*
*/
protected void moveMatchByHighlighter() {
AbstractHighlighter searchHL = getConfiguredMatchHighlighter();
// no match
if (!hasMatch()) {
return;
} else {
ensureInsertedSearchHighlighters(searchHL);
table.scrollCellToVisible(lastSearchResult.foundRow,
lastSearchResult.foundColumn);
}
}
/**
* {@inheritDoc}
*
*
* Overridden to convert the column index in the table's view coordinate
* system to model coordinate.
*
*
* PENDING JW: this is only necessary because the SearchPredicate wants its
* highlight column in model coordinates. But code comments in the
* SearchPredicate seem to indicate that we probably want to revise that
* (legacy?).
*/
@Override
protected int convertColumnIndexToModel(int viewColumn) {
return getTarget().convertColumnIndexToModel(viewColumn);
}
/**
* Moves the row selection to the matching cell and ensures its visibility,
* if any. Does nothing if there is no match.
*
*/
protected void moveMatchBySelection() {
if (!hasMatch()) {
return;
}
int row = lastSearchResult.foundRow;
int column = lastSearchResult.foundColumn;
table.changeSelection(row, column, false, false);
if (!table.getAutoscrolls()) {
// scrolling not handled by moving selection
Rectangle cellRect = table.getCellRect(row, column, true);
if (cellRect != null) {
table.scrollRectToVisible(cellRect);
}
}
}
/**
* {@inheritDoc}
*
*/
@Override
protected void moveMatchMarker() {
if (markByHighlighter()) {
moveMatchByHighlighter();
} else { // use selection
moveMatchBySelection();
}
}
/**
* {@inheritDoc}
*
*/
@Override
protected void removeHighlighter(Highlighter searchHighlighter) {
table.removeHighlighter(searchHighlighter);
}
/**
* {@inheritDoc}
*
*/
@Override
protected Highlighter[] getHighlighters() {
return table.getHighlighters();
}
/**
* {@inheritDoc}
*
*/
@Override
protected void addHighlighter(Highlighter highlighter) {
table.addHighlighter(highlighter);
}
}