I have built a simple wild card pattern matcher algorithm.
I am concerned where it can be improved.
- 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 {
private static enum State {
JUST_STARTED, NORMAL, EAGER, END
}
private final int pl; // pattern length
private final int pob; // pattern out bound
private final int sl; // string length
private final int sob; // string out bound
private final String p; // pattern
private final String s; // string to match
private static final char MATCH_ALL = '*';
private static final char MATCH_ONE = '?';
private int pp; // position of pattern
private int ps; // position of string
private State z; // state
private boolean m = false; // is match
public SimpleMatch(String p, String s) {
if (p == null || s == null) {
throw new IllegalArgumentException(
"Pattern and String must not be null");
}
this.p = p;
this.s = s;
pl = p.length();
sl = s.length();
if (pl == 0 || sl == 0) {
throw new IllegalArgumentException(
"Pattern and String must have at least one character");
}
pob = pl - 1;
sob = sl - 1;
pp = 0;
ps = 0;
z = State.JUST_STARTED;
}
private SimpleMatch(String p, String s, int pp, int ps) {
this(p, s);
this.pp = pp;
this.ps = ps;
}
private void calcState() {
//calculate state
if (z == State.END) {
return;
}
if (!psafe() || !ssafe()) {
z = State.END;
} else if (pc() == MATCH_ALL) {
if (!pnsafe()) {
z = State.END;
m = true;
} else {
z = State.EAGER;
}
} else {
z = State.NORMAL;
}
}
private void eat() {
//eat a character
if (z == State.END) {
return;
}
m = false;
if (z == State.EAGER) {
SimpleMatch smo = new SimpleMatch(p, s, pp + 1, ps + 1);
if (smo.match()) {
z = State.END;
m = true;
return;
}
ips();
} else if (z == State.NORMAL) {
if (mo()) {
ips();
ipp();
m = true;
} else {
z = State.END;
m = false;
}
}
}
private boolean mo() {
// match one
char pc = pc();
return (pc == MATCH_ONE || pc == sc());
}
private char pc() {
// pattern current char
return p.charAt(pp);
}
private char sc() {
// str current char
return s.charAt(ps);
}
private boolean psafe() {
//pattern position bound check
return pp <= pob;
}
private boolean pnsafe() {
//pattern next position bound check
return (pp + 1) <= pob;
}
private boolean ssafe() {
//string bound check
return ps <= sob;
}
private void ipp() {
//increase position of pattern
pp++;
}
private void ips() {
//increate position of string
ps++;
}
/**
* Match and return result
* @return true if match
*/
public boolean match() {
if (pob > sob) {
return false;
}
while (z != State.END) {
calcState();
eat();
}
return m;
}
/**
* Match and return result
* @param p pattern
* @param s string to match
* @return true if match
* @throws IllegalArgumentException
*/
public static boolean match(String p, String s) throws
IllegalArgumentException {
return new SimpleMatch(p, s).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 m1 = new SimpleMatch("a*", "bb");
Assert.assertFalse(m1.match());
}
@Test
public void test2() {
SimpleMatch m2 = new SimpleMatch("a*b", "anj");
Assert.assertFalse(m2.match());
}
@Test
public void test3() {
SimpleMatch m3 = new SimpleMatch("a?b", "acd");
Assert.assertFalse(m3.match());
}
@Test
public void test4() {
SimpleMatch m4 = new SimpleMatch("a*", "abcdefg");
Assert.assertTrue(m4.match());
}
@Test
public void test5() {
SimpleMatch m5 = new SimpleMatch("a*ba", "abba");
Assert.assertTrue(m5.match());
}
@Test
public void test6() {
SimpleMatch m6 = new SimpleMatch("bhathiya", "bhathiya");
Assert.assertTrue(m6.match());
}
@Test
public void test7() {
SimpleMatch m7 = new SimpleMatch("a?", "a1");
Assert.assertTrue(m7.match());
}
@Test
public void test8() {
SimpleMatch m8 = new SimpleMatch("bhathiya", "blah");
Assert.assertFalse(m8.match());
}
@Test
public void test9() {
SimpleMatch m9 = new SimpleMatch("/img/abc.jpg", "/img/abc.jpg");
Assert.assertTrue(m9.match());
}
@Test
public void test11() {
SimpleMatch m9 = new SimpleMatch("/x/*/z/abc.jpg", "/x/a/z/abc.jpg");
Assert.assertTrue(m9.match());
}
@Test
public void test12() {
SimpleMatch m9 = new SimpleMatch("/x/*/z/abc.jpg", "/x/a/j/abc.jpg");
Assert.assertFalse(m9.match());
}
@Test
public void test13() {
SimpleMatch m9 = new SimpleMatch("a", "a");
Assert.assertTrue(m9.match());
}
@Test
public void test14() {
SimpleMatch m9 = new SimpleMatch("a", "b");
Assert.assertFalse(m9.match());
}
@Test
public void test15() {
SimpleMatch m9 = new SimpleMatch("aa", "ab");
Assert.assertFalse(m9.match());
}
}
Related Links:
- travis-ci: https://travis-ci.org/JaDogg/SimpleMatch
- repository: https://github.com/JaDogg/SimpleMatch
*
represent no character ? Like*test
would matchtest
. \$\endgroup\$**
,?*
and*?
to a single*
\$\endgroup\$