PatternModel.java
资源名称:swingx.zip [点击查看]
上传用户:zhengdagz
上传日期:2014-03-06
资源大小:1956k
文件大小:18k
源码类别:
xml/soap/webservice
开发平台:
Java
- /*
- * $Id: PatternModel.java,v 1.17 2005/10/12 11:26:53 kleopatra Exp $
- *
- * Copyright 2004 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;
- import java.beans.PropertyChangeListener;
- import java.beans.PropertyChangeSupport;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- import java.util.regex.Pattern;
- import javax.swing.ActionMap;
- import javax.swing.UIManager;
- import org.jdesktop.swingx.action.AbstractActionExt;
- import org.jdesktop.swingx.action.BoundAction;
- /**
- * Presentation Model for Find/Filter Widgets.
- * <p>
- *
- * Compiles and holds a Pattern from rawText. There are different
- * predefined strategies to control the compilation:
- *
- * <ul>
- * <li> TODO: list and explain
- * </ul>
- *
- * Holds state for controlling the match process
- * for both find and filter (TODO - explain).
- * Relevant in all
- *
- * <ul>
- * <li> caseSensitive -
- * <li> empty - true if there's no searchString
- * <li> incremental - a hint to clients to react immediately
- * to pattern changes.
- *
- * </ul>
- *
- * Relevant in find contexts:
- * <ul>
- * <li> backwards - search direction if used in a find context
- * <li> wrapping - wrap over the end/start if not found
- * <li> foundIndex - storage for last found index
- * <li> autoAdjustFoundIndex - flag to indicate auto-incr/decr of foundIndex on setting.
- * Here the property correlates to !isIncremental() - to simplify batch vs.
- * incremental search ui.
- * </ul>
- *
- *
- * JW: Work-in-progress - Anchors will be factored into AnchoredSearchMode
- * <b>Anchors</b> By default, the scope of the pattern relative to strings
- * being tested are unanchored, ie, the pattern will match any part of the
- * tested string. Traditionally, special characters ('^' and '$') are used to
- * describe patterns that match the beginning (or end) of a string. If those
- * characters are included in the pattern, the regular expression will honor
- * them. However, for ease of use, two properties are included in this model
- * that will determine how the pattern will be evaluated when these characters
- * are omitted.
- * <p>
- * The <b>StartAnchored</b> property determines if the pattern must match from
- * the beginning of tested strings, or if the pattern can appear anywhere in the
- * tested string. Likewise, the <b>EndAnchored</b> property determines if the
- * pattern must match to the end of the tested string, or if the end of the
- * pattern can appear anywhere in the tested string. The default values (false
- * in both cases) correspond to the common database 'LIKE' operation, where the
- * pattern is considered to be a match if any part of the tested string matches
- * the pattern.
- *
- * @author Jeanette Winzenburg
- * @author David Hall
- */
- public class PatternModel {
- /**
- * The prefix marker to find component related properties in the
- * resourcebundle.
- */
- public static final String SEARCH_PREFIX = "Search.";
- public static final String REGEX_UNCHANGED = "regex";
- public static final String REGEX_ANCHORED = "anchored";
- public static final String REGEX_WILDCARD = "wildcard";
- public static final String REGEX_MATCH_RULES = "explicit";
- public static final String MATCH_RULE_CONTAINS = "contains";
- public static final String MATCH_RULE_EQUALS = "equals";
- public static final String MATCH_RULE_ENDSWITH = "endsWith";
- public static final String MATCH_RULE_STARTSWITH = "startsWith";
- public static final String MATCH_BACKWARDS_ACTION_COMMAND = "backwardsSearch";
- public static final String MATCH_WRAP_ACTION_COMMAND = "wrapSearch";
- public static final String MATCH_CASE_ACTION_COMMAND = "matchCase";
- public static final String MATCH_INCREMENTAL_ACTION_COMMAND = "matchIncremental";
- private String rawText;
- private boolean backwards;
- private Pattern pattern;
- private int foundIndex = -1;
- private boolean caseSensitive;
- private PropertyChangeSupport propertySupport;
- private String regexCreatorKey;
- private RegexCreator regexCreator;
- private boolean wrapping;
- private ActionMap actionMap;
- private boolean incremental;
- //---------------------- misc. properties not directly related to Pattern.
- public int getFoundIndex() {
- return foundIndex;
- }
- public void setFoundIndex(int foundIndex) {
- int old = getFoundIndex();
- updateFoundIndex(foundIndex);
- firePropertyChange("foundIndex", old, getFoundIndex());
- }
- /**
- *
- * @param newFoundIndex
- */
- protected void updateFoundIndex(int newFoundIndex) {
- if (newFoundIndex < 0) {
- this.foundIndex = newFoundIndex;
- return;
- }
- if (isAutoAdjustFoundIndex()) {
- foundIndex = backwards ? newFoundIndex -1 : newFoundIndex + 1;
- } else {
- foundIndex = newFoundIndex;
- }
- }
- public boolean isAutoAdjustFoundIndex() {
- return !isIncremental();
- }
- public boolean isBackwards() {
- return backwards;
- }
- public void setBackwards(boolean backwards) {
- boolean old = isBackwards();
- this.backwards = backwards;
- firePropertyChange("backwards", old, isBackwards());
- setFoundIndex(getFoundIndex());
- }
- public boolean isWrapping() {
- return wrapping;
- }
- public void setWrapping(boolean wrapping) {
- boolean old = isWrapping();
- this.wrapping = wrapping;
- firePropertyChange("wrapping", old, isWrapping());
- }
- public void setIncremental(boolean incremental) {
- boolean old = isIncremental();
- this.incremental = incremental;
- firePropertyChange("incremental", old, isIncremental());
- }
- public boolean isIncremental() {
- return incremental;
- }
- public boolean isCaseSensitive() {
- return caseSensitive;
- }
- public void setCaseSensitive(boolean caseSensitive) {
- boolean old = isCaseSensitive();
- this.caseSensitive = caseSensitive;
- updatePattern(caseSensitive);
- firePropertyChange("caseSensitive", old, isCaseSensitive());
- }
- public Pattern getPattern() {
- return pattern;
- }
- public String getRawText() {
- return rawText;
- }
- public void setRawText(String findText) {
- String old = getRawText();
- boolean oldEmpty = isEmpty();
- this.rawText = findText;
- updatePattern(createRegEx(findText));
- firePropertyChange("rawText", old, getRawText());
- firePropertyChange("empty", oldEmpty, isEmpty());
- }
- public boolean isEmpty() {
- return isEmpty(getRawText());
- }
- /**
- * returns a regEx for compilation into a pattern. Here: either a "contains"
- * (== partial find) or null if the input was empty.
- *
- * @param searchString
- * @return null if the input was empty, or a regex according to the internal
- * rules
- */
- private String createRegEx(String searchString) {
- if (isEmpty(searchString))
- return null; //".*";
- return getRegexCreator().createRegEx(searchString);
- }
- /**
- *
- * @param s
- * @return
- */
- private boolean isEmpty(String text) {
- return (text == null) || (text.length() == 0);
- }
- private void updatePattern(String regEx) {
- Pattern old = getPattern();
- if (isEmpty(regEx)) {
- pattern = null;
- } else if ((old == null) || (!old.pattern().equals(regEx))) {
- pattern = Pattern.compile(regEx, getFlags());
- }
- firePropertyChange("pattern", old, getPattern());
- }
- private int getFlags() {
- return isCaseSensitive() ? 0 : getCaseInsensitiveFlag();
- }
- private int getCaseInsensitiveFlag() {
- return Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
- }
- private void updatePattern(boolean caseSensitive) {
- if (pattern == null)
- return;
- Pattern old = getPattern();
- int flags = old.flags();
- int flag = getCaseInsensitiveFlag();
- if ((caseSensitive) && ((flags & flag) != 0)) {
- pattern = Pattern.compile(pattern.pattern(), 0);
- } else if (!caseSensitive && ((flags & flag) == 0)) {
- pattern = Pattern.compile(pattern.pattern(), flag);
- }
- firePropertyChange("pattern", old, getPattern());
- }
- public void addPropertyChangeListener(PropertyChangeListener l) {
- if (propertySupport == null) {
- propertySupport = new PropertyChangeSupport(this);
- }
- propertySupport.addPropertyChangeListener(l);
- }
- public void removePropertyChangeListener(PropertyChangeListener l) {
- if (propertySupport == null)
- return;
- propertySupport.removePropertyChangeListener(l);
- }
- protected void firePropertyChange(String name, Object oldValue,
- Object newValue) {
- if (propertySupport == null)
- return;
- propertySupport.firePropertyChange(name, oldValue, newValue);
- }
- /**
- * Responsible for converting a "raw text" into a valid
- * regular expression in the context of a set of rules.
- *
- */
- public static class RegexCreator {
- protected String matchRule;
- private List rules;
- public String getMatchRule() {
- if (matchRule == null) {
- matchRule = getDefaultMatchRule();
- }
- return matchRule;
- }
- public boolean isAutoDetect() {
- return false;
- }
- public String createRegEx(String searchString) {
- if (MATCH_RULE_CONTAINS.equals(getMatchRule())) {
- return createContainedRegEx(searchString);
- }
- if (MATCH_RULE_EQUALS.equals(getMatchRule())) {
- return createEqualsRegEx(searchString);
- }
- if (MATCH_RULE_STARTSWITH.equals(getMatchRule())){
- return createStartsAnchoredRegEx(searchString);
- }
- if (MATCH_RULE_ENDSWITH.equals(getMatchRule())) {
- return createEndAnchoredRegEx(searchString);
- }
- return searchString;
- }
- protected String createEndAnchoredRegEx(String searchString) {
- return Pattern.quote(searchString) + "$";
- }
- protected String createStartsAnchoredRegEx(String searchString) {
- return "^" + Pattern.quote(searchString);
- }
- protected String createEqualsRegEx(String searchString) {
- return "^" + Pattern.quote(searchString) + "$";
- }
- protected String createContainedRegEx(String searchString) {
- return Pattern.quote(searchString);
- }
- public void setMatchRule(String category) {
- this.matchRule = category;
- }
- protected String getDefaultMatchRule() {
- return MATCH_RULE_CONTAINS;
- }
- public List getMatchRules() {
- if (rules == null) {
- rules = createAndInitRules();
- }
- return rules;
- }
- private List createAndInitRules() {
- if (!supportsRules()) return Collections.EMPTY_LIST;
- List<String> list = new ArrayList<String>();
- list.add(MATCH_RULE_CONTAINS);
- list.add(MATCH_RULE_EQUALS);
- list.add(MATCH_RULE_STARTSWITH);
- list.add(MATCH_RULE_ENDSWITH);
- return list;
- }
- private boolean supportsRules() {
- return true;
- }
- }
- /**
- * Support for anchored input.
- *
- * PENDING: NOT TESTED - simply moved!
- * Need to define requirements...
- *
- */
- public static class AnchoredSearchMode extends RegexCreator {
- public boolean isAutoDetect() {
- return true;
- }
- public String createRegEx(String searchExp) {
- if (isAutoDetect()) {
- StringBuffer buf = new StringBuffer(searchExp.length() + 4);
- if (!hasStartAnchor(searchExp)) {
- if (isStartAnchored()) {
- buf.append("^");
- }
- }
- //PENDING: doesn't escape contained regex metacharacters...
- buf.append(searchExp);
- if (!hasEndAnchor(searchExp)) {
- if (isEndAnchored()) {
- buf.append("$");
- }
- }
- return buf.toString();
- }
- return super.createRegEx(searchExp);
- }
- private boolean hasStartAnchor(String str) {
- return str.startsWith("^");
- }
- private boolean hasEndAnchor(String str) {
- int len = str.length();
- if ((str.charAt(len - 1)) != '$')
- return false;
- // the string "$" is anchored
- if (len == 1)
- return true;
- // scan backwards along the string: if there's an odd number
- // of backslashes, then the last escapes the dollar and the
- // pattern is not anchored. if there's an even number, then
- // the dollar is unescaped and the pattern is anchored.
- for (int n = len - 2; n >= 0; --n)
- if (str.charAt(n) != '\')
- return (len - n) % 2 == 0;
- // The string is of the form "+$". If the length is an odd
- // number (ie, an even number of '' and a '$') the pattern is
- // anchored
- return len % 2 != 0;
- }
- /**
- * returns true if the pattern must match from the beginning of the string,
- * or false if the pattern can match anywhere in a string.
- */
- public boolean isStartAnchored() {
- return MATCH_RULE_EQUALS.equals(getMatchRule()) ||
- MATCH_RULE_STARTSWITH.equals(getMatchRule());
- }
- //
- // /**
- // * sets the default interpretation of the pattern for strings it will later
- // * be given. Setting this value to true will force the pattern to match from
- // * the beginning of tested strings. Setting this value to false will allow
- // * the pattern to match any part of a tested string.
- // */
- // public void setStartAnchored(boolean startAnchored) {
- // boolean old = isStartAnchored();
- // this.startAnchored = startAnchored;
- // updatePattern(createRegEx(getRawText()));
- // firePropertyChange("startAnchored", old, isStartAnchored());
- // }
- //
- /**
- * returns true if the pattern must match from the beginning of the string,
- * or false if the pattern can match anywhere in a string.
- */
- public boolean isEndAnchored() {
- return MATCH_RULE_EQUALS.equals(getMatchRule()) ||
- MATCH_RULE_ENDSWITH.equals(getMatchRule());
- }
- //
- // /**
- // * sets the default interpretation of the pattern for strings it will later
- // * be given. Setting this value to true will force the pattern to match the
- // * end of tested strings. Setting this value to false will allow the pattern
- // * to match any part of a tested string.
- // */
- // public void setEndAnchored(boolean endAnchored) {
- // boolean old = isEndAnchored();
- // this.endAnchored = endAnchored;
- // updatePattern(createRegEx(getRawText()));
- // firePropertyChange("endAnchored", old, isEndAnchored());
- // }
- //
- // public boolean isStartEndAnchored() {
- // return isEndAnchored() && isStartAnchored();
- // }
- //
- // /**
- // * sets the default interpretation of the pattern for strings it will later
- // * be given. Setting this value to true will force the pattern to match the
- // * end of tested strings. Setting this value to false will allow the pattern
- // * to match any part of a tested string.
- // */
- // public void setStartEndAnchored(boolean endAnchored) {
- // boolean old = isStartEndAnchored();
- // this.endAnchored = endAnchored;
- // this.startAnchored = endAnchored;
- // updatePattern(createRegEx(getRawText()));
- // firePropertyChange("StartEndAnchored", old, isStartEndAnchored());
- // }
- }
- /**
- *
- * @param mode
- */
- public void setRegexCreatorKey(String mode) {
- if (getRegexCreatorKey().equals(mode)) return;
- String old = getRegexCreatorKey();
- regexCreatorKey = mode;
- firePropertyChange("regexCreatorKey", old, getRegexCreatorKey());
- }
- public String getRegexCreatorKey() {
- if (regexCreatorKey == null) {
- regexCreatorKey = getDefaultRegexCreatorKey();
- }
- return regexCreatorKey;
- }
- private String getDefaultRegexCreatorKey() {
- return REGEX_MATCH_RULES;
- }
- public void setMatchRule(String category) {
- if (getMatchRule().equals(category)) {
- return;
- }
- String old = getMatchRule();
- getRegexCreator().setMatchRule(category);
- updatePattern(createRegEx(getRawText()));
- firePropertyChange("matchRule", old, getMatchRule());
- }
- public String getMatchRule() {
- return getRegexCreator().getMatchRule();
- }
- private RegexCreator getRegexCreator() {
- if (regexCreator == null) {
- regexCreator = new RegexCreator();
- }
- return regexCreator;
- }
- public List getMatchRules() {
- return getRegexCreator().getMatchRules();
- }
- }