001: /*
002: * Copyright (c) 2003-2008, Franz-Josef Elmer, All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * - Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: *
013: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
014: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
015: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
016: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
017: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
018: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
019: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
020: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
021: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
022: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
023: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
024: */
025: package classycle.util;
026:
027: import java.util.StringTokenizer;
028:
029: /**
030: * Wildcard string pattern matching class. Only '*' is interpreted as wild card
031: * meaning the occurance of any number of arbitray characters.
032: * <p>
033: * This is a thread-safe immutable class.
034: * <p>
035: * Example: The code snippet
036: * <pre><tt>
037: * StringPattern pattern = new WildCardPattern("Hello*");
038: * System.out.println(pattern.matches("Hello world!"));
039: * System.out.println(pattern.matches("Hi Jim!"));
040: * </tt></pre>
041: * will produce the output
042: * <pre><tt>
043: * true
044: * false
045: * </tt></pre>
046: *
047: * @author Franz-Josef Elmer
048: */
049: public class WildCardPattern implements StringPattern {
050: private static final String WILD_CARD = "*";
051:
052: /**
053: * Returns a {@link StringPattern} object based on a sequences of
054: * wild-card patterns separated by the specified delimiter characters.
055: * The return object matches a string if at least one of the
056: * wild-card pattern matches.
057: * @param patterns Wild-card patterns separated by delimiters defined
058: * in <tt>delimiters</tt>. The actual pattern will be trimed.
059: * That is, leading and trailing white-space characters are removed.
060: * @param delimiters Recognized delimiters.
061: * @return
062: */
063: public static StringPattern createFromsPatterns(String patterns,
064: String delimiters) {
065: if (delimiters.indexOf(WILD_CARD) >= 0) {
066: throw new IllegalArgumentException("No wild card '"
067: + WILD_CARD + "' are allowed as delimiters: "
068: + delimiters);
069: }
070: OrStringPattern result = new OrStringPattern();
071: StringTokenizer tokenizer = new StringTokenizer(patterns,
072: delimiters);
073: while (tokenizer.hasMoreTokens()) {
074: result.appendPattern(new WildCardPattern(tokenizer
075: .nextToken().trim()));
076: }
077: return result;
078: }
079:
080: private final String _pattern;
081: private final String[] _constantParts;
082: private final boolean _startsWithAnything;
083: private final boolean _endsWithAnything;
084:
085: /**
086: * Creates an instance based on the specified pattern.
087: * @param pattern Pattern which may contain '*' wildcard characters.
088: * Must be not <tt>null</tt>.
089: */
090: public WildCardPattern(String pattern) {
091: _pattern = pattern;
092: _startsWithAnything = pattern.startsWith(WILD_CARD);
093: _endsWithAnything = pattern.endsWith(WILD_CARD);
094: StringTokenizer tokenizer = new StringTokenizer(pattern,
095: WILD_CARD);
096: _constantParts = new String[tokenizer.countTokens()];
097: for (int i = 0; i < _constantParts.length; i++) {
098: _constantParts[i] = tokenizer.nextToken();
099: }
100: }
101:
102: /**
103: * Returns the pattern as delivered to the constructor.
104: */
105: public String toString() {
106: return _pattern;
107: }
108:
109: /**
110: * @return <tt>false</tt> if <tt>string == null</tt>.
111: */
112: public boolean matches(String string) {
113: return string == null ? false : matches(string, 0, 0);
114: }
115:
116: private boolean matches(String string, int indexInString,
117: int indexInConstantParts) {
118: boolean result = true;
119: if (indexInConstantParts < _constantParts.length) {
120: String constantPart = _constantParts[indexInConstantParts];
121: do {
122: int index = string.indexOf(constantPart, indexInString);
123: if (index < 0
124: || (indexInString == 0 && !_startsWithAnything && index > 0)) {
125: result = false;
126: break;
127: }
128: indexInString = index + constantPart.length();
129: result = matches(string, indexInString,
130: indexInConstantParts + 1);
131: } while (result == false);
132: } else {
133: result = result
134: && (_endsWithAnything || indexInString == string
135: .length());
136: }
137: return result;
138: }
139: }
|