AbstractSearchable.java
上传用户:zhengdagz
上传日期:2014-03-06
资源大小:1956k
文件大小:12k
源码类别:

xml/soap/webservice

开发平台:

Java

  1. /*
  2.  * $Id: AbstractSearchable.java,v 1.2 2005/10/10 18:01:49 rbair Exp $
  3.  *
  4.  * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
  5.  * Santa Clara, California 95054, U.S.A. All rights reserved.
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  * 
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20.  */
  21. package org.jdesktop.swingx;
  22. import java.util.regex.MatchResult;
  23. import java.util.regex.Matcher;
  24. import java.util.regex.Pattern;
  25. /**
  26.  * An abstract implementation of Searchable supporting
  27.  * incremental search.
  28.  * 
  29.  * Keeps internal state to represent the previous search result.
  30.  * For all methods taking a string as parameter: compiles the String 
  31.  * to a Pattern as-is and routes to the central method taking a Pattern.
  32.  * 
  33.  * 
  34.  * @author Jeanette Winzenburg
  35.  */
  36. public abstract class AbstractSearchable implements Searchable {
  37.     /**
  38.      * a constant representing not-found state.
  39.      */
  40.     public static final SearchResult NO_MATCH = new SearchResult();
  41.     /**
  42.      * stores the result of the previous search.
  43.      */
  44.     protected SearchResult lastSearchResult = new SearchResult();
  45.     
  46.     /** key for client property to use SearchHighlighter as match marker. */
  47.     public static final String MATCH_HIGHLIGHTER = "match.highlighter";
  48.     /**
  49.      * Performs a forward search starting at the beginning 
  50.      * across the Searchable using String that represents a
  51.      * regex pattern; {@link java.util.regex.Pattern}. 
  52.      * @return the position of the match in appropriate coordinates or -1 if
  53.      *   no match found.
  54.      */
  55.     public int search(String searchString) {
  56.         return search(searchString, -1);
  57.     }
  58.     /**
  59.      * Performs a forward search starting at the given startIndex
  60.      * using String that represents a regex
  61.      * pattern; {@link java.util.regex.Pattern}. 
  62.      * @return the position of the match in appropriate coordinates or -1 if
  63.      *   no match found.
  64.      */
  65.     public int search(String searchString, int startIndex) {
  66.         return search(searchString, startIndex, false);
  67.     }
  68.     /**
  69.      * Performs a  search starting at the given startIndex
  70.      * using String that represents a regex
  71.      * pattern; {@link java.util.regex.Pattern}. The search direction 
  72.      * depends on the boolean parameter: forward/backward if false/true, respectively.
  73.      * @return the position of the match in appropriate coordinates or -1 if
  74.      *   no match found.
  75.      */
  76.     public int search(String searchString, int startIndex, boolean backward) {
  77.         Pattern pattern = null;
  78.         if (!isEmpty(searchString)) {
  79.             pattern = Pattern.compile(searchString, 0);
  80.         }
  81.         return search(pattern, startIndex, backward);
  82.     }
  83.     /**
  84.      * Performs a forward search starting at the beginning 
  85.      * across the Searchable using the pattern; {@link java.util.regex.Pattern}. 
  86.      * @return the position of the match in appropriate coordinates or -1 if
  87.      *   no match found.
  88.      */
  89.     public int search(Pattern pattern) {
  90.         return search(pattern, -1);
  91.     }
  92.     /**
  93.      * Performs a forward search starting at the given startIndex
  94.      * using the Pattern; {@link java.util.regex.Pattern}. 
  95.      * @return the position of the match in appropriate coordinates or -1 if
  96.      *   no match found.
  97.      */
  98.     public int search(Pattern pattern, int startIndex) {
  99.         return search(pattern, startIndex, false);
  100.     }
  101.     /**
  102.      * Performs a  search starting at the given startIndex
  103.      * using the pattern; {@link java.util.regex.Pattern}. 
  104.      * The search direction depends on the boolean parameter: 
  105.      * forward/backward if false/true, respectively.
  106.      * 
  107.      * Updates visible and internal search state.
  108.      * 
  109.      * @param startIndex the index to start search
  110.      * @param backwards flag for search direction
  111.      * @param pattern 
  112.      * @return the position of the match in appropriate coordinates or -1 if
  113.      *   no match found.
  114.      */
  115.     public int search(Pattern pattern, int startIndex, boolean backwards) {
  116.         int matchingRow = doSearch(pattern, startIndex, backwards);
  117.         moveMatchMarker();
  118.         return matchingRow;
  119.     }
  120.     /**
  121.      * Performs a  search starting at the given startIndex
  122.      * using the pattern; {@link java.util.regex.Pattern}. 
  123.      * The search direction depends on the boolean parameter: 
  124.      * forward/backward if false/true, respectively.
  125.      * 
  126.      * Updates internal search state.
  127.      * 
  128.      * @param pattern
  129.      * @param startIndex
  130.      * @param backwards
  131.      * @return the position of the match in appropriate coordinates or -1 if
  132.      *   no match found.
  133.      */
  134.     protected int doSearch(Pattern pattern, final int startIndex, boolean backwards) {
  135.         if (isTrivialNoMatch(pattern, startIndex)) {
  136.             updateState(null);
  137.             return lastSearchResult.foundRow;
  138.         }
  139.         
  140.         int startRow;
  141.         if (isEqualStartIndex(startIndex)) { // implies: the last found coordinates are valid
  142.             if (!isEqualPattern(pattern)) {
  143.                SearchResult searchResult = findExtendedMatch(pattern, startIndex);
  144.                if (searchResult != null) {
  145.                    updateState(searchResult);
  146.                    return lastSearchResult.foundRow;
  147.                }
  148.             }
  149.             // didn't find a match, make sure to move the startPosition
  150.             // for looking for the next/previous match
  151.             startRow = moveStartPosition(startIndex, backwards);
  152.             
  153.         } else { 
  154.             // startIndex is different from last search, reset the column to -1
  155.             // and make sure a -1 startIndex is mapped to first/last row, respectively.
  156.             startRow = adjustStartPosition(startIndex, backwards); 
  157.         }
  158.         findMatchAndUpdateState(pattern, startRow, backwards);
  159.         return lastSearchResult.foundRow;
  160.     }
  161.     /**
  162.      * loops through the searchable until a match is found or the 
  163.      * end is reached. Updates internal search state.
  164.      * @param pattern
  165.      * @param startRow
  166.      * @param backwards
  167.      */
  168.     protected abstract void findMatchAndUpdateState(Pattern pattern, int startRow, boolean backwards);
  169.     /**
  170.      * Checks and returns if it can be trivially decided to not match.
  171.      * Here: pattern is null or startIndex exceeds the upper size limit.
  172.      * 
  173.      * @param pattern
  174.      * @param startIndex
  175.      * @return
  176.      */
  177.     protected boolean isTrivialNoMatch(Pattern pattern, final int startIndex) {
  178.         return (pattern == null) || (startIndex >= getSize());
  179.     }
  180.     /**
  181.      * Called if 
  182.      * startIndex is different from last search
  183.      * and make sure a backwards/forwards search starts at last/first row,
  184.      * respectively.
  185.      * @param startIndex
  186.      * @param backwards
  187.      * @return
  188.      */
  189.     protected int adjustStartPosition(int startIndex, boolean backwards) {
  190.         if (startIndex < 0) {
  191.             if (backwards) {
  192.                 return getSize() - 1;
  193.             } else {
  194.                 return 0;
  195.             }
  196.         }
  197.         return startIndex;
  198.     }
  199.     /**
  200.      * Moves the internal start position for matching as appropriate and returns
  201.      * the new startIndex to use.
  202.      * Called if search was messaged with the same startIndex as previously.
  203.      * 
  204.      * @param startIndex
  205.      * @param backwards
  206.      * @return
  207.      */
  208.     protected int moveStartPosition(int startIndex, boolean backwards) {
  209.         if (backwards) {
  210.                    startIndex--;
  211.            } else {
  212.                    startIndex++;
  213.            }
  214.         return startIndex;
  215.     }
  216.     
  217.     /**
  218.      * Checks if the given Pattern should be considered as the same as 
  219.      * in a previous search.
  220.      * 
  221.      * Here: compares the patterns' regex.
  222.      * 
  223.      * @param pattern
  224.      * @return
  225.      */
  226.     protected boolean isEqualPattern(Pattern pattern) {
  227.         return pattern.pattern().equals(lastSearchResult.getRegEx());
  228.     }
  229.     /**
  230.      * Checks if the startIndex should be considered as the same as in
  231.      * the previous search.
  232.      * 
  233.      * @param startIndex
  234.      * @return true if the startIndex should be re-matched, false if not.
  235.      */
  236.     protected boolean isEqualStartIndex(final int startIndex) {
  237.         return isValidIndex(startIndex) && (startIndex == lastSearchResult.foundRow);
  238.     }
  239.     /**
  240.      * checks if the searchString should be interpreted as empty.
  241.      * here: returns true if string is null or has zero length.
  242.      * 
  243.      * @param searchString
  244.      * @return
  245.      */
  246.     protected boolean isEmpty(String searchString) {
  247.         return (searchString == null) || searchString.length() == 0;
  248.     }
  249.     /**
  250.      * called if sameRowIndex && !hasEqualRegEx.
  251.      * Matches the cell at row/lastFoundColumn against the pattern.
  252.      * PRE: lastFoundColumn valid.
  253.      * 
  254.      * @param pattern
  255.      * @param row 
  256.      * @return
  257.      */
  258.     protected abstract SearchResult findExtendedMatch(Pattern pattern, int row);
  259.  
  260.     /**
  261.      * Factory method to create a SearchResult from the given parameters.
  262.      * 
  263.      * @param matcher the matcher after a successful find. Must not be null.
  264.      * @param row the found index
  265.      * @param column the found column
  266.      * @return
  267.      */
  268.    protected SearchResult createSearchResult(Matcher matcher, int row, int column) {
  269.         return new SearchResult(matcher.pattern(), 
  270.                 matcher.toMatchResult(), row, column);
  271.     }
  272. /** 
  273.     * checks if index is in range: 0 <= index < getSize().
  274.     * @param index
  275.     * @return
  276.     */ 
  277.    protected boolean isValidIndex(int index) {
  278.         return index >= 0 && index < getSize();
  279.     }
  280.    /**
  281.     * returns the size of this searchable.
  282.     * 
  283.     * @return
  284.     */
  285.    protected abstract int getSize();
  286.    
  287.     protected void updateState(SearchResult searchResult) {
  288.         lastSearchResult.updateFrom(searchResult);
  289.     }
  290.     /**
  291.      * Moves the match marker according to current found state.
  292.      */
  293.     protected abstract void moveMatchMarker();
  294.     /**
  295.      * A convenience class to hold search state.
  296.      * NOTE: this is still in-flow, probably will take more responsibility/
  297.      * or even change altogether on further factoring
  298.      */
  299.     public static class SearchResult {
  300.         int foundRow;
  301.         int foundColumn;
  302.         MatchResult matchResult;
  303.         Pattern pattern;
  304.         public SearchResult() {
  305.             reset();
  306.         }
  307.         
  308.         public void updateFrom(SearchResult searchResult) {
  309.             if (searchResult == null) {
  310.                 reset();
  311.                 return;
  312.             }
  313.             foundRow = searchResult.foundRow;
  314.             foundColumn = searchResult.foundColumn;
  315.             matchResult = searchResult.matchResult;
  316.             pattern = searchResult.pattern;
  317.             
  318.         }
  319.         public String getRegEx() {
  320.             return pattern != null ? pattern.pattern() : null;
  321.         }
  322.         public SearchResult(Pattern ex, MatchResult result, int row, int column) {
  323.             pattern = ex;
  324.             matchResult = result;
  325.             foundRow = row;
  326.             foundColumn = column;
  327.         }
  328.         
  329.         public void reset() {
  330.             foundRow= -1;
  331.             foundColumn = -1;
  332.             matchResult = null;
  333.             pattern = null;
  334.         }
  335.         
  336.         
  337.     }
  338.     
  339. }