001/*
002 * $Id: TreeSearchable.java 4178 2012-06-20 08:52:11Z kleopatra $
003 *
004 * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020 */
021package org.jdesktop.swingx.search;
022
023import java.util.regex.Matcher;
024import java.util.regex.Pattern;
025
026import org.jdesktop.swingx.JXTree;
027import org.jdesktop.swingx.decorator.AbstractHighlighter;
028import org.jdesktop.swingx.decorator.Highlighter;
029import org.jdesktop.swingx.util.Contract;
030
031/**
032 * A searchable targetting the visible rows of a JXTree.
033 * 
034 * 
035 */
036public class TreeSearchable extends AbstractSearchable {
037
038    protected JXTree tree;
039
040    /**
041     * Instantiates a Searchable for the given JTree.
042     * 
043     * @param tree the JTree to search, must not be null.
044     */
045    public TreeSearchable(JXTree tree) {
046        this.tree = Contract.asNotNull(tree, "tree must not be null");
047    }
048
049    @Override
050    protected void findMatchAndUpdateState(Pattern pattern, int startRow,
051            boolean backwards) {
052        SearchResult searchResult = null;
053        if (backwards) {
054            for (int index = startRow; index >= 0 && searchResult == null; index--) {
055                searchResult = findMatchAt(pattern, index);
056            }
057        } else {
058            for (int index = startRow; index < getSize()
059                    && searchResult == null; index++) {
060                searchResult = findMatchAt(pattern, index);
061            }
062        }
063        updateState(searchResult);
064
065    }
066
067    @Override
068    protected SearchResult findExtendedMatch(Pattern pattern, int row) {
069        return findMatchAt(pattern, row);
070    }
071
072    /**
073     * Matches the cell content at row/col against the given Pattern. Returns an
074     * appropriate SearchResult if matching or null if no matching
075     * 
076     * @param pattern
077     * @param row a valid row index in view coordinates a valid column index in
078     *        view coordinates
079     * @return an appropriate <code>SearchResult</code> if matching or null if
080     *         no matching
081     */
082    protected SearchResult findMatchAt(Pattern pattern, int row) {
083        String text = tree.getStringAt(row);
084        if ((text != null) && (text.length() > 0)) {
085            Matcher matcher = pattern.matcher(text);
086            if (matcher.find()) {
087                return createSearchResult(matcher, row, 0);
088            }
089        }
090        return null;
091    }
092
093    @Override
094    protected int getSize() {
095        return tree.getRowCount();
096    }
097
098    /**
099     * {@inheritDoc}
100     */
101    @Override
102    public JXTree getTarget() {
103        return tree;
104    }
105
106    /**
107     * {@inheritDoc}
108     */
109    @Override
110    protected void moveMatchMarker() {
111        if (markByHighlighter()) {
112            moveMatchByHighlighter();
113        } else { // use selection
114            moveMatchBySelection();
115        }
116    }
117
118    protected void moveMatchBySelection() {
119        // // the common behaviour (JXList, JXTable) is to not
120        // // move the selection if not found
121        if (!hasMatch()) {
122            return;
123        }
124        tree.setSelectionRow(lastSearchResult.foundRow);
125        tree.scrollRowToVisible(lastSearchResult.foundRow);
126    }
127
128    /**
129     * use and move the match highlighter. PRE: markByHighlighter
130     * 
131     */
132    protected void moveMatchByHighlighter() {
133        AbstractHighlighter searchHL = getConfiguredMatchHighlighter();
134        // no match
135        if (!hasMatch()) {
136            return;
137        } else {
138            ensureInsertedSearchHighlighters(searchHL);
139            tree.scrollRowToVisible(lastSearchResult.foundRow);
140        }
141    }
142
143    /**
144     * @param searchHighlighter
145     */
146    @Override
147    protected void removeHighlighter(Highlighter searchHighlighter) {
148        tree.removeHighlighter(searchHighlighter);
149    }
150
151    /**
152     * @return all registered highlighters
153     */
154    @Override
155    protected Highlighter[] getHighlighters() {
156        return tree.getHighlighters();
157    }
158
159    /**
160     * @param highlighter
161     */
162    @Override
163    protected void addHighlighter(Highlighter highlighter) {
164        tree.addHighlighter(highlighter);
165    }
166
167}