001: // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
002:
003: package jodd.util;
004:
005: /**
006: * Checks whether a string matches a given wildcard pattern.
007: * Possible patterns allow to match single characters ('?') or any count of
008: * characters ('*'). Wildcard characters can be escaped (by an '\').
009: * <p>
010: * This method uses recursive matching, as in linux or windows. regexp works the same.
011: * This method is very fast, comparing to similar implementations.
012: */
013: public class Wildcard {
014:
015: /**
016: * Checks whether a string matches a given wildcard pattern.
017: *
018: * @param string input string
019: * @param pattern pattern to match
020: * @return <code>true</code> if string matches the pattern, otherwise <code>false</code>
021: */
022: public static boolean match(String string, String pattern) {
023: return match(string, pattern, 0, 0);
024: }
025:
026: /**
027: * Checks if two strings are equals or if they {@link #match(String, String)}.
028: * Useful for cases when matching a lot of equal strings and speed is important.
029: */
030: public static boolean equalsOrMatch(String string, String pattern) {
031: if (string.equals(pattern) == true) {
032: return true;
033: }
034: return match(string, pattern, 0, 0);
035: }
036:
037: /**
038: * Internal matching recursive function.
039: */
040: private static boolean match(String string, String pattern,
041: int stringStartNdx, int patternStartNdx) {
042: int pNdx = patternStartNdx;
043: int sNdx = stringStartNdx;
044: int pLen = pattern.length();
045: if (pLen == 1) {
046: if (pattern.charAt(0) == '*') { // speed-up
047: return true;
048: }
049: }
050: int sLen = string.length();
051: boolean nextIsNotWildcard = false;
052:
053: while (true) {
054:
055: // check if end of string and/or pattern occurred
056: if ((sNdx >= sLen) == true) { // end of string still may have pending '*' in pattern
057: while ((pNdx < pLen) && (pattern.charAt(pNdx) == '*')) {
058: pNdx++;
059: }
060: return pNdx >= pLen;
061: }
062: if (pNdx >= pLen) { // end of pattern, but not end of the string
063: return false;
064: }
065: char p = pattern.charAt(pNdx); // pattern char
066:
067: // perform logic
068: if (nextIsNotWildcard == false) {
069:
070: if (p == '\\') {
071: pNdx++;
072: nextIsNotWildcard = true;
073: continue;
074: }
075: if (p == '?') {
076: sNdx++;
077: pNdx++;
078: continue;
079: }
080: if (p == '*') {
081: char pnext = 0; // next pattern char
082: if (pNdx + 1 < pLen) {
083: pnext = pattern.charAt(pNdx + 1);
084: }
085: if (pnext == '*') { // double '*' have the same effect as one '*'
086: pNdx++;
087: continue;
088: }
089: int i;
090: pNdx++;
091:
092: // find recursively if there is any substring from the end of the
093: // line that matches the rest of the pattern !!!
094: for (i = string.length(); i >= sNdx; i--) {
095: if (match(string, pattern, i, pNdx) == true) {
096: return true;
097: }
098: }
099: return false;
100: }
101: } else {
102: nextIsNotWildcard = false;
103: }
104:
105: // check if pattern char and string char are equals
106: if (p != string.charAt(sNdx)) {
107: return false;
108: }
109:
110: // everything matches for now, continue
111: sNdx++;
112: pNdx++;
113: }
114: }
115:
116: // ---------------------------------------------------------------- utilities
117:
118: /**
119: * Matches string to at least one pattern.
120: * Returns index of matched pattern, or <code>-1</code> otherwise.
121: * @see #match(String, String)
122: */
123: public static int matchOne(String src, String[] patterns) {
124: for (int i = 0; i < patterns.length; i++) {
125: if (match(src, patterns[i]) == true) {
126: return i;
127: }
128: }
129: return -1;
130: }
131:
132: }
|