001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.xml.xam.ui.search;
043:
044: /**
045: * A string matcher that supports a small set of wildcards (e.g. * and ?).
046: * See the documentation for the <code>match()</code> method for details.
047: *
048: * @see #match(String, String)
049: * @author Nathan Fiedler
050: */
051: public class WildcardStringMatcher {
052:
053: /**
054: * Creates a new instance of WildcardStringMatcher.
055: */
056: private WildcardStringMatcher() {
057: }
058:
059: /**
060: * Determines if the given query string contains wildcards that this
061: * string matcher understands.
062: *
063: * @param query query string, which may or may not contain wildcards.
064: * @return true if query has recognized wildcards, false otherwise.
065: */
066: public static boolean containsWildcards(String query) {
067: return query.contains("*") || query.contains("?");
068: }
069:
070: /**
071: * Scans the given text for a pattern, indicating if the text matched
072: * the pattern or not. The pattern wildcards are as follows:
073: *
074: * <ul>
075: * <li>? matches any single character.</li>
076: * <li>* matches zero or more characters.</li>
077: * </ul>
078: *
079: * <p>The text must match the entire pattern, or it is not considered
080: * a match. That is, if "floss*" is the pattern, then it will only
081: * match text that begins with "floss", while "floss?" will match
082: * "flossy" but not "floss" since it expects an additional character.</p>
083: *
084: * <p>Note that * is greedy, such that "*foo" will match "foofoofoo".</p>
085: *
086: * @param text the text in which to look for the pattern.
087: * @param query the pattern to match, may contain wildcards.
088: * @return true if matches, false otherwise.
089: */
090: public static boolean match(String text, String query) {
091: int ti;
092: int qi;
093: int tl = text.length();
094: int ql = query.length();
095: boolean star = false;
096:
097: for (ti = 0, qi = 0; ti < tl; ti++, qi++) {
098: // This line allows this algorithm to be greedy, such that
099: // "*foo" will match "foofoofoo", and "*a" matches "aaa".
100: char qc = qi < ql ? query.charAt(qi) : 0;
101: switch (qc) {
102: case '?':
103: // We allow question marks to match anything.
104: break;
105: case '*':
106: star = true;
107: // Skip over consecutive asterisks.
108: do {
109: qi++;
110: } while (qi < ql && query.charAt(qi) == '*');
111: if (qi == ql) {
112: // Query ended with an asterisk, that makes a match.
113: return true;
114: }
115: // Simulate recursion on both substrings.
116: text = text.substring(ti);
117: query = query.substring(qi);
118: tl = text.length();
119: ql = query.length();
120: ti = -1;
121: qi = -1;
122: break;
123: default:
124: char tc = text.charAt(ti);
125: if (tc != qc) {
126: if (!star) {
127: // No asterisk and not a match, exit immediately.
128: return false;
129: }
130: // Simulate recursion on the text substring.
131: text = text.substring(1);
132: tl--;
133: ti = -1;
134: qi = -1;
135: }
136: break;
137: }
138: }
139: // Consume any trailing asterisks in query.
140: while (qi < ql && query.charAt(qi) == '*') {
141: qi++;
142: }
143: // It is a match only if we reached the ends of both strings.
144: return ti >= tl && qi >= ql;
145: }
146: }
|