This is a follow-up review of Simple wildcard pattern matcher in Java
I have built a simple wild card pattern matcher algorithm.
I am concerned where it can be improved further.
- Main concern is algorithm.
- Any other improvements are welcome too.
What it should does:
- Match
*
-> one or more of any character- Match
?
-> any single character similar to the file search wild card pattern (ex: Windows file search)
Code:
public class SimpleMatch {
//state enums
private static enum State {
JUST_STARTED, NORMAL, EAGER, END
}
//constants
private static final char MATCH_ALL = '*';
private static final char MATCH_ONE = '?';
private final int ptnOutBound; // pattern out bound
private final int strOutBound; // string out bound
private final String pattern; // pattern
private final String matchString; // string to match
private int ptnPosition; // position of pattern
private int strPosition; // position of string
private State state = State.JUST_STARTED; // state
private boolean matchFound = false; // is match
public SimpleMatch(String pattern, String matchStr) {
if (pattern == null || matchStr == null) {
throw new IllegalArgumentException(
"Pattern and String must not be null");
}
this.pattern = pattern;
this.matchString = matchStr;
int pl = pattern.length();
int sl = matchStr.length();
if (pl == 0 || sl == 0) {
throw new IllegalArgumentException(
"Pattern and String must have at least one character");
}
ptnOutBound = pl - 1;
strOutBound = sl - 1;
ptnPosition = 0;
strPosition = 0;
}
private void calcState() {
//calculate state
if (state == State.END) {
return;
}
if (!patternCheckBound() || !matchStrCheckBound()) {
state = State.END;
} else if (patternChar() == MATCH_ALL) {
if (!patternNextCheckBound()) {
state = State.END;
matchFound = true;
} else {
state = State.EAGER;
}
} else {
state = State.NORMAL;
}
}
private void eat() {
//eat a character
if (state == State.END) {
return;
}
matchFound = false;
if (state == State.EAGER) {
int curStrPosition = strPosition;
int curPtnPosition = ptnPosition;
strPosition++;
ptnPosition++;
if (match()) {
state = State.END;
matchFound = true;
return;
} else {
strPosition = curStrPosition;
ptnPosition = curPtnPosition;
state = State.EAGER;
}
strPosition++;
} else if (state == State.NORMAL) {
if (matchOne()) {
strPosition++;
ptnPosition++;
matchFound = true;
} else {
state = State.END;
}
}
}
private boolean matchOne() {
// match one
char pc = patternChar();
return (pc == MATCH_ONE || pc == matchStrChar());
}
private char patternChar() {
// pattern current char
return pattern.charAt(ptnPosition);
}
private char matchStrChar() {
// str current char
return matchString.charAt(strPosition);
}
private boolean patternCheckBound() {
//pattern position bound check
return ptnPosition <= ptnOutBound;
}
private boolean patternNextCheckBound() {
//pattern next position bound check
return (ptnPosition + 1) <= ptnOutBound;
}
private boolean matchStrCheckBound() {
//string bound check
return strPosition <= strOutBound;
}
/**
* Match and return result
*
* @return true if match
*/
public boolean match() {
if (ptnOutBound > strOutBound) {
return false;
}
while (state != State.END) {
calcState();
eat();
}
return matchFound;
}
/**
* Match and return result
*
* @param pattern pattern
* @param matchStr string to match
* @return true if match
* @throws IllegalArgumentException
*/
public static boolean match(String pattern, String matchStr) throws
IllegalArgumentException {
return new SimpleMatch(pattern, matchStr).match();
}
}
Unit Test:
This code currently passes following jUnit Test Cases
import org.junit.Assert;
import org.junit.Test;
/**
* Unit Test for SimpleMatch
*
* @author Bhathiya
*/
public class SimpleMatchTest {
@Test
public void test1() {
SimpleMatch m = new SimpleMatch("a*", "bb");
Assert.assertFalse(m.match());
}
@Test
public void test2() {
SimpleMatch m = new SimpleMatch("a*b", "anj");
Assert.assertFalse(m.match());
}
@Test
public void test3() {
SimpleMatch m = new SimpleMatch("a?b", "acd");
Assert.assertFalse(m.match());
}
@Test
public void test4() {
SimpleMatch m = new SimpleMatch("a*", "abcdefg");
Assert.assertTrue(m.match());
}
@Test
public void test5() {
SimpleMatch m = new SimpleMatch("a*ba", "abba");
Assert.assertTrue(m.match());
}
@Test
public void test6() {
SimpleMatch m = new SimpleMatch("bhathiya", "bhathiya");
Assert.assertTrue(m.match());
}
@Test
public void test7() {
SimpleMatch m = new SimpleMatch("a?", "a1");
Assert.assertTrue(m.match());
}
@Test
public void test8() {
SimpleMatch m = new SimpleMatch("bhathiya", "blah");
Assert.assertFalse(m.match());
}
@Test
public void test9() {
SimpleMatch m = new SimpleMatch("/img/abc.jpg", "/img/abc.jpg");
Assert.assertTrue(m.match());
}
@Test
public void test11() {
SimpleMatch m = new SimpleMatch("/x/*/z/abc.jpg", "/x/a/z/abc.jpg");
Assert.assertTrue(m.match());
}
@Test
public void test12() {
SimpleMatch m = new SimpleMatch("/x/*/z/abc.jpg", "/x/a/j/abc.jpg");
Assert.assertFalse(m.match());
}
@Test
public void test13() {
SimpleMatch m = new SimpleMatch("a", "a");
Assert.assertTrue(m.match());
}
@Test
public void test14() {
SimpleMatch m = new SimpleMatch("a", "b");
Assert.assertFalse(m.match());
}
@Test
public void test15() {
SimpleMatch m = new SimpleMatch("aa", "ab");
Assert.assertFalse(m.match());
}
@Test
public void test16() {
SimpleMatch m = new SimpleMatch("aaaa", "a");
Assert.assertFalse(m.match());
m = new SimpleMatch("aaaa", "aaa");
Assert.assertFalse(m.match());
m = new SimpleMatch("aaaa", "aa");
Assert.assertFalse(m.match());
}
@Test
public void test17() {
SimpleMatch m = new SimpleMatch("a*", "a");
Assert.assertFalse(m.match());
}
@Test
public void test18() {
SimpleMatch m = new SimpleMatch("a*xxxxxxxxxxb", "axxxxxxxxxxxb");
Assert.assertTrue(m.match());
}
@Test
public void test19() {
SimpleMatch m = new SimpleMatch("a*xxxxxxxxxxb", "axxxxxxxxxxxc");
Assert.assertFalse(m.match());
}
@Test
public void test20() {
SimpleMatch m = new SimpleMatch("a*b*c*d", "abbccdd");
Assert.assertTrue(m.match());
}
@Test
public void test21() {
SimpleMatch m = new SimpleMatch("a*xxxxxxx*xxb", "axxxxxxxxxhelloxxb");
Assert.assertTrue(m.match());
}
@Test
public void test22() {
SimpleMatch m = new SimpleMatch("a*xxxxxxxxxx*", "axxxxxxxxxxxhello");
Assert.assertTrue(m.match());
}
@Test
public void test23() {
SimpleMatch m = new SimpleMatch(
"start*in-part-1*in-part-2*in-part-3*end",
"start[because]in-part-1[I'm]in-part-2[Batman]in-part-3[!]end");
Assert.assertTrue(m.match());
}
}
Related Links:
- travis-ci: https://travis-ci.org/JaDogg/SimpleMatch
- repository: https://github.com/JaDogg/SimpleMatch