[JGIT PATCH v4 01/24] Added the package fnmatch and two exceptions.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Florian Koeberle <florianskarten@xxxxxx>
---
 .../spearce/jgit/fnmatch/FileNameMatcherTest.java  |  678 ++++++++++++++++++++
 .../jgit/errors/InvalidPatternException.java       |   65 ++
 .../jgit/errors/NoClosingBracketException.java     |   69 ++
 .../src/org/spearce/jgit/fnmatch/AbstractHead.java |   74 +++
 .../org/spearce/jgit/fnmatch/CharacterHead.java    |   53 ++
 .../org/spearce/jgit/fnmatch/FileNameMatcher.java  |  327 ++++++++++
 .../src/org/spearce/jgit/fnmatch/GroupHead.java    |  220 +++++++
 .../src/org/spearce/jgit/fnmatch/Head.java         |   50 ++
 .../src/org/spearce/jgit/fnmatch/LastHead.java     |   56 ++
 .../jgit/fnmatch/RestrictedWildCardHead.java       |   52 ++
 .../src/org/spearce/jgit/fnmatch/WildCardHead.java |   49 ++
 11 files changed, 1693 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/FileNameMatcherTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/errors/InvalidPatternException.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/errors/NoClosingBracketException.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHead.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatcher.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/RestrictedWildCardHead.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/FileNameMatcherTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/FileNameMatcherTest.java
new file mode 100644
index 0000000..74e88fa
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/FileNameMatcherTest.java
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.fnmatch;
+
+import org.spearce.jgit.errors.InvalidPatternException;
+import org.spearce.jgit.fnmatch.FileNameMatcher;
+
+import junit.framework.TestCase;
+
+public class FileNameMatcherTest extends TestCase {
+
+	private void assertMatch(final String pattern, final String input,
+			final boolean matchExpected, final boolean appendCanMatchExpected)
+			throws InvalidPatternException {
+		final FileNameMatcher matcher = new FileNameMatcher(pattern, null);
+		matcher.append(input);
+		assertEquals(matchExpected, matcher.isMatch());
+		assertEquals(appendCanMatchExpected, matcher.canAppendMatch());
+	}
+
+	private void assertFileNameMatch(final String pattern, final String input,
+			final char excludedCharacter, final boolean matchExpected,
+			final boolean appendCanMatchExpected)
+			throws InvalidPatternException {
+		final FileNameMatcher matcher = new FileNameMatcher(pattern,
+				new Character(excludedCharacter));
+		matcher.append(input);
+		assertEquals(matchExpected, matcher.isMatch());
+		assertEquals(appendCanMatchExpected, matcher.canAppendMatch());
+	}
+
+	public void testVerySimplePatternCase0() throws Exception {
+		assertMatch("", "", true, false);
+	}
+
+	public void testVerySimplePatternCase1() throws Exception {
+		assertMatch("ab", "a", false, true);
+	}
+
+	public void testVerySimplePatternCase2() throws Exception {
+		assertMatch("ab", "ab", true, false);
+	}
+
+	public void testVerySimplePatternCase3() throws Exception {
+		assertMatch("ab", "ac", false, false);
+	}
+
+	public void testVerySimplePatternCase4() throws Exception {
+		assertMatch("ab", "abc", false, false);
+	}
+
+	public void testVerySimpleWirdcardCase0() throws Exception {
+		assertMatch("?", "a", true, false);
+	}
+
+	public void testVerySimpleWildCardCase1() throws Exception {
+		assertMatch("??", "a", false, true);
+	}
+
+	public void testVerySimpleWildCardCase2() throws Exception {
+		assertMatch("??", "ab", true, false);
+	}
+
+	public void testVerySimpleWildCardCase3() throws Exception {
+		assertMatch("??", "abc", false, false);
+	}
+
+	public void testVerySimpleStarCase0() throws Exception {
+		assertMatch("*", "", true, true);
+	}
+
+	public void testVerySimpleStarCase1() throws Exception {
+		assertMatch("*", "a", true, true);
+	}
+
+	public void testVerySimpleStarCase2() throws Exception {
+		assertMatch("*", "ab", true, true);
+	}
+
+	public void testSimpleStarCase0() throws Exception {
+		assertMatch("a*b", "a", false, true);
+	}
+
+	public void testSimpleStarCase1() throws Exception {
+		assertMatch("a*c", "ac", true, true);
+	}
+
+	public void testSimpleStarCase2() throws Exception {
+		assertMatch("a*c", "ab", false, true);
+	}
+
+	public void testSimpleStarCase3() throws Exception {
+		assertMatch("a*c", "abc", true, true);
+	}
+
+	public void testManySolutionsCase0() throws Exception {
+		assertMatch("a*a*a", "aaa", true, true);
+	}
+
+	public void testManySolutionsCase1() throws Exception {
+		assertMatch("a*a*a", "aaaa", true, true);
+	}
+
+	public void testManySolutionsCase2() throws Exception {
+		assertMatch("a*a*a", "ababa", true, true);
+	}
+
+	public void testManySolutionsCase3() throws Exception {
+		assertMatch("a*a*a", "aaaaaaaa", true, true);
+	}
+
+	public void testManySolutionsCase4() throws Exception {
+		assertMatch("a*a*a", "aaaaaaab", false, true);
+	}
+
+	public void testVerySimpleGroupCase0() throws Exception {
+		assertMatch("[ab]", "a", true, false);
+	}
+
+	public void testVerySimpleGroupCase1() throws Exception {
+		assertMatch("[ab]", "b", true, false);
+	}
+
+	public void testVerySimpleGroupCase2() throws Exception {
+		assertMatch("[ab]", "ab", false, false);
+	}
+
+	public void testVerySimpleGroupRangeCase0() throws Exception {
+		assertMatch("[b-d]", "a", false, false);
+	}
+
+	public void testVerySimpleGroupRangeCase1() throws Exception {
+		assertMatch("[b-d]", "b", true, false);
+	}
+
+	public void testVerySimpleGroupRangeCase2() throws Exception {
+		assertMatch("[b-d]", "c", true, false);
+	}
+
+	public void testVerySimpleGroupRangeCase3() throws Exception {
+		assertMatch("[b-d]", "d", true, false);
+	}
+
+	public void testVerySimpleGroupRangeCase4() throws Exception {
+		assertMatch("[b-d]", "e", false, false);
+	}
+
+	public void testVerySimpleGroupRangeCase5() throws Exception {
+		assertMatch("[b-d]", "-", false, false);
+	}
+
+	public void testTwoGroupsCase0() throws Exception {
+		assertMatch("[b-d][ab]", "bb", true, false);
+	}
+
+	public void testTwoGroupsCase1() throws Exception {
+		assertMatch("[b-d][ab]", "ca", true, false);
+	}
+
+	public void testTwoGroupsCase2() throws Exception {
+		assertMatch("[b-d][ab]", "fa", false, false);
+	}
+
+	public void testTwoGroupsCase3() throws Exception {
+		assertMatch("[b-d][ab]", "bc", false, false);
+	}
+
+	public void testTwoRangesInOneGroupCase0() throws Exception {
+		assertMatch("[b-ce-e]", "a", false, false);
+	}
+
+	public void testTwoRangesInOneGroupCase1() throws Exception {
+		assertMatch("[b-ce-e]", "b", true, false);
+	}
+
+	public void testTwoRangesInOneGroupCase2() throws Exception {
+		assertMatch("[b-ce-e]", "c", true, false);
+	}
+
+	public void testTwoRangesInOneGroupCase3() throws Exception {
+		assertMatch("[b-ce-e]", "d", false, false);
+	}
+
+	public void testTwoRangesInOneGroupCase4() throws Exception {
+		assertMatch("[b-ce-e]", "e", true, false);
+	}
+
+	public void testTwoRangesInOneGroupCase5() throws Exception {
+		assertMatch("[b-ce-e]", "f", false, false);
+	}
+
+	public void testIncompleteRangesInOneGroupCase0() throws Exception {
+		assertMatch("a[b-]", "ab", true, false);
+	}
+
+	public void testIncompleteRangesInOneGroupCase1() throws Exception {
+		assertMatch("a[b-]", "ac", false, false);
+	}
+
+	public void testIncompleteRangesInOneGroupCase2() throws Exception {
+		assertMatch("a[b-]", "a-", true, false);
+	}
+
+	public void testCombinedRangesInOneGroupCase0() throws Exception {
+		assertMatch("[a-c-e]", "b", true, false);
+	}
+
+	/**
+	 * The c belongs to the range a-c. "-e" is no valid range so d should not
+	 * match.
+	 * 
+	 * @throws Exception
+	 *             for some reasons
+	 */
+	public void testCombinedRangesInOneGroupCase1() throws Exception {
+		assertMatch("[a-c-e]", "d", false, false);
+	}
+
+	public void testCombinedRangesInOneGroupCase2() throws Exception {
+		assertMatch("[a-c-e]", "e", true, false);
+	}
+
+	public void testInversedGroupCase0() throws Exception {
+		assertMatch("[!b-c]", "a", true, false);
+	}
+
+	public void testInversedGroupCase1() throws Exception {
+		assertMatch("[!b-c]", "b", false, false);
+	}
+
+	public void testInversedGroupCase2() throws Exception {
+		assertMatch("[!b-c]", "c", false, false);
+	}
+
+	public void testInversedGroupCase3() throws Exception {
+		assertMatch("[!b-c]", "d", true, false);
+	}
+
+	public void testAlphaGroupCase0() throws Exception {
+		assertMatch("[[:alpha:]]", "d", true, false);
+	}
+
+	public void testAlphaGroupCase1() throws Exception {
+		assertMatch("[[:alpha:]]", ":", false, false);
+	}
+
+	public void testAlphaGroupCase2() throws Exception {
+		assertMatch("[[:alpha:]]", "ö", true, false);
+	}
+
+	public void test2AlphaGroupsCase0() throws Exception {
+		assertMatch("[[:alpha:]][[:alpha:]]", "aö", true, false);
+		assertMatch("[[:alpha:]][[:alpha:]]", "a1", false, false);
+	}
+
+	public void testAlnumGroupCase0() throws Exception {
+		assertMatch("[[:alnum:]]", "a", true, false);
+	}
+
+	public void testAlnumGroupCase1() throws Exception {
+		assertMatch("[[:alnum:]]", "1", true, false);
+	}
+
+	public void testAlnumGroupCase2() throws Exception {
+		assertMatch("[[:alnum:]]", ":", false, false);
+	}
+
+	public void testBlankGroupCase0() throws Exception {
+		assertMatch("[[:blank:]]", " ", true, false);
+	}
+
+	public void testBlankGroupCase1() throws Exception {
+		assertMatch("[[:blank:]]", "\t", true, false);
+	}
+
+	public void testBlankGroupCase2() throws Exception {
+		assertMatch("[[:blank:]]", "\r", false, false);
+	}
+
+	public void testBlankGroupCase3() throws Exception {
+		assertMatch("[[:blank:]]", "\n", false, false);
+	}
+
+	public void testBlankGroupCase4() throws Exception {
+		assertMatch("[[:blank:]]", "a", false, false);
+	}
+
+	public void testCntrlGroupCase0() throws Exception {
+		assertMatch("[[:cntrl:]]", "a", false, false);
+	}
+
+	public void testCntrlGroupCase1() throws Exception {
+		assertMatch("[[:cntrl:]]", String.valueOf((char) 7), true, false);
+	}
+
+	public void testDigitGroupCase0() throws Exception {
+		assertMatch("[[:digit:]]", "0", true, false);
+	}
+
+	public void testDigitGroupCase1() throws Exception {
+		assertMatch("[[:digit:]]", "5", true, false);
+	}
+
+	public void testDigitGroupCase2() throws Exception {
+		assertMatch("[[:digit:]]", "9", true, false);
+	}
+
+	public void testDigitGroupCase3() throws Exception {
+		assertMatch("[[:digit:]]", "Û¹", true, false);
+	}
+
+	public void testDigitGroupCase4() throws Exception {
+		assertMatch("[[:digit:]]", "a", false, false);
+	}
+
+	public void testDigitGroupCase5() throws Exception {
+		assertMatch("[[:digit:]]", "]", false, false);
+	}
+
+	public void testGraphGroupCase0() throws Exception {
+		assertMatch("[[:graph:]]", "]", true, false);
+	}
+
+	public void testGraphGroupCase1() throws Exception {
+		assertMatch("[[:graph:]]", "a", true, false);
+	}
+
+	public void testGraphGroupCase2() throws Exception {
+		assertMatch("[[:graph:]]", ".", true, false);
+	}
+
+	public void testGraphGroupCase3() throws Exception {
+		assertMatch("[[:graph:]]", "0", true, false);
+	}
+
+	public void testGraphGroupCase4() throws Exception {
+		assertMatch("[[:graph:]]", " ", false, false);
+	}
+
+	public void testGraphGroupCase5() throws Exception {
+		assertMatch("[[:graph:]]", "ö", true, false);
+	}
+
+	public void testLowerGroupCase0() throws Exception {
+		assertMatch("[[:lower:]]", "a", true, false);
+	}
+
+	public void testLowerGroupCase1() throws Exception {
+		assertMatch("[[:lower:]]", "h", true, false);
+	}
+
+	public void testLowerGroupCase2() throws Exception {
+		assertMatch("[[:lower:]]", "A", false, false);
+	}
+
+	public void testLowerGroupCase3() throws Exception {
+		assertMatch("[[:lower:]]", "H", false, false);
+	}
+
+	public void testLowerGroupCase4() throws Exception {
+		assertMatch("[[:lower:]]", "ä", true, false);
+	}
+
+	public void testLowerGroupCase5() throws Exception {
+		assertMatch("[[:lower:]]", ".", false, false);
+	}
+
+	public void testPrintGroupCase0() throws Exception {
+		assertMatch("[[:print:]]", "]", true, false);
+	}
+
+	public void testPrintGroupCase1() throws Exception {
+		assertMatch("[[:print:]]", "a", true, false);
+	}
+
+	public void testPrintGroupCase2() throws Exception {
+		assertMatch("[[:print:]]", ".", true, false);
+	}
+
+	public void testPrintGroupCase3() throws Exception {
+		assertMatch("[[:print:]]", "0", true, false);
+	}
+
+	public void testPrintGroupCase4() throws Exception {
+		assertMatch("[[:print:]]", " ", true, false);
+	}
+
+	public void testPrintGroupCase5() throws Exception {
+		assertMatch("[[:print:]]", "ö", true, false);
+	}
+
+	public void testPunctGroupCase0() throws Exception {
+		assertMatch("[[:punct:]]", ".", true, false);
+	}
+
+	public void testPunctGroupCase1() throws Exception {
+		assertMatch("[[:punct:]]", "@", true, false);
+	}
+
+	public void testPunctGroupCase2() throws Exception {
+		assertMatch("[[:punct:]]", " ", false, false);
+	}
+
+	public void testPunctGroupCase3() throws Exception {
+		assertMatch("[[:punct:]]", "a", false, false);
+	}
+
+	public void testSpaceGroupCase0() throws Exception {
+		assertMatch("[[:space:]]", " ", true, false);
+	}
+
+	public void testSpaceGroupCase1() throws Exception {
+		assertMatch("[[:space:]]", "\t", true, false);
+	}
+
+	public void testSpaceGroupCase2() throws Exception {
+		assertMatch("[[:space:]]", "\r", true, false);
+	}
+
+	public void testSpaceGroupCase3() throws Exception {
+		assertMatch("[[:space:]]", "\n", true, false);
+	}
+
+	public void testSpaceGroupCase4() throws Exception {
+		assertMatch("[[:space:]]", "a", false, false);
+	}
+
+	public void testUpperGroupCase0() throws Exception {
+		assertMatch("[[:upper:]]", "a", false, false);
+	}
+
+	public void testUpperGroupCase1() throws Exception {
+		assertMatch("[[:upper:]]", "h", false, false);
+	}
+
+	public void testUpperGroupCase2() throws Exception {
+		assertMatch("[[:upper:]]", "A", true, false);
+	}
+
+	public void testUpperGroupCase3() throws Exception {
+		assertMatch("[[:upper:]]", "H", true, false);
+	}
+
+	public void testUpperGroupCase4() throws Exception {
+		assertMatch("[[:upper:]]", "Ã?", true, false);
+	}
+
+	public void testUpperGroupCase5() throws Exception {
+		assertMatch("[[:upper:]]", ".", false, false);
+	}
+
+	public void testXDigitGroupCase0() throws Exception {
+		assertMatch("[[:xdigit:]]", "a", true, false);
+	}
+
+	public void testXDigitGroupCase1() throws Exception {
+		assertMatch("[[:xdigit:]]", "d", true, false);
+	}
+
+	public void testXDigitGroupCase2() throws Exception {
+		assertMatch("[[:xdigit:]]", "f", true, false);
+	}
+
+	public void testXDigitGroupCase3() throws Exception {
+		assertMatch("[[:xdigit:]]", "0", true, false);
+	}
+
+	public void testXDigitGroupCase4() throws Exception {
+		assertMatch("[[:xdigit:]]", "5", true, false);
+	}
+
+	public void testXDigitGroupCase5() throws Exception {
+		assertMatch("[[:xdigit:]]", "9", true, false);
+	}
+
+	public void testXDigitGroupCase6() throws Exception {
+		assertMatch("[[:xdigit:]]", "Û¹", false, false);
+	}
+
+	public void testXDigitGroupCase7() throws Exception {
+		assertMatch("[[:xdigit:]]", ".", false, false);
+	}
+
+	public void testWordroupCase0() throws Exception {
+		assertMatch("[[:word:]]", "g", true, false);
+	}
+
+	public void testWordroupCase1() throws Exception {
+		assertMatch("[[:word:]]", "ö", true, false);
+	}
+
+	public void testWordroupCase2() throws Exception {
+		assertMatch("[[:word:]]", "5", true, false);
+	}
+
+	public void testWordroupCase3() throws Exception {
+		assertMatch("[[:word:]]", "_", true, false);
+	}
+
+	public void testWordroupCase4() throws Exception {
+		assertMatch("[[:word:]]", " ", false, false);
+	}
+
+	public void testWordroupCase5() throws Exception {
+		assertMatch("[[:word:]]", ".", false, false);
+	}
+
+	public void testSpecialGroupCase0() throws Exception {
+		assertMatch("[[]", "[", true, false);
+	}
+
+	public void testSpecialGroupCase1() throws Exception {
+		assertMatch("[]]", "]", true, false);
+	}
+
+	public void testSpecialGroupCase2() throws Exception {
+		assertMatch("[]a]", "]", true, false);
+	}
+
+	public void testSpecialGroupCase3() throws Exception {
+		assertMatch("[a[]", "[", true, false);
+	}
+
+	public void testSpecialGroupCase4() throws Exception {
+		assertMatch("[a[]", "a", true, false);
+	}
+
+	public void testSpecialGroupCase5() throws Exception {
+		assertMatch("[!]]", "]", false, false);
+	}
+
+	public void testSpecialGroupCase6() throws Exception {
+		assertMatch("[!]]", "x", true, false);
+	}
+
+	public void testSpecialGroupCase7() throws Exception {
+		assertMatch("[:]]", ":]", true, false);
+	}
+
+	public void testSpecialGroupCase8() throws Exception {
+		assertMatch("[:]]", ":", false, true);
+	}
+
+	public void testSpecialGroupCase9() throws Exception {
+		try {
+			assertMatch("[[:]", ":", true, true);
+			fail("InvalidPatternException expected");
+		} catch (InvalidPatternException e) {
+			// expected
+		}
+	}
+
+	public void testUnsupportedGroupCase0() throws Exception {
+		try {
+			assertMatch("[[=a=]]", "b", false, false);
+			fail("InvalidPatternException expected");
+		} catch (InvalidPatternException e) {
+			assertTrue(e.getMessage().contains("[=a=]"));
+		}
+	}
+
+	public void testUnsupportedGroupCase1() throws Exception {
+		try {
+			assertMatch("[[.a.]]", "b", false, false);
+			fail("InvalidPatternException expected");
+		} catch (InvalidPatternException e) {
+			assertTrue(e.getMessage().contains("[.a.]"));
+		}
+	}
+
+	public void testFilePathSimpleCase() throws Exception {
+		assertFileNameMatch("a/b", "a/b", '/', true, false);
+	}
+
+	public void testFilePathCase0() throws Exception {
+		assertFileNameMatch("a*b", "a/b", '/', false, false);
+	}
+
+	public void testFilePathCase1() throws Exception {
+		assertFileNameMatch("a?b", "a/b", '/', false, false);
+	}
+
+	public void testFilePathCase2() throws Exception {
+		assertFileNameMatch("a*b", "a\\b", '\\', false, false);
+	}
+
+	public void testFilePathCase3() throws Exception {
+		assertFileNameMatch("a?b", "a\\b", '\\', false, false);
+	}
+
+	public void testReset() throws Exception {
+		final String pattern = "helloworld";
+		final FileNameMatcher matcher = new FileNameMatcher(pattern, null);
+		matcher.append("helloworld");
+		assertEquals(true, matcher.isMatch());
+		assertEquals(false, matcher.canAppendMatch());
+		matcher.reset();
+		matcher.append("hello");
+		assertEquals(false, matcher.isMatch());
+		assertEquals(true, matcher.canAppendMatch());
+		matcher.append("world");
+		assertEquals(true, matcher.isMatch());
+		assertEquals(false, matcher.canAppendMatch());
+		matcher.append("to much");
+		assertEquals(false, matcher.isMatch());
+		assertEquals(false, matcher.canAppendMatch());
+		matcher.reset();
+		matcher.append("helloworld");
+		assertEquals(true, matcher.isMatch());
+		assertEquals(false, matcher.canAppendMatch());
+	}
+
+	public void testCreateMatcherForSuffix() throws Exception {
+		final String pattern = "helloworld";
+		final FileNameMatcher matcher = new FileNameMatcher(pattern, null);
+		matcher.append("hello");
+		final FileNameMatcher childMatcher = matcher.createMatcherForSuffix();
+		assertEquals(false, matcher.isMatch());
+		assertEquals(true, matcher.canAppendMatch());
+		assertEquals(false, childMatcher.isMatch());
+		assertEquals(true, childMatcher.canAppendMatch());
+		matcher.append("world");
+		assertEquals(true, matcher.isMatch());
+		assertEquals(false, matcher.canAppendMatch());
+		assertEquals(false, childMatcher.isMatch());
+		assertEquals(true, childMatcher.canAppendMatch());
+		childMatcher.append("world");
+		assertEquals(true, matcher.isMatch());
+		assertEquals(false, matcher.canAppendMatch());
+		assertEquals(true, childMatcher.isMatch());
+		assertEquals(false, childMatcher.canAppendMatch());
+		childMatcher.reset();
+		assertEquals(true, matcher.isMatch());
+		assertEquals(false, matcher.canAppendMatch());
+		assertEquals(false, childMatcher.isMatch());
+		assertEquals(true, childMatcher.canAppendMatch());
+		childMatcher.append("world");
+		assertEquals(true, matcher.isMatch());
+		assertEquals(false, matcher.canAppendMatch());
+		assertEquals(true, childMatcher.isMatch());
+		assertEquals(false, childMatcher.canAppendMatch());
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/errors/InvalidPatternException.java b/org.spearce.jgit/src/org/spearce/jgit/errors/InvalidPatternException.java
new file mode 100644
index 0000000..02f67fe
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/errors/InvalidPatternException.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.errors;
+
+/**
+ * Thrown when a pattern passed in an argument was wrong.
+ * 
+ */
+public class InvalidPatternException extends Exception {
+	private final String pattern;
+
+	/**
+	 * @param message
+	 *            explains what was wrong with the pattern.
+	 * @param pattern
+	 *            the invalid pattern.
+	 */
+	public InvalidPatternException(String message, String pattern) {
+		super(message);
+		this.pattern = pattern;
+	}
+
+	/**
+	 * @return the invalid pattern.
+	 */
+	public String getPattern() {
+		return pattern;
+	}
+
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/errors/NoClosingBracketException.java b/org.spearce.jgit/src/org/spearce/jgit/errors/NoClosingBracketException.java
new file mode 100644
index 0000000..1a93906
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/errors/NoClosingBracketException.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.errors;
+
+/**
+ * Thrown when a pattern contains a character group which is open to the right
+ * side or a character class which is open to the right side.
+ */
+public class NoClosingBracketException extends InvalidPatternException {
+
+	/**
+	 * @param indexOfOpeningBracket
+	 *            the position of the [ character which has no ] character.
+	 * @param openingBracket
+	 *            the unclosed bracket.
+	 * @param closingBracket
+	 *            the missing closing bracket.
+	 * @param pattern
+	 *            the invalid pattern.
+	 */
+	public NoClosingBracketException(final int indexOfOpeningBracket,
+			final String openingBracket, final String closingBracket,
+			final String pattern) {
+		super(createMessage(indexOfOpeningBracket, openingBracket,
+				closingBracket), pattern);
+	}
+
+	private static String createMessage(final int indexOfOpeningBracket,
+			final String openingBracket, final String closingBracket) {
+		return String.format("No closing %s found for %s at index %s.",
+				closingBracket, openingBracket, new Integer(
+						indexOfOpeningBracket));
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead.java
new file mode 100644
index 0000000..1e9a0ca
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.fnmatch;
+
+import java.util.List;
+
+abstract class AbstractHead implements Head {
+	private List<Head> newHeads = null;
+
+	private final boolean star;
+
+	protected abstract boolean matches(char c);
+
+	AbstractHead(boolean star) {
+		this.star = star;
+	}
+
+	/**
+	 * 
+	 * @param newHeads
+	 *            a list of {@link Head}s which will not be modified.
+	 */
+	public final void setNewHeads(List<Head> newHeads) {
+		if (this.newHeads != null)
+			throw new IllegalStateException("Property is already non null");
+		this.newHeads = newHeads;
+	}
+
+	public List<Head> getNextHeads(char c) {
+		if (matches(c))
+			return newHeads;
+		else
+			return FileNameMatcher.EMPTY_HEAD_LIST;
+	}
+
+	boolean isStar() {
+		return star;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHead.java
new file mode 100644
index 0000000..01c3403
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHead.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.fnmatch;
+
+final class CharacterHead extends AbstractHead {
+	private final char expectedCharacter;
+
+	protected CharacterHead(final char expectedCharacter) {
+		super(false);
+		this.expectedCharacter = expectedCharacter;
+	}
+
+	@Override
+	protected final boolean matches(final char c) {
+		return c == expectedCharacter;
+	}
+
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatcher.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatcher.java
new file mode 100644
index 0000000..21fbf77
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatcher.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.fnmatch;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.spearce.jgit.errors.InvalidPatternException;
+import org.spearce.jgit.errors.NoClosingBracketException;
+
+/**
+ * This class can be used to match filenames against fnmatch like patterns. It
+ * is not thread save.
+ */
+public class FileNameMatcher {
+	static final List<Head> EMPTY_HEAD_LIST = Collections.emptyList();
+
+	private static final Pattern characterClassStartPattern = Pattern
+			.compile("\\[[.:=]");
+
+	private List<Head> headsStartValue;
+
+	private List<Head> heads;
+
+	/**
+	 * {{@link #extendStringToMatchByOneCharacter(char)} needs a list for the
+	 * new heads, allocating a new array would be bad for the performance, as
+	 * the method gets called very often.
+	 * 
+	 */
+	private List<Head> listForLocalUseage;
+
+	/**
+	 * 
+	 * @param headsStartValue
+	 *            must be a list which will never be modified.
+	 */
+	private FileNameMatcher(final List<Head> headsStartValue) {
+		this.headsStartValue = headsStartValue;
+		this.heads = new ArrayList<Head>(headsStartValue.size());
+		this.heads.addAll(this.headsStartValue);
+		this.listForLocalUseage = new ArrayList<Head>(headsStartValue.size());
+	}
+
+	/**
+	 * @param patternString
+	 *            must contain a pattern which fnmatch would accept.
+	 * @param invalidWildgetCharacter
+	 *            if this parameter isn't null then this character will not
+	 *            match at wildcards(* and ? are wildcards).
+	 * @throws InvalidPatternException
+	 *             if the patternString contains a invalid fnmatch pattern.
+	 */
+	public FileNameMatcher(final String patternString,
+			final Character invalidWildgetCharacter)
+			throws InvalidPatternException {
+		this(createHeadsStartValues(patternString, invalidWildgetCharacter));
+	}
+
+	private static List<Head> createHeadsStartValues(
+			final String patternString, final Character invalidWildgetCharacter)
+			throws InvalidPatternException {
+
+		final List<AbstractHead> allHeads = parseHeads(patternString,
+				invalidWildgetCharacter);
+
+		List<Head> nextHeadsSuggestion = new ArrayList<Head>(2);
+		nextHeadsSuggestion.add(LastHead.INSTANCE);
+		for (int i = allHeads.size() - 1; i >= 0; i--) {
+			final AbstractHead head = allHeads.get(i);
+
+			// explanation:
+			// a and * of the pattern "a*b"
+			// need *b as newHeads
+			// that's why * extends the list for it self and it's left neighbor.
+			if (head.isStar()) {
+				nextHeadsSuggestion.add(head);
+				head.setNewHeads(nextHeadsSuggestion);
+			} else {
+				head.setNewHeads(nextHeadsSuggestion);
+				nextHeadsSuggestion = new ArrayList<Head>(2);
+				nextHeadsSuggestion.add(head);
+			}
+		}
+		return nextHeadsSuggestion;
+	}
+
+	private static int findGroupEnd(final int indexOfStartBracket,
+			final String pattern) throws InvalidPatternException {
+		int firstValidCharClassIndex = indexOfStartBracket + 1;
+		int firstValidEndBracketIndex = indexOfStartBracket + 2;
+
+		if (indexOfStartBracket + 1 >= pattern.length())
+			throw new NoClosingBracketException(indexOfStartBracket, "[", "]",
+					pattern);
+
+		if (pattern.charAt(firstValidCharClassIndex) == '!') {
+			firstValidCharClassIndex++;
+			firstValidEndBracketIndex++;
+		}
+
+		final Matcher charClassStartMatcher = characterClassStartPattern
+				.matcher(pattern);
+
+		int groupEnd = -1;
+		while (groupEnd == -1) {
+
+			final int possibleGroupEnd = pattern.indexOf(']',
+					firstValidEndBracketIndex);
+			if (possibleGroupEnd == -1)
+				throw new NoClosingBracketException(indexOfStartBracket, "[",
+						"]", pattern);
+
+			final boolean foundCharClass = charClassStartMatcher
+					.find(firstValidCharClassIndex);
+
+			if (foundCharClass
+					&& charClassStartMatcher.start() < possibleGroupEnd) {
+
+				final String classStart = charClassStartMatcher.group(0);
+				final String classEnd = classStart.charAt(1) + "]";
+
+				final int classStartIndex = charClassStartMatcher.start();
+				final int classEndIndex = pattern.indexOf(classEnd,
+						classStartIndex + 2);
+
+				if (classEndIndex == -1)
+					throw new NoClosingBracketException(classStartIndex,
+							classStart, classEnd, pattern);
+
+				firstValidCharClassIndex = classEndIndex + 2;
+				firstValidEndBracketIndex = firstValidCharClassIndex;
+			} else {
+				groupEnd = possibleGroupEnd;
+			}
+		}
+		return groupEnd;
+	}
+
+	private static List<AbstractHead> parseHeads(final String pattern,
+			final Character invalidWildgetCharacter)
+			throws InvalidPatternException {
+
+		int currentIndex = 0;
+		List<AbstractHead> heads = new ArrayList<AbstractHead>();
+		while (currentIndex < pattern.length()) {
+			final int groupStart = pattern.indexOf('[', currentIndex);
+			if (groupStart == -1) {
+				final String patternPart = pattern.substring(currentIndex);
+				heads.addAll(createSimpleHeads(patternPart,
+						invalidWildgetCharacter));
+				currentIndex = pattern.length();
+			} else {
+				final String patternPart = pattern.substring(currentIndex,
+						groupStart);
+				heads.addAll(createSimpleHeads(patternPart,
+						invalidWildgetCharacter));
+
+				final int groupEnd = findGroupEnd(groupStart, pattern);
+				final String groupPart = pattern.substring(groupStart + 1,
+						groupEnd);
+				heads.add(new GroupHead(groupPart, pattern));
+				currentIndex = groupEnd + 1;
+			}
+		}
+		return heads;
+	}
+
+	private static List<AbstractHead> createSimpleHeads(
+			final String patternPart, final Character invalidWildgetCharacter) {
+		final List<AbstractHead> heads = new ArrayList<AbstractHead>(
+				patternPart.length());
+		for (int i = 0; i < patternPart.length(); i++) {
+			final char c = patternPart.charAt(i);
+			switch (c) {
+			case '*': {
+				final AbstractHead head = createWildCardHead(
+						invalidWildgetCharacter, true);
+				heads.add(head);
+				break;
+			}
+			case '?': {
+				final AbstractHead head = createWildCardHead(
+						invalidWildgetCharacter, false);
+				heads.add(head);
+				break;
+			}
+			default:
+				final CharacterHead head = new CharacterHead(c);
+				heads.add(head);
+			}
+		}
+		return heads;
+	}
+
+	private static AbstractHead createWildCardHead(
+			final Character invalidWildgetCharacter, final boolean star) {
+		if (invalidWildgetCharacter != null)
+			return new RestrictedWildCardHead(invalidWildgetCharacter
+					.charValue(), star);
+		else
+			return new WildCardHead(star);
+	}
+
+	private void extendStringToMatchByOneCharacter(final char c) {
+		final List<Head> newHeads = listForLocalUseage;
+		newHeads.clear();
+		List<Head> lastAddedHeads = null;
+		for (int i = 0; i < heads.size(); i++) {
+			final Head head = heads.get(i);
+			final List<Head> headsToAdd = head.getNextHeads(c);
+			// Why the next performance optimization isn't wrong:
+			// Some times two heads return the very same list.
+			// We save future effort if we don't add these heads again.
+			// This is the case with the heads "a" and "*" of "a*b" which
+			// both can return the list ["*","b"]
+			if (headsToAdd != lastAddedHeads) {
+				newHeads.addAll(headsToAdd);
+				lastAddedHeads = headsToAdd;
+			}
+		}
+		listForLocalUseage = heads;
+		heads = newHeads;
+	}
+
+	/**
+	 * 
+	 * @param stringToMatch
+	 *            extends the string which is matched against the patterns of
+	 *            this class.
+	 */
+	public void append(final String stringToMatch) {
+		for (int i = 0; i < stringToMatch.length(); i++) {
+			final char c = stringToMatch.charAt(i);
+			extendStringToMatchByOneCharacter(c);
+		}
+	}
+
+	/**
+	 * Resets this matcher to it's state right after construction.
+	 */
+	public void reset() {
+		heads.clear();
+		heads.addAll(headsStartValue);
+	}
+
+	/**
+	 * 
+	 * @return a {@link FileNameMatcher} instance which uses the same pattern
+	 *         like this matcher, but has the current state of this matcher as
+	 *         reset and start point.
+	 */
+	public FileNameMatcher createMatcherForSuffix() {
+		final List<Head> copyOfHeads = new ArrayList<Head>(heads.size());
+		copyOfHeads.addAll(heads);
+		return new FileNameMatcher(copyOfHeads);
+	}
+
+	/**
+	 * 
+	 * @return true, if the string currently being matched does match.
+	 */
+	public boolean isMatch() {
+		final ListIterator<Head> headIterator = heads
+				.listIterator(heads.size());
+		while (headIterator.hasPrevious()) {
+			final Head head = headIterator.previous();
+			if (head == LastHead.INSTANCE) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * 
+	 * @return false, if the string being matched will not match when the string
+	 *         gets extended.
+	 */
+	public boolean canAppendMatch() {
+		for (int i = 0; i < heads.size(); i++) {
+			if (heads.get(i) != LastHead.INSTANCE) {
+				return true;
+			}
+		}
+		return false;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.java
new file mode 100644
index 0000000..9f72010
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.fnmatch;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.spearce.jgit.errors.InvalidPatternException;
+
+final class GroupHead extends AbstractHead {
+	private final List<CharacterPattern> characterClasses;
+
+	private static final Pattern REGEX_PATTERN = Pattern
+			.compile("([^-][-][^-]|\\[[.:=].*?[.:=]\\])");
+
+	private final boolean inverse;
+
+	GroupHead(String pattern, final String wholePattern)
+			throws InvalidPatternException {
+		super(false);
+		this.characterClasses = new ArrayList<CharacterPattern>();
+		this.inverse = pattern.startsWith("!");
+		if (inverse) {
+			pattern = pattern.substring(1);
+		}
+		final Matcher matcher = REGEX_PATTERN.matcher(pattern);
+		while (matcher.find()) {
+			final String characterClass = matcher.group(0);
+			if (characterClass.length() == 3 && characterClass.charAt(1) == '-') {
+				final char start = characterClass.charAt(0);
+				final char end = characterClass.charAt(2);
+				characterClasses.add(new CharacterRange(start, end));
+			} else if (characterClass.equals("[:alnum:]")) {
+				characterClasses.add(LetterPattern.INSTANCE);
+				characterClasses.add(DigitPattern.INSTANCE);
+			} else if (characterClass.equals("[:alpha:]")) {
+				characterClasses.add(LetterPattern.INSTANCE);
+			} else if (characterClass.equals("[:blank:]")) {
+				characterClasses.add(new OneCharacterPattern(' '));
+				characterClasses.add(new OneCharacterPattern('\t'));
+			} else if (characterClass.equals("[:cntrl:]")) {
+				characterClasses.add(new CharacterRange('\u0000', '\u001F'));
+				characterClasses.add(new OneCharacterPattern('\u007F'));
+			} else if (characterClass.equals("[:digit:]")) {
+				characterClasses.add(DigitPattern.INSTANCE);
+			} else if (characterClass.equals("[:graph:]")) {
+				characterClasses.add(new CharacterRange('\u0021', '\u007E'));
+				characterClasses.add(LetterPattern.INSTANCE);
+				characterClasses.add(DigitPattern.INSTANCE);
+			} else if (characterClass.equals("[:lower:]")) {
+				characterClasses.add(LowerPattern.INSTANCE);
+			} else if (characterClass.equals("[:print:]")) {
+				characterClasses.add(new CharacterRange('\u0020', '\u007E'));
+				characterClasses.add(LetterPattern.INSTANCE);
+				characterClasses.add(DigitPattern.INSTANCE);
+			} else if (characterClass.equals("[:punct:]")) {
+				characterClasses.add(PunctPattern.INSTANCE);
+			} else if (characterClass.equals("[:space:]")) {
+				characterClasses.add(WhitespacePattern.INSTANCE);
+			} else if (characterClass.equals("[:upper:]")) {
+				characterClasses.add(UpperPattern.INSTANCE);
+			} else if (characterClass.equals("[:xdigit:]")) {
+				characterClasses.add(new CharacterRange('0', '9'));
+				characterClasses.add(new CharacterRange('a', 'f'));
+				characterClasses.add(new CharacterRange('A', 'F'));
+			} else if (characterClass.equals("[:word:]")) {
+				characterClasses.add(new OneCharacterPattern('_'));
+				characterClasses.add(LetterPattern.INSTANCE);
+				characterClasses.add(DigitPattern.INSTANCE);
+			} else {
+				final String message = String.format(
+						"The character class %s is not supported.",
+						characterClass);
+				throw new InvalidPatternException(message, wholePattern);
+			}
+
+			pattern = matcher.replaceFirst("");
+			matcher.reset(pattern);
+		}
+		// pattern contains now no ranges
+		for (int i = 0; i < pattern.length(); i++) {
+			final char c = pattern.charAt(i);
+			characterClasses.add(new OneCharacterPattern(c));
+		}
+	}
+
+	@Override
+	protected final boolean matches(final char c) {
+		for (CharacterPattern pattern : characterClasses) {
+			if (pattern.matches(c)) {
+				return !inverse;
+			}
+		}
+		return inverse;
+	}
+
+	private interface CharacterPattern {
+		/**
+		 * @param c
+		 *            the character to test
+		 * @return returns true if the character matches a pattern.
+		 */
+		boolean matches(char c);
+	}
+
+	private static final class CharacterRange implements CharacterPattern {
+		private final char start;
+
+		private final char end;
+
+		CharacterRange(char start, char end) {
+			this.start = start;
+			this.end = end;
+		}
+
+		public final boolean matches(char c) {
+			return start <= c && c <= end;
+		}
+	}
+
+	private static final class DigitPattern implements CharacterPattern {
+		static final GroupHead.DigitPattern INSTANCE = new DigitPattern();
+
+		public final boolean matches(char c) {
+			return Character.isDigit(c);
+		}
+	}
+
+	private static final class LetterPattern implements CharacterPattern {
+		static final GroupHead.LetterPattern INSTANCE = new LetterPattern();
+
+		public final boolean matches(char c) {
+			return Character.isLetter(c);
+		}
+	}
+
+	private static final class LowerPattern implements CharacterPattern {
+		static final GroupHead.LowerPattern INSTANCE = new LowerPattern();
+
+		public final boolean matches(char c) {
+			return Character.isLowerCase(c);
+		}
+	}
+
+	private static final class UpperPattern implements CharacterPattern {
+		static final GroupHead.UpperPattern INSTANCE = new UpperPattern();
+
+		public final boolean matches(char c) {
+			return Character.isUpperCase(c);
+		}
+	}
+
+	private static final class WhitespacePattern implements CharacterPattern {
+		static final GroupHead.WhitespacePattern INSTANCE = new WhitespacePattern();
+
+		public final boolean matches(char c) {
+			return Character.isWhitespace(c);
+		}
+	}
+
+	private static final class OneCharacterPattern implements CharacterPattern {
+		private char expectedCharacter;
+
+		OneCharacterPattern(final char c) {
+			this.expectedCharacter = c;
+		}
+
+		public final boolean matches(char c) {
+			return this.expectedCharacter == c;
+		}
+	}
+
+	private static final class PunctPattern implements CharacterPattern {
+		static final GroupHead.PunctPattern INSTANCE = new PunctPattern();
+
+		private static String punctCharacters = "-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~";
+
+		public boolean matches(char c) {
+			return punctCharacters.indexOf(c) != -1;
+		}
+	}
+
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java
new file mode 100644
index 0000000..498f96c
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.fnmatch;
+
+import java.util.List;
+
+interface Head {
+	/**
+	 * 
+	 * @param c
+	 *            the character which decides which heads are returned.
+	 * @return a list of heads based on the input.
+	 */
+	public abstract List<Head> getNextHeads(char c);
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.java
new file mode 100644
index 0000000..d3c9813
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.fnmatch;
+
+import java.util.List;
+
+final class LastHead implements Head {
+	static final Head INSTANCE = new LastHead();
+
+	/**
+	 * Don't call this constructor, use {@link #INSTANCE}
+	 */
+	private LastHead() {
+		// defined because of javadoc and visibility modifier.
+	}
+
+	public List<Head> getNextHeads(char c) {
+		return FileNameMatcher.EMPTY_HEAD_LIST;
+	}
+
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/RestrictedWildCardHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/RestrictedWildCardHead.java
new file mode 100644
index 0000000..9d8d277
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/RestrictedWildCardHead.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.fnmatch;
+
+final class RestrictedWildCardHead extends AbstractHead {
+	private final char excludedCharacter;
+
+	RestrictedWildCardHead(final char excludedCharacter, final boolean star) {
+		super(star);
+		this.excludedCharacter = excludedCharacter;
+	}
+
+	@Override
+	protected final boolean matches(final char c) {
+		return c != excludedCharacter;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead.java
new file mode 100644
index 0000000..570e374
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008, Florian Köberle <florianskarten@xxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.fnmatch;
+
+final class WildCardHead extends AbstractHead {
+	WildCardHead(boolean star) {
+		super(star);
+	}
+
+	@Override
+	protected final boolean matches(final char c) {
+		return true;
+	}
+}
-- 
1.5.4.3

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux